Skip to content

Add variable font weight axis support#65

Open
animeshbhuria wants to merge 2 commits into
palmier-io:mainfrom
animeshbhuria:variable-font-weight-final
Open

Add variable font weight axis support#65
animeshbhuria wants to merge 2 commits into
palmier-io:mainfrom
animeshbhuria:variable-font-weight-final

Conversation

@animeshbhuria

@animeshbhuria animeshbhuria commented Jun 20, 2026

Copy link
Copy Markdown
Contributor

Closes #50.
Adds a fontWeight property to TextStyle and a Weight slider in the text inspector's Typography section, applied via CoreText's wght variation axis in resolvedFont.
BundledFonts.swift already handled variable font registration correctly (dedups by family, per the existing comments) — the actual gap was downstream: the font picker and TextStyle only ever carried a font name, so nothing could select or apply a font's weight range even though it loaded correctly.
The slider is disabled (and visually dimmed) for fonts with no wght axis, detected via CTFontCopyVariationAxes — so static fonts like Poppins or Skia don't show a control that silently does nothing.
Scoped to static weight selection, addressing #50. Full animatable motion typography (keyframing weight/width over time) would be a separate, larger follow-up.
Tested locally and verified that: — confirmed the slider visibly changes rendered weight on bundled variable fonts (Geist, Inter, SpaceGrotesk, etc.), correctly stays disabled on static fonts (Poppins, Skia, Anton), and that existing saved project files without a fontWeight field still load correctly.


Note

Low Risk
Localized text styling and rendering changes with safe fallbacks; minor layout risk from removing explicit word-wrap on paragraph style.

Overview
Adds fontWeight (default 400) to TextStyle with backward-compatible decoding, and a Weight scrubber in the text inspector Typography section that previews/commits through the existing text-style editor and refits clip bounds like font size.

Rendering now applies weight via CoreText’s wght variation axis in resolvedFont, with NaN/infinity clamping and a safe fallback when variation resolution fails. fontSupportsWeightAxis uses CTFontCopyVariationAxes so the slider is disabled and dimmed for fonts without a weight axis.

paragraphStyle no longer sets lineBreakMode to word wrapping. A small unit test covers fontWeight default and JSON round-trip.

Reviewed by Cursor Bugbot for commit 84363a3. Bugbot is set up for automated code reviews on this repo. Configure here.

@davelab6

Copy link
Copy Markdown

Nice! Agreed that weight only is good to start; I suggest doing all axes for instance selection (so Kablammo or Recursive can pick a precis style desires) and tying optical size to font-size, as a middle step between this and motion typography.

Also STAT table support to select named styles from a drop-down, per axis, as demonstrated in lorp.github.io/samsa - would be another great waypoint.

Poppins and Skis are available as variable fonts btw

@animeshbhuria

Copy link
Copy Markdown
Contributor Author

Thanks! Agreed all of those are good follow-ups — wanted to keep this PR scoped to just weight so it’s easy to review. Happy to look at STAT/named-instance support as a next PR if that’s useful direction-wise. Also noted re: Poppins/Skia — makes sense, the bundled files here are static, but good to know variable versions exist.

TimLai666 added a commit to TimLai666/fronda that referenced this pull request Jun 23, 2026
…ontWeight, palmier-io#92 wordsPerCaption

- Add transcriptionLanguage field to Timeline (skip_serializing_if None)
- Add fontWeight (f64, default 400) to TextStyle
- Add wordsPerCaption param to add_captions tool input
- Add language param to inspect_media, get_transcript, add_captions tools
- Update tool descriptions per upstream
- Port all corresponding tests
@animeshbhuria
animeshbhuria force-pushed the variable-font-weight-final branch from a069601 to 5ebccc1 Compare June 25, 2026 17:05

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Want higher recall? High effort reviews run extra passes and find more bugs. A team admin can switch effort levels in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 5ebccc1. Configure here.

