summaryrefslogtreecommitdiffhomepage
path: root/src/app
diff options
context:
space:
mode:
authorAdam Malczewski <[email protected]>2026-06-07 14:35:53 +0900
committerAdam Malczewski <[email protected]>2026-06-07 14:35:53 +0900
commit0cb08678ffead285afb1f93ba50cd5a144ed5e7d (patch)
treecf1396f1d7a065b5777ede0fd64f67ae8d6063ec /src/app
parent2663fe7f7b7eb438dc295fe9dea221aa8b8b8f81 (diff)
downloaddispatch-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.svelte42
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);
- }}
- >
- &times;
- </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">