Commit Graph
7213 Commits
Author SHA1 Message Date
Mike-Solar 3efdee5a10 fix: share one language-test mutex; read the OFX marker via Win32 env
i18n: test_lock() and lang_test_lock() used to be TWO different static
mutexes, so the i18n tests and the ~30 app/actions/dialogs tests that
mutate the language global never excluded each other. Windows thread
scheduling exposed the race: tr_falls_back_to_english_then_the_key got
the English value because another test flipped the language mid-assert.
Both entry points now lock the same mutex.

oak_test_plugin.c: on Windows the plugin DLL has its own CRT
environment block, so getenv() never sees what the host's
std::env::set_var set via SetEnvironmentVariableW — the interact
lifecycle test's marker file stayed empty ("lifecycle actions
missing: []"). Read the marker path through GetEnvironmentVariableA
on _WIN32.
2026-08-21 13:36:19 +08:00
Mike-Solar d9dc16ec26 cd: bundle all runtime dylibs on Windows/macOS; version from workspace
Windows: tooling/package/bundle-dylibs-windows.sh collects the MSYS2
runtime DLLs (libstdc++, libgcc, OpenColorIO, ...) with ntldd -R,
iterated to a fixpoint over freshly copied DLLs; a packager resources
glob places them next to the executables in the NSIS installer.

macOS: tooling/package/bundle-dylibs-macos.sh copies every non-system
dylib otool reports into Contents/Frameworks, rewrites the install
names to @executable_path/../Frameworks to a fixpoint, and ad-hoc
re-signs every modified Mach-O (rewriting invalidates the seal).

The CD package version no longer comes from the git tag: the root
Cargo.toml gains [workspace.package] version = "0.5.0", the oak
package inherits it (version.workspace = true — which cargo-packager
also picks up), and the Linux container packaging parses that field.
2026-08-21 12:53:24 +08:00
Mike-Solar 498669509a docs: current bilingual build guide; retire the C++/CMake one
docs/build.md + docs/zh/build.md rewritten for the Rust workspace:
project-built FFmpeg 8.1 (.cargo/config.toml presets FFMPEG_DIR),
vendored static OCIO on Linux/macOS vs MSYS2 dynamic OCIO on Windows
(with the OCIO_INSTALL_DIR/OCIO_RS_LINK env), the Windows GNU toolchain
requirements (MSYS2 Rust, RUSTFLAGS=-C link-args=-lmsvcrt for the
mingw-w64 _assert forwarding, unset INCLUDE/LIB), Linux audio dev
packages and xvfb headless testing, container packaging, and a
troubleshooting section. The macOS-only guides gain a deprecation
pointer. Also correct two stale comments in tooling/install-deps.sh
(FFmpeg is built by tooling/ffmpeg/build-ffmpeg.sh, not by cargo).
2026-08-21 12:53:11 +08:00
Mike-Solar eacc47930a tests: assemble the OFX fixture bundle correctly on Windows
The fixture plugin binary was copied into Contents/Linux-x86-64 under
the extension-less name "plugin" on every non-macOS platform. On
Windows the host never loads it: LoadLibrary appends .dll to
extension-less module names, so the scan found the bundle but produced
no plugin — and because the (passing) draw-overlay test scans first,
the path dedupe then hid the failure from the lifecycle test, which
died with "interact variant instance: NotFound". Use Contents/Win64
and plugin.dll on Windows in both bundle assembly sites.
2026-08-21 12:53:11 +08:00
Mike-Solar e163702a6b fix: entry-point-less test binaries on Linux; gate timeformat test FFI
oakrender/build.rs: -Wl,-export_dynamic is the macOS spelling. Since
Rust 1.90 x86_64-unknown-linux-gnu links with rust-lld by default, and
lld parses the single-dash form as '-e xport_dynamic', every oakrender
integration test binary was linked with NO entry point and died with
SIGSEGV inside ld.so's dl_main (jumping to the image base) before
printing anything — the copier_test CI failure. Emit the flag on macOS
only.

