- 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.
The zoom spring animation in both frameRenderer and modernFrameRenderer
was using performance.now() (wall-clock time) to compute spring deltas.
During export, wall-clock time and content time are decoupled — a frame
representing 33ms of video may render in 3ms or 500ms. This caused zoom
animations to appear choppy/laggy in exports while previews were smooth.
Switched to content-time deltas (timeMs) so the spring advances at the
same rate as the video regardless of actual render speed.
Also bumps version to 1.1.20.
handleZoomSuggested creates regions with mode: 'auto',
handleZoomAdded creates regions with mode: 'manual'.
Wire zoomSmoothness state and mode change handler.
Both legacy (FrameRenderer) and modern (ModernFrameRenderer) exporters
now use inlined spring solver and cursor follow camera for auto zooms.
Wire zoomSmoothness config through both exporter paths.
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.
Add complete Korean translation across all 7 namespaces:
common, launch, editor, timeline, settings, dialogs, shortcuts.
All 403 translation keys covered. Passes npm run i18n:check.