@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.js CHANGED
@@ -1,14 +1,14 @@
1
1
  import { create } from 'zustand';
2
2
  import { persist } from 'zustand/middleware';
3
3
  import { createContext, useState, useEffect, useCallback, useMemo, useContext, useRef } from 'react';
4
- import { MessageSquare, Cpu, Code, Cloud, Layers, Target, Award, MapPin, Phone, Mail, Lock, Globe, Heart, Star, Rocket, Shield, Zap, Sparkles, Gavel, Scale, Bell, Activity, BarChart3, DollarSign, TrendingUp, Info, AlertCircle, CheckCircle, Clock, Calendar, Briefcase, Users, Folder, FileText, RotateCcw, Palette, Search, ArrowUpRight, ArrowDownRight, ImageIcon, ArrowRight, Quote, ChevronDown, XCircle, AlertTriangle, CheckCircle2, Check, ArrowLeft, Loader2, ChevronUp, BrainCircuit, Bot, LayoutGrid, FileJson, Save, Download, ImagePlus, Upload, Link2 } from 'lucide-react';
5
- import { Input, Card, CardHeader, CardTitle, CardContent, useArbi, ArbiProvider, useAiTask, useWorkspaceDocs as useWorkspaceDocs$1, useSemanticSearch as useSemanticSearch$1, cn, Table, TableHeader, TableRow, TableHead, TableBody, TableCell, Button, Avatar, AvatarImage, AvatarFallback, AiMarkdown, Badge, Switch, useConfigs, useAgents, Checkbox, Tabs as Tabs$1, TabsList, TabsTrigger, TabsContent, Separator, ArbiWebSocketProvider, useDocuments, useThumbnails } from '@arbidocs/react';
4
+ import { MessageSquare, Cpu, Code, Cloud, Layers, Target, Award, MapPin, Phone, Mail, Lock, Globe, Heart, Star, Rocket, Shield, Zap, Sparkles, Gavel, Scale, Bell, Activity, BarChart3, DollarSign, TrendingUp, Info, AlertCircle, CheckCircle, Clock, Calendar, Briefcase, Users, Folder, FileText, RotateCcw, Palette, Search, ArrowUpRight, ArrowDownRight, ImageIcon, ArrowRight, Quote, ChevronDown, XCircle, AlertTriangle, CheckCircle2, Check, X, Menu, ArrowLeft, Loader2, ChevronUp, BrainCircuit, Bot, LayoutGrid, FileJson, Save, Download, ImagePlus, Upload, Link2, MessagesSquare } from 'lucide-react';
5
+ import { Input, Card, CardHeader, CardTitle, CardContent, useArbi, ArbiProvider, useAiTask, useWorkspaceDocs as useWorkspaceDocs$1, useSemanticSearch as useSemanticSearch$1, cn, Table, TableHeader, TableRow, TableHead, TableBody, TableCell, Button, Avatar, AvatarImage, AvatarFallback, AiMarkdown, Badge, Switch, useConfigs, useAgents, Checkbox, Tabs as Tabs$1, TabsList, TabsTrigger, TabsContent, Separator, ArbiWebSocketProvider, useDocuments, useThumbnails, useConversations } from '@arbidocs/react';
6
6
  export { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, redlineStats, textToArtifact, toRedlineMarkdown } from '@arbidocs/react';
7
7
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
8
8
  import { QueryClient, QueryClientProvider, useQuery, useQueryClient } from '@tanstack/react-query';
9
9
  import { AgGridReact } from 'ag-grid-react';
10
10
  import { themeQuartz, AllCommunityModule } from 'ag-grid-community';
11
- import { Link, useParams } from 'react-router-dom';
11
+ import { useLocation, Link, useParams, NavLink } from 'react-router-dom';
12
12
  import { Puck, Render } from '@measured/puck';
13
13
 
14
14
  // src/prompts.ts
@@ -2297,6 +2297,322 @@ function TestimonialGrid({ items, columns = "3", testId }) {
2297
2297
  i
2298
2298
  )) });
2299
2299
  }