oaknode timeformat: value_localtime_flag_routes_to_localtime_r called
localtime_r/gmtime_r directly, which do not exist on Windows. Factor
the cfg-gated FFI (localtime_r/gmtime_r vs _localtime64_s/_gmtime64_s)
into break_down_time() and use it from both value() and the test.
2026-08-21 11:56:24 +08:00
Mike-Solar 8660cbcf97 ci: unbreak Windows tests; sharpen Linux loader-crash forensics
oakcodec: gate find_ffmpeg_searches_path to unix (chmod 0755 + shebang
fixture) and make find_ffmpeg_missing_returns_empty assert absoluteness
instead of a '/' prefix so the tests compile and pass on Windows.

ci (Windows): export RUSTFLAGS=-C link-args=-lmsvcrt in the build and
test steps. mingw-w64 (Nov 2025) forwards _assert to __msvcrt_assert
inside libmingwex.a, and rustc's link order leaves -lmingwex last, so
binaries that pull _assert.o (oakcommon's real_ocio test) fail to link;
a trailing -lmsvcrt re-scans the CRT import lib afterwards.

ci (Linux): copier_test dies inside ld.so before printing anything.
Replace the LD_DEBUG probe with stronger forensics: exported dynsyms
(interposition suspects), strace tail, valgrind tail, and siginfo
(si_code/si_addr) from the gdb run.
2026-08-21 11:17:39 +08:00
Mike-Solar bea50f4d80 oakaudio: drop asio-sys from the lock (feature removal follow-up) 2026-08-21 10:37:22 +08:00
Mike-Solar e837277557 ci: fix Windows asio-sys link failure; improve Linux crash diagnostics
oakaudio: drop cpal's `asio` feature. asio-sys needs the proprietary
Steinberg ASIO SDK at link time (undefined ASIOGetSamplePosition etc.
on the GNU toolchain); WASAPI remains the Windows backend.

ci: the failure-only gdb step passed test args without --args, so gdb
treated --nocapture as a core file. Also collect loader-stage evidence
for the copier_test dl_main SIGSEGV: IRELATIVE reloc count, LD_DEBUG
tail, full backtrace and registers.
2026-08-21 10:29:54 +08:00
Mike-Solar b5a2ea7697 ci+fix: link the exported UCRT time symbols; gdb over copier_test too
- 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
2026-08-21 09:51:22 +08:00
Mike-Solar 7fb9c92931 ci+fix: UCRT time functions on Windows; skip libsnappy on MinGW; NUL-terminated names in the mt test
- 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)
2026-08-21 09:17:29 +08:00
Mike-Solar 032a3a559b ci+tests: skip libopenh264 on MinGW; fix UB varargs in the message-suite test
- FFmpeg for Windows no longer enables libopenh264 (redundant with the
  native h264 decoder + x264 encoder; its MinGW packaging does not
  satisfy the static link — Wels* undefined references)
- suites_test's question-type call passed a 3-placeholder format with
  one variadic arg — UB that vsnprintf turns into a SIGSEGV on glibc
  (masked on macOS); use a placeholder-free format
2026-08-21 08:41:54 +08:00
Mike-Solar cd95514cf8 ci: skip -ldl on MinGW links; widen the gdb backtrace to the plugin test binaries
- oakffmpeg-link forwards pkg-config --static --libs verbatim; FFmpeg's
  .pc files can list -ldl via external deps, and MinGW has no libdl
- suites_test segfaults on the Linux runner too; run both plugin test
  binaries under gdb on test failure
2026-08-21 08:09:11 +08:00
Mike-Solar 2794f8bb80 fix(plugin): guard plugin calls across host shutdown generations
Tests (and any host that shuts down then rescans) can hold instances of
a PREVIOUS plugin generation; their entry points dangle after dlclose,
so the next shutdown's destroyInstance notification jumped into
unmapped memory (SIGSEGV on Linux; masked on macOS). Plugin gains an
 flag set by unload_all before dlclose; call_action/call_entry
