@arbidocs/blocks 0.3.111 → 0.3.113
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 +1548 -112
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +371 -31
- package/dist/index.d.ts +371 -31
- package/dist/index.js +1529 -116
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/src/studio/studio.css +19 -18
package/dist/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
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, ArrowLeft, Loader2, ChevronUp, BrainCircuit, Bot, LayoutGrid, FileJson,
|
|
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, TabsList, TabsTrigger, TabsContent, Separator, ArbiWebSocketProvider } 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, 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';
|
|
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 } from '@tanstack/react-query';
|
|
@@ -808,7 +808,7 @@ function EmptyState({
|
|
|
808
808
|
"data-testid": testId,
|
|
809
809
|
children: [
|
|
810
810
|
Icon && /* @__PURE__ */ jsx("div", { className: "mb-3 flex size-11 items-center justify-center rounded-full bg-muted text-muted-foreground", children: /* @__PURE__ */ jsx(Icon, { className: "size-5" }) }),
|
|
811
|
-
/* @__PURE__ */ jsx("p", { className: "font-display text-base font-semibold text-
|
|
811
|
+
/* @__PURE__ */ jsx("p", { className: "font-display text-base font-semibold text-foreground", children: title }),
|
|
812
812
|
description && /* @__PURE__ */ jsx("p", { className: "mt-1 max-w-sm text-sm text-muted-foreground", children: description }),
|
|
813
813
|
action && /* @__PURE__ */ jsx("div", { className: "mt-4", children: action })
|
|
814
814
|
]
|
|
@@ -1176,10 +1176,10 @@ function GridView({
|
|
|
1176
1176
|
const BORDER = 2;
|
|
1177
1177
|
const PREFERRED_PAD = 16;
|
|
1178
1178
|
const padTotal = Math.max(0, Math.min(PREFERRED_PAD, rowHeight - BORDER - LINE_PX));
|
|
1179
|
-
const
|
|
1179
|
+
const lines2 = Math.max(1, Math.floor((rowHeight - BORDER - padTotal) / LINE_PX));
|
|
1180
1180
|
const vars = style;
|
|
1181
1181
|
vars["--abstract-pad-y"] = `${padTotal / 2}px`;
|
|
1182
|
-
vars["--abstract-max-h"] = `${
|
|
1182
|
+
vars["--abstract-max-h"] = `${lines2 * LINE_PX + padTotal + BORDER}px`;
|
|
1183
1183
|
}
|
|
1184
1184
|
return style;
|
|
1185
1185
|
}, [height, rowHeight]);
|
|
@@ -1301,8 +1301,8 @@ function PageHeader({
|
|
|
1301
1301
|
"data-testid": testId,
|
|
1302
1302
|
children: [
|
|
1303
1303
|
/* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
|
|
1304
|
-
eyebrow && /* @__PURE__ */ jsx("p", { className: "
|
|
1305
|
-
/* @__PURE__ */ jsx("h1", { className: "font-display text-2xl font-semibold tracking-tight text-
|
|
1304
|
+
eyebrow && /* @__PURE__ */ jsx("p", { className: "mb-1 text-xs font-semibold uppercase tracking-wider text-muted-foreground", children: eyebrow }),
|
|
1305
|
+
/* @__PURE__ */ jsx("h1", { className: "font-display text-2xl font-semibold tracking-tight text-foreground", children: title }),
|
|
1306
1306
|
description && /* @__PURE__ */ jsx("p", { className: "mt-1 max-w-2xl text-sm text-muted-foreground", children: description })
|
|
1307
1307
|
] }),
|
|
1308
1308
|
actions && /* @__PURE__ */ jsx("div", { className: "flex shrink-0 items-center gap-2", children: actions })
|
|
@@ -1316,16 +1316,26 @@ function SectionHeading({
|
|
|
1316
1316
|
lead,
|
|
1317
1317
|
align = "left",
|
|
1318
1318
|
className,
|
|
1319
|
-
invert
|
|
1319
|
+
invert,
|
|
1320
|
+
eyebrowClassName
|
|
1320
1321
|
}) {
|
|
1321
1322
|
return /* @__PURE__ */ jsxs("div", { className: cn("max-w-3xl", align === "center" && "mx-auto text-center", className), children: [
|
|
1322
|
-
eyebrow && /* @__PURE__ */ jsx(
|
|
1323
|
+
eyebrow && /* @__PURE__ */ jsx(
|
|
1324
|
+
"p",
|
|
1325
|
+
{
|
|
1326
|
+
className: cn(
|
|
1327
|
+
"mb-3 text-xs font-semibold uppercase tracking-wider",
|
|
1328
|
+
eyebrowClassName ?? (invert ? "text-background/60" : "text-muted-foreground")
|
|
1329
|
+
),
|
|
1330
|
+
children: eyebrow
|
|
1331
|
+
}
|
|
1332
|
+
),
|
|
1323
1333
|
/* @__PURE__ */ jsx(
|
|
1324
1334
|
"h2",
|
|
1325
1335
|
{
|
|
1326
1336
|
className: cn(
|
|
1327
1337
|
"font-display text-3xl font-semibold tracking-tight text-balance sm:text-4xl",
|
|
1328
|
-
invert ? "text-
|
|
1338
|
+
invert ? "text-background" : "text-foreground"
|
|
1329
1339
|
),
|
|
1330
1340
|
children: title
|
|
1331
1341
|
}
|
|
@@ -1335,7 +1345,7 @@ function SectionHeading({
|
|
|
1335
1345
|
{
|
|
1336
1346
|
className: cn(
|
|
1337
1347
|
"mt-4 text-lg leading-relaxed",
|
|
1338
|
-
invert ? "text-
|
|
1348
|
+
invert ? "text-background/70" : "text-muted-foreground"
|
|
1339
1349
|
),
|
|
1340
1350
|
children: lead
|
|
1341
1351
|
}
|
|
@@ -1387,15 +1397,15 @@ function MetricCard({
|
|
|
1387
1397
|
icon: Icon,
|
|
1388
1398
|
hint,
|
|
1389
1399
|
delta,
|
|
1390
|
-
accent = "
|
|
1400
|
+
accent = "default",
|
|
1391
1401
|
testId
|
|
1392
1402
|
}) {
|
|
1393
|
-
const
|
|
1403
|
+
const accentStyle = accent && accent !== "default" ? { color: `var(--color-${accent})` } : void 0;
|
|
1394
1404
|
const deltaGood = delta?.good ?? delta?.direction === "up";
|
|
1395
1405
|
return /* @__PURE__ */ jsxs(Card, { variant: "elevated", className: "card-hover rounded-xl p-5", "data-testid": testId, children: [
|
|
1396
1406
|
/* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-3", children: [
|
|
1397
1407
|
/* @__PURE__ */ jsx("p", { className: "text-xs font-medium uppercase tracking-wide text-muted-foreground", children: label }),
|
|
1398
|
-
Icon && /* @__PURE__ */ jsx(Icon, { className:
|
|
1408
|
+
Icon && /* @__PURE__ */ jsx(Icon, { className: "size-4 shrink-0", style: accentStyle })
|
|
1399
1409
|
] }),
|
|
1400
1410
|
/* @__PURE__ */ jsx(
|
|
1401
1411
|
"p",
|
|
@@ -1423,6 +1433,54 @@ function MetricCard({
|
|
|
1423
1433
|
] })
|
|
1424
1434
|
] });
|
|
1425
1435
|
}
|
|
1436
|
+
var ASSET_REF_PREFIX = "asset:";
|
|
1437
|
+
function asAssetRef(docId) {
|
|
1438
|
+
return `${ASSET_REF_PREFIX}${docId}`;
|
|
1439
|
+
}
|
|
1440
|
+
function assetRefId(src) {
|
|
1441
|
+
return src.slice(ASSET_REF_PREFIX.length);
|
|
1442
|
+
}
|
|
1443
|
+
var AssetResolverContext = createContext(false);
|
|
1444
|
+
function AssetResolverProvider({ children }) {
|
|
1445
|
+
return /* @__PURE__ */ jsx(AssetResolverContext.Provider, { value: true, children });
|
|
1446
|
+
}
|
|
1447
|
+
function useAssetResolverActive() {
|
|
1448
|
+
return useContext(AssetResolverContext);
|
|
1449
|
+
}
|
|
1450
|
+
function isAssetRef(src) {
|
|
1451
|
+
return !!src && src.startsWith(ASSET_REF_PREFIX);
|
|
1452
|
+
}
|
|
1453
|
+
function useAssetImage(docId) {
|
|
1454
|
+
const arbi = useArbi();
|
|
1455
|
+
const { data: thumbs } = useThumbnails([docId]);
|
|
1456
|
+
const thumb = thumbs?.[docId];
|
|
1457
|
+
const { data: objectUrl } = useQuery({
|
|
1458
|
+
queryKey: ["arbi", "asset-image", docId],
|
|
1459
|
+
queryFn: async () => {
|
|
1460
|
+
const res = await arbi.documents.download(docId);
|
|
1461
|
+
const blob = await res.blob();
|
|
1462
|
+
return URL.createObjectURL(blob);
|
|
1463
|
+
},
|
|
1464
|
+
// Only download when the thumbnail is genuinely absent (not merely loading).
|
|
1465
|
+
enabled: !!docId && thumbs != null && !thumb && arbi.hasWorkspace,
|
|
1466
|
+
staleTime: 10 * 6e4
|
|
1467
|
+
});
|
|
1468
|
+
useEffect(() => {
|
|
1469
|
+
return () => {
|
|
1470
|
+
if (objectUrl) URL.revokeObjectURL(objectUrl);
|
|
1471
|
+
};
|
|
1472
|
+
}, [objectUrl]);
|
|
1473
|
+
return thumb ?? objectUrl;
|
|
1474
|
+
}
|
|
1475
|
+
function AssetThumb({
|
|
1476
|
+
docId,
|
|
1477
|
+
alt,
|
|
1478
|
+
className,
|
|
1479
|
+
testId
|
|
1480
|
+
}) {
|
|
1481
|
+
const src = useAssetImage(docId);
|
|
1482
|
+
return /* @__PURE__ */ jsx("img", { src: src ?? void 0, alt: alt ?? "", className, "data-testid": testId });
|
|
1483
|
+
}
|
|
1426
1484
|
var ASPECT = {
|
|
1427
1485
|
"16:9": "aspect-video",
|
|
1428
1486
|
"4:3": "aspect-[4/3]",
|
|
@@ -1441,11 +1499,28 @@ function BlockImage({
|
|
|
1441
1499
|
alt = "",
|
|
1442
1500
|
aspect = "16:9",
|
|
1443
1501
|
rounded = "lg",
|
|
1502
|
+
framed,
|
|
1444
1503
|
className,
|
|
1445
1504
|
testId
|
|
1446
1505
|
}) {
|
|
1447
|
-
const shape = cn(
|
|
1448
|
-
|
|
1506
|
+
const shape = cn(
|
|
1507
|
+
ASPECT[aspect] ?? ASPECT["16:9"],
|
|
1508
|
+
ROUNDED[rounded] ?? ROUNDED.lg,
|
|
1509
|
+
framed && "ring-1 ring-border shadow-xl"
|
|
1510
|
+
);
|
|
1511
|
+
const canResolve = useAssetResolverActive();
|
|
1512
|
+
if (canResolve && isAssetRef(src)) {
|
|
1513
|
+
return /* @__PURE__ */ jsx(
|
|
1514
|
+
AssetThumb,
|
|
1515
|
+
{
|
|
1516
|
+
docId: assetRefId(src),
|
|
1517
|
+
alt,
|
|
1518
|
+
className: cn("w-full object-cover", shape, className),
|
|
1519
|
+
testId
|
|
1520
|
+
}
|
|
1521
|
+
);
|
|
1522
|
+
}
|
|
1523
|
+
if (src && !isAssetRef(src)) {
|
|
1449
1524
|
return /* @__PURE__ */ jsx(
|
|
1450
1525
|
"img",
|
|
1451
1526
|
{
|
|
@@ -1477,38 +1552,82 @@ var TONE_SURFACE = {
|
|
|
1477
1552
|
muted: "bg-muted text-foreground",
|
|
1478
1553
|
ink: "bg-foreground text-background"
|
|
1479
1554
|
};
|
|
1555
|
+
function renderTitle(title, emphasis) {
|
|
1556
|
+
if (!emphasis || !title.includes(emphasis)) return title;
|
|
1557
|
+
const parts = title.split(emphasis);
|
|
1558
|
+
return parts.map((part, i) => /* @__PURE__ */ jsxs("span", { children: [
|
|
1559
|
+
part,
|
|
1560
|
+
i < parts.length - 1 && /* @__PURE__ */ jsx("span", { className: "text-primary", children: emphasis })
|
|
1561
|
+
] }, i));
|
|
1562
|
+
}
|
|
1480
1563
|
function Hero({
|
|
1481
1564
|
eyebrow,
|
|
1482
1565
|
title,
|
|
1566
|
+
emphasis,
|
|
1483
1567
|
subtitle,
|
|
1484
1568
|
primaryCta,
|
|
1485
1569
|
secondaryCta,
|
|
1486
1570
|
align = "left",
|
|
1487
1571
|
tone = "default",
|
|
1488
1572
|
imageUrl,
|
|
1573
|
+
backgroundImage,
|
|
1574
|
+
overlay = true,
|
|
1575
|
+
eyebrowClassName,
|
|
1489
1576
|
testId
|
|
1490
1577
|
}) {
|
|
1491
|
-
const
|
|
1492
|
-
const
|
|
1493
|
-
const
|
|
1494
|
-
const
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1578
|
+
const onDark = Boolean(backgroundImage) || tone === "ink";
|
|
1579
|
+
const subtle = onDark ? "text-background/70" : "text-muted-foreground";
|
|
1580
|
+
const eyebrowClass = eyebrowClassName ?? (onDark ? "text-background/60" : "text-muted-foreground");
|
|
1581
|
+
const centered = align === "center" || Boolean(backgroundImage);
|
|
1582
|
+
const heading = renderTitle(title, emphasis);
|
|
1583
|
+
const copy = /* @__PURE__ */ jsxs(
|
|
1584
|
+
"div",
|
|
1585
|
+
{
|
|
1586
|
+
className: cn(
|
|
1587
|
+
"max-w-2xl",
|
|
1588
|
+
centered && "mx-auto text-center",
|
|
1589
|
+
backgroundImage && "text-background"
|
|
1590
|
+
),
|
|
1591
|
+
children: [
|
|
1592
|
+
eyebrow && /* @__PURE__ */ jsx("p", { className: cn("mb-4 text-sm font-semibold uppercase tracking-wider", eyebrowClass), children: eyebrow }),
|
|
1593
|
+
/* @__PURE__ */ jsx("h1", { className: "text-4xl font-semibold tracking-tight text-balance sm:text-5xl", children: heading }),
|
|
1594
|
+
subtitle && /* @__PURE__ */ jsx("p", { className: cn("mt-6 text-lg leading-relaxed", subtle), children: subtitle }),
|
|
1595
|
+
(primaryCta?.label || secondaryCta?.label) && /* @__PURE__ */ jsxs(
|
|
1596
|
+
"div",
|
|
1597
|
+
{
|
|
1598
|
+
className: cn("mt-8 flex flex-col gap-3 sm:flex-row", centered && "sm:justify-center"),
|
|
1599
|
+
children: [
|
|
1600
|
+
primaryCta?.label && /* @__PURE__ */ jsx(Button, { asChild: true, size: "lg", children: /* @__PURE__ */ jsxs("a", { href: primaryCta.href || "#", children: [
|
|
1601
|
+
primaryCta.label,
|
|
1602
|
+
/* @__PURE__ */ jsx(ArrowRight, { className: "size-4" })
|
|
1603
|
+
] }) }),
|
|
1604
|
+
secondaryCta?.label && /* @__PURE__ */ jsx(Button, { asChild: true, size: "lg", variant: "outline", children: /* @__PURE__ */ jsx("a", { href: secondaryCta.href || "#", children: secondaryCta.label }) })
|
|
1605
|
+
]
|
|
1606
|
+
}
|
|
1607
|
+
)
|
|
1608
|
+
]
|
|
1609
|
+
}
|
|
1610
|
+
);
|
|
1611
|
+
if (backgroundImage) {
|
|
1612
|
+
const bgStyle = {
|
|
1613
|
+
backgroundImage: `url("${backgroundImage}")`,
|
|
1614
|
+
backgroundSize: "cover",
|
|
1615
|
+
backgroundPosition: "center"
|
|
1616
|
+
};
|
|
1617
|
+
return /* @__PURE__ */ jsxs(
|
|
1618
|
+
"section",
|
|
1500
1619
|
{
|
|
1501
|
-
className:
|
|
1620
|
+
className: "relative isolate overflow-hidden",
|
|
1621
|
+
style: bgStyle,
|
|
1622
|
+
"data-testid": testId,
|
|
1623
|
+
"data-bg": testId && `${testId}-bg`,
|
|
1502
1624
|
children: [
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
/* @__PURE__ */ jsx(ArrowRight, { className: "size-4" })
|
|
1506
|
-
] }) }),
|
|
1507
|
-
secondaryCta?.label && /* @__PURE__ */ jsx(Button, { asChild: true, size: "lg", variant: "outline", children: /* @__PURE__ */ jsx("a", { href: secondaryCta.href || "#", children: secondaryCta.label }) })
|
|
1625
|
+
overlay && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-black/55", "aria-hidden": true }),
|
|
1626
|
+
/* @__PURE__ */ jsx("div", { className: "relative mx-auto max-w-7xl px-5 py-28 lg:px-8 lg:py-36", children: copy })
|
|
1508
1627
|
]
|
|
1509
1628
|
}
|
|
1510
|
-
)
|
|
1511
|
-
|
|
1629
|
+
);
|
|
1630
|
+
}
|
|
1512
1631
|
return /* @__PURE__ */ jsx("section", { className: cn(TONE_SURFACE[tone] ?? TONE_SURFACE.default), "data-testid": testId, children: /* @__PURE__ */ jsx("div", { className: "mx-auto max-w-7xl px-5 py-20 lg:px-8 lg:py-28", children: centered ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-12", children: [
|
|
1513
1632
|
copy,
|
|
1514
1633
|
imageUrl && /* @__PURE__ */ jsx(
|
|
@@ -1593,23 +1712,49 @@ var TONE_SURFACE2 = {
|
|
|
1593
1712
|
muted: "bg-muted text-foreground",
|
|
1594
1713
|
ink: "bg-foreground text-background"
|
|
1595
1714
|
};
|
|
1596
|
-
function CTABanner({
|
|
1597
|
-
|
|
1715
|
+
function CTABanner({
|
|
1716
|
+
title,
|
|
1717
|
+
subtitle,
|
|
1718
|
+
cta,
|
|
1719
|
+
tone = "default",
|
|
1720
|
+
backgroundImage,
|
|
1721
|
+
overlay = true,
|
|
1722
|
+
testId
|
|
1723
|
+
}) {
|
|
1724
|
+
const onImage = Boolean(backgroundImage);
|
|
1725
|
+
const subtle = onImage ? "text-white/80" : tone === "default" ? "text-primary-foreground/80" : tone === "ink" ? "text-background/70" : "text-muted-foreground";
|
|
1726
|
+
const bgStyle = backgroundImage ? {
|
|
1727
|
+
backgroundImage: `url("${backgroundImage}")`,
|
|
1728
|
+
backgroundSize: "cover",
|
|
1729
|
+
backgroundPosition: "center"
|
|
1730
|
+
} : void 0;
|
|
1598
1731
|
return /* @__PURE__ */ jsxs(
|
|
1599
1732
|
"div",
|
|
1600
1733
|
{
|
|
1601
1734
|
className: cn(
|
|
1602
|
-
"rounded-2xl px-8 py-14 text-center",
|
|
1603
|
-
TONE_SURFACE2[tone] ?? TONE_SURFACE2.default
|
|
1735
|
+
"relative isolate overflow-hidden rounded-2xl px-8 py-14 text-center",
|
|
1736
|
+
onImage ? "text-white" : TONE_SURFACE2[tone] ?? TONE_SURFACE2.default
|
|
1604
1737
|
),
|
|
1738
|
+
style: bgStyle,
|
|
1605
1739
|
"data-testid": testId,
|
|
1606
1740
|
children: [
|
|
1607
|
-
/* @__PURE__ */ jsx("
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
/* @__PURE__ */ jsx(
|
|
1612
|
-
|
|
1741
|
+
onImage && overlay && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-black/55", "aria-hidden": true }),
|
|
1742
|
+
/* @__PURE__ */ jsxs("div", { className: "relative", children: [
|
|
1743
|
+
/* @__PURE__ */ jsx("h2", { className: "text-3xl font-semibold tracking-tight text-balance sm:text-4xl", children: title }),
|
|
1744
|
+
subtitle && /* @__PURE__ */ jsx("p", { className: cn("mx-auto mt-4 max-w-2xl text-lg", subtle), children: subtitle }),
|
|
1745
|
+
cta?.label && /* @__PURE__ */ jsx("div", { className: "mt-8", children: /* @__PURE__ */ jsx(
|
|
1746
|
+
Button,
|
|
1747
|
+
{
|
|
1748
|
+
asChild: true,
|
|
1749
|
+
size: "lg",
|
|
1750
|
+
variant: tone === "default" && !onImage ? "secondary" : "default",
|
|
1751
|
+
children: /* @__PURE__ */ jsxs("a", { href: cta.href || "#", children: [
|
|
1752
|
+
cta.label,
|
|
1753
|
+
/* @__PURE__ */ jsx(ArrowRight, { className: "size-4" })
|
|
1754
|
+
] })
|
|
1755
|
+
}
|
|
1756
|
+
) })
|
|
1757
|
+
] })
|
|
1613
1758
|
]
|
|
1614
1759
|
}
|
|
1615
1760
|
);
|
|
@@ -1774,6 +1919,449 @@ function Container({ width = "default", padding = "md", children, testId }) {
|
|
|
1774
1919
|
}
|
|
1775
1920
|
);
|
|
1776
1921
|
}
|
|
1922
|
+
function PricingTable({ plans, testId }) {
|
|
1923
|
+
return /* @__PURE__ */ jsx(
|
|
1924
|
+
"div",
|
|
1925
|
+
{
|
|
1926
|
+
className: cn("grid gap-6", (plans?.length ?? 0) >= 3 ? "md:grid-cols-3" : "sm:grid-cols-2"),
|
|
1927
|
+
"data-testid": testId,
|
|
1928
|
+
children: (plans ?? []).map((plan, i) => /* @__PURE__ */ jsxs(
|
|
1929
|
+
Card,
|
|
1930
|
+
{
|
|
1931
|
+
variant: plan.featured ? "elevated" : "outline",
|
|
1932
|
+
className: cn("flex h-full flex-col p-6", plan.featured && "ring-2 ring-primary"),
|
|
1933
|
+
"data-testid": testId && `${testId}-item-${i}`,
|
|
1934
|
+
children: [
|
|
1935
|
+
/* @__PURE__ */ jsx("h3", { className: "text-lg font-semibold tracking-tight", children: plan.name }),
|
|
1936
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-3 flex items-baseline gap-1", children: [
|
|
1937
|
+
/* @__PURE__ */ jsx("span", { className: "text-3xl font-semibold tracking-tight", children: plan.price }),
|
|
1938
|
+
plan.period && /* @__PURE__ */ jsxs("span", { className: "text-sm text-muted-foreground", children: [
|
|
1939
|
+
"/",
|
|
1940
|
+
plan.period
|
|
1941
|
+
] })
|
|
1942
|
+
] }),
|
|
1943
|
+
plan.description && /* @__PURE__ */ jsx("p", { className: "mt-2 text-sm leading-relaxed text-muted-foreground", children: plan.description }),
|
|
1944
|
+
plan.features && plan.features.length > 0 && /* @__PURE__ */ jsx("ul", { className: "mt-5 flex-1 space-y-2", children: plan.features.map((f, j) => /* @__PURE__ */ jsxs("li", { className: "flex items-start gap-2 text-sm", children: [
|
|
1945
|
+
/* @__PURE__ */ jsx(Check, { className: "mt-0.5 size-4 shrink-0 text-primary" }),
|
|
1946
|
+
/* @__PURE__ */ jsx("span", { children: f })
|
|
1947
|
+
] }, j)) }),
|
|
1948
|
+
plan.cta?.label && /* @__PURE__ */ jsx(Button, { asChild: true, className: "mt-6 w-full", variant: plan.featured ? "default" : "outline", children: /* @__PURE__ */ jsx("a", { href: plan.cta.href || "#", children: plan.cta.label }) })
|
|
1949
|
+
]
|
|
1950
|
+
},
|
|
1951
|
+
i
|
|
1952
|
+
))
|
|
1953
|
+
}
|
|
1954
|
+
);
|
|
1955
|
+
}
|
|
1956
|
+
function Tabs({ items, testId }) {
|
|
1957
|
+
const [active, setActive] = useState(0);
|
|
1958
|
+
const list = items ?? [];
|
|
1959
|
+
const current = list[Math.min(active, Math.max(list.length - 1, 0))];
|
|
1960
|
+
return /* @__PURE__ */ jsxs("div", { "data-testid": testId, children: [
|
|
1961
|
+
/* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1 border-b border-border", role: "tablist", children: list.map((t, i) => {
|
|
1962
|
+
const selected = i === active;
|
|
1963
|
+
return /* @__PURE__ */ jsx(
|
|
1964
|
+
"button",
|
|
1965
|
+
{
|
|
1966
|
+
type: "button",
|
|
1967
|
+
role: "tab",
|
|
1968
|
+
"aria-selected": selected,
|
|
1969
|
+
onClick: () => setActive(i),
|
|
1970
|
+
className: cn(
|
|
1971
|
+
"-mb-px border-b-2 px-4 py-2 text-sm font-medium transition-colors",
|
|
1972
|
+
selected ? "border-primary text-foreground" : "border-transparent text-muted-foreground hover:text-foreground"
|
|
1973
|
+
),
|
|
1974
|
+
"data-testid": testId && `${testId}-tab-${i}`,
|
|
1975
|
+
children: t.label
|
|
1976
|
+
},
|
|
1977
|
+
i
|
|
1978
|
+
);
|
|
1979
|
+
}) }),
|
|
1980
|
+
current && /* @__PURE__ */ jsx(
|
|
1981
|
+
"div",
|
|
1982
|
+
{
|
|
1983
|
+
role: "tabpanel",
|
|
1984
|
+
className: "pt-5 text-sm leading-relaxed text-muted-foreground",
|
|
1985
|
+
"data-testid": testId && `${testId}-panel`,
|
|
1986
|
+
children: current.content
|
|
1987
|
+
}
|
|
1988
|
+
)
|
|
1989
|
+
] });
|
|
1990
|
+
}
|
|
1991
|
+
function Accordion({ items, allowMultiple = false, testId }) {
|
|
1992
|
+
const [open, setOpen] = useState([]);
|
|
1993
|
+
const toggle2 = (i) => setOpen(
|
|
1994
|
+
(prev) => prev.includes(i) ? prev.filter((n) => n !== i) : allowMultiple ? [...prev, i] : [i]
|
|
1995
|
+
);
|
|
1996
|
+
return /* @__PURE__ */ jsx("div", { className: "divide-y divide-border border-y border-border", "data-testid": testId, children: (items ?? []).map((item, i) => {
|
|
1997
|
+
const isOpen = open.includes(i);
|
|
1998
|
+
return /* @__PURE__ */ jsxs("div", { "data-testid": testId && `${testId}-item-${i}`, children: [
|
|
1999
|
+
/* @__PURE__ */ jsxs(
|
|
2000
|
+
"button",
|
|
2001
|
+
{
|
|
2002
|
+
type: "button",
|
|
2003
|
+
"aria-expanded": isOpen,
|
|
2004
|
+
onClick: () => toggle2(i),
|
|
2005
|
+
className: "flex w-full items-center justify-between gap-4 py-4 text-left text-base font-medium",
|
|
2006
|
+
"data-testid": testId && `${testId}-trigger-${i}`,
|
|
2007
|
+
children: [
|
|
2008
|
+
item.title,
|
|
2009
|
+
/* @__PURE__ */ jsx(
|
|
2010
|
+
ChevronDown,
|
|
2011
|
+
{
|
|
2012
|
+
className: cn("size-4 shrink-0 transition-transform", isOpen && "rotate-180")
|
|
2013
|
+
}
|
|
2014
|
+
)
|
|
2015
|
+
]
|
|
2016
|
+
}
|
|
2017
|
+
),
|
|
2018
|
+
isOpen && /* @__PURE__ */ jsx(
|
|
2019
|
+
"p",
|
|
2020
|
+
{
|
|
2021
|
+
className: "pb-4 text-sm leading-relaxed text-muted-foreground",
|
|
2022
|
+
"data-testid": testId && `${testId}-content-${i}`,
|
|
2023
|
+
children: item.content
|
|
2024
|
+
}
|
|
2025
|
+
)
|
|
2026
|
+
] }, i);
|
|
2027
|
+
}) });
|
|
2028
|
+
}
|
|
2029
|
+
var COLS2 = {
|
|
2030
|
+
"2": "grid-cols-2",
|
|
2031
|
+
"3": "grid-cols-2 sm:grid-cols-3",
|
|
2032
|
+
"4": "grid-cols-2 sm:grid-cols-4"
|
|
2033
|
+
};
|
|
2034
|
+
function Gallery({
|
|
2035
|
+
images,
|
|
2036
|
+
columns = "3",
|
|
2037
|
+
aspect = "1:1",
|
|
2038
|
+
rounded = "lg",
|
|
2039
|
+
testId
|
|
2040
|
+
}) {
|
|
2041
|
+
return /* @__PURE__ */ jsx("div", { className: cn("grid gap-4", COLS2[columns] ?? COLS2["3"]), "data-testid": testId, children: (images ?? []).map((img, i) => /* @__PURE__ */ jsxs("figure", { "data-testid": testId && `${testId}-item-${i}`, children: [
|
|
2042
|
+
/* @__PURE__ */ jsx(
|
|
2043
|
+
BlockImage,
|
|
2044
|
+
{
|
|
2045
|
+
src: img.src,
|
|
2046
|
+
alt: img.alt,
|
|
2047
|
+
aspect,
|
|
2048
|
+
rounded,
|
|
2049
|
+
testId: testId && `${testId}-item-${i}-image`
|
|
2050
|
+
}
|
|
2051
|
+
),
|
|
2052
|
+
img.caption && /* @__PURE__ */ jsx("figcaption", { className: "mt-1.5 text-xs text-muted-foreground", children: img.caption })
|
|
2053
|
+
] }, i)) });
|
|
2054
|
+
}
|
|
2055
|
+
var ASPECT2 = {
|
|
2056
|
+
"16:9": "aspect-video",
|
|
2057
|
+
"4:3": "aspect-[4/3]",
|
|
2058
|
+
"1:1": "aspect-square"
|
|
2059
|
+
};
|
|
2060
|
+
function toEmbedUrl(url) {
|
|
2061
|
+
if (!url) return null;
|
|
2062
|
+
const yt = url.match(/(?:youtube\.com\/watch\?v=|youtu\.be\/|youtube\.com\/embed\/)([\w-]{11})/);
|
|
2063
|
+
if (yt) return `https://www.youtube.com/embed/${yt[1]}`;
|
|
2064
|
+
const vimeo = url.match(/vimeo\.com\/(?:video\/)?(\d+)/);
|
|
2065
|
+
if (vimeo) return `https://player.vimeo.com/video/${vimeo[1]}`;
|
|
2066
|
+
const loom = url.match(/loom\.com\/(?:share|embed)\/([\w-]+)/);
|
|
2067
|
+
if (loom) return `https://www.loom.com/embed/${loom[1]}`;
|
|
2068
|
+
return null;
|
|
2069
|
+
}
|
|
2070
|
+
function VideoEmbed({
|
|
2071
|
+
url,
|
|
2072
|
+
title,
|
|
2073
|
+
aspect = "16:9",
|
|
2074
|
+
rounded = true,
|
|
2075
|
+
testId
|
|
2076
|
+
}) {
|
|
2077
|
+
const embed = toEmbedUrl(url);
|
|
2078
|
+
const frame = cn(
|
|
2079
|
+
"w-full overflow-hidden border border-border bg-muted",
|
|
2080
|
+
ASPECT2[aspect] ?? ASPECT2["16:9"],
|
|
2081
|
+
rounded && "rounded-lg"
|
|
2082
|
+
);
|
|
2083
|
+
return /* @__PURE__ */ jsx("div", { className: frame, "data-testid": testId, children: embed ? /* @__PURE__ */ jsx(
|
|
2084
|
+
"iframe",
|
|
2085
|
+
{
|
|
2086
|
+
src: embed,
|
|
2087
|
+
title: title || "Embedded video",
|
|
2088
|
+
className: "size-full",
|
|
2089
|
+
allow: "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture",
|
|
2090
|
+
allowFullScreen: true,
|
|
2091
|
+
"data-testid": testId && `${testId}-iframe`
|
|
2092
|
+
}
|
|
2093
|
+
) : url ? /* @__PURE__ */ jsx("video", { src: url, controls: true, className: "size-full", "data-testid": testId && `${testId}-video` }) : null });
|
|
2094
|
+
}
|
|
2095
|
+
function ContactForm({
|
|
2096
|
+
fields,
|
|
2097
|
+
submitLabel = "Send",
|
|
2098
|
+
action,
|
|
2099
|
+
successMessage = "Thanks \u2014 we\u2019ll be in touch shortly.",
|
|
2100
|
+
testId
|
|
2101
|
+
}) {
|
|
2102
|
+
const [sent, setSent] = useState(false);
|
|
2103
|
+
const list = fields ?? [];
|
|
2104
|
+
const onSubmit = (e) => {
|
|
2105
|
+
if (!action) {
|
|
2106
|
+
e.preventDefault();
|
|
2107
|
+
setSent(true);
|
|
2108
|
+
}
|
|
2109
|
+
};
|
|
2110
|
+
if (sent) {
|
|
2111
|
+
return /* @__PURE__ */ jsx(
|
|
2112
|
+
"p",
|
|
2113
|
+
{
|
|
2114
|
+
className: "rounded-lg border border-border bg-muted p-6 text-center text-sm text-foreground",
|
|
2115
|
+
role: "status",
|
|
2116
|
+
"data-testid": testId && `${testId}-success`,
|
|
2117
|
+
children: successMessage
|
|
2118
|
+
}
|
|
2119
|
+
);
|
|
2120
|
+
}
|
|
2121
|
+
return /* @__PURE__ */ jsxs(
|
|
2122
|
+
"form",
|
|
2123
|
+
{
|
|
2124
|
+
action,
|
|
2125
|
+
method: action ? "post" : void 0,
|
|
2126
|
+
onSubmit,
|
|
2127
|
+
className: "mx-auto flex max-w-xl flex-col gap-4",
|
|
2128
|
+
"data-testid": testId,
|
|
2129
|
+
children: [
|
|
2130
|
+
list.map((f, i) => {
|
|
2131
|
+
const inputClass = "w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground outline-none focus:border-primary";
|
|
2132
|
+
return /* @__PURE__ */ jsxs("label", { className: "flex flex-col gap-1.5 text-left", children: [
|
|
2133
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm font-medium", children: f.label }),
|
|
2134
|
+
f.type === "textarea" ? /* @__PURE__ */ jsx(
|
|
2135
|
+
"textarea",
|
|
2136
|
+
{
|
|
2137
|
+
name: f.name,
|
|
2138
|
+
required: f.required,
|
|
2139
|
+
rows: 4,
|
|
2140
|
+
className: inputClass,
|
|
2141
|
+
"data-testid": testId && `${testId}-field-${f.name}`
|
|
2142
|
+
}
|
|
2143
|
+
) : /* @__PURE__ */ jsx(
|
|
2144
|
+
"input",
|
|
2145
|
+
{
|
|
2146
|
+
name: f.name,
|
|
2147
|
+
type: f.type || "text",
|
|
2148
|
+
required: f.required,
|
|
2149
|
+
className: inputClass,
|
|
2150
|
+
"data-testid": testId && `${testId}-field-${f.name}`
|
|
2151
|
+
}
|
|
2152
|
+
)
|
|
2153
|
+
] }, i);
|
|
2154
|
+
}),
|
|
2155
|
+
/* @__PURE__ */ jsx(
|
|
2156
|
+
Button,
|
|
2157
|
+
{
|
|
2158
|
+
type: "submit",
|
|
2159
|
+
className: cn("mt-1 self-start"),
|
|
2160
|
+
"data-testid": testId && `${testId}-submit`,
|
|
2161
|
+
children: submitLabel
|
|
2162
|
+
}
|
|
2163
|
+
)
|
|
2164
|
+
]
|
|
2165
|
+
}
|
|
2166
|
+
);
|
|
2167
|
+
}
|
|
2168
|
+
var TONE = {
|
|
2169
|
+
primary: "bg-primary text-primary-foreground",
|
|
2170
|
+
muted: "bg-muted text-foreground",
|
|
2171
|
+
ink: "bg-foreground text-background"
|
|
2172
|
+
};
|
|
2173
|
+
function Banner({ text, cta, tone = "primary", testId }) {
|
|
2174
|
+
return /* @__PURE__ */ jsxs(
|
|
2175
|
+
"div",
|
|
2176
|
+
{
|
|
2177
|
+
className: cn("w-full px-5 py-2.5 text-center text-sm", TONE[tone] ?? TONE.primary),
|
|
2178
|
+
"data-testid": testId,
|
|
2179
|
+
children: [
|
|
2180
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium", children: text }),
|
|
2181
|
+
cta?.label && /* @__PURE__ */ jsxs(
|
|
2182
|
+
"a",
|
|
2183
|
+
{
|
|
2184
|
+
href: cta.href || "#",
|
|
2185
|
+
className: "ml-2 inline-flex items-center gap-1 font-semibold underline underline-offset-4",
|
|
2186
|
+
"data-testid": testId && `${testId}-cta`,
|
|
2187
|
+
children: [
|
|
2188
|
+
cta.label,
|
|
2189
|
+
/* @__PURE__ */ jsx(ArrowRight, { className: "size-3.5" })
|
|
2190
|
+
]
|
|
2191
|
+
}
|
|
2192
|
+
)
|
|
2193
|
+
]
|
|
2194
|
+
}
|
|
2195
|
+
);
|
|
2196
|
+
}
|
|
2197
|
+
function MediaText({
|
|
2198
|
+
eyebrow,
|
|
2199
|
+
title,
|
|
2200
|
+
body,
|
|
2201
|
+
imageUrl,
|
|
2202
|
+
mediaSide = "left",
|
|
2203
|
+
cta,
|
|
2204
|
+
testId
|
|
2205
|
+
}) {
|
|
2206
|
+
const media = /* @__PURE__ */ jsx(BlockImage, { src: imageUrl, aspect: "4:3", rounded: "lg", testId: testId && `${testId}-image` });
|
|
2207
|
+
const copy = /* @__PURE__ */ jsxs("div", { children: [
|
|
2208
|
+
eyebrow && /* @__PURE__ */ jsx("p", { className: "mb-3 text-xs font-semibold uppercase tracking-wider text-muted-foreground", children: eyebrow }),
|
|
2209
|
+
/* @__PURE__ */ jsx("h2", { className: "text-3xl font-semibold tracking-tight text-balance", children: title }),
|
|
2210
|
+
body && /* @__PURE__ */ jsx("p", { className: "mt-4 text-lg leading-relaxed text-muted-foreground", children: body }),
|
|
2211
|
+
cta?.label && /* @__PURE__ */ jsx(Button, { asChild: true, className: "mt-6", children: /* @__PURE__ */ jsxs("a", { href: cta.href || "#", children: [
|
|
2212
|
+
cta.label,
|
|
2213
|
+
/* @__PURE__ */ jsx(ArrowRight, { className: "size-4" })
|
|
2214
|
+
] }) })
|
|
2215
|
+
] });
|
|
2216
|
+
return /* @__PURE__ */ jsx("div", { className: "grid items-center gap-10 lg:grid-cols-2", "data-testid": testId, children: mediaSide === "right" ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2217
|
+
copy,
|
|
2218
|
+
media
|
|
2219
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2220
|
+
media,
|
|
2221
|
+
copy
|
|
2222
|
+
] }) });
|
|
2223
|
+
}
|
|
2224
|
+
var COLS3 = {
|
|
2225
|
+
"2": "sm:grid-cols-2",
|
|
2226
|
+
"3": "sm:grid-cols-2 lg:grid-cols-3",
|
|
2227
|
+
"4": "sm:grid-cols-2 lg:grid-cols-4"
|
|
2228
|
+
};
|
|
2229
|
+
function TeamGrid({ members, columns = "3", testId }) {
|
|
2230
|
+
return /* @__PURE__ */ jsx("div", { className: cn("grid gap-6", COLS3[columns] ?? COLS3["3"]), "data-testid": testId, children: (members ?? []).map((m, i) => /* @__PURE__ */ jsxs(
|
|
2231
|
+
Card,
|
|
2232
|
+
{
|
|
2233
|
+
variant: "outline",
|
|
2234
|
+
className: "flex h-full flex-col items-center p-6 text-center",
|
|
2235
|
+
"data-testid": testId && `${testId}-item-${i}`,
|
|
2236
|
+
children: [
|
|
2237
|
+
/* @__PURE__ */ jsxs(Avatar, { className: "size-20", children: [
|
|
2238
|
+
m.avatarUrl && /* @__PURE__ */ jsx(AvatarImage, { src: m.avatarUrl, alt: m.name }),
|
|
2239
|
+
/* @__PURE__ */ jsx(AvatarFallback, { children: initials(m.name || "?") })
|
|
2240
|
+
] }),
|
|
2241
|
+
/* @__PURE__ */ jsx("h3", { className: "mt-4 text-base font-semibold tracking-tight", children: m.name }),
|
|
2242
|
+
m.role && /* @__PURE__ */ jsx("p", { className: "text-sm text-primary", children: m.role }),
|
|
2243
|
+
m.bio && /* @__PURE__ */ jsx("p", { className: "mt-2 text-sm leading-relaxed text-muted-foreground", children: m.bio })
|
|
2244
|
+
]
|
|
2245
|
+
},
|
|
2246
|
+
i
|
|
2247
|
+
)) });
|
|
2248
|
+
}
|
|
2249
|
+
function Steps({ steps, variant = "numbered", testId }) {
|
|
2250
|
+
const list = steps ?? [];
|
|
2251
|
+
if (variant === "timeline") {
|
|
2252
|
+
return /* @__PURE__ */ jsx("ol", { className: "relative ml-3 border-l border-border", "data-testid": testId, children: list.map((s, i) => /* @__PURE__ */ jsxs("li", { className: "mb-8 ml-6 last:mb-0", "data-testid": testId && `${testId}-item-${i}`, children: [
|
|
2253
|
+
/* @__PURE__ */ jsx("span", { className: "absolute -left-3 flex size-6 items-center justify-center rounded-full bg-primary text-xs font-semibold text-primary-foreground", children: i + 1 }),
|
|
2254
|
+
/* @__PURE__ */ jsx("h3", { className: "text-base font-semibold tracking-tight", children: s.title }),
|
|
2255
|
+
s.description && /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm leading-relaxed text-muted-foreground", children: s.description })
|
|
2256
|
+
] }, i)) });
|
|
2257
|
+
}
|
|
2258
|
+
return /* @__PURE__ */ jsx(
|
|
2259
|
+
"div",
|
|
2260
|
+
{
|
|
2261
|
+
className: cn("grid gap-6", list.length >= 4 ? "md:grid-cols-4" : "sm:grid-cols-3"),
|
|
2262
|
+
"data-testid": testId,
|
|
2263
|
+
children: list.map((s, i) => /* @__PURE__ */ jsxs("div", { "data-testid": testId && `${testId}-item-${i}`, children: [
|
|
2264
|
+
/* @__PURE__ */ jsx("span", { className: "flex size-9 items-center justify-center rounded-full bg-primary/10 text-sm font-semibold text-primary", children: i + 1 }),
|
|
2265
|
+
/* @__PURE__ */ jsx("h3", { className: "mt-4 text-base font-semibold tracking-tight", children: s.title }),
|
|
2266
|
+
s.description && /* @__PURE__ */ jsx("p", { className: "mt-2 text-sm leading-relaxed text-muted-foreground", children: s.description })
|
|
2267
|
+
] }, i))
|
|
2268
|
+
}
|
|
2269
|
+
);
|
|
2270
|
+
}
|
|
2271
|
+
var COLS4 = {
|
|
2272
|
+
"2": "sm:grid-cols-2",
|
|
2273
|
+
"3": "sm:grid-cols-2 lg:grid-cols-3"
|
|
2274
|
+
};
|
|
2275
|
+
function TestimonialGrid({ items, columns = "3", testId }) {
|
|
2276
|
+
return /* @__PURE__ */ jsx("div", { className: cn("grid gap-6", COLS4[columns] ?? COLS4["3"]), "data-testid": testId, children: (items ?? []).map((t, i) => /* @__PURE__ */ jsxs(
|
|
2277
|
+
Card,
|
|
2278
|
+
{
|
|
2279
|
+
variant: "outline",
|
|
2280
|
+
className: "flex h-full flex-col p-6",
|
|
2281
|
+
"data-testid": testId && `${testId}-item-${i}`,
|
|
2282
|
+
children: [
|
|
2283
|
+
/* @__PURE__ */ jsx(Quote, { className: "size-6 text-primary/40", "aria-hidden": true }),
|
|
2284
|
+
/* @__PURE__ */ jsx("blockquote", { className: "mt-3 flex-1 text-base leading-relaxed", children: t.quote }),
|
|
2285
|
+
(t.author || t.avatarUrl) && /* @__PURE__ */ jsxs("div", { className: "mt-5 flex items-center gap-3", children: [
|
|
2286
|
+
/* @__PURE__ */ jsxs(Avatar, { className: "size-9", children: [
|
|
2287
|
+
t.avatarUrl && /* @__PURE__ */ jsx(AvatarImage, { src: t.avatarUrl, alt: t.author ?? "" }),
|
|
2288
|
+
/* @__PURE__ */ jsx(AvatarFallback, { children: initials(t.author || "?") })
|
|
2289
|
+
] }),
|
|
2290
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
2291
|
+
t.author && /* @__PURE__ */ jsx("p", { className: "text-sm font-semibold", children: t.author }),
|
|
2292
|
+
t.role && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: t.role })
|
|
2293
|
+
] })
|
|
2294
|
+
] })
|
|
2295
|
+
]
|
|
2296
|
+
},
|
|
2297
|
+
i
|
|
2298
|
+
)) });
|
|
2299
|
+
}
|
|
2300
|
+
|
|
2301
|
+
// src/studio/publish.ts
|
|
2302
|
+
function blobToDataUrl(blob) {
|
|
2303
|
+
return new Promise((resolve, reject) => {
|
|
2304
|
+
const reader = new FileReader();
|
|
2305
|
+
reader.onload = () => resolve(reader.result);
|
|
2306
|
+
reader.onerror = () => reject(reader.error);
|
|
2307
|
+
reader.readAsDataURL(blob);
|
|
2308
|
+
});
|
|
2309
|
+
}
|
|
2310
|
+
function createArbiMaterializer(arbi, opts = {}) {
|
|
2311
|
+
return async (docId) => {
|
|
2312
|
+
try {
|
|
2313
|
+
const res = await arbi.documents.download(docId);
|
|
2314
|
+
const blob = await res.blob();
|
|
2315
|
+
if (opts.publishAsset) return await opts.publishAsset(docId, blob);
|
|
2316
|
+
return await blobToDataUrl(blob);
|
|
2317
|
+
} catch {
|
|
2318
|
+
return null;
|
|
2319
|
+
}
|
|
2320
|
+
};
|
|
2321
|
+
}
|
|
2322
|
+
function collectAssetRefs(node, acc) {
|
|
2323
|
+
if (typeof node === "string") {
|
|
2324
|
+
if (isAssetRef(node)) acc.add(node);
|
|
2325
|
+
return;
|
|
2326
|
+
}
|
|
2327
|
+
if (Array.isArray(node)) {
|
|
2328
|
+
for (const child of node) collectAssetRefs(child, acc);
|
|
2329
|
+
return;
|
|
2330
|
+
}
|
|
2331
|
+
if (node && typeof node === "object") {
|
|
2332
|
+
for (const value of Object.values(node)) collectAssetRefs(value, acc);
|
|
2333
|
+
}
|
|
2334
|
+
}
|
|
2335
|
+
function replaceAssetRefs(node, replacements) {
|
|
2336
|
+
if (typeof node === "string") {
|
|
2337
|
+
return replacements.get(node) ?? node;
|
|
2338
|
+
}
|
|
2339
|
+
if (Array.isArray(node)) {
|
|
2340
|
+
return node.map((child) => replaceAssetRefs(child, replacements));
|
|
2341
|
+
}
|
|
2342
|
+
if (node && typeof node === "object") {
|
|
2343
|
+
const out = {};
|
|
2344
|
+
for (const [key, value] of Object.entries(node)) {
|
|
2345
|
+
out[key] = replaceAssetRefs(value, replacements);
|
|
2346
|
+
}
|
|
2347
|
+
return out;
|
|
2348
|
+
}
|
|
2349
|
+
return node;
|
|
2350
|
+
}
|
|
2351
|
+
async function materializePageData(data, materialize) {
|
|
2352
|
+
const refs = /* @__PURE__ */ new Set();
|
|
2353
|
+
collectAssetRefs(data, refs);
|
|
2354
|
+
if (refs.size === 0) return data;
|
|
2355
|
+
const replacements = /* @__PURE__ */ new Map();
|
|
2356
|
+
await Promise.all(
|
|
2357
|
+
[...refs].map(async (ref) => {
|
|
2358
|
+
const src = await materialize(assetRefId(ref));
|
|
2359
|
+
if (src) replacements.set(ref, src);
|
|
2360
|
+
})
|
|
2361
|
+
);
|
|
2362
|
+
if (replacements.size === 0) return data;
|
|
2363
|
+
return replaceAssetRefs(data, replacements);
|
|
2364
|
+
}
|
|
1777
2365
|
var EMPTY_DATA = { content: [], root: {} };
|
|
1778
2366
|
function StudioEditor({
|
|
1779
2367
|
config,
|
|
@@ -1786,9 +2374,14 @@ function StudioEditor({
|
|
|
1786
2374
|
backLabel = "Back",
|
|
1787
2375
|
live,
|
|
1788
2376
|
initialSlug,
|
|
1789
|
-
testIdPrefix = "studio"
|
|
2377
|
+
testIdPrefix = "studio",
|
|
2378
|
+
publishAsset
|
|
1790
2379
|
}) {
|
|
1791
2380
|
const arbi = useArbi();
|
|
2381
|
+
const materialize = useMemo(
|
|
2382
|
+
() => createArbiMaterializer(arbi, { publishAsset }),
|
|
2383
|
+
[arbi, publishAsset]
|
|
2384
|
+
);
|
|
1792
2385
|
const [slug, setSlug] = useState(initialSlug ?? pages[0]?.slug ?? "home");
|
|
1793
2386
|
const [data, setData] = useState(EMPTY_DATA);
|
|
1794
2387
|
const [rev, setRev] = useState(0);
|
|
@@ -1816,11 +2409,16 @@ function StudioEditor({
|
|
|
1816
2409
|
const persist4 = useCallback(
|
|
1817
2410
|
async (toSave) => {
|
|
1818
2411
|
setSaving(true);
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
2412
|
+
try {
|
|
2413
|
+
const where = await persistence.savePage(arbi, live, slug, toSave);
|
|
2414
|
+
const published = await materializePageData(toSave, materialize);
|
|
2415
|
+
await persistence.savePublished(arbi, live, slug, published);
|
|
2416
|
+
setVia(where);
|
|
2417
|
+
} finally {
|
|
2418
|
+
setSaving(false);
|
|
2419
|
+
}
|
|
1822
2420
|
},
|
|
1823
|
-
[arbi, live, slug, persistence]
|
|
2421
|
+
[arbi, live, slug, persistence, materialize]
|
|
1824
2422
|
);
|
|
1825
2423
|
useEffect(() => {
|
|
1826
2424
|
let cancelled = false;
|
|
@@ -1850,7 +2448,7 @@ function StudioEditor({
|
|
|
1850
2448
|
Link,
|
|
1851
2449
|
{
|
|
1852
2450
|
to: backHref,
|
|
1853
|
-
className: "inline-flex items-center gap-1.5 rounded-md px-2 py-1 text-sm text-muted-foreground hover:text-
|
|
2451
|
+
className: "inline-flex items-center gap-1.5 rounded-md px-2 py-1 text-sm text-muted-foreground hover:text-primary",
|
|
1854
2452
|
"data-testid": `${testIdPrefix}-back`,
|
|
1855
2453
|
children: [
|
|
1856
2454
|
/* @__PURE__ */ jsx(ArrowLeft, { className: "size-4" }),
|
|
@@ -1861,7 +2459,7 @@ function StudioEditor({
|
|
|
1861
2459
|
),
|
|
1862
2460
|
/* @__PURE__ */ jsx("span", { className: "h-4 w-px bg-border" })
|
|
1863
2461
|
] }),
|
|
1864
|
-
/* @__PURE__ */ jsx("span", { className: "font-display text-sm font-semibold text-
|
|
2462
|
+
/* @__PURE__ */ jsx("span", { className: "font-display text-sm font-semibold text-foreground", children: brandLabel }),
|
|
1865
2463
|
/* @__PURE__ */ jsx(
|
|
1866
2464
|
"select",
|
|
1867
2465
|
{
|
|
@@ -1889,7 +2487,7 @@ function StudioEditor({
|
|
|
1889
2487
|
{
|
|
1890
2488
|
type: "button",
|
|
1891
2489
|
onClick: () => setThemeOpen((o) => !o),
|
|
1892
|
-
className: "inline-flex items-center gap-1.5 rounded-md border border-border px-3 py-1.5 text-sm font-medium text-foreground hover:border-
|
|
2490
|
+
className: "inline-flex items-center gap-1.5 rounded-md border border-border px-3 py-1.5 text-sm font-medium text-foreground hover:border-primary hover:text-primary",
|
|
1893
2491
|
"data-testid": `${testIdPrefix}-theme-toggle`,
|
|
1894
2492
|
children: [
|
|
1895
2493
|
/* @__PURE__ */ jsx(Palette, { className: "size-4" }),
|
|
@@ -1903,7 +2501,7 @@ function StudioEditor({
|
|
|
1903
2501
|
type: "button",
|
|
1904
2502
|
onClick: () => void persist4(latest.current),
|
|
1905
2503
|
disabled: saving || loading,
|
|
1906
|
-
className: "inline-flex items-center gap-1.5 rounded-md bg-
|
|
2504
|
+
className: "inline-flex items-center gap-1.5 rounded-md bg-primary px-3 py-1.5 text-sm font-medium text-primary-foreground hover:bg-primary/90 disabled:opacity-60",
|
|
1907
2505
|
"data-testid": `${testIdPrefix}-publish`,
|
|
1908
2506
|
children: [
|
|
1909
2507
|
saving ? /* @__PURE__ */ jsx(Loader2, { className: "size-4 animate-spin" }) : /* @__PURE__ */ jsx(Rocket, { className: "size-4" }),
|
|
@@ -1926,7 +2524,7 @@ function StudioEditor({
|
|
|
1926
2524
|
"\u201D\u2026"
|
|
1927
2525
|
]
|
|
1928
2526
|
}
|
|
1929
|
-
) : /* @__PURE__ */ jsx(
|
|
2527
|
+
) : /* @__PURE__ */ jsx(AssetResolverProvider, { children: /* @__PURE__ */ jsx(
|
|
1930
2528
|
Puck,
|
|
1931
2529
|
{
|
|
1932
2530
|
config,
|
|
@@ -1937,7 +2535,7 @@ function StudioEditor({
|
|
|
1937
2535
|
onPublish: (d) => void persist4(d)
|
|
1938
2536
|
},
|
|
1939
2537
|
`${slug}-${rev}`
|
|
1940
|
-
) }),
|
|
2538
|
+
) }) }),
|
|
1941
2539
|
themeOpen && /* @__PURE__ */ jsx(
|
|
1942
2540
|
"aside",
|
|
1943
2541
|
{
|
|
@@ -1975,11 +2573,13 @@ function PageRenderer({
|
|
|
1975
2573
|
let cancelled = false;
|
|
1976
2574
|
setLoading(true);
|
|
1977
2575
|
void (async () => {
|
|
1978
|
-
const [{ data:
|
|
1979
|
-
persistence.
|
|
2576
|
+
const [{ data: published }, { data: theme }] = await Promise.all([
|
|
2577
|
+
persistence.loadPublished(arbi, live, slug),
|
|
1980
2578
|
persistence.loadTheme(arbi, live)
|
|
1981
2579
|
]);
|
|
1982
2580
|
if (cancelled) return;
|
|
2581
|
+
const page = published ?? (await persistence.loadPage(arbi, live, slug)).data;
|
|
2582
|
+
if (cancelled) return;
|
|
1983
2583
|
applyThemeVars(
|
|
1984
2584
|
theme ? { ...defaultTheme, ...theme, colors: { ...defaultTheme.colors, ...theme.colors } } : defaultTheme
|
|
1985
2585
|
);
|
|
@@ -2009,7 +2609,7 @@ function PageRenderer({
|
|
|
2009
2609
|
className: "mx-auto max-w-2xl px-5 py-24 text-center",
|
|
2010
2610
|
"data-testid": `${testIdPrefix}-render-empty`,
|
|
2011
2611
|
children: [
|
|
2012
|
-
/* @__PURE__ */ jsx("h1", { className: "font-display text-2xl font-semibold text-
|
|
2612
|
+
/* @__PURE__ */ jsx("h1", { className: "font-display text-2xl font-semibold text-foreground", children: title }),
|
|
2013
2613
|
/* @__PURE__ */ jsx("p", { className: "mt-2 text-sm text-muted-foreground", children: body })
|
|
2014
2614
|
]
|
|
2015
2615
|
}
|
|
@@ -2023,9 +2623,11 @@ function createStudioPersistence(cfg) {
|
|
|
2023
2623
|
const WORKSPACE_ID = cfg.workspaceId;
|
|
2024
2624
|
const FOLDER = cfg.folder ?? "site";
|
|
2025
2625
|
const pageKey = (slug) => `${cfg.storagePrefix}${slug}`;
|
|
2626
|
+
const publishedKey = (slug) => `${cfg.storagePrefix}published-${slug}`;
|
|
2026
2627
|
const THEME_KEY = cfg.themeStorageKey ?? `${cfg.storagePrefix}theme`;
|
|
2027
2628
|
const THEME_FILE = cfg.themeFileName ?? "theme.json";
|
|
2028
2629
|
const pageFile = cfg.pageFileName ?? ((slug) => `${slug}.json`);
|
|
2630
|
+
const publishedFile = cfg.publishedFileName ?? ((slug) => `${slug}.published.json`);
|
|
2029
2631
|
function canUseArbi(arbi, live) {
|
|
2030
2632
|
return live && !!arbi;
|
|
2031
2633
|
}
|
|
@@ -2048,39 +2650,47 @@ function createStudioPersistence(cfg) {
|
|
|
2048
2650
|
return JSON.parse(raw);
|
|
2049
2651
|
}
|
|
2050
2652
|
}
|
|
2051
|
-
async function
|
|
2653
|
+
async function writePage(arbi, live, kind, localKey, fileName, data) {
|
|
2052
2654
|
try {
|
|
2053
|
-
localStorage.setItem(
|
|
2655
|
+
localStorage.setItem(localKey, JSON.stringify(data));
|
|
2054
2656
|
} catch {
|
|
2055
2657
|
}
|
|
2056
2658
|
if (canUseArbi(arbi, live)) {
|
|
2057
2659
|
try {
|
|
2058
|
-
await uploadJson(arbi,
|
|
2059
|
-
console.info(`[studio] saved
|
|
2660
|
+
await uploadJson(arbi, fileName, data);
|
|
2661
|
+
console.info(`[studio] saved ${kind} "${fileName}" \u2192 ARBI (${WORKSPACE_ID})`);
|
|
2060
2662
|
return "arbi";
|
|
2061
2663
|
} catch (e) {
|
|
2062
|
-
console.warn(`[studio] ARBI save failed for "${
|
|
2664
|
+
console.warn(`[studio] ARBI save failed for "${fileName}", kept localStorage`, e);
|
|
2063
2665
|
}
|
|
2064
2666
|
}
|
|
2065
|
-
console.info(`[studio] saved
|
|
2667
|
+
console.info(`[studio] saved ${kind} "${fileName}" \u2192 localStorage`);
|
|
2066
2668
|
return "local";
|
|
2067
2669
|
}
|
|
2068
|
-
async function
|
|
2670
|
+
async function readPage(arbi, live, localKey, fileName) {
|
|
2069
2671
|
if (canUseArbi(arbi, live)) {
|
|
2070
2672
|
try {
|
|
2071
|
-
const data = await downloadJson(arbi,
|
|
2072
|
-
if (data) {
|
|
2073
|
-
console.info(`[studio] loaded page "${slug}" \u2190 ARBI`);
|
|
2074
|
-
return { data, via: "arbi" };
|
|
2075
|
-
}
|
|
2673
|
+
const data = await downloadJson(arbi, fileName);
|
|
2674
|
+
if (data) return { data, via: "arbi" };
|
|
2076
2675
|
} catch (e) {
|
|
2077
|
-
console.warn(`[studio] ARBI load failed for "${
|
|
2676
|
+
console.warn(`[studio] ARBI load failed for "${fileName}", trying localStorage`, e);
|
|
2078
2677
|
}
|
|
2079
2678
|
}
|
|
2080
|
-
const raw = safeLocalGet(
|
|
2081
|
-
console.info(`[studio] loaded page "${slug}" \u2190 localStorage`);
|
|
2679
|
+
const raw = safeLocalGet(localKey);
|
|
2082
2680
|
return { data: raw ? JSON.parse(raw) : null, via: "local" };
|
|
2083
2681
|
}
|
|
2682
|
+
function savePage(arbi, live, slug, data) {
|
|
2683
|
+
return writePage(arbi, live, "page", pageKey(slug), pageFile(slug), data);
|
|
2684
|
+
}
|
|
2685
|
+
function loadPage(arbi, live, slug) {
|
|
2686
|
+
return readPage(arbi, live, pageKey(slug), pageFile(slug));
|
|
2687
|
+
}
|
|
2688
|
+
function savePublished(arbi, live, slug, data) {
|
|
2689
|
+
return writePage(arbi, live, "published page", publishedKey(slug), publishedFile(slug), data);
|
|
2690
|
+
}
|
|
2691
|
+
function loadPublished(arbi, live, slug) {
|
|
2692
|
+
return readPage(arbi, live, publishedKey(slug), publishedFile(slug));
|
|
2693
|
+
}
|
|
2084
2694
|
async function saveTheme(arbi, live, theme) {
|
|
2085
2695
|
try {
|
|
2086
2696
|
localStorage.setItem(THEME_KEY, JSON.stringify(theme));
|
|
@@ -2117,7 +2727,7 @@ function createStudioPersistence(cfg) {
|
|
|
2117
2727
|
return null;
|
|
2118
2728
|
}
|
|
2119
2729
|
}
|
|
2120
|
-
return { savePage, loadPage, saveTheme, loadTheme };
|
|
2730
|
+
return { savePage, loadPage, savePublished, loadPublished, saveTheme, loadTheme };
|
|
2121
2731
|
}
|
|
2122
2732
|
function AiTextField({
|
|
2123
2733
|
value,
|
|
@@ -2232,14 +2842,286 @@ function createAiFields(cfg) {
|
|
|
2232
2842
|
});
|
|
2233
2843
|
return { aiText, aiTextarea };
|
|
2234
2844
|
}
|
|
2235
|
-
function
|
|
2236
|
-
|
|
2237
|
-
|
|
2845
|
+
function latestImageDocId(artifacts) {
|
|
2846
|
+
for (let i = artifacts.length - 1; i >= 0; i--) {
|
|
2847
|
+
if (artifacts[i].mode === "image") return artifacts[i].doc_ext_id;
|
|
2848
|
+
}
|
|
2849
|
+
return null;
|
|
2238
2850
|
}
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2851
|
+
function useImageGen() {
|
|
2852
|
+
const task = useFirmAiTask();
|
|
2853
|
+
const { run } = task;
|
|
2854
|
+
const generate = useCallback(
|
|
2855
|
+
async (prompt) => {
|
|
2856
|
+
const trimmed = prompt.trim();
|
|
2857
|
+
if (!trimmed) return null;
|
|
2858
|
+
const result = await run(`/image ${trimmed}`, []);
|
|
2859
|
+
return latestImageDocId(result?.artifacts ?? []);
|
|
2860
|
+
},
|
|
2861
|
+
[run]
|
|
2862
|
+
);
|
|
2863
|
+
const docExtId = useMemo(() => latestImageDocId(task.artifacts), [task.artifacts]);
|
|
2864
|
+
return {
|
|
2865
|
+
docExtId,
|
|
2866
|
+
status: task.status,
|
|
2867
|
+
error: task.error,
|
|
2868
|
+
isGenerating: task.isRunning,
|
|
2869
|
+
generate,
|
|
2870
|
+
reset: task.reset
|
|
2871
|
+
};
|
|
2872
|
+
}
|
|
2873
|
+
var IMAGE_EXT = /\.(png|jpe?g|webp|gif|avif|svg)$/i;
|
|
2874
|
+
function ImageFieldControl({
|
|
2875
|
+
value,
|
|
2876
|
+
onChange,
|
|
2877
|
+
name,
|
|
2878
|
+
workspaceId,
|
|
2879
|
+
folder,
|
|
2880
|
+
testIdPrefix
|
|
2881
|
+
}) {
|
|
2882
|
+
const [mode, setMode] = useState("asset");
|
|
2883
|
+
const [url, setUrl] = useState("");
|
|
2884
|
+
const [prompt, setPrompt] = useState("");
|
|
2885
|
+
const [busy, setBusy] = useState(false);
|
|
2886
|
+
const arbi = useArbi();
|
|
2887
|
+
const gen = useImageGen();
|
|
2888
|
+
const tp = (...q) => tid(`${testIdPrefix}-image`, name, ...q);
|
|
2889
|
+
const { data: docs } = useDocuments(workspaceId);
|
|
2890
|
+
const imageDocs = useMemo(
|
|
2891
|
+
() => (docs ?? []).filter(
|
|
2892
|
+
(d) => d.file_name && IMAGE_EXT.test(d.file_name)
|
|
2893
|
+
),
|
|
2894
|
+
[docs]
|
|
2895
|
+
);
|
|
2896
|
+
const imageIds = useMemo(
|
|
2897
|
+
() => imageDocs.map((d) => d.external_id),
|
|
2898
|
+
[imageDocs]
|
|
2899
|
+
);
|
|
2900
|
+
const { data: thumbs } = useThumbnails(imageIds);
|
|
2901
|
+
const onUpload = async (e) => {
|
|
2902
|
+
const file = e.target.files?.[0];
|
|
2903
|
+
if (!file) return;
|
|
2904
|
+
setBusy(true);
|
|
2905
|
+
try {
|
|
2906
|
+
await arbi.selectWorkspace(workspaceId);
|
|
2907
|
+
const res = await arbi.documents.uploadFile(file, file.name, { folder });
|
|
2908
|
+
const id = res.doc_ext_ids?.[0];
|
|
2909
|
+
if (id) onChange(asAssetRef(id));
|
|
2910
|
+
} finally {
|
|
2911
|
+
setBusy(false);
|
|
2912
|
+
}
|
|
2913
|
+
};
|
|
2914
|
+
const onGenerate = async () => {
|
|
2915
|
+
if (!prompt.trim() || gen.isGenerating) return;
|
|
2916
|
+
const id = await gen.generate(prompt);
|
|
2917
|
+
if (id) onChange(asAssetRef(id));
|
|
2918
|
+
};
|
|
2919
|
+
const MODES = [
|
|
2920
|
+
{ key: "asset", label: "Assets", icon: ImagePlus },
|
|
2921
|
+
{ key: "upload", label: "Upload", icon: Upload },
|
|
2922
|
+
{ key: "url", label: "URL", icon: Link2 },
|
|
2923
|
+
{ key: "generate", label: "Generate", icon: Sparkles }
|
|
2924
|
+
];
|
|
2925
|
+
const inputClass = "w-full rounded-md border border-border bg-card px-2.5 py-1.5 text-sm text-foreground outline-none focus:border-primary";
|
|
2926
|
+
const tabClass = (on) => `inline-flex items-center gap-1 rounded-md px-2 py-1 text-xs font-medium ${on ? "bg-primary text-primary-foreground" : "text-muted-foreground hover:text-foreground"}`;
|
|
2927
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", "data-testid": tp(), children: [
|
|
2928
|
+
/* @__PURE__ */ jsx(BlockImage, { src: value, aspect: "16:9", rounded: "md", testId: tp("preview") }),
|
|
2929
|
+
/* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1", children: MODES.map((m) => /* @__PURE__ */ jsxs(
|
|
2930
|
+
"button",
|
|
2931
|
+
{
|
|
2932
|
+
type: "button",
|
|
2933
|
+
className: tabClass(mode === m.key),
|
|
2934
|
+
onClick: () => setMode(m.key),
|
|
2935
|
+
"data-testid": tp("mode", m.key),
|
|
2936
|
+
children: [
|
|
2937
|
+
/* @__PURE__ */ jsx(m.icon, { className: "size-3" }),
|
|
2938
|
+
" ",
|
|
2939
|
+
m.label
|
|
2940
|
+
]
|
|
2941
|
+
},
|
|
2942
|
+
m.key
|
|
2943
|
+
)) }),
|
|
2944
|
+
mode === "asset" && /* @__PURE__ */ jsxs("div", { className: "grid max-h-48 grid-cols-3 gap-1.5 overflow-auto", "data-testid": tp("assets"), children: [
|
|
2945
|
+
imageDocs.length === 0 && /* @__PURE__ */ jsx("p", { className: "col-span-3 py-3 text-center text-xs text-muted-foreground", children: "No image assets yet \u2014 upload or generate one." }),
|
|
2946
|
+
imageDocs.map((d) => {
|
|
2947
|
+
const thumb = thumbs?.[d.external_id];
|
|
2948
|
+
const selected = value === asAssetRef(d.external_id);
|
|
2949
|
+
return /* @__PURE__ */ jsx(
|
|
2950
|
+
"button",
|
|
2951
|
+
{
|
|
2952
|
+
type: "button",
|
|
2953
|
+
onClick: () => onChange(asAssetRef(d.external_id)),
|
|
2954
|
+
className: `overflow-hidden rounded border ${selected ? "border-primary ring-1 ring-primary" : "border-border"}`,
|
|
2955
|
+
title: d.file_name ?? "",
|
|
2956
|
+
"data-testid": tp("asset", d.external_id),
|
|
2957
|
+
children: thumb ? /* @__PURE__ */ jsx(
|
|
2958
|
+
"img",
|
|
2959
|
+
{
|
|
2960
|
+
src: thumb,
|
|
2961
|
+
alt: d.file_name ?? "",
|
|
2962
|
+
className: "aspect-square w-full object-cover"
|
|
2963
|
+
}
|
|
2964
|
+
) : /* @__PURE__ */ jsx("span", { className: "flex aspect-square w-full items-center justify-center bg-muted text-muted-foreground", children: /* @__PURE__ */ jsx(ImagePlus, { className: "size-4" }) })
|
|
2965
|
+
},
|
|
2966
|
+
d.external_id
|
|
2967
|
+
);
|
|
2968
|
+
})
|
|
2969
|
+
] }),
|
|
2970
|
+
mode === "upload" && /* @__PURE__ */ jsxs("label", { className: "inline-flex cursor-pointer items-center gap-2 rounded-md border border-border px-2.5 py-1.5 text-sm text-foreground", children: [
|
|
2971
|
+
busy ? /* @__PURE__ */ jsx(Loader2, { className: "size-4 animate-spin" }) : /* @__PURE__ */ jsx(Upload, { className: "size-4" }),
|
|
2972
|
+
busy ? "Uploading\u2026" : "Choose a file",
|
|
2973
|
+
/* @__PURE__ */ jsx(
|
|
2974
|
+
"input",
|
|
2975
|
+
{
|
|
2976
|
+
type: "file",
|
|
2977
|
+
accept: "image/*",
|
|
2978
|
+
className: "hidden",
|
|
2979
|
+
disabled: busy,
|
|
2980
|
+
onChange: (e) => void onUpload(e),
|
|
2981
|
+
"data-testid": tp("upload")
|
|
2982
|
+
}
|
|
2983
|
+
)
|
|
2984
|
+
] }),
|
|
2985
|
+
mode === "url" && /* @__PURE__ */ jsxs("div", { className: "flex gap-1.5", children: [
|
|
2986
|
+
/* @__PURE__ */ jsx(
|
|
2987
|
+
"input",
|
|
2988
|
+
{
|
|
2989
|
+
className: inputClass,
|
|
2990
|
+
placeholder: "https://\u2026",
|
|
2991
|
+
value: url,
|
|
2992
|
+
onChange: (e) => setUrl(e.target.value),
|
|
2993
|
+
"data-testid": tp("url")
|
|
2994
|
+
}
|
|
2995
|
+
),
|
|
2996
|
+
/* @__PURE__ */ jsx(
|
|
2997
|
+
"button",
|
|
2998
|
+
{
|
|
2999
|
+
type: "button",
|
|
3000
|
+
className: "rounded-md bg-primary px-2.5 py-1.5 text-sm font-medium text-primary-foreground disabled:opacity-50",
|
|
3001
|
+
disabled: !url.trim(),
|
|
3002
|
+
onClick: () => onChange(url.trim()),
|
|
3003
|
+
"data-testid": tp("url-apply"),
|
|
3004
|
+
children: "Use"
|
|
3005
|
+
}
|
|
3006
|
+
)
|
|
3007
|
+
] }),
|
|
3008
|
+
mode === "generate" && /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1.5", children: [
|
|
3009
|
+
/* @__PURE__ */ jsx(
|
|
3010
|
+
"textarea",
|
|
3011
|
+
{
|
|
3012
|
+
className: inputClass,
|
|
3013
|
+
rows: 2,
|
|
3014
|
+
placeholder: "Describe the image\u2026",
|
|
3015
|
+
value: prompt,
|
|
3016
|
+
onChange: (e) => setPrompt(e.target.value),
|
|
3017
|
+
"data-testid": tp("prompt")
|
|
3018
|
+
}
|
|
3019
|
+
),
|
|
3020
|
+
/* @__PURE__ */ jsxs(
|
|
3021
|
+
"button",
|
|
3022
|
+
{
|
|
3023
|
+
type: "button",
|
|
3024
|
+
className: "inline-flex items-center gap-1.5 self-start rounded-md bg-primary px-2.5 py-1.5 text-sm font-medium text-primary-foreground disabled:opacity-50",
|
|
3025
|
+
disabled: !prompt.trim() || gen.isGenerating,
|
|
3026
|
+
onClick: () => void onGenerate(),
|
|
3027
|
+
"data-testid": tp("generate"),
|
|
3028
|
+
children: [
|
|
3029
|
+
gen.isGenerating ? /* @__PURE__ */ jsx(Loader2, { className: "size-3.5 animate-spin" }) : /* @__PURE__ */ jsx(Sparkles, { className: "size-3.5" }),
|
|
3030
|
+
gen.isGenerating ? "Generating\u2026" : "Generate"
|
|
3031
|
+
]
|
|
3032
|
+
}
|
|
3033
|
+
),
|
|
3034
|
+
gen.error && /* @__PURE__ */ jsx("span", { className: "text-xs text-destructive", role: "alert", children: gen.error })
|
|
3035
|
+
] }),
|
|
3036
|
+
value && /* @__PURE__ */ jsx(
|
|
3037
|
+
"button",
|
|
3038
|
+
{
|
|
3039
|
+
type: "button",
|
|
3040
|
+
className: "self-start text-xs text-muted-foreground hover:text-foreground",
|
|
3041
|
+
onClick: () => onChange(""),
|
|
3042
|
+
"data-testid": tp("clear"),
|
|
3043
|
+
children: "Clear"
|
|
3044
|
+
}
|
|
3045
|
+
)
|
|
3046
|
+
] });
|
|
3047
|
+
}
|
|
3048
|
+
function createImageField(cfg) {
|
|
3049
|
+
const { workspaceId } = cfg;
|
|
3050
|
+
const folder = cfg.folder ?? "assets";
|
|
3051
|
+
const testIdPrefix = cfg.testIdPrefix ?? "studio";
|
|
3052
|
+
const imageField = (label) => ({
|
|
3053
|
+
type: "custom",
|
|
3054
|
+
label,
|
|
3055
|
+
render: ({ value, onChange, name }) => /* @__PURE__ */ jsx(
|
|
3056
|
+
ImageFieldControl,
|
|
3057
|
+
{
|
|
3058
|
+
value: value ?? "",
|
|
3059
|
+
onChange,
|
|
3060
|
+
name,
|
|
3061
|
+
workspaceId,
|
|
3062
|
+
folder,
|
|
3063
|
+
testIdPrefix
|
|
3064
|
+
}
|
|
3065
|
+
)
|
|
3066
|
+
});
|
|
3067
|
+
return { imageField };
|
|
3068
|
+
}
|
|
3069
|
+
var TEXTURE_STYLE = {
|
|
3070
|
+
backgroundImage: "radial-gradient(currentColor 0.5px, transparent 0.5px)",
|
|
3071
|
+
backgroundSize: "4px 4px",
|
|
3072
|
+
opacity: 0.04
|
|
3073
|
+
};
|
|
3074
|
+
var PADDING2 = {
|
|
3075
|
+
none: "",
|
|
3076
|
+
sm: "py-8 lg:py-10",
|
|
3077
|
+
md: "py-16 lg:py-20",
|
|
3078
|
+
lg: "py-24 lg:py-28"
|
|
3079
|
+
};
|
|
3080
|
+
var WIDTH2 = {
|
|
3081
|
+
narrow: "max-w-3xl",
|
|
3082
|
+
default: "max-w-7xl",
|
|
3083
|
+
wide: "max-w-[90rem]",
|
|
3084
|
+
full: "max-w-none"
|
|
3085
|
+
};
|
|
3086
|
+
var INVERTED_VARS = {
|
|
3087
|
+
["--color-background"]: "var(--color-foreground)",
|
|
3088
|
+
["--color-foreground"]: "var(--color-background)",
|
|
3089
|
+
["--color-card"]: "color-mix(in srgb, var(--color-foreground) 92%, #fff)",
|
|
3090
|
+
["--color-muted"]: "color-mix(in srgb, var(--color-foreground) 85%, #fff)",
|
|
3091
|
+
["--color-muted-foreground"]: "color-mix(in srgb, var(--color-background) 60%, transparent)",
|
|
3092
|
+
["--color-border"]: "color-mix(in srgb, var(--color-background) 18%, transparent)",
|
|
3093
|
+
["--color-accent"]: "color-mix(in srgb, var(--color-foreground) 80%, #fff)"
|
|
3094
|
+
};
|
|
3095
|
+
function Section({
|
|
3096
|
+
children,
|
|
3097
|
+
tone = "default",
|
|
3098
|
+
padding = "md",
|
|
3099
|
+
maxWidth = "default",
|
|
3100
|
+
texture,
|
|
3101
|
+
className = ""
|
|
3102
|
+
}) {
|
|
3103
|
+
const inverted = tone === "ink";
|
|
3104
|
+
const toneClass = inverted ? "bg-background text-foreground" : tone === "muted" ? "border-y border-border bg-card/40" : "";
|
|
3105
|
+
return /* @__PURE__ */ jsxs("section", { className: cn("relative", toneClass), style: inverted ? INVERTED_VARS : void 0, children: [
|
|
3106
|
+
texture && /* @__PURE__ */ jsx("div", { className: "pointer-events-none absolute inset-0", style: TEXTURE_STYLE, "aria-hidden": true }),
|
|
3107
|
+
/* @__PURE__ */ jsx(
|
|
3108
|
+
"div",
|
|
3109
|
+
{
|
|
3110
|
+
className: cn(
|
|
3111
|
+
"relative mx-auto px-5 lg:px-8",
|
|
3112
|
+
WIDTH2[maxWidth],
|
|
3113
|
+
PADDING2[padding],
|
|
3114
|
+
className
|
|
3115
|
+
),
|
|
3116
|
+
children
|
|
3117
|
+
}
|
|
3118
|
+
)
|
|
3119
|
+
] });
|
|
3120
|
+
}
|
|
3121
|
+
var iconMap = {
|
|
3122
|
+
FileText,
|
|
3123
|
+
Folder,
|
|
3124
|
+
Users,
|
|
2243
3125
|
Briefcase,
|
|
2244
3126
|
Calendar,
|
|
2245
3127
|
Clock,
|
|
@@ -2277,10 +3159,12 @@ var ICON_OPTIONS = [
|
|
|
2277
3159
|
{ label: "None", value: "" },
|
|
2278
3160
|
...Object.keys(iconMap).map((name) => ({ label: name, value: name }))
|
|
2279
3161
|
];
|
|
2280
|
-
function createBlockKit(ai) {
|
|
3162
|
+
function createBlockKit(ai, image, eyebrowClassName) {
|
|
2281
3163
|
return {
|
|
3164
|
+
eyebrowClassName,
|
|
2282
3165
|
textField: (label, placeholder) => ai ? ai.aiText(label, placeholder) : { type: "text", label, placeholder },
|
|
2283
3166
|
textareaField: (label, placeholder, kind) => ai ? ai.aiTextarea(label, placeholder, kind) : { type: "textarea", label, placeholder },
|
|
3167
|
+
imageField: (label) => image ? image(label) : { type: "text", label },
|
|
2284
3168
|
boolRadio: (label) => ({
|
|
2285
3169
|
type: "radio",
|
|
2286
3170
|
label,
|
|
@@ -2310,7 +3194,7 @@ var toneField = {
|
|
|
2310
3194
|
]
|
|
2311
3195
|
};
|
|
2312
3196
|
function createWebsiteBlocks(kit) {
|
|
2313
|
-
const { textField, textareaField, boolRadio, iconField, resolveIcon, blockTestId } = kit;
|
|
3197
|
+
const { textField, textareaField, imageField, boolRadio, iconField, resolveIcon, blockTestId } = kit;
|
|
2314
3198
|
const ctaField = (label) => ({
|
|
2315
3199
|
type: "object",
|
|
2316
3200
|
label,
|
|
@@ -2409,6 +3293,7 @@ function createWebsiteBlocks(kit) {
|
|
|
2409
3293
|
fields: {
|
|
2410
3294
|
eyebrow: textField("Eyebrow"),
|
|
2411
3295
|
title: textField("Title"),
|
|
3296
|
+
emphasis: { type: "text", label: "Emphasised word" },
|
|
2412
3297
|
subtitle: textareaField("Subtitle", void 0, "lead"),
|
|
2413
3298
|
primaryCta: ctaField("Primary CTA"),
|
|
2414
3299
|
secondaryCta: ctaField("Secondary CTA"),
|
|
@@ -2421,29 +3306,51 @@ function createWebsiteBlocks(kit) {
|
|
|
2421
3306
|
]
|
|
2422
3307
|
},
|
|
2423
3308
|
tone: toneField,
|
|
2424
|
-
imageUrl:
|
|
3309
|
+
imageUrl: imageField("Side image"),
|
|
3310
|
+
backgroundImage: imageField("Background image"),
|
|
3311
|
+
overlay: boolRadio("Background overlay")
|
|
2425
3312
|
},
|
|
2426
3313
|
defaultProps: {
|
|
2427
3314
|
eyebrow: "Introducing",
|
|
2428
3315
|
title: "Build a better website, faster.",
|
|
3316
|
+
emphasis: "",
|
|
2429
3317
|
subtitle: "A flexible platform that helps your team ship polished, on-brand pages without waiting on engineering.",
|
|
2430
3318
|
primaryCta: { label: "Get started", href: "#" },
|
|
2431
3319
|
secondaryCta: { label: "Learn more", href: "#" },
|
|
2432
3320
|
align: "left",
|
|
2433
3321
|
tone: "default",
|
|
2434
|
-
imageUrl: ""
|
|
3322
|
+
imageUrl: "",
|
|
3323
|
+
backgroundImage: "",
|
|
3324
|
+
overlay: true
|
|
2435
3325
|
},
|
|
2436
|
-
render: ({
|
|
3326
|
+
render: ({
|
|
3327
|
+
id,
|
|
3328
|
+
eyebrow,
|
|
3329
|
+
title,
|
|
3330
|
+
emphasis,
|
|
3331
|
+
subtitle,
|
|
3332
|
+
primaryCta,
|
|
3333
|
+
secondaryCta,
|
|
3334
|
+
align,
|
|
3335
|
+
tone,
|
|
3336
|
+
imageUrl,
|
|
3337
|
+
backgroundImage,
|
|
3338
|
+
overlay
|
|
3339
|
+
}) => /* @__PURE__ */ jsx(
|
|
2437
3340
|
Hero,
|
|
2438
3341
|
{
|
|
2439
3342
|
eyebrow,
|
|
2440
3343
|
title,
|
|
3344
|
+
emphasis,
|
|
2441
3345
|
subtitle,
|
|
2442
3346
|
primaryCta,
|
|
2443
3347
|
secondaryCta,
|
|
2444
3348
|
align,
|
|
2445
3349
|
tone,
|
|
2446
3350
|
imageUrl,
|
|
3351
|
+
backgroundImage,
|
|
3352
|
+
overlay,
|
|
3353
|
+
eyebrowClassName: kit.eyebrowClassName,
|
|
2447
3354
|
testId: blockTestId(id)
|
|
2448
3355
|
}
|
|
2449
3356
|
)
|
|
@@ -2560,15 +3467,30 @@ function createWebsiteBlocks(kit) {
|
|
|
2560
3467
|
title: textField("Title"),
|
|
2561
3468
|
subtitle: textareaField("Subtitle"),
|
|
2562
3469
|
cta: ctaField("Button"),
|
|
2563
|
-
tone: toneField
|
|
3470
|
+
tone: toneField,
|
|
3471
|
+
backgroundImage: imageField("Background image"),
|
|
3472
|
+
overlay: boolRadio("Background overlay")
|
|
2564
3473
|
},
|
|
2565
3474
|
defaultProps: {
|
|
2566
3475
|
title: "Ready to get started?",
|
|
2567
3476
|
subtitle: "Join thousands of teams already building with us.",
|
|
2568
3477
|
cta: { label: "Start free trial", href: "#" },
|
|
2569
|
-
tone: "default"
|
|
3478
|
+
tone: "default",
|
|
3479
|
+
backgroundImage: "",
|
|
3480
|
+
overlay: true
|
|
2570
3481
|
},
|
|
2571
|
-
render: ({ id, title, subtitle, cta, tone }) => /* @__PURE__ */ jsx(
|
|
3482
|
+
render: ({ id, title, subtitle, cta, tone, backgroundImage, overlay }) => /* @__PURE__ */ jsx(
|
|
3483
|
+
CTABanner,
|
|
3484
|
+
{
|
|
3485
|
+
title,
|
|
3486
|
+
subtitle,
|
|
3487
|
+
cta,
|
|
3488
|
+
tone,
|
|
3489
|
+
backgroundImage,
|
|
3490
|
+
overlay,
|
|
3491
|
+
testId: blockTestId(id)
|
|
3492
|
+
}
|
|
3493
|
+
)
|
|
2572
3494
|
};
|
|
2573
3495
|
const LogoCloudBlock = {
|
|
2574
3496
|
label: "Logo cloud",
|
|
@@ -2660,7 +3582,7 @@ function createWebsiteBlocks(kit) {
|
|
|
2660
3582
|
const ImageBlock = {
|
|
2661
3583
|
label: "Image",
|
|
2662
3584
|
fields: {
|
|
2663
|
-
src:
|
|
3585
|
+
src: imageField("Image"),
|
|
2664
3586
|
alt: textField("Alt text"),
|
|
2665
3587
|
aspect: {
|
|
2666
3588
|
type: "radio",
|
|
@@ -2681,10 +3603,21 @@ function createWebsiteBlocks(kit) {
|
|
|
2681
3603
|
{ label: "L", value: "lg" },
|
|
2682
3604
|
{ label: "Full", value: "full" }
|
|
2683
3605
|
]
|
|
2684
|
-
}
|
|
3606
|
+
},
|
|
3607
|
+
framed: boolRadio("Framed")
|
|
2685
3608
|
},
|
|
2686
|
-
defaultProps: { src: "", alt: "Image", aspect: "16:9", rounded: "lg" },
|
|
2687
|
-
render: ({ id, src, alt, aspect, rounded }) => /* @__PURE__ */ jsx(
|
|
3609
|
+
defaultProps: { src: "", alt: "Image", aspect: "16:9", rounded: "lg", framed: false },
|
|
3610
|
+
render: ({ id, src, alt, aspect, rounded, framed }) => /* @__PURE__ */ jsx(
|
|
3611
|
+
BlockImage,
|
|
3612
|
+
{
|
|
3613
|
+
src,
|
|
3614
|
+
alt,
|
|
3615
|
+
aspect,
|
|
3616
|
+
rounded,
|
|
3617
|
+
framed,
|
|
3618
|
+
testId: blockTestId(id)
|
|
3619
|
+
}
|
|
3620
|
+
)
|
|
2688
3621
|
};
|
|
2689
3622
|
const AvatarBlockConfig = {
|
|
2690
3623
|
label: "Avatar",
|
|
@@ -2830,8 +3763,440 @@ function createWebsiteBlocks(kit) {
|
|
|
2830
3763
|
List: ListBlockConfig
|
|
2831
3764
|
};
|
|
2832
3765
|
}
|
|
3766
|
+
var lines = (s) => (s ?? "").split("\n").map((l) => l.trim()).filter(Boolean);
|
|
3767
|
+
function createSiteBlocks(kit) {
|
|
3768
|
+
const { textField, textareaField, imageField, boolRadio, blockTestId } = kit;
|
|
3769
|
+
const ctaField = (label) => ({
|
|
3770
|
+
type: "object",
|
|
3771
|
+
label,
|
|
3772
|
+
objectFields: {
|
|
3773
|
+
label: textField("Label"),
|
|
3774
|
+
href: { type: "text", label: "Link" }
|
|
3775
|
+
}
|
|
3776
|
+
});
|
|
3777
|
+
const columnsField = (values) => ({
|
|
3778
|
+
type: "radio",
|
|
3779
|
+
label: "Columns",
|
|
3780
|
+
options: values.map((v) => ({ label: v, value: v }))
|
|
3781
|
+
});
|
|
3782
|
+
const PricingBlock = {
|
|
3783
|
+
label: "Pricing",
|
|
3784
|
+
fields: {
|
|
3785
|
+
plans: {
|
|
3786
|
+
type: "array",
|
|
3787
|
+
label: "Plans",
|
|
3788
|
+
getItemSummary: (item) => item?.name || "Plan",
|
|
3789
|
+
defaultItemProps: {
|
|
3790
|
+
name: "Plan",
|
|
3791
|
+
price: "\xA30",
|
|
3792
|
+
period: "mo",
|
|
3793
|
+
description: "",
|
|
3794
|
+
features: "Feature one\nFeature two",
|
|
3795
|
+
ctaLabel: "Choose",
|
|
3796
|
+
ctaHref: "#",
|
|
3797
|
+
featured: false
|
|
3798
|
+
},
|
|
3799
|
+
arrayFields: {
|
|
3800
|
+
name: textField("Name"),
|
|
3801
|
+
price: { type: "text", label: "Price" },
|
|
3802
|
+
period: { type: "text", label: "Period" },
|
|
3803
|
+
description: textareaField("Description"),
|
|
3804
|
+
features: { type: "textarea", label: "Features (one per line)" },
|
|
3805
|
+
ctaLabel: { type: "text", label: "Button label" },
|
|
3806
|
+
ctaHref: { type: "text", label: "Button link" },
|
|
3807
|
+
featured: boolRadio("Featured")
|
|
3808
|
+
}
|
|
3809
|
+
}
|
|
3810
|
+
},
|
|
3811
|
+
defaultProps: {
|
|
3812
|
+
plans: [
|
|
3813
|
+
{
|
|
3814
|
+
name: "Starter",
|
|
3815
|
+
price: "\xA30",
|
|
3816
|
+
period: "mo",
|
|
3817
|
+
description: "For getting started.",
|
|
3818
|
+
features: "Up to 3 projects\nCommunity support",
|
|
3819
|
+
ctaLabel: "Get started",
|
|
3820
|
+
ctaHref: "#",
|
|
3821
|
+
featured: false
|
|
3822
|
+
},
|
|
3823
|
+
{
|
|
3824
|
+
name: "Pro",
|
|
3825
|
+
price: "\xA329",
|
|
3826
|
+
period: "mo",
|
|
3827
|
+
description: "For growing teams.",
|
|
3828
|
+
features: "Unlimited projects\nPriority support\nAdvanced analytics",
|
|
3829
|
+
ctaLabel: "Start free trial",
|
|
3830
|
+
ctaHref: "#",
|
|
3831
|
+
featured: true
|
|
3832
|
+
},
|
|
3833
|
+
{
|
|
3834
|
+
name: "Enterprise",
|
|
3835
|
+
price: "Custom",
|
|
3836
|
+
period: "",
|
|
3837
|
+
description: "For large organisations.",
|
|
3838
|
+
features: "SSO & SAML\nDedicated success manager\nSLA",
|
|
3839
|
+
ctaLabel: "Contact sales",
|
|
3840
|
+
ctaHref: "#",
|
|
3841
|
+
featured: false
|
|
3842
|
+
}
|
|
3843
|
+
]
|
|
3844
|
+
},
|
|
3845
|
+
render: ({ id, plans }) => /* @__PURE__ */ jsx(
|
|
3846
|
+
PricingTable,
|
|
3847
|
+
{
|
|
3848
|
+
plans: (plans ?? []).map(
|
|
3849
|
+
(p) => ({
|
|
3850
|
+
name: p.name,
|
|
3851
|
+
price: p.price,
|
|
3852
|
+
period: p.period,
|
|
3853
|
+
description: p.description,
|
|
3854
|
+
features: lines(p.features),
|
|
3855
|
+
cta: { label: p.ctaLabel, href: p.ctaHref },
|
|
3856
|
+
featured: p.featured
|
|
3857
|
+
})
|
|
3858
|
+
),
|
|
3859
|
+
testId: blockTestId(id)
|
|
3860
|
+
}
|
|
3861
|
+
)
|
|
3862
|
+
};
|
|
3863
|
+
const TabsBlock = {
|
|
3864
|
+
label: "Tabs",
|
|
3865
|
+
fields: {
|
|
3866
|
+
items: {
|
|
3867
|
+
type: "array",
|
|
3868
|
+
label: "Tabs",
|
|
3869
|
+
getItemSummary: (item) => item?.label || "Tab",
|
|
3870
|
+
defaultItemProps: { label: "Tab", content: "Tab content." },
|
|
3871
|
+
arrayFields: {
|
|
3872
|
+
label: textField("Label"),
|
|
3873
|
+
content: textareaField("Content")
|
|
3874
|
+
}
|
|
3875
|
+
}
|
|
3876
|
+
},
|
|
3877
|
+
defaultProps: {
|
|
3878
|
+
items: [
|
|
3879
|
+
{ label: "Overview", content: "A quick overview of this section." },
|
|
3880
|
+
{ label: "Details", content: "The finer details go here." },
|
|
3881
|
+
{ label: "FAQ", content: "Answers to common questions." }
|
|
3882
|
+
]
|
|
3883
|
+
},
|
|
3884
|
+
render: ({ id, items }) => /* @__PURE__ */ jsx(Tabs, { items, testId: blockTestId(id) })
|
|
3885
|
+
};
|
|
3886
|
+
const AccordionBlock = {
|
|
3887
|
+
label: "Accordion",
|
|
3888
|
+
fields: {
|
|
3889
|
+
allowMultiple: boolRadio("Allow multiple open"),
|
|
3890
|
+
items: {
|
|
3891
|
+
type: "array",
|
|
3892
|
+
label: "Items",
|
|
3893
|
+
getItemSummary: (item) => item?.title || "Item",
|
|
3894
|
+
defaultItemProps: { title: "Section", content: "Section content." },
|
|
3895
|
+
arrayFields: {
|
|
3896
|
+
title: textField("Title"),
|
|
3897
|
+
content: textareaField("Content")
|
|
3898
|
+
}
|
|
3899
|
+
}
|
|
3900
|
+
},
|
|
3901
|
+
defaultProps: {
|
|
3902
|
+
allowMultiple: false,
|
|
3903
|
+
items: [
|
|
3904
|
+
{ title: "What is included?", content: "Everything you need to get started." },
|
|
3905
|
+
{ title: "How does billing work?", content: "Monthly or annually, cancel anytime." }
|
|
3906
|
+
]
|
|
3907
|
+
},
|
|
3908
|
+
render: ({ id, allowMultiple, items }) => /* @__PURE__ */ jsx(Accordion, { allowMultiple, items, testId: blockTestId(id) })
|
|
3909
|
+
};
|
|
3910
|
+
const GalleryBlock = {
|
|
3911
|
+
label: "Gallery",
|
|
3912
|
+
fields: {
|
|
3913
|
+
columns: columnsField(["2", "3", "4"]),
|
|
3914
|
+
aspect: {
|
|
3915
|
+
type: "radio",
|
|
3916
|
+
label: "Aspect",
|
|
3917
|
+
options: [
|
|
3918
|
+
{ label: "1:1", value: "1:1" },
|
|
3919
|
+
{ label: "4:3", value: "4:3" },
|
|
3920
|
+
{ label: "16:9", value: "16:9" }
|
|
3921
|
+
]
|
|
3922
|
+
},
|
|
3923
|
+
images: {
|
|
3924
|
+
type: "array",
|
|
3925
|
+
label: "Images",
|
|
3926
|
+
getItemSummary: (item) => item?.alt || "Image",
|
|
3927
|
+
defaultItemProps: { src: "", alt: "Image", caption: "" },
|
|
3928
|
+
arrayFields: {
|
|
3929
|
+
src: imageField("Image"),
|
|
3930
|
+
alt: { type: "text", label: "Alt text" },
|
|
3931
|
+
caption: { type: "text", label: "Caption" }
|
|
3932
|
+
}
|
|
3933
|
+
}
|
|
3934
|
+
},
|
|
3935
|
+
defaultProps: {
|
|
3936
|
+
columns: "3",
|
|
3937
|
+
aspect: "1:1",
|
|
3938
|
+
images: [
|
|
3939
|
+
{ src: "", alt: "Image one", caption: "" },
|
|
3940
|
+
{ src: "", alt: "Image two", caption: "" },
|
|
3941
|
+
{ src: "", alt: "Image three", caption: "" }
|
|
3942
|
+
]
|
|
3943
|
+
},
|
|
3944
|
+
render: ({ id, columns, aspect, images }) => /* @__PURE__ */ jsx(Gallery, { columns, aspect, images, testId: blockTestId(id) })
|
|
3945
|
+
};
|
|
3946
|
+
const VideoBlock = {
|
|
3947
|
+
label: "Video",
|
|
3948
|
+
fields: {
|
|
3949
|
+
url: { type: "text", label: "Video URL (YouTube/Vimeo/Loom or file)" },
|
|
3950
|
+
title: { type: "text", label: "Title" },
|
|
3951
|
+
aspect: {
|
|
3952
|
+
type: "radio",
|
|
3953
|
+
label: "Aspect",
|
|
3954
|
+
options: [
|
|
3955
|
+
{ label: "16:9", value: "16:9" },
|
|
3956
|
+
{ label: "4:3", value: "4:3" },
|
|
3957
|
+
{ label: "1:1", value: "1:1" }
|
|
3958
|
+
]
|
|
3959
|
+
},
|
|
3960
|
+
rounded: boolRadio("Rounded")
|
|
3961
|
+
},
|
|
3962
|
+
defaultProps: { url: "", title: "", aspect: "16:9", rounded: true },
|
|
3963
|
+
render: ({ id, url, title, aspect, rounded }) => /* @__PURE__ */ jsx(
|
|
3964
|
+
VideoEmbed,
|
|
3965
|
+
{
|
|
3966
|
+
url,
|
|
3967
|
+
title,
|
|
3968
|
+
aspect,
|
|
3969
|
+
rounded,
|
|
3970
|
+
testId: blockTestId(id)
|
|
3971
|
+
}
|
|
3972
|
+
)
|
|
3973
|
+
};
|
|
3974
|
+
const ContactFormBlock = {
|
|
3975
|
+
label: "Contact form",
|
|
3976
|
+
fields: {
|
|
3977
|
+
submitLabel: { type: "text", label: "Submit label" },
|
|
3978
|
+
action: { type: "text", label: "POST endpoint (optional)" },
|
|
3979
|
+
successMessage: textareaField("Success message"),
|
|
3980
|
+
fields: {
|
|
3981
|
+
type: "array",
|
|
3982
|
+
label: "Fields",
|
|
3983
|
+
getItemSummary: (item) => item?.label || "Field",
|
|
3984
|
+
defaultItemProps: { name: "field", label: "Field", type: "text", required: false },
|
|
3985
|
+
arrayFields: {
|
|
3986
|
+
name: { type: "text", label: "Name attribute" },
|
|
3987
|
+
label: textField("Label"),
|
|
3988
|
+
type: {
|
|
3989
|
+
type: "select",
|
|
3990
|
+
label: "Type",
|
|
3991
|
+
options: [
|
|
3992
|
+
{ label: "Text", value: "text" },
|
|
3993
|
+
{ label: "Email", value: "email" },
|
|
3994
|
+
{ label: "Phone", value: "tel" },
|
|
3995
|
+
{ label: "Multi-line", value: "textarea" }
|
|
3996
|
+
]
|
|
3997
|
+
},
|
|
3998
|
+
required: boolRadio("Required")
|
|
3999
|
+
}
|
|
4000
|
+
}
|
|
4001
|
+
},
|
|
4002
|
+
defaultProps: {
|
|
4003
|
+
submitLabel: "Send",
|
|
4004
|
+
action: "",
|
|
4005
|
+
successMessage: "Thanks \u2014 we\u2019ll be in touch shortly.",
|
|
4006
|
+
fields: [
|
|
4007
|
+
{ name: "name", label: "Name", type: "text", required: true },
|
|
4008
|
+
{ name: "email", label: "Email", type: "email", required: true },
|
|
4009
|
+
{ name: "message", label: "Message", type: "textarea", required: true }
|
|
4010
|
+
]
|
|
4011
|
+
},
|
|
4012
|
+
render: ({ id, submitLabel, action, successMessage, fields }) => /* @__PURE__ */ jsx(
|
|
4013
|
+
ContactForm,
|
|
4014
|
+
{
|
|
4015
|
+
submitLabel,
|
|
4016
|
+
action: action || void 0,
|
|
4017
|
+
successMessage,
|
|
4018
|
+
fields,
|
|
4019
|
+
testId: blockTestId(id)
|
|
4020
|
+
}
|
|
4021
|
+
)
|
|
4022
|
+
};
|
|
4023
|
+
const BannerBlock = {
|
|
4024
|
+
label: "Banner",
|
|
4025
|
+
fields: {
|
|
4026
|
+
text: textField("Text"),
|
|
4027
|
+
cta: ctaField("CTA"),
|
|
4028
|
+
tone: {
|
|
4029
|
+
type: "radio",
|
|
4030
|
+
label: "Tone",
|
|
4031
|
+
options: [
|
|
4032
|
+
{ label: "Primary", value: "primary" },
|
|
4033
|
+
{ label: "Muted", value: "muted" },
|
|
4034
|
+
{ label: "Dark", value: "ink" }
|
|
4035
|
+
]
|
|
4036
|
+
}
|
|
4037
|
+
},
|
|
4038
|
+
defaultProps: {
|
|
4039
|
+
text: "We just shipped something new.",
|
|
4040
|
+
cta: { label: "Read more", href: "#" },
|
|
4041
|
+
tone: "primary"
|
|
4042
|
+
},
|
|
4043
|
+
render: ({ id, text, cta, tone }) => /* @__PURE__ */ jsx(Banner, { text, cta, tone, testId: blockTestId(id) })
|
|
4044
|
+
};
|
|
4045
|
+
const MediaTextBlock = {
|
|
4046
|
+
label: "Media & text",
|
|
4047
|
+
fields: {
|
|
4048
|
+
eyebrow: textField("Eyebrow"),
|
|
4049
|
+
title: textField("Title"),
|
|
4050
|
+
body: textareaField("Body", void 0, "lead"),
|
|
4051
|
+
imageUrl: imageField("Image"),
|
|
4052
|
+
mediaSide: {
|
|
4053
|
+
type: "radio",
|
|
4054
|
+
label: "Media side",
|
|
4055
|
+
options: [
|
|
4056
|
+
{ label: "Left", value: "left" },
|
|
4057
|
+
{ label: "Right", value: "right" }
|
|
4058
|
+
]
|
|
4059
|
+
},
|
|
4060
|
+
cta: ctaField("CTA")
|
|
4061
|
+
},
|
|
4062
|
+
defaultProps: {
|
|
4063
|
+
eyebrow: "",
|
|
4064
|
+
title: "A closer look at what we do.",
|
|
4065
|
+
body: "Pair a compelling visual with the copy that explains it \u2014 the classic split section, on any theme.",
|
|
4066
|
+
imageUrl: "",
|
|
4067
|
+
mediaSide: "left",
|
|
4068
|
+
cta: { label: "Learn more", href: "#" }
|
|
4069
|
+
},
|
|
4070
|
+
render: ({ id, eyebrow, title, body, imageUrl, mediaSide, cta }) => /* @__PURE__ */ jsx(
|
|
4071
|
+
MediaText,
|
|
4072
|
+
{
|
|
4073
|
+
eyebrow,
|
|
4074
|
+
title,
|
|
4075
|
+
body,
|
|
4076
|
+
imageUrl,
|
|
4077
|
+
mediaSide,
|
|
4078
|
+
cta,
|
|
4079
|
+
testId: blockTestId(id)
|
|
4080
|
+
}
|
|
4081
|
+
)
|
|
4082
|
+
};
|
|
4083
|
+
const TeamGridBlock = {
|
|
4084
|
+
label: "Team",
|
|
4085
|
+
fields: {
|
|
4086
|
+
columns: columnsField(["2", "3", "4"]),
|
|
4087
|
+
members: {
|
|
4088
|
+
type: "array",
|
|
4089
|
+
label: "Members",
|
|
4090
|
+
getItemSummary: (item) => item?.name || "Member",
|
|
4091
|
+
defaultItemProps: { name: "New member", role: "Role", bio: "", avatarUrl: "" },
|
|
4092
|
+
arrayFields: {
|
|
4093
|
+
name: textField("Name"),
|
|
4094
|
+
role: { type: "text", label: "Role" },
|
|
4095
|
+
bio: textareaField("Bio"),
|
|
4096
|
+
avatarUrl: { type: "text", label: "Avatar URL" }
|
|
4097
|
+
}
|
|
4098
|
+
}
|
|
4099
|
+
},
|
|
4100
|
+
defaultProps: {
|
|
4101
|
+
columns: "3",
|
|
4102
|
+
members: [
|
|
4103
|
+
{ name: "Alex Morgan", role: "Founder & CEO", bio: "", avatarUrl: "" },
|
|
4104
|
+
{ name: "Sam Rivera", role: "Head of Product", bio: "", avatarUrl: "" },
|
|
4105
|
+
{ name: "Jordan Lee", role: "Lead Engineer", bio: "", avatarUrl: "" }
|
|
4106
|
+
]
|
|
4107
|
+
},
|
|
4108
|
+
render: ({ id, columns, members }) => /* @__PURE__ */ jsx(TeamGrid, { columns, members, testId: blockTestId(id) })
|
|
4109
|
+
};
|
|
4110
|
+
const StepsBlock = {
|
|
4111
|
+
label: "Steps",
|
|
4112
|
+
fields: {
|
|
4113
|
+
variant: {
|
|
4114
|
+
type: "radio",
|
|
4115
|
+
label: "Style",
|
|
4116
|
+
options: [
|
|
4117
|
+
{ label: "Numbered", value: "numbered" },
|
|
4118
|
+
{ label: "Timeline", value: "timeline" }
|
|
4119
|
+
]
|
|
4120
|
+
},
|
|
4121
|
+
steps: {
|
|
4122
|
+
type: "array",
|
|
4123
|
+
label: "Steps",
|
|
4124
|
+
getItemSummary: (item) => item?.title || "Step",
|
|
4125
|
+
defaultItemProps: { title: "Step", description: "Describe this step." },
|
|
4126
|
+
arrayFields: {
|
|
4127
|
+
title: textField("Title"),
|
|
4128
|
+
description: textareaField("Description")
|
|
4129
|
+
}
|
|
4130
|
+
}
|
|
4131
|
+
},
|
|
4132
|
+
defaultProps: {
|
|
4133
|
+
variant: "numbered",
|
|
4134
|
+
steps: [
|
|
4135
|
+
{ title: "Sign up", description: "Create your account in seconds." },
|
|
4136
|
+
{ title: "Configure", description: "Set things up to match your workflow." },
|
|
4137
|
+
{ title: "Launch", description: "Go live and start seeing results." }
|
|
4138
|
+
]
|
|
4139
|
+
},
|
|
4140
|
+
render: ({ id, variant, steps }) => /* @__PURE__ */ jsx(Steps, { variant, steps, testId: blockTestId(id) })
|
|
4141
|
+
};
|
|
4142
|
+
const TestimonialGridBlock = {
|
|
4143
|
+
label: "Testimonial grid",
|
|
4144
|
+
fields: {
|
|
4145
|
+
columns: columnsField(["2", "3"]),
|
|
4146
|
+
items: {
|
|
4147
|
+
type: "array",
|
|
4148
|
+
label: "Testimonials",
|
|
4149
|
+
getItemSummary: (item) => item?.author || "Testimonial",
|
|
4150
|
+
defaultItemProps: { quote: "A great quote.", author: "Author", role: "", avatarUrl: "" },
|
|
4151
|
+
arrayFields: {
|
|
4152
|
+
quote: textareaField("Quote"),
|
|
4153
|
+
author: textField("Author"),
|
|
4154
|
+
role: { type: "text", label: "Role" },
|
|
4155
|
+
avatarUrl: { type: "text", label: "Avatar URL" }
|
|
4156
|
+
}
|
|
4157
|
+
}
|
|
4158
|
+
},
|
|
4159
|
+
defaultProps: {
|
|
4160
|
+
columns: "3",
|
|
4161
|
+
items: [
|
|
4162
|
+
{
|
|
4163
|
+
quote: "This changed how our whole team works.",
|
|
4164
|
+
author: "Alex Morgan",
|
|
4165
|
+
role: "CTO, Northwind",
|
|
4166
|
+
avatarUrl: ""
|
|
4167
|
+
},
|
|
4168
|
+
{
|
|
4169
|
+
quote: "The best tool we adopted this year.",
|
|
4170
|
+
author: "Sam Rivera",
|
|
4171
|
+
role: "Ops Lead, Acme",
|
|
4172
|
+
avatarUrl: ""
|
|
4173
|
+
},
|
|
4174
|
+
{
|
|
4175
|
+
quote: "Simple, fast, and it just works.",
|
|
4176
|
+
author: "Jordan Lee",
|
|
4177
|
+
role: "Founder, Globex",
|
|
4178
|
+
avatarUrl: ""
|
|
4179
|
+
}
|
|
4180
|
+
]
|
|
4181
|
+
},
|
|
4182
|
+
render: ({ id, columns, items }) => /* @__PURE__ */ jsx(TestimonialGrid, { columns, items, testId: blockTestId(id) })
|
|
4183
|
+
};
|
|
4184
|
+
return {
|
|
4185
|
+
Pricing: PricingBlock,
|
|
4186
|
+
Tabs: TabsBlock,
|
|
4187
|
+
Accordion: AccordionBlock,
|
|
4188
|
+
Gallery: GalleryBlock,
|
|
4189
|
+
Video: VideoBlock,
|
|
4190
|
+
ContactForm: ContactFormBlock,
|
|
4191
|
+
Banner: BannerBlock,
|
|
4192
|
+
MediaText: MediaTextBlock,
|
|
4193
|
+
Team: TeamGridBlock,
|
|
4194
|
+
Steps: StepsBlock,
|
|
4195
|
+
TestimonialGrid: TestimonialGridBlock
|
|
4196
|
+
};
|
|
4197
|
+
}
|
|
2833
4198
|
function createArbiBlocksConfig(opts = {}) {
|
|
2834
|
-
const kit = createBlockKit(opts.aiFields);
|
|
4199
|
+
const kit = createBlockKit(opts.aiFields, opts.imageField, opts.eyebrowClassName);
|
|
2835
4200
|
const { textField, textareaField, boolRadio, iconField, resolveIcon, blockTestId } = kit;
|
|
2836
4201
|
const SectionHeadingBlock = {
|
|
2837
4202
|
label: "Section heading",
|
|
@@ -2850,7 +4215,17 @@ function createArbiBlocksConfig(opts = {}) {
|
|
|
2850
4215
|
invert: boolRadio("Invert (on dark)")
|
|
2851
4216
|
},
|
|
2852
4217
|
defaultProps: { title: "Section title", align: "left", invert: false },
|
|
2853
|
-
render: ({ id, title, eyebrow, lead, align, invert }) => /* @__PURE__ */ jsx("div", { "data-testid": blockTestId(id), children: /* @__PURE__ */ jsx(
|
|
4218
|
+
render: ({ id, title, eyebrow, lead, align, invert }) => /* @__PURE__ */ jsx("div", { "data-testid": blockTestId(id), children: /* @__PURE__ */ jsx(
|
|
4219
|
+
SectionHeading,
|
|
4220
|
+
{
|
|
4221
|
+
title,
|
|
4222
|
+
eyebrow,
|
|
4223
|
+
lead,
|
|
4224
|
+
align,
|
|
4225
|
+
invert,
|
|
4226
|
+
eyebrowClassName: kit.eyebrowClassName
|
|
4227
|
+
}
|
|
4228
|
+
) })
|
|
2854
4229
|
};
|
|
2855
4230
|
const PageHeaderBlock = {
|
|
2856
4231
|
label: "Page header",
|
|
@@ -2900,9 +4275,10 @@ function createArbiBlocksConfig(opts = {}) {
|
|
|
2900
4275
|
type: "radio",
|
|
2901
4276
|
label: "Accent",
|
|
2902
4277
|
options: [
|
|
2903
|
-
{ label: "
|
|
2904
|
-
{ label: "
|
|
2905
|
-
{ label: "
|
|
4278
|
+
{ label: "Default", value: "default" },
|
|
4279
|
+
{ label: "Primary", value: "primary" },
|
|
4280
|
+
{ label: "Success", value: "success" },
|
|
4281
|
+
{ label: "Muted", value: "muted" }
|
|
2906
4282
|
]
|
|
2907
4283
|
},
|
|
2908
4284
|
delta: {
|
|
@@ -2923,7 +4299,7 @@ function createArbiBlocksConfig(opts = {}) {
|
|
|
2923
4299
|
},
|
|
2924
4300
|
icon: iconField()
|
|
2925
4301
|
},
|
|
2926
|
-
defaultProps: { label: "Metric", value: "0", accent: "
|
|
4302
|
+
defaultProps: { label: "Metric", value: "0", accent: "default" },
|
|
2927
4303
|
render: ({ id, label, value, hint, accent, delta, icon }) => /* @__PURE__ */ jsx(
|
|
2928
4304
|
MetricCard,
|
|
2929
4305
|
{
|
|
@@ -3026,12 +4402,39 @@ function createArbiBlocksConfig(opts = {}) {
|
|
|
3026
4402
|
options: [
|
|
3027
4403
|
{ label: "Default", value: "default" },
|
|
3028
4404
|
{ label: "Muted", value: "muted" },
|
|
3029
|
-
{ label: "
|
|
4405
|
+
{ label: "Dark", value: "ink" }
|
|
3030
4406
|
]
|
|
3031
|
-
}
|
|
4407
|
+
},
|
|
4408
|
+
padding: {
|
|
4409
|
+
type: "radio",
|
|
4410
|
+
label: "Padding",
|
|
4411
|
+
options: [
|
|
4412
|
+
{ label: "None", value: "none" },
|
|
4413
|
+
{ label: "S", value: "sm" },
|
|
4414
|
+
{ label: "M", value: "md" },
|
|
4415
|
+
{ label: "L", value: "lg" }
|
|
4416
|
+
]
|
|
4417
|
+
},
|
|
4418
|
+
maxWidth: {
|
|
4419
|
+
type: "radio",
|
|
4420
|
+
label: "Width",
|
|
4421
|
+
options: [
|
|
4422
|
+
{ label: "Narrow", value: "narrow" },
|
|
4423
|
+
{ label: "Default", value: "default" },
|
|
4424
|
+
{ label: "Wide", value: "wide" },
|
|
4425
|
+
{ label: "Full", value: "full" }
|
|
4426
|
+
]
|
|
4427
|
+
},
|
|
4428
|
+
texture: boolRadio("Texture")
|
|
4429
|
+
},
|
|
4430
|
+
defaultProps: {
|
|
4431
|
+
tone: "default",
|
|
4432
|
+
padding: "md",
|
|
4433
|
+
maxWidth: "default",
|
|
4434
|
+
texture: false,
|
|
4435
|
+
children: []
|
|
3032
4436
|
},
|
|
3033
|
-
|
|
3034
|
-
render: ({ id, tone, children: Children }) => /* @__PURE__ */ jsx("div", { "data-testid": blockTestId(id), children: /* @__PURE__ */ jsx(Section, { tone, children: /* @__PURE__ */ jsx(Children, {}) }) })
|
|
4437
|
+
render: ({ id, tone, padding, maxWidth, texture, children: Children }) => /* @__PURE__ */ jsx("div", { "data-testid": blockTestId(id), children: /* @__PURE__ */ jsx(Section, { tone, padding, maxWidth, texture, children: /* @__PURE__ */ jsx(Children, {}) }) })
|
|
3035
4438
|
};
|
|
3036
4439
|
const CardBlock = {
|
|
3037
4440
|
label: "Card",
|
|
@@ -3107,6 +4510,7 @@ function createArbiBlocksConfig(opts = {}) {
|
|
|
3107
4510
|
render: ({ id }) => /* @__PURE__ */ jsx(Separator, { "data-testid": blockTestId(id) })
|
|
3108
4511
|
};
|
|
3109
4512
|
const website = createWebsiteBlocks(kit);
|
|
4513
|
+
const site = createSiteBlocks(kit);
|
|
3110
4514
|
const components = {
|
|
3111
4515
|
SectionHeading: SectionHeadingBlock,
|
|
3112
4516
|
PageHeader: PageHeaderBlock,
|
|
@@ -3118,7 +4522,8 @@ function createArbiBlocksConfig(opts = {}) {
|
|
|
3118
4522
|
Button: ButtonBlock,
|
|
3119
4523
|
Badge: BadgeBlock,
|
|
3120
4524
|
Separator: SeparatorBlock,
|
|
3121
|
-
...website
|
|
4525
|
+
...website,
|
|
4526
|
+
...site
|
|
3122
4527
|
};
|
|
3123
4528
|
const config = {
|
|
3124
4529
|
categories: {
|
|
@@ -3133,16 +4538,24 @@ function createArbiBlocksConfig(opts = {}) {
|
|
|
3133
4538
|
"FeatureGrid",
|
|
3134
4539
|
"StatGroup",
|
|
3135
4540
|
"Testimonial",
|
|
4541
|
+
"TestimonialGrid",
|
|
3136
4542
|
"CTABanner",
|
|
4543
|
+
"Banner",
|
|
3137
4544
|
"LogoCloud",
|
|
3138
|
-
"FAQ"
|
|
4545
|
+
"FAQ",
|
|
4546
|
+
"MediaText",
|
|
4547
|
+
"Steps",
|
|
4548
|
+
"Team"
|
|
3139
4549
|
]
|
|
3140
4550
|
},
|
|
3141
4551
|
Content: {
|
|
3142
4552
|
title: "Content",
|
|
3143
4553
|
components: ["SectionHeading", "PageHeader", "EmptyState", "RichText", "Callout", "List"]
|
|
3144
4554
|
},
|
|
3145
|
-
|
|
4555
|
+
Interactive: { title: "Interactive", components: ["Tabs", "Accordion"] },
|
|
4556
|
+
Media: { title: "Media", components: ["Image", "Avatar", "Gallery", "Video"] },
|
|
4557
|
+
Commerce: { title: "Commerce", components: ["Pricing"] },
|
|
4558
|
+
Forms: { title: "Forms", components: ["ContactForm"] },
|
|
3146
4559
|
Navigation: { title: "Navigation", components: ["Navbar", "Footer"] },
|
|
3147
4560
|
Data: { title: "Data", components: ["MetricCard", "DataTable"] },
|
|
3148
4561
|
Primitives: { title: "Primitives", components: ["Button", "Badge", "Separator"] }
|
|
@@ -3152,8 +4565,8 @@ function createArbiBlocksConfig(opts = {}) {
|
|
|
3152
4565
|
render: ({ children }) => /* @__PURE__ */ jsx(
|
|
3153
4566
|
"div",
|
|
3154
4567
|
{
|
|
3155
|
-
className: "bg-background text-foreground font-sans",
|
|
3156
|
-
"data-testid": "arbi-blocks-page-root",
|
|
4568
|
+
className: cn("bg-background text-foreground font-sans", opts.root?.className),
|
|
4569
|
+
"data-testid": opts.root?.testId ?? "arbi-blocks-page-root",
|
|
3157
4570
|
children
|
|
3158
4571
|
}
|
|
3159
4572
|
)
|
|
@@ -3393,7 +4806,7 @@ function ModuleManager({ store, testIdPrefix = "builder" }) {
|
|
|
3393
4806
|
] }),
|
|
3394
4807
|
/* @__PURE__ */ jsx(Icon, { className: "size-4 shrink-0 text-muted-foreground" }),
|
|
3395
4808
|
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
3396
|
-
/* @__PURE__ */ jsx("p", { className: "truncate text-sm font-medium text-
|
|
4809
|
+
/* @__PURE__ */ jsx("p", { className: "truncate text-sm font-medium text-foreground", children: m.label }),
|
|
3397
4810
|
/* @__PURE__ */ jsx("p", { className: "truncate font-mono text-[11px] text-muted-foreground", children: m.to })
|
|
3398
4811
|
] }),
|
|
3399
4812
|
m.arbi && /* @__PURE__ */ jsxs(Badge, { variant: "accent", className: "text-[10px]", children: [
|
|
@@ -3464,7 +4877,7 @@ function AgentsPanel({ store, testIdPrefix = "builder" }) {
|
|
|
3464
4877
|
"data-testid": `${testIdPrefix}-agents-configs`,
|
|
3465
4878
|
children: [
|
|
3466
4879
|
/* @__PURE__ */ jsx(CardHeader, { children: /* @__PURE__ */ jsxs(CardTitle, { className: "flex items-center gap-2 text-base", children: [
|
|
3467
|
-
/* @__PURE__ */ jsx(BrainCircuit, { className: "size-4 text-
|
|
4880
|
+
/* @__PURE__ */ jsx(BrainCircuit, { className: "size-4 text-primary" }),
|
|
3468
4881
|
" Behaviour configurations",
|
|
3469
4882
|
/* @__PURE__ */ jsx(Badge, { variant: "secondary", className: "ml-1", children: versions.length })
|
|
3470
4883
|
] }) }),
|
|
@@ -3486,7 +4899,7 @@ function AgentsPanel({ store, testIdPrefix = "builder" }) {
|
|
|
3486
4899
|
}
|
|
3487
4900
|
),
|
|
3488
4901
|
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
3489
|
-
/* @__PURE__ */ jsx("p", { className: "truncate text-sm font-medium text-
|
|
4902
|
+
/* @__PURE__ */ jsx("p", { className: "truncate text-sm font-medium text-foreground", children: v.title ?? "Untitled behaviour" }),
|
|
3490
4903
|
/* @__PURE__ */ jsx("p", { className: "truncate font-mono text-[11px] text-muted-foreground", children: v.external_id })
|
|
3491
4904
|
] }),
|
|
3492
4905
|
/* @__PURE__ */ jsx(ConfigModelBadge, { configId: v.external_id, testIdPrefix })
|
|
@@ -3500,7 +4913,7 @@ function AgentsPanel({ store, testIdPrefix = "builder" }) {
|
|
|
3500
4913
|
),
|
|
3501
4914
|
/* @__PURE__ */ jsxs(Card, { variant: "elevated", className: "rounded-xl", "data-testid": `${testIdPrefix}-agents-agents`, children: [
|
|
3502
4915
|
/* @__PURE__ */ jsx(CardHeader, { children: /* @__PURE__ */ jsxs(CardTitle, { className: "flex items-center gap-2 text-base", children: [
|
|
3503
|
-
/* @__PURE__ */ jsx(Bot, { className: "size-4 text-
|
|
4916
|
+
/* @__PURE__ */ jsx(Bot, { className: "size-4 text-primary" }),
|
|
3504
4917
|
" Agents",
|
|
3505
4918
|
/* @__PURE__ */ jsx(Badge, { variant: "secondary", className: "ml-1", children: agents.length })
|
|
3506
4919
|
] }) }),
|
|
@@ -3522,7 +4935,7 @@ function AgentsPanel({ store, testIdPrefix = "builder" }) {
|
|
|
3522
4935
|
}
|
|
3523
4936
|
),
|
|
3524
4937
|
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
3525
|
-
/* @__PURE__ */ jsx("p", { className: "truncate text-sm font-medium text-
|
|
4938
|
+
/* @__PURE__ */ jsx("p", { className: "truncate text-sm font-medium text-foreground", children: (a.given_name ?? "Agent").replace(/-/g, " ") }),
|
|
3526
4939
|
/* @__PURE__ */ jsx("p", { className: "truncate font-mono text-[11px] text-muted-foreground", children: a.external_id })
|
|
3527
4940
|
] })
|
|
3528
4941
|
]
|
|
@@ -3547,7 +4960,7 @@ function ExportPanel({
|
|
|
3547
4960
|
return /* @__PURE__ */ jsxs(Card, { variant: "elevated", className: "rounded-xl", "data-testid": `${testIdPrefix}-export-panel`, children: [
|
|
3548
4961
|
/* @__PURE__ */ jsx(CardHeader, { children: /* @__PURE__ */ jsxs(CardTitle, { className: "flex items-center justify-between text-base", children: [
|
|
3549
4962
|
/* @__PURE__ */ jsxs("span", { className: "flex items-center gap-2", children: [
|
|
3550
|
-
/* @__PURE__ */ jsx(FileJson, { className: "size-4 text-
|
|
4963
|
+
/* @__PURE__ */ jsx(FileJson, { className: "size-4 text-primary" }),
|
|
3551
4964
|
" Vertical config"
|
|
3552
4965
|
] }),
|
|
3553
4966
|
/* @__PURE__ */ jsxs(
|
|
@@ -3567,7 +4980,7 @@ function ExportPanel({
|
|
|
3567
4980
|
/* @__PURE__ */ jsx(CardContent, { children: /* @__PURE__ */ jsx(
|
|
3568
4981
|
"pre",
|
|
3569
4982
|
{
|
|
3570
|
-
className: "scrollbar-slim max-h-[520px] overflow-auto rounded-md bg-
|
|
4983
|
+
className: "scrollbar-slim max-h-[520px] overflow-auto rounded-md bg-foreground p-4 font-mono text-[11px] leading-relaxed text-background",
|
|
3571
4984
|
"data-testid": `${testIdPrefix}-export-preview`,
|
|
3572
4985
|
children: json
|
|
3573
4986
|
}
|
|
@@ -3585,7 +4998,7 @@ function VerticalBuilder({
|
|
|
3585
4998
|
}) {
|
|
3586
4999
|
return /* @__PURE__ */ jsxs("div", { className: "space-y-6", "data-testid": `page-${testIdPrefix}`, children: [
|
|
3587
5000
|
header,
|
|
3588
|
-
/* @__PURE__ */ jsxs(Tabs, { defaultValue: "theme", children: [
|
|
5001
|
+
/* @__PURE__ */ jsxs(Tabs$1, { defaultValue: "theme", children: [
|
|
3589
5002
|
/* @__PURE__ */ jsxs(TabsList, { "data-testid": `${testIdPrefix}-tabs`, children: [
|
|
3590
5003
|
/* @__PURE__ */ jsxs(TabsTrigger, { value: "theme", "data-testid": `${testIdPrefix}-tab-theme`, children: [
|
|
3591
5004
|
/* @__PURE__ */ jsx(Palette, { className: "size-4" }),
|
|
@@ -3629,6 +5042,6 @@ function VerticalBuilder({
|
|
|
3629
5042
|
] });
|
|
3630
5043
|
}
|
|
3631
5044
|
|
|
3632
|
-
export { AgentsPanel, AvatarBlock, BlockImage, CTABanner, Callout, Columns, ConnectionProvider, Container, DataTable, DataTableBlock, EmptyState, FAQ, FeatureGrid, Footer, GridView, Hero, LegalArbiProvider, ListBlock, LogoCloud, MetricCard, ModuleManager, Navbar, PageHeader, PageRenderer, RichTextBlock, Section, SectionHeading, Spacer, StatGroup, StudioEditor, THEME_STYLE_ID, Testimonial, ThemeEditor, Toolbar, VerticalBuilder, applyStoredTheme, applyThemeVars, clearThemeVars, createAgentSelectionStore, createAiFields, createArbiBlocksConfig, createBlockKit, createModuleRegistry, createModuleStore, createPromptLibrary, createStudioPersistence, createThemeStore, createVerticalExport, createWebsiteBlocks, daysUntil, fmtDate, fmtDateTime, fromNow, gbp, hrs, iconMap, initials, isAuthenticated, matterToContext, pct, tid, toHslTriplet, useConnection, useFirmAiTask, useSemanticSearch, useWorkspaceDocs };
|
|
5045
|
+
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 };
|
|
3633
5046
|
//# sourceMappingURL=index.js.map
|
|
3634
5047
|
//# sourceMappingURL=index.js.map
|