2300
+ function NavItemLink({ item, onNavigate }) {
2301
+ return /* @__PURE__ */ jsxs(
2302
+ NavLink,
2303
+ {
2304
+ to: item.to,
2305
+ end: item.end,
2306
+ onClick: onNavigate,
2307
+ "data-testid": tid("sidebar-nav", item.id),
2308
+ className: ({ isActive }) => cn(
2309
+ "group flex items-center gap-3 rounded-md px-3 py-2 text-sm font-medium transition-colors",
2310
+ isActive ? "bg-sidebar-accent text-sidebar-accent-foreground" : "text-sidebar-foreground hover:bg-sidebar-accent/60 hover:text-sidebar-accent-foreground"
2311
+ ),
2312
+ children: [
2313
+ /* @__PURE__ */ jsx(item.icon, { className: "size-[18px] shrink-0" }),
2314
+ /* @__PURE__ */ jsx("span", { className: "flex-1 truncate", children: item.label }),
2315
+ item.arbi && /* @__PURE__ */ jsx(Sparkles, { className: "size-3.5 text-sidebar-primary", "aria-label": "Powered by ARBI" })
2316
+ ]
2317
+ }
2318
+ );
2319
+ }
2320
+ function SidebarBody({
2321
+ sections,
2322
+ footerItems,
2323
+ logo,
2324
+ logoHref,
2325
+ logoLabel,
2326
+ sidebarFooter,
2327
+ onNavigate
2328
+ }) {
2329
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
2330
+ logo && /* @__PURE__ */ jsx("div", { className: "flex h-16 items-center border-b border-sidebar-border px-5", children: logoHref ? /* @__PURE__ */ jsx(Link, { to: logoHref, "aria-label": logoLabel, onClick: onNavigate, children: logo }) : logo }),
2331
+ /* @__PURE__ */ jsx("nav", { className: "flex-1 space-y-6 overflow-y-auto px-3 py-5", children: sections.map((section) => /* @__PURE__ */ jsxs("div", { children: [
2332
+ /* @__PURE__ */ jsx("p", { className: "px-3 pb-2 text-[10px] font-semibold uppercase tracking-[0.14em] text-sidebar-foreground/55", children: section.heading }),
2333
+ /* @__PURE__ */ jsx("div", { className: "space-y-0.5", children: section.items.map((item) => /* @__PURE__ */ jsx(NavItemLink, { item, onNavigate }, item.id)) })
2334
+ ] }, section.heading)) }),
2335
+ (footerItems?.length || sidebarFooter) && /* @__PURE__ */ jsxs("div", { className: "border-t border-sidebar-border px-3 py-3", children: [
2336
+ footerItems?.map((item) => /* @__PURE__ */ jsx(NavItemLink, { item, onNavigate }, item.id)),
2337
+ sidebarFooter
2338
+ ] })
2339
+ ] });
2340
+ }
2341
+ function AppShell({
2342
+ sections,
2343
+ footerItems,
2344
+ logo,
2345
+ logoHref,
2346
+ logoLabel,
2347
+ sidebarFooter,
2348
+ topbar,
2349
+ rightRail,
2350
+ children,
2351
+ mainClassName,
2352
+ testIdPrefix = "app"
2353
+ }) {
2354
+ const [open, setOpen] = useState(false);
2355
+ const location = useLocation();
2356
+ useEffect(() => {
2357
+ setOpen(false);
2358
+ }, [location.pathname]);
2359
+ const body = (onNavigate) => /* @__PURE__ */ jsx(
2360
+ SidebarBody,
2361
+ {
2362
+ sections,
2363
+ footerItems,
2364
+ logo,
2365
+ logoHref,
2366
+ logoLabel,
2367
+ sidebarFooter,
2368
+ onNavigate
2369
+ }
2370
+ );
2371
+ return /* @__PURE__ */ jsxs(
2372
+ "div",
2373
+ {
2374
+ className: "flex h-screen overflow-hidden bg-background",
2375
+ "data-testid": `${testIdPrefix}-shell`,
2376
+ children: [
2377
+ /* @__PURE__ */ jsx(
2378
+ "aside",
2379
+ {
2380
+ className: "hidden h-full w-64 shrink-0 flex-col border-r border-sidebar-border bg-sidebar lg:flex",
2381
+ "data-testid": `${testIdPrefix}-sidebar`,
2382
+ children: body()
2383
+ }
2384
+ ),
2385
+ open && /* @__PURE__ */ jsxs(
2386
+ "div",
2387
+ {
2388
+ className: "fixed inset-0 z-50 lg:hidden",
2389
+ "data-testid": `${testIdPrefix}-sidebar-mobile`,
2390
+ children: [
2391
+ /* @__PURE__ */ jsx(
2392
+ "div",
2393
+ {
2394
+ className: "absolute inset-0 bg-black/60 backdrop-blur-sm",
2395
+ onClick: () => setOpen(false),
2396
+ "aria-hidden": true
2397
+ }
2398
+ ),
2399
+ /* @__PURE__ */ 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: [
2400
+ /* @__PURE__ */ jsx(
2401
+ "button",
2402
+ {
2403
+ type: "button",
2404
+ onClick: () => setOpen(false),
2405
+ 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",
2406
+ "aria-label": "Close menu",
2407
+ "data-testid": `${testIdPrefix}-sidebar-close`,
2408
+ children: /* @__PURE__ */ jsx(X, { className: "size-5" })
2409
+ }
2410
+ ),
2411
+ body(() => setOpen(false))
2412
+ ] })
2413
+ ]
2414
+ }
2415
+ ),
2416
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 flex-col", children: [
2417
+ /* @__PURE__ */ jsxs(
2418
+ "header",
2419
+ {
2420
+ className: "flex h-16 shrink-0 items-center gap-4 border-b border-border bg-card/80 px-6 backdrop-blur",
2421
+ "data-testid": `${testIdPrefix}-topbar`,
2422
+ children: [
2423
+ /* @__PURE__ */ jsx(
2424
+ "button",
2425
+ {
2426
+ type: "button",
2427
+ onClick: () => setOpen(true),
2428
+ className: "flex size-9 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-accent hover:text-foreground lg:hidden",
2429
+ "data-testid": `${testIdPrefix}-nav-toggle`,
2430
+ "aria-label": "Open menu",
2431
+ children: /* @__PURE__ */ jsx(Menu, { className: "size-5" })
2432
+ }
2433
+ ),
2434
+ topbar
2435
+ ]
2436
+ }
2437
+ ),
2438
+ /* @__PURE__ */ jsx(
2439
+ "main",
2440
+ {
2441
+ className: cn("flex-1 overflow-y-auto", mainClassName),
2442
+ "data-testid": `${testIdPrefix}-main`,
2443
+ children
2444
+ }
2445
+ )
2446
+ ] }),
2447
+ rightRail
2448
+ ]
2449
+ }
2450
+ );
2451
+ }
2452
+ var LiveDataContext = createContext(false);
2453
+ function useLiveDataActive() {
2454
+ return useContext(LiveDataContext);
2455
+ }
2456
+ function LiveDataProvider({ children }) {
2457
+ return /* @__PURE__ */ jsx(LiveDataContext.Provider, { value: true, children });
2458
+ }
2459
+ function WidgetPlaceholder({
2460
+ label,
2461
+ icon: Icon = Sparkles,
2462
+ testId
2463
+ }) {
2464
+ return /* @__PURE__ */ jsxs(
2465
+ "div",
2466
+ {
2467
+ 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",
2468
+ "data-testid": testId,
2469
+ children: [
2470
+ /* @__PURE__ */ jsx("span", { className: "flex size-9 shrink-0 items-center justify-center rounded-full bg-muted text-muted-foreground", children: /* @__PURE__ */ jsx(Icon, { className: "size-4" }) }),
2471
+ /* @__PURE__ */ jsxs("span", { children: [
2472
+ /* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: label }),
2473
+ /* @__PURE__ */ jsx("span", { className: "ml-1", children: "\xB7 live in the app" })
2474
+ ] })
2475
+ ]
2476
+ }
2477
+ );
2478
+ }
2479
+ function useActiveWorkspaceId() {
2480
+ const { workspaceId, fallbackWorkspaceId } = useConnection();
2481
+ return workspaceId ?? fallbackWorkspaceId ?? void 0;
2482
+ }
2483
+ function timeAgo(iso) {
2484
+ if (!iso) return "";
2485
+ const then = new Date(iso).getTime();
2486
+ if (Number.isNaN(then)) return "";
2487
+ const s = Math.max(0, Math.round((Date.now() - then) / 1e3));
2488
+ if (s < 60) return "just now";
2489
+ const m = Math.round(s / 60);
2490
+ if (m < 60) return `${m}m ago`;
2491
+ const h = Math.round(m / 60);
2492
+ if (h < 24) return `${h}h ago`;
2493
+ return `${Math.round(h / 24)}d ago`;
2494
+ }
2495
+ var METRIC_SOURCES = {
2496
+ documents: { label: "Documents", icon: FileText },
2497
+ conversations: { label: "Agent threads", icon: Bot }
2498
+ };
2499
+ var asMetricSource = (s) => s in METRIC_SOURCES ? s : "documents";
2500
+ function AgentActivityLive({
2501
+ title,
2502
+ limit,
2503
+ testId
2504
+ }) {
2505
+ const wsId = useActiveWorkspaceId();
2506
+ const { data, isLoading } = useConversations(wsId);
2507
+ const rows = [...data ?? []].sort((a, b) => +new Date(b.updated_at ?? 0) - +new Date(a.updated_at ?? 0)).slice(0, limit);
2508
+ return /* @__PURE__ */ jsxs(Card, { variant: "elevated", className: "rounded-xl p-5", "data-testid": testId, children: [
2509
+ /* @__PURE__ */ jsxs("div", { className: "mb-3 flex items-center gap-2", children: [
2510
+ /* @__PURE__ */ jsx(Bot, { className: "size-4 text-muted-foreground" }),
2511
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-semibold text-foreground", children: title })
2512
+ ] }),
2513
+ isLoading ? /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", "data-testid": `${testId}-loading`, children: "Loading agent threads\u2026" }) : rows.length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", "data-testid": `${testId}-empty`, children: "No agent threads yet." }) : /* @__PURE__ */ jsx("ul", { className: "space-y-2", "data-testid": `${testId}-list`, children: rows.map((c, i) => /* @__PURE__ */ jsxs(
2514
+ "li",
2515
+ {
2516
+ className: "flex items-center justify-between gap-3",
2517
+ "data-testid": `${testId}-item-${i}`,
2518
+ children: [
2519
+ /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-2 truncate", children: [
2520
+ /* @__PURE__ */ jsx(MessagesSquare, { className: "size-3.5 shrink-0 text-muted-foreground" }),
2521
+ /* @__PURE__ */ jsx("span", { className: "truncate text-sm text-foreground", children: c.title?.trim() || "Untitled thread" })
2522
+ ] }),
2523
+ /* @__PURE__ */ jsx("span", { className: "shrink-0 text-[11px] text-muted-foreground", children: timeAgo(c.updated_at) })
2524
+ ]
2525
+ },
2526
+ c.external_id ?? i
2527
+ )) })
2528
+ ] });
2529
+ }
2530
+ function LiveMetricInner({
2531
+ label,
2532
+ source,
2533
+ accent,
2534
+ testId
2535
+ }) {
2536
+ const wsId = useActiveWorkspaceId();
2537
+ const docs = useDocuments(wsId, { enabled: source === "documents" });
2538
+ const convos = useConversations(wsId, { enabled: source === "conversations" });
2539
+ const query = source === "documents" ? docs : convos;
2540
+ const count = Array.isArray(query.data) ? query.data.length : 0;
2541
+ return /* @__PURE__ */ jsx(
2542
+ MetricCard,
2543
+ {
2544
+ label: label || METRIC_SOURCES[source].label,
2545
+ value: query.isLoading ? "\u2014" : String(count),
2546
+ accent,
2547
+ icon: METRIC_SOURCES[source].icon,
2548
+ testId
2549
+ }
2550
+ );
2551
+ }
2552
+ function AgentActivityBlock({
2553
+ title,
2554
+ limit,
2555
+ testId
2556
+ }) {
2557
+ return useLiveDataActive() ? /* @__PURE__ */ jsx(AgentActivityLive, { title, limit, testId }) : /* @__PURE__ */ jsx(WidgetPlaceholder, { label: String(title || "Agent activity"), icon: Bot, testId });
2558
+ }
2559
+ function LiveMetricBlock({
2560
+ label,
2561
+ source,
2562
+ accent,
2563
+ testId
2564
+ }) {
2565
+ const src = asMetricSource(source);
2566
+ return useLiveDataActive() ? /* @__PURE__ */ jsx(LiveMetricInner, { label, source: src, accent, testId }) : /* @__PURE__ */ jsx(
2567
+ WidgetPlaceholder,
2568
+ {
2569
+ label: String(label || METRIC_SOURCES[src].label),
2570
+ icon: METRIC_SOURCES[src].icon,
2571
+ testId
2572
+ }
2573
+ );
2574
+ }
2575
+ function createLiveWidgets(kit) {
2576
+ const { blockTestId } = kit;
2577
+ const AgentActivityWidget = {
2578
+ label: "Agent activity (live)",
2579
+ fields: {
2580
+ title: { type: "text", label: "Title" },
2581
+ limit: { type: "number", label: "Rows", min: 1, max: 8 }
2582
+ },
2583
+ defaultProps: { title: "Recent agent activity", limit: 4 },
2584
+ render: ({ id, title, limit }) => /* @__PURE__ */ jsx(AgentActivityBlock, { title, limit, testId: blockTestId(id) })
2585
+ };
2586
+ const LiveMetricWidget = {
2587
+ label: "Live metric",
2588
+ fields: {
2589
+ label: { type: "text", label: "Label" },
2590
+ source: {
2591
+ type: "select",
2592
+ label: "Source",
2593
+ options: [
2594
+ { label: "Documents", value: "documents" },
2595
+ { label: "Agent threads", value: "conversations" }
2596
+ ]
2597
+ },
2598
+ accent: {
2599
+ type: "radio",
2600
+ label: "Accent",
2601
+ options: [
2602
+ { label: "Default", value: "default" },
2603
+ { label: "Primary", value: "primary" },
2604
+ { label: "Success", value: "success" }
2605
+ ]
2606
+ }
2607
+ },
2608
+ defaultProps: { label: "", source: "documents", accent: "primary" },
2609
+ render: ({ id, label, source, accent }) => /* @__PURE__ */ jsx(LiveMetricBlock, { label, source, accent, testId: blockTestId(id) })
2610
+ };
2611
+ return {
2612
+ AgentActivity: AgentActivityWidget,
2613
+ LiveMetric: LiveMetricWidget
2614
+ };
2615
+ }
2300
2616
 