Comment thread Sources/PalmierPro/Inspector/Tabs/TextTab.swift
Comment thread Sources/PalmierPro/Models/TextStyle.swift Outdated
TimLai666 added a commit to TimLai666/fronda that referenced this pull request Jul 4, 2026
Adds fontWeight (400 regular / 700 bold, TextStyle field from PR palmier-io#65) to
the per-entry text styling parsed by cmd_add_texts, so bold titles can be
created directly. Schema updated; test now asserts weight 700 applies.
TimLai666 added a commit to TimLai666/fronda that referenced this pull request Jul 4, 2026
…ontWeight

set_clip_properties had grown to 12 positional Option args and still
could not set three TextStyle fields the model supports: font_weight
(PR palmier-io#65) and background/border fills (Issue palmier-io#18).

- Replaced the positional signature with a ClipPropertyUpdate struct
  (all callers updated; test call sites are now readable).
- Added font_weight, background, border handling; the executor parses
  fontWeight plus background/border objects ({enabled, color, padding,
  cornerRadius}) via a shared parse_text_fill helper.
- Reconciled the previously-orphaned mutation::validate_set_clip_properties,
  which had drifted to old field names (text/textColor/textAlignment/
  textBackground/textBorder, trimStart/trimEnd), to the executor + Swift
  convention (content/color/alignment/background/border, trimStartFrame/
  trimEndFrame). Its Issue palmier-io#18 / MUT-010 / MUT-012 tests updated to match.
- Schema documents the full text-clip field set.

New executor tests cover fontWeight, background (with padding/cornerRadius),
border, and invalid-fill-colour rejection. Closes the set_clip_properties
half of the flagged text-styling gap.
TimLai666 added a commit to TimLai666/fronda that referenced this pull request Jul 4, 2026
…ier-io#65 compat bug

Following the adversarial audit of the FCPXML keyframe/title output (which
confirmed the keyframe time axis and position top-left→centre coupling are
faithful):
- Title border now emits glyph strokeColor/strokeWidth (0.04*fontSize when
  border.enabled), closing a documented deferral. 1 test.
- Regression test: a keyframed clip starting at frame 100 emits clip-relative
  keyframe times (0s, 30/30s), not absolute — while the clip offset stays
  absolute (100/30s). Locks the verified axis.

Also records a real finding the audit surfaced: palmier-io#65 TextStyle DIVERGES from the
current Swift on-disk format. Swift stores isBold/isItalic (bools, bold-default,
fontName-inferred); Rust stores font_weight (f64, default 400) and reads
neither — so Swift-authored bold/italic text loads as regular in Rust and
Rust's fontWeight is dropped by Swift. Documented in the audit + porting tables
as a careful high-stakes data-model fix (serde bridge + is_italic field), not
rushed here. The FCPXML face is correct for Rust-authored data.
TimLai666 added a commit to TimLai666/fronda that referenced this pull request Jul 4, 2026
…r-io#254)

Extracted append_opacity_blend (static or keyframed <adjust-blend>) and shared
it between asset-clips and titles, so animated text opacity now round-trips too
(titles previously got static opacity only). 1 title-keyframe test; the
asset-clip opacity tests validate the refactor.

FCPXML palmier-io#254 geometry is now comprehensive for the Resolve target: static +
keyframed transform/crop/opacity, volume (incl. collapsed-pair partner gain),
enabled, source timecode, relink-by-filename, and text titles (font/color/
alignment/border-stroke/transform/opacity). Remaining: FCP-target value
encoding (needs a target-selection decision), no-explicit-transform auto-fit,
and the palmier-io#65 TextStyle isBold/isItalic on-disk-format compat fix.
TimLai666 added a commit to TimLai666/fronda that referenced this pull request Jul 4, 2026
…/isItalic)

The Rust TextStyle stored font_weight (f64) while current Swift (9a3ae50)
stores isBold/isItalic bools — so Swift-authored bold/italic text loaded as
regular in Rust and Rust's fontWeight was dropped by Swift (bidirectional
data loss on the .palmier format).

