← Back to home
Changelog
All notable changes to CUTFORGE are documented in this file.
[0.9.41] - 2026-08-27
Changed
- Ops error view - `/ops` shows export/tracking failures by error code plus the last 50 rows (`errorRaw`, project, output path).
- Export validation - clip effects `cool`, `bw`, `vintage`, `tealorange`, and `cinematic` are accepted (they already render in FFmpeg). The previous allowlist only listed `blur` / `sharpen` / `vignette` / `warm`, so a N&B clip failed export with `unknown effect 'bw'`.
- Paddle refunds - `adjustment.created` / `adjustment.updated` with action `refund` or `chargeback` (status `approved` / `approved_pending`) revoke the matching PRO entitlement. Subscribe those events on the Paddle notification destination.
- Private updater gate - header `X-Cutforge-Private-Updater-Gate` only. Query `?k=` is rejected. The token is injected into the Tauri updater plugin in Rust and is no longer returned to the webview. TEST apps older than 0.9.39 that still put the gate in the URL must reinstall.
- Analytics privacy - `site_events.ip_address` stores an HMAC (not the raw IP); user-agent is truncated. Weekly cron deletes events older than 90 days and strips old Paddle webhook JSON payloads.
- Concat demuxer - concat list paths strip CR/LF/control chars.
[0.9.40] - 2026-08-26
Added
- SAM 2 PRO object tracking - `TrackMode::Sam2` propagates a SAM 2.1 tiny memory bank instead of re-detecting each frame, so a clicked subject keeps its identity through motion and occlusion. Ships as a separate on-demand `tracking-sam2` pack 1.0.0 (5 ONNX graphs, ~162 MiB) served by `/api/download-sam2-pack` -> signed R2. Gated on an active PRO licence and a discrete GPU: DirectML runs ~140 ms/frame where CPU needs ~1.5 s. A mid-clip anchor propagates both forwards and backwards from the click; analysis is rejected when the object is lost on more than 30 % of samples. Mask decoder object logits pass through a sigmoid so `track.json` keyframe scores stay within 0..1.
- SAM 2 pack tooling - `scripts/build-sam2-pack.ps1` builds the pack and its per-file manifest reproducibly; `scripts/r2-private-assets.mjs` gains `upload-sam2` and the `sam2` latest-only purge family; `site/pages/api/download-sam2-pack.js` proxies the private channel.
- Dev-only licence tier switch - debug builds expose a one-click FREE/PRO toggle (`dev_set_license_tier`), compiled out of release builds by `debug_assertions`. A new test asserts parity between `invoke_handler` and the `cutforge-main.toml` allowlist, so a command can no longer be registered without being allowlisted.
Removed
- YOLO26 object tracking - removed `object-yolo26` / `TrackMode::ObjectYolo26`, UI button, pack probe (`supportsYolo26`), ONNX e2e decode, and fetch script. Accuracy never justified the 9.5 MiB. Legacy projects with `trackMode: "object-yolo26"` silently degrade to YOLOv8n `object`. YOLOv8 Track, saliency, and face modes unchanged.
Changed
- Tracking pack 1.7.0 - rebuilt without `yolo26n.onnx`: 75 MiB instead of 84. `minAppVersion` raised to 0.9.40 so a 0.9.39 app cannot pull a pack that no longer carries the detector it knows.
[0.9.39] - 2026-08-25
Added
- YOLO26 object tracking - Montage YOLO26 button runs `--track-mode object-yolo26` (Ultralytics YOLO26n ONNX, NMS-free `(300, 6)` decode) alongside YOLOv8 Track. Tracking pack 1.6.0 (SCRFD + BiSeNet + YOLOv8n + YOLO26n).
- Saliency auto subject - Montage Auto subject seeds a bbox from the interesting region (difference-of-Gaussians, no extra model) then tracks it like Super Track. Outdated tracking packs get an install prompt.
Fixed
- YOLO26 / object tracking RGB pipe - FFmpeg 7 removed `-vsync`; the tracker now uses `-fps_mode passthrough` so analyze no longer exits with "No frames sampled". Tracking pack 1.6.0 republished (same version, new zip SHA).
- Private updater gate - TEST builds send `X-Cutforge-Private-Updater-Gate` instead of putting the token in the manifest URL `?k=`. Older installed TEST apps still work (server accepts both).
- Preview playhead ticks - SequencePreview no longer re-renders on every rAF; React updates at most every 80 ms while playing.
- Auto subject pack gate - do not spawn `--track-mode saliency` on a worker that does not advertise it; skip a stale `target/release` tracker and try `debug`; disable the Montage button until the resolved worker is capable.
[0.9.38] - 2026-08-19
Fixed
- Site analytics - `POST /api/event` uses the same rate limit as `/api/events`.
- Log rotation - old `session-*.log` and `cutforge-archive-*.log` files are pruned.
- Install attribution - a stale `cutforge-install.json` in Downloads is archived into app data instead of being deleted.
- Export cancel - FFmpeg trees are assigned to a Windows Job Object (`KILL_ON_JOB_CLOSE`) instead of `taskkill /PID`.
Changed
- Repo hygiene - unused `OldAssets/` watermark removed from git; Edge WebDriver lives under `e2e/`.
[0.9.37] - 2026-08-19
Changed
- User manual (EN/FR/RU) - reverse preview is documented (REV badge); tracking pack 1.5.0 / ~75 MB; 30 xfade transitions. Speech-to-text and face _analyze_ still require a non-reversed clip.
- Preview lookups - text, overlay, and audio-mix queries at the playhead use the sorted timeline index instead of a full linear scan.
Fixed
- Changelog site build - `[Unreleased]` body is stripped from cutforge.app HTML, not just the heading.
- Stale face-mask export - mask cache keys include source mtime+size, so replacing a file at the same path cannot reuse the previous mask.
- Stale media proxy - preview proxy keys include mtime+size; generation writes `.part` then rename; ffprobe failure no longer pretends 1280×720.
- Crossfade concat hang - concat FFmpeg runs with `-loglevel error` and drained pipes so Windows cannot stall on a full 64 KB pipe.
- Unbounded disk caches - proxies, thumbnails, and export face-masks prune oldest files (LRU).
- STT cancel - Whisper children are killed via the process handle only (no `taskkill /PID`).
- Project backup - a failed `.bak` write is logged instead of being ignored.
- Export progress mutex poison - a panicked progress thread no longer crashes the next lock.
- Missing watermark input - compositor returns an error instead of panicking.
[0.9.36] - 2026-08-13
Fixed
- Preview uses the dense face track - the overlay loads `trackCachePath` (IPC, path-sandboxed) instead of the slim 120-keyframe IPC copy, so blur/outline follow the same interpolation as export.
- Skin / all-faces export uses the tracker mask - Advanced skin and "blur all faces" spawn `cutforge-tracker --mask-out` (BiSeNet / N ellipses) instead of baking one union ellipse. `multi_face_samples` are persisted on `track.json`.
- WhisperX cache is pinned - after a successful run, HF weight files are hashed into `whisperx-{id}.json`; a tampered blob no longer counts as installed.
- Reverse preview seek-drives source time - HTML cannot play backward; the playhead still advances and the video is seeked to the export reverse mapping. Badge `REV` on the preview.
- More transition previews - fade to black/white/gray, circle, zoom, pixelize, slices, blur, squeeze, and smooth wipes have CSS approximations.
- Teal & orange preview is closer to the FFmpeg `colorbalance` look (sepia + hue + contrast).
[0.9.35] - 2026-08-13
Fixed
- Export errors keep their codes - timeout / empty timeline / missing FFmpeg / HEVC now surface as `EXPORT_*` instead of collapsing to `ffmpeg_failed`.
- Tauri v2 object rejections expose `code` - `ipcErrorCode` / `parseIpcError` read `{code, message}` so tracking cancel is classified correctly.
- Smart-reframe never letterboxes on sendcmd failure - falls back to a static 9:16 crop from the first keyframe (or centered cover).
- Face-mask cache lock is RAII - a panic during mask encode no longer spins forever on that clip.
- STT integrity sees swapped binaries - pack fingerprint includes each listed file's mtime+size; WhisperX "installed" is true only after a real cache marker.
- Mask encode has a 600 s timeout - a stuck FFmpeg flush no longer waits for the 2 h watchdog.
- `.ass` / `.fc.txt` stay in temp - sidecar files are no longer left next to the user's export.
- Segment cache probes off the global lock - ffprobe no longer serializes parallel Rayon workers.
- Export/probe require the media extension allowlist - same rules as `asset:` registration.
- Site hardening - checkout origin ignores spoofed Host in production; rate-limit uses Vercel's IP header; Redis/KV down fails closed in prod; updater manifest is `private, no-store`; HSTS; events / pricing / Windows download are rate-limited; license POST body is capped.
- STT one job at a time - a second transcribe is refused; viral caption `y` is in the same undo as the captions.
- Dual-versus bottom uses source time - pause at clip end uses `isMediaPastClipEnd`.
- Mark beats is one undo - all beat markers land in a single history checkpoint.
- Autosave skips stale backend writes and sizes with `TextEncoder`; playhead scrub triggers autosave.
- Tracker association is tighter - IoU ≥ 0.3 or nearby center; click must hit an expanded bbox.
- Export progress is session-scoped - a second export no longer eats the first job's %.
- Project name / FPS / aspect are undoable.
- Seek fallback timers are cleared - no leaked 2 s `setTimeout` per seek.
- Undo re-normalizes snapshots.
- `foo..bar.mp4` is a valid media name - only `..` path segments are rejected.
- Compositor output prefixes `./` when the file starts with `-`.
- Watermark PNG is a reused temp file (no `NamedTempFile::keep()` leak).
- smartblur probe times out after 8 s.
- Tracking on reversed clips uses the export source-time mapping.
- Wipe/slide transitions preview as a translate.
Changed
- Face-filter copy is honest - Advanced skin is a softer ellipse blur; "all faces" is one union box.
[0.9.34] - 2026-08-13
Changed
- FFmpeg auto-install uses the R2 mirror - the pinned GPL zip is no longer fetched from a dated BtbN GitHub URL (those releases are pruned and returned HTTP 404). Runtime URL is `cutforge.app/api/download-ffmpeg?pack=1.0.0` (private gate -> signed R2). Bytes are BtbN `autobuild-2026-08-12-13-15` / `ffmpeg-N-126086-ge5ecfe8970-win64-gpl.zip`, SHA-256 still verified before extract.
Fixed
- Silence trim uses source time - leading/trailing silence is matched against `trimInMs` (not timeline `startMs`) and `trimInMs` is advanced with the cut, so clips that do not start at 0 no longer skip or drift.
- Tracking no longer holds undo gesture - face/subject analyze no longer wraps the IPC in `beginGesture`, so other edits during a long track are not swallowed into one undo.
- Ops weekly report requires `CRON_SECRET` - `x-vercel-cron: 1` alone no longer sends the report. New `GET /api/ops/weekly-report` (Vercel cron) and dashboard both require the Bearer secret.
- STT can be cancelled and times out - Whisper/WhisperX children are killed on cancel (`taskkill /T`) or after `clamp(120s, duration×3, 1800s)`. Overlay has an Annuler button.
- Face-filter export can use the dense track - analyze still slims IPC keyframes for the project, but writes the full `track.json` to disk (`trackCachePath`) so mask / smart-reframe interpolation is no longer stuck on 120 boxes.
- Save no longer resets the session - Ctrl+S / Save keeps the playhead, undo history, selection, playback state, and derived media (proxy, thumbnails, waveforms). Previously `hydrateProject` after save slimmed the project like an undo snapshot and rewound to 0.
- Legacy exports no longer go silent - videos with unknown `has_audio` (`None` on older projects) map the source audio again. Clips known to have no audio track still get a silence stream so concat does not hang.
- Export cancel / timeout cleanup - a failed mask-prep no longer leaves a watchdog that can cancel a later export; FFmpeg PIDs are unregistered on timeout and the child is killed on cancel; stale PIDs are cleared at the start of each export.
[0.9.33] - 2026-08-07
Added
- Machine specs dialog - new "Specs" entry under the Help menu scans the system (CPU brand, physical cores/threads, total RAM, GPU vendor + model + VRAM via DXGI) and shows them in a dialog; integrated GPUs (Intel UHD / AMD Radeon Graphics) are hidden when a discrete GPU is present; data stays local (no telemetry).
- Face tracking UI reorder - classic face tracking (pick a face in the preview, then analyze) now comes first in the Face filter panel; "SMART DETECT" renamed to "Smart Detect" and moved below it as the automatic main-face choice.
- Info hints - a small "ⓘ" glyph on the face-tracking and subject-tracking controls shows a one-line tooltip (hover / keyboard focus) explaining what each control does; available in EN/FR/RU.
- Transitions library - 14 new xfade transitions (fade to black/white, dissolve, wipe up/down, slide up/down, smooth left/right, circle open/close, pixelize, radial, zoom in) on top of the existing fade/wipe/slide; preview treats dissolve like fade. Later expanded with 10 more: fade to grayscale, smooth up/down, circle/rectangle crop, horizontal/vertical slices, horizontal blur, vertical/horizontal squeeze (30 total).
- Color presets (LUT-like) - five new native color looks (Cool, Black & white, Vintage, Teal & orange, Cinematic) built from FFmpeg filters (no external assets) with CSS preview equivalents; shared TS/Rust contract tests.
- Intro/outro templates - Timeline menu gains "Add intro (title)" and "Add outro (subscribe)" which insert editable text clips at the start and end of the timeline.
- Export never-hang guard - a per-process render timeout (default 600 s, `CUTFORGE_SEGMENT_TIMEOUT_SEC` in debug) now kills a stuck FFmpeg segment and fails the export with a clear message instead of hanging forever; a segment stuck at 99 % surfaces "Finalizing encoding..." in the progress dialog, and the concat phase already shows "Final assembly...".
- Export fix: audio-less videos - segments from video assets without any audio track (e.g. wildlife clips) now fall back to a silence track during render, so the concat `acrossfade` always finds an audio stream on every segment (previously such exports hung until the segment timeout). Export error messages from plain Tauri v2 object rejections no longer display as "[object Object]".
- Info hints everywhere - the "ⓘ" glyph + one-line tooltip now covers the main editing panels (text, audio, image/color, frame fit, dual-versus, effects, keyframes, proxy, silence trim, speaker zoom, SRT, STT, stickers, one-click viral subtitles, transcript edit, montage transform, inspector, media library, global silence cut, export settings) in EN/FR/RU, with plain-language explanations (no technical terms).
- Music tools (#10 roadmap) - local BPM detection (`analyze_bpm`, autocorrelation on 8 kHz PCM, no dependency) shown in the media library with a "Tempo" button; "Mark beats" places a beat marker on every beat of the analyzed track (snap-to-beat already works); automatic music fade-in/out (0.5 s) on audio tracks at export, default on, toggleable in export settings. Fades apply on both the composite and the segmented export paths. BPM is now detected automatically at import for music/audio assets (chutier shows it right away). Sub-frame segments (< 1 video frame) are dropped before export - they previously hung FFmpeg with `-shortest`.
[0.9.32] - 2026-08-06
Added
- Super Track button - a third button in Object/Animal tracking runs the template-rectangle engine (ZNCC match) on the clicked subject, with the tracked box outline shown automatically after a successful run and per-sample honest scores (real correlation, not model confidence).
- Tracked box outline auto-enable - after a successful subject tracking, "Show the tracked subject outline" turns on by itself; "Start over" turns it off.
Changed
- Lighting-robust template matching (ZNCC) - subject tracker scores matches with zero-mean normalized cross-correlation instead of plain pixel difference; shadows/glare/brightness drift no longer drop the score. Best of two templates (live + anchor) per frame, EMA 70/30 refresh.
- Fast Super Track analysis - matching runs on a downscaled frame with an integral-image mean (O(1) per position) and a narrower adaptive search: a 1080p clip analyzes in roughly real time (~150× faster than the initial implementation).
- Robust Super Track on moving subjects - tighter click template (mostly the subject), living template refreshes on every accepted match, continuity grace accepts dips down to 0.45: a 44 s geese clip now tracks end-to-end (previously lost after ~2 s).
- Multi-core Super Track - per-frame template search (2 templates × 3 scales) runs in parallel on `available CPUs − 1` workers (capped 6, `CUTFORGE_TRACK_THREADS` override): 44 s 1080p analyzes in ~8 s (was ~15 s), one core stays free.
- Kill tracker child on timeout/cancel - cancelling or timing out a tracking run kills the background process so it cannot keep writing phantom progress logs.
Fixed
- "[object Object]" error in subject tracking UI - `ipcErrorMessage` now extracts the message from raw Tauri v2 object rejections instead of stringifying to `[object Object]`.
- Unreadable tracking error messages - tracker stderr progress lines (`cutforge-progress:` / JSON) are stripped from exit messages; "Subject lost" errors are now a single clean line.
[0.9.31] - 2026-08-02
Added
- One-click viral subtitles - new "1-click" menu (header right) with a one-click action that generates STT captions with the viral karaoke preset, positions them automatically (bottom by default, top when the subject/face is in the lower half of the frame), and opens the 9:16 export dialog pre-filled with the Shorts preset. No manual settings exposed.
Fixed
- STT integrity check latency - the installed STT pack (multi-GB WhisperX runtime) is no longer fully re-hashed on every status check; the integrity scan is cached and re-run only when the pack fingerprint (manifest mtime/size + file count) changes. First-click "Preparing STT module..." is now near-instant.
- STT progress messaging - the overlay now shows "Preparing STT module..." from the click (was stuck on "Verifying STT module..."), and the transcription phases (loading model -> transcribing -> aligning -> done) update the label and progress bar as before.
[0.9.30] - 2026-08-02
Security
- Revocation cache hardening - `license-revocation-cache.json` timestamps in the future (tampered clock/cache) are rejected at read, closing a local bypass of remote license revocation.
- Pack install integrity - debug/e2e IPC installs of STT/tracking packs now verify the pinned release SHA-256 before extraction (was manifest-only); release builds always verify.
- Executable override gating - `CUTFORGE_TRACKER_EXE`, `CUTFORGE_WHISPERX_PYTHON` and the runtime Sentry DSN are read in debug/e2e builds only; release builds use the pinned/bundled binaries and compile-time DSN.
- Media scope allowlist - the webview `asset:` scope now accepts only media extensions (mp4/mov/mkv/webm/mp3/wav/aac/png/jpg/jpeg/gif/webp/bmp/svg/srt/json), enforced on both backend and frontend - arbitrary files can no longer be registered.
- Zip extraction hardening - NTFS alternate data stream entries (`name:stream`) are rejected by the FFmpeg/STT/tracking zip extractors (in addition to zip-slip).
- Log injection guard - `write_log` IPC rejects `\n`/`\r` in messages, preventing forged log lines from a compromised webview.
- Tracker argument allowlist - face-tracking analyze validates `filterKind` (blur/warm) and `trackMode` (face/object) before spawning the tracker.
Fixed
- Smart-reframe export race - per-segment sendcmd cleanup removed; cleanup now runs once after all parallel segments finish, fixing intermittent export failures/corrupt crops on multi-segment smart-reframe exports.
- Export cancel window - `reset_export_cancel()` runs before `begin_export_session()` so a cancel arriving between the two is no longer lost.
- ASS subtitle escaping - `\r` escaped to `\N` and text capped at 10 000 chars; subtitle file path escapes `, [ ] ;` for the ffmpeg `subtitles=` filter (single-pass exports with commas/brackets in file names).
- Output paths starting with `-` - ffmpeg output args are prefixed (`./`) so export file names like `-output.mp4` no longer parse as options.
- SRT import clamping - timestamps clamped to [0, 24 h] with finite checks (no more negative wraps from hostile SRT hours).
- Project NaN validation - asset/clip durations validated with `Number.isFinite` before normalizing.
- Autosave ordering - writes are serialized with a generation token (latest-wins) and a flush runs on window close, closing the ~1.5 s loss window.
- Caption cleanup - deleting a media clip removes its linked transcript captions (`sourceClipId`); pasting a clip validates the source asset still exists.
- Reversed-clip transcript ripple - delete/trim source deltas use the direction-aware reverse-source-time mapping.
- Undo phantom entries - gesture fingerprints ignore `updatedAt`, so no-op gestures no longer push undo entries.
- Cache fingerprints - proxy/thumbnail cache keys use SHA-256 (was 64-bit DefaultHasher) and segment cache fingerprints use nanosecond mtime.
- Download size cap - FFmpeg/pack downloads are aborted beyond 500 MB (content-length or streamed).
- Tracking cache growth - analyze cache pruned to the 32 most recent files.
- Install races - STT/tracking pack installs are serialized with a cross-process lock file.
- Path redaction - export error scrubber now redacts Windows paths containing spaces.
- Pack manifest auto-repair - gated to debug/e2e builds (release never re-blesses replaced worker binaries).
[0.9.29] - 2026-08-02
Security
- Rate limiting - OPS dashboard endpoint now rate-limited (10 req/min per IP, `Retry-After` header); in-memory rate limiter capped at 10 000 buckets with oldest-entry eviction to prevent memory exhaustion under IP spoofing attacks.
- Log sanitization - private updater gate token is masked (`[REDACTED]`) in all Rust log output via `register_sensitive_token()` / `sanitize_message()` in `logging.rs`.
- Media scope defense-in-depth - frontend `isSafeMediaPath()` filters traversal (`..`) and forbidden characters (`<>"|?*`) before invoking `register_media_paths` IPC (Rust backend already enforces canonicalization).
- Project JSON size guard - byte-limit check uses `TextEncoder` (UTF-8 bytes) instead of `raw.length` (UTF-16 chars), preventing undercount of multibyte content.
Changed
- Export error codes - `AppError` extended with `ExportEmpty`, `ExportFfmpegMissing`, `ExportTimeout`, `ExportHevcUnsupported`, `ExportCancelled`, `ExportPipelineFailed`, `ExportConcatFailed`, `ExportNoVideoStream` (stable IPC codes `EXPORT_*`); pipeline uses typed errors for timeline-empty/FFmpeg-missing/timeout paths; concat uses `ExportConcatFailed` with FFmpeg error detail.
- EXPORT_PIDS mutex - replaced `std::sync::Mutex` with `parking_lot::Mutex` for lock-free contention on PID tracking.
- Clippy allow scope - replaced global `#![allow(clippy::too_many_arguments)]` in `lib.rs` with 15 targeted `#[allow]` attributes on specific compositor/export/render functions.
- STT module split - `stt_parse.rs` extracted from `stt_transcribe.rs` (whisper/whisperx JSON parsing + `normalize_transcript_segments`, 345 lines); `stt_transcribe.rs` reduced from 871 -> 544 lines.
- Single-pass export - warning emitted in Rust logs when single-pass composite is used on timelines longer than 30 seconds (segmented pipeline is ~2.3× faster); documented in `docs/EXPORT_PERF.md`.
- Rayon thread pool policy - documented in `docs/EXPORT_PERF.md`: Rayon global pool is reserved for the export pipeline; do not use `par_iter` or `rayon::spawn` elsewhere in the application.
- WhisperX standalone roadmap - documented in `docs/STT-PACK.md` §7: plan to package WhisperX as a standalone `.exe` (PyInstaller/Nuitka) in STT pack ≥ 3.0.0, eliminating the Python runtime dependency.
- Tracker logging future - noted in `tracker_process.rs` doc comment: migrate `cutforge-progress: N` text parsing to structured JSON-lines (`{"type":"progress","percent":50}`) for richer metadata.
- Tracker progress structured - `cutforge-tracker` now emits JSON-lines on stdout (`{"type":"progress","percent":N}`) in parallel with legacy stderr `cutforge-progress:` lines; host-side `parse_tracker_json_progress()` parses both formats; structured protocol enables future `done`/`error` message types.
- Tracker timeout - `wait_for_export_tracker_child` and `wait_for_ui_tracker_child` enforce a 600-second timeout (was unbounded); blocked ONNX inference or hung tracker processes are killed and reported.
- Compositor audio chain - `build_audio_filter_chain()` extracted from `build_composite_ffmpeg_args` (34 lines -> dedicated function), reducing complexity in `compositor/render.rs`.
Fixed
- OPS dashboard - authorized requests now pass through rate limiter (10/min) before data fetch; cron-triggered weekly report is not rate-limited.
- Rate limiter bucket leak - `MAX_BUCKETS = 10_000` cap prevents unbounded `Map` growth when attackers spoof `X-Forwarded-For` headers.
- `resolve_whisperx_command` tests - added unit tests for sidecar-present and sidecar-absent cases.
- Public site - pricing/changelog segmented toggles readable; Fonctionnalités nav links to `/#why` with sticky offset.
[0.9.15] - 2026-07-09
Fixed
- Trim undo/redo - keep `trimClip` delta ops on the opposite stack (was converting to slim checkpoints and dropping waveforms / live fields); restore face-filter and linked captions with the trim patch.
- Start-handle trim at 0s - dragging the left handle left when the clip is already at timeline 0 no longer expands/pushes the clip to the right (clamp order: source head before timeline edge).
Changed
- `src/lib` modularization (phases 2-8) - `ui-shell/`, `telemetry/`, `export/`, `media/`, `face-filter/`, `preview/`, `project-io/`, `timeline/` (hub + leaves) with shims at old `@/lib/*` paths (ADR 010).
- Domain timing/mask - pure implementations in `src/lib/domain/` (`reverse-source-time`, dual-versus geometry, mask geometry); face-filter mask re-exports domain.
- Undo history - dynamic depth by snapshot size; v2 typed ops (`checkpoint` + `trimClip`); trim drag and playhead trim push compact `trimClip` ops.
- Export compositor - full-timeline audio, face-mask inputs, HW helpers, and video/sticker layer chains extracted from `render.rs` (`audio.rs`, `mask_inputs.rs`, `render_helpers.rs`, `video_layers.rs`).
- Shared types codegen - production TS re-exports generated `FaceFilter` (full object), `FaceFilterPoint` / `FaceFilterKeyframe`, and `TextClipWord`; Rust field-name parity tests (structs still hand-written).
- Domain shims - removed flat `@/lib/reverse-source-time` shim (imports use `@/lib/domain/reverse-source-time`).
[0.9.14] - 2026-07-09
Fixed
- FFmpeg auto-install loop - invalidate status cache after managed install / path changes so a successful download is recognized in the same session (was stuck on Configuration FFmpeg after download).
Changed
- R2 private channel - latest only - after each upload, purge older objects so only one app installer (plus `latest.json` / checksums) and one tracking/STT pack version remain (`purge-stale`).
[0.9.13] - 2026-07-09
Fixed
- FFmpeg auto-install - bump pinned BtbN win64-gpl zip (`autobuild-2026-07-09-14-21`); previous June pin returned HTTP 404 after BtbN pruned the release.
Changed
- Private channel downloads (R2-only) - installer TEST, tracking pack, STT pack, and updater `latest.json`: gate on `cutforge.app`, 307 redirect to Cloudflare R2 only (no GitHub `releases-private` stream/fallback). Publish scripts upload directly to R2 (`CUTFORGE_RELEASES_CHANNEL=r2`). See `docs/R2-PRIVATE-ASSETS.md`.
- Pack download pins (CI) - shared URL policy + live gate/R2 checks for tracking and STT (same resilience pattern as FFmpeg pin); FFmpeg Rust↔sidecar sync test; publish script defaults aligned to embedded pack versions; docs/Rules aligned to R2-only private channel.
- Gated pack APIs - `Cache-Control: no-store` on tracking/STT download redirects so CDN cannot cache a 307 without the gate.
[0.9.12] - 2026-07-09
Changed
- FFmpeg PATH (docs + regression tests) - release PATH gate documented for bundled builds; contract test asserts debug-only PATH probing; Rust regression `path_fallback_disabled_in_release_builds`.
Fixed
- Undo history - regression tests lock the 100-entry cap and heavy-project (STT words + face-filter keyframes) serialization.
- Export / tracking cancel - additional Rust tests for mid-job `EXPORT_CANCELLED` / `FACE_TRACKING_CANCELLED` and isolated cancel flags; export hook test asserts `exportJob` cleared after cancel.
- Media probe edge cases - regression tests for zero dimensions, corrupt stream JSON fields, and scrubbed invalid-JSON errors (no panic on import).
- Rotated phone media (displaymatrix) - `probe_media` and tracker `probe_dimensions` store display-oriented width/height from ffprobe rotation metadata; preview face pick and tracker RGB pipe aligned with WebView / FFmpeg autorotate.
[0.9.11] - 2026-07-09
Changed
- Site machine_id validation - checkout, Paddle fulfillment, and app-event accept only hashed machine id (64-hex) or legacy UUID, aligned with `/api/license` and `/ops`.
- Face tracking vs privacy blur - SMART DETECT / Analyze store track-only keyframes (`amount: 0`) by default so smart reframe no longer implies blur; privacy blur is an explicit opt-in in the Face filter panel (kind/intensity when enabled). Preview skips the blur replica when `amount ≤ 1` and still shows the tracking outline.
Fixed
- Face filter preview playback - when the active video visual is not loaded (`readyState < 2`), the playhead no longer advances on wall-clock time (mask/badge no longer animate while the frame stays frozen).
- Face blur replica sync - blur duplicate video receives its ref before `useFaceBlurMediaSync` attaches, so playback stays aligned with the sharp preview.
- Face blur layout - nested `.preview-media` fills the contain box (`object-fit: fill`); blur replica matches so the oval stays on the face instead of drifting right.
[0.9.10] - 2026-07-09
Added
- Preview↔export parity (Phase 0-1) - inventory and export golden luma fixtures under `fixtures/parity/` (color, opacity, reverse, dual-versus, face-filter), `npm run test:parity:export` in `test:ci`.
- Preview↔export parity (Phase 2) - TS↔Rust contracts for reverse source-time and dual-versus geometry; `merge_segments` keeps keyframe subdivision cuts.
- Preview↔export parity (Phase 3) - soft parity without WebView: CSS color contract locks, reverse preview product-decision tests, dual-versus preview uses shared slot geometry helpers, `npm run test:parity:soft` (FFmpeg filter-chain luma vs Phase 1 color golden).
[0.9.5] - 2026-07-08
Fixed
- WebM / VP9 import - waveform peak extraction no longer panics on `i16::MIN` PCM; `probe_media` catches waveform failures without crashing the app.
- Face blur preview - ellipse mask from tracked bbox (not tiny landmark polygon); clipped duplicate video blur in WebView2; playback sync via `useFaceBlurMediaSync`.
- Face tracking `--all-faces` - dev tracker binary probe + friendly error when an outdated pack lacks the flag; falls back to installed pack when needed.
- Preview playback - pause guards (`shouldResumePreviewPlayback`) and overlay video sync reduce double-audio / stale autoplay after pause.
- STT trim - caption `text` / `words` stay aligned when trimming linked text clips to the playhead.
Added
- Tests - face blur preview media, dual versus / frame fit / smart reframe UI, `useFaceBlurMediaSync`, `project-meta`, playback pause; extended caption-sync and video-seek coverage.
[0.9.4] - 2026-07-07
Fixed
- STT pack download (private builds) - `stt-pack-index.json` again uses `cutforge.app/api/download-stt-pack` (gate header) instead of a direct `releases-private` GitHub URL that fails without GitHub auth in-app.
Added
- Regression - `scripts/stt-pack-index-url.mjs` + contract/gate tests lock STT proxy URL policy before every `test:ci`.
[0.9.3] - 2026-07-07
Added
- Caption re-sync - STT captions stay aligned after media split/trim and global silence cut (`caption-sync.ts`, `sourceClipId`).
- YouTube chapters - generate timeline markers from subtitle gaps; export `MM:SS` chapter file from the Pro panel.
- Speaker zoom - auto keyframes punch-in on speech segments (waveform VAD + face center when available).
- Multi-face blur - `all-faces` mode blurs every detected face per frame (crowd anonymization).
Docs
- Phase B differentiation items #8, #10, #11, #12 marked complete in product TODO and status docs.
[0.9.2] - 2026-07-07
Added
- Captions word-pop export - ASS `\t` bounce transform on active words (export parity with preview pop).
- Caption word editor - merge adjacent words and split a word (text + timings) in the Text inspector.
- Timeline silence markers - shaded regions on the ruler from waveform peaks; click to seek.
- STT on public builds - `stt-pack-index.json` uses direct GitHub Releases URL so `downloadReady` works without the private gate.
- Dual versus layout - split preview (top/bottom tracks), inspector section, export compositor path.
- Transcript edit - remove spoken ranges from the transcript panel; ripple cuts on linked clips.
- Clip frame fit - contain vs pan for aspect mismatch; preview drag pan; export compositor support.
- New project templates - Shorts 9:16, YouTube 16:9, podcast audio picker on File -> New.
Fixed
- E2E - `new-project.e2e.cjs` dismisses template dialog after the picker assertion (unblocks second test).
- Deps - `crossbeam-epoch` 0.9.20 (RUSTSEC-2026-0204); document `memmap2` transitive ignore in `.cargo/audit.toml`.
Docs
- Phase A short-form items marked complete in product TODO and status docs.
- Doc hub: docs index, decisions, research, refreshed roadmap and V1 checklist.
[0.9.1] - 2026-07-05
UI
- Frameless window (Windows) - custom title bar with minimize, maximize, close, and drag; Tauri ACL permissions for window chrome IPC
- Smart reframe - disabling "Follow face on 9:16 export" restores project preview/export aspect to 16:9 when no other clip uses smart reframe
- STT progress - subtitle generation overlay shows per-phase status (module check, download, install, audio extract, WhisperX load / transcribe / word alignment)
Fixed
- Startup - remove React `StrictMode` in production (avoided double FFmpeg bootstrap on launch)
- i18n - `windowChrome.*` message keys typed in `MessageKey`
- STT (WhisperX) - skip `__pycache__` / `.pyc` in pack integrity checks so Python imports no longer invalidate the installed pack and force a full re-download on every subtitle run; stream WhisperX stderr for live progress; `stt` app log lines per step
Tests / dev
- E2E - `window-chrome.e2e.cjs` (launch, minimize, maximize, drag, close when isolated); `test:e2e:window-chrome` script
- Startup profiling - dev-only `[startup]` marks; Rust `check_ffmpeg_tools` wall-clock regression test (~337 ms budget)
[0.9.0] - 2026-07-04
Security (audit v6)
- CSP / theme - anti-flash script moved to `public/theme-init.js` (no inline script in `index.html`); `theme.css` design tokens loaded before `App.css`
- FFmpeg PATH - release builds no longer fall back to system `PATH` unless `CUTFORGE_ALLOW_PATH_FFMPEG=1`
- License machine id - SHA-256 hashed id (`machine_id.rs`) with legacy UUID compatibility; revocation check uses `X-Cutforge-Machine-Id` header (site coordinated)
- Activation - refuse license activation when machine uid cannot be resolved
- Private download gate - STT/tracking pack URLs no longer append `?k=` query; header auth only
- Repo hygiene - `target-karaoke-test/` gitignored; smart-reframe e2e writes to OS temp
- IPC / media - path traversal guard comments; `probe_media` errors when duration missing (no silent 5s default); extra extensions in `guess_kind_from_path`
- Progress events - Tauri progress channels suffixed by request session id (`progress_events.rs`, frontend `invokeWithProgressSession`)
- FFmpeg tooling - undated git snapshot builds no longer assumed year 2024
UI
- Appearance - View menu: Dark, Light, and Auto (follow OS `prefers-color-scheme`); choice persisted in `localStorage`
- Light theme - full Slate Canvas palette (soft #F8F9FA canvas, white surfaces, blue accent family shared with dark); timeline lanes, menus, modals, inspector, and media bin tokenized
- Stickers (light theme) - darker SVG fills for sparkle/thumbs-up/camera/music; drop-shadow on preview overlays
- Sticker drag - pointer mapping uses export canvas (1:1 with preview); stickers always composited as overlays (draggable even without V1 video); hit-testing on sticker pixels only (selected sticker on top)
[0.8.84] - 2026-07-03
Export
- Face mask encode progress - `export-progress` events during face-mask FFmpeg prep (`Encoding face mask N of M (P%)`) update overlay bar + phase text
Tracking quality (cutforge-tracker)
- Analyze loop - full-frame `detect_rgb` on every sampled frame; removed adaptive ONNX skip, ROI cascade, and fast letterbox helpers (~12 s vs ~8 s on Oies-multi-1.mp4, still ~5× faster than pre-RGB JPEG path)
Tracking pack Phase 2 (cutforge-tracker, local dev)
- RGB prefetch - background ffmpeg decode thread (`CUTFORGE_RGB_PREFETCH=0` to disable)
- Skin mask - BiSeNet every N frames (`CUTFORGE_SKIN_MASK_EVERY`, default 2) with mask reuse
- ORT EP features - optional `cuda` / `tensorrt` Cargo features + `CUTFORGE_ORT_EP`
- Benchmarks - `npm run benchmark:tracker` on reference clips (face / object / click-box)
Tracking stability (P1)
- Export cancel - no longer cancels unrelated UI face/object analyze (`request_export_cancel` decoupled from face-tracking cancel)
- Progress poll - `get_face_tracking_progress` reports idle unless a UI tracking session is active
- Mask engine - changing engine in Face filter clears persisted filter until re-analyze (draft vs export mismatch)
- Job store - reject overlapping `runFaceTracking` while `faceTrackingJob` is set; progress updates ignore stale clip ids
- Mask cache - deduplicate parallel prep by cache key; publish via `.mp4.part` + rename with per-key generation lock
Tracking stability (P0)
- UI session mutex - reject concurrent `run_face_tracking` invokes; export tracker spawns use per-thread wait + export PID registration (parallel-safe)
- ONNX inference errors - exit `MODEL_ERROR` after 3 consecutive SCRFD/YOLO failures instead of silent empty detections
[0.8.83] - 2026-07-02
Inspector & preview
- Face filter - single Face tracking card (SMART DETECT, pick, analyze); smart reframe 9:16 checkbox only (no duplicate subject pick)
- Montage - Object / animal tracking card + smart reframe 9:16 for non-face subjects (YOLO)
- Preview - smart-reframe zoom applies immediately after tracking ends; detection % badge counter-scaled in 9:16 preview
[0.8.82] - 2026-07-01
Smart reframe & subject tracking
- GPU object tracking (pack 1.4.0) - `--track-mode object` runs YOLOv8n on DirectML; Choose subject in Face filter tracks animals/objects without CPU template matching
- Two-step subject pick - crosshair + dashed box in preview; reposition by clicking, then Start tracking (no auto-start on first click)
- Reset subject - clears smart reframe keyframes, UI pick state, and restores project aspect to 16:9 when no other reframe is active
- Face filter panel layout - SMART DETECT / pick face at top; Smart reframe 9:16 block (enable, pick subject, headroom, static building) below; blur/warm effect controls last
- Progress overlay - Object tracking title/messages for subject runs vs Face tracking for face filter
- Preview fix - clear stale smart-reframe zoom transform after reset so the subject pick box stays the correct size
Tracking pack 1.4.0
- Embeds `yolov8n.onnx` (~12 MB) alongside SCRFD; index on private channel (`tracking-pack-index.json`)
- Tracker modes: `face`, `object`, `click-box`, `static-subject`
Smart reframe (Phase B1 - carried from 0.8.80+)
- Decoupled subject track from face blur - `smartReframe.keyframes` with legacy `faceFilter` fallback
- Fix first-second crop offset - anchor keyframe at clip-local time, not forced `timeMs: 0`
- Export cleanup - delete `sr_*.sendcmd` scratch after each segment
- Preview export parity - sequence preview zooms to tracked 9:16 crop when reframe is active
[0.8.81] - 2026-07-01
Face filter / tracking
- SMART DETECT - auto-pick the primary face (size + center) without a preview click; manual pick remains available in the Face filter panel
- Tracking pack 1.3.3 - `cutforge-tracker` supports `--click auto`; embedded index points to the new pack on the private channel
- Pack manifest repair - if only `cutforge-tracker.exe` or `whisper-cli.exe` was replaced locally, the app refreshes the worker SHA256 in `manifest.json` instead of re-downloading the full pack on every analyze/transcribe
- Dev helpers - `npm run patch:tracking-tracker`, `install:tracking-pack:local`, `install:stt-pack:local` (both `com.cutforge.studio` and `com.cutforge.studio.private` app data folders)
- Clearer outdated-pack error when SMART DETECT runs against an old tracker (no more misleading "Video file not found" for clap `--click auto`)
[0.8.80] - 2026-07-01
Smart Reframe
- Smart Reframe 9:16 - face-centered vertical export (1080×1920): blue crop preview, single continuous MP4 with per-frame `sendcmd` crop (validated); requires face analysis + Montage toggle; export FPS follows resolved timeline rate
Tracker / face filter
- Face tracking UI - progress overlay stays visible through post-analyze steps (parse, cache, apply); tracker stderr capped at 90% so 91-100% cover Rust finalize; `faceFilter` returned over IPC (no asset fetch on the UI thread); mask warm-up still skipped at analyze time
- Fix multi-minute freeze after analyze - the tracker's ~10 progress events/s were flooding Tauri's global event system (`emit`/`listen`), queueing ahead of the command response so the `run_face_tracking` `invoke` only resolved minutes after the Rust work finished (observed ~15 s of work -> ~240 s until the UI regained control). Progress now streams over a per-invocation Tauri channel instead, and mid-analysis emits are throttled (~1 per 300 ms, deduped; boundary/finalize percents always sent), so the response returns promptly.
Inspector
- Clip FPS - inspector shows probed source frame rate for the selected video clip (below Resolution)
[0.8.74] - 2026-06-30
Captions
- Animated captions polish - ASS export parity for caption box fill, drop shadow, and word-pop scale (`\fscx`/`\fscy` on active syllable); inspector word-by-word editor (text + start/end ms); `updateTextClip` accepts `words` patch with auto-sync on text/duration edits; split/trim partition per-word timings; preview karaoke/word-pop syncs to decoded audio/video clock with `sourceClipId`; STT audio extract uses sample-accurate FFmpeg trim (`-ss` after `-i`) so Whisper word timings match preview playback.
STT
- WhisperX sidecar (pack v2) - optional STT pack engine `whisperx`: embedded Python + torch + WhisperX with forced word alignment (wav2vec2) for karaoke-grade `words[]` timings; Rust dispatch in `transcribe_clip_sync`, manifest `engine`, build via `npm run build:stt-pack:whisperx`; dev override `CUTFORGE_WHISPERX_PYTHON`.
- STT pack 2.0.1 - fixes portable Python runtime (`pyvenv.cfg` / embed layout); published on `releases-private` (`stt-2.0.1`, WhisperX, ~579 MiB); replaces broken 2.0.0.
Hygiene
- Remove unused STT pack placeholder / `whisper_model_path`; `install_stt_pack_from_file` returns `()`; drop `normalizeClipWords` re-export; STT pack gate live test reads `packVersion` from embedded index (fixes 502 after latest-only purge).
[0.8.73] - 2026-06-29
Tracker / face filter
- SCRFD - letterbox padding uses normalized `0.0` (neutral gray) instead of raw `127.5`
- ONNX - register DirectML execution provider with CPU fallback (`onnx_session`)
- Skin mask - decode source segment via single FFmpeg RGB pipe (no per-frame JPEG spawns)
- Robustness - UltraLight output shape validation; `SparseKeyframe` struct; densify O(n) prepass
- Dead code - remove unused tracker APIs (`has_landmark_outputs`, `from_bbox`, `MaskEngine::as_str`)
Export / backend
- Export progress UI - parallel segment exports track completion by timeline index (not finish order); overlay shows completed count, active segment %, and global bar without duplicate phase %
- Export hardware - AMF/QSV encoder support, NVIDIA multi-GPU index, `get_export_hardware_profile` IPC, hardware summary in export settings
- Segment merge - collapse text-only boundaries on a single video track (one render job instead of per-subtitle segments)
- Filter graph - long graphs use `-filter_complex //?/path` on Windows (FFmpeg 62+)
- Face filter export - fix invalid FFmpeg graph when face blur chains into overlay fades (empty `[vlface]` pad)
- Export profiler - `ExportProfile` on every `export_timeline` (raw/merged segments, boundary breakdown, phase timings, per-segment stats); optional JSON via `CUTFORGE_EXPORT_PROFILE`
- Single-pass export (opt-in) - `CUTFORGE_EXPORT_SINGLE_PASS=1` renders eligible timelines in one full `filter_complex` job (`exportMode: singlePassComposite`); segmented pipeline stays default (faster on DJI workflow benchmark)
- Face mask cache - key v7 includes `export_fps` and export dimensions; warm uses probed source size
- Manifests - strip UTF-8 BOM before JSON parse (tracking + STT packs)
- Downloads - reject non-HTTPS zip URLs; fix RFC1918 `172.16-31` check (no `172.2` over-block)
- Capabilities - remove unused `default` / `desktop-capability` Tauri configs
Preview / frontend
- Toolbar layout - customizable quick and timeline toolbars (minimal / standard / full presets, per-action checkboxes); default production layout is minimal (export + split + zoom); Edit -> Customize toolbar...; persisted in `localStorage`; E2E builds keep full toolbar via `VITE_E2E`; `EditorPage` falls back to menu when toolbar buttons are hidden
- Inspector - drag-reorder section blocks (Montage, Pro, Text, ...); order persisted in localStorage
- STT captions - `caption-clips` pipeline with word-level timings for karaoke / word-pop presets
- Playback - `useSequencePlayback` uses memoized `TimelineIndex` (parity with RAF loop)
- Playback engine - `computePlaybackTick` / handoff helpers use indexed timeline resolution (closes R3-1 residual)
- IPC progress - scoped listeners ignore events after invoke completes
CI / hygiene
- E2E workflow benchmark - `test:e2e:workflow-dji` (DJI + wife, FX max, phased JSON report); offline `build:benchmark-dji-60s` / `export:benchmark-dji-60s`
- Remove accidental `$s/` stub directory from the repo
- Contracts - `mask-geometry-contract` + repo `$` path guard in `test:export-contracts`
[0.8.72] - 2026-06-19
Editor (beta UX)
- Inspector - subsection headers fully clickable (`InspectorSubsection` on Pro, Montage, Text, Sticker, Face filter, Audio, Image, main inspector)
- Inspector cards - Pro and Montage panels use bordered `InspectorCard` groups (keyframes, effects, proxy, SRT, STT, silence; speed, transition, transform)
- Timeline toolbar - compact grouped actions, icon-only undo/redo, abbreviated labels with shortcuts; zoom slider (5%-400%) centered in the toolbar row
- Timeline tracks - + / − on the last overlay, audio, and text track (remove only when empty; at least one track per kind kept)
- Timeline layout - text tracks (T1, T2, ...) displayed above the video stack (subtitles on top, V3->V1 below)
Tests
- Component tests for `InspectorSubsection`, `InspectorCard`, Pro/Montage card layout, compact timeline toolbar
- `inspector-ux-visual` CSS contract + `inspector-visual-contract` test ids
- E2E `inspector-ux.e2e.cjs` + static spec test
[0.8.71] - 2026-06-25
Editor (STT - Phase 1)
- Subtitles - local Whisper pack (optional download): transcribe selected audio/video clip -> text clips on T1
- Inspector (Pro) - "Generate subtitles" button with install/transcribe progress overlays
- Privacy - speech never leaves the machine (whisper.cpp in `%APPDATA%/stt`)
Editor (styled subtitles - Phase 2)
- Subtitle presets - viral styles (box, bold, neon, minimal, pop) with live preview and matching FFmpeg `drawtext` burn-in
- Caption box - semi-opaque background (`box=1`) on export when preset uses `boxColor`
- Inspector (Pro) - pick preset before STT generation; Apply to T1 restyles all subtitles on the text track
Editor (multi-export batch - Phase 3)
- Export settings - "Export for all networks" produces 4 MP4s (YouTube, Shorts, TikTok, Instagram) in one folder
- Progress - batch overlay shows platform index (e.g. 2/4) with combined progress bar
- Resilience - partial batch failure continues remaining presets and reports summary
Editor (podcast assistant - Phase 4)
- Wizard - File -> Podcast assistant: 6-step flow (project template -> import -> silence cut -> ducking -> local STT captions -> batch export)
- Skippable steps - each stage can be skipped; summary before export
- Orchestration - reuses podcast template, global silence cut, subtitle presets, and multi-export batch from Phases 1-3
- Wizard STT - inline errors and unpublished-pack hint on the captions step
Editor (STT pack - private channel)
- STT pack - `stt-1.0.1` on `releases-private` (Whisper 1.9.1 + ggml-small + whisper.cpp runtime DLLs)
- Download proxy - `GET /api/download-stt-pack?pack=1.0.1` (private gate, same as tracking pack)
- Pack download - append private gate `?k=` on cutforge.app STT/tracking proxy URLs (fixes HTTP 401)
- Gate resolution - runtime `PRIVATE_UPDATER_GATE_TOKEN` fallback for dev builds; compile-time gated pack URLs in `build.rs`
- Gate source - read private updater `?k=` from embedded Tauri config (CUTFORGE-TEST installs)
- Gate bundle - ship `private-gate.embed` in CUTFORGE-TEST; wizard blocks subtitle generation on public CUTFORGE with a clear PRIVÉ message
- Catalog - `downloadReady` flag on STT pack catalog
- Errors - block ungated pack downloads with a clear CUTFORGE PRIVÉ message instead of opaque HTTP 401
- Wizard - fix duplicate error toast on subtitle generation failure
- Transcript - clamp whisper segment timestamps to clip duration
- Smoke - `npm run smoke:stt` (wife-front.mp4 fixture)
- Subtitle length - max on-screen duration presets (Short 3 s / Medium 5 s / Long 10 s / Auto) in Pro inspector and podcast wizard; reflow after Whisper
- STT pack - manifest integrity fixes (no self-hash in `files[]`); regression tests for repack and manual manifest edits
Fixes
- STT sync - subtitle cues follow speech timing: Whisper full JSON (`-ojf`) per-token timestamps; reflow splits on word timings instead of character-length ratio
[0.8.70] - 2026-06-19
_Thank you to ṪëṄäḲïṄṾḞẌ for the beta feedback._
Editor (beta UX)
- Timeline - ruler and playhead no longer select timecode text while scrubbing (`user-select: none`)
- Inspector - all numeric sliders (audio, image, text, sticker, montage, chroma, effects, face filter, silence cut) accept typed values beside the range control
- Montage - clip rotation range extended to ±360° (preview + FFmpeg export)
- Keyframes - translated property labels, seek on list/timeline click, enriched guidance hint
FFmpeg
- First launch - setup dialog retries automatic FFmpeg download (BtbN GPL build into `%APPDATA%`) before manual gyan.dev steps; clears broken saved paths before install
- Auto-install - bump pinned BtbN autobuild URL (upstream purged the previous pin -> HTTP 404 on fresh install)
[0.8.69] - 2026-06-19
Security (code audit W1-W5)
- Tracking pack - download proxy requires private gate header; install IPC gated in release builds
- Telemetry - desktop `app-event` posts require gate token (compile-time + site validation)
- Stickers - export path validation rejects traversal outside allowed directories
- Tauri - splash window capability split from main IPC surface
- Ops - timing-safe bearer check for cron dashboard routes
Performance (code audit W6-W9)
- Undo - history stores serialized project snapshots (lower RAM on large timelines)
- Media library - no longer subscribes to live playhead ticks
- Playback - timeline index cache for RAF hot-path clip lookups
- Face filter - mask cache pre-warmed after tracking analyze completes
Editor (code audit W10-W15)
- Preview audio - all dedicated audio tracks (A1, A2, ...) play together in preview
- Waveforms - peaks extracted during media probe on import
- Preview - export frame keeps correct 16/9 aspect ratio when resizing the preview panel
- Menus - keyboard navigation in menu bar and dropdown menus
- Timeline - focusable scroll area; arrow/Home/End scrub; clips selectable from keyboard
- i18n - English `a11y.inspector` label (no French fallback)
[0.8.68] - 2026-06-19
Added
- Media library - user-created bins (chutiers) to organize assets; imports go into the active bin
- Edit menu - customizable keyboard shortcuts (saved per device)
Changed
- Effects - stronger vignette and warm LUT at the same slider value (preview + export)
[0.8.67] - 2026-06-19
Added
- Editor - classic menu bar (File, Edit, Timeline, View, Help) with keyboard hints; quick toolbar keeps Save / Import / Export
- Media import - native Tauri drag-drop on the main window (`dragDropEnabled`) with Explorer file routing to timeline lanes or media library
Fixes
- Playback - preview embedded video audio sync via decoded-time state (no ref read during render); RAF loop resync tolerance for V1 handoff
[0.8.66] - 2026-06-18
Added
- Timeline - NLE track order (V1 bottom, overlays above); OS file drag-drop onto lanes; playhead seek by frame (`←/->`), 1s (`Shift`), 5s (`Ctrl`); timeline gutter wheel 1s (Shift 10s)
[0.8.65] - 2026-06-17
Fixes
- Face analyze - tracking pack 1.3.2 fixes FFmpeg 7+ JPEG frame extraction (`yuvj420p`) for phone/webcam sources
[0.8.64] - 2026-06-17
Fixes
- Inspector - Text and Stickers panels always visible in the right sidebar (restore default picker UX after lazy-mount regression)
[0.8.63] - 2026-06-17
Security
- IPC paths - sticker cache limited to canonical app-data directory; media path traversal rejected; relink paths validated on blocking pool
- Media scope - `register_media_paths` failures reported to Sentry and surfaced in UI (i18n toast)
Editor
- Autosave recovery - startup dialog to restore or ignore recovered project (no silent auto-restore)
- Undo history - lighter snapshots via `projectForHistory()` (exclude waveformPeaks, thumbnailPath, proxy)
- Accessibility - toast dismiss `aria-label`; preview splitter keyboard resize
Fixes
- Error boundary - unexpected crash message uses i18n `error.unexpected`
[0.8.62] - 2026-06-16
Editor
- Global silence cut - remove internal silences across unlocked audio/video tracks with ripple; toolbar action and speech-segment helpers on waveform peaks
- Face filter preview - overlay driven from playhead bus at 60 Hz during playback; backdrop blur skipped while playing for smoother motion
- Preview text layers - position updates at playhead-bus rate instead of per-frame React state
- Face filter score badge - throttled to 10 Hz during playback
- Timeline ruler - adaptive tick density on long projects
- Inspector - lazy-mount panels by selection type
- Trim drag - clip geometry via DOM during drag; store commit on mouseup; text clips use CSS transform like media
- Face mask export - per-clip progress in export overlay (EN/FR/RU)
Fixes
- Face filter / tracking pack - embedded index points to `cutforge.app/api/download-tracking-pack` (proxies private GitHub release); fixes HTTP 404 when downloading models without GitHub auth
- ClipProPanel - removed FREE tier gate (full editor on FREE; PRO = watermark removal only)
- FREE watermark - preview alignment with FFmpeg overlay math
- License API - fail-closed Paddle idempotency, upsert-before-email, subscription revoke; 503 on DB errors
- IPC - sticker and media path handlers on `spawn_blocking`
Site
- Tracking pack API - `GET /api/download-tracking-pack?pack=` streams `tracking-*` assets from `releases-private` via server token
- Windows download - homepage card and `/api/download-windows` show Coming soon (same as macOS); public GitHub releases repo emptied and set private; default channel `releases-private`; public app releases only from v1.0.0+
- Built-in updater - disabled in stock `tauri.conf.json` (private gate unchanged on site)
Architecture
- Codegen - manifest-driven TS/Rust shared types; sync gate in `test:export-contracts`
- Timeline - `timeline-clip-timing` contract JSON; shared `isTimeInsideClip`; Rust cross-ref in `timeline_resolve`
- Contracts - `ProjectSnapshot` asserts `textClips` and `beatMarkers`; notify inject for tests
- Playhead bus - playback drops 60 Hz `displayMs` React state; preview transforms subscribe to bus
Tests & CI
- E2E face-filter export (analyze fixture, MP4 + corner luma guard); `test:e2e:long` in CI; macOS WDIO smoke job
- P1 e2e verify script; updater E2E available path; webhook idempotency env tests
[0.8.61] - 2026-06-14
Sentry
- Handled errors - `captureAppException` normalizes non-Error rejections; wired for export, import, project session, autosave, stickers, and missing media
Editor
- Feature split - timeline, inspector, and export logic moved into dedicated `src/features/editor` modules
Site
- Changelog build - repair corrupted user release notes; normalize em dashes, arrows, and narrow spaces in published HTML bodies
[0.8.60] - 2026-06-14
Face filter (Phase 5)
- Temporal mask smoothing - EMA on skin-engine masks (alpha=0.4) for steadier blur during motion
- Tracking pack 1.3.1 - rebuilt tracker; catalog zipSha256 aligned with GitHub release; minAppVersion 0.8.59
[0.8.59] - 2026-06-14
Face filter - dual engine (MVP)
- Engine selector - `standard` (landmark polygon mask) / `skin` (BiSeNet face parsing) on clip `FaceFilter`
- Skin export - `smartblur` when FFmpeg supports it; `gblur` fallback (~15% higher sigma)
- Re-analyze gate - engine change invalidates cached masks/keyframes until re-analyze; localized UI prompt (EN/FR/RU)
- Tracking pack 1.3.0 - adds `face_parsing_resnet18.onnx`; `minAppVersion` 0.8.59; index SHA updated locally (GitHub publish pending)
Tests
- Dual-engine unit/contract coverage; skin export E2E via `npm run test:face-filter-export-e2e` (FFmpeg + fixtures)
[0.8.58] - 2026-06-13
Face filter (Phase 8)
- Analyze progress - real `%` from `cutforge-tracker` stderr (`cutforge-progress:`) -> `face-tracking-progress` -> overlay
- Cancel polish - cancel during tracking-pack download; localized cancel toast; overlay tests
- Preview pick - `preview-media-pick.ts` maps clicks through clip scale/crop/position (`useFaceFilterPick`)
- Detection model - default `fetch:tracking-model` = SCRFD 2.5G bn kps (bbox + keypoints)
- Manual - SmartScreen / Authenticode note for `cutforge-tracker.exe` (EN/FR/RU)
Tests & CI
- Vitest 80 % branches thresholds on `face-filter.ts` / store actions
- Knip clean; `cargo fmt` / clippy on tracking modules
- E2E tracking install - local zip IPC skips catalog SHA pin (fixture rebuild)
[0.8.57] - 2026-06-13
Security
- Tracking pack local install - SHA-256 pin, path validation, IPC gated to debug/E2E
- External URL allowlist - hardened against backslash/userinfo bypass
- FFmpeg directory selection - native dialog only (no arbitrary IPC paths)
- Production CSP - `base-uri`, `object-src`, `frame-ancestors`, `form-action`; removed `style-src unsafe-inline`
- Telemetry IPC - event allowlist + field sanitization
- Error messages - scrub absolute paths from export and `probe_media` errors
Export
- Face mask encode - stream frames (lower memory), stderr drain, cancel/kill improvements
- Export watchdog - before mask prep; parallel bounded mask generation
- Render cache - serialized prune, atomic writes, skip cache on direct-output single pass
- Cancel PID - race fixes
UI
- Modal accessibility - focus trap, Escape, `aria-labelledby`
- Editor store - narrower subscriptions (fewer re-renders)
License
- Offline revocation grace - reduced to 3 days; clock rollback detection
[0.8.56] - 2026-06-10
Changed
- Contact email - app, site, legal docs, and license fulfillment now use `info@cutforge.app` (Proton custom domain)
[0.8.55] - 2026-06-12
Fixed
- Face filter export - installed builds no longer panic with `CARGO_MANIFEST_DIR: NotPresent`; face-blur curve JSON is baked in at compile time
[0.8.54] - 2026-06-12
Fixed
- Face tracking / export - hide Windows console windows when spawning `cutforge-tracker` and FFmpeg mask encode (`CREATE_NO_WINDOW` via `hidden_command`)
- Face tracking UI - localized progress messages (preparing / analyzing) in EN, FR, and RU
[0.8.53] - 2026-06-12
Added
- Face blur curve contract - shared `face-blur-curve.json` for preview CSS and FFmpeg `gblur` sigma (wider 80-100 % range)
- FREE watermark preview - overlay in sequence preview aligned with export layout
- GitHub releases cleanup - publish scripts purge older `v*` / `tracking-*` tags (latest-only policy)
Changed
- Face filter export - landmark polygon mask at encode (parity with preview); mask cache key v5
- Face filter UI - intensity slider label + hint (EN/FR/RU); preview blur follows export curve
- Face filter export E2E - fixture fallbacks (`wife-front.mp4`) and on-the-fly mask generation
Fixed
- Face filter export - scale before `gblur` to avoid OOM on long clips; mask pipe drain / keyframe-only cache
[0.8.52] - 2026-06-12
Added
- Face tracking module uninstall - Help -> Privacy -> remove the optional tracking pack from `%APPDATA%\com.cutforge.studio\tracking\`
- Preview clip transform - face-filter pick, mask, and score follow clip scale/crop/position in the sequence preview
- Confidence score on preview - detection score badge anchored at the nose landmark (red when below 50%)
- E2E tracking install - local fixture zip (`tracking-pack-e2e.zip`) avoids GitHub download in CI
Changed
- Tracking pack index - embeds `tracking-1.1.0` (landmarks + Kalman + landmark masks from 0.8.52)
- User manual - face filter section updated (cancel, uninstall, preview ~ export)
- Face filter hints - preview copy reflects landmark mask parity with export
[0.8.51] - 2026-06-12
Changed
- FREE export watermark - updated branding asset (`Cutforge.app` logo PNG, bottom-right overlay)
[0.8.50] - 2026-06-12
Changed
- Telemetry consent - one-time opt-in banner shown after the first successful export (not at launch); rating prompt unchanged
- Telemetry decline - banner accept and decline both POST `telemetry_opt_in_changed` (`source: banner`) even when full telemetry is off (`consentOnly` allowlist)
Added
- Ops dashboard - post-export telemetry KPIs: opt-in % (banner accept rate), banner decline %, prompted machine counts (`telemetryBannerPromptedMachines`, `telemetryBannerDeclinedMachines`, `telemetryBannerDeclineRate`)
- Face filter unit tests - `src/lib/face-filter.test.ts` (normalize/merge helpers, `TrackingAnalyzeOutput` fixtures)
Docs
- Analytics - document post-export consent flow and ops KPI definitions in internal analytics docs
[0.8.38] - 2026-06-11
Fixed
- Face filter export - bind source video to FFmpeg input 0 (was using mask input -> black background + wandering ellipse)
- Face filter export - fix empty FFmpeg filter segment before `[vpre]` label
- Face filter analyze - pass app FFmpeg sidecars to tracker; remove unsupported `-hide_banner` in worker
- Face filter preview - pick coordinates in letterboxed media contain box (resize / fullscreen)
- Face filter preview - frosted overlay effect (reliable vs `backdrop-filter` on `
Added
- Face filter panel - inline analyze error message; detection confidence % after tracking
- Face filter export E2E - `npm run test:face-filter-export-e2e` (`ref-face-front.mp4`, luma corner check)
Changed
- Export render cache - bump generation `render=v16-face-filter-source-input`
[0.8.37] - 2026-06-11
Fixed
- Tracking pack download - correct GitHub URL (`/releases/releases/download/...`) for repo `CUTFORGE-VIDEO/releases` (was HTTP 404)
- Face analyze - UI analyze no longer passes `--mask-out` (compatible with pack 1.0.0 during tracking; mask at export only)
- Face filter panel - effect kind/strength editable before first analyze; show pack status hint when module missing
[0.8.36] - 2026-06-11
Added
- Face filters (filtres visage) - inspector panel: pick face in preview, analyze, live bbox overlay, blur/warm sliders
- Face filter export - precomputed mask video (`--mask-out`) + FFmpeg `alphamerge` with `gblur` / `eq` on export (simple + compositor paths)
- Face tracking pack 1.0.1 - republished with mask generation support (`tracking-1.0.1` tag)
Changed
- Tracking pack index - `packVersion` 1.0.1, `minAppVersion` 0.8.36, updated zip SHA256
[0.8.35] - 2026-06-11
Added
- Face tracking pack 1.0.0 published on GitHub Releases (`tracking-1.0.0` tag) - `cutforge-tracking-1.0.0-win64.zip` (~17 MiB)
- Build scripts `npm run build:tracking-pack` and `npm run publish:tracking-pack`
Changed
- Tracking pack index - real download URL and SHA256; `minAppVersion` 0.8.34; manifest lists `DirectML.dll` (ONNX Runtime statically linked in worker)
[0.8.34] - 2026-06-11
Added
- Face tracking pack - installer module (download, SHA256 verify, extract, manifest check) mirroring FFmpeg install; dev toolbar button + progress overlay
- Tracking IPC - `check_tracking_pack`, `ensure_tracking_pack_installed`, `tracking-install-progress` events
Fixed
- Face tracker - decode Ultra-Light RFB-320 bboxes as normalized 0-1 coords (M1 real-video validation)
[0.8.33] - 2026-06-11
Fixed
- Stickers export - accept `sticker-cache` PNG asset paths at export validation (regression from FABLE v4 `validate_export_media_paths`); regenerate from bundled SVG when cache file is missing
[0.8.32] - 2026-06-11
Security
- Clip effects & chroma key - validate chroma hex and effect parameters at the IPC boundary (FABLE v4 audit)
Added
- Cross-lang contracts - clip effects and chroma key FFmpeg filter JSON fixtures verified in TypeScript and Rust
Fixed
- Keyframes - aligned scale bounds across preview, IPC, and export
- Install attribution - atomic write for consumed marker file
- Editor - remove preview splitter listeners on `EditorShell` unmount; export cancel telemetry fallback when session missing
Tests
- CI - clip-effects contract fixtures typecheck; validate test formatting
[0.8.31] - 2026-06-11
Added
- Cross-lang contracts - timeline active clips and clip color / FFmpeg filter JSON fixtures verified in TypeScript and Rust (audit C10)
- Ops dashboard - export timing heatmaps (`cacheHitRateBucket`, `maxSegmentRenderMsBucket`)
- Project backup - `.cutforge.bak` written atomically before overwrite on save
Security
- License IPC - 64 KiB cap on `activate_license` body before JSON parse
- Telemetry - `post_app_telemetry` async (non-blocking reqwest)
- Config I/O - atomic writes for `launch_guard` and `ffmpeg_config`
- Stickers - 2 MiB cap before SVG rasterization; watermark temp file uses random name
Changed
- Timeline - memoized `
` per track (fewer re-renders during edit) - Export - thread-local `asset_fingerprint` cache before parallel segment render; `commitGesture` avoids double project stringify
Tests
- CI - `test:timeline-contract` and `test:clip-color-contract` in `test:export-contracts`
- OSV - Windows-target allowlist for GTK/Linux transitive advisories (`audit:osv`, informational)
[0.8.30] - 2026-06-11
Added
- Export errors - typed `CUTFORGE_EXPORT:
` from Rust with localized UI messages (en/fr/ru) - Keyframe contract - shared opacity interpolation JSON verified in TypeScript and Rust
Changed
- Timeline drag - CSS preview during mousemove; store and undo history commit on mouseup only (no `updatedAt` churn mid-gesture)
- FFmpeg - per-segment thread budget aligned with rayon parallelism (replaces `-threads 0`)
- Export keyframes - subdivision interval 500 ms -> 2000 ms (fewer segments on long spans)
- Waveform - extraction capped at asset duration (max 300 s)
Tests
- CI - `test:export-contracts` and sticker export e2e in `test:ci`
- Timeline - drag keeps store unchanged during mousemove; gesture `updatedAt` behavior
[0.8.29] - 2026-06-11
Added
- Export telemetry - per-segment render duration and cache hit/miss in Rust logs; `app_export_timing` includes cache hit rate and segment render buckets (audit v3 §7.4)
Tests
- Timeline - component test: drag clip then undo restores original position (audit v3 §7.2)
[0.8.28] - 2026-06-11
Security
- Asset protocol - narrower `asset://` scope; per-file registration with path canonicalization (no whole-parent directory grant)
- Export paths - validate media paths before render; guards on proxy and waveform IPC
- Telemetry - pin `cutforge.app` origin in release builds (debug override only)
- External links - host allowlist for `open_external_url`
- FFmpeg download - pinned BtbN build with SHA-256 verification (no rolling `latest` tag)
- IPC - bound `volume_gain`, `playback_rate`, `opacity`, and keyframe values at project validation
Fixed
- Export - atomic finalize via `.part` rename; Windows staging uses `out.part.mp4` so FFmpeg accepts the muxer
- Export - render watchdog during parallel segments; license revocation no longer blocks export start on HTTP
- Export - reject concurrent `export_timeline` calls; stable cancellation error code
- Stickers - accept bundled sticker paths at export validation
- FFmpeg install - reject zip-slip paths when extracting the GPL archive
- Keyboard - Q/W/S timeline shortcuts ignore Ctrl/Alt combos
- Undo - `beginGesture` / `commitGesture` for drag and trim (replaces scattered `commitHistory` flag)
- License tests - skip remote revocation checks when unit-test machine id is set
Changed
- Export cache - segment cache key built from serialized render inputs (full fingerprint matrix)
- Playback - consolidated ref bag into `PlaybackRuntime`; named A/V resync thresholds
- Timeline - O(1) asset and per-track clip lookups during render
- FFmpeg install - stream GPL zip to a temp file instead of buffering in RAM
- Export UI - warn when motion interpolation (`minterpolate`) is enabled
- CI - `cargo fmt --check` and `clippy -D warnings` in `test:ci`
[0.8.27] - 2026-06-10
Fixed
- Preview stickers - stack stickers above PiP/image overlays (export compositor parity); raise preview z-index so WebView2 video no longer paints over sticker overlays
[0.8.26] - 2026-06-10
Fixed
- Stickers - resolve bundled SVG paths from exe directory and manifest (export, preview, and rasterization no longer fail with `resources/stickers/*.svg` not found in debug/no-bundle runs)
Added
- E2E - `export-sticker.e2e.cjs` (export + center luma verification); stage stickers beside E2E binary; `addStickerAt` on E2E bridge
[0.8.25] - 2026-06-10
Fixed
- Export cache - segment cache key fingerprints text content/style, reversed, playback rate, keyframes, effects, chroma key, and strip-metadata so re-exports stay correct after edits
- Project I/O - atomic writes for save project, autosave session, and license files (crash-safe)
- Undo - drag/trim gestures snapshot state at pointer-down; Ctrl+Z restores the pre-gesture position
- Keyboard - skip global undo/redo while typing in inputs; Ctrl+Shift+Z redo works with uppercase key
- FFmpeg install - reject zip-slip paths when extracting the GPL archive
- Export - reject concurrent `export_timeline` calls (shared cancel/PID state)
- QA - gesture undo skips no-op drags; E2E text export fixtures and peak-luma assertion
[0.8.24] - 2026-06-10
Fixed
- Export keyframes - subdivide long keyframe spans (500 ms steps) and sample transforms at segment midpoint for smoother motion at export
- Export text E2E - verify title visibility window via frame luma sampling (not only file size)
- License revocation - background check on startup; export uses cached status; offline grace reuses last successful result for 7 days
- Release hardening - debug-only env overrides for license path, machine id, export timeout
Added
- Tests - `ffmpeg-frame` E2E helper, keyframe subdivision/opacity sampling, `split_transform_montage` compositor unit test, offline grace cache test
Changed
- Compositor - shared `fps_interpolation_part` / `split_transform_montage` helpers (sticker + overlay chains)
[0.8.23] - 2026-05-30
Fixed
- Export keyframes - segment boundaries follow clip keyframes; transform sampling uses segment start (not mid-segment) so motion no longer freezes between keyframes
- Autosave (large projects) - payloads above the 1.5 MB localStorage cap persist in `app_data_dir` via backend session commands
Added
- E2E - long export with live progress and cancel; export with text offset from clip cuts
- Tests - Rust coverage for keyframe segment boundaries, hostile `drawtext` escaping, and autosave backend round-trip
[0.8.22] - 2026-06-10
Fixed
- Export text timing - titles use segment boundaries and `enable=between(t,...)` so preview matches export when text is not aligned on clip cuts
- UI freeze - heavy Tauri commands (`export`, probe, proxy, waveform, license, project I/O) run on the async blocking pool
- License signing - canonical JSON key order aligned with Node; golden vector cross-check in CI
- FFmpeg process I/O - stdout drained concurrently; stderr decoded as UTF-8
- Autosave - warn when project exceeds local size cap; no longer re-triggered on timeline scrub
- Updater notes - download dialog shows full changelog entries between installed and target version
Security
- Validate text clip styles on export/load; hardened `drawtext` escaping; non-recursive asset scope; embedded FREE watermark bytes
[0.8.21] - 2026-06-10
Fixed
- Rating prompt - shown after first export even without full telemetry opt-in; rating events stored via `consentRating`
- Ops opt-in KPI - counts `app_launch` and explicit toggles (rate capped at 100%)
- Site deploy (Vercel Hobby) - ops CSV export merged into `GET /api/ops/dashboard?export=csv`; desktop telemetry routed via `/api/event?target=app` rewrite (12-function limit)
Added
- Ops test coverage - KPI normalizers, mocked `fetchOpsDashboard`, CI Postgres e2e gate
[0.8.20] - 2026-05-30
Added
- Analytics P0-P3 - ops KPIs (opt-in %, weekly active machines, activation J+7, feature adoption, launches by country, rating summary); email on `app_export_failed`; weekly ops report cron
- Desktop telemetry - `app_feature_used` (SRT, silence trim, proxy, ducking), session summary on close, export abandon detection, 1-5 rating prompt after first export
[0.8.19] - 2026-05-30
Changed
- Toolbar - Help and language menus grouped on the right next to FFmpeg status
[0.8.18] - 2026-05-30
Changed
- Help menu - log mode (Normal / Debug), privacy, license, and check for updates moved from the toolbar into Aide
[0.8.17] - 2026-05-30
Changed
- Help menu - single entry point for feedback and logs; removed duplicate beta feedback panel in the inspector and the toolbar Open logs button
[0.8.16] - 2026-05-30
Added
- Help menu (toolbar) - manual (site), write to team (prefilled mailto + diagnostics), open logs; always available (including post-1.0); `app_feedback_opened` with `source: toolbar_help`
[0.8.15] - 2026-06-08
Added
- Product analytics (opt-in) - desktop telemetry P0-P2: export lifecycle, project/feature usage, updater, FFmpeg, license, session heartbeats, export timing, beta feedback; enriched `/ops` dashboard (in-app funnel, D1/D7 retention, heatmaps, time-to-first-export)
- Site - `hero_cta_click` on Windows download CTA
[0.8.14] - 2026-06-07
Fixed
- License revocation - forced server check on status read; background poll every ~2 min while app is open (second OPS revoke no longer stuck on PRO)
- OPS dashboard - `ops.js` crash (`escapeHtml` duplicate) caused black login page; token paste/hash login and restore (test) button
Added
- `scripts/setup-ops-dashboard-token.mjs` - rotate `OPS_DASHBOARD_TOKEN` on Vercel (`--force`)
[0.8.13] - 2026-06-07
Added
- Remote license revocation (desktop) - `GET cutforge.app/api/license?machine_id=...` on startup and before export; clears local PRO + toast when revoked (fail-open offline, 5 min cache)
- Site (already deployed) - `license_entitlements` table, unified `/api/license` (check + OPS revoke), Paddle fulfillment records entitlements; license ops runbook (internal)
[0.8.12] - 2026-06-07
Fixed
- Export audio - embedded video audio follows the top overlay (V2/V3), not V1 underneath (preview parity)
- Dev launch - Rust export compile fix; `devCsp` for Vite HMR while production CSP stays strict
- Updater toolbar - status colours in `tauri dev` (green / blue after silent check)
- Empty timeline - 30 s ruler on first launch, zoom fits lane width
Security
- Production CSP: removed `unsafe-eval` from `script-src`; Sentry ingest allowed in `connect-src`
- `test:security` gate: npm + cargo audit (OSV optional)
Added
- OPS dashboard: `updaterChannel` on `app_launch` (public vs PRIVÉ)
- Sentry `captureAppException` on export, import, and project I/O failures
- Tests: export audio stack V1/V2/V3, updater dev, timeline empty layout, CSP `devCsp`
Added (historical - pre-0.8.10 draft notes)
Added
- Paddle Billing checkout via `GET /api/checkout` (`variant=monthly|yearly|choose`, `machine_id`)
- Paddle setup - Vercel env vars, sandbox vs live, price IDs
- App Passer en PRO opens `cutforge.app/api/checkout?variant=...&machine_id=...`
- Site pricing buttons wired to the same checkout API
- GitHub Releases publish script + `npm run release:github`
Changed
- License dialog (PRO): primary Fermer button; Retirer / Importer secondary; machine ID hidden when tier is PRO
- i18n: `common.close`; license hints reference Paddle checkout via cutforge.app
- Legal docs: Paddle as Merchant of Record
- `site/config.js`: `checkoutApi` replaces `checkoutBase`; `releasesUrl` -> `github.com/CUTFORGE/releases`
Notes
- Vercel: `PADDLE_*`, webhook (`PADDLE_WEBHOOK_SECRET`), license keys, `RESEND_API_KEY` - see Paddle setup notes (internal)
- Fallback: manual `scripts/sign-license.mjs` if webhook or email fails
- Installed app builds predating this work still use old checkout URLs - rebuild required
[0.8.10] - 2026-05-30
Fixed
- Desktop telemetry delivery - POST via Rust (`reqwest`) instead of WebView `fetch`; session launch flag only set after successful send
[0.8.9] - 2026-05-30
Fixed
- Desktop telemetry - public Windows build CSP blocked `fetch` to `cutforge.app` (`connect-src`); opt-in events were silently dropped
[0.8.8] - 2026-05-30
Fixed
- Privacy toolbar button (always visible) - telemetry opt-in was unreachable in 0.8.x because the License dialog is hidden before v1.0
[0.8.7] - 2026-05-30
Added
- Telemetry toggle in License dialog - re-enable or disable anonymous usage stats after declining the first-run banner
[0.8.6] - 2026-05-30
Added
- Auto-install FFmpeg on first launch (Windows): silent download of BtbN GPL build into `%APPDATA%\com.cutforge.studio\ffmpeg\bin\` with progress overlay; manual setup dialog only if auto-install fails
- FFmpeg GPL compliance: `licenses/` notices, site `/third-party` (FR/EN/RU), in-app Open-source licenses link, written source offer
- Timeline bar: Undo/Redo with ←/-> icons, zoom centered in toolbar
- Global shortcuts: Space play/pause, ←/-> seek ±5 s, ↑/↓ track navigation
- Ctrl + mouse wheel zoom on timeline scroll area
- Sticky track label column when scrolling timeline horizontally
- Site user manual (`/manual`, FR/EN/RU) linked from download section
- v1.0.0 checklist - must-have vs nice-to-have for v1.0.0 (internal)
Changed
- Zoom scale: 100 % = 60 s visible, 200 % = 30 s; minimum zoom 5 % (was 25 %); viewport-based `pixelsPerSecond`
- Undo/Redo moved from main toolbar to timeline bar
- Site header: brand icons and top-left label removed
- Neutral brand icon (OG + Tauri app icons regenerated from master artwork)
Fixed
- Legal page build template: header no longer re-adds brand icon on `build:legal`
- Fresh Windows installs without admin rights no longer block on FFmpeg setup when network is available
[0.8.5] - 2026-06-04
Added
- Hardware HEVC decode for export: CUDA -> D3D11VA (Windows) / VAAPI (Linux) -> CPU fallback; `hwdownload` formats fixed for 8-bit (`nv12`) and 10-bit (`p010le`)
- Export setting Hardware decode (`exportHwDecode`: auto / off) in export dialog + i18n
- `hasAudio` on import (probe) - video files without an audio track no longer break compositor export
- Segment export progress (`ffmpeg_progress.rs`, overlay UI) and clearer FFmpeg stderr in logs on failure
- E2E benchmark-classic-fx export spec (optional, replaces long-video)
Fixed
- Session restore: register media paths before autosave restore so preview video loads (not black) after app restart
- Preview seek on load when paused (`onVideoLoaded` applies playhead position)
- Compositor export for video-only sources: skip embedded-audio duplicate input; optional `-map N:a?`
- Removed dead compositor/util code; Rust warning cleanup
Changed
- Export performance - hardware decode chain and troubleshooting (internal)
- Vitest branch coverage threshold and additional unit tests
[0.8.3] - 2026-05-31
Added
- Export watermark `CUTFORGE-WATERMARK.png` (FFmpeg overlay, 30 % opacity, bottom-right)
- Buy PRO button in license dialog -> cutforge.app/#pricing
- Site pricing section (FREE vs PRO, €1/mo · €10/yr, manual activation note)
- Tests: `LicenseDialog` component, `license-urls`, E2E license dialog smoke, watermark FFmpeg E2E
Changed
- Watermark asset: classic logo PNG instead of text `drawtext` / FREE icon
- License / export i18n aligned with PNG filigrane wording
[0.8.2] - 2026-05-31
Added
- Phase 1 licensing: Ed25519 signed `license.json`, machine-bound activation, FREE/PRO badge in toolbar
- FREE export watermark (PNG overlay, bottom-right, enforced in Rust/FFmpeg)
- License dialog (import `.json`, copy machine ID), `scripts/sign-license.mjs`, `scripts/generate-license-keys.mjs`
- Splash screen fixed 3.33 s minimum while main window loads in background
- Branding icons FREE/PRO (`public/branding/`)
Changed
- Inspector clip tools panel label PRO -> Avancé (commercial PRO is separate `license` namespace)
- App/window icons aligned on FREE badge; neutral icon archived under `public/branding/archive/`
[0.8.0] - 2026-05-29
Added
- Landing site (`site/`) for cutforge.app (v0.8.0, i18n, Vercel config)
- Single-pass project normalization (`normalize-project.ts`)
- E2E Page Object (`e2e/pages/EditorPage.cjs`) and specs: export-cancel, relink-media, undo-redo (12 specs total)
- Quality toolchain: `typecheck`, ESLint 9, Prettier, Vitest coverage thresholds in CI
- `scripts/sync-version.mjs` propagates version to Tauri, splash, site, and E2E
Changed
- Split `export/compositor.rs` into `compositor/` module (policy, render, filters, audio, inputs, util, tests)
- Split `useSequencePlayback.ts` into orchestrator + `hooks/playback/` (RAF loop, media sync, preview style)
- E2E bridge gated (`DEV` or `VITE_E2E`); dedicated `build:e2e` mode
- Rust: `total_cmp` for f64 ordering (no `partial_cmp().unwrap()`)
Fixed
- `duplicateTextClip` guard when source clip missing
- Prettier/format consistency on playback hooks and new E2E specs
[0.6.7] - 2026-05-28
Added
- Sticker test suite: store placement, timeline compositing, preview overlay layers, FFmpeg rgba export chain
Fixed
- Sticker default position: first sticker on a track uses layout index 0 (was offset by one)
[0.6.6] - 2026-05-28
Version sync (no functional changes beyond v0.6.5).
[0.6.5] - 2026-05-28
Added
- Second text track (T2) on the timeline - subtitles/captions on two layers
- Double-click a text lane to add a title on T1 or T2
- Drag text clips between T1 and T2 with lane-accurate positioning
Changed
- New titles use the selected clip's text track, or T1 when none is selected
- Overlap rules apply per text track (same time allowed on T1 and T2)
- Older projects gain T2 automatically via `ensureDefaultTracks`
[0.6.4] - 2026-05-28
Changed
- Inspector panel renamed PRO (was « Phase C - Pro »)
- Dev launcher script renamed `CUTFORGE.bat` (was `lancer-cutforge.bat`)
Added
- PRO polish: keyframe list + timeline ticks, chroma sliders, warm LUT effect, SRT file import/export, silence trim preview with thresholds, 720p proxy generation, beat snap (B) / Shift+click delete, GPU encoder in export settings dialog
Fixed
- Phase C panel crash (invalid GPU i18n keys) - `0bf4fd3`
- Timeline clips misaligned when dragging between video tracks (V3 -> V2 / V1)
[0.6.3] - 2026-05-28
Added - Phase C (CC parity foundation)
- Keyframes - opacity, scale, position X/Y on clip timeline (preview + FFmpeg export)
- Effect stack - blur, sharpen, vignette per clip
- Chroma key - green-screen colorkey on export
- Beat markers - timeline markers + snap helper
- SRT import - subtitles to T1 text track
- Silence trim - suggestion from waveform peaks
- Proxy media - optional proxy path on assets (4K workflow)
- Export GPU preference - auto / software (x264) / NVENC
- Platform flags - cloud, mobile, marketplace (beta toggles)
- AI tools - STT / background removal planned (SRT + chroma available now)
- Inspector Phase C panel, Vitest + Rust tests, E2E `phase-c.e2e.cjs`
[0.6.0] - 2026-05-28
Fixed
- Export presets - aspect ratio from the settings dialog (e.g. Instagram 1:1) is applied on export instead of a stale project snapshot
- `normalizeProject` - preserves `exportPresetId`, ducking, and other optional meta fields sent to FFmpeg
Added
- Tests for Instagram / all social export dimensions (Rust + Vitest)
- `scripts/test-export-aspects.cjs` - optional ffmpeg/ffprobe smoke test for 16:9, 9:16, 1:1, 4:3
[0.5.0] - 2026-05-28
Added
- Text track UX - drag, trim handles, snap, overlap avoidance on T1
- Text clipboard - copy, cut, paste; split at playhead; duplicate
- Text inspector - position X/Y, timing (ms), fonts, outline, shadow, style presets
- Preview - drag selected text overlay to reposition
- Export - FFmpeg drawtext with font file, border, shadow on Windows
- E2E - WebDriver scenarios for text track (`e2e/specs/text-track.e2e.cjs`)
[0.3.0] - 2026-05-28
Added
- Phase B: social export presets (YouTube, Shorts, TikTok, Instagram)
- Project templates (vertical short, YouTube, podcast) on new project
- Text track T1 - static titles, inspector, timeline blocks, preview overlay, FFmpeg `drawtext` on export
- Sticker pack - SVG stickers on overlay track (V3)
- PiP transform - position X/Y, uniform crop, clip opacity (preview + FFmpeg export)
- Audio ducking - lower music on audio tracks when video dialogue is present
- Q / W - trim clip start/end to playhead on selected clip
- Preview crossfade - optional fade at clip end in sequence preview
- Roadmap - Phase B/C CC parity plan (internal)
[0.2.1] - 2026-05-28
Added
- Phase A montage: per-clip speed (0.25×-4×), reverse on export, transitions (fade / wipe / slide), scale & rotation
- Inspector Montage panel, color presets in Image, timeline snap (grid, playhead, clip edges)
- Playback shortcuts J / K / L (scrub back, play/pause, play)
- Rust export: `clip_montage` (speed, transform, per-junction xfade kinds)
- Tests: montage store/UI, snap, playback keyboard, `clip_montage` unit tests (Vitest + `cargo test --lib`)
Changed
- Preview respects clip playback rate and transform; scaled media clipped inside the preview pane
Fixed
- Preview overflow when clip scale > 100% (video no longer covers toolbar, timeline, or inspector)
[0.2.0] - 2026-05-28
Added
- App icon asset `public/cutforge-icon.png` (window title bar, favicon, splash screen)
- Tauri splash window at launch; closes when the editor shell is ready
- FFmpeg setup dialog and persistent user paths (`%APPDATA%\com.cutforge.studio`)
- Export segment disk cache and single-pass export for one merged segment
- Release scripts: `npm run fetch:ffmpeg`, `npm run release:win`, `sign-windows-release.ps1`
- Preview/export fidelity banners and localized export phases
- E2E: FFmpeg staging beside app binary, precheck before WebDriver run
Changed
- Version bump to 0.2.0 (package, Tauri, Cargo)
- stricter FFmpeg readiness: requires libx264 + HEVC decode for "ready" status
Fixed
- TypeScript build (i18n locales, `ToastKind`, unused imports)
- Crossfade no longer forces per-segment `filter_complex` (faster export)
- Export cache module compiles in release builds
[0.1.0] - 2025
Initial MVP: timeline editor, FFmpeg export, FR/EN/RU, Windows desktop (Tauri 2).