2301
2617
  // src/studio/publish.ts
2302
2618
  function blobToDataUrl(blob) {
@@ -2571,34 +2887,36 @@ function PageRenderer({
2571
2887
  live,
2572
2888
  slug: slugProp,
2573
2889
  emptyState,
2574
- testIdPrefix = "studio"
2890
+ testIdPrefix = "studio",
2891
+ liveData = false,
2892
+ initialData,
2893
+ applyTheme = true
2575
2894
  }) {
2576
2895
  const params = useParams();
2577
2896
  const slug = slugProp ?? params.slug ?? "home";
2578
2897
  const arbi = useArbi();
2579
- const [data, setData] = useState(null);
2580
- const [loading, setLoading] = useState(true);
2898
+ const [data, setData] = useState(initialData ?? null);
2899
+ const [loading, setLoading] = useState(!initialData);
2581
2900
  useEffect(() => {
2582
2901
  let cancelled = false;
2583
- setLoading(true);
2902
+ setLoading(!initialData);
2584
2903
  void (async () => {
2585
- const [{ data: published }, { data: theme }] = await Promise.all([
2586
- persistence.loadPublished(arbi, live, slug),
2587
- persistence.loadTheme(arbi, live)
2588
- ]);
2904
+ const theme = applyTheme ? (await persistence.loadTheme(arbi, live)).data : void 0;
2589
2905
  if (cancelled) return;
2590
- const page = published ?? (await persistence.loadPage(arbi, live, slug)).data;
2906
+ const page = initialData ?? (await persistence.loadPublished(arbi, live, slug)).data ?? (await persistence.loadPage(arbi, live, slug)).data;
2591
2907
  if (cancelled) return;
2592
- applyThemeVars(
2593
- theme ? { ...defaultTheme, ...theme, colors: { ...defaultTheme.colors, ...theme.colors } } : defaultTheme
2594
- );
2908
+ if (applyTheme) {
2909
+ applyThemeVars(
2910
+ theme ? { ...defaultTheme, ...theme, colors: { ...defaultTheme.colors, ...theme.colors } } : defaultTheme
2911
+ );
2912
+ }
2595
2913
  setData(page);
2596
2914
  setLoading(false);
2597
2915
  })();
2598
2916
  return () => {
2599
2917
  cancelled = true;
2600
2918
  };
2601
- }, [arbi, live, slug, persistence, defaultTheme]);
2919
+ }, [arbi, live, slug, persistence, defaultTheme, initialData, applyTheme]);
2602
2920
  if (loading) {
2603
2921
  return /* @__PURE__ */ jsx(
2604
2922
  "div",
@@ -2624,7 +2942,8 @@ function PageRenderer({
2624
2942
  }
2625
2943
  );
2626
2944
  }
2627
- return /* @__PURE__ */ jsx("main", { "data-testid": `page-${testIdPrefix}-render`, children: /* @__PURE__ */ jsx(Render, { config, data }) });
2945
+ const rendered = /* @__PURE__ */ jsx(Render, { config, data });
2946
+ return /* @__PURE__ */ jsx("main", { "data-testid": `page-${testIdPrefix}-render`, children: liveData ? /* @__PURE__ */ jsx(LiveDataProvider, { children: rendered }) : rendered });
2628
2947
  }
