summaryrefslogtreecommitdiffhomepage
path: root/src/features/tabs/ui
diff options
context:
space:
mode:
authorAdam Malczewski <[email protected]>2026-06-10 11:40:16 +0900
committerAdam Malczewski <[email protected]>2026-06-10 11:40:16 +0900
commit7b345f132763fa6405ae858b74e46229629c19d9 (patch)
tree4600200e5a92eccbe880f46b3760cf0b1217737d /src/features/tabs/ui
parent89ca80bac1e143a4ec5ba6e2e1d4998acce2553c (diff)
downloaddispatch-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.svelte27
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();