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 (see timelineSelectors.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

  1. StateUpdateQueue: Orders batched changes to minimize intermediate re‑renders.
  2. DurationMiddleware: Debounced recompute of content/timeline durations after edits.
  3. Validation: Guards invariant violations (e.g., element times, track bounds).
  4. 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 like recordHistory, 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 by currentTime 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 expose storeFile, 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/*