- Extension loader with manifest validation, ID sanitization, and path traversal protection
- Permission-gated runtime API (render, cursor, audio, timeline, ui, assets, export)
- Extension host with lifecycle management (activate/deactivate)
- Marketplace client for browsing, downloading, and installing extensions
- Extension Manager UI panel with enable/disable/uninstall
- IPC bridge for extension discovery, management, and marketplace operations
- Proxy guard on window.electronAPI to block extension access to privileged IPC
- Zip-slip protection via post-extraction directory traversal validation
- Safe PowerShell invocation for Windows zip extraction (no string interpolation)
- File URL resolution with path escape prevention
- Add rowId to allRegionSpans so collision detection only considers
siblings on the same timeline row
- Split clampToNeighbours into clampResizedSpanToNeighbours and
clampDraggedSpanToNeighbours for resize vs drag semantics
- Preserve original clip duration on drag (pure translation) to prevent
pixel-to-ms rounding drift at different zoom levels
The analytical velocity fix makes the spring settle more precisely,
slightly increasing frame count to convergence. Relax threshold
from 120 to 400 frames.
Use analytical velocity (closed-form derivative) instead of frame-to-frame
finite differences for spring state propagation. Add overshoot clamp for
overdamped springs (ζ ≥ 1) to prevent counter-oscillation when the zoom
easing target reverses direction mid-animation.
Align decoded frame timestamps with the media timeline so trimmed exports do not end early on recordings whose video stream starts after the container timeline.
When deleting the Whisper model failed, the frontend kept stale state
(whisperModelDownloadStatus stayed as 'downloaded'), which blocked
re-downloading because the download handler saw the model as already
present. On the backend, no progress event was sent on failure, leaving
the renderer out of sync.
Frontend: reset whisperModelDownloadStatus and progress to idle on
delete failure so re-download is unblocked.
Backend: verify whether the file was actually removed despite the error
and send appropriate progress events in either case.
Closes#152
The hasOverlap function in TimelineEditor checked zoom, trim, speed, and
audio regions for overlap but was missing clip regions entirely. This
caused clips to freely overlap each other when dragged or resized,
leading to playback bugs when trimmed clips intersected.
Add clip region overlap detection so the existing clampToNeighbours
logic in TimelineWrapper correctly constrains clip boundaries.
Closes#166
When loading a saved project on startup, applyLoadedProject() overwrites
all editor state — including user preference settings like padding,
aspect ratio, and export format — with potentially stale project values.
The subsequent save effect then writes those stale values back to
localStorage, effectively erasing the user's latest preferences.
Now re-applies preference-level settings from localStorage after project
load so that per-user settings (padding, border radius, aspect ratio,
export format/quality/encoding, gif settings) survive across sessions.
Fixes#197
Instead of freezing at 'Finalizing 99%' while audio renders in real-time,
the export UI now shows 'Rendering audio XX%' with a live progress bar and
an explanatory note about why real-time playback is required.
Progress is reported every 250ms from the RAF tick loop in AudioProcessor
and piped through VideoExporter to the UI via the existing onProgress callback.
handleZoomSuggested creates regions with mode: 'auto',
handleZoomAdded creates regions with mode: 'manual'.
Wire zoomSmoothness state and mode change handler.
Cursor follow camera tracks cursor position for auto zoom regions
with edge snapping and zoom-out freeze. Edge snap focus remaps cursor
to clamp near screen boundaries.