- localtime_s/gmtime_s are MinGW header inlines, not symbols — link
_localtime64_s/_gmtime64_s
- copier_test also segfaults only on the Linux runner; add it to the
on-failure gdb backtrace
- timeformat node: localtime_s/gmtime_s (reversed args, 64-bit time_t)
on Windows — MinGW has no localtime_r/gmtime_r
- the multithread suite test passed non-NUL-terminated property names
(str::as_ptr) to the C property suite — UB that resolved to garbage
lookups on the CI runner
- libsnappy off on the MinGW FFmpeg build (only feeds the hap encoder;
its pkg-config entry does not reach the static link)
- ValueType::Parametric; the node input carries the whole curve set as
NodeValue::Text(JSON) so undo and project serialization come for free
- translation pass builds the input with the default-curve JSON and the
dimension/range/ui-colour properties
- edits flow both ways: node input (UI) -> curves_from_json ->
set_ofx(Parametric) on the instance; plugin-side Set/Add/Delete ->
notify_instance_changed -> JSON written back to the input (undoable)
- screenshot example: gate the macOS-only offscreen capture items so
the workspace tests build on Linux/Windows
- serializer resolves node types through the factory's dynamic
(runtime-registered OpenFX) entries, so a project carrying plugin
nodes loads again (was: "unknown node type"); covered by a new
CI-gated round-trip test driving the real fixture plugin
- built-in effect nodes expose their inputs as inspector parameters
like the C++ parameter editor: localized input names from the
behavior, combo option tables via the new
NodeBehavior::input_combo_strings (16 nodes, string-for-string from
the C++ set_combo_box_strings), connection/data inputs excluded
- effect library: live drag-and-drop — onto the inspector's effect
stack (lands at the indicator position) and onto the node editor
canvas (creates the node at the drop point); double-click still
appends to the selected clip
- inspector parameter controls are no longer recreated per render
(gpui stack view caches them per effect), so sliders drag and
checkboxes click; the view observes the engine and silently re-syncs
values (undo/redo land on the widgets)
- timeline: left-press selects clips (plain/keep-multi/Ctrl-Cmd
toggle); clip moves clamp the shared delta so no clip of a linked
group lands before frame 0 instead of failing with "invalid move
target"
- oakplugin: createInstance-rejected instances skip the destroyInstance
notification (the plugin never owned them); vendor-suite fetchSuite
misses moved behind OAK_OFX_TRACE; the worker logs the discovered/
registered plugin counts
- CI: the OFX probe step also runs the serialization round-trip test
- gpui submodule: params view caching, clip click-select, library
drag payload, graph_position_at
- Right-clicking an unselected clip selects it first (C++ parity) —
this is what made Cut/Delete appear to do nothing.
- Cut/Copy/Paste clipboard: clipboard_copy/cut/paste on the engine,
clipboard clips keep footage/range/speed/track kind and stay linked
in the pasted group; paste lands at the playhead as one undo entry.
- Dropping a video-with-audio footage places the video clip plus a
linked audio clip at the same range in ONE 'Add Clip' undo entry.
- Add Video/Audio Track buttons in the timeline toolbar and the track
header context menu; new sequences start with 2 video + 2 audio
tracks (not an undoable edit).
- oaknode Graph::add_entry now reclaims the slot from the free list —
before, a detached-then-reattached node left its slot in the free
list, so node_count undercounted and the next add_node silently
clobbered the restored node. This was the user's 'undo, redo, undo,
redo and the result changed' bug; regression covered by cycle tests
(move/trim/delete/split/add-track/linked-placement all converge).
With the C ABI facade (oakengine) retired, the frozen-ABI rationale is
gone. UndoCommand now boxes a Send Command trait (new/from_closures/
multi), dropping OakUndoCommandVtable, the userdata trampolines, the
refcount shell, the handle module, and all undostack_* handle exports.
The global facade loses its raw-pointer out-params (can_undo/can_redo
return bool, command_name returns String). oaktimeline/oaknode/
oakplugin/oaktask construct commands directly via UndoCommand::new.
oakundo src is now free of unsafe; behavior (ordering, idempotence,
done flags, groups, observers, 200-row cap) is unchanged and pinned by
the rewritten tests.
- oaktimeline::multicam: clip_find_multicam (buffer/tex_in depth-1
lookup), multicam_enable/disable (rewire sequence<->clip through a
MultiCamNode), multicam_switch (split-preserving-links at the
playhead, each half owns an independent multicam copy, linked clips
switched together) as single undo commands with C++ labels.
- BlockSplitCommand now duplicates the clip's whole dependency graph
(copy_node_and_dependency_graph_minus_items) instead of just the
block core, matching the C++ BlockSplitCommand::prepare semantics;
undo detaches the copied subgraph, redo re-attaches identity-
preserving.
- oaknode: fix serializer dropping edges from the first-created node
(ptr=0 was not registered in id_map), restoring sequence_in edge
round-trips; multicam node and clip wiring serializer round-trip
tests.
Proxy: preview-path proxy substitution (global UseProxyMedia AND
per-footage enabled AND on-disk ready; export always uses originals),
proxy generate/delete/reveal/enable actions, ProxyDialog with global
and per-footage custom params, Tools menu + context-menu Proxy
submenus, progress in the status bar, OVE serialization of proxy
metadata and source_start_time.
Sync: timeline context-menu Synchronize by Source Time / by Waveform /
by Waveform (Adjust Speed) with ctrl-shift-w, cache-envelope
extraction with validity masks, reference/anchor selection and
single multi-undo application (replace-with-gap, speed adjust,
re-place) mirroring timelinewidget.cpp semantics.
FootageBehavior now keeps the probed stream list (video/audio, per-stream
duration in rationals) instead of dropping it, and the probe CLI walks
that inventory to report real durations, frame rates and stream counts
rather than the previous zero placeholders.
Module-internal object references are Rust types now (values, Arc,
Mutex); CHandle remains only at the oakengine C-ABI boundary:
- oakundo: the global stack holds UndoStack/UndoCommand values
directly (stack token is the static's address)
- oaktimeline: marker/workarea boxes carry Arc<Mutex<T>>; commands
share the same allocation through Arc clones (readers in oakengine
stubs and the app's graphops updated to lock)
- oaktask/oakstorage: sessions, write-through bindings and the
database backend pass ProjectArc; the Session drops its manual
release bookkeeping; nodeutil keeps the CHandle<->Arc boundary
conversion (release_project restored for the app)
- oakcodec: handle.rs deleted outright (no facade entry needed it);
texture/block placeholders are unit structs
- oakrender: copier's project handle is an identity u64; alive-count
machinery removed; handle.rs is make_owned/get/get_mut only
- oakplugin: the instance registry is gone (its unregister key never
matched, leaking weak entries); handle.rs is the RefBox boundary type
- oaknode/oakcommon: only dead guard/borrow helpers removed; external
payload handles (texture/processor) documented as the boundary
Flake hunts landed along the way: the audio recording test serializes
on the shared manager lock with a normalized state; the autocacher
cancel test uses a slow producer so cancellation is deterministic.
Single-lib cleanup: the per-crate src/bridge/ and src/ffi.rs layers are
gone (oakundo/oakcommon/oaknode/oaktimeline/oakcodec/oakaudio/
oakrender/oaktask/oakplugin/oakstorage); cross-crate calls are plain
Rust, CHandle marshalling shrinks to the oakengine boundary, and tests
call the Rust APIs directly (pure C-ABI wrapper tests removed where
the domain layer already covers the behavior).
exporter.h family implemented: oakengine_export_render (CLI contract),
oakengine_export_render_with_params (was a stub), last_error and
progress callback; synchronous path reuses task_create_export +
start_sync. Fixes on the way: oaktask video ticket self-deadlock,
audio params dropped on the export path, codec encoder AAC slicing and
H.264 time base. Real-mp4 tests cover both entry points, progress and
the illegal-argument matrix.
Also: oakstorage session maps null project handles to None (version-
info path), configstore test double literal 3.14 -> 3.15 (clippy PI
lint), oakaudio output callback scratch buffer + env-aware P1 test,
cli media round-trip test uses a generated 16-frame clip (no more
minute-long debug runs).
oakstorage (new workspace member): URI dispatch, pluggable backends
(ove-xml built in, otio/fcpxml via oakotio, C-vtable foreign
registration), the M10 C API surface, version info codes, last-error
and alive accounting; round-trip tests per backend.
oaknode serializer: persists the full timeline — sequence track lists,
track block lists, block ranges/media_in/speed/flags, clip footage
references, footage filename+streams, folder children — through
<custom> behavior hooks with two-phase reference resolution; loads the
C++ <olive><project><layout> containers (golden: tests/
project_with_footage.ove); round-trip is field-by-field and
byte-idempotent.
- oakengine_sequence_move_clip implemented for real (oaktimeline
TrackMoveBlockCommand; fixes the graph-ownership/gap-anchor/ripple
trim bugs the stub was hiding); same-track via the frozen C ABI,
cross-track supported by the module command
- oaknode clip blocks now declare a tex_in texture input and set
effect_input to it, so timeline clips can host effect chains; facade
test covers effect insert/remove on a real clip
- oakffmpeg-link: FFMPEG_DIR is now mandatory with a clear panic (a
Homebrew upgrade left the system ffmpeg .pc pointing at a deleted
dav1d Cellar path, breaking links); reads a git-ignored workspace
.env for IDEs that cannot inject env vars (RustRover); links the C++
stdlib for C++ codec libs (svt-av1)
- oakengine re-exports oaknode so tests share one crate instance;
it_node uses the direct instance's value type where it calls the
module FFI (the --workspace dev-dependency feature split builds
oaknode twice)
facade (oakengine/oaknode, API only extended):
- oaknode_node_get_effect_input / oaknode_node_get_flags module exports
- oakengine_clip_as_node, oakengine_node_effect_count/at/insert/remove/
move/set_enabled, oakengine_node_identity/is_enabled/get_type_id,
oakengine_node_factory_id_at — chain edits wrap disconnect/reconnect
into single undo groups; insert/move/remove validated against the
factory and the host's effect input
- it_node covers enumeration/insert/remove/move/toggle/undo-redo and
the NULL/illegal-input matrix
app:
- RealEngine binds the selected clip's effect chain to the inspector's
effect stack (cards, enable, reorder, remove, expansion state), all
edits undoable through the facade
- AppEngine gains set_selected_clips / addable_effects / add_effect
(default no-ops keep the mock untouched); the timeline selection
drives the stack target; the inspector gets a small add-effect menu
- known module gap (documented): ClipBlockBehavior sets no effect_input
yet, so timeline clips cannot host effects until oaknode grows tex_in