State management
Klippy uses Redux Toolkit for predictable state, with purpose‑built slices, memoized selectors, and a layered middleware pipeline for performance (debounced duration updates, validation, and unified history tracking).
1) Store overview
- Store file:
app/store/index.ts
— configures reducers and middleware. - Slices:
projectState
: media/text/zoom, captions, transitions, export settings, recording, project meta, history.timeline
: currentTime, play state, zoom, snapping, scroll.selection
: selected element IDs and related UI state.transcripts
: transcripts per media, panel prefs.edits
: remove/mute/silence ranges with dedicated undo/redo.theme
,ffmpeg
,aiComponents
,preferences
,secureApi
,audioEffects
,projects
.
- Selectors: under
app/store/selectors/*
with Reselect memoization (seetimelineSelectors.ts
). - Persistence: Projects/files in IndexedDB via
storageManager
; state snapshots for undo/redo in memory.
2) Key slices
projectState
- Holds timeline content:
mediaFiles
,textElements
,zoomElements
, caption tracks. - Derived durations recalculated when content changes (
calculateDurations
helper). - Transitions map (
betweenClipTransitions
) and IDs. - Recording settings/state and TTS state.
- History arrays (
history
/future
) for project‑level undo/redo.
timeline
- Playback (
currentTime
,isPlaying
), timeline zoom, snapping flags. - Scroll positions and hover times for synchronized headers/markers.
edits
- Remove/mute/silence ranges with local undo/redo (
undoEdits
/redoEdits
). - Preview lane and focused range for quick operations.
transcripts
- Transcript text and timings per media ID.
- Panel preferences (virtualization, snap to phrases, merge thresholds).
3) Middleware pipeline
- StateUpdateQueue: Orders batched changes to minimize intermediate re‑renders.
- DurationMiddleware: Debounced recompute of content/timeline durations after edits.
- Validation: Guards invariant violations (e.g., element times, track bounds).
- UnifiedHistory + CombinedUndoRedo: Consolidates meaningful actions into a single history entry and mirrors edits slice undo/redo with project undo/redo where applicable.
See app/store/index.ts
for the exact order and comments.
4) Undo and redo
- Project‑level undo/redo:
projectState.history
/future
with helpers likerecordHistory
,undo
,redo
. - Edits‑level undo/redo: independent stacks in
edits
slice for transcript‑driven ranges. - Middleware ensures both stacks remain consistent when cross‑slice actions occur.
5) Selectors
Heavy timeline UIs rely on memoized selectors from app/store/selectors/timelineSelectors.ts
to avoid recomputation. Examples:
selectVisibleMediaFiles
— windowing bycurrentTime
and rows.selectContentDuration
— derives from media + text + audio.selectSelectedElements
— resolves models from selected IDs.selectZoomLevel
,selectCurrentTime
,selectIsPlaying
— high‑frequency reads.
6) Persistence & file storage
- Files and projects are stored in IndexedDB using a high‑level
storageManager
. - Utility functions in
app/store/index.ts
exposestoreFile
,getFile
,listProjects
, etc. - Blob URLs created for preview are cleaned up by the components that own them.
7) Patterns for performance
- Prefer slice actions that update arrays immutably but compactly (e.g.,
setMediaFiles
batches duration recompute). - Use Reselect selectors in timeline rendering to avoid O(n) passes on every mouse move.
- Use
runInBatch
(where applicable) to group multi‑dispatch operations into a single history entry. - Virtualize large lists (timeline rows, transcript words) and reduce overscan on low‑RAM/mobile.
8) Where to look in code
- Store:
app/store/index.ts
- Project slice:
app/store/slices/projectSlice.ts
- Timeline slice:
app/store/slices/timelineSlice.ts
- Edits slice:
app/store/slices/editsSlice.ts
- Transcripts slice:
app/store/slices/transcriptsSlice.ts
- Selectors:
app/store/selectors/timelineSelectors.ts
- History middlewares:
app/store/middleware/*