@arbidocs/blocks 0.3.115 → 0.3.116

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.
package/dist/index.cjs CHANGED
@@ -2298,6 +2298,322 @@ function TestimonialGrid({ items, columns = "3", testId }) {
2298
2298
  i
2299
2299
  )) });
2300
2300
  }
2301
+ function NavItemLink({ item, onNavigate }) {
2302
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2303
+ reactRouterDom.NavLink,
2304
+ {
2305
+ to: item.to,
2306
+ end: item.end,
2307
+ onClick: onNavigate,
2308
+ "data-testid": tid("sidebar-nav", item.id),
2309
+ className: ({ isActive }) => react.cn(
2310
+ "group flex items-center gap-3 rounded-md px-3 py-2 text-sm font-medium transition-colors",
2311
+ isActive ? "bg-sidebar-accent text-sidebar-accent-foreground" : "text-sidebar-foreground hover:bg-sidebar-accent/60 hover:text-sidebar-accent-foreground"
2312
+ ),
2313
+ children: [
2314
+ /* @__PURE__ */ jsxRuntime.jsx(item.icon, { className: "size-[18px] shrink-0" }),
2315
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-1 truncate", children: item.label }),
2316
+ item.arbi && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Sparkles, { className: "size-3.5 text-sidebar-primary", "aria-label": "Powered by ARBI" })
2317
+ ]
2318
+ }
2319
+ );
2320
+ }
2321
+ function SidebarBody({
2322
+ sections,
2323
+ footerItems,
2324
+ logo,
2325
+ logoHref,
2326
+ logoLabel,
2327
+ sidebarFooter,
2328
+ onNavigate
2329
+ }) {
2330
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2331
+ logo && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-16 items-center border-b border-sidebar-border px-5", children: logoHref ? /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Link, { to: logoHref, "aria-label": logoLabel, onClick: onNavigate, children: logo }) : logo }),
2332
+ /* @__PURE__ */ jsxRuntime.jsx("nav", { className: "flex-1 space-y-6 overflow-y-auto px-3 py-5", children: sections.map((section) => /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
2333
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "px-3 pb-2 text-[10px] font-semibold uppercase tracking-[0.14em] text-sidebar-foreground/55", children: section.heading }),
2334
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-0.5", children: section.items.map((item) => /* @__PURE__ */ jsxRuntime.jsx(NavItemLink, { item, onNavigate }, item.id)) })
2335
+ ] }, section.heading)) }),
2336
+ (footerItems?.length || sidebarFooter) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "border-t border-sidebar-border px-3 py-3", children: [
2337
+ footerItems?.map((item) => /* @__PURE__ */ jsxRuntime.jsx(NavItemLink, { item, onNavigate }, item.id)),
2338
+ sidebarFooter
2339
+ ] })
2340
+ ] });
2341
+ }
2342
+ function AppShell({
2343
+ sections,
2344
+ footerItems,
2345
+ logo,
2346
+ logoHref,
2347
+ logoLabel,
2348
+ sidebarFooter,
2349
+ topbar,
2350
+ rightRail,
2351
+ children,
2352
+ mainClassName,
2353
+ testIdPrefix = "app"
2354
+ }) {
2355
+ const [open, setOpen] = react$1.useState(false);
2356
+ const location = reactRouterDom.useLocation();
2357
+ react$1.useEffect(() => {
2358
+ setOpen(false);
2359
+ }, [location.pathname]);
2360
+ const body = (onNavigate) => /* @__PURE__ */ jsxRuntime.jsx(
2361
+ SidebarBody,
2362
+ {
2363
+ sections,
2364
+ footerItems,
2365
+ logo,
2366
+ logoHref,
2367
+ logoLabel,
2368
+ sidebarFooter,
2369
+ onNavigate
2370
+ }
2371
+ );
2372
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2373
+ "div",
2374
+ {
2375
+ className: "flex h-screen overflow-hidden bg-background",
2376
+ "data-testid": `${testIdPrefix}-shell`,
2377
+ children: [
2378
+ /* @__PURE__ */ jsxRuntime.jsx(
2379
+ "aside",
2380
+ {
2381
+ className: "hidden h-full w-64 shrink-0 flex-col border-r border-sidebar-border bg-sidebar lg:flex",
2382
+ "data-testid": `${testIdPrefix}-sidebar`,
2383
+ children: body()
2384
+ }
2385
+ ),
2386
+ open && /* @__PURE__ */ jsxRuntime.jsxs(
2387
+ "div",
2388
+ {
2389
+ className: "fixed inset-0 z-50 lg:hidden",
2390
+ "data-testid": `${testIdPrefix}-sidebar-mobile`,
2391
+ children: [
2392
+ /* @__PURE__ */ jsxRuntime.jsx(
2393
+ "div",
2394
+ {
2395
+ className: "absolute inset-0 bg-black/60 backdrop-blur-sm",
2396
+ onClick: () => setOpen(false),
2397
+ "aria-hidden": true
2398
+ }
2399
+ ),
2400
+ /* @__PURE__ */ jsxRuntime.jsxs("aside", { className: "absolute inset-y-0 left-0 flex w-64 max-w-[82%] flex-col border-r border-sidebar-border bg-sidebar shadow-xl", children: [
2401
+ /* @__PURE__ */ jsxRuntime.jsx(
2402
+ "button",
2403
+ {
2404
+ type: "button",
2405
+ onClick: () => setOpen(false),
2406
+ className: "absolute right-3 top-4 z-10 flex size-8 items-center justify-center rounded-md text-sidebar-foreground/70 hover:bg-sidebar-accent/60 hover:text-sidebar-accent-foreground",
2407
+ "aria-label": "Close menu",
2408
+ "data-testid": `${testIdPrefix}-sidebar-close`,
2409
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "size-5" })
2410
+ }
2411
+ ),
2412
+ body(() => setOpen(false))
2413
+ ] })
2414
+ ]
2415
+ }
2416
+ ),
2417
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-w-0 flex-1 flex-col", children: [
2418
+ /* @__PURE__ */ jsxRuntime.jsxs(
2419
+ "header",
2420
+ {
2421
+ className: "flex h-16 shrink-0 items-center gap-4 border-b border-border bg-card/80 px-6 backdrop-blur",
2422
+ "data-testid": `${testIdPrefix}-topbar`,
2423
+ children: [
2424
+ /* @__PURE__ */ jsxRuntime.jsx(
2425
+ "button",
2426
+ {
2427
+ type: "button",
2428
+ onClick: () => setOpen(true),
2429
+ className: "flex size-9 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-accent hover:text-foreground lg:hidden",
2430
+ "data-testid": `${testIdPrefix}-nav-toggle`,
2431
+ "aria-label": "Open menu",
2432
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Menu, { className: "size-5" })
2433
+ }
2434
+ ),
2435
+ topbar
2436
+ ]
2437
+ }
2438
+ ),
2439
+ /* @__PURE__ */ jsxRuntime.jsx(
2440
+ "main",
2441
+ {
2442
+ className: react.cn("flex-1 overflow-y-auto", mainClassName),
2443
+ "data-testid": `${testIdPrefix}-main`,
2444
+ children
2445
+ }
2446
+ )
2447
+ ] }),
2448
+ rightRail
2449
+ ]
2450
+ }
2451
+ );
2452
+ }
2453
+ var LiveDataContext = react$1.createContext(false);
2454
+ function useLiveDataActive() {
2455
+ return react$1.useContext(LiveDataContext);
2456
+ }
2457
+ function LiveDataProvider({ children }) {
2458
+ return /* @__PURE__ */ jsxRuntime.jsx(LiveDataContext.Provider, { value: true, children });
2459
+ }
2460
+ function WidgetPlaceholder({
2461
+ label,
2462
+ icon: Icon = lucideReact.Sparkles,
2463
+ testId
2464
+ }) {
2465
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2466
+ "div",
2467
+ {
2468
+ className: "flex items-center gap-3 rounded-xl border border-dashed border-border bg-muted/30 px-5 py-6 text-sm text-muted-foreground",
2469
+ "data-testid": testId,
2470
+ children: [
2471
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex size-9 shrink-0 items-center justify-center rounded-full bg-muted text-muted-foreground", children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { className: "size-4" }) }),
2472
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
2473
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-foreground", children: label }),
2474
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ml-1", children: "\xB7 live in the app" })
2475
+ ] })
2476
+ ]
2477
+ }
2478
+ );
2479
+ }
2480
+ function useActiveWorkspaceId() {
2481
+ const { workspaceId, fallbackWorkspaceId } = useConnection();
2482
+ return workspaceId ?? fallbackWorkspaceId ?? void 0;
2483
+ }
2484
+ function timeAgo(iso) {
2485
+ if (!iso) return "";
2486
+ const then = new Date(iso).getTime();
2487
+ if (Number.isNaN(then)) return "";
2488
+ const s = Math.max(0, Math.round((Date.now() - then) / 1e3));
2489
+ if (s < 60) return "just now";
2490
+ const m = Math.round(s / 60);
2491
+ if (m < 60) return `${m}m ago`;
2492
+ const h = Math.round(m / 60);
2493
+ if (h < 24) return `${h}h ago`;
2494
+ return `${Math.round(h / 24)}d ago`;
2495
+ }
2496
+ var METRIC_SOURCES = {
2497
+ documents: { label: "Documents", icon: lucideReact.FileText },
2498
+ conversations: { label: "Agent threads", icon: lucideReact.Bot }
2499
+ };
2500
+ var asMetricSource = (s) => s in METRIC_SOURCES ? s : "documents";
2501
+ function AgentActivityLive({
2502
+ title,
2503
+ limit,
2504
+ testId
2505
+ }) {
2506
+ const wsId = useActiveWorkspaceId();
2507
+ const { data, isLoading } = react.useConversations(wsId);
2508
+ const rows = [...data ?? []].sort((a, b) => +new Date(b.updated_at ?? 0) - +new Date(a.updated_at ?? 0)).slice(0, limit);
2509
+ return /* @__PURE__ */ jsxRuntime.jsxs(react.Card, { variant: "elevated", className: "rounded-xl p-5", "data-testid": testId, children: [
2510
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-3 flex items-center gap-2", children: [
2511
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Bot, { className: "size-4 text-muted-foreground" }),
2512
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-semibold text-foreground", children: title })
2513
+ ] }),
2514
+ isLoading ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground", "data-testid": `${testId}-loading`, children: "Loading agent threads\u2026" }) : rows.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground", "data-testid": `${testId}-empty`, children: "No agent threads yet." }) : /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "space-y-2", "data-testid": `${testId}-list`, children: rows.map((c, i) => /* @__PURE__ */ jsxRuntime.jsxs(
2515
+ "li",
2516
+ {
2517
+ className: "flex items-center justify-between gap-3",
2518
+ "data-testid": `${testId}-item-${i}`,
2519
+ children: [
2520
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex items-center gap-2 truncate", children: [
2521
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.MessagesSquare, { className: "size-3.5 shrink-0 text-muted-foreground" }),
2522
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate text-sm text-foreground", children: c.title?.trim() || "Untitled thread" })
2523
+ ] }),
2524
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0 text-[11px] text-muted-foreground", children: timeAgo(c.updated_at) })
2525
+ ]
2526
+ },
2527
+ c.external_id ?? i
2528
+ )) })
2529
+ ] });
2530
+ }
2531
+ function LiveMetricInner({
2532
+ label,
2533
+ source,
2534
+ accent,
2535
+ testId
2536
+ }) {
2537
+ const wsId = useActiveWorkspaceId();
2538
+ const docs = react.useDocuments(wsId, { enabled: source === "documents" });
2539
+ const convos = react.useConversations(wsId, { enabled: source === "conversations" });
2540
+ const query = source === "documents" ? docs : convos;
2541
+ const count = Array.isArray(query.data) ? query.data.length : 0;
2542
+ return /* @__PURE__ */ jsxRuntime.jsx(
2543
+ MetricCard,
2544
+ {
2545
+ label: label || METRIC_SOURCES[source].label,
2546
+ value: query.isLoading ? "\u2014" : String(count),
2547
+ accent,
2548
+ icon: METRIC_SOURCES[source].icon,
2549
+ testId
2550
+ }
2551
+ );
2552
+ }
2553
+ function AgentActivityBlock({
2554
+ title,
2555
+ limit,
2556
+ testId
2557
+ }) {
2558
+ return useLiveDataActive() ? /* @__PURE__ */ jsxRuntime.jsx(AgentActivityLive, { title, limit, testId }) : /* @__PURE__ */ jsxRuntime.jsx(WidgetPlaceholder, { label: String(title || "Agent activity"), icon: lucideReact.Bot, testId });
2559
+ }
2560
+ function LiveMetricBlock({
2561
+ label,
2562
+ source,
2563
+ accent,
2564
+ testId
2565
+ }) {
2566
+ const src = asMetricSource(source);
2567
+ return useLiveDataActive() ? /* @__PURE__ */ jsxRuntime.jsx(LiveMetricInner, { label, source: src, accent, testId }) : /* @__PURE__ */ jsxRuntime.jsx(
2568
+ WidgetPlaceholder,
2569
+ {
2570
+ label: String(label || METRIC_SOURCES[src].label),
2571
+ icon: METRIC_SOURCES[src].icon,
2572
+ testId
2573
+ }
2574
+ );
2575
+ }
2576
+ function createLiveWidgets(kit) {
2577
+ const { blockTestId } = kit;
2578
+ const AgentActivityWidget = {
2579
+ label: "Agent activity (live)",
2580
+ fields: {
2581
+ title: { type: "text", label: "Title" },
2582
+ limit: { type: "number", label: "Rows", min: 1, max: 8 }
2583
+ },
2584
+ defaultProps: { title: "Recent agent activity", limit: 4 },
2585
+ render: ({ id, title, limit }) => /* @__PURE__ */ jsxRuntime.jsx(AgentActivityBlock, { title, limit, testId: blockTestId(id) })
2586
+ };
2587
+ const LiveMetricWidget = {
2588
+ label: "Live metric",
2589
+ fields: {
2590
+ label: { type: "text", label: "Label" },
2591
+ source: {
2592
+ type: "select",
2593
+ label: "Source",
2594
+ options: [
2595
+ { label: "Documents", value: "documents" },
2596
+ { label: "Agent threads", value: "conversations" }
2597
+ ]
2598
+ },
2599
+ accent: {
2600
+ type: "radio",
2601
+ label: "Accent",
2602
+ options: [
2603
+ { label: "Default", value: "default" },
2604
+ { label: "Primary", value: "primary" },
2605
+ { label: "Success", value: "success" }
2606
+ ]
2607
+ }
2608
+ },
2609
+ defaultProps: { label: "", source: "documents", accent: "primary" },
2610
+ render: ({ id, label, source, accent }) => /* @__PURE__ */ jsxRuntime.jsx(LiveMetricBlock, { label, source, accent, testId: blockTestId(id) })
2611
+ };
2612
+ return {
2613
+ AgentActivity: AgentActivityWidget,
2614
+ LiveMetric: LiveMetricWidget
2615
+ };
2616
+ }
2301
2617
 
