diff --git a/tests/test_browser_agent_regressions.py b/tests/test_browser_agent_regressions.py index 6c076c42d..fff617ce5 100644 --- a/tests/test_browser_agent_regressions.py +++ b/tests/test_browser_agent_regressions.py @@ -830,7 +830,7 @@ def test_surface_buttons_keep_modal_and_canvas_entry_points_separate(): canvas_store.index("async undockActiveSurface") ] should_render_block = canvas_store[ - canvas_store.index("shouldRender()"): + canvas_store.index("\n shouldRender()"): canvas_store.index("};\n\nexport const store") ] surface_button_block = surfaces_js[ @@ -845,6 +845,7 @@ def test_surface_buttons_keep_modal_and_canvas_entry_points_separate(): assert "markSurfaceMounted(targetId)" in canvas_store assert "isSurfaceRendered(id)" in canvas_store assert "isSurfaceVisible(id)" in canvas_store + assert 'import { store as chatsStore } from "/components/sidebar/chats/chats-store.js";' in canvas_store assert "async openLatest(surfaceId" in canvas_store assert "async openModalSurface(surfaceId" in canvas_store assert "this.recordSurfaceMode(targetId, SURFACE_MODE_DOCKED" in canvas_store @@ -856,8 +857,10 @@ def test_surface_buttons_keep_modal_and_canvas_entry_points_separate(): assert "return await this.openModalSurface(targetId, payload);" in canvas_store assert "return await this.open(targetId, payload);" in canvas_store assert 'return await this.open(this.activeSurfaceId || this.panelSurfaces[0]?.id || "", { source: "mobile-toggle" });' in canvas_store - assert "return true;" in should_render_block + assert "isWelcomeVisible()" in canvas_store + assert "return !this.isWelcomeVisible();" in should_render_block assert "isMobileMode" not in should_render_block + assert 'document.body.classList.toggle("right-canvas-open", this.isOpen && !this.isMobileMode && this.shouldRender());' in canvas_store assert "this.mountedSurfaces = {}" not in close_block assert "surface?.close" not in close_block assert "this.mountedSurfaces = {}" not in undock_block diff --git a/tests/test_file_browser_navigation.py b/tests/test_file_browser_navigation.py index be18e04e8..4ef4ebf84 100644 --- a/tests/test_file_browser_navigation.py +++ b/tests/test_file_browser_navigation.py @@ -144,6 +144,7 @@ def test_file_browser_is_registered_as_right_canvas_surface() -> None: register = read("extensions", "webui", "right_canvas_register_surfaces", "register-files.js") panel = read("extensions", "webui", "right-canvas-panels", "files-panel.html") input_store = read("webui", "components", "chat", "input", "input-store.js") + welcome_store = read("webui", "components", "welcome", "welcome-store.js") assert 'id: "files"' in surfaces assert 'title: "Files"' in surfaces @@ -167,6 +168,9 @@ def test_file_browser_is_registered_as_right_canvas_surface() -> None: assert 'data-surface-id="files"' in panel assert 'path="modals/file-browser/file-browser.html" mode="canvas"' in panel assert 'openLatestSurface("files"' in input_store + assert 'import { store as fileBrowserStore } from "/components/modals/file-browser/file-browser-store.js";' in welcome_store + assert "fileBrowserStore.open()" in welcome_store + assert "chatInputStore.browseFiles" not in welcome_store def test_file_browser_reports_missing_directory(tmp_path: Path) -> None: diff --git a/webui/components/canvas/AGENTS.md b/webui/components/canvas/AGENTS.md index c0548378d..77dec922a 100644 --- a/webui/components/canvas/AGENTS.md +++ b/webui/components/canvas/AGENTS.md @@ -15,6 +15,7 @@ - Keep registered surfaces compatible with WebUI extension hooks. - Preserve responsive layout and avoid overlapping the chat/sidebar shells. - Right-canvas rail and tab buttons are explicit canvas entry points above the mobile breakpoint; at mobile widths, keep the rail visible but route non-action surfaces into floating modals instead of the side canvas shell. +- Keep the right-canvas rail and docked shell hidden while the welcome screen is active; non-action surface opens during welcome must route into floating/modal surfaces instead of docking beside the welcome screen. - In mobile mode, keep the rail below blocking modal layers and compact it on very narrow screens instead of letting it cover modal content. ## Work Guidance diff --git a/webui/components/canvas/right-canvas-store.js b/webui/components/canvas/right-canvas-store.js index 1718d4613..315eeabd1 100644 --- a/webui/components/canvas/right-canvas-store.js +++ b/webui/components/canvas/right-canvas-store.js @@ -1,4 +1,5 @@ import { createStore } from "/js/AlpineStore.js"; +import { store as chatsStore } from "/components/sidebar/chats/chats-store.js"; import { callJsExtensions } from "/js/extensions.js"; import { SURFACE_MODE_DOCKED, @@ -135,6 +136,10 @@ const model = { return true; } + if (!this.shouldRender()) { + return await this.openModalSurface(targetId, payload); + } + if (this.isMobileMode) { this.activeSurfaceId = targetId; this.isOpen = false; @@ -342,6 +347,7 @@ const model = { }, async toggleCanvas() { + if (!this.shouldRender()) return false; if (this.isMobileMode) { return await this.open(this.activeSurfaceId || this.panelSurfaces[0]?.id || "", { source: "mobile-toggle" }); } @@ -476,7 +482,7 @@ const model = { applyLayoutState() { this.updateLayoutMode(); document.documentElement.style.setProperty("--right-canvas-width", `${this.width}px`); - document.body.classList.toggle("right-canvas-open", this.isOpen && !this.isMobileMode); + document.body.classList.toggle("right-canvas-open", this.isOpen && !this.isMobileMode && this.shouldRender()); document.body.classList.toggle("right-canvas-overlay-mode", this.isOverlayMode); document.body.classList.toggle("right-canvas-mobile-mode", this.isMobileMode); }, @@ -517,8 +523,12 @@ const model = { return this.currentSurface()?.title || "Canvas"; }, + isWelcomeVisible() { + return !chatsStore.selected; + }, + shouldRender() { - return true; + return !this.isWelcomeVisible(); }, }; diff --git a/webui/components/welcome/AGENTS.md b/webui/components/welcome/AGENTS.md index 624209b3b..3fd814d5f 100644 --- a/webui/components/welcome/AGENTS.md +++ b/webui/components/welcome/AGENTS.md @@ -16,6 +16,7 @@ - Do not show setup prompts for already configured plugins when backend status can prevent it. - The welcome screen mounts the shared chat composer to start a new chat; keep it mutually exclusive with the normal chat input DOM. - Render `system-resources` as the dedicated System Resources panel, not as a generic alert banner. +- Utility quick actions from welcome must keep the first screen intact; use modal/floating entry points instead of docking the right canvas beside welcome content. ## Work Guidance diff --git a/webui/components/welcome/welcome-store.js b/webui/components/welcome/welcome-store.js index eeb41e25b..26d770372 100644 --- a/webui/components/welcome/welcome-store.js +++ b/webui/components/welcome/welcome-store.js @@ -2,7 +2,7 @@ import { createStore } from "/js/AlpineStore.js"; import { store as chatsStore } from "/components/sidebar/chats/chats-store.js"; import { store as memoryStore } from "/plugins/_memory/webui/memory-dashboard-store.js"; import { store as projectsStore } from "/components/projects/projects-store.js"; -import { store as chatInputStore } from "/components/chat/input/input-store.js"; +import { store as fileBrowserStore } from "/components/modals/file-browser/file-browser-store.js"; import * as API from "/js/api.js"; import { getCurrentUserISOString } from "/js/time-utils.js"; @@ -261,7 +261,7 @@ const model = { memoryStore.openModal(); break; case "files": - chatInputStore.browseFiles(); + fileBrowserStore.open(); break; case "website": window.open("https://agent-zero.ai", "_blank");