- {settingsContent}
-
-
Open video or project
-
void run(onImportFile)}
- >
- Open file
-
-
-
-
-
Recordings folder
-
- {recordings}
-
-
-
- void run(async () => {
- const result = await window.electronAPI.chooseRecordingsDirectory();
- if (result.canceled) return;
- if (!result.success || !result.path)
- throw Error("Could not change recordings folder");
- setRecordings(result.path);
- })
- }
- >
- Change folder
-
-
- {captureSupported && (
+
+
+ {settingsContent}
+
+
-
-
Hide HUD from recordings
-
- Only while recording. The idle HUD stays visible in captures.
+
Open video or project
+
void run(onImportFile)}
+ >
+ Open file
+
+
+
+
+
+
+
Recordings folder
+
+ {recordings}
-
- void run(async () => {
- const result =
- await window.electronAPI.setHudOverlayCaptureProtection(
- enabled,
- );
- if (!result.success)
- throw Error("Could not update capture protection");
- setHideHud(result.enabled);
- })
- }
- />
-
- )}
- {import.meta.env.DEV && (
-
-
Preview update UI
void run(async () => {
- await window.electronAPI.previewUpdateToast();
+ const result =
+ await window.electronAPI.chooseRecordingsDirectory();
+ if (result.canceled) return;
+ if (!result.success || !result.path)
+ throw Error("Could not change recordings folder");
+ setRecordings(result.path);
})
}
>
- Preview
+ Change folder
- )}
-
-
-
Projects folder
- {directory && (
-
+
+
Hide HUD from recordings
+
+ Only while recording. The idle HUD stays visible in captures.
+
+
+
+ void run(async () => {
+ const result =
+ await window.electronAPI.setHudOverlayCaptureProtection(
+ enabled,
+ );
+ if (!result.success)
+ throw Error("Could not update capture protection");
+ setHideHud(result.enabled);
+ })
+ }
+ />
+
+ )}
+
+
+ {import.meta.env.DEV && (
+
+
Preview update UI
+
+ void run(async () => {
+ await window.electronAPI.previewUpdateToast();
+ })
+ }
>
- {directory}
-
- )}
+ Preview
+
+
+ )}
+
+
+
+
+
Projects folder
+ {directory && (
+
+ {directory}
+
+ )}
+
+
{
+ try {
+ const result = await window.electronAPI.getProjectsDirectory();
+ if (!result.success || !result.path)
+ throw Error("Could not open projects folder");
+ setDirectory(result.path);
+ await window.electronAPI.revealInFolder(result.path);
+ } catch (e) {
+ toast.error(String(e));
+ }
+ }}
+ >
+ Show folder
+
- {
- try {
- const result = await window.electronAPI.getProjectsDirectory();
- if (!result.success || !result.path)
- throw Error("Could not open projects folder");
- setDirectory(result.path);
- await window.electronAPI.revealInFolder(result.path);
- } catch (e) {
- toast.error(String(e));
- }
- }}
- >
- Show folder
-
-
-
- Named projects save automatically. Previews refresh when you return to Projects.
-
-
+
+ Named projects save automatically. Previews refresh when you return to
+ Projects.
+
+
+
);
}
diff --git a/src/components/video-editor/hooks/useZoomRegionCommands.ts b/src/components/video-editor/hooks/useZoomRegionCommands.ts
index 513653c7..23206915 100644
--- a/src/components/video-editor/hooks/useZoomRegionCommands.ts
+++ b/src/components/video-editor/hooks/useZoomRegionCommands.ts
@@ -3,6 +3,7 @@ import { type Dispatch, type MutableRefObject, type SetStateAction, useCallback
import {
clampFocusToDepth,
DEFAULT_AUTO_ZOOM_DEPTH,
+ DEFAULT_ZOOM_DEPTH,
type EditorEffectSection,
type ZoomDepth,
type ZoomFocus,
@@ -72,7 +73,7 @@ export function useZoomRegionCommands({
const handleZoomAdded = useCallback(
(span: Span) => {
const id = `zoom-${nextZoomIdRef.current++}`;
- const depth: ZoomDepth = 2;
+ const depth = DEFAULT_ZOOM_DEPTH;
const newRegion: ZoomRegion = {
id,
startMs: Math.round(span.start),
diff --git a/src/components/video-editor/types.ts b/src/components/video-editor/types.ts
index 1839ba01..4c947cea 100644
--- a/src/components/video-editor/types.ts
+++ b/src/components/video-editor/types.ts
@@ -647,7 +647,7 @@ export const ZOOM_DEPTH_SCALES: Record
= {
};
export const DEFAULT_ZOOM_DEPTH: ZoomDepth = 3;
-export const DEFAULT_AUTO_ZOOM_DEPTH: ZoomDepth = 2;
+export const DEFAULT_AUTO_ZOOM_DEPTH: ZoomDepth = DEFAULT_ZOOM_DEPTH;
export function clampFocusToDepth(focus: ZoomFocus, _depth: ZoomDepth): ZoomFocus {
return {
diff --git a/tests/ui/desktop-windows.spec.ts b/tests/ui/desktop-windows.spec.ts
index bc00dee8..cfcd6e48 100644
--- a/tests/ui/desktop-windows.spec.ts
+++ b/tests/ui/desktop-windows.spec.ts
@@ -30,6 +30,7 @@ test("editor inspector and playback controls fit a smaller desktop", async ({ pa
await expect(page.getByRole("button", { name: "Export", exact: true })).toBeInViewport();
await expect(page.getByRole("button", { name: "Play", exact: true })).toBeInViewport();
await page.getByRole("radio", { name: "Settings", exact: true }).click();
+ await page.getByRole("row", { name: "Motion", exact: true }).click();
await expect(
page.getByRole("radiogroup", { name: "Motion Presets", exact: true }),
).toBeVisible();
diff --git a/tests/ui/editor-layout.spec.ts b/tests/ui/editor-layout.spec.ts
index bdb9f675..562aa3aa 100644
--- a/tests/ui/editor-layout.spec.ts
+++ b/tests/ui/editor-layout.spec.ts
@@ -140,3 +140,14 @@ test("header name edits in place and saves on blur without a boxed input", async
await input.press("Escape");
await expect(page.getByRole("button", { name: "Rename project" })).toContainText("Launch demo");
});
+
+test("new zoom blocks default to 1.8x", async ({ page }) => {
+ await installDesktopBridge(page);
+ await page.goto("/?windowType=editor");
+ await page.getByRole("button", { name: "Add Zoom (Z)", exact: true }).click();
+ await page.locator('[data-variant="zoom"] .timeline-block').first().click();
+ await expect(page.getByRole("row", { name: "1.8×", exact: true })).toHaveAttribute(
+ "aria-selected",
+ "true",
+ );
+});
diff --git a/tests/ui/editor.spec.ts b/tests/ui/editor.spec.ts
index 9bb68be1..fe4127a0 100644
--- a/tests/ui/editor.spec.ts
+++ b/tests/ui/editor.spec.ts
@@ -64,7 +64,9 @@ test("editor loads video, switches tools and edits export options", async ({ pag
animations: "disabled",
});
await page.getByRole("button", { name: "Home", exact: true }).click();
- await expect(page.getByRole("dialog", { name: "Projects", exact: true })).toBeVisible();
+ await expect(
+ page.getByRole("dialog", { name: "Projects dashboard", exact: true }),
+ ).toBeVisible();
await page.keyboard.press("Escape");
await page.getByRole("button", { name: "Crop Video", exact: true }).click();
await expect(page.getByRole("dialog", { name: "Crop Video", exact: true })).toBeVisible();
@@ -72,15 +74,7 @@ test("editor loads video, switches tools and edits export options", async ({ pag
await page.getByRole("button", { name: "16:9", exact: true }).click();
await page.getByRole("menuitem", { name: "1:1", exact: true }).click();
await expect(page.getByRole("button", { name: "1:1", exact: true })).toBeVisible();
- await page.getByRole("button", { name: "Open presets", exact: true }).click();
- await page
- .getByRole("textbox", { name: "Preset name", exact: true })
- .fill("HeroUI test preset");
- await page.getByRole("button", { name: "Save", exact: true }).click();
- await expect(
- page.getByRole("button", { name: "Delete preset HeroUI test preset", exact: true }),
- ).toBeVisible();
- await page.keyboard.press("Escape");
+ await expect(page.getByRole("button", { name: "Open presets", exact: true })).toHaveCount(0);
await page.getByRole("button", { name: "Add Layer", exact: true }).click();
await page.getByRole("menuitem", { name: "Annotation", exact: true }).click();
await expect
@@ -117,27 +111,9 @@ test("recorder opens device and source popovers", async ({ page }) => {
await page.getByRole("button", { name: "Built-in Display", exact: true }).click();
await expect(page.getByRole("dialog")).toBeVisible();
await page.keyboard.press("Escape");
- await page.getByRole("button", { name: "More", exact: true }).click();
- await page.getByRole("button", { name: "Dark", exact: true }).click();
- await expect(page.locator("html")).toHaveClass(/dark/);
- await page.screenshot({
- path: "test-results/hud-dark.png",
- fullPage: true,
- animations: "disabled",
- });
- await page.getByRole("button", { name: "More", exact: true }).click();
- await page.getByRole("button", { name: "Light", exact: true }).click();
- await page.getByRole("button", { name: "More", exact: true }).click();
- await page
- .getByRole("dialog")
- .getByRole("button", { name: "Open project", exact: false })
- .click();
- await expect(page.getByText("No saved projects yet", { exact: true })).toBeVisible();
- await page.keyboard.press("Escape");
- await page.screenshot({
- path: "test-results/hud-light.png",
- fullPage: true,
- animations: "disabled",
- });
+ await expect(page.getByRole("button", { name: "More", exact: true })).toHaveCount(0);
+ await page.getByRole("button", { name: "Home", exact: true }).click();
+ await expect(page.locator("html")).toHaveAttribute("data-dashboard-opened", "true");
+
expect(errors).toEqual([]);
});
diff --git a/tests/ui/project-dashboard.spec.ts b/tests/ui/project-dashboard.spec.ts
index fc51e905..bcfc0fa3 100644
--- a/tests/ui/project-dashboard.spec.ts
+++ b/tests/ui/project-dashboard.spec.ts
@@ -381,17 +381,27 @@ test("dashboard supports creation sort, independent folders, shared settings and
await expect(home.getByRole("textbox", { name: "Search projects" })).toHaveCount(0);
await expect(home.getByRole("button", { name: "Sort projects" })).toHaveCount(0);
await expect(home.getByRole("row", { name: "Dark", exact: true })).toBeVisible();
+ await expect(home.getByRole("grid", { name: "Settings sections" })).toHaveCount(1);
+ await expect(home.getByRole("switch", { name: "Connect Zooms" })).toHaveCount(0);
+ await home.getByRole("row", { name: "Advanced", exact: true }).click();
await expect(home.getByRole("switch", { name: "Experimental updates" })).toBeVisible();
+ await expect(home.getByText("Preview update UI", { exact: true })).toBeVisible();
+ await home.getByRole("row", { name: "Motion", exact: true }).click();
await expect(home.getByRole("switch", { name: "Connect Zooms" })).toBeVisible();
+ await home.getByRole("row", { name: "Recording", exact: true }).click();
await expect(home.getByText("Recordings folder", { exact: true })).toBeVisible();
await home.getByRole("button", { name: "Change folder" }).click();
await expect(home.getByText("/new-recordings", { exact: true })).toBeVisible();
const capture = home.getByRole("switch", { name: "Hide HUD from recordings" });
await capture.press("Space");
await expect(page.locator("html")).toHaveAttribute("data-hide-hud", "true");
+ await home.getByRole("row", { name: "Files", exact: true }).click();
await expect(home.getByRole("button", { name: "Open file", exact: true })).toBeVisible();
- await expect(home.getByText("Preview update UI", { exact: true })).toBeVisible();
- await page.screenshot({ path: "test-results/dashboard-settings.png" });
+ await home.getByRole("row", { name: "Recording", exact: true }).click();
+ await expect(home.getByText("/new-recordings", { exact: true })).toBeVisible();
+ await home.getByRole("row", { name: "General", exact: true }).click();
+ await expect(home.getByRole("row", { name: "Dark", exact: true })).toBeVisible();
+ await page.screenshot({ path: "test-results/dashboard-settings.png", animations: "disabled" });
});
test("Solar navigation selection, circular initials, and Raw sources are consistent", async ({