feat(ui): Design refresh with dark mode, assistant, and global search#74
Conversation
Replace the unused PrimeVue dependency and ad-hoc colours with a semantic design-token layer that supports a persisted light and dark theme, and consolidate iconography onto a single offline Iconify set. Existing screens, modals, and cards were migrated to the tokens so they adapt to the theme.
Refresh the sidebar and header to the new design language with a light and dark toggle, and add a global command palette reachable with Ctrl or Cmd+K that searches deployments, containers, images, volumes, and networks as well as every page.
Replace the header AI button with a floating launcher and move the chat into a right-hand slide-over with sample prompts, a history view, and file-upload affordances. File and log viewers can now open the assistant inline beside the content, passing what is on screen as context.
Single start, stop, restart, and rebuild actions ran silently in the background. Each now shows a per-action spinner, disables only the affected control while it runs, and reports success or failure with a toast.
| @@ -0,0 +1,34 @@ | |||
| import { ref } from "vue"; | |||
There was a problem hiding this comment.
The current state uses a Set<string> which is not reactive in the way Vue expects if you assign to it using busy.value = new Set(...) without careful tracking. While functional here because of complete re-assignment, consider using a reactive(new Set()) or simply an object map Record<string, boolean> for better performance and standard Vue reactivity patterns.
| import { ref } from "vue"; | |
| import { reactive } from "vue"; | |
| import { useNotificationsStore } from "@/stores/notifications"; | |
| export function useActionRunner() { | |
| const busy = reactive<Record<string, boolean>>({}); | |
| const notifications = useNotificationsStore(); | |
| const isBusy = (key: string) => !!busy[key]; | |
| async function run( | |
| key: string, | |
| fn: () => Promise<unknown>, | |
| opts: { success?: string; error?: string } = {}, | |
| ): Promise<void> { | |
| if (busy[key]) return; | |
| busy[key] = true; | |
| try { | |
| await fn(); | |
| if (opts.success) notifications.success(opts.success); | |
| } catch (e) { | |
| const detail = e instanceof Error ? e.message : ""; | |
| notifications.error(opts.error ?? "Action failed", detail); | |
| } finally { | |
| delete busy[key]; | |
| } | |
| } | |
| return { isBusy, run }; | |
| } |
| activeIndex.value = 0; | ||
| }); | ||
|
|
||
| const isMac = typeof navigator !== "undefined" && /Mac|iPhone|iPad/.test(navigator.platform); |
There was a problem hiding this comment.
The check for Mac platforms using navigator.platform is deprecated. While it works for now, it's better to use the modern navigator.userAgentData if available, or check navigator.userAgent as a fallback.
| const isMac = typeof navigator !== "undefined" && /Mac|iPhone|iPad/.test(navigator.platform); | |
| const isMac = typeof navigator !== "undefined" && (/Mac|iPhone|iPad/.test(navigator.userAgent) || navigator.platform.includes('Mac')); |
| const entitiesLoaded = ref(false); | ||
| const entitiesLoading = ref(false); | ||
|
|
||
| async function loadEntities() { |
There was a problem hiding this comment.
This function fetches from multiple APIs concurrently. Since this is called on every focus of the search input, it might create unnecessary load. Consider debouncing the load or implementing a basic TTL cache if the entity list doesn't change frequently.

A design pass over the panel that clears long-standing UI debt: three competing icon sets, an unused PrimeVue dependency, no dark mode, and actions that ran silently. First of the Albacore UI passes; per-screen polish and modal consolidation come later.
Foundation. A semantic token layer drives a persisted light/dark theme (defaulting to the OS preference), iconography is unified on one offline Iconify set behind an
<Icon>wrapper, and the unused PrimeVue dependency is gone. The shell, modals, cards, tables, and swept screens use the tokens, so they follow the theme.Shell and search. Refreshed sidebar and header with a theme toggle, and a global command palette (Ctrl/Cmd+K) that searches deployments, containers, images, volumes, and networks alongside every page.
Assistant. The header button becomes a floating launcher; the chat moves into a right-hand slide-over with sample prompts, a history view, and file-upload UI. File and log viewers open the assistant inline beside the content, passing what is on screen as context.
Per-action feedback. Single start, stop, restart, and rebuild actions show a spinner on the affected control and toast success or failure, instead of running silently.
Two caveats: file uploads in the chat are UI-only for now, and the history view shows placeholder entries until the persistence API lands. Dark mode covers the shell, shared components, and modals; a few bespoke screens still need their own pass.