frontend: quick actions redesign; collapsible wrapper

Alessandro committed Mar 24, 2026 at 18:06 UTC 1cc055c7d89190fcc32ebc3352afe94b7c304e89
6 files changed +282 -186
webui/components/sidebar/left-sidebar.html
+22 -19
@@ -10,27 +10,30 @@
10 <body>
11 <div x-data>
12 <template x-if="$store.sidebar">
13 - <div id="left-panel" class="panel" x-data :class="{'hidden': !$store.sidebar.isOpen}">
14 - <x-extension id="sidebar-start"></x-extension>
15 - <!--Sidebar upper elements-->
16 - <div class="left-panel-top no-scrollbar">
17 - <!-- Top Section: Header Icons + Quick Actions -->
18 - <x-component path="sidebar/top-section/sidebar-top.html"></x-component>
19 - <!-- Chats List -->
20 - <div class="config-section" id="chats-section">
21 - <x-component path="sidebar/chats/chats-list.html"></x-component>
13 + <div class="sidebar-shell">
14 + <x-component path="sidebar/top-section/header-icons.html"></x-component>
15 + <div id="left-panel" class="panel" x-data :class="{'hidden': !$store.sidebar.isOpen}">
16 + <x-extension id="sidebar-start"></x-extension>
17 + <!--Sidebar upper elements-->
18 + <div class="left-panel-top no-scrollbar">
19 + <!-- Top Section: Quick Actions -->
20 + <x-component path="sidebar/top-section/sidebar-top.html"></x-component>
21 + <!-- Chats List -->
22 + <div class="config-section" id="chats-section">
23 + <x-component path="sidebar/chats/chats-list.html"></x-component>
24 + </div>
25 +
26 + <!-- Tasks List -->
27 + <div class="config-section" id="tasks-section">
28 + <x-component path="sidebar/tasks/tasks-list.html"></x-component>
29 + </div>
30 </div>
23 -
24 - <!-- Tasks List -->
25 - <div class="config-section" id="tasks-section">
26 - <x-component path="sidebar/tasks/tasks-list.html"></x-component>
31 + <!--Sidebar lower elements-->
32 + <div class="left-panel-bottom">
33 + <x-component path="sidebar/bottom/sidebar-bottom.html"></x-component>
34 </div>
35 + <x-extension id="sidebar-end"></x-extension>
36 </div>
29 - <!--Sidebar lower elements-->
30 - <div class="left-panel-bottom">
31 - <x-component path="sidebar/bottom/sidebar-bottom.html"></x-component>
32 - </div>
33 - <x-extension id="sidebar-end"></x-extension>
37 </div>
38 </template>
39 </div>
@@ -85,7 +88,7 @@
88 /* Critical for allowing flex children to shrink */
89 overflow: hidden;
90 justify-content: space-between;
88 - margin-top: 6rem;
91 + margin-top: 6.5rem;
92 padding: var(--spacing-md) var(--spacing-md) 0 var(--spacing-md);
93 }
94
webui/components/sidebar/sidebar-store.js
+8 -2
@@ -93,10 +93,16 @@ const model = {
93 updateDropdownPosition(triggerElement) {
94 if (!triggerElement) return;
95 const rect = triggerElement.getBoundingClientRect();
96 + const menuWidth = Math.max(rect.width, 180);
97 + const viewportPadding = 8;
98 + const maxLeft = Math.max(
99 + viewportPadding,
100 + window.innerWidth - menuWidth - viewportPadding,
101 + );
102 this.dropdownStyle = {
103 top: `${rect.bottom + 8}px`,
98 - left: `${rect.left}px`,
99 - width: `${rect.width}px`
104 + left: `${Math.min(Math.max(rect.left, viewportPadding), maxLeft)}px`,
105 + width: `${menuWidth}px`
106 };
107 },
108 };
webui/components/sidebar/top-section/header-icons.html
+235 -73
@@ -3,79 +3,165 @@
3 <script type="module">
4 import { store as sidebarStore } from "/components/sidebar/sidebar-store.js";
5 import { store as chatsStore } from "/components/sidebar/chats/chats-store.js";
6 + import { store as projectsStore } from "/components/projects/projects-store.js";
7 </script>
8 </head>
9 <body>
9 - <div class="sidebar-header-stack">
10 - <!--
11 - Single chrome block for logo + toolbar (tokens aligned with .config-button in quick-actions).
12 - Spacer reserves in-flow height so quick-actions clears the fixed overlay (see .left-panel-top margin-top).
13 - -->
14 - <div id="sidebar-header-panel" class="sidebar-header-panel">
10 + <div
11 + class="sidebar-header-stack"
12 + x-data="{ dropdownOpen: false }"
13 + @click.outside="dropdownOpen = false"
14 + @keydown.escape.window="dropdownOpen = false"
15 + x-init="$watch('dropdownOpen', open => open && $nextTick(() => $store.sidebar.updateDropdownPosition($refs.quickActionsToggle)))"
16 + >
17 + <!-- Fixed panel that expands on hover -->
18 + <div id="sidebar-header-panel" class="sidebar-header-panel" :class="{ 'sidebar-header-panel-open': $store.sidebar.isOpen }">
19 <div id="logo-bar">
20 <a href="https://github.com/agent0ai/agent-zero" target="_blank" rel="noopener noreferrer" aria-label="Agent Zero on GitHub">
17 - <span id="a0-logo" role="img" aria-label="Agent Zero"></span>
21 + <div class="logo-container">
22 + <span id="a0-logo" class="logo-expanded" role="img" aria-label="Agent Zero"></span>
23 + <!-- Placeholder for the single 'A' logo asset -->
24 + <span id="a0-logo-collapsed" class="logo-collapsed" role="img" aria-label="Agent Zero"></span>
25 + </div>
26 </a>
27 </div>
28
21 - <div class="icons-section" id="hide-button" x-data>
29 + <div class="icons-section" id="hide-button">
30 <!-- Sidebar Toggle Button -->
31 <button id="toggle-sidebar" class="toggle-sidebar-button" @click="$store.sidebar.toggle()" aria-label="Toggle Sidebar" aria-expanded="false">
32 <span class="material-symbols-outlined" aria-hidden="true">menu</span>
33 </button>
34
27 - <button class="config-button header-action-button" id="header-dashboard" @click="deselectChat()" title="Dashboard" aria-label="Dashboard">
35 + <button class="config-button header-action-button" id="header-dashboard" @click="deselectChat()" title="Dashboard" aria-label="Dashboard" tabindex="-1">
36 <span class="material-symbols-outlined" aria-hidden="true">home</span>
37 </button>
38
31 - <button class="config-button header-action-button" id="header-plugins" @click="openModal('components/plugins/list/plugin-list.html')" title="Plugins" aria-label="Plugins">
39 + <button class="config-button header-action-button" id="header-plugins" @click="openModal('components/plugins/list/plugin-list.html')" title="Plugins" aria-label="Plugins" tabindex="-1">
40 <span class="material-symbols-outlined" aria-hidden="true">extension</span>
41 </button>
42
35 - <button class="config-button header-action-button" id="header-settings" @click="openModal('settings/settings.html')" title="Settings" aria-label="Settings">
43 + <button class="config-button header-action-button" id="header-settings" @click="openModal('settings/settings.html')" title="Settings" aria-label="Settings" tabindex="-1">
44 <span class="material-symbols-outlined" aria-hidden="true">settings</span>
45 </button>
46 +
47 + <button
48 + class="config-button header-action-button dropdown-toggle"
49 + id="header-more"
50 + x-ref="quickActionsToggle"
51 + @click="dropdownOpen = !dropdownOpen"
52 + title="More options"
53 + aria-label="More options"
54 + tabindex="-1"
55 + >
56 + <span class="material-symbols-outlined" aria-hidden="true" :class="dropdownOpen ? 'rotated' : ''">expand_more</span>
57 + </button>
58 </div>
39 - </div>
59
60 + <div class="dropdown-menu quick-actions-dropdown" x-show="dropdownOpen" :style="$store.sidebar.dropdownStyle">
61 + <div id="sidebar-quick-actions-dropdown-slot-start"></div>
62 +
63 + <div class="dropdown-header">Navigation</div>
64 +
65 + <button class="dropdown-item" @click="deselectChat(); dropdownOpen = false">
66 + <span class="material-symbols-outlined">home</span>
67 + <span>Dashboard</span>
68 + </button>
69 +
70 + <button class="dropdown-item" @click="$store.projects.openProjectsModal(); dropdownOpen = false">
71 + <span class="material-symbols-outlined">snippet_folder</span>
72 + <span>Projects</span>
73 + </button>
74 +
75 + <button class="dropdown-item" @click="openModal('components/modals/scheduler/scheduler-modal.html'); dropdownOpen = false">
76 + <span class="material-symbols-outlined">schedule</span>
77 + <span>Scheduler</span>
78 + </button>
79 +
80 + <button class="dropdown-item" @click="openModal('settings/settings.html'); dropdownOpen = false">
81 + <span class="material-symbols-outlined">settings</span>
82 + <span>Settings</span>
83 + </button>
84 +
85 + <div class="dropdown-separator"></div>
86 + <div class="dropdown-header">Chat Actions</div>
87 +
88 + <button class="dropdown-item" @click="$store.chats.newChat(); dropdownOpen = false">
89 + <span class="material-symbols-outlined">chat_add_on</span>
90 + <span>New Chat</span>
91 + </button>
92 +
93 + <button class="dropdown-item" @click="$store.chats.loadChats(); dropdownOpen = false">
94 + <span class="material-symbols-outlined">folder_open</span>
95 + <span>Load Chat</span>
96 + </button>
97 +
98 + <button class="dropdown-item" @click="$store.chats.saveChat(); dropdownOpen = false" :disabled="!$store.chats.selected">
99 + <span class="material-symbols-outlined">save</span>
100 + <span>Save Chat</span>
101 + </button>
102 +
103 + <button class="dropdown-item" @click="$confirmClick($event, () => { $store.chats.resetChat(); dropdownOpen = false })" :disabled="!$store.chats.selected">
104 + <span class="material-symbols-outlined">delete_sweep</span>
105 + <span>Clear Chat</span>
106 + </button>
107 +
108 + <div class="dropdown-separator"></div>
109 +
110 + <button class="dropdown-item" @click="$confirmClick($event, () => { $store.chats.logout(); dropdownOpen = false })" :disabled="!$store.chats.loggedIn">
111 + <span class="material-symbols-outlined">logout</span>
112 + <span>Logout</span>
113 + </button>
114 +
115 + <button class="dropdown-item" @click="$confirmClick($event, () => { $store.chats.restart(); dropdownOpen = false })">
116 + <span class="material-symbols-outlined">restart_alt</span>
117 + <span>Restart A0</span>
118 + </button>
119 +
120 + <div id="sidebar-quick-actions-dropdown-slot-end"></div>
121 + </div>
122 + </div>
123 </div>
124
125
126 <style>
45 - .sidebar-header-stack {
46 - /*
47 - Panel top (viewport) minus .left-panel-top margin-top: flow starts at 5rem; reserve overlap region.
48 - Tuned for: padding + logo 1.47rem + gap + icon row ~2.2rem + padding.
49 - */
50 - --sidebar-header-flow-clearance: calc(1.9rem + (var(--spacing-xs) * 5) + 1.47rem + 2.2rem - 5rem);
127 + :root {
128 + --header-panel-expanded-width: calc(250px - (var(--spacing-md) * 2));
129 + /* Padding left (0.625rem) + hamburger width (2.2rem) + padding right (0.625rem) = 3.45rem */
130 + --header-panel-collapsed-width: 3.45rem;
131 }
132
53 - /* Shell: same language as .config-button (quick-actions.html) */
133 + /* Shell: fixed panel with hover expansion */
134 .sidebar-header-panel {
135 position: fixed;
136 top: var(--spacing-md);
137 left: var(--spacing-md);
58 - width: calc(250px - (var(--spacing-md) * 2));
138 + width: var(--header-panel-collapsed-width);
139 box-sizing: border-box;
140 z-index: 1004;
141 display: flex;
142 flex-direction: column;
63 - align-items: stretch;
143 + align-items: flex-start;
144 padding: var(--spacing-xs) var(--spacing-sm);
65 - background-color: var(--color-panel);
66 - border: 0.05rem solid var(--color-border);
145 + background-color: var(--color-chat-background);
146 + border: none;
147 border-radius: 8px;
68 - transition: background-color var(--transition-speed),
69 - border-color var(--transition-speed), box-shadow var(--transition-speed),
70 - opacity var(--transition-speed) ease-in-out;
148 + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
149 + overflow: hidden;
150 + white-space: nowrap;
151 + transition: width 0.3s cubic-bezier(0.4, 0, 0.2, 1),
152 + background-color var(--transition-speed),
153 + border-color var(--transition-speed),
154 + box-shadow var(--transition-speed);
155 }
156
157 #logo-bar {
158 display: flex;
159 align-items: center;
76 - justify-content: center;
77 - width: 100%;
160 + justify-content: flex-start;
161 + width: calc(var(--header-panel-expanded-width) - (var(--spacing-sm) * 2));
162 padding-top: var(--spacing-xxs);
163 + /* Align logo with the center of the 2.2rem hamburger */
164 + padding-left: 0.1rem;
165 }
166
167 #logo-bar a {
@@ -84,20 +170,62 @@
170 width: min(100%, 11.75rem);
171 }
172
173 + .logo-container {
174 + position: relative;
175 + width: 100%;
176 + height: 1.64rem;
177 + }
178 +
179 + #a0-logo, #a0-logo-collapsed {
180 + position: absolute;
181 + top: 0; left: 8px;
182 + height: 100%;
183 + -webkit-mask-repeat: no-repeat;
184 + mask-repeat: no-repeat;
185 + background-color: var(--color-text);
186 + transition: opacity 0.3s ease;
187 + }
188 +
189 + /* Expanded wordmark logo */
190 + #a0-logo {
191 + width: 100%;
192 + left: 8px;
193 + -webkit-mask-image: url(/public/a0-fullDark.svg);
194 + mask-image: url(/public/a0-fullDark.svg);
195 + -webkit-mask-size: contain;
196 + mask-size: contain;
197 + -webkit-mask-position: left center;
198 + mask-position: left center;
199 + opacity: 0;
200 + }
201 +
202 + /* Collapsed symbol logo placeholder */
203 + #a0-logo-collapsed {
204 + width: 2.0rem; /* Scale to fit nicely above hamburger */
205 + -webkit-mask-image: url(/public/a0-collapsed.svg);
206 + mask-image: url(/public/a0-collapsed.svg);
207 + -webkit-mask-size: contain;
208 + mask-size: contain;
209 + -webkit-mask-position: left center;
210 + mask-position: left center;
211 + opacity: 0.8;
212 + }
213 +
214 #hide-button {
88 - display: grid;
89 - grid-template-columns: repeat(4, 1fr);
215 + display: flex;
216 gap: var(--spacing-xs);
91 - width: 100%;
217 + width: max-content;
218 + margin-top: var(--spacing-xs);
219 }
220
94 - /* Toolbar icons sit on the panel surface (no nested config-button boxes) */
221 + /* Toolbar icons */
222 .sidebar-header-panel #hide-button .config-button,
223 .sidebar-header-panel .toggle-sidebar-button {
224 background-color: transparent;
225 border: none;
226 border-radius: 4px;
227 box-shadow: none;
228 + flex: 0 0 auto;
229 }
230
231 .sidebar-header-panel #hide-button .config-button {
@@ -105,13 +233,16 @@
233 display: flex;
234 align-items: center;
235 justify-content: center;
108 - opacity: 0.8;
236 padding: var(--spacing-xs);
110 - min-width: 0;
111 - min-height: 2.2rem;
237 + width: 2.2rem;
238 + height: 2.2rem;
239 + opacity: 0;
240 + transform: translateX(-10px);
241 + pointer-events: none; /* Disable when collapsed */
242 transition: background-color var(--transition-speed),
113 - border-color var(--transition-speed), box-shadow var(--transition-speed),
114 - opacity var(--transition-speed);
243 + box-shadow var(--transition-speed),
244 + opacity 0.3s ease,
245 + transform 0.3s ease;
246 }
247
248 .sidebar-header-panel #hide-button .config-button .material-symbols-outlined {
@@ -119,16 +250,18 @@
250 transition: transform 0.2s ease;
251 }
252
253 + .sidebar-header-panel #hide-button .config-button .material-symbols-outlined.rotated {
254 + transform: rotate(180deg);
255 + }
256 +
257 .sidebar-header-panel #hide-button .config-button:hover {
258 background-color: var(--color-background-hover);
124 - opacity: 1;
259 + opacity: 1 !important;
260 box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--color-primary) 18%, transparent);
126 - z-index: 1;
127 - position: relative;
261 }
262
263 .sidebar-header-panel #hide-button .config-button:active {
131 - opacity: 0.5;
264 + opacity: 0.5 !important;
265 box-shadow: inset 0 0 0 999px rgba(0, 0, 0, 0.06);
266 }
267
@@ -139,10 +272,9 @@
272 display: flex;
273 align-items: center;
274 justify-content: center;
142 - min-width: 0;
143 - min-height: 2.2rem;
275 + width: 2.2rem;
276 + height: 2.2rem;
277 padding: var(--spacing-xs);
145 - -webkit-transition: all var(--transition-speed) ease-in-out;
278 transition: all var(--transition-speed) ease-in-out;
279 }
280
@@ -159,37 +291,75 @@
291
292 .toggle-sidebar-button .material-symbols-outlined {
293 font-size: 22px;
162 - -webkit-transition: all var(--transition-speed) ease;
294 transition: all var(--transition-speed) ease;
295 }
296
297 .toggle-sidebar-button:active .material-symbols-outlined {
167 - -webkit-transform: scaleY(0.8);
298 transform: scaleY(0.8);
299 }
300
171 - #a0-logo {
172 - display: block;
173 - width: 100%;
174 - height: 1.64rem;
175 - -webkit-mask-image: url(/public/a0-fullDark.svg);
176 - mask-image: url(/public/a0-fullDark.svg);
177 - -webkit-mask-repeat: no-repeat;
178 - mask-repeat: no-repeat;
179 - -webkit-mask-size: contain;
180 - mask-size: contain;
181 - -webkit-mask-position: left;
182 - mask-position: center;
183 - background-color: var(--color-text);
184 - opacity: 0.8;
185 - -webkit-transition: opacity var(--transition-speed) ease, background-color var(--transition-speed) ease;
186 - transition: opacity var(--transition-speed) ease, background-color var(--transition-speed) ease;
187 - }
301 + /* --- Desktop Hover Expand --- */
302 + @media (min-width: 769px) {
303 + .sidebar-header-panel.sidebar-header-panel-open,
304 + .sidebar-header-panel:hover,
305 + .sidebar-header-panel:focus-within {
306 + width: var(--header-panel-expanded-width);
307 + }
308
189 - #logo-bar a:hover #a0-logo {
190 - opacity: 1;
309 + .sidebar-header-panel.sidebar-header-panel-open #a0-logo,
310 + .sidebar-header-panel:hover #a0-logo,
311 + .sidebar-header-panel:focus-within #a0-logo {
312 + opacity: 0.8;
313 + }
314 +
315 + .sidebar-header-panel.sidebar-header-panel-open #logo-bar a:hover #a0-logo,
316 + .sidebar-header-panel:hover #logo-bar a:hover #a0-logo,
317 + .sidebar-header-panel:focus-within #logo-bar a:hover #a0-logo {
318 + opacity: 1;
319 + }
320 +
321 + .sidebar-header-panel.sidebar-header-panel-open #a0-logo-collapsed,
322 + .sidebar-header-panel:hover #a0-logo-collapsed,
323 + .sidebar-header-panel:focus-within #a0-logo-collapsed {
324 + opacity: 0;
325 + }
326 +
327 + .sidebar-header-panel.sidebar-header-panel-open #hide-button .config-button,
328 + .sidebar-header-panel:hover #hide-button .config-button,
329 + .sidebar-header-panel:focus-within #hide-button .config-button {
330 + opacity: 0.8;
331 + transform: translateX(0);
332 + pointer-events: auto;
333 + }
334 }
335
336 + /* --- Mobile Always Expanded --- */
337 + @media (max-width: 768px) {
338 + .sidebar-header-panel {
339 + width: var(--header-panel-expanded-width);
340 + transition: all 0.3s ease;
341 + }
342 +
343 + #a0-logo {
344 + opacity: 0.8;
345 + }
346 +
347 + #logo-bar a:hover #a0-logo {
348 + opacity: 1;
349 + }
350 +
351 + #a0-logo-collapsed {
352 + opacity: 0;
353 + }
354 +
355 + .sidebar-header-panel #hide-button .config-button {
356 + opacity: 0.8 !important;
357 + transform: translateX(0) !important;
358 + pointer-events: auto !important;
359 + }
360 + }
361 +
362 + /* Light mode fixes */
363 .light-mode .sidebar-header-panel {
364 background-color: var(--color-background);
365 color: #333333;
@@ -204,14 +374,6 @@
374 .light-mode .sidebar-header-panel .toggle-sidebar-button:active {
375 background-color: var(--color-background-hover);
376 }
207 -
208 - @media (max-width: 768px) {
209 - .sidebar-header-panel {
210 - -webkit-transition: all 0.3s ease;
211 - transition: all 0.3s ease;
212 - }
213 - }
377 </style>
378 </body>
379 </html>
217 -
webui/components/sidebar/top-section/quick-actions.html
+11 -89
@@ -1,104 +1,27 @@
1 <html>
2
3 -<head>
4 - <script type="module">
5 - import { store as chatsStore } from "/components/sidebar/chats/chats-store.js";
6 - import { store as projectsStore } from "/components/projects/projects-store.js";
7 - import { store as sidebarStore } from "/components/sidebar/sidebar-store.js";
8 - </script>
9 -</head>
3 +<head></head>
4
5 <body>
12 - <div class="wrapper" x-data="{ dropdownOpen: false }" @click.outside="dropdownOpen = false" @keydown.escape.window="dropdownOpen = false" x-init="$watch('dropdownOpen', v => v && $store.sidebar.updateDropdownPosition($el))">
13 - <div id="quick-actions">
6 + <div class="quick-actions-surface-host" x-data>
7 + <template x-teleport="#sidebar-quick-actions-dropdown-slot-start">
8 + <x-extension id="sidebar-quick-actions-dropdown-start"></x-extension>
9 + </template>
10
11 + <div id="quick-actions">
12 <x-extension id="sidebar-quick-actions-main-start"></x-extension>
16 -
13 <x-extension id="sidebar-quick-actions-main-end"></x-extension>
18 -
19 - <!-- Dropdown Toggle -->
20 - <button class="config-button dropdown-toggle" @click="dropdownOpen = !dropdownOpen" title="More options">
21 - <span class="material-symbols-outlined" :class="dropdownOpen ? 'rotated' : ''">expand_more</span>
22 - </button>
14 </div>
15
25 - <!-- Dropdown Menu -->
26 - <div class="dropdown-menu quick-actions-dropdown"
27 - x-show="dropdownOpen"
28 - :style="$store.sidebar.dropdownStyle">
29 -
30 - <x-extension id="sidebar-quick-actions-dropdown-start"></x-extension>
31 -
32 - <div class="dropdown-header">Navigation</div>
33 -
34 - <button class="dropdown-item" @click="deselectChat(); dropdownOpen = false">
35 - <span class="material-symbols-outlined">home</span>
36 - <span>Dashboard</span>
37 - </button>
38 -
39 - <button class="dropdown-item" @click="$store.projects.openProjectsModal(); dropdownOpen = false">
40 - <span class="material-symbols-outlined">snippet_folder</span>
41 - <span>Projects</span>
42 - </button>
43 -
44 - <button class="dropdown-item" @click="openModal('components/modals/scheduler/scheduler-modal.html'); dropdownOpen = false">
45 - <span class="material-symbols-outlined">schedule</span>
46 - <span>Scheduler</span>
47 - </button>
48 -
49 - <button class="dropdown-item" @click="openModal('settings/settings.html'); dropdownOpen = false">
50 - <span class="material-symbols-outlined">settings</span>
51 - <span>Settings</span>
52 - </button>
53 -
54 - <div class="dropdown-separator"></div>
55 - <div class="dropdown-header">Chat Actions</div>
56 -
57 - <button class="dropdown-item" @click="$store.chats.newChat(); dropdownOpen = false">
58 - <span class="material-symbols-outlined">chat_add_on</span>
59 - <span>New Chat</span>
60 - </button>
61 -
62 - <button class="dropdown-item" @click="$store.chats.loadChats(); dropdownOpen = false">
63 - <span class="material-symbols-outlined">folder_open</span>
64 - <span>Load Chat</span>
65 - </button>
66 -
67 - <button class="dropdown-item" @click="$store.chats.saveChat(); dropdownOpen = false" :disabled="!$store.chats.selected">
68 - <span class="material-symbols-outlined">save</span>
69 - <span>Save Chat</span>
70 - </button>
71 -
72 - <button class="dropdown-item" @click="$confirmClick($event, () => { $store.chats.resetChat(); dropdownOpen = false })" :disabled="!$store.chats.selected">
73 - <span class="material-symbols-outlined">delete_sweep</span>
74 - <span>Clear Chat</span>
75 - </button>
76 -
77 - <div class="dropdown-separator"></div>
78 -
79 -
80 - <button class="dropdown-item" @click="$confirmClick($event, () => { $store.chats.logout(); dropdownOpen = false })" :disabled="!$store.chats.loggedIn">
81 - <span class="material-symbols-outlined">logout</span>
82 - <span>Logout</span>
83 - </button>
84 -
85 - <button class="dropdown-item" @click="$confirmClick($event, () => { $store.chats.restart(); dropdownOpen = false })">
86 - <span class="material-symbols-outlined">restart_alt</span>
87 - <span>Restart A0</span>
88 - </button>
89 -
16 + <template x-teleport="#sidebar-quick-actions-dropdown-slot-end">
17 <x-extension id="sidebar-quick-actions-dropdown-end"></x-extension>
91 - </div>
18 + </template>
19 </div>
20 </body>
21
22 <style>
96 - /* Wrapper container for icon toolbar */
97 - .wrapper {
98 - position: relative;
99 - border-radius: 0.6rem;
100 - z-index: 100;
101 - /* Ensure dropdown appears above all sidebar content */
23 + .quick-actions-surface-host {
24 + display: contents;
25 }
26
27 /* Icon toolbar layout – grid for extension-friendly wrapping */
@@ -111,9 +34,8 @@
34 gap: var(--spacing-xs);
35 }
36
114 - .quick-actions-anchor {
37 + #quick-actions:not(:has(> x-extension:not(:empty))) {
38 display: none;
116 - pointer-events: none;
39 }
40
41 #quick-actions > x-extension:not(:empty) {
webui/components/sidebar/top-section/sidebar-top.html
+5 -3
@@ -9,9 +9,6 @@
9 <template x-if="$store.sidebar">
10 <div class="sidebar-top-wrapper">
11 <x-extension id="sidebar-top-wrapper-start"></x-extension>
12 - <!-- Header Icons (Toggle + Logo) -->
13 - <x-component path="sidebar/top-section/header-icons.html"></x-component>
14 -
12 <!-- Quick Actions Buttons -->
13 <x-component path="sidebar/top-section/quick-actions.html"></x-component>
14 <x-extension id="sidebar-top-wrapper-end"></x-extension>
@@ -27,6 +24,11 @@
24 flex-direction: column;
25 flex-shrink: 0;
26 }
27 +
28 + .sidebar-top-wrapper > x-component,
29 + .sidebar-top-wrapper > x-component > .quick-actions-surface-host {
30 + display: contents;
31 + }
32 </style>
33 </body>
34 </html>
webui/public/a0-collapsed.svg new
+1
@@ -0,0 +1 @@
1 +<svg version="1.2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 26" width="16" height="26"><style>.a{fill:#7a7a7a}</style><path class="a" d="m13 19.6c-1.6-2.8-3.3-5.7-5-8.6q-2.6 4.3-5.1 8.7h-2.5c2.5-4.4 7.6-13.1 7.6-13.1 0 0 5.1 8.7 7.6 13.1h-2.6z"/><path class="a" d="m11.2 19.7h-6.5q0.6-1.2 1.2-2.3h4.1q0.6 1.1 1.2 2.3z"/></svg>
\ No newline at end of file