Refine right canvas rail controls

Move the right canvas rail into a centered floating socket in both open and closed states so chat space is preserved and the canvas content keeps the full panel width. Add a dedicated dock-style show/hide control, make surface buttons select surfaces without hiding the canvas, remove the header close button, and align the diff viewer header padding with shared spacing tokens. Update right-canvas.css

Alessandro committed Apr 27, 2026 at 00:30 UTC e92e518ab25883222f7f5bc38f637982e64e4ae0
4 files changed +50 -27
plugins/_diff_viewer/webui/diff-viewer-panel.html
+1 -1
@@ -210,7 +210,7 @@
210 align-items: center;
211 gap: 6px;
212 min-height: 44px;
213 - padding: 7px 9px;
213 + padding: var(--spacing-xs) var(--spacing-md);
214 border-bottom: 1px solid color-mix(in srgb, var(--color-border) 66%, transparent);
215 background: color-mix(in srgb, var(--color-background) 92%, #000 8%);
216 }
webui/components/canvas/right-canvas-store.js
+8
@@ -175,6 +175,14 @@ const model = {
175 return await this.open(targetId, payload);
176 },
177
178 + async toggleCanvas() {
179 + if (this.isOpen) {
180 + await this.close();
181 + return false;
182 + }
183 + return await this.open(this.activeSurfaceId || this.surfaces[0]?.id || "");
184 + },
185 +
186 setWidth(px, options = {}) {
187 const { persist = true } = options;
188 const max = this.maxWidth();
webui/components/canvas/right-canvas.css
+26 -16
@@ -39,14 +39,30 @@ body.right-canvas-resizing {
39 }
40
41 .right-canvas-rail {
42 + position: absolute;
43 + top: 50%;
44 + right: 100%;
45 + z-index: 4;
46 display: flex;
47 flex: 0 0 52px;
48 flex-direction: column;
49 align-items: center;
50 gap: 7px;
51 + height: auto;
52 + max-height: min(70vh, calc(100vh - 32px));
53 padding: 10px 7px;
48 - border-right: 1px solid var(--right-canvas-border);
49 - background: color-mix(in srgb, var(--color-background) 92%, #000 8%);
54 + transform: translateY(-50%);
55 + border-right: 0;
56 + border-radius: 8px 0 0 8px;
57 + box-shadow: -10px 0 30px rgba(0, 0, 0, 0.24);
58 + background: var(--color-chat-background);
59 +}
60 +
61 +.right-canvas-rail-separator {
62 + width: 24px;
63 + height: 1px;
64 + margin: 1px 0 3px;
65 + background: color-mix(in srgb, var(--right-canvas-border) 76%, transparent);
66 }
67
68 .right-canvas.is-closed {
@@ -57,25 +73,15 @@ body.right-canvas-resizing {
73 background: transparent;
74 }
75
76 +.right-canvas.is-open {
77 + overflow: visible;
78 +}
79 +
80 .right-canvas.is-closed .right-canvas-shell,
81 .right-canvas.is-closed .right-canvas-resize-handle {
82 display: none;
83 }
84
65 -.right-canvas.is-closed .right-canvas-rail {
66 - position: absolute;
67 - top: 50%;
68 - right: 100%;
69 - z-index: 2;
70 - height: auto;
71 - max-height: min(70vh, calc(100vh - 32px));
72 - transform: translateY(-50%);
73 - border: 1px solid var(--right-canvas-border);
74 - border-right: 0;
75 - border-radius: 8px 0 0 8px;
76 - box-shadow: -10px 0 30px rgba(0, 0, 0, 0.24);
77 -}
78 -
85 .right-canvas-rail-button,
86 .right-canvas-icon-button,
87 .right-canvas-tab {
@@ -102,6 +108,10 @@ body.right-canvas-resizing {
108 transition: background-color 0.16s ease, border-color 0.16s ease, opacity 0.16s ease;
109 }
110
111 +.right-canvas-toggle-button {
112 + color: color-mix(in srgb, var(--color-primary) 70%, var(--color-text));
113 +}
114 +
115 .right-canvas-rail-button:hover,
116 .right-canvas-icon-button:hover,
117 .right-canvas-rail-button.is-active {
webui/components/canvas/right-canvas.html
+15 -10
@@ -24,6 +24,20 @@
24 <div class="right-canvas-resize-handle" title="Resize canvas" @pointerdown="$store.rightCanvas.startResize($event)"></div>
25
26 <nav class="right-canvas-rail" aria-label="Canvas surfaces">
27 + <button
28 + type="button"
29 + class="right-canvas-rail-button right-canvas-toggle-button"
30 + :class="{ 'is-active': $store.rightCanvas.isOpen }"
31 + :title="$store.rightCanvas.isOpen ? 'Hide canvas' : 'Show canvas'"
32 + :aria-label="$store.rightCanvas.isOpen ? 'Hide canvas' : 'Show canvas'"
33 + :aria-pressed="$store.rightCanvas.isOpen.toString()"
34 + @click="$store.rightCanvas.toggleCanvas()"
35 + >
36 + <span class="material-symbols-outlined">dock_to_right</span>
37 + </button>
38 +
39 + <div class="right-canvas-rail-separator" aria-hidden="true"></div>
40 +
41 <template x-for="surface in $store.rightCanvas.surfaces" :key="surface.id">
42 <button
43 type="button"
@@ -31,7 +45,7 @@
45 :class="{ 'is-active': $store.rightCanvas.isSurfaceActive(surface.id) && $store.rightCanvas.isOpen }"
46 :title="surface.title"
47 :aria-label="surface.title"
34 - @click="$store.rightCanvas.toggle(surface.id)"
48 + @click="$store.rightCanvas.open(surface.id)"
49 >
50 <span class="material-symbols-outlined" x-text="surface.icon"></span>
51 </button>
@@ -73,15 +87,6 @@
87 >
88 <span class="material-symbols-outlined">open_in_new</span>
89 </button>
76 - <button
77 - type="button"
78 - class="right-canvas-icon-button"
79 - title="Close canvas"
80 - aria-label="Close canvas"
81 - @click="$store.rightCanvas.close()"
82 - >
83 - <span class="material-symbols-outlined">close</span>
84 - </button>
90 <x-extension id="right-canvas-toolbar-end"></x-extension>
91 </div>
92 </header>