diff options
| author | Adam Malczewski <[email protected]> | 2026-06-22 00:36:31 +0900 |
|---|---|---|
| committer | Adam Malczewski <[email protected]> | 2026-06-22 00:36:31 +0900 |
| commit | 250575df308469c58477a53865f8763084612e5f (patch) | |
| tree | 1966514e814faf6170ac7d2e7f76ac3ecaff77f6 /src/features | |
| parent | 3be12bccbd7eed767968e18f460693325093006a (diff) | |
| download | dispatch-web-250575df308469c58477a53865f8763084612e5f.tar.gz dispatch-web-250575df308469c58477a53865f8763084612e5f.zip | |
feat(tabs): cross-device tab sync via conversation lifecycle
Consume the conversation lifecycle handoff ([email protected], [email protected]).
Re-pinned file: deps + re-mirrored .dispatch/*.reference.md.
- fetchOpenConversations() on connect: GET /conversations?status=active,idle
restores the tab bar across devices (merges with localStorage — opens new
tabs, removes closed ones, updates titles from backend)
- conversation.statusChanged WS handler: closed → removeTabLocally (no
re-POST); active → open tab + spinner; idle → update status map
- conversation.compacted WS handler: dispose stale store + cache, reload
history from server
- TabBar shows a spinner on active conversations (statusFor prop)
- closeTab refactored to use removeTabLocally (extracted cleanup)
- conformance guards + WS adapter tests cover all 3 new WsServerMessage types
686 tests green.
Diffstat (limited to 'src/features')
| -rw-r--r-- | src/features/tabs/ui/TabBar.svelte | 6 |
1 files changed, 6 insertions, 0 deletions
diff --git a/src/features/tabs/ui/TabBar.svelte b/src/features/tabs/ui/TabBar.svelte index 812a663..9d224b9 100644 --- a/src/features/tabs/ui/TabBar.svelte +++ b/src/features/tabs/ui/TabBar.svelte @@ -5,12 +5,15 @@ let { tabs, activeConversationId, + statusFor, onSelect, onClose, onNewDraft, }: { tabs: readonly Tab[]; activeConversationId: string | null; + /** Returns the conversation's lifecycle status, or undefined when unknown. */ + statusFor?: (conversationId: string) => string | undefined; onSelect: (conversationId: string) => void; onClose: (conversationId: string) => void; onNewDraft: () => void; @@ -84,6 +87,9 @@ {handles.get(tab.conversationId) ?? tab.conversationId} </span> <span class="min-w-0 flex-1 truncate text-left">{tab.title}</span> + {#if statusFor?.(tab.conversationId) === "active"} + <span class="loading loading-spinner loading-xs shrink-0 text-primary"></span> + {/if} <button class="btn btn-ghost btn-xs shrink-0" aria-label="Close tab" |
