@arbidocs/blocks 0.3.114 → 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 +432 -27
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +158 -55
- package/dist/index.d.ts +158 -55
- package/dist/index.js +432 -32
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
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) {
|
|
@@ -2376,7 +2692,8 @@ function StudioEditor({
|
|
|
2376
2692
|
live,
|
|
2377
2693
|
initialSlug,
|
|
2378
2694
|
testIdPrefix = "studio",
|
|
2379
|
-
publishAsset
|
|
2695
|
+
publishAsset,
|
|
2696
|
+
resetSeam = false
|
|
2380
2697
|
}) {
|
|
2381
2698
|
const arbi = react.useArbi();
|
|
2382
2699
|
const materialize = react$1.useMemo(
|
|
@@ -2407,6 +2724,14 @@ function StudioEditor({
|
|
|
2407
2724
|
cancelled = true;
|
|
2408
2725
|
};
|
|
2409
2726
|
}, [slug, live]);
|
|
2727
|
+
react$1.useEffect(() => {
|
|
2728
|
+
if (!resetSeam) return;
|
|
2729
|
+
const w = window;
|
|
2730
|
+
w.__studioResetSlug = (s) => persistence.deletePage(arbi, live, s);
|
|
2731
|
+
return () => {
|
|
2732
|
+
delete w.__studioResetSlug;
|
|
2733
|
+
};
|
|
2734
|
+
}, [resetSeam, arbi, live, persistence]);
|
|
2410
2735
|
const persist4 = react$1.useCallback(
|
|
2411
2736
|
async (toSave) => {
|
|
2412
2737
|
setSaving(true);
|
|
@@ -2563,34 +2888,36 @@ function PageRenderer({
|
|
|
2563
2888
|
live,
|
|
2564
2889
|
slug: slugProp,
|
|
2565
2890
|
emptyState,
|
|
2566
|
-
testIdPrefix = "studio"
|
|
2891
|
+
testIdPrefix = "studio",
|
|
2892
|
+
liveData = false,
|
|
2893
|
+
initialData,
|
|
2894
|
+
applyTheme = true
|
|
2567
2895
|
}) {
|
|
2568
2896
|
const params = reactRouterDom.useParams();
|
|
2569
2897
|
const slug = slugProp ?? params.slug ?? "home";
|
|
2570
2898
|
const arbi = react.useArbi();
|
|
2571
|
-
const [data, setData] = react$1.useState(null);
|
|
2572
|
-
const [loading, setLoading] = react$1.useState(
|
|
2899
|
+
const [data, setData] = react$1.useState(initialData ?? null);
|
|
2900
|
+
const [loading, setLoading] = react$1.useState(!initialData);
|
|
2573
2901
|
react$1.useEffect(() => {
|
|
2574
2902
|
let cancelled = false;
|
|
2575
|
-
setLoading(
|
|
2903
|
+
setLoading(!initialData);
|
|
2576
2904
|
void (async () => {
|
|
2577
|
-
const
|
|
2578
|
-
persistence.loadPublished(arbi, live, slug),
|
|
2579
|
-
persistence.loadTheme(arbi, live)
|
|
2580
|
-
]);
|
|
2905
|
+
const theme = applyTheme ? (await persistence.loadTheme(arbi, live)).data : void 0;
|
|
2581
2906
|
if (cancelled) return;
|
|
2582
|
-
const page =
|
|
2907
|
+
const page = initialData ?? (await persistence.loadPublished(arbi, live, slug)).data ?? (await persistence.loadPage(arbi, live, slug)).data;
|
|
2583
2908
|
if (cancelled) return;
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2909
|
+
if (applyTheme) {
|
|
2910
|
+
applyThemeVars(
|
|
2911
|
+
theme ? { ...defaultTheme, ...theme, colors: { ...defaultTheme.colors, ...theme.colors } } : defaultTheme
|
|
2912
|
+
);
|
|
2913
|
+
}
|
|
2587
2914
|
setData(page);
|
|
2588
2915
|
setLoading(false);
|
|
2589
2916
|
})();
|
|
2590
2917
|
return () => {
|
|
2591
2918
|
cancelled = true;
|
|
2592
2919
|
};
|
|
2593
|
-
}, [arbi, live, slug, persistence, defaultTheme]);
|
|
2920
|
+
}, [arbi, live, slug, persistence, defaultTheme, initialData, applyTheme]);
|
|
2594
2921
|
if (loading) {
|
|
2595
2922
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2596
2923
|
"div",
|
|
@@ -2616,7 +2943,8 @@ function PageRenderer({
|
|
|
2616
2943
|
}
|
|
2617
2944
|
);
|
|
2618
2945
|
}
|
|
2619
|
-
|
|
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 });
|
|
2620
2948
|
}
|
|
2621
2949
|
|
|
2622
2950
|
// src/studio/persistence.ts
|
|
@@ -2692,6 +3020,26 @@ function createStudioPersistence(cfg) {
|
|
|
2692
3020
|
function loadPublished(arbi, live, slug) {
|
|
2693
3021
|
return readPage(arbi, live, publishedKey(slug), publishedFile(slug));
|
|
2694
3022
|
}
|
|
3023
|
+
async function deletePage(arbi, live, slug) {
|
|
3024
|
+
try {
|
|
3025
|
+
localStorage.removeItem(pageKey(slug));
|
|
3026
|
+
localStorage.removeItem(publishedKey(slug));
|
|
3027
|
+
} catch {
|
|
3028
|
+
}
|
|
3029
|
+
if (!canUseArbi(arbi, live)) return;
|
|
3030
|
+
try {
|
|
3031
|
+
await arbi.selectWorkspace(WORKSPACE_ID);
|
|
3032
|
+
const docs = await arbi.documents.list();
|
|
3033
|
+
const names = [pageFile(slug), publishedFile(slug)];
|
|
3034
|
+
const ids = docs.filter(
|
|
3035
|
+
(d) => (d.folder ?? "").includes(FOLDER) && names.some((n) => (d.file_name ?? "").endsWith(n))
|
|
3036
|
+
).map((d) => d.external_id);
|
|
3037
|
+
if (ids.length) await arbi.documents.delete(ids);
|
|
3038
|
+
console.info(`[studio] deleted ${ids.length} doc(s) for "${slug}" from ARBI`);
|
|
3039
|
+
} catch (e) {
|
|
3040
|
+
console.warn(`[studio] ARBI delete failed for "${slug}"`, e);
|
|
3041
|
+
}
|
|
3042
|
+
}
|
|
2695
3043
|
async function saveTheme(arbi, live, theme) {
|
|
2696
3044
|
try {
|
|
2697
3045
|
localStorage.setItem(THEME_KEY, JSON.stringify(theme));
|
|
@@ -2728,7 +3076,7 @@ function createStudioPersistence(cfg) {
|
|
|
2728
3076
|
return null;
|
|
2729
3077
|
}
|
|
2730
3078
|
}
|
|
2731
|
-
return { savePage, loadPage, savePublished, loadPublished, saveTheme, loadTheme };
|
|
3079
|
+
return { savePage, loadPage, savePublished, loadPublished, deletePage, saveTheme, loadTheme };
|
|
2732
3080
|
}
|
|
2733
3081
|
function AiTextField({
|
|
2734
3082
|
value,
|
|
@@ -2968,7 +3316,7 @@ function ImageFieldControl({
|
|
|
2968
3316
|
{
|
|
2969
3317
|
src: thumb,
|
|
2970
3318
|
alt: d.file_name ?? "",
|
|
2971
|
-
className: "aspect-square w-full object-
|
|
3319
|
+
className: "aspect-square w-full bg-muted object-contain"
|
|
2972
3320
|
}
|
|
2973
3321
|
) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex aspect-square w-full items-center justify-center bg-muted text-muted-foreground", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ImagePlus, { className: "size-4" }) })
|
|
2974
3322
|
},
|
|
@@ -4204,6 +4552,13 @@ function createSiteBlocks(kit) {
|
|
|
4204
4552
|
TestimonialGrid: TestimonialGridBlock
|
|
4205
4553
|
};
|
|
4206
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" };
|
|
4207
4562
|
function createArbiBlocksConfig(opts = {}) {
|
|
4208
4563
|
const kit = createBlockKit(opts.aiFields, opts.imageField, opts.eyebrowClassName);
|
|
4209
4564
|
const { textField, textareaField, boolRadio, iconField, resolveIcon, blockTestId } = kit;
|
|
@@ -4241,16 +4596,19 @@ function createArbiBlocksConfig(opts = {}) {
|
|
|
4241
4596
|
fields: {
|
|
4242
4597
|
title: textField("Title"),
|
|
4243
4598
|
eyebrow: textField("Eyebrow"),
|
|
4244
|
-
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"] }
|
|
4245
4603
|
},
|
|
4246
|
-
defaultProps: { title: "Page title" },
|
|
4247
|
-
|
|
4248
|
-
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(
|
|
4249
4606
|
PageHeader,
|
|
4250
4607
|
{
|
|
4251
4608
|
title,
|
|
4252
4609
|
eyebrow,
|
|
4253
4610
|
description,
|
|
4611
|
+
actions: /* @__PURE__ */ jsxRuntime.jsx(Actions, { className: "flex items-center gap-2" }),
|
|
4254
4612
|
testId: blockTestId(id)
|
|
4255
4613
|
}
|
|
4256
4614
|
)
|
|
@@ -4260,16 +4618,18 @@ function createArbiBlocksConfig(opts = {}) {
|
|
|
4260
4618
|
fields: {
|
|
4261
4619
|
title: textField("Title"),
|
|
4262
4620
|
description: textareaField("Description", void 0, "description"),
|
|
4263
|
-
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"] }
|
|
4264
4624
|
},
|
|
4265
|
-
defaultProps: { title: "Nothing here yet" },
|
|
4266
|
-
|
|
4267
|
-
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(
|
|
4268
4627
|
EmptyState,
|
|
4269
4628
|
{
|
|
4270
4629
|
title,
|
|
4271
4630
|
description,
|
|
4272
4631
|
icon: resolveIcon(icon),
|
|
4632
|
+
action: /* @__PURE__ */ jsxRuntime.jsx(Action, {}),
|
|
4273
4633
|
testId: blockTestId(id)
|
|
4274
4634
|
}
|
|
4275
4635
|
)
|
|
@@ -4322,6 +4682,42 @@ function createArbiBlocksConfig(opts = {}) {
|
|
|
4322
4682
|
}
|
|
4323
4683
|
)
|
|
4324
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
|
+
};
|
|
4325
4721
|
const DataTableBlockConfig = {
|
|
4326
4722
|
label: "Data table",
|
|
4327
4723
|
fields: {
|
|
@@ -4520,11 +4916,13 @@ function createArbiBlocksConfig(opts = {}) {
|
|
|
4520
4916
|
};
|
|
4521
4917
|
const website = createWebsiteBlocks(kit);
|
|
4522
4918
|
const site = createSiteBlocks(kit);
|
|
4919
|
+
const widgets = opts.liveWidgets ? createLiveWidgets(kit) : {};
|
|
4523
4920
|
const components = {
|
|
4524
4921
|
SectionHeading: SectionHeadingBlock,
|
|
4525
4922
|
PageHeader: PageHeaderBlock,
|
|
4526
4923
|
EmptyState: EmptyStateBlock,
|
|
4527
4924
|
MetricCard: MetricCardBlock,
|
|
4925
|
+
MetricRow: MetricRowBlock,
|
|
4528
4926
|
DataTable: DataTableBlockConfig,
|
|
4529
4927
|
Section: SectionBlock,
|
|
4530
4928
|
Card: CardBlock,
|
|
@@ -4532,7 +4930,8 @@ function createArbiBlocksConfig(opts = {}) {
|
|
|
4532
4930
|
Badge: BadgeBlock,
|
|
4533
4931
|
Separator: SeparatorBlock,
|
|
4534
4932
|
...website,
|
|
4535
|
-
...site
|
|
4933
|
+
...site,
|
|
4934
|
+
...widgets
|
|
4536
4935
|
};
|
|
4537
4936
|
const config = {
|
|
4538
4937
|
categories: {
|
|
@@ -4566,7 +4965,8 @@ function createArbiBlocksConfig(opts = {}) {
|
|
|
4566
4965
|
Commerce: { title: "Commerce", components: ["Pricing"] },
|
|
4567
4966
|
Forms: { title: "Forms", components: ["ContactForm"] },
|
|
4568
4967
|
Navigation: { title: "Navigation", components: ["Navbar", "Footer"] },
|
|
4569
|
-
Data: { title: "Data", components: ["MetricCard", "DataTable"] },
|
|
4968
|
+
Data: { title: "Data", components: ["MetricRow", "MetricCard", "DataTable"] },
|
|
4969
|
+
...opts.liveWidgets ? { Widgets: { title: "Widgets", components: ["AgentActivity", "LiveMetric"] } } : {},
|
|
4570
4970
|
Primitives: { title: "Primitives", components: ["Button", "Badge", "Separator"] }
|
|
4571
4971
|
},
|
|
4572
4972
|
components,
|
|
@@ -5090,6 +5490,7 @@ Object.defineProperty(exports, "toRedlineMarkdown", {
|
|
|
5090
5490
|
exports.ASSET_REF_PREFIX = ASSET_REF_PREFIX;
|
|
5091
5491
|
exports.Accordion = Accordion;
|
|
5092
5492
|
exports.AgentsPanel = AgentsPanel;
|
|
5493
|
+
exports.AppShell = AppShell;
|
|
5093
5494
|
exports.AssetResolverProvider = AssetResolverProvider;
|
|
5094
5495
|
exports.AvatarBlock = AvatarBlock;
|
|
5095
5496
|
exports.Banner = Banner;
|
|
@@ -5111,6 +5512,7 @@ exports.GridView = GridView;
|
|
|
5111
5512
|
exports.Hero = Hero;
|
|
5112
5513
|
exports.LegalArbiProvider = LegalArbiProvider;
|
|
5113
5514
|
exports.ListBlock = ListBlock;
|
|
5515
|
+
exports.LiveDataProvider = LiveDataProvider;
|
|
5114
5516
|
exports.LogoCloud = LogoCloud;
|
|
5115
5517
|
exports.MediaText = MediaText;
|
|
5116
5518
|
exports.MetricCard = MetricCard;
|
|
@@ -5135,6 +5537,7 @@ exports.ThemeEditor = ThemeEditor;
|
|
|
5135
5537
|
exports.Toolbar = Toolbar;
|
|
5136
5538
|
exports.VerticalBuilder = VerticalBuilder;
|
|
5137
5539
|
exports.VideoEmbed = VideoEmbed;
|
|
5540
|
+
exports.WidgetPlaceholder = WidgetPlaceholder;
|
|
5138
5541
|
exports.applyStoredTheme = applyStoredTheme;
|
|
5139
5542
|
exports.applyThemeVars = applyThemeVars;
|
|
5140
5543
|
exports.asAssetRef = asAssetRef;
|
|
@@ -5146,6 +5549,7 @@ exports.createArbiBlocksConfig = createArbiBlocksConfig;
|
|
|
5146
5549
|
exports.createArbiMaterializer = createArbiMaterializer;
|
|
5147
5550
|
exports.createBlockKit = createBlockKit;
|
|
5148
5551
|
exports.createImageField = createImageField;
|
|
5552
|
+
exports.createLiveWidgets = createLiveWidgets;
|
|
5149
5553
|
exports.createModuleRegistry = createModuleRegistry;
|
|
5150
5554
|
exports.createModuleStore = createModuleStore;
|
|
5151
5555
|
exports.createPromptLibrary = createPromptLibrary;
|
|
@@ -5174,6 +5578,7 @@ exports.useAssetResolverActive = useAssetResolverActive;
|
|
|
5174
5578
|
exports.useConnection = useConnection;
|
|
5175
5579
|
exports.useFirmAiTask = useFirmAiTask;
|
|
5176
5580
|
exports.useImageGen = useImageGen;
|
|
5581
|
+
exports.useLiveDataActive = useLiveDataActive;
|
|
5177
5582
|
exports.useSemanticSearch = useSemanticSearch;
|
|
5178
5583
|
exports.useWorkspaceDocs = useWorkspaceDocs;
|
|
5179
5584
|
//# sourceMappingURL=index.cjs.map
|