2302
2618
  // src/studio/publish.ts
2303
2619
  function blobToDataUrl(blob) {
@@ -2572,34 +2888,36 @@ function PageRenderer({
2572
2888
  live,
2573
2889
  slug: slugProp,
2574
2890
  emptyState,
2575
- testIdPrefix = "studio"
2891
+ testIdPrefix = "studio",
2892
+ liveData = false,
2893
+ initialData,
2894
+ applyTheme = true
2576
2895
  }) {
2577
2896
  const params = reactRouterDom.useParams();
2578
2897
  const slug = slugProp ?? params.slug ?? "home";
2579
2898
  const arbi = react.useArbi();
2580
- const [data, setData] = react$1.useState(null);
2581
- const [loading, setLoading] = react$1.useState(true);
2899
+ const [data, setData] = react$1.useState(initialData ?? null);
2900
+ const [loading, setLoading] = react$1.useState(!initialData);
2582
2901
  react$1.useEffect(() => {
2583
2902
  let cancelled = false;
2584
- setLoading(true);
2903
+ setLoading(!initialData);
2585
2904
  void (async () => {
2586
- const [{ data: published }, { data: theme }] = await Promise.all([
2587
- persistence.loadPublished(arbi, live, slug),
2588
- persistence.loadTheme(arbi, live)
2589
- ]);
2905
+ const theme = applyTheme ? (await persistence.loadTheme(arbi, live)).data : void 0;
2590
2906
  if (cancelled) return;
2591
- const page = published ?? (await persistence.loadPage(arbi, live, slug)).data;
2907
+ const page = initialData ?? (await persistence.loadPublished(arbi, live, slug)).data ?? (await persistence.loadPage(arbi, live, slug)).data;
2592
2908
  if (cancelled) return;
2593
- applyThemeVars(
2594
- theme ? { ...defaultTheme, ...theme, colors: { ...defaultTheme.colors, ...theme.colors } } : defaultTheme
2595
- );
2909
+ if (applyTheme) {
2910
+ applyThemeVars(
2911
+ theme ? { ...defaultTheme, ...theme, colors: { ...defaultTheme.colors, ...theme.colors } } : defaultTheme
2912
+ );
2913
+ }
2596
2914
  setData(page);
2597
2915
  setLoading(false);
2598
2916
  })();
2599
2917
  return () => {
2600
2918
  cancelled = true;
2601
2919
  };
2602
- }, [arbi, live, slug, persistence, defaultTheme]);
2920
+ }, [arbi, live, slug, persistence, defaultTheme, initialData, applyTheme]);
2603
2921
  if (loading) {
2604
2922
  return /* @__PURE__ */ jsxRuntime.jsx(
2605
2923
  "div",
@@ -2625,7 +2943,8 @@ function PageRenderer({
2625
2943
  }
2626
2944
  );
2627
2945
  }
2628
- return /* @__PURE__ */ jsxRuntime.jsx("main", { "data-testid": `page-${testIdPrefix}-render`, children: /* @__PURE__ */ jsxRuntime.jsx(puck.Render, { config, data }) });
2946
+ const rendered = /* @__PURE__ */ jsxRuntime.jsx(puck.Render, { config, data });
2947
+ return /* @__PURE__ */ jsxRuntime.jsx("main", { "data-testid": `page-${testIdPrefix}-render`, children: liveData ? /* @__PURE__ */ jsxRuntime.jsx(LiveDataProvider, { children: rendered }) : rendered });
2629
2948
  }