fail fast instead of calling into freed code.
2026-08-21 07:33:18 +08:00
Mike-Solar 74b18ea71a ci: gate ocio-sys' forced MSVC includes behind OCIO_RS_NO_MSVC_INCLUDES
The crate's build.rs unconditionally adds the MSVC + Windows SDK
include dirs on Windows (for MSVC hosts); on the GNU toolchain that
breaks the bridge compile with MSVC-only headers. The runner's job
hook re-exports INCLUDE/LIB per step, so the in-step unset did not
help — patch the extracted build.rs instead (both the env-var failure
modes are now documented in the step comment).
2026-08-21 07:08:31 +08:00
Mike-Solar ed7389ea95 ci: clear hook-injected MSVC env in-step; gdb backtrace for the Linux segfault
- the warp runner's job hook re-exports MSVC INCLUDE/LIB per step, so
  the GITHUB_ENV clear did not stick — unset in the Build/Test steps
  themselves
- node_e2e_test segfaults only on the Linux runner; rerun the binary
  under gdb on failure to capture the native stack
2026-08-21 06:38:11 +08:00
Mike-Solar f6e7bf20a4 cd: the package builder scripts (ignored by the *build-* glob)
Force-added: .gitignore's *build-* pattern matches the filenames.
2026-08-21 05:59:41 +08:00
Mike-Solar 32cd8f46f0 cd: container-native Linux packaging (deb/rpm/pacman) + AppImage
Each distro package builds inside that distro's container so declared
dependencies always resolve to native names: hand-rolled deb via
dpkg-shlibdeps + dpkg-deb, rpm via rpmbuild's auto-requires, Arch via
makepkg (non-root builder user). git/curl install before checkout
(container jobs). AppImage keeps cargo-packager on the Ubuntu runner.
The release gates on all four package jobs plus macOS/Windows.
2026-08-21 05:58:43 +08:00
Mike-Solar 551909df62 ci: clear MSVC INCLUDE/LIB for the GNU build; worker test helper fixes; hw tolerance
- the Windows runner image exports MSVC's INCLUDE/LIB; cc-rs was
  appending the MSVC SDK headers to MinGW compiles (vcruntime.h not
  found)
- oak-worker handshake test helper advertised the input pool's total
  byte size as per-slot data bytes (macOS tolerated the oversized
  attach; Linux correctly rejects it)
- hw/sw decode comparison tolerance 0.05 -> 0.08 (VideoToolbox's
  YUV->RGB legitimately differs by ~1 LSB of intermediate depth)
2026-08-21 05:52:30 +08:00
Mike-Solar 738120bffc ci: Windows uses the MSYS2 OpenColorIO (2.5.2, dynamic); test diagnostics
- the vendored OCIO source needs MSVC-only constructs (wide-path
  ifstream); MSYS2's mingw build of the exact 2.5.2 the bridge targets
  is the sane Windows path — DLLs get packaged next to the binaries
- oak-worker handshake test prints the error response on failure
  (CI-only attach failure needs the message)
2026-08-21 05:17:54 +08:00
Mike-Solar f6a7dfd0b3 feat(ui): curve editor for OFX parametric parameters
Each dimension of a parametric param renders as a CurveEditor in the
inspector (bezier handles map to the Hermite slopes of the host curve
model; edits serialize back through the JSON mirror — undoable and
project-persisted). The engine re-sync skips in-progress drags and
identical curves so the per-render sync neither steals gestures nor
loops. Also: physical-memory probe for the worker-count policy on
Windows (GlobalMemoryStatusEx).
2026-08-21 04:42:33 +08:00
Mike-Solar 13b1799c71 ci: resolve CARGO_HOME via cygpath for the ocio-sys patch; gate screenshot example to macOS
- rust-toolchain sets CARGO_HOME to the Windows userprofile path while
  the msys2 shell's HOME is elsewhere — the yaml-cpp patch targeted an
  empty directory and the assertion ls failed
- examples/screenshot.rs uses the macOS-only VisualTestAppContext; its
  items are now cfg-gated with a non-macOS stub main so workspace test
  builds pass on Linux/Windows
