Skip to content

Light Mode, Render+Replace - #45

Merged
kmwallio merged 8 commits into
mainfrom
beta
Jun 29, 2026
Merged

Light Mode, Render+Replace#45
kmwallio merged 8 commits into
mainfrom
beta

Conversation

@kmwallio

Copy link
Copy Markdown
Owner

No description provided.

kmwallio and others added 8 commits June 3, 2026 14:35
Add a Preferences → General → Appearance section with a Theme dropdown
(System / Light / Dark) and an accent color picker, applied live with no
restart.

- Tokenize src/style.css to @define-color refs (~180 hex literals → ~14
  families); media-viewport surfaces and semantic status colors stay literal.
- New src/ui/theme.rs builds the stylesheet at runtime (light/dark header +
  accent with derived hover/active shades) into one re-loadable CssProvider.
  System mode resolves and watches the XDG org.freedesktop.appearance
  color-scheme portal (live SettingChanged, dark fallback).
- Accent drives CSS chrome plus the Cairo timeline selection highlight via
  runtime accent in colors.rs (set_selection_accent + selection_fill/border,
  replacing the COLOR_SELECTION_* consts).
- ThemeMode (default Dark) + accent_color persist in PreferencesState with
  backward-compatible serde defaults.
- Timeline / program-monitor / scopes canvases stay on the dark editing
  palette in all modes (standard NLE behavior).

9 new unit tests; 1466 tests pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add a fourth "High Contrast" option to Preferences → General → Appearance
(ThemeMode::HighContrast), built on the runtime theme system:

- Pure-black palette (HC_HEADER): black surfaces, white text, bright #ffffff
  borders.
- HC_EXTRA rules, appended only in this mode: prominent :focus-visible accent
  outlines, stronger control borders/separators, and small-text floors that
  raise the 10/11px utility classes to a readable 13px without shrinking
  display text (preserves the type hierarchy).

Applies live with no restart; timeline/monitor/scopes stay dark; accent drives
the focus/selection colors. +1 unit test; 1467 tests pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Groundwork for native-Rust AAF (Pro Tools / Avid) export. AAF is a binary
Compound-File container wrapping the AAF object model; every file must embed
the full AAF MetaDictionary, which readers (pyaaf2, the Avid SDK) require.

Rather than reimplement the fixed metadictionary, embed a pyaaf2-generated
skeleton (header + full metadict + empty ContentStorage.Mobs) and append the
dynamic Mob graph into it with the `cfb` crate.

- Add `cfb = "0.14"`; register `mod aaf;`.
- src/aaf/{mod,ids,encode,writer}.rs scaffold + embedded template.aaf.
- encode.rs: AAF properties-stream encoder + UTF-16 string encoder.
- ids.rs: stored-form codes + property-stream version constants (from pyaaf2).
- writer.rs: write_aaf / build_aaf_bytes entry points (skeleton passthrough).

Risk retired: the Rust cfb crate opens pyaaf2's 450KB container, reads its
storages, and round-trips it with added storages/streams. 4 new tests; 1471
tests pass. Object-graph encoders (slots, sequences, source clips, descriptors,
index streams, weak refs) follow.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Decode the AAF structured-storage binary format from a pyaaf2 reference file
and implement the encoders, each asserted byte-for-byte against the captured
reference bytes:

- encode.rs: scalar encoders (u32, i64, Rational, 32-byte MobID w/ fixed
  prefix), UTF-16 strings, weak object references (refIndex/keyPid/key), and
  both strong-ref index-stream formats — vector (count/nextfree/keys) and set
  (+ keyPid/keySize + per-entry localKey/refCount/key).
- ids.rs: class CLSIDs, property IDs, stored-form codes, and DataDefinition
  AUIDs (Sound/Picture/Timecode), ported from pyaaf2 (MIT) and cross-checked
  against the reference.

Key finding baked in: the embedded skeleton already carries the full
metadictionary, the Dictionary DataDefinitions, and the /referenced properties
path for DataDefinition (index 2) — so the writer only appends Mobs and never
touches those shared structures (ContainerFormat omitted as optional).

+8 tests (all assert vs pyaaf2 reference bytes); 1479 tests pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Build the dynamic AAF object graph into the embedded skeleton with the cfb
crate: a CompositionMob (one TimelineMobSlot per audio track — a Sequence of
Filler gaps + SourceClips — plus a flattened video-reference slot and a
Timecode slot) and one SourceMob per unique source file (PCMDescriptor for
audio, ImportDescriptor for the video reference) each with a NetworkLocator to
the external file:// URL. Linked media; no embedded essence.

Strong-ref sets/vectors are appended by writing <base>{N} storages + rewriting
the <base> index stream; the parent property streams are untouched. CLSIDs set
via uuid (cfb's GUID-LE field order round-trips pyaaf2's class ids). Edit-unit
lengths: samples for audio, frames for video (reusing edl timecode helpers).

Validated end-to-end: pyaaf2 reads the output with full fidelity — tracks,
edits, sample-accurate clip lengths, gap fillers, MobID references, linked
media URLs, and timecode. New tools/validate_aaf.py (pyaaf2 round-trip) is the
dev/CI gate. +2 Rust tests; 1481 tests pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- toolbar.rs: "Export AAF…" entry in the Export ▼ popover (FileDialog, *.aaf
  filter) calling crate::aaf::writer::write_aaf, mirroring Export EDL.
- MCP save_aaf tool: schema + dispatch (server.rs), SaveAaf command (mod.rs),
  handler (mcp_handler.rs).
- cargo-sources.json: vendor cfb + web-time (archive + checksum) for Flatpak
  offline builds.
- CHANGELOG + ROADMAP (line 703 checked off).

App launches cleanly with the new menu; pyaaf2 validates exported files.
1481 tests pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@kmwallio
kmwallio merged commit 9f28cae into main Jun 29, 2026
2 checks passed
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.

1 participant