diff options
| author | Adam Malczewski <[email protected]> | 2026-06-07 14:35:53 +0900 |
|---|---|---|
| committer | Adam Malczewski <[email protected]> | 2026-06-07 14:35:53 +0900 |
| commit | 0cb08678ffead285afb1f93ba50cd5a144ed5e7d (patch) | |
| tree | cf1396f1d7a065b5777ede0fd64f67ae8d6063ec /src/app | |
| parent | 2663fe7f7b7eb438dc295fe9dea221aa8b8b8f81 (diff) | |
| download | dispatch-web-0cb08678ffead285afb1f93ba50cd5a144ed5e7d.tar.gz dispatch-web-0cb08678ffead285afb1f93ba50cd5a144ed5e7d.zip | |
feat(tabs): extract TabBar component with horizontal scroll + sticky end '+'
Move inline tab-bar markup from the composition root into a thin
presentational TabBar in the tabs feature (feature-as-a-library: pure
reducer -> reactive store -> UI). Adds overflow-x scroll (min-w-max strip)
and a sticky right-pinned new-chat '+' that floats over scrolling tabs.
Draft-on-select / create-on-send behavior unchanged.
Diffstat (limited to 'src/app')
| -rw-r--r-- | src/app/App.svelte | 42 |
1 files changed, 8 insertions, 34 deletions
diff --git a/src/app/App.svelte b/src/app/App.svelte index 811dc75..cc9866e 100644 --- a/src/app/App.svelte +++ b/src/app/App.svelte @@ -1,6 +1,7 @@ <script lang="ts"> import type { InvokeMessage } from "@dispatch/ui-contract"; import { ChatView, Composer, ModelSelector } from "../features/chat"; + import { TabBar } from "../features/tabs"; import { SurfaceView } from "../features/surface-host"; import type { AppStore } from "./store.svelte"; @@ -42,40 +43,13 @@ </div> {/if} - <div class="flex items-center gap-2 border-b border-base-300 px-4"> - <div class="tabs tabs-border flex-1"> - {#each store.tabs as tab (tab.conversationId)} - <div - class="tab" - class:tab-active={tab.conversationId === store.activeConversationId} - role="tab" - tabindex="0" - onclick={() => store.selectTab(tab.conversationId)} - onkeydown={(e) => { if (e.key === "Enter") store.selectTab(tab.conversationId); }} - > - <span class="max-w-[120px] truncate">{tab.title}</span> - <button - class="btn btn-ghost btn-xs ml-1" - aria-label="Close tab" - onclick={(e) => { - e.stopPropagation(); - store.closeTab(tab.conversationId); - }} - > - × - </button> - </div> - {/each} - <button - class="tab" - class:tab-active={store.activeConversationId === null} - onclick={() => store.newDraft()} - aria-label="New chat" - > - + - </button> - </div> - </div> + <TabBar + tabs={store.tabs} + activeConversationId={store.activeConversationId} + onSelect={(id) => store.selectTab(id)} + onClose={(id) => store.closeTab(id)} + onNewDraft={() => store.newDraft()} + /> <div class="flex flex-1 flex-col overflow-hidden"> <div class="flex items-center gap-2 px-4 py-2"> |
