Files
oak-editor/crates/oak-timeline
Mike-Solar 4a2614b3fc timeline: adjustment layers and first-class transitions
Adjustment layers (docs/zh/plans/adjustment-layers-and-transitions.md):
a new timeline block type whose effect chain grades the composite of
every video track below it, over its own range (spanning clips or a
slice of one). The graph path flushes the lower tracks at the block's
track boundary and sweeps the composite through the chain via a
transient texture-source node; the montage path mirrors it with
AdjustmentSpan tickets (wire-compatible), so worker previews and
exports agree. An empty-area context menu creates one; the block
trims/moves/deletes like a clip, with undo everywhere.

Transitions: seam blocks come alive - cross dissolve/fade/wipe/slide
evaluate both neighbors through the graph path with progress from the
transition's own range (never the whole clip). Ctrl+Shift+D or the clip
menu inserts a default transition; the gpui wedges render and drag to
resize offsets undoably, and TransitionRemoveCommand now restores
offsets and edges on undo. The transitionfx node form runs the same
shaders on an adjustment layer with progress_in auto-filled from the
layer's span (explicit value wins).

Also: every built-in effect name and parameter name is now
translatable (360 node.* keys per locale, zh-CN fully translated, two
coverage tests guard future gaps); the new nodes register in
nodes/mod.rs with the factory smoke table updated; textfootage and
adjustment-layer i18n keys included.
2026-09-10 22:03:15 +08:00
..
2026-09-03 17:42:20 +08:00

oaktimeline Rust crate (declaration draft, for review)

Status: declaration draft. Signatures + doc comments are the spec; every body is todo!(). Not wired into any build. The crate template (FFI discipline, testing layers) follows crates/oakplugin/README.md and crates/oaknode/README.md.

Scope

Replaces the C++ oaktimeline module (src/timeline/src): timeline markers and work areas, the timeline undo-command family (add/remove tracks, place/trim/split blocks, ripple edits, slide, gap insertion), and the shared Timeline namespace / utility helpers.

Public contract: include/timeline/*.h (error.h, displaymode.h, marker.h, workarea.h, edit.h) — frozen, implemented verbatim by src/ffi.rs. The timeline value handles (OakTimelineMarkerList, OakTimelineWorkArea) and every edit command are exported through this ABI only; consumers (the facade/app, the oaknode crate) never see the internal Rust types.

Key architectural decisions (C++ → Rust mapping)

  1. Domain modules mirror the C++ header files. The C++ module is a flat set of headers, not a deep class hierarchy, so the Rust crate keeps one module per C++ header family (common, marker, workarea, undocommon, undotrack, undogeneral, undopointer, undoripple, undosplit, util). COVERAGE.md maps every C++ type to its Rust home; review that first.

  2. No C++ UndoCommand subclass hierarchy. Following the oaknode crate decision (#4), each undo command is a plain Rust struct that exposes prepare() / redo() / undo() (all todo!() here) and is surfaced to the world through the oakundo C ABI vtable (bridge::undo::oakundo_command_init, Rust callbacks as userdata). Every command struct carries a to_command() factory that wraps it into an oakundo::undocommand::UndoCommand value for the undo stack.

  3. Value types come from oakcore-rs. Markers and work areas are built on Rational/TimeRange, so the crate depends on oakcore-rs (crates/oakcore) exactly like oaknode does; no pixel/sample formats are involved here.

  4. All cross-module access goes through the C ABI. Per the project rule (no cross-module C++ member calls), timeline commands touch the node graph exclusively through the oaknode C ABI (bridge::node), undo through the oakundo C ABI (bridge::undo), and XML/config through the oak_core C ABI (bridge::common). The C++ internal helpers oakundo_capi::make_command_handle / oaknode_c_api::to_native are not replicated in Rust — their role is subsumed by vtable commands and by value handles treated as opaque.

  5. Handles. handle.rs keeps the RefBox/CHandle scaffolding for the oakengine facade boundary (the C ABI export layer is the only place left that talks handles). Every make_owned value handle boxes an Arc<Mutex<T>> — the same pattern as the oaknode project handles — so the crate's commands hold the shared marker list / work area as a plain Rust Arc<Mutex<…>> and the facade entries convert the handle back to the Arc at the boundary.

Layout

src/
  lib.rs         crate doc + module map
  error.rs       error codes (mirrors include/timeline/error.h)
  handle.rs      refcounted-handle scaffolding (same pattern as node)
  common.rs      Timeline namespace (MovementMode/ThumbnailMode/
                 WaveformMode, EditToInfo) — timelinecommon.h
  marker.rs      TimelineMarker/MarkerList + 5 marker commands
  workarea.rs    TimelineWorkArea + 2 workarea commands
  undocommon.rs  node/block remove helpers + MultiUndoCommand
  undotrack.rs   track ripple/prepend/insert-after/replace commands
  undogeneral.rs resize/media-in/add/remove-track/transition/gap/
                 enable-disable/insert-gaps/default-transition commands
  undopointer.rs BlockTrimCommand/TrackSlideCommand/TrackPlaceBlockCommand
  undoripple.rs  ripple remove-area / ripple-tool / delete-gaps commands
  undosplit.rs   BlockSplitCommand/BlockSplitPreservingLinksCommand/
                 TrackSplitAtTimeCommand
  util.rs        timelineutil.h inline helpers (rat_nd, same_*,
                 free_detached_handle, block/track queries)
  bridge/        C ABI imports: node.rs, undo.rs, common.rs
  ffi.rs         include/timeline/*.h export layer
tests/           contract + golden tests (see README test section)

Hard rules for the implementer

  1. Every extern "C" body goes through handle::guard*; no panic crosses FFI.
  2. Timeline objects never outlive their owning node; borrowed handles are created under a guard that owns the node reference.
  3. Behavior parity with C++ is proven by the C ABI test-suite (src/timeline/tests, unchanged) plus the golden tests in tests/ (XML save/load formats captured verbatim from src/timeline/src/timelinemarker.cpp / timelineworkarea.cpp).
  4. Where C++ behavior is genuinely load-bearing but ugly (e.g. marker list kept sorted by time, ripple's compensation gap rules), port the behavior, not the aesthetics; leave a // CPP-PARITY: comment with the C++ file:line.

Dependency policy

Prefer mature third-party crates (MIT/Apache-2.0/BSD, GPL-compatible) over hand-rolling; register each addition (name + reason) here. Large existing C++ libraries (OTIO, OCIO, OIIO, FFmpeg) are NEVER rewritten — they are consumed through their C ABI / bridge layers.