@growthub/cli 0.13.7 → 0.13.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (18) hide show
  1. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/helper/query/route.js +98 -34
  2. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/swarm-condition/route.js +106 -0
  3. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/components/WorkspaceActivationPanel.jsx +17 -0
  4. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/components/WorkspaceContributionGraph.jsx +119 -0
  5. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/components/WorkspaceHelperSetupModal.jsx +357 -0
  6. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/components/WorkspaceLensPanel.jsx +488 -0
  7. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/components/WorkspaceLensWalkthrough.jsx +69 -0
  8. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/HelperSidecar.jsx +37 -2
  9. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/globals.css +267 -25
  10. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/workspace-builder.jsx +55 -2
  11. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/workspace-lens/page.jsx +76 -0
  12. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/workspace-rail.jsx +140 -4
  13. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-activation.js +1025 -0
  14. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-data-model.js +2 -3
  15. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-helper-apply.js +24 -8
  16. package/assets/worker-kits/growthub-custom-workspace-starter-v1/kit.json +5 -0
  17. package/dist/index.js +5224 -5225
  18. package/package.json +1 -1
@@ -36,14 +36,18 @@ import Link from "next/link";
36
36
  import { usePathname, useRouter, useSearchParams } from "next/navigation";
37
37
  import {
38
38
  Archive,
39
+ BarChart3,
39
40
  ChevronDown,
40
41
  ChevronRight,
42
+ Database,
43
+ Eye,
41
44
  Folder,
42
45
  FolderPlus,
43
46
  GitBranch,
44
47
  Home,
45
48
  LayoutDashboard,
46
49
  MessageCircle,
50
+ Wrench,
47
51
  MessageCirclePlus,
48
52
  MoreHorizontal,
49
53
  MoreVertical,
@@ -66,6 +70,34 @@ import {
66
70
  nextNavItemId,
67
71
  } from "@/lib/workspace-helper-apply";
68
72
  import { listAvailableWorkflows } from "@/lib/nav-workflows";
73
+ import { deriveWorkspaceActivationState, deriveLensWalkthroughState, LENS_WALKTHROUGH_DISMISS_FLAG } from "@/lib/workspace-activation";
74
+ import { WorkspaceLensWalkthrough } from "./components/WorkspaceLensWalkthrough.jsx";
75
+ import { isHelperConfigured, WorkspaceHelperSetupModal } from "./components/WorkspaceHelperSetupModal.jsx";
76
+
77
+ // Set a flag on the governed workspace-ui-cache "activation" row (pure
78
+ // transform) — the same row the onboarding dismiss persists to. Returned
79
+ // config is PATCHed via the existing /api/workspace boundary.
80
+ function withUiCacheFlag(config, key, value) {
81
+ const dataModel = config?.dataModel && typeof config.dataModel === "object" ? config.dataModel : {};
82
+ const objects = Array.isArray(dataModel.objects) ? dataModel.objects : [];
83
+ const existing = objects.find((o) => o?.id === "workspace-ui-cache");
84
+ const cacheObject = existing || {
85
+ id: "workspace-ui-cache", label: "Workspace UI Cache", source: "Workspace UI Cache",
86
+ objectType: "custom", icon: "Settings", columns: ["id", key], rows: [],
87
+ binding: { mode: "manual", source: "Workspace UI Cache" },
88
+ };
89
+ const columns = Array.from(new Set([...(Array.isArray(cacheObject.columns) ? cacheObject.columns : ["id"]), key]));
90
+ const rows = Array.isArray(cacheObject.rows) ? cacheObject.rows : [];
91
+ const hasRow = rows.some((r) => r?.id === "activation");
92
+ const nextRows = hasRow
93
+ ? rows.map((r) => (r?.id === "activation" ? { ...r, [key]: value } : r))
94
+ : [...rows, { id: "activation", [key]: value }];
95
+ const nextCache = { ...cacheObject, columns, rows: nextRows };
96
+ const nextObjects = existing
97
+ ? objects.map((o) => (o?.id === "workspace-ui-cache" ? nextCache : o))
98
+ : [...objects, nextCache];
99
+ return { ...config, dataModel: { ...dataModel, objects: nextObjects } };
100
+ }
69
101
  import { ICON_PICKER_SET, LucideIcon } from "./data-model/components/dm-shared.jsx";
70
102
 
71
103
  function textColorForAccent(accent) {
@@ -1504,6 +1536,40 @@ export function WorkspaceRail({
1504
1536
  const workspaceName = branding.name || workspaceConfig?.name || "Growthub Workspace";
1505
1537
  const pathname = usePathname() || "/";
1506
1538
  const router = useRouter();
1539
+ // Workspace Lens unlocks only after the primary activation loop completes —
1540
+ // onboarding first, operating surface second. Derived from the same config
1541
+ // the rail already holds, so the gate is consistent across every page.
1542
+ const lensUnlocked = useMemo(
1543
+ () => Boolean(deriveWorkspaceActivationState({ workspaceConfig: workspaceConfig || {} }).complete),
1544
+ [workspaceConfig],
1545
+ );
1546
+ // One-time Workspace Lens reveal: shown anchored to the (newly visible) lens
1547
+ // nav item only in the in-between state, and never on the lens page itself.
1548
+ const lensWalkthrough = useMemo(
1549
+ () => deriveLensWalkthroughState({ workspaceConfig: workspaceConfig || {} }),
1550
+ [workspaceConfig],
1551
+ );
1552
+ const showLensReveal = lensWalkthrough.show && pathname === "/";
1553
+ const lensNavRef = useRef(null);
1554
+ const [lensRevealStyle, setLensRevealStyle] = useState(null);
1555
+ const dismissLensWalkthrough = useCallback(async () => {
1556
+ const next = withUiCacheFlag(workspaceConfig || {}, LENS_WALKTHROUGH_DISMISS_FLAG, true);
1557
+ if (onConfigChange) onConfigChange(next);
1558
+ try {
1559
+ const res = await fetch("/api/workspace", {
1560
+ method: "PATCH",
1561
+ headers: { "content-type": "application/json" },
1562
+ body: JSON.stringify({ dataModel: next.dataModel }),
1563
+ });
1564
+ const body = await res.json();
1565
+ if (body?.workspaceConfig && onConfigChange) onConfigChange(body.workspaceConfig);
1566
+ } catch {
1567
+ /* optimistic update already applied; cache write is best-effort */
1568
+ }
1569
+ }, [workspaceConfig, onConfigChange]);
1570
+ const enterLensWalkthrough = useCallback(() => {
1571
+ router.push("/workspace-lens?walkthrough=2");
1572
+ }, [router]);
1507
1573
 
1508
1574
  const [activeTab, setActiveTab] = useState("home");
1509
1575
  const [railCollapsed, setRailCollapsed] = useState(Boolean(defaultCollapsed));
@@ -1512,6 +1578,7 @@ export function WorkspaceRail({
1512
1578
  const [renameDraft, setRenameDraft] = useState("");
1513
1579
  const [chatSearch, setChatSearch] = useState("");
1514
1580
  const [chatExpanded, setChatExpanded] = useState(false);
1581
+ const [helperSetupOpen, setHelperSetupOpen] = useState(false);
1515
1582
  const menuWrapRef = useRef(null);
1516
1583
  const CHAT_PREVIEW_COUNT = 10;
1517
1584
 
@@ -1525,6 +1592,31 @@ export function WorkspaceRail({
1525
1592
  return () => document.removeEventListener("pointerdown", onPointerDown);
1526
1593
  }, [openMenuId]);
1527
1594
 
1595
+ useEffect(() => {
1596
+ if (!showLensReveal) {
1597
+ setLensRevealStyle(null);
1598
+ return undefined;
1599
+ }
1600
+ const updatePosition = () => {
1601
+ const rect = lensNavRef.current?.getBoundingClientRect();
1602
+ if (!rect) return;
1603
+ const width = 264;
1604
+ const gap = 12;
1605
+ const minLeft = 16;
1606
+ const maxLeft = Math.max(minLeft, window.innerWidth - width - 16);
1607
+ const left = Math.min(Math.max(rect.right + gap, minLeft), maxLeft);
1608
+ const top = Math.max(16, Math.round(rect.top - 4));
1609
+ setLensRevealStyle({ left, top });
1610
+ };
1611
+ updatePosition();
1612
+ window.addEventListener("resize", updatePosition);
1613
+ window.addEventListener("scroll", updatePosition, true);
1614
+ return () => {
1615
+ window.removeEventListener("resize", updatePosition);
1616
+ window.removeEventListener("scroll", updatePosition, true);
1617
+ };
1618
+ }, [showLensReveal, railCollapsed]);
1619
+
1528
1620
  const threads = useMemo(() => getHelperThreadRows(workspaceConfig), [workspaceConfig]);
1529
1621
 
1530
1622
  useEffect(() => {
@@ -1538,6 +1630,10 @@ export function WorkspaceRail({
1538
1630
  }, [railCollapsed]);
1539
1631
 
1540
1632
  const handleAskHelperClick = () => {
1633
+ if (!isHelperConfigured(workspaceConfig)) {
1634
+ setHelperSetupOpen(true);
1635
+ return;
1636
+ }
1541
1637
  if (onOpenHelper) {
1542
1638
  onOpenHelper();
1543
1639
  return;
@@ -1749,24 +1845,41 @@ export function WorkspaceRail({
1749
1845
  <div className="workspace-rail-activation-slot">{activationSlot}</div>
1750
1846
  ) : null}
1751
1847
  {dashboardsSlot ?? (
1752
- <Link href="/" className={pathname === "/" ? "active" : undefined}>
1753
- Builder
1848
+ <Link href="/" title="Builder" className={pathname === "/" ? "active" : undefined}>
1849
+ <Wrench size={15} aria-hidden="true" />
1850
+ <span className="workspace-nav-label">Builder</span>
1754
1851
  </Link>
1755
1852
  )}
1853
+ {lensUnlocked ? (
1854
+ <div className="workspace-rail-lens-nav" ref={lensNavRef}>
1855
+ <Link
1856
+ href="/workspace-lens"
1857
+ title="Workspace Lens"
1858
+ className={pathname.startsWith("/workspace-lens") ? "active" : undefined}
1859
+ >
1860
+ <Eye size={15} aria-hidden="true" />
1861
+ <span className="workspace-nav-label">Workspace Lens</span>
1862
+ </Link>
1863
+ </div>
1864
+ ) : null}
1756
1865
  {dataModelSlot ?? (
1757
1866
  <Link
1758
1867
  href="/data-model"
1868
+ title="Management"
1759
1869
  className={pathname.startsWith("/data-model") ? "active" : undefined}
1760
1870
  >
1761
- Management
1871
+ <Database size={15} aria-hidden="true" />
1872
+ <span className="workspace-nav-label">Management</span>
1762
1873
  </Link>
1763
1874
  )}
1764
1875
  {settingsSlot ?? (
1765
1876
  <Link
1766
1877
  href="/settings/general"
1878
+ title="Workspace Settings"
1767
1879
  className={"workspace-nav-bottom" + (pathname.startsWith("/settings") ? " active" : "")}
1768
1880
  >
1769
- Workspace Settings
1881
+ <Settings size={15} aria-hidden="true" />
1882
+ <span className="workspace-nav-label">Workspace Settings</span>
1770
1883
  </Link>
1771
1884
  )}
1772
1885
  </nav>
@@ -1934,6 +2047,29 @@ export function WorkspaceRail({
1934
2047
  <span className="status-dot" />
1935
2048
  {authority || "local-catalog"}
1936
2049
  </div>
2050
+ {showLensReveal && lensRevealStyle && typeof document !== "undefined"
2051
+ ? createPortal(
2052
+ <WorkspaceLensWalkthrough
2053
+ step={1}
2054
+ className="is-rail-reveal"
2055
+ style={lensRevealStyle}
2056
+ onPrimary={enterLensWalkthrough}
2057
+ onDismiss={dismissLensWalkthrough}
2058
+ />,
2059
+ document.body,
2060
+ )
2061
+ : null}
2062
+ <WorkspaceHelperSetupModal
2063
+ workspaceConfig={workspaceConfig}
2064
+ open={helperSetupOpen}
2065
+ onClose={() => setHelperSetupOpen(false)}
2066
+ onSaved={(nextConfig) => {
2067
+ setHelperSetupOpen(false);
2068
+ if (onConfigChange) onConfigChange(nextConfig);
2069
+ if (onOpenHelper) onOpenHelper();
2070
+ else router.push("/data-model?helper=open");
2071
+ }}
2072
+ />
1937
2073
  </aside>
1938
2074
  );
1939
2075
  }