2630
2949
 
2631
2950
  // src/studio/persistence.ts
@@ -4233,6 +4552,13 @@ function createSiteBlocks(kit) {
4233
4552
  TestimonialGrid: TestimonialGridBlock
4234
4553
  };
4235
4554
  }
4555
+ var emptySlot2 = [];
4556
+ var METRIC_COLS = {
4557
+ "2": "sm:grid-cols-2",
4558
+ "3": "sm:grid-cols-2 lg:grid-cols-3",
4559
+ "4": "sm:grid-cols-2 lg:grid-cols-4"
4560
+ };
4561
+ var METRIC_GAP = { sm: "gap-3", md: "gap-4", lg: "gap-6" };
4236
4562
  function createArbiBlocksConfig(opts = {}) {
4237
4563
  const kit = createBlockKit(opts.aiFields, opts.imageField, opts.eyebrowClassName);
4238
4564
  const { textField, textareaField, boolRadio, iconField, resolveIcon, blockTestId } = kit;
@@ -4270,16 +4596,19 @@ function createArbiBlocksConfig(opts = {}) {
4270
4596
  fields: {
4271
4597
  title: textField("Title"),
4272
4598
  eyebrow: textField("Eyebrow"),
4273
- description: textareaField("Description", void 0, "description")
4599
+ description: textareaField("Description", void 0, "description"),
4600
+ // `actions` is a slot so the top band can host real buttons/badges authored
4601
+ // as blocks (drop a Button in), while the interactive body stays bespoke.
4602
+ actions: { type: "slot", allow: ["Button", "Badge"] }
4274
4603
  },
4275
- defaultProps: { title: "Page title" },
4276
- // `actions` (ReactNode) is deferred for v1 — see module notes.
4277
- render: ({ id, title, eyebrow, description }) => /* @__PURE__ */ jsxRuntime.jsx(
4604
+ defaultProps: { title: "Page title", actions: emptySlot2 },
4605
+ render: ({ id, title, eyebrow, description, actions: Actions }) => /* @__PURE__ */ jsxRuntime.jsx(
4278
4606
  PageHeader,
4279
4607
  {
4280
4608
  title,
4281
4609
  eyebrow,
4282
4610
  description,
4611
+ actions: /* @__PURE__ */ jsxRuntime.jsx(Actions, { className: "flex items-center gap-2" }),
4283
4612
  testId: blockTestId(id)
4284
4613
  }
4285
4614
  )
@@ -4289,16 +4618,18 @@ function createArbiBlocksConfig(opts = {}) {
4289
4618
  fields: {
4290
4619
  title: textField("Title"),
4291
4620
  description: textareaField("Description", void 0, "description"),
4292
- icon: iconField()
4621
+ icon: iconField(),
4622
+ // `action` is a slot so an empty state can offer a real call-to-action button.
4623
+ action: { type: "slot", allow: ["Button"] }
4293
4624
  },
4294
- defaultProps: { title: "Nothing here yet" },
4295
- // `action` (ReactNode) is deferred for v1 — see module notes.
4296
- render: ({ id, title, description, icon }) => /* @__PURE__ */ jsxRuntime.jsx(
4625
+ defaultProps: { title: "Nothing here yet", action: emptySlot2 },
4626
+ render: ({ id, title, description, icon, action: Action }) => /* @__PURE__ */ jsxRuntime.jsx(
4297
4627
  EmptyState,
4298
4628
  {
4299
4629
  title,
4300
4630
  description,
4301
4631
  icon: resolveIcon(icon),
4632
+ action: /* @__PURE__ */ jsxRuntime.jsx(Action, {}),
4302
4633
  testId: blockTestId(id)
4303
4634
  }
4304
4635
  )
@@ -4351,6 +4682,42 @@ function createArbiBlocksConfig(opts = {}) {
4351
4682
  }
4352
4683
  )
4353
4684
  };
4685
+ const MetricRowBlock = {
4686
+ label: "Metric row",
4687
+ fields: {
4688
+ columns: {
4689
+ type: "radio",
4690
+ label: "Columns",
4691
+ options: [
4692
+ { label: "2", value: "2" },
4693
+ { label: "3", value: "3" },
4694
+ { label: "4", value: "4" }
4695
+ ]
4696
+ },
4697
+ gap: {
4698
+ type: "radio",
4699
+ label: "Gap",
4700
+ options: [
4701
+ { label: "S", value: "sm" },
4702
+ { label: "M", value: "md" },
4703
+ { label: "L", value: "lg" }
4704
+ ]
4705
+ },
4706
+ // Static KPI tiles and (when registered) live metric widgets both belong in a band.
4707
+ items: { type: "slot", allow: ["MetricCard", "LiveMetric"] }
4708
+ },
4709
+ defaultProps: { columns: "3", gap: "md", items: emptySlot2 },
4710
+ render: ({ id, columns, gap, items: Items }) => /* @__PURE__ */ jsxRuntime.jsx("div", { "data-testid": blockTestId(id), children: /* @__PURE__ */ jsxRuntime.jsx(
4711
+ Items,
4712
+ {
4713
+ className: react.cn(
4714
+ "grid grid-cols-1",
4715
+ METRIC_COLS[columns] ?? METRIC_COLS["3"],
4716
+ METRIC_GAP[gap] ?? METRIC_GAP.md
4717
+ )
4718
+ }
4719
+ ) })
4720
+ };
4354
4721
  const DataTableBlockConfig = {
4355
4722
  label: "Data table",
4356
4723
  fields: {
@@ -4549,11 +4916,13 @@ function createArbiBlocksConfig(opts = {}) {
4549
4916
  };
4550
4917
  const website = createWebsiteBlocks(kit);
4551
4918
  const site = createSiteBlocks(kit);
4919
+ const widgets = opts.liveWidgets ? createLiveWidgets(kit) : {};
4552
4920
  const components = {
4553
4921
  SectionHeading: SectionHeadingBlock,
4554
4922
  PageHeader: PageHeaderBlock,
4555
4923
  EmptyState: EmptyStateBlock,
4556
4924
  MetricCard: MetricCardBlock,
4925
+ MetricRow: MetricRowBlock,
4557
4926
  DataTable: DataTableBlockConfig,
4558
4927
  Section: SectionBlock,
4559
4928
  Card: CardBlock,
@@ -4561,7 +4930,8 @@ function createArbiBlocksConfig(opts = {}) {
4561
4930
  Badge: BadgeBlock,
4562
4931
  Separator: SeparatorBlock,
4563
4932
  ...website,
4564
- ...site
4933
+ ...site,
4934
+ ...widgets
4565
4935
  };
4566
4936
  const config = {
4567
4937
  categories: {
@@ -4595,7 +4965,8 @@ function createArbiBlocksConfig(opts = {}) {
4595
4965
  Commerce: { title: "Commerce", components: ["Pricing"] },
4596
4966
  Forms: { title: "Forms", components: ["ContactForm"] },
4597
4967
  Navigation: { title: "Navigation", components: ["Navbar", "Footer"] },
4598
- Data: { title: "Data", components: ["MetricCard", "DataTable"] },
4968
+ Data: { title: "Data", components: ["MetricRow", "MetricCard", "DataTable"] },
4969
+ ...opts.liveWidgets ? { Widgets: { title: "Widgets", components: ["AgentActivity", "LiveMetric"] } } : {},
4599
4970
  Primitives: { title: "Primitives", components: ["Button", "Badge", "Separator"] }
4600
4971
  },
4601
4972
  components,
@@ -5119,6 +5490,7 @@ Object.defineProperty(exports, "toRedlineMarkdown", {
5119
5490
  exports.ASSET_REF_PREFIX = ASSET_REF_PREFIX;
5120
5491
  exports.Accordion = Accordion;
5121
5492
  exports.AgentsPanel = AgentsPanel;
5493
+ exports.AppShell = AppShell;
5122
5494
  exports.AssetResolverProvider = AssetResolverProvider;
5123
5495
  exports.AvatarBlock = AvatarBlock;
5124
5496
  exports.Banner = Banner;
@@ -5140,6 +5512,7 @@ exports.GridView = GridView;
5140
5512
  exports.Hero = Hero;
5141
5513
  exports.LegalArbiProvider = LegalArbiProvider;
5142
5514
  exports.ListBlock = ListBlock;
5515
+ exports.LiveDataProvider = LiveDataProvider;
5143
5516
  exports.LogoCloud = LogoCloud;
5144
5517
  exports.MediaText = MediaText;
5145
5518
  exports.MetricCard = MetricCard;
@@ -5164,6 +5537,7 @@ exports.ThemeEditor = ThemeEditor;
5164
5537
  exports.Toolbar = Toolbar;
5165
5538
  exports.VerticalBuilder = VerticalBuilder;
5166
5539
  exports.VideoEmbed = VideoEmbed;
5540
+ exports.WidgetPlaceholder = WidgetPlaceholder;
5167
5541
  exports.applyStoredTheme = applyStoredTheme;
5168
5542
  exports.applyThemeVars = applyThemeVars;
5169
5543
  exports.asAssetRef = asAssetRef;
@@ -5175,6 +5549,7 @@ exports.createArbiBlocksConfig = createArbiBlocksConfig;
5175
5549
  exports.createArbiMaterializer = createArbiMaterializer;
5176
5550
  exports.createBlockKit = createBlockKit;
5177
5551
  exports.createImageField = createImageField;
5552
+ exports.createLiveWidgets = createLiveWidgets;
5178
5553
  exports.createModuleRegistry = createModuleRegistry;
5179
5554
  exports.createModuleStore = createModuleStore;
5180
5555
  exports.createPromptLibrary = createPromptLibrary;
@@ -5203,6 +5578,7 @@ exports.useAssetResolverActive = useAssetResolverActive;
5203
5578
  exports.useConnection = useConnection;
5204
5579
  exports.useFirmAiTask = useFirmAiTask;
5205
5580
  exports.useImageGen = useImageGen;
5581
+ exports.useLiveDataActive = useLiveDataActive;
5206
5582
  exports.useSemanticSearch = useSemanticSearch;
5207
5583
  exports.useWorkspaceDocs = useWorkspaceDocs;
5208
5584
  //# sourceMappingURL=index.cjs.map