2026-08-21 04:25:43 +08:00
Mike-Solar a43302d9b7 feat(plugin,node): bridge parametric params into the node/inspector path
- 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
2026-08-21 04:20:14 +08:00
Mike-Solar 980c41acec feat(plugin,codec): Windows plugin loading and ffmpeg discovery
- oakplugin host: Win32 LoadLibraryExW/GetProcAddress/FreeLibrary
  backend (LOAD_WITH_ALTERED_SEARCH_PATH so bundle-sibling DLLs
  resolve), same dl_open/dl_sym/dlclose surface — the POSIX path is
  untouched; OFX hosts now compile on Windows
- proxymanager: PATH split via std::env::split_paths (Windows ';'),
  ffmpeg.exe name, Windows candidate locations; split logic unit
  tested
2026-08-21 04:06:53 +08:00
Mike-Solar 4f3c140b28 feat(render): Windows shared memory (CreateFileMapping/MapViewOfFile)
SharedMemoryRegion gains a Win32 backend behind the unchanged public
API: Local\OakShm<key> names, OpenFileMapping for attach, VirtualQuery
for the size check, UnmapViewOfFile/CloseHandle for teardown. Semantic
differences from POSIX are documented: unlink_key is a no-op (the
kernel destroys the object with the last handle, so crashed owners
self-heal) and Create on a live name fails instead of replacing.
Windows CI builds the workspace again.
2026-08-21 03:47:09 +08:00
Mike-Solar e8aac4a30d ci: create the registry src dir before patching ocio-sys
On a fresh runner registry/src has no hash subdir yet, so the unpack
glob never expanded and the step exited 2; derive it from the cache
dir and assert the patched file exists at the end.
2026-08-21 03:34:45 +08:00
Mike-Solar c9d557e127 feat(plugin): OfxParametricParameterSuite v1
Parametric (curve/LUT) parameters: ParamValue::Parametric holds one
ordered control-point curve per dimension (identity default over the
declared range), evaluated as piecewise cubic Hermite with auto
(centered-difference) slopes; the full suite — evaluate / count / get /
set / add / delete / delete-all — with the spec's error codes, descriptor
defaults copied to instances, and instanceChanged notifications on
edits. paramDefine accepts OfxParamTypeParametric; the dimension/range
and UI-colour properties round-trip. 148/148 real plugins discovered,
135 registered (one more than before: the parametric-suite consumer).
2026-08-21 03:03:50 +08:00
Mike-Solar 09cfd9f09e fix(ui): don't bump the display-color generation on the first build
The first processor build has no prior state to be stale against; the
spurious bump made the next cpu_frame call drop the freshly cached
image (playback_display_tracks_the_playhead regression).
2026-08-21 02:47:39 +08:00
Mike-Solar 2d4342ff48 ci: unpack ocio-sys before patching yaml-cpp; fix oakaudio test compile
- cargo fetch does not extract sources; the yaml-cpp <cstdint> patch
  now untars the .crate into the registry src dir first (the glob
  found nothing and the step failed with exit 2)
- oakaudio: the watchdog-wrapped audio test called Self::... from a
  free-function test module (compile error in lib test)
2026-08-21 02:40:44 +08:00
Mike-Solar d5b8cd99d6 chore: bump gpui (macOS layer colorspace coordination) 2026-08-21 02:17:19 +08:00
Mike-Solar eff845a5cd ci: linux xkbcommon-x11, yaml-cpp cstdint patch, hwaccel test skips on VT-less hosts
- Linux: libxkbcommon-x11-dev for the gpui X11 client link
- Windows: patch <cstdint> into the vendored yaml-cpp (a cached cmake
  configure ignores CXXFLAGS; the patch is idempotent and runs after
  cargo fetch)
- macOS: the hw-decode test skips its VideoToolbox engagement
  assertions on hosts where VT cannot initialize (headless/virtualized
  runners) instead of failing
