From c4c1d76fec972e5d902a669f0cb1953eaa528f16 Mon Sep 17 00:00:00 2001 From: Adam Malczewski Date: Thu, 15 Oct 2026 14:38:50 +0900 Subject: feat(tabs): 2-column tab grid with nestable subagent groups + tab menu (add/remove from group) --- src/features/tabs/ui/TabCard.svelte | 232 ++++++++++++++++++++++++ src/features/tabs/ui/TabGroupBox.svelte | 53 ++++++ src/features/tabs/ui/TabList.svelte | 310 ++++++++++++++------------------ src/features/tabs/ui/TabMenu.svelte | 145 +++++++++++++++ 4 files changed, 568 insertions(+), 172 deletions(-) create mode 100644 src/features/tabs/ui/TabCard.svelte create mode 100644 src/features/tabs/ui/TabGroupBox.svelte create mode 100644 src/features/tabs/ui/TabMenu.svelte (limited to 'src/features/tabs/ui') diff --git a/src/features/tabs/ui/TabCard.svelte b/src/features/tabs/ui/TabCard.svelte new file mode 100644 index 0000000..a95f8e6 --- /dev/null +++ b/src/features/tabs/ui/TabCard.svelte @@ -0,0 +1,232 @@ + + + + + diff --git a/src/features/tabs/ui/TabGroupBox.svelte b/src/features/tabs/ui/TabGroupBox.svelte new file mode 100644 index 0000000..5cbfb27 --- /dev/null +++ b/src/features/tabs/ui/TabGroupBox.svelte @@ -0,0 +1,53 @@ + + +
+
+ {@render card(node.tab, true, node.children.length)} + {#each node.children as child (child.tab.conversationId)} + {#if child.children.length > 0} + + {:else} + {@render card(child.tab, false, 0)} + {/if} + {/each} +
+
diff --git a/src/features/tabs/ui/TabList.svelte b/src/features/tabs/ui/TabList.svelte index 6d85065..06d5aa6 100644 --- a/src/features/tabs/ui/TabList.svelte +++ b/src/features/tabs/ui/TabList.svelte @@ -1,183 +1,149 @@ +{#snippet card(tab: Tab, wide: boolean, subagentCount: number)} + +{/snippet} +
- -
- {#each tabs as tab (tab.conversationId)} - - {/each} -
+ +
(menu = null)} + > +
+ {#each forest.roots as node (node.tab.conversationId)} + {#if node.children.length > 0} + + {:else} + {@render card(node.tab, false, 0)} + {/if} + {/each} +
+
- +
+ +{#if menu !== null && menuTab !== null && onSetTabGroup !== undefined} + {@const setGroup = onSetTabGroup} + {@const target = menuTab.conversationId} + {@const children = [...menuChildren]} + setGroup(target, parentId)} + onRemoveFromGroup={() => setGroup(target, null)} + onUngroupChildren={() => { + for (const id of children) setGroup(id, null); + }} + onDismiss={() => (menu = null)} + /> +{/if} diff --git a/src/features/tabs/ui/TabMenu.svelte b/src/features/tabs/ui/TabMenu.svelte new file mode 100644 index 0000000..513463f --- /dev/null +++ b/src/features/tabs/ui/TabMenu.svelte @@ -0,0 +1,145 @@ + + + { + if (e.key === "Escape") onDismiss(); + }} + onpointerdown={(e) => { + if (menuEl !== undefined && !menuEl.contains(e.target as Node)) onDismiss(); + }} + onresize={onDismiss} +/> + + -- cgit v1.2.3