diff --git a/web/package.json b/web/package.json index bb748fc03..97ed46f24 100644 --- a/web/package.json +++ b/web/package.json @@ -7,7 +7,7 @@ "dev": "vite --host 0.0.0.0 --port 3000", "build": "tsc --noEmit && vite build", "typecheck": "tsc --noEmit", - "test": "bun test test/api-request.test.ts test/video-api-contracts.test.ts test/image-api-contracts.test.ts test/channel-model-catalog.test.ts test/model-protocols.test.ts test/agent-prompt-cache.test.ts test/canvas-agent-context.test.ts test/canvas-batch-connection.test.ts test/canvas-character-reference.test.ts test/canvas-document.test.ts test/canvas-drawing-engine.test.ts test/canvas-emotion-edit-plan.test.ts test/canvas-extension-nodes.test.ts test/canvas-folder.test.ts test/canvas-generation-layout.test.ts test/canvas-image-batch-retry.test.ts test/canvas-leafer-viewport.test.ts test/canvas-local-runtime.test.ts test/canvas-model-policy.test.ts test/canvas-resource-mention-editor.test.ts test/canvas-resource-references.test.ts test/canvas-storyboard-assets.test.ts test/canvas-storyboard-references.test.ts test/canvas-node-config-reset.test.ts test/canvas-node-registry.test.ts test/canvas-video-regeneration.test.ts test/canvas-video-timeline-segments.test.ts test/create-canvas-handoff.test.ts test/create-library-button.test.ts test/creation-references.test.ts test/generation-asset-repository.node.test.ts test/generation-storage-consistency.test.ts test/generation-task-durability.node.test.ts test/generation-task-local.test.ts test/generation-task-materializer.test.ts test/task-media-fallback.test.ts test/user-scoped-generation-persistence.test.ts test/local-channel-permission.test.ts test/local-channel-runtime-projection.node.test.mjs test/local-channel-task-center.node.test.mjs test/local-channel-ui.node.test.mjs test/local-cli-settings.test.ts test/local-dreamina-cli.test.ts test/local-dreamina-generation.test.ts test/local-dreamina-model-catalog.test.ts test/local-runtime-bootstrap.test.ts test/local-runtime-connection.test.ts test/local-runtime.test.ts test/client-id.test.ts test/grok-image-prompt-limit.test.ts test/image-resolution-tiers.test.ts test/gemini-image.test.ts test/libtv-import.test.ts test/timeline-to-ffmpeg.test.ts test/canvas-video-segment-args.test.ts test/tapnow-import.test.ts && bun test test/local-dreamina-cross-runtime.test.ts", + "test": "bun test test/api-request.test.ts test/video-api-contracts.test.ts test/image-api-contracts.test.ts test/channel-model-catalog.test.ts test/model-protocols.test.ts test/agent-prompt-cache.test.ts test/canvas-agent-context.test.ts test/canvas-batch-connection.test.ts test/canvas-character-reference.test.ts test/canvas-document.test.ts test/canvas-drawing-engine.test.ts test/canvas-emotion-edit-plan.test.ts test/canvas-extension-nodes.test.ts test/canvas-folder.test.ts test/canvas-generation-layout.test.ts test/canvas-image-batch-retry.test.ts test/canvas-leafer-viewport.test.ts test/canvas-local-runtime.test.ts test/canvas-model-policy.test.ts test/canvas-resource-mention-editor.test.ts test/canvas-resource-references.test.ts test/canvas-storyboard-assets.test.ts test/canvas-storyboard-references.test.ts test/canvas-node-config-reset.test.ts test/canvas-node-registry.test.ts test/canvas-video-regeneration.test.ts test/canvas-video-timeline-segments.test.ts test/create-canvas-handoff.test.ts test/create-library-button.test.ts test/creation-references.test.ts test/generation-asset-repository.node.test.ts test/generation-storage-consistency.test.ts test/generation-task-durability.node.test.ts test/generation-task-local.test.ts test/generation-task-materializer.test.ts test/task-media-fallback.test.ts test/user-scoped-generation-persistence.test.ts test/local-channel-permission.test.ts test/local-channel-runtime-projection.node.test.mjs test/local-channel-task-center.node.test.mjs test/local-channel-ui.node.test.mjs test/local-cli-settings.test.ts test/local-dreamina-cli.test.ts test/local-dreamina-generation.test.ts test/local-dreamina-model-catalog.test.ts test/local-runtime-bootstrap.test.ts test/local-runtime-connection.test.ts test/local-runtime.test.ts test/client-id.test.ts test/grok-image-prompt-limit.test.ts test/image-resolution-tiers.test.ts test/gemini-image.test.ts test/libtv-import.test.ts test/timeline-to-ffmpeg.test.ts test/canvas-video-segment-args.test.ts test/tapnow-import.test.ts test/prompt-optimizer-drawer.test.ts && bun test test/local-dreamina-cross-runtime.test.ts", "start": "vite preview --host 0.0.0.0 --port 3000", "format": "prettier --write .", "format:check": "prettier --check ." diff --git a/web/src/components/canvas/canvas-prompt-optimizer-drawer.tsx b/web/src/components/canvas/canvas-prompt-optimizer-drawer.tsx index 42fddba5a..a94678618 100644 --- a/web/src/components/canvas/canvas-prompt-optimizer-drawer.tsx +++ b/web/src/components/canvas/canvas-prompt-optimizer-drawer.tsx @@ -101,6 +101,7 @@ export function CanvasPromptOptimizerDrawer({ open, children, prompt, generation const { message } = App.useApp(); const abortRef = useRef(null); const chatRef = useRef(null); + const contentRef = useRef(null); const [mode, setMode] = useState("expand"); const [activeOptimizerModel, setActiveOptimizerModel] = useState(""); const [draftPrompt, setDraftPrompt] = useState(prompt); @@ -176,7 +177,7 @@ export function CanvasPromptOptimizerDrawer({ open, children, prompt, generation }, [open]); const syncPanelPositionToViewport = useCallback(() => { - const popoverRoot = document.querySelector(".canvas-prompt-optimizer-popover"); + const popoverRoot = contentRef.current?.closest(".canvas-prompt-optimizer-popover"); if (!popoverRoot) return; const rect = popoverRoot.getBoundingClientRect(); @@ -381,7 +382,7 @@ export function CanvasPromptOptimizerDrawer({ open, children, prompt, generation }; const nudgePanel = (deltaX: number, deltaY: number) => { - const popoverRoot = document.querySelector(".canvas-prompt-optimizer-popover"); + const popoverRoot = contentRef.current?.closest(".canvas-prompt-optimizer-popover"); if (!popoverRoot) { setPanelOffset((current) => ({ x: current.x + deltaX, y: current.y + deltaY })); return; @@ -622,7 +623,7 @@ export function CanvasPromptOptimizerDrawer({ open, children, prompt, generation } as CSSProperties; const content = ( -
+
{ diff --git a/web/test/prompt-optimizer-drawer.test.ts b/web/test/prompt-optimizer-drawer.test.ts new file mode 100644 index 000000000..5e500b490 --- /dev/null +++ b/web/test/prompt-optimizer-drawer.test.ts @@ -0,0 +1,14 @@ +import { describe, expect, test } from "bun:test"; +import { readFileSync } from "node:fs"; +import { resolve } from "node:path"; + +const source = readFileSync(resolve(import.meta.dir, "../src/components/canvas/canvas-prompt-optimizer-drawer.tsx"), "utf8"); + +describe("prompt optimizer drawer instance isolation", () => { + test("resolves the popover from the current drawer content instead of the first global match", () => { + expect(source).toContain("const contentRef = useRef(null);"); + expect(source).toContain('contentRef.current?.closest(".canvas-prompt-optimizer-popover")'); + expect(source).toContain('
(".canvas-prompt-optimizer-popover")'); + }); +}); diff --git a/web/test/workspace-route-loading.test.ts b/web/test/workspace-route-loading.test.ts index 126bf345f..08526f545 100644 --- a/web/test/workspace-route-loading.test.ts +++ b/web/test/workspace-route-loading.test.ts @@ -19,13 +19,14 @@ describe("workspace route loading", () => { test("preloads the reported workspace routes before navigation", () => { const modules = source("../src/lib/workspace-route-modules.ts"); - const navigation = source("../src/components/layout/app-top-nav.tsx"); + const navigation = source("../src/components/layout/workspace-sidebar-nav.tsx"); for (const route of ["projects", "canvas", "assets", "wallet", "create"]) { expect(modules).toContain(`${route}: () => import`); } - expect(navigation).toContain("onPointerEnter={() => preloadWorkspaceRoute(tool.slug)}"); - expect(navigation).toContain("onFocus={() => preloadWorkspaceRoute(tool.slug)}"); + expect(navigation).toContain("onPointerEnter={() => preloadWorkspaceRoute(linkTo)}"); + expect(navigation).toContain("onPointerDown={() => preloadWorkspaceRoute(linkTo)}"); + expect(navigation).toContain("onFocus={() => preloadWorkspaceRoute(linkTo)}"); }); test("uses a quiet workspace skeleton for initial hydration", () => {