- display color management: the display ICC (system or custom) is
  applied to viewer frames at present time (F32 in place, or in place
  on the BGRA staging copy with the R/B swizzle baked into the OCIO
  chain); preferences get a Color section (mode + custom ICC file); on
  macOS the Metal layer is tagged with the display colorspace when
  self-managing so ColorSync passes pixels through (no double
  correction); frame caches track the transform generation so a mode
  or profile change drops stale pixels
2026-08-21 02:16:39 +08:00
Mike-Solar fa7d8153bd cd: declare the full shlib dependency set on the deb
dpkg-shlibdeps over the three shipped binaries resolves every NEEDED
library to exact build-distro package names (FFmpeg/OCIO are static so
only base-OS packages appear) and rewrites the deb's Depends. Distros
with divergent package names (openKylin) get their own build instead
of a wrong-name dependency list.
2026-08-21 00:51:28 +08:00
Mike-Solar e1871494ae cd: audit the release binaries' NEEDED list on Linux
Static FFmpeg + static OCIO leave only base-OS libraries; the audit
step prints objdump NEEDED for each packaged binary so any accidental
dynamic dependency (and any distro-specific package-name surface) is
visible in the build log.
2026-08-21 00:46:40 +08:00
Mike-Solar 6b91748a5a ci: static OCIO link, full audio dev set on Linux, yaml-cpp cstdint fix
- OCIO_RS_LINK=static everywhere: the vendored OCIO is linked into the
  binaries statically — the package carries no OCIO dependency
- Linux: libasound2-dev (alsa-sys), libpulse-dev, libsndfile1-dev —
  the full audio dev set
- Windows: -include cstdint for the vendored yaml-cpp (pre-GCC-13
  transitive includes)
