Dragging a handle of a rectangle, ellipse or polygon in the Program
monitor changed the layer's Scale, and a corner scaled width and height
alike, so a shape could not be made wider or taller from a corner.
As in Premiere, the handles now change the shape's Size (Premiere's
Path) and leave Scale alone: the dragged side or corner follows the
pointer, the opposite one stays, and the anchor point is renumbered so
that it and Position stay on the same spot of the picture. A corner
moves freely; Shift keeps the proportions. A path has no Size, so it
still stretches (and its corners follow the same Shift rule).
The paragraph-text box and the shape box share the side-dragging code
(drag_sides).
Tests: graphics_handles_ui (a shape's handles change its Size and Shift
keeps the ratio; a polygon's handles keep the opposite corner).
Progress docs per craftrules standards/progress-docs.md: target-app parity, gaps, roadmap with alpha gate, readiness by audience (full / mainstream / essentials) with Opus 5.5 hours, and feature, format, hardware, UI and localization parity docs.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The ui.set table row is one long line; #412 (PR #656) also edits it, so
the two PRs conflicted with each other in docs/control-protocol.md
whichever merged second. The thumbnails parameter is now described in
its own paragraph below the table, leaving that row untouched.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Problem: with the Source monitor focused, L only ever played at normal
speed, and J and the slow shuttle commands returned "Source playback
currently supports normal forward speed": the Source clock always ran at 1x.
Fix: the Source clock runs at a signed speed. L/J play forward/backward
and pressing the same key again doubles the speed up to 8x; Shuttle Slow
Right/Left play at 1/4 speed. As in the Program monitor, speeds other than
normal forward play without sound, and backward playback stops on the
first frame. docs/monitors.md updated.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Problem: a video clip only ever showed its first frame. Show Video
Thumbnails was a plain on/off switch, so a wide clip at high zoom stayed a
flat colour after its head.
Fix: the wrench menu keeps Show Video Thumbnails and adds Head (the
default, as before), Head and Tail and Continuous under it. Continuous
tiles aspect-correct frames across the clip on a grid anchored at its head,
only where the panel shows them (at most 256 per clip), each frame the
source time under the tile's left edge (speed, reverse, frame holds,
multicam angles), requested at thumbnail priority through the existing
frame cache. A clip too narrow for the tiles falls back to its head. The
mode is UI state (`thumbnail_mode`, serde default Head, so saved settings
keep working) and `ui.set {timeline: {thumbnails}}` sets it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Headless import merged into the current project, so a demo or leftover timeline mixed into export. replace:true starts from file.newProject with the same name, then imports, which is the documented empty-project-start workflow.
Problem: the keyframe lane highlighted every keyframe whose time equals the
playhead. A click on a keyframe moves the playhead there, so every
parameter's keyframe at that frame lit up (point 4 of #412).
Fix: a serde keyframe selection in the UI state (clip, effect index, param,
mask, media time). A click (or drag start) selects exactly that keyframe and
still moves the playhead; highlighting reads the selection. A moved keyframe
stays selected; references to deleted keyframes or clips no longer shown are
dropped. Readable via ui.inspect, settable via ui.set keyframeSelection.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- GpuRendering defaults to Off (opt-in per export) until measured on Windows and macOS;
docs and ROADMAP say so.
- The shared export device installs an uncaptured-error handler and a device-lost callback
that log once and retire GPU export for the session (frames go to the CPU) instead of
wgpu's default panic.
- RendererPool drops a renderer that panicked and retires the pool, so the rest of the
export renders on the CPU (regression test in gpu_hook_scenarios); the duplicate
hostile-size test that raced on the global registry is folded into it.
- Rebased onto main's render_plan/transitions: render_export_prepared reads the linear
accumulator after render_plan; transition (Composite) plans fall back to the CPU.
- es/ja rows for the new Export strings; web_time::Instant in the export pipeline.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- ja.tsv rows for the new appearance strings (no duplicate Dark/Light);
uk.tsv gets Next Appearance Mode, which the pt-br menu catalog now has.
- appearance.colorTheme is legacy input but no longer goes stale: it follows the
mode and theme choices (the theme a fixed mode shows, the dark theme in Auto).
- docs/project-files.md documents appearanceMode / darkTheme / lightTheme.
- #489's menu/settings label test compares with the theme cards' choices.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Narrations take schema v14 (main's v13 is audio streams): v13_to_v14, docs updated.
- neural-voices is off by default in the desktop app, like whisper; the model download stays
an explicit, confirmed job.
- Removed the debug probe2 test module from tts-text.
- project_subset prunes narrations of items it drops.
- unique_wav_path is bounded (99 999 names) and returns an error instead of looping.
- narration.rs uses web_time::Instant (wasm clock test); tts.* commands get the
"Text to Speech" shortcut category, with es/ja rows for it and Edit Narration.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Nothing is written unless FILMCRAFT_DEV_DESKTOP_ENTRY=1 (debug builds no longer write into
~/.local/share on every cargo run).
- The 256/512 PNGs are copied from this checkout's assets/app-icon at runtime instead of
include_bytes! into every Linux binary; a missing icon is logged and skipped.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Rebased onto main's cli()/out! refactor: `--help` is checked as a parsed flag, so
`filmcraft-cli export --help` prints the reference too (test extended).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
tests/setting.rs: keep the check against the backend register() reported
(main's fix expects VA-API on Linux, which NVDEC now sits in front of).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`captions.list` resolved `track` with track_param and treated an
unresolved selector the same as an omitted one, so a missing numeric id
(or a label past the last track, such as `C3` with two tracks) returned
every caption track with success. It now fails with "no such caption
track", as captions.export, setTrack, setStyle, deleteTrack and add do.
Omitting `track` still lists every track, and valid ids and C-labels
still return just their track.
Test: two caption tracks; each id and each label returns only its own
track; a missing id, `C3`, `C0` and a non-label string are errors.
Fixes#545
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Adds a second Linux decoder backend, crates/platform/src/nvdec, that talks to
NVIDIA's driver directly (libnvcuvid + the CUDA driver, both loaded at run
time). It is registered in front of VA-API: what NVDEC declines still reaches
VA-API, then the software decoders. H.264 8-bit and HEVC 8/10-bit, 4:2:0
progressive.
NVIDIA's parser takes Annex B access units, so there is no host-side DPB.
Pictures are read back as NV12 / P016 through the existing biplanar path and
are bit-exact with the software decoders. The CUDA context is the one
nvenc::device already retains; it gains push/pop and copy-to-host. A seek
recreates the parser and keeps the decoder.
Also fixes tests/setting.rs, which expected the Media Foundation backend name
on Linux, and updates the Hardware decoding tooltip and its Spanish row.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
graphics.newShape, newRectangle, newEllipse and newPolygon already take
`seconds`, `track` and `time` through place_video_clip, as graphics.newText
does. Their parameter docs (what command_list shows) and the table in
docs/graphics.md listed only position, size and clip; they now list all of
them and say that position is the shape's centre.
place_video_clip takes the id of the command that called it for its errors.
It named graphics.newText in every placement error (V3 is not free here),
whether a shape, a file, a template or an upgraded caption had asked.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Stops were mixed in encoded sRGB, and any run with its own fill turned the whole layer into a solid colour. The ramp now blends in linear light, turns with the layer, and leaves a per-character fill solid.
Appearance › Fill Type paints a ramp across the layer bounds, and the ramp turns with the layer. A per-character fill colour still paints those characters as a solid.
8-bit NVENC sessions (H.265 Main, hardware H.264) now take the export's RGBA
frames as ABGR input buffers and NVENC converts them to BT.709 limited 4:2:0
on the GPU, with the same codes as rgba_to_yuv420_8. The CPU conversion ran
on the encode thread, where it competed with the render of the next frames
for the thread pool (about 13 ms per 1080p frame instead of 1.5 ms) and held
NVENC back. A driver that refuses RGB input falls back to the CPU conversion
into NV12. HEVC Main 10 is unchanged.
60 s of a 1080p30 project as H.265 in the app: 18.7 s -> 15.0 s (96 -> 120
fps); headless 30.3 s -> 16.8 s.
Co-Authored-By: Claude <noreply@anthropic.com>
Review feedback on #246:
- Gate the install to dev runs: a debug build, or a release run with
FILMCRAFT_DEV_DESKTOP_ENTRY set (the documented cargo run --release flow);
never when APPIMAGE or FLATPAK_ID is set — an AppImage mounts at a fresh
/tmp/.mount_* each launch, so its Exec path changes every time, and packaged
installs ship their own entry and must not write into the profile.
- Escape Exec for both desktop-entry layers exactly like
packaging/linux/install.sh: shell layer first (` " $ \ get a backslash),
then the file-value layer (every backslash doubled), always double-quoted.
Binary paths with =, % or control characters are rejected instead of written
(install.sh rejects them for the same reason).
- Compare the Exec path exactly: parse the first Exec argument through both
layers instead of a substring contains, so /a/filmcraft-old no longer
counts as ours and a stale entry is rewritten.
- eprintln -> log::warn for the skip message (the logging module landed meanwhile).
🤖 Generated with [Kimi Code](https://github.com/storyteller/kimi-code)
Co-Authored-By: Kimi <noreply@storyteller.dev>
The engine already keeps one playhead per sequence while a project is open, and the
project file's view already stores each sequence's zoom, scroll and track heights, but
not its playhead: after saving and reopening, every sequence started at zero.
ProjectView gains `playheads` (sequence id -> Tick), written beside the tabs and views
and restored with them when "Restore open sequences when opening projects" is on. Files
written before it have none and open as they did. Values from a file are not trusted:
ids that are not sequences are dropped, playheads are clamped to 0..ProjectView::MAX_PLAYHEAD
and snapped to a frame of their sequence.
Tests: each_sequence_reopens_with_its_playhead_where_it_was_left (fails without the
fix), hostile playheads in a_damaged_view_in_a_project_file_is_cleaned_up_or_ignored,
a deleted sequence's playhead is not written, and the format round trip carries it.
Also adds my contributor credit entry (people.toml).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qq8BK1LmohzE1ga4NgAdWM