feat(app): P5 — async full-res render, full preferences, shortcut map

- viewers show the 480px proxy immediately and a background thread
  fills the sequence-resolution frame (per-monitor in-flight job,
  generation-based staleness, playback skips full-res)
- preferences dialog complete: cache dir (now consumed by
  default_disk_cache_path), proxy policy/divider, snapshot interval
  (write-through era autosave), default transition length, audio
  in/out devices (new facade device-enumeration exports; audio init
  from config — playback was never creating the audio instance),
  language/theme/renderer backend, all persisted via config
- shortcut map (src/shortcuts.rs): space/J/K/L, I/O, S split, A/^A,
  ⌘Z/⌘⇧Z, ⌘N/⌘O/⌘S/⌘E/⌘Q, frame step, Home, track zoom; dispatch
  shares the menu action path and stays silent over modals
- screenshots: preferences dialog zh/en captured and reviewed
This commit is contained in:
2026-08-16 18:05:03 +08:00
parent 466cdcb7d7
commit e346ea5338
17 changed files with 2237 additions and 72 deletions
+26
View File
@@ -50,6 +50,8 @@ const OUT_ZH: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/docs/screenshot-windo
const OUT_EN: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/docs/screenshot-window-en.png");
const OUT_MGR_ZH: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/docs/screenshot-manager.png");
const OUT_MGR_EN: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/docs/screenshot-manager-en.png");
const OUT_PREF_ZH: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/docs/screenshot-preferences.png");
const OUT_PREF_EN: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/docs/screenshot-preferences-en.png");
/// Logical y of the timeline toolbar row, which sits at the top of the
/// bottom dock panel in the default layout: the dock starts at y 27.5 (the
@@ -86,6 +88,7 @@ fn main() -> Result<()> {
image.save(OUT_ZH)?;
println!("wrote {OUT_ZH} ({}×{})", image.width(), image.height());
assert_toolbar(&image, "zh-CN");
capture_preferences(&mut cx, handle, &root, OUT_PREF_ZH)?;
capture_manager(&mut cx, handle, &root, OUT_MGR_ZH)?;
}
i18n::set_language(Language::EnUs);
@@ -96,6 +99,7 @@ fn main() -> Result<()> {
image.save(OUT_EN)?;
println!("wrote {OUT_EN} ({}×{})", image.width(), image.height());
assert_toolbar(&image, "en-US");
capture_preferences(&mut cx, handle, &root, OUT_PREF_EN)?;
capture_manager(&mut cx, handle, &root, OUT_MGR_EN)?;
}
i18n::set_language(original);
@@ -155,6 +159,28 @@ fn settle(cx: &mut VisualTestAppContext, handle: AnyWindowHandle) {
cx.run_until_parked();
}
/// Opens the preferences dialog on the shell (M12 P5b) and captures it: the
/// modal lists the grouped settings (general / rendering / cache / proxy /
/// project / audio). Drives the root ENTITY (not the window handle — see
/// [`capture_manager`]). The dialog closes afterwards so the manager
/// capture starts from a clean shell.
fn capture_preferences(
cx: &mut VisualTestAppContext,
handle: gpui::WindowHandle<OakApp<MockEngine>>,
root: &Entity<OakApp<MockEngine>>,
out: &str,
) -> Result<()> {
root.update(cx, |app, cx| app.open_preferences(cx));
settle(cx, handle.into());
let image = cx.capture_screenshot(handle.into())?;
image.save(out)?;
println!("wrote {out} ({}×{})", image.width(), image.height());
// Close the dialog (the Close button path: commit + dismiss).
root.update(cx, |app, cx| app.close_modal(cx));
cx.run_until_parked();
Ok(())
}
/// Opens the project manager on the shell (M13 D4) and captures it: the
/// modal card lists the mock library with its per-project stats. Drives the
/// root ENTITY (not the window handle — a window update borrows the window,