Implemented a `#[serde(from/into)]` TextStyleWire bridge:
- LOAD accepts EITHER fontWeight (Rust) OR isBold (Swift → 700/400); fontWeight
  wins when both present. isItalic loads into a new TextStyle.is_italic field.
- SAVE writes BOTH fontWeight AND isBold/isItalic, so a .palmier written by
  either app round-trips bold + italic. Additive — no key removed, so existing
  Rust readers are unaffected (the full-project round-trip test still passes).
- FCPXML title fontFace now reflects bold AND italic (Bold/Italic/Bold Italic/
  Regular), mirroring Swift fontFaceFallback.

is_italic ripples to the (few) full TextStyle literals; Default = false.
3 new compat tests (Swift keys load, both-key precedence, dual-write
round-trip) + the existing palmier-io#65 fontWeight test + full-project serde round-trip.

Surfaced 2026-07-05 by the adversarial FCPXML title audit.
TimLai666 added a commit to TimLai666/fronda that referenced this pull request Jul 4, 2026
The adversarial audit of the palmier-io#65 serde bridge confirmed it is faithful (no
data loss/corruption — every field verified both directions, backward-compat
additive, no panics), and flagged one gap: fmt_009_timeline_round_trip_
preserves_all_fields asserted only 5/13 TextStyle fields despite its name, so a
FUTURE dropped field would slip through. Now also asserts color, is_italic,
shadow (offset/blur), background, and border — so a wire-bridge regression that
drops a field fails this round-trip test.
TimLai666 added a commit to TimLai666/fronda that referenced this pull request Jul 4, 2026
TimLai666 added a commit to TimLai666/fronda that referenced this pull request Jul 10, 2026
…r-io#169 palmier-io#67 palmier-io#164)

palmier-io#45 arrow/line shapes: compositor rasterize_line_or_arrow — endpoints
normalized 0..1 of the box (documented assumption; shapes are Rust-native,
no Swift rasterizer), horizontal-centre default, Arrow barbs scaled to
stroke width. 3 golden tests.

palmier-io#65 variable-font wght: ab_glyph 0.2.32 supports the wght axis
(VariableFont::set_variation) — applied in render_text (no-op on static
faces). Wired the bundled variable families (Inter/Geist/GeistMono/DMSans/
Caveat/PlayfairDisplay/SpaceGrotesk) into font_for so the axis is reachable,
matching Swift BundledFonts. 2 tests.

palmier-io#169 viewer guides: completed the Guides dropdown UI over the existing
palmier-io#167 overlay/state — guide_menu_rows + ⊞ button toggle + toggle_guide,
mutually exclusive with the settings menus. Pure-function test.

palmier-io#67 project-card Duplicate: card menu entry (accessible-gated) → host-side
plan_duplicate + recursive package copy → "(Copy).palmier" + recents
register + Home cards refresh. Menu + fs-copy tests. Hub save_now() absent
(data slice palmier-io#211) — Home-transition autosave deferred.

palmier-io#164 shortcut parity: added [ / ] trim aliases and Shift+Backspace ripple
delete (menu.rs route table 44->47, global_shortcuts !input bindings,
app_root RippleDeleteSelection handler). V/C/A/Shift+A/Esc deferred — no
tool-mode/deselect concept yet (gap list in tasks.md).

Gates (exit 0): render_core lib 263, app_shell_gpui desktop-app lib 407,
cargo check --bin fronda.
TimLai666 added a commit to TimLai666/fronda that referenced this pull request Jul 10, 2026
palmier-io#211 autosave, palmier-io#138 HDR export, palmier-io#176 duplicate_clips (56→57), palmier-io#284 aspect
labels, palmier-io#45 arrow/line shapes, palmier-io#65 wght variable-font axis, palmier-io#169 viewer
guides, palmier-io#67 project-card duplicate, palmier-io#164 shortcut parity (+ cross-slice
autosave/aspect wiring). Deferred (feature-absent, documented): V/C/A tool
shortcuts, HDR runtime verify (no libx265 in CI).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support variable fonts for motion typography

2 participants