From c91ed9615b492fd0e2a2910b1d1778fc2ec5666a Mon Sep 17 00:00:00 2001 From: ojfbot <151410806+ojfbot@users.noreply.github.com> Date: Wed, 25 Mar 2026 01:24:08 -0500 Subject: [PATCH 1/8] feat: enforce subgrouping + tag badges on tab tiles - Strengthen LLM prompt to MUST-decompose large groups into children at specificity 5+ (6+ tabs) and 7+ (4+ tabs) - Add enforceSubgroups() post-LLM enforcement via domain clustering - Lower shouldRePrompt thresholds for more aggressive re-prompting - Add tabTags to LLM schema (Zod + prompts + types) - Render tag badge pills on large tab tiles (h>64, w>120) - Add .gt-tag pill CSS styles Co-Authored-By: Claude Opus 4.6 --- src/app/styles.css | 43 ++++++++++++ src/components/TreemapNode.tsx | 56 ++++++++++++++- src/lib/ai.ts | 13 +++- src/lib/grouping.ts | 125 ++++++++++++++++++++++++++++++--- src/lib/prompts.ts | 88 +++++++++++++++++++++-- src/types/index.ts | 9 +++ 6 files changed, 311 insertions(+), 23 deletions(-) diff --git a/src/app/styles.css b/src/app/styles.css index 77c82a8..82f7200 100644 --- a/src/app/styles.css +++ b/src/app/styles.css @@ -931,6 +931,29 @@ body { filter: brightness(1.3); } +/* Micro tab tiles inside gutter groups — microchip look */ +.gt-node--micro { + cursor: default; + padding: 0; + display: flex; + align-items: center; + justify-content: center; + border-width: 1px; + border-radius: 2px; + overflow: hidden; + pointer-events: none; +} + +.gt-node--micro .gt-node__tab-close { + display: none !important; +} + +.gt-node__tab-favicon--micro { + width: 12px; + height: 12px; + opacity: 0.7; +} + /* Focused group = background/header behind tab tiles */ .gt-node--focused { cursor: pointer; @@ -1091,6 +1114,26 @@ body { max-width: 100%; } +.gt-node__tab-tags { + display: flex; + flex-wrap: wrap; + gap: 3px; + max-width: 100%; + overflow: hidden; + margin-top: 1px; +} + +.gt-tag { + display: inline-flex; + padding: 1px 5px; + font-size: 8px; + font-weight: 500; + border-radius: 3px; + color: rgba(255, 255, 255, 0.85); + white-space: nowrap; + line-height: 1.4; +} + .gt-node__tab-close { position: absolute; top: 4px; diff --git a/src/components/TreemapNode.tsx b/src/components/TreemapNode.tsx index 8b2a8e4..7dc64f1 100644 --- a/src/components/TreemapNode.tsx +++ b/src/components/TreemapNode.tsx @@ -237,9 +237,14 @@ function ChildGroupContent({ onClick={() => onClickGroup(group.id)} data-node-id={node.id} > - + {group.label} - {group.sublabel && / {group.sublabel}} + {w > 60 && group.sublabel && ( + / {group.sublabel} + )} ); @@ -271,6 +276,7 @@ function TabContent({ onCloseTab: (id: number) => void; }) { const tab = node.tab!; + const isMicro = node.opacity < 1; const domain = useMemo(() => getDomain(tab.url), [tab.url]); const nodeRef = useRef(null); @@ -280,6 +286,7 @@ function TabContent({ const hoverTimer = useRef>(); const onEnter = useCallback(() => { + if (isMicro) return; hoverTimer.current = setTimeout(() => { if (nodeRef.current) { const r = nodeRef.current.getBoundingClientRect(); @@ -297,13 +304,46 @@ function TabContent({ .catch(() => {}); } }, 200); - }, [tab.thumbnail, tab.id, capturedThumb]); + }, [tab.thumbnail, tab.id, capturedThumb, isMicro]); const onLeave = useCallback(() => { clearTimeout(hoverTimer.current); setHovered(false); }, []); + // Micro gutter tabs — minimal tile with favicon only + if (isMicro) { + return ( +
onFocusTab(tab.id)} + data-node-id={node.id} + > + {w > 10 && h > 10 && ( + { + (e.target as HTMLImageElement).style.display = "none"; + }} + /> + )} +
+ ); + } + return (
56 && {domain}} + {h > 64 && w > 120 && tab.tags && tab.tags.length > 0 && ( +
+ {tab.tags.slice(0, 3).map((tag) => ( + + {tag} + + ))} +
+ )} +
+
+ + +

+ When enabled, browser signals are sent to the LLM for smarter grouping. + Data is gathered fresh per request and never stored. + {enrichment !== "off" && " Additional browser permissions will be requested."} +

+
+