2026-08-21 00:44:35 +08:00
Mike-Solar 05fda5bcfc ci: libjack-jackd2-dev on Linux (cpal's JACK backend build dep) 2026-08-21 00:20:14 +08:00
Mike-Solar a1e4f47300 ci: GNU-target Rust on Windows, pipewire dev packages on Linux
- Windows: install MSYS2's own Rust (x86_64-pc-windows-gnu host); the
  rustup MSVC toolchain is not on the msys2 shell's PATH and the MSVC
  linker rejects the Unix-style link args the build scripts emit
- Linux: libpipewire-0.3-dev + libspa-0.2-dev for libspa-sys (gpui's
  Linux screen-capture/audio stack)
2026-08-21 00:12:55 +08:00
Mike-Solar 3c31c67f99 ci: build OpenColorIO from the ocio-sys vendored source on every platform
The distro OCIO is too old for the bridge's API floor where it matters
(Ubuntu 24.04 ships 2.1; the bridge uses 2.4+ APIs), and version drift
across platforms is a support hazard — enable ocio-rs' bundled feature
and drop the OCIO_INSTALL_DIR/system-package wiring from CI and CD so
Linux, macOS and Windows all build the same vendored OCIO. cmake/make/
diffutils added where the runners lack them (Windows FFmpeg build needs
make + cmp).
2026-08-21 00:01:14 +08:00
Mike-Solar 9c77d27b7f ci: fix dependency install on macOS and Windows runners
- Homebrew renamed libtheora->theora and libwebp->webp; the old names
  no longer resolve, failing the macOS dependency step
- retry the MSYS2 pacman install (3 attempts, --needed resumes): CI
  mirrors stall mid-download ("Operation too slow") often enough to
  matter
2026-08-20 23:53:50 +08:00
Mike-Solar 9003b78176 feat: built-in effect params, clip click-select, effect drag-and-drop, project load with plugins
- 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
2026-08-20 23:46:40 +08:00
Mike-Solar 0a7f3766e2 ci: probe OFX plugin discovery with a real fixture plugin; cd: restore Windows NSIS job
- new minimal C OFX plugin fixture (ci_test_plugin.c) compiled into a
  real .ofx.bundle by build_fixture.sh; the CI step points
  OFX_PLUGIN_PATH at it and asserts the scan_probe example discovers
  AND registers it (Linux/macOS)
- host bundle binary search now also covers the OFX-standard Win64
  platform directory
- cd.yml: restore the Windows NSIS packaging job (obsolete oakengine
  cdylib prebuild dropped) and repair the job indentation that had
  silently detached the macos/release jobs from the jobs: map;
  releases now gate on all three platforms
2026-08-20 22:37:22 +08:00
Mike-Solar 12ffce77db feat(common): persist configuration as TOML, migrate legacy config.ini
- save() writes <config>/config.toml (atomic temp+rename), flat keys
  at the top level and group/sub keys as [group] tables, values as
  native TOML int/float/bool/string (non-finite doubles degrade to
  strings and restore via the declared type)
- load() prefers config.toml; a legacy config.ini (C++ or pre-TOML
  Rust builds) is read once and immediately re-persisted as TOML; the
  INI file is left in place; a corrupt TOML is reported, never
  silently discarded
- cd.yml: drop a stale oakengine comment (the crate is retired)
2026-08-20 22:28:58 +08:00
Mike-Solar c5455c7521 fix(ui): track growth direction, proxy status, effect library search
- NLE track growth is now a display concern: video/subtitle track
  lists render reversed (a new track lands on top), audio lists render
  in order (a new track lands at the bottom); the graph list always
  appends. Track-add undo removes THIS track by id instead of blindly
  removing the last one
- add_track returns the actual index of the new track (diffed against
  the pre-command list) instead of assuming append-at-end
- status bar proxy segment reflects the real Use Proxy Media switch
  instead of a static "Proxy: Off"
- proxy transcode PROGRESS events no longer invalidate the rendered
  frame cache on every tick (only completion does) — progress updates
  used to keep the playback cache permanently cold while generating
- effect library: live search box (name/type-id substring), Built-in
  group header, and the addable-effects table is sorted alphabetically
  (built-ins first, then OFX sub-category groups)
2026-08-20 22:28:43 +08:00
Mike-Solar 5498504398 fix(plugin): full OFX plugin discovery — host conformance fixes
Real openfx-misc/CImg/Shadertoy bundles (148 plugins at
/Library/OFX/Plugins) all failed to load before; every failure was
silent. Root causes found one by one with a probe example + lldb:

- property suite rejected propSet on undefined properties and
  propGetDimension on empty ones, and disallowed the index==size
  append — OFX semantics are create-on-set and appendable dimensions
  (this alone failed every plugin's describe)
- host property set missed the mandatory OfxPropType/OfxPropAPIVersion
  and the capability props ofxs' fetchHostDescription reads with
  throwOnFailure=true (IsBackground, TemporalClipAccess, MaxPages,
  PageRowColumnCount, host SupportedContexts, ...) — one missing prop
  aborted the read chain and left a half-initialised host description,
  which made every temporal plugin refuse to load
- MultiThreadSuiteV1 lacked the five mutex functions (the plugin reads
  past the short table — UB); implemented as a real counting-semaphore
  registry
- the OfxHost struct was a stack local; ofxs keeps the POINTER past
  setHost, so describe/render-time fetchSuite calls dereferenced a
  dangling stack address (bus error once plugins actually loaded) —
  the struct is now a leaked process global
- General is a standard OFX context and is no longer filtered out
  (Roto/AppendClip/STMap declare only it)
- every scan/load/describe early-out now logs its reason; suite entry
  points report non-OK statuses with caller location under
  OAK_OFX_TRACE
- examples/scan_probe.rs: scans the real plugin dirs and prints
  discovered/registered counts (also usable from CI)

Result: 148/148 plugins discovered, 134 registered as node types (the
remaining 14 need vendor suites — Vegas stereoscopic etc. — and are
logged, not silent)
2026-08-20 22:28:23 +08:00
Mike-Solar bf0416c50b feat(i18n): externalize UI strings to YAML language packs
- string tables live in assets/i18n/<lang>.yaml, loaded at runtime
  (user pack dir ~/.oak/i18n, app bundle Resources/i18n, dev checkout)
  with the compiled-in English/Chinese tables as fallback
- new status.proxy.on/off keys; inspector.params copy no longer says
  "placeholder"; effect_library.group.builtin key
- bundle the packs as cargo-bundle resources; dev profile dep opt-level
  dropped to 1 for faster iteration builds
2026-08-20 22:27:57 +08:00
Mike-Solar 73bb686940 fix(app): thumbnail PNG encoding, effect-card collapse echo, linked A/V drag
- Thumbnails never appeared because the PNG was written to a .part
  file with format inferred from the extension (always failing); the
  writer now uses an explicit PNG encoder, and an e2e test proves the
  pipeline yields real files.
- Mock engine: the CardSelected -> SelectionChanged echo no longer
  re-expands a card the same click just collapsed.
- Dragging a clip moves its linked audio/video partners by the same
  frame offset in a single undo entry; the dragged clip may change
  tracks while partners keep theirs.
2026-08-20 18:56:01 +08:00
Mike-Solar cc0a15919f style(app): apply the refreshed theme, drop-ghost timeline shell, misc
- Root view paints the near-black base so dock gaps match.
- Waveform trace follows the deep-green design color.
- actions.rs: reset the shortcut overrides in
  save_writes_only_entries_that_differ_from_default (pre-existing
  flake: a leaked override from a previous test poisoned the shared
  lock under parallel test order).
2026-08-20 17:43:44 +08:00
Mike-Solar 02cea7e3ee feat(app): node editor follows the selected clip, two-way selection sync
- With a clip selected, the node editor shows that clip's context
  chain (footage -> effects -> clip) instead of the global graph; the
  clip's node is highlighted. No selection keeps the full graph.
- Node clicks in the graph select the node and expand/highlight the
  matching effect card in the inspector; clicking an inspector card
  highlights the node in the graph (single source of truth: the
  engine's selected_graph_node).
2026-08-20 17:08:26 +08:00
Mike-Solar d7cbeba850 feat(app): window menu checkmarks with panel toggle, OFX standard search paths
- The Window menu lists every panel, checks the open ones, and toggles
  visibility on click (closed panels reopen at their last dock target,
  falling back to the default group) — a panel closed by accident
  (e.g. the inspector) is one menu click away again. The menu refreshes
  on dock structure events.
- OFX plugin scanning now covers the full standard location set:
  per-user (~/.OFX/Plugins, ~/.local/share, ~/Library/OFX/Plugins on
  macOS), system-level (/Library/OFX/Plugins, /usr/OFX/Plugins,
  /usr/local, %ProgramFiles%\Common Files\OFX\Plugins), app-relative,
  and the OFX_PLUGIN_PATH environment variable.
2026-08-20 16:20:49 +08:00
Mike-Solar 877f577564 fix(oaktimeline): placement sets the block's in point (drop-at-cursor fix)
TrackPlaceBlockCommand::redo now homes the block's in point to the
placement target (capturing the original for undo): the Rust block
stores its position on the block, so a fresh clip that never had its
in point set always rendered at the timeline zero — the 'drops always
land at zero' bug. The original in point is captured on the first redo
and restored on undo, keeping the sync re-place round-trip exact. The
A/V drop test now asserts the clip lands at the drop frame.
2026-08-20 15:33:43 +08:00
Mike-Solar 1f6ed30423 chore: bump gpui submodule (viewer interact events) 2026-08-20 15:11:29 +08:00
Mike-Solar 844c747681 feat(app): drop-at-cursor placement and a translucent drop ghost
- Footage drops no longer clamp to the sequence length — that clamp
  squashed every drop on an empty or short timeline to frame zero.
  Dropping past the end now extends the timeline, so the clip lands
  where it is released.
- While dragging, a translucent ghost (35% opacity, accent border)
  previews the resolved track, start frame and footage length at the
  cursor; a new AppEngine::footage_length_frames (probed duration x
  frame rate) feeds its extent, with a mock implementation for demo
  mode.
2026-08-20 15:10:50 +08:00