Two export killers fixed and the feature surfaced properly:
- TicketArena::allocate() reaps finished fire-and-forget slots on every
new submit, but RenderTask classified finished tickets by asking the
arena for their kind/time afterwards - any ticket that completed
before the next submit (all of them on the inline backend) failed
with 'Render ticket reported an unexpected timestamp' and the export
died after writing a header-only file. RenderTask now records each
ticket's delivery key at submit time instead.
- File > Export becomes 导出序列 (all 8 locales): the dialog offers a
sequence picker (any sequence in the project, the current one
preselected) instead of silently exporting the current one, and the
project bin's sequence context menu gains 导出序列 opening the same
dialog. Engines expose sequence_entries / current_sequence_id /
start_export_of.
- The export-project dialog drops its dead hand-typed path field (OK
already asked the platform save dialog).
- New end-to-end test: a one-second sequence exports to a real
H.264/AAC MP4 that probes and decodes back to the clip's content.
The three values are SpinBoxes (typed input + stepping): width & height
(0 = use the sequence size), frame rate in fps (0 = sequence rate),
bit rate in bit/s (0 = the codec default). The engine already accepted
arbitrary values, so the dialog now exposes them instead of the closed
preset lists. i18n across the 8 packs.
- ExportDialogContent: container format (existing), then the video and
audio codec combos rebuilt per container from the compatibility tables
(an unsupported codec is never offered — switching container
re-selects the first compatible entry), a color-space combo (SDR
Rec.709 8-bit / HDR BT.2020 10-bit), an export-range combo (whole
timeline / in-out) and the output path
- ComboBox::set_options added for the container-driven list rebuild
- settings() gathers the ExportSettings the engine consumes
- begin_export routes through start_export_with
- i18n keys across the 8 packs (zh-CN uses distinct values so the
en==zh equality test passes)
- 文件 → 新建项目 opens an in-app dialog: project name + the first
sequence's format (preset / custom / interlaced, reusing
SequenceFormatFields); OK creates the library project then seeds the
first sequence from the chosen format
- 打开项目 prompt advertises the supported project formats
(.ove / .otio / .fcpxml)
- manager.new.project_name across the 8 i18n packs
- action-search first-item test follows the new flow (New Project now
opens its dialog)
文件 → 导出工程文件… now opens an in-app dialog (format dropdown:
OpenTimelineIO / Oak / FCPXML + output path) instead of the bare
platform save prompt; OK routes the chosen extension through the
engine's export_project_path. Adds project.export.format i18n across
the 8 packs.
- Project explorer header gains a 新建序列 button (opens the existing
new-sequence dialog, seeded like the menu action).
- Sequence presets gain 4K UHD (3840x2160@25) and 4K DCI (4096x2160@24);
the sequence-properties dialog re-selects them on reopen. Format fields
are also seedable from a probed footage format (the drop flow).
- Chroma Key (and Color Difference Key) value() now box a ShaderJobPayload
like Despill: the old OCIO-processor gate pushed nothing (the processor
is never populated without the render bridge), so the traverser handed
the clip NodeValue::None and the rendered frame lost the clip. The
renderer resolves the OCIO stub at compile time from OCIO_SHADER_STUBS.
End-to-end graph test: green key on a green frame keys out, red key
keeps it.
- The OFX color picker's SV palette / hue bar / preview / swatch canvases
get size_full(): the bare canvases collapsed to zero height in the
block layout, so the palette painted nothing (the reported 色板没显示).
Regression test clicks the palette center and expects mid s/v.
Preview now follows the project output colorspace end to end: the
display chain derives its content space from the project's OutputColorSpec
instead of a hardcoded sRGB name, self-managed ICC transforms go through
an XYZ D65 interchange stage (OCIO cie_xyz_d65_interchange) for non-sRGB
targets, and the platform layer declares the content colorspace (gpui
submodule bump). macOS defaults to OS-managed (fixes wide-gamut UI
oversaturation); Windows ACM warns once on non-sRGB targets.
Multi-monitor: the display ICC is looked up per the window's current
screen (macOS display id, Windows per-monitor DC, X11 RandR output
profile) with a throttled poll that invalidates frame caches on moves.
Pipeline precision: 10-bit+ sources fall back to YUV444P16LE + a Rust
matrix conversion when swscale lacks F32 output (no more 8-bit
truncation); BT.709/2020 SDR decodes with BT.1886 gamma 2.4 instead of
the sRGB EOTF; working-space compositing no longer clamps RGB to [0,1]
(alpha still clamped); the output node clamps to the target gamut;
frames without colorimetry metadata convert with BT.709 defaults
(warned once) instead of passing through; scopes read the
output-colorspace signal on both F32 paths.
Also: only emit rerun-if-changed for .env when it exists (a missing file
made every build fully dirty).
- the popup now offers a large SV palette with a hue bar plus RGB/HSV
mode tabs (shared slider model swap, hsv<->rgb pure functions)
- while the eyedropper is armed, clicks outside the popup pick a colour
instead of dismissing it, so the picked draft can still be confirmed
- regression test pinning the OFX plugin colour set/read/undo chain
- i18n: ofx.color.mode_rgb/mode_hsv in all eight packs
Preferences gains a display-bit-depth combo stored in the config store;
at startup the app forwards it to gpui_wgpu via OAK_DISPLAY_BIT_DEPTH,
which prefers Rgb10a2Unorm for 10-bit presentation. Takes effect after
restart (noted in the dialog); i18n in all eight packs.
Replaces the project-thumbnail picker: the color popup's eyedropper
button arms the program viewer, the next click on the picture samples
that pixel (contain-fit mapped, BGRA from the current CPU frame) into
the picker's draft; Esc or the button cancels. Coordinated through a
global eyedropper mailbox on the engine.
- File > New > Sequence opens a real dialog (name, PAL/NTSC/HD presets,
width/height/frame rate, progressive/interlaced); VideoParams gains an
interlaced flag
- File > New > Folder creates a folder in the project root
- sequences are mounted under the root folder so they appear in the
project explorer (including the auto-created Sequence 1 and orphans
from older projects); right-click > Sequence Properties edits the
parameters afterwards
- dropping footage onto an empty, sequence-less timeline auto-creates a
sequence from the footage's first video stream
- the swatch now follows the draft color while the popup is open
(cancel still reverts) and set_effect_param failures are logged
instead of silently reverting the swatch
- the popup lists project footage thumbnails; clicking a pixel picks
its color into the draft (contain-fit coordinate mapping, no new
dependencies)
- i18n: ofx.color.pick_project in all eight packs
Proxy generation no longer pegs the machine:
- the transcode probes for a hardware H.264 encoder once per process
and uses it when present (macOS h264_videotoolbox; Windows/Linux
h264_nvenc -> h264_qsv -> h264_amf; libx264 remains the universal
fallback and the untouched C++ parity path), with decode-side
-hwaccel auto (HEVC 4:2:2 10-bit decodes in hardware on Apple
Silicon / RTX 50+ / Intel GPUs and falls back to software cleanly);
quality/preset map from the proxy CRF/preset per encoder
- the concurrency limit is configurable (ProxyMaxConcurrent, default
1) in the Proxy Settings dialog; auto-generated jobs queue and the
next starts when a slot frees. The invariant
concurrency x per-job threads <= logical cores / 2 holds by
construction (thread budget = half the cores / concurrency, clamped
to [1,8], covered by a unit test), and on Unix ffmpeg runs nice -n 10
so the background task never starves the foreground
- deleting a footage's proxy also removes it from the auto-generation
queue
- Help > About Oak… opens a real dialog (was a placeholder print): name
+ version, the GPL-3.0 line, the Olive fork notice and the thanks to
Enzo GD (Community Promoter).
- Modal (gpui_widgets, submodule bump): a title-row close button on
every dialog and opt-in backdrop dismissal (dismiss_on_mask) — Help >
Search Actions and About use it, so they close on an outside click
(previously a buttonless modal could only be closed with Escape).
- Preferences general tab grows two live settings: the cache-ahead
pre-render window (PlaybackPreRenderFrames, consumed by the preview
scheduler every playback tick) and the library storage backend
(Storage/Backend sqlite/pg + Storage/PgUrl connection string, read by
oak-storage's write-through when a project binds; the pg URL field
shows only for PostgreSQL and commits on dialog close). The C++
auto-recovery toggle is skipped on purpose: the Rust snapshot loop
has no disable semantic, so the switch would be dead.
i18n keys for all eight packs; new tests cover the about modal and the
preferences writes.
The menu item was a placeholder print; it now opens a real dialog (the
C++ ProjectPropertiesDialog):
- Per-project OCIO config override with a 浏览… picker: validated on OK
(an invalid config keeps the dialog open with the error shown, like
the C++ accept()), persisted in the project settings, applied to the
display color pipeline on accept and on project open, and reverted to
the app default when the project closes. oak-render gains
set_up_default_config_from for the explicit-path load.
- Disk-cache location (default / alongside the project / custom path):
persisted through the OVE serializer (cachesetting/customcachepath
round-trip the settings map, clamped on load) and honored by the
thumbnail writer — the first live consumer of Project::cache_path.
- PathField gains an enabled state (the custom path field follows the
combo selection).
The C++ color tab's Default Input Color Space and Reference Space
combos are intentionally absent: the Rust render pipeline has no
consumer for them today (decode performs no input transfer conversion),
so showing them would be dead settings.
Tests: dialog opens, OK applies the cache location, an invalid OCIO
config keeps the dialog open with the error row. i18n keys for all
eight packs.
Three real-engine throughput loops (thumbnail pipeline, full-res fill
behind the proxy, playback window supply) failed on machine slowness:
their pass/fail was a wall-clock Instant deadline, so a loaded machine
broke them for speed, not for a broken pipeline. Each loop now counts
engine pumps — machine-speed independent — and asserts the condition
within a bounded number of pumps. The two single-frame worker channel
receives keep a generous 60 s recv_timeout (a one-shot bounded
operation, not a throughput loop).
oak-cli: the integration fixtures moved with the app crate during the
workspace restructure; point the fixture helpers at
../oak-app/tests instead of the (now empty) repo-root tests/.
New complete language packs (431 keys each, key-aligned with en-US):
French, German, Russian, Japanese, Spanish, Portuguese — contributed
via the translation subagent, reviewed for key parity and genuine
translations.
Runtime discovery replaces compiled-in packs: include_str! and the
Language enum are gone; every <code>.yaml found in the searched
directories registers a language, so adding one needs neither a
rebuild nor a code change. Each pack names itself via its
"language.name" key (the endonym), which the pickers display. The
menu bar language submenu is built from the discovered packs (menu ids
in the new LANG_ITEM_BASE range, dispatched before the registry), the
preferences combo enumerates them the same way, and switching goes
through set_language_code. The C++-style LangZh/LangEn registry
actions are removed (menu ids 303/304 stay reserved). pack_dirs gains
the system-install layout <exe>/../share/oak/i18n, and en.yaml is
renamed en-US.yaml so the discovered code matches the fallback code.
Test hardening (a Windows/Linux CI flake chain exposed by this work):
the two language-test mutexes are one shared poison-tolerant mutex;
every test that flips the process language restores en-US before
releasing it; tests that match menus by localized labels take the lock
and pin en-US first.
- 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
- 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
- App no longer depends on the oakengine rlib: build.rs links the
built liboakengine.dylib (+rpath, -export_dynamic, IOSurface) and
src/oakui/ffi.rs declares the pure-C surface; RealEngine calls only
the frozen oakengine_* C ABI
- host_syms.rs provides the oakcore_*/fb_* host symbols the dylib
imports via dynamic lookup
- Fix Preferences dialog crash (spawn_modal reentrancy) with a
regression test
- Timeline toolbar and viewer transport render C++-era icons (16px
grid, dark/light themes) with localized tooltips
- i18n: complete en-US table, add untranslated-key detection test
- New dialogs module (preferences, export, progress)