summaryrefslogtreecommitdiffhomepage
path: root/src/app
diff options
context:
space:
mode:
authorAdam Malczewski <[email protected]>2026-06-27 02:17:24 +0900
committerAdam Malczewski <[email protected]>2026-06-27 02:17:24 +0900
commit0e0601817712033b3247695646acd22d6496330a (patch)
tree87af231c6b97b0cdca87820f25522e9885b5791c /src/app
parent2fa03f8d7410c2b8d6be8e10ad088863e83d7177 (diff)
downloaddispatch-web-0e0601817712033b3247695646acd22d6496330a.tar.gz
dispatch-web-0e0601817712033b3247695646acd22d6496330a.zip
feat(sidebar-tabs): move tab bar from top into sidebar as vertical list
Diffstat (limited to 'src/app')
-rw-r--r--src/app/App.svelte44
1 files changed, 26 insertions, 18 deletions
diff --git a/src/app/App.svelte b/src/app/App.svelte
index 09be947..9a17999 100644
--- a/src/app/App.svelte
+++ b/src/app/App.svelte
@@ -40,7 +40,7 @@
import { parseMessageQueuePayload } from "../features/surface-host/logic/message-queue";
import { parseTodoPayload } from "../features/surface-host/logic/todo";
import TodoList from "../features/surface-host/ui/TodoList.svelte";
- import { manifest as tabsManifest, TabBar } from "../features/tabs";
+ import { manifest as tabsManifest, TabList } from "../features/tabs";
import { manifest as viewsManifest, ViewSidebar } from "../features/views";
import {
CwdField,
@@ -99,6 +99,7 @@
// The view kinds offered in the sidebar's dropdown. Generic data — the
// `viewContent` snippet below maps each kind id to its renderer.
const viewKinds = [
+ { id: "tabs", label: "Tabs" },
{ id: "model", label: "Model" },
{ id: "lsp", label: "Language Servers" },
{ id: "mcp", label: "MCP Servers" },
@@ -111,8 +112,9 @@
{ id: "settings", label: "Settings" },
] as const;
- // Default sidebar layout: just the Model view.
- const DEFAULT_VIEWS: readonly string[] = ["model"];
+ // Default sidebar layout: the Tabs list (the moved-from-top tab bar) plus the
+ // Model view, so a fresh user sees their conversations + model controls.
+ const DEFAULT_VIEWS: readonly string[] = ["tabs", "model"];
const sidebarStore = createLocalStore<readonly string[]>("dispatch.sidebar.views", {
storage: untrack(() => store.storage),
});
@@ -416,21 +418,12 @@
<main class="relative flex h-screen overflow-hidden">
<!-- LEFT: everything except the sidebar. The full-height sidebar is a sibling
- (below), so opening it shrinks this ENTIRE column — tab row included, which
- slides the hamburger left. -->
+ (below), so opening it shrinks this ENTIRE column. -->
<div class="flex min-w-0 flex-1 flex-col overflow-hidden pt-[5px]">
- <!-- Tab row: the tab strip fills + scrolls internally (flex-1 min-w-0), with
- a permanently seated hamburger pinned to the far right. -->
- <div class="flex min-w-0 items-center">
- <TabBar
- tabs={store.tabs}
- activeConversationId={store.activeConversationId}
- statusFor={(id) => store.conversationStatus(id)}
- onSelect={(id) => store.selectTab(id)}
- onClose={(id) => store.closeTab(id)}
- onNewDraft={() => store.newDraft()}
- onRename={(id, title) => store.renameTab(id, title)}
- />
+ <!-- Slim header: the tab bar moved into the sidebar (the "Tabs" view), so
+ the top row now holds only the build version + the sidebar toggle,
+ right-aligned. -->
+ <div class="flex items-center justify-end px-1">
<span
class="shrink-0 select-none px-1 font-mono text-[10px] leading-none text-base-content/30"
title="Build version (git short hash)"
@@ -585,7 +578,22 @@
{/if}
{#snippet viewContent(kind: string)}
- {#if kind === "model"}
+ {#if kind === "tabs"}
+ <!-- The conversation tab list (moved out of the top bar into the sidebar).
+ Re-mount per workspace so the filtered tab set + scroll reset cleanly
+ on a workspace switch. -->
+ {#key store.activeWorkspaceId}
+ <TabList
+ tabs={store.tabs}
+ activeConversationId={store.activeConversationId}
+ statusFor={(id) => store.conversationStatus(id)}
+ onSelect={(id) => store.selectTab(id)}
+ onClose={(id) => store.closeTab(id)}
+ onNewDraft={() => store.newDraft()}
+ onRename={(id, title) => store.renameTab(id, title)}
+ />
+ {/key}
+ {:else if kind === "model"}
<div class="flex flex-col gap-3">
<ModelSelector models={store.models} selected={store.activeModel} onSelect={handleSelectModel} />
<!-- Keyed on the workspace conversation (active tab OR draft) so the inputs