2629
2948
 
2630
2949
  // src/studio/persistence.ts
@@ -3156,7 +3475,7 @@ function Section({
3156
3475
  ] });
3157
3476
  }
3158
3477
  var iconMap = {
3159
- FileText,
3478
+ FileText: FileText,
3160
3479
  Folder,
3161
3480
  Users,
3162
3481
  Briefcase,
@@ -4232,6 +4551,13 @@ function createSiteBlocks(kit) {
4232
4551
  TestimonialGrid: TestimonialGridBlock
4233
4552
  };
4234
4553
  }
4554
+ var emptySlot2 = [];
4555
+ var METRIC_COLS = {
4556
+ "2": "sm:grid-cols-2",
4557
+ "3": "sm:grid-cols-2 lg:grid-cols-3",
4558
+ "4": "sm:grid-cols-2 lg:grid-cols-4"
4559
+ };
4560
+ var METRIC_GAP = { sm: "gap-3", md: "gap-4", lg: "gap-6" };
4235
4561
  function createArbiBlocksConfig(opts = {}) {
4236
4562
  const kit = createBlockKit(opts.aiFields, opts.imageField, opts.eyebrowClassName);
4237
4563
  const { textField, textareaField, boolRadio, iconField, resolveIcon, blockTestId } = kit;
@@ -4269,16 +4595,19 @@ function createArbiBlocksConfig(opts = {}) {
4269
4595
  fields: {
4270
4596
  title: textField("Title"),
4271
4597
  eyebrow: textField("Eyebrow"),
4272
- description: textareaField("Description", void 0, "description")
4598
+ description: textareaField("Description", void 0, "description"),
4599
+ // `actions` is a slot so the top band can host real buttons/badges authored
4600
+ // as blocks (drop a Button in), while the interactive body stays bespoke.
4601
+ actions: { type: "slot", allow: ["Button", "Badge"] }
4273
4602
  },
4274
- defaultProps: { title: "Page title" },
4275
- // `actions` (ReactNode) is deferred for v1 — see module notes.
4276
- render: ({ id, title, eyebrow, description }) => /* @__PURE__ */ jsx(
4603
+ defaultProps: { title: "Page title", actions: emptySlot2 },
4604
+ render: ({ id, title, eyebrow, description, actions: Actions }) => /* @__PURE__ */ jsx(
4277
4605
  PageHeader,
4278
4606
  {
4279
4607
  title,
4280
4608
  eyebrow,
4281
4609
  description,
4610
+ actions: /* @__PURE__ */ jsx(Actions, { className: "flex items-center gap-2" }),
4282
4611
  testId: blockTestId(id)
4283
4612
  }
4284
4613
  )
@@ -4288,16 +4617,18 @@ function createArbiBlocksConfig(opts = {}) {
4288
4617
  fields: {
4289
4618
  title: textField("Title"),
4290
4619
  description: textareaField("Description", void 0, "description"),
4291
- icon: iconField()
4620
+ icon: iconField(),
4621
+ // `action` is a slot so an empty state can offer a real call-to-action button.
4622
+ action: { type: "slot", allow: ["Button"] }
4292
4623
  },
4293
- defaultProps: { title: "Nothing here yet" },
4294
- // `action` (ReactNode) is deferred for v1 — see module notes.
4295
- render: ({ id, title, description, icon }) => /* @__PURE__ */ jsx(
4624
+ defaultProps: { title: "Nothing here yet", action: emptySlot2 },
4625
+ render: ({ id, title, description, icon, action: Action }) => /* @__PURE__ */ jsx(
4296
4626
  EmptyState,
4297
4627
  {
4298
4628
  title,
4299
4629
  description,
4300
4630
  icon: resolveIcon(icon),
4631
+ action: /* @__PURE__ */ jsx(Action, {}),
4301
4632
  testId: blockTestId(id)
4302
4633
  }
4303
4634
  )
@@ -4350,6 +4681,42 @@ function createArbiBlocksConfig(opts = {}) {
4350
4681
  }
4351
4682
  )
4352
4683
  };
4684
+ const MetricRowBlock = {
4685
+ label: "Metric row",
4686
+ fields: {
4687
+ columns: {
4688
+ type: "radio",
4689
+ label: "Columns",
4690
+ options: [
4691
+ { label: "2", value: "2" },
4692
+ { label: "3", value: "3" },
4693
+ { label: "4", value: "4" }
4694
+ ]
4695
+ },
4696
+ gap: {
4697
+ type: "radio",
4698
+ label: "Gap",
4699
+ options: [
4700
+ { label: "S", value: "sm" },
4701
+ { label: "M", value: "md" },
4702
+ { label: "L", value: "lg" }
4703
+ ]
4704
+ },
4705
+ // Static KPI tiles and (when registered) live metric widgets both belong in a band.
4706
+ items: { type: "slot", allow: ["MetricCard", "LiveMetric"] }
4707
+ },
4708
+ defaultProps: { columns: "3", gap: "md", items: emptySlot2 },
4709
+ render: ({ id, columns, gap, items: Items }) => /* @__PURE__ */ jsx("div", { "data-testid": blockTestId(id), children: /* @__PURE__ */ jsx(
4710
+ Items,
4711
+ {
4712
+ className: cn(
4713
+ "grid grid-cols-1",
4714
+ METRIC_COLS[columns] ?? METRIC_COLS["3"],
4715
+ METRIC_GAP[gap] ?? METRIC_GAP.md
4716
+ )
4717
+ }
4718
+ ) })
4719
+ };
4353
4720
  const DataTableBlockConfig = {
4354
4721
  label: "Data table",
4355
4722
  fields: {
@@ -4548,11 +4915,13 @@ function createArbiBlocksConfig(opts = {}) {
4548
4915
  };
4549
4916
  const website = createWebsiteBlocks(kit);
4550
4917
  const site = createSiteBlocks(kit);
4918
+ const widgets = opts.liveWidgets ? createLiveWidgets(kit) : {};
4551
4919
  const components = {
4552
4920
  SectionHeading: SectionHeadingBlock,
4553
4921
  PageHeader: PageHeaderBlock,
4554
4922
  EmptyState: EmptyStateBlock,
4555
4923
  MetricCard: MetricCardBlock,
4924
+ MetricRow: MetricRowBlock,
4556
4925
  DataTable: DataTableBlockConfig,
4557
4926
  Section: SectionBlock,
4558
4927
  Card: CardBlock,
@@ -4560,7 +4929,8 @@ function createArbiBlocksConfig(opts = {}) {
4560
4929
  Badge: BadgeBlock,
4561
4930
  Separator: SeparatorBlock,
4562
4931
  ...website,
4563
- ...site
4932
+ ...site,
4933
+ ...widgets
4564
4934
  };
4565
4935
  const config = {
4566
4936
  categories: {
@@ -4594,7 +4964,8 @@ function createArbiBlocksConfig(opts = {}) {
4594
4964
  Commerce: { title: "Commerce", components: ["Pricing"] },
4595
4965
  Forms: { title: "Forms", components: ["ContactForm"] },
4596
4966
  Navigation: { title: "Navigation", components: ["Navbar", "Footer"] },
4597
- Data: { title: "Data", components: ["MetricCard", "DataTable"] },
4967
+ Data: { title: "Data", components: ["MetricRow", "MetricCard", "DataTable"] },
4968
+ ...opts.liveWidgets ? { Widgets: { title: "Widgets", components: ["AgentActivity", "LiveMetric"] } } : {},
4598
4969
  Primitives: { title: "Primitives", components: ["Button", "Badge", "Separator"] }
4599
4970
  },
4600
4971
  components,
@@ -5079,6 +5450,6 @@ function VerticalBuilder({
5079
5450
  ] });
5080
5451
  }
5081
5452
 
5082
- export { ASSET_REF_PREFIX, Accordion, AgentsPanel, AssetResolverProvider, AvatarBlock, Banner, BlockImage, CTABanner, Callout, Columns, ConnectionProvider, ContactForm, Container, DataTable, DataTableBlock, EmptyState, FAQ, FeatureGrid, Footer, Gallery, GridView, Hero, LegalArbiProvider, ListBlock, LogoCloud, MediaText, MetricCard, ModuleManager, Navbar, PageHeader, PageRenderer, PricingTable, RichTextBlock, Section, SectionHeading, Spacer, StatGroup, Steps, StudioEditor, THEME_STYLE_ID, Tabs, TeamGrid, Testimonial, TestimonialGrid, ThemeEditor, Toolbar, VerticalBuilder, VideoEmbed, applyStoredTheme, applyThemeVars, asAssetRef, assetRefId, clearThemeVars, createAgentSelectionStore, createAiFields, createArbiBlocksConfig, createArbiMaterializer, createBlockKit, createImageField, createModuleRegistry, createModuleStore, createPromptLibrary, createSiteBlocks, createStudioPersistence, createThemeStore, createVerticalExport, createWebsiteBlocks, daysUntil, fmtDate, fmtDateTime, fromNow, gbp, hrs, iconMap, initials, isAssetRef, isAuthenticated, materializePageData, matterToContext, pct, tid, toEmbedUrl, toHslTriplet, useAssetResolverActive, useConnection, useFirmAiTask, useImageGen, useSemanticSearch, useWorkspaceDocs };
5453
+ export { ASSET_REF_PREFIX, Accordion, AgentsPanel, AppShell, AssetResolverProvider, AvatarBlock, Banner, BlockImage, CTABanner, Callout, Columns, ConnectionProvider, ContactForm, Container, DataTable, DataTableBlock, EmptyState, FAQ, FeatureGrid, Footer, Gallery, GridView, Hero, LegalArbiProvider, ListBlock, LiveDataProvider, LogoCloud, MediaText, MetricCard, ModuleManager, Navbar, PageHeader, PageRenderer, PricingTable, RichTextBlock, Section, SectionHeading, Spacer, StatGroup, Steps, StudioEditor, THEME_STYLE_ID, Tabs, TeamGrid, Testimonial, TestimonialGrid, ThemeEditor, Toolbar, VerticalBuilder, VideoEmbed, WidgetPlaceholder, applyStoredTheme, applyThemeVars, asAssetRef, assetRefId, clearThemeVars, createAgentSelectionStore, createAiFields, createArbiBlocksConfig, createArbiMaterializer, createBlockKit, createImageField, createLiveWidgets, createModuleRegistry, createModuleStore, createPromptLibrary, createSiteBlocks, createStudioPersistence, createThemeStore, createVerticalExport, createWebsiteBlocks, daysUntil, fmtDate, fmtDateTime, fromNow, gbp, hrs, iconMap, initials, isAssetRef, isAuthenticated, materializePageData, matterToContext, pct, tid, toEmbedUrl, toHslTriplet, useAssetResolverActive, useConnection, useFirmAiTask, useImageGen, useLiveDataActive, useSemanticSearch, useWorkspaceDocs };
5083
5454
  //# sourceMappingURL=index.js.map
5084
5455
  //# sourceMappingURL=index.js.map