Keep welcome screen free of docked canvas
Route non-action canvas surfaces to their floating/modal forms while the welcome screen is active, and keep the canvas rail/shell hidden until a chat is selected. Open the welcome Files quick action through the File Browser modal directly so the first screen remains composed. Document the welcome/canvas boundary in the local DOX contracts and add focused regression assertions for the canvas guard and Files quick action.
Alessandro committed
Jun 23, 2026 at 17:23 UTC
617adaf8a21888fc117bb3cf601fb20009f7fea6
6 files changed
+25
-6
tests/test_browser_agent_regressions.py
+5
-2
@@ -830,7 +830,7 @@ def test_surface_buttons_keep_modal_and_canvas_entry_points_separate():
830
canvas_store.index("async undockActiveSurface")
831
]
832
should_render_block = canvas_store[
833
- canvas_store.index("shouldRender()"):
833
+ canvas_store.index("\n shouldRender()"):
834
canvas_store.index("};\n\nexport const store")
835
]
836
surface_button_block = surfaces_js[
@@ -845,6 +845,7 @@ def test_surface_buttons_keep_modal_and_canvas_entry_points_separate():
845
assert "markSurfaceMounted(targetId)" in canvas_store
846
assert "isSurfaceRendered(id)" in canvas_store
847
assert "isSurfaceVisible(id)" in canvas_store
848
+ assert 'import { store as chatsStore } from "/components/sidebar/chats/chats-store.js";' in canvas_store
849
assert "async openLatest(surfaceId" in canvas_store
850
assert "async openModalSurface(surfaceId" in canvas_store
851
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():
857
assert "return await this.openModalSurface(targetId, payload);" in canvas_store
858
assert "return await this.open(targetId, payload);" in canvas_store
859
assert 'return await this.open(this.activeSurfaceId || this.panelSurfaces[0]?.id || "", { source: "mobile-toggle" });' in canvas_store
859
- assert "return true;" in should_render_block
860
+ assert "isWelcomeVisible()" in canvas_store
861
+ assert "return !this.isWelcomeVisible();" in should_render_block
862
assert "isMobileMode" not in should_render_block
863
+ assert 'document.body.classList.toggle("right-canvas-open", this.isOpen && !this.isMobileMode && this.shouldRender());' in canvas_store
864
assert "this.mountedSurfaces = {}" not in close_block
865
assert "surface?.close" not in close_block
866
assert "this.mountedSurfaces = {}" not in undock_block
tests/test_file_browser_navigation.py
+4
@@ -144,6 +144,7 @@ def test_file_browser_is_registered_as_right_canvas_surface() -> None:
144
register = read("extensions", "webui", "right_canvas_register_surfaces", "register-files.js")
145
panel = read("extensions", "webui", "right-canvas-panels", "files-panel.html")
146
input_store = read("webui", "components", "chat", "input", "input-store.js")
147
+ welcome_store = read("webui", "components", "welcome", "welcome-store.js")
148
149
assert 'id: "files"' in surfaces
150
assert 'title: "Files"' in surfaces
@@ -167,6 +168,9 @@ def test_file_browser_is_registered_as_right_canvas_surface() -> None:
168
assert 'data-surface-id="files"' in panel
169
assert 'path="modals/file-browser/file-browser.html" mode="canvas"' in panel
170
assert 'openLatestSurface("files"' in input_store
171
+ assert 'import { store as fileBrowserStore } from "/components/modals/file-browser/file-browser-store.js";' in welcome_store
172
+ assert "fileBrowserStore.open()" in welcome_store
173
+ assert "chatInputStore.browseFiles" not in welcome_store
174
175
176
def test_file_browser_reports_missing_directory(tmp_path: Path) -> None:
webui/components/canvas/AGENTS.md
+1
@@ -15,6 +15,7 @@
15
- Keep registered surfaces compatible with WebUI extension hooks.
16
- Preserve responsive layout and avoid overlapping the chat/sidebar shells.
17
- 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.
18
+- 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.
19
- In mobile mode, keep the rail below blocking modal layers and compact it on very narrow screens instead of letting it cover modal content.
20
21
## Work Guidance
webui/components/canvas/right-canvas-store.js
+12
-2
@@ -1,4 +1,5 @@
1
import { createStore } from "/js/AlpineStore.js";
2
+import { store as chatsStore } from "/components/sidebar/chats/chats-store.js";
3
import { callJsExtensions } from "/js/extensions.js";
4
import {
5
SURFACE_MODE_DOCKED,
@@ -135,6 +136,10 @@ const model = {
136
return true;
137
}
138
139
+ if (!this.shouldRender()) {
140
+ return await this.openModalSurface(targetId, payload);
141
+ }
142
+
143
if (this.isMobileMode) {
144
this.activeSurfaceId = targetId;
145
this.isOpen = false;
@@ -342,6 +347,7 @@ const model = {
347
},
348
349
async toggleCanvas() {
350
+ if (!this.shouldRender()) return false;
351
if (this.isMobileMode) {
352
return await this.open(this.activeSurfaceId || this.panelSurfaces[0]?.id || "", { source: "mobile-toggle" });
353
}
@@ -476,7 +482,7 @@ const model = {
482
applyLayoutState() {
483
this.updateLayoutMode();
484
document.documentElement.style.setProperty("--right-canvas-width", `${this.width}px`);
479
- document.body.classList.toggle("right-canvas-open", this.isOpen && !this.isMobileMode);
485
+ document.body.classList.toggle("right-canvas-open", this.isOpen && !this.isMobileMode && this.shouldRender());
486
document.body.classList.toggle("right-canvas-overlay-mode", this.isOverlayMode);
487
document.body.classList.toggle("right-canvas-mobile-mode", this.isMobileMode);
488
},
@@ -517,8 +523,12 @@ const model = {
523
return this.currentSurface()?.title || "Canvas";
524
},
525
526
+ isWelcomeVisible() {
527
+ return !chatsStore.selected;
528
+ },
529
+
530
shouldRender() {
521
- return true;
531
+ return !this.isWelcomeVisible();
532
},
533
};
534
webui/components/welcome/AGENTS.md
+1
@@ -16,6 +16,7 @@
16
- Do not show setup prompts for already configured plugins when backend status can prevent it.
17
- The welcome screen mounts the shared chat composer to start a new chat; keep it mutually exclusive with the normal chat input DOM.
18
- Render `system-resources` as the dedicated System Resources panel, not as a generic alert banner.
19
+- 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.
20
21
## Work Guidance
22
webui/components/welcome/welcome-store.js
+2
-2
@@ -2,7 +2,7 @@ import { createStore } from "/js/AlpineStore.js";
2
import { store as chatsStore } from "/components/sidebar/chats/chats-store.js";
3
import { store as memoryStore } from "/plugins/_memory/webui/memory-dashboard-store.js";
4
import { store as projectsStore } from "/components/projects/projects-store.js";
5
-import { store as chatInputStore } from "/components/chat/input/input-store.js";
5
+import { store as fileBrowserStore } from "/components/modals/file-browser/file-browser-store.js";
6
import * as API from "/js/api.js";
7
import { getCurrentUserISOString } from "/js/time-utils.js";
8
@@ -261,7 +261,7 @@ const model = {
261
memoryStore.openModal();
262
break;
263
case "files":
264
- chatInputStore.browseFiles();
264
+ fileBrowserStore.open();
265
break;
266
case "website":
267
window.open("https://agent-zero.ai", "_blank");