Commit Graph
68 Commits
Author SHA1 Message Date
Brandon ThomasandClaude Opus 5.5 c3d2f740d0 M3.10: Edit/Clip/File menu commands — labels, Paste/Remove Attributes, subclips, frame holds, time interpolation, audio options, Replace With Clip
Edit: Label ▸ 16 colours + Select Label Group, Paste Attributes… (⌥⌘V, with
Scale Attribute Times), Remove Attributes…, Select All Matching, Remove
Unused, Consolidate Duplicates. File: New ▸ Sequence From Clip, Bin From
Selection (⇧B), Offline File…; Close Project (⇧⌘W, asks to save), Save All.
Clip: Make Subclip… (⌘U, Restrict Trims), Edit Subclip…, Modify ▸ Audio
Channels… (⇧G) / Timecode…, Video Options ▸ Frame Hold Options…, Add Frame
Hold (now splits at the playhead), Insert Frame Hold Segment, Field
Options…, Time Interpolation (Frame Sampling / Frame Blending / Optical
Flow, blending rendered on CPU; optical flow falls back to blending),
Fit to frame / Fill frame; Audio Options ▸ Breakout to Mono, Extract Audio
(WAV next to the project, imported); Replace With Clip ▸ From Source
Monitor / Match Frame / From Bin. Hold Filters: effects animate through a
hold unless set. Menus support nested submenus (egui and native) and follow
Premiere's order. Dialogs in panels::clip_dialogs with automation ids and
ui.set {clipDialog}.

Project schema v9 (no-op migration v8→v9, v8-subclip fixture): clip
time_interpolation / hold_filters / field_options / source_channels,
Interpretation.audio_channels, Subclip.restrict_trims.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 23:28:37 -04:00
Brandon ThomasandClaude Opus 5.5 9947fee359 M4.7: View menu and monitor view options: Paused Resolution, 1/16, High Quality Playback, Display Mode (alpha/RGB channels, comparison, multi-camera, source audio waveform, video+waveform split), Magnification with panning, rulers, guides (drag from rulers, lock, clear, Add Guide, templates in preferences), Snap in Program Monitor
MonitorView state is serde and settable with ui.set {program|source}; view.* UI commands with
menu checkmarks; wrench and zoom menus with automation ids; headless kittest tests (view_ui.rs).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 23:23:27 -04:00
Brandon ThomasandClaude Opus 5.5 7095a986ff fix: control channel never steals keyboard focus — UI requests and screenshots bring the window forward without activating the app (orderFrontRegardless); --control launches without activation; only ui.focus activates
Agents' ui.* requests used ViewportCommand::Focus, so the user's typing in another app landed in FilmCraft as shortcuts (stray Extract, Mark In/Out, markers…). Verified live: the frontmost app stays frontmost through launch, ui.click, ui.key and ui.screenshot, and the requests still take effect.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 11:53:19 -04:00
Brandon ThomasandClaude Opus 5.5 9f4208bb08 Merge main into M11.7–9 (relink, proxies, project manager): GopCache keeps main's continuation algorithm with a same-thread reentrancy guard (nested rayon requests decode privately instead of deadlocking); ProRes export combines profile choice with HDR signalling; project schema v6
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 09:19:19 -04:00
Brandon ThomasandClaude Opus 5.5 b66074a77c M11.8: Project Manager (collect files, consolidate and transcode)
- file.projectManager {destination, mode: collect|consolidate, sequences?,
  excludeUnused, handles, preset, includeProxies, includePreviews, dryRun, wait}.
- Collect copies every file the chosen sequences use (nested sequences and subclips
  followed), optionally proxies and render previews; consolidate writes only the used
  media range plus handles with a transcode preset and re-bases media time so edits,
  keyframes and markers stay put. Unused clips can be excluded.
- Dry run reports original / resulting disk space and the file plan; the copy runs
  as a job and writes the project last with the new paths and fingerprints.
- UI: Project Manager dialog (sequences, mode, preset, options, destination,
  Calculate → sizes) with automation ids pm.*.
- Tests: collected project opens with its media moved away and renders the same
  frames; consolidate trims to used range + handles and matches (>38 dB) the original.
