diff options
| author | Adam Malczewski <[email protected]> | 2026-06-10 11:40:16 +0900 |
|---|---|---|
| committer | Adam Malczewski <[email protected]> | 2026-06-10 11:40:16 +0900 |
| commit | 7b345f132763fa6405ae858b74e46229629c19d9 (patch) | |
| tree | 4600200e5a92eccbe880f46b3760cf0b1217737d /src/features/tabs/ui | |
| parent | 89ca80bac1e143a4ec5ba6e2e1d4998acce2553c (diff) | |
| download | dispatch-web-7b345f132763fa6405ae858b74e46229629c19d9.tar.gz dispatch-web-7b345f132763fa6405ae858b74e46229629c19d9.zip | |
feat(tabs,app): tab id handles, fixed-width tabs-lift, slim shell + full-height sidebar
Tabs:
- short-handle ID badge per tab (shortest unique conversationId prefix, min 4)
- fixed-width (w-48) tabs with tabs-lift folder borders
Shell (composition root):
- drop the Dispatch title bar; tabs sit at the very top with a 5px gap
- big faded "Dispatch" watermark centered on an empty chat
- collapsible right sidebar (empty shell) spanning full window height: a
permanently right-pinned hamburger in the tab row toggles it; in-flow push
that shrinks the whole left column (tabs included) at >=lg, overlay + backdrop
below lg; open-by-default on wide / closed on narrow
- main is overflow-hidden with a min-w-0 shrink chain; app.css pins
html/body/#app height + body overflow hidden so the page never overflows
Diffstat (limited to 'src/features/tabs/ui')
| -rw-r--r-- | src/features/tabs/ui/TabBar.svelte | 27 |
1 files changed, 21 insertions, 6 deletions
diff --git a/src/features/tabs/ui/TabBar.svelte b/src/features/tabs/ui/TabBar.svelte index eb5b5e5..812a663 100644 --- a/src/features/tabs/ui/TabBar.svelte +++ b/src/features/tabs/ui/TabBar.svelte @@ -1,6 +1,6 @@ <script lang="ts"> import type { Tab } from "../tabs"; - import { isStuckToEnd } from "../tabs"; + import { isStuckToEnd, shortHandle } from "../tabs"; let { tabs, @@ -23,6 +23,15 @@ let scrollEl = $state<HTMLDivElement>(); let stuck = $state(false); + // Git-style short handle (shortest unique prefix) per open tab — the visible + // "tab ID". Derived from the set of open conversation ids; pure helper. + const handles = $derived.by(() => { + const ids = tabs.map((t) => t.conversationId); + const map = new Map<string, string>(); + for (const id of ids) map.set(id, shortHandle(id, ids)); + return map; + }); + function recompute(): void { const el = scrollEl; if (el === undefined) { @@ -55,11 +64,11 @@ }); </script> -<div bind:this={scrollEl} class="overflow-x-auto border-b border-base-300"> - <div class="tabs tabs-border min-w-max"> +<div bind:this={scrollEl} class="min-w-0 flex-1 overflow-x-auto"> + <div class="tabs tabs-lift min-w-max"> {#each tabs as tab (tab.conversationId)} <div - class="tab" + class="tab flex w-48 shrink-0 items-center gap-1.5" class:tab-active={tab.conversationId === activeConversationId} role="tab" tabindex="0" @@ -68,9 +77,15 @@ if (e.key === "Enter") onSelect(tab.conversationId); }} > - <span class="max-w-[120px] truncate">{tab.title}</span> + <span + class="shrink-0 rounded bg-base-300 px-1 py-0.5 font-mono text-[10px] leading-none text-base-content/60" + title="Tab ID" + > + {handles.get(tab.conversationId) ?? tab.conversationId} + </span> + <span class="min-w-0 flex-1 truncate text-left">{tab.title}</span> <button - class="btn btn-ghost btn-xs ml-1" + class="btn btn-ghost btn-xs shrink-0" aria-label="Close tab" onclick={(e) => { e.stopPropagation(); |
