Responsive Design - Time remaps only a graphic clip's layer keyframes;
the clip's own effects and masks are read at plain clip time. Applying
the remap to them would move keyframes in existing projects, so keep the
behaviour, state it in docs/graphics.md and pin it with a test
(layer keyframe remapped, clip Opacity keyframe not).
Fixes#3361
* feat(text): strikethrough for graphic text
Text layers get a Strikethrough toggle next to Underline: per layer
(`strikethrough` parameter, Properties ▸ Text ▸ Style "S") and per
character (`graphics.setCharStyle` style `strikethrough`, stored in the
layer's style runs).
The stroke uses the font's OS/2 strikeout position and size; a font that
leaves them out gets a stroke centred on half the x-height. Strokes are
added to the layout's decoration rectangles next to underlines, so every
renderer and the path export draw them without further changes. Vertical
text gets a stroke down the middle of each cell.
generated with Claude Code
* contributors: add alexanderalber
generated with Claude Code
* graphics properties: draw the letter-button marks in a helper
Keeps the Properties scroll closure under the cognitive-complexity limit with the new
Strikethrough button.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* #3415: fit graphics template width alongside height
Scale independent layer width and its keyframes when fitting graphics templates to another canvas. Add static and animated regression coverage and document the behavior.
Validation: contributor reports all requested verification passed; agent ran no tests or builds.
Assisted-by: OpenAI Codex
* #3415: fit omitted scale defaults in imported templates
Materialize definition defaults for both scale axes before fitting, preserving explicit values and animation. Cover imported text and shape layers with omitted parameters and document the behavior.
Validation: contributor confirmed verification passed. Agent ran no tests or builds.
Assisted-by: OpenAI Codex
Caption text was always the family's SemiBold: caption_style hard-coded
the style. CaptionStyle gains font_style (captions.setStyle `fontStyle`,
default "SemiBold", serde default so old projects load unchanged),
resolved like graphic text: the nearest weight when the family has no
face of that name, SemiBold when empty.
- render_caption uses it (Program monitor and burned-in export)
- captions.list reports it; the Text panel's Captions tab offers the
family's styles next to the font
- Upgrade Caption to Graphic carries it to the text layer's font style;
graphics.fonts.used lists it and file.replaceFonts matches and
replaces it
- docs/captions.md, docs/graphics.md
The default keeps default_style_renders_like_the_reference green
(pixel-identical); font_style_is_used renders Regular and Bold against
SemiBold.
Fixes#3357
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Letter spacing (Tracking) does animate in the engine — keyframes on the text
layer interpolate and the rendered width follows them — but the Essential
Graphics panel had no way to start that: its number and point rows had no
stopwatch, so a change there always set the value for the whole clip.
Every animatable number/point row in the panel (Tracking, Leading, Font
Size, Baseline Shift, Position, Anchor, Scale, Rotation, Opacity…) now has
the same stopwatch as Effect Controls. It calls `effects.toggleAnimation` on
the layer; once a property animates, a change in the panel adds or updates a
keyframe at the playhead (graphics.set was already keyframe-aware). A
Type-tool character selection edits character styles, which have no
keyframes, so those rows show no stopwatch while it is active.
Automation id: `graphics.prop.<id>.stopwatch`.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L8DvNHjchh5Gf2q24UsWQn
With the Selection tool, Shift-click adds a layer of the selected graphic to
the selection or takes it out, Cmd/Ctrl-click takes it out, and a plain click
still selects only it. Dragging any selected layer now moves every selected
layer by the same amount, snapping by their combined outline; Shift-dragging an
unselected layer adds it first. The move is one undo step through a new
`graphics.moveLayers` command, which shares Align's mover so pinned layers keep
following their targets. Dragging a single layer is unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L8DvNHjchh5Gf2q24UsWQn
A rectangle's rounded corners could only be set by typing a Corner
Radius in Properties. As in Premiere, a selected rectangle now shows
four small circles just inside its corners: dragging one towards the
middle rounds all four corners (Corner Radius is one value), dragging it
back out squares them, and the radius stops at half the shorter side,
where the renderer caps it too.
The circles sit on each corner's diagonal at the radius, at least 12
points in, and show only on rectangles at least four times that across
on screen. While dragging, a white outline of the rounded rectangle
previews the result; the radius is set on release with graphics.set, one
undo step per drag. Automation ids: program.layer.<clip>.<layer>.corner.<n>.
Tests: graphics_handles_ui (the circles round all four corners, clamp
and undo; ellipses and tiny rectangles have none).
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).
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.
Every layer of the selected graphic was outlined on the Program monitor,
so a box stayed after its layer was deselected, and a layer whose
visibility was off kept its box and could still be clicked and dragged.
As in Premiere Pro 26: a layer has a box only while it is selected (or
typed into), and a layer whose visibility is off is not on the monitor
at all: no box, no handles, no click target.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A text layer is now one of two types, each with Premiere Pro 26's handles:
- Point text (Type tool click): every handle scales the layer about its
anchor point, both axes alike; the font size is untouched.
- Paragraph text (Type tool drag): the dragged rectangle is a box the text
wraps in. Handles move that side or corner of the box, the opposite side
stays, the text re-wraps at the same size. Lines below the box are not
shown and the bottom-right handle turns into a red plus.
- The anchor point can be dragged with the Selection tool; the layer stays.
It is picked before anything under it, so a shape dragged by its exact
centre moves its anchor.
- Properties > Text > wrench opens Text Properties, where Text Layer Type
switches a layer between the two (graphics.setTextType); the text stays
where it is.
Shapes keep stretching away from their opposite side.
New: text layer parameter box_height, graphics.newText `box`, and
textType / box / overflow / anchor / scale in graphics.list. A layer saved
before a parameter existed can now be given it with graphics.set.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Vertical text layout in the text engine (one line per character, columns right to left).
All edits are undoable engine commands; engine tests cover each menu group.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Program monitor overlay for graphic clips: Type tool click creates a text layer (in the
selected graphic under the playhead or a new clip) and edits it in place with a caret,
selection highlight, arrows/word/line navigation, Shift-select, Cmd+A/C/X/V, Return,
Backspace/Delete, Esc; typing is one undo step. Selection tool selects, moves and scales
layers (bounding box, handles, anchor); double-click edits text. Rectangle/Ellipse tools
drag out shapes; Pen tool places a closed path. Automation ids program.layer.*.
- Properties (when a graphic is selected) and Essential Graphics: Layers list, Align and
Transform (align / distribute), Text (source text, font family + style incl. system fonts,
size, paragraph alignment, tracking, leading, baseline shift, box width, faux bold/italic,
caps, underline, kerning/ligatures), Shape, Appearance (fill, 2 strokes, background,
shadow). Automation ids graphics.*.
- Headless UI test (egui_kittest) drives the whole flow through the control channel; with
FILMCRAFT_UI_SHOTS it renders PNGs via wgpu (works with a locked screen).
- docs/graphics.md; architecture, ROADMAP, ATTRIBUTION (code-drawn glyphs) updated.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>