- ROADMAP: M11 done.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 09:13:56 -04:00
Brandon ThomasandClaude Opus 5.5 a1cc0a6838 M11.9: proxies (create / attach / toggle / reconnect) and ingest settings
- media.createProxies: background job (jobs.list progress) transcoding with our own
  encoders — ProRes 422 Proxy ¼/½, ProRes LT ½, H.264 ¼/½ (audio included) — into
  <media dir>/Proxies or a chosen folder; attached when the job finishes
  (Session::poll_persistence applies finished media jobs).
- media.attachProxies (duration ±1 frame and frame rate must match; size/aspect may
  differ), media.detachProxies, media.reconnectFullRes, media.toggleProxies
  (Preferences ▸ Media ▸ enableProxies; bumps the view revision without dirtying the
  project), media.proxyPresets.
- Proxy frames are smaller; ProxySource reports the original size and the compositor
  derives its pixel scale from the frame, so Motion and effects render the same
  picture (44 dB PSNR vs full res with Motion + Gaussian Blur, 48 dB at ½).
  Export always renders full resolution.
- ExportSettings::prores_profile (proxy / lt / standard / hq).
- project.ingestSettings + file.import: copy (fingerprint-verified), transcode,
  create proxies, copy and create proxies.
- UI: Toggle Proxies button on both monitors (lit when on), Create Proxies dialog,
  Project panel Proxy submenu and P badges.
- Measured (one core, 4K H.264 testsrc2): CPU composite 201 → 35 ms/frame at ¼ with
  ProRes Proxy ¼, 277 → 131 ms at ½ with H.264 ¼; decode 9.2 → 0.7 ms with H.264 ¼.
- Tests: create+attach+toggle same composite modulo scale, attach checks, export
  ignores proxies, ingest copy + proxies, ignored 4K benchmark, headless UI test.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 09:06:56 -04:00
Brandon ThomasandClaude Opus 5.5 a9af6fac71 Merge M8 (LUTs, colour management, Colour Match) into M4.6 playback; project schema v5 for colour management; architecture §5.3
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 09:05:55 -04:00
Brandon Thomas c47c90f070 Merge branch 'main' into worktree-agent-a0342df5d4a386968 2026-10-01 09:03:47 -04:00
Brandon ThomasandClaude Opus 5.5 3dab45f45a M8.5: Colour Match (Lumetri ▸ Color Wheels & Match ▸ Apply Match) with skin-tone protection
- render::color_match: non-ML statistics matching — Oklab means per tonal range (smoothstep
  shadow/midtone/highlight weights) plus mean chroma; "Face Detection" down-weights skin-tone
  pixels (Oklab hue 20–75°) in the colour statistics and then limits the skin hue shift to 8°;
  the grade is solved in Lumetri's own parameters (3 wheels, their lightness, saturation) by
  damped Gauss–Newton through the real Lumetri implementation, so the result stays editable
- render::render_clip: one clip's layer at a time (the shot as Lumetri sees it)
- lumetri.applyMatch {clip?, referenceTime|Frame|Timecode, faceDetection} sets the grade in one
  undo step and reports the statistics distance before/after; refuses a reference inside the
  clip itself
- Lumetri panel: Color Match row (reference timecode, Face Detection, Apply Match; automation
  ids lumetri.match.*)
- tests: Oklab reference values, self-match is a no-op, a known grade is recovered (residual
  −86 %), skin protection holds the skin hue shift to ≤ 8° (51° without), engine and headless
  UI tests
- docs: control protocol (colour commands and ids), testing; ROADMAP M8 row and log

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 09:02:54 -04:00
Brandon ThomasandClaude Opus 5.5 b98c886133 M11.7: offline media and relinking
- Missing media no longer fails: opening a project scans for missing files
  (`media.findMissing`, `file.open` → missingMedia). Missing clips render our own
  offline slate (render::offline: red striped field, warning triangle, "MEDIA NOT
  FOUND", file name, hint) with silent audio, and the Link Media dialog opens.
- Media identity: imports record the size and a fast content fingerprint (hash of
  size + first/last MiB) in MediaClip::identity (optional field, older builds
  ignore it, no schema bump).
- media.relink checks file name / extension / fingerprint ("Clip ID") / duration /
  media start / frame size+rate+channels, refuses a mismatched fingerprint unless
  forced, supports Align Timecode (Project::shift_media_time), and relinks the
  others automatically by the derived folder remap (Windows/macOS separators).
- media.autoRelink {from,to} | {folder}, media.search, media.replaceFootage,
  media.makeOffline {deleteFiles?}, media.offlineAll, media.status, media.linkMedia.
- Media pool caches sources per reference (path, offline flag), so relink, Make
  Offline and undo take effect at once; proxy-aware resolution and a full-res
  provider that export uses.
- codecs::gop no longer holds its lock while decoding: rayon decoders inside a
  parallel export could steal another frame of the same source and deadlock.
- UI: Link Media dialog (rows, match options, folder search with preview, Locate…,
  Link, Offline, Offline All, Cancel), Make Offline dialog, Project panel offline
  markers and context menu, media thumbnails keyed by media reference.
- Tests: moved media opens offline (slate rendered) and relinks others by folder,
  prefix remap and folder search, fingerprint mismatch refused, Make Offline,
  headless UI test of the dialog.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 09:00:35 -04:00
Brandon ThomasandClaude Opus 5.5 995621ff67 M7.4: Essential Sound panel — Browse/Edit tabs, type buttons, collapsible sections with switches and sliders, presets, Clip Volume/Mute footer; docs
- crates/ui-egui/src/panels/essential_sound.rs: per-type sections (Loudness with Auto-Match,
  Repair, Clarity, Creative, Ducking with Generate Keyframes, Duration note, Pan), preset dropdown
  with save/delete, code-drawn type glyphs, automation ids for every control; every control runs an
  essentialSound.* command and a slider drag is one undo step
- headless kittest test (essential_sound_ui.rs) with optional wgpu snapshots
- docs: architecture §5.2, control-protocol ids, testing, ROADMAP

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 08:40:20 -04:00
Brandon ThomasandClaude Opus 5.5 4b83e088eb Merge main into the playback branch (mixer recordStart moves to the end of preroll; GopCache keeps main's random-access key walk-back)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 08:30:04 -04:00
Brandon ThomasandClaude Opus 5.5 9baa34fa45 Merge M10.1–2 + M3.2/M3.11 into M7.2 (audio mixer): preferences gain audio + playback + trim groups; ROADMAP refreshed
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 02:17:12 -04:00
Brandon ThomasandClaude Opus 5.5 e7bb3c431c docs: audio mixer (architecture §5.1, control protocol ids, mixer tests); ROADMAP: M7 mixing
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 02:12:54 -04:00
Brandon ThomasandClaude Opus 5.5 985a848c5e M3.11: keyboard shortcuts editor + presets
- Engine shortcut set (crates/engine/src/shortcuts.rs): bindings as data
  (command, canonical chord, optional panel), panel shortcuts override app ones
  while the panel has focus; per-OS modifiers (Cmd = Cmd/Ctrl, Ctrl = macOS
  Control, folded into Ctrl elsewhere and reported as conflicts); conflict
  detection, reassignment, undo/redo, Cancel restores; frontend commands
  registered so tools/transport/panels are bindable too.
- Presets: FilmCraft Default, Premiere Pro / Final Cut Pro / Avid Media
  Composer Compatible (key tables mapped onto FilmCraft command ids); custom
  presets as JSON in <data dir>/Keyboard Shortcuts/, active set persisted,
  Save As / Delete / Export / Import (Windows-notation files converted).
- shortcuts.* commands for agents/MCP (list/get/set/clear/undo/redo/conflicts/
  forKey/resolve/presets/loadPreset/savePreset/deletePreset/export/import/audit).
- Premiere-default audit: commands without a shortcut get Premiere's key when
  free (Shift+D Apply Default Transitions, Shift+J/L slow shuttle, Shift+K Play
  Around, Project > Shift+O Open in Source Monitor); Audio Clip/Track Mixer
  window shortcuts corrected (Shift+9 / Shift+6).
- Edit > Keyboard Shortcuts... (Cmd+Alt+K): keyboard drawn in code with
  app/panel/both colouring and modifier filter, searchable command list with a
  record-on-click Shortcut column and +, Key: detail list, conflict warnings,
  Undo/Redo/Clear/Cancel/OK; automation ids shortcuts.*.
- Menus (in-app and native macOS key equivalents), command.list and the input
  loop follow the active bindings live.
- Tests: 7 engine tests; scripted kittest test (record keys, live menu update,
  reassignment, undo/redo, cancel, OK, panel-specific Left in History).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 01:56:47 -04:00
Brandon ThomasandClaude Opus 5.5 5110b694d8 M4.5: fix UI hang after preview render (macOS App Nap starved the hidden app; ui.screenshot waits forever)
Root cause: not a lock. When the window is hidden or behind others (always when the app is
driven over the control channel), macOS App Nap drops every thread of the process to background
priority 4 and defers its timers. On a loaded machine (load avg ~150) a priority-4 process gets no
CPU: after Render In to Out + play the UI thread stayed runnable inside the run loop's timer
callout, every thread at PRI 4 (`ps -M`), 0 % CPU, control channel silent. Relaunched with App Nap
off the same script ran through: render 2–13 s instead of 15 s, every reply immediate (2/2 runs).

- apps/filmcraft/src/app_nap.rs: hold a user-initiated, latency-critical NSProcessInfo activity
  for the life of the process (= NSAppSleepDisabled for a bundle; idle system sleep allowed).
- ui.screenshot: a hidden window or sleeping display never presents, so the screenshot event
  never came and the UI repainted at full speed forever (100 % CPU). It now fails after 10 s with
  "no frame was presented (window hidden or display asleep)" and polls at 60 Hz meanwhile.

Regression test: app_nap::tests (the activity is taken on macOS). The starvation itself needs a
hidden window on a loaded machine and is checked with the control-channel script in the commit
notes (captions.newTrack/add, markIn/Out, Full, renderInToOut wait, stop, playhead.set,
renderBar, ui.screenshot): all replies within 10 s.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 01:17:13 -04:00
Brandon ThomasandClaude Opus 5.5 516978b4d4 M11.2: auto-save ring + crash-recovery journal
- Engine autosave module: Preferences (autoSave.enabled/intervalMinutes=5/maxVersions=20/
  saveCurrentProject/recoveryJournal/recoveryIntervalSeconds), persisted in the per-user data
  dir; prefs.get/set/reset commands.
- Background worker (filmcraft-autosave thread): the UI thread only hands over the Arc<Project>
  snapshot; JSON encoding and atomic writes happen on the worker. Premiere-style auto-saves to
  <project dir>/Auto-Save/<name>-YYYY-MM-DD_HH-MM-SS.fcproj (local time), only when changed,
  rotated to Maximum Project Versions, optionally also saving the project.
- Recovery journal: debounced snapshot of unsaved changes in <data>/Recovery/<session>/ guarded
  by an OS file lock; on launch, sessions whose lock is free and that left a snapshot are offered.
  Clean quit removes the journal (or keeps it, marked cleanExit, if changes were unsaved).
- Commands: file.recover, file.discardRecovery, file.recoveryList, file.autoSaveNow,
  file.autoSaveStatus, file.listAutoSaves.
- UI: Preferences ▸ Auto Save page (Edit ▸ Preferences, Cmd+,; FilmCraft ▸ Settings… on macOS),
  "Recover Unsaved Changes" dialog at startup, Revert confirmation; all with automation ids.
- Desktop: --recover / --no-recover / --data-dir; local-time offset via chrono (clock).
- Verified in the app: edits, kill -9, relaunch, dialog, Recover restores the edits (unsaved, original path).
- docs/project-files.md: auto-save, recovery and measured costs (2,000 clips: 2.5 ms encode,
  1.4 MB, ~30 ms atomic write+fsync, all off the UI thread). ROADMAP M11 updated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 00:10:23 -04:00
Brandon ThomasandClaude Opus 5.5 3eac0a2ee7 M14: MCP server (headless + bridge to live app), filmcraft-cli (probe/commands/render/run/mcp), .mcp.json, control protocol docs
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 06:50:31 -04:00