diff options
| author | Adam Malczewski <[email protected]> | 2026-10-15 09:47:12 +0900 |
|---|---|---|
| committer | Adam Malczewski <[email protected]> | 2026-10-15 09:47:12 +0900 |
| commit | 391ddcfeb833d5ff06a1dfe35261648332f2bcda (patch) | |
| tree | e0233b19eb55b0318900fc0edea043b0016a0867 | |
| parent | 0a76db397e92c945fefabcf08ff89dcc97f6e6c3 (diff) | |
| download | dispatch-web-391ddcfeb833d5ff06a1dfe35261648332f2bcda.tar.gz dispatch-web-391ddcfeb833d5ff06a1dfe35261648332f2bcda.zip | |
feat(ui): Artix-style inset wells (chat thinking/tool blocks + sidebar views) and darkstrap navbar header in theme colours
| -rw-r--r-- | src/app.css | 24 | ||||
| -rw-r--r-- | src/app/App.svelte | 46 | ||||
| -rw-r--r-- | src/features/chat/ui.test.ts | 3 | ||||
| -rw-r--r-- | src/features/chat/ui/LazyCollapse.svelte | 4 | ||||
| -rw-r--r-- | src/features/chat/ui/ToolCard.svelte | 4 | ||||
| -rw-r--r-- | src/features/layout/ui/TilePanels.svelte | 4 |
6 files changed, 73 insertions, 12 deletions
diff --git a/src/app.css b/src/app.css index e5e2a99..3eae342 100644 --- a/src/app.css +++ b/src/app.css @@ -12,6 +12,30 @@ themes: monokai --default; } +/* Inset "well" — the Artix wiki's code blocks (darkstrap `pre`): a panel that + reads as pressed INTO the page — the page colour darkened, a dark inner + shadow under the top edge, a faint light catch-line under the bottom edge. + Shared by the chat's thinking/tool blocks (LazyCollapse) and the sidebar + views (TilePanels). darkstrap: background rgba(0,0,0,.3); box-shadow + rgba(255,255,255,.1) 0 1px 0, rgba(0,0,0,.8) 0 1px 7px 0 inset; radius 4px. + Ported onto the active daisyUI theme so it always matches, and toned down a + step: the fill is the theme's page colour (base-100 — both the chat and the + sidebar sit on it) darkened 18% (darkstrap: 30%), computed OPAQUE and exposed + as --inset-well-bg so sticky children can paint the same colour; the inner + shadow is 0.6 (darkstrap: 0.8); the catch-line is the theme's text colour at + 10% instead of plain white. In @layer components so utilities can override. */ +@layer components { + .inset-well { + --inset-well-bg: color-mix(in srgb, var(--color-base-100) 82%, black); + background-color: var(--inset-well-bg); + border: 0; + border-radius: var(--radius-box); + box-shadow: + 0 1px 0 color-mix(in srgb, var(--color-base-content) 10%, transparent), + inset 0 1px 7px 0 rgb(0 0 0 / 0.6); + } +} + /* Rendered-Markdown (assistant messages) typography — scoped to .markdown-body so it never leaks into the rest of the app. */ .markdown-body { diff --git a/src/app/App.svelte b/src/app/App.svelte index 9eaba9c..5ea1e07 100644 --- a/src/app/App.svelte +++ b/src/app/App.svelte @@ -88,14 +88,15 @@ } </script> -<main class="relative flex h-screen flex-col overflow-hidden pt-[5px]"> +<main class="relative flex h-screen flex-col overflow-hidden"> <!-- Slim header: the tab bar moved into the sidebar (the "Tabs" view), so the top row now shows the active tab's title on the left (or "New Tab" for an unstarted draft), with the build version + sidebar toggle on - the right. CHROME — stays outside the tree (layout-00 decision 2). --> - <div class="flex items-center justify-between gap-2 px-2 py-2"> + the right. CHROME — stays outside the tree (layout-00 decision 2). + `.app-navbar` (below): the Artix wiki's darkstrap navbar look. --> + <div class="app-navbar flex items-center justify-between gap-2 px-2 py-2"> <span - class="min-w-0 flex-1 shrink truncate pl-2 text-sm font-medium opacity-70" + class="min-w-0 flex-1 shrink truncate pl-2 text-sm font-semibold text-primary" data-testid="top-bar-title" title={topBarTitle} aria-label="Active conversation title" @@ -104,7 +105,7 @@ </span> <a href="/" - class="btn btn-ghost btn-sm shrink-0 px-2" + class="btn btn-ghost btn-sm shrink-0 px-2 text-primary" aria-label="Back to dashboard" title="Back to dashboard" onclick={(e) => { @@ -129,14 +130,14 @@ </svg> </a> <span - class="shrink-0 select-none px-1 font-mono text-[10px] leading-none text-base-content/30" + class="shrink-0 select-none px-1 font-mono text-[10px] leading-none text-secondary" title="Build version (git short hash)" > build: {__APP_VERSION__} </span> {#if collapseKey !== null} <button - class="btn btn-square btn-ghost btn-sm mx-1 shrink-0" + class="btn btn-square btn-ghost btn-sm mx-1 shrink-0 text-primary" aria-label={sidebarOpen ? "Close sidebar" : "Open sidebar"} aria-expanded={sidebarOpen} onclick={toggleSidebar} @@ -219,3 +220,34 @@ /> {/key} {/if} + +<style> + /* The Artix wiki's navbar (darkstrap `.navbar-inner` on a fixed-top bar): + a top-to-bottom gradient (#444 → #222), a 1px near-black bottom border + (#030303), a soft drop shadow under the bar (0 1px 10px), and text + engraved with a 1px black text-shadow. Ported onto the active daisyUI + theme so it always matches. darkstrap's bar is LIGHTER than its near-black + page (that's what makes the border + shadow read), so the gradient is + shifted to keep that relationship on our lighter page: it runs from the + page colour (base-100) lifted 20% toward the text colour down to base-100 + lifted 3%; the border is base-100 darkened 90%. The shadow is 0.5 + (darkstrap: 0.1 — invisible on our lighter page). + Positioned + z-indexed so the shadow paints over the content below. */ + .app-navbar { + position: relative; + z-index: 10; + flex-shrink: 0; + padding-top: calc(0.5rem + 5px); + background-image: linear-gradient( + to bottom, + color-mix(in srgb, var(--color-base-100) 80%, var(--color-base-content)), + color-mix(in srgb, var(--color-base-100) 97%, var(--color-base-content)) + ); + border-bottom: 1px solid color-mix(in srgb, var(--color-base-100) 10%, black); + box-shadow: 0 1px 10px rgb(0 0 0 / 0.5); + } + /* Engraved text — the title only; on the tiny faint build label it muddies. */ + .app-navbar :global([data-testid="top-bar-title"]) { + text-shadow: 0 1px 0 black; + } +</style> diff --git a/src/features/chat/ui.test.ts b/src/features/chat/ui.test.ts index 6a00e49..91585e2 100644 --- a/src/features/chat/ui.test.ts +++ b/src/features/chat/ui.test.ts @@ -285,8 +285,7 @@ describe("ChatView", () => { expect(collapse).not.toHaveClass("collapse-arrow"); // no indicator icon expect(collapse).not.toHaveClass("collapse-plus"); // Visible bubble, like tool cards. - expect(collapse).toHaveClass("bg-base-200"); - expect(collapse).toHaveClass("rounded-box"); + expect(collapse).toHaveClass("inset-well"); // shared recessed panel (app.css) expect(screen.getByRole("checkbox", { name: "Toggle thoughts" })).toBeInTheDocument(); }); diff --git a/src/features/chat/ui/LazyCollapse.svelte b/src/features/chat/ui/LazyCollapse.svelte index 912adb4..3ce34dd 100644 --- a/src/features/chat/ui/LazyCollapse.svelte +++ b/src/features/chat/ui/LazyCollapse.svelte @@ -31,7 +31,9 @@ }); </script> -<div class="collapse w-full rounded-box bg-base-200 text-sm"> +<!-- `.inset-well` (app.css): the recessed panel shared by thinking/tool blocks + and the sidebar views. --> +<div class="collapse inset-well w-full text-sm"> <input type="checkbox" aria-label={label} bind:checked={open} /> <div class="collapse-title flex min-h-0 min-w-0 items-center gap-2 py-2 whitespace-nowrap font-medium {titleClass}"> {@render title()} diff --git a/src/features/chat/ui/ToolCard.svelte b/src/features/chat/ui/ToolCard.svelte index 0a72bb5..10f8528 100644 --- a/src/features/chat/ui/ToolCard.svelte +++ b/src/features/chat/ui/ToolCard.svelte @@ -231,7 +231,9 @@ .output-controls { padding-block: 0.5rem; color: var(--color-base-content); - background: var(--color-base-200); + /* Same fill as the LazyCollapse inset well this sits in (opaque, so + scrolled output never shows through the sticky bar). */ + background: var(--inset-well-bg, var(--color-base-200)); } .sticky-controls { position: sticky; diff --git a/src/features/layout/ui/TilePanels.svelte b/src/features/layout/ui/TilePanels.svelte index 78dd7d1..f7e1846 100644 --- a/src/features/layout/ui/TilePanels.svelte +++ b/src/features/layout/ui/TilePanels.svelte @@ -44,7 +44,9 @@ style={renderMarker ? markerStyle(node.marker) : undefined} > {#each node.panels as panel, idx (idx)} - <div class="flex flex-col rounded-lg bg-base-200 p-3"> + <!-- `.inset-well` (app.css): recessed into the sidebar, like the chat's + thinking/tool blocks. --> + <div class="inset-well flex flex-col p-3"> <div class="flex items-center gap-1"> <select class="select select-bordered select-sm flex-1" |
