@ai-matrx/records-ui 0.51.0 → 0.52.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -9398,28 +9398,325 @@ function Invite({ card, onInvited }) {
9398
9398
  ] });
9399
9399
  }
9400
9400
 
9401
- // src/SubscriptionsPanel.tsx
9402
- import { useCallback as useCallback19, useEffect as useEffect22, useState as useState32 } from "react";
9401
+ // src/DigestScheduler.tsx
9402
+ import { useCallback as useCallback19, useEffect as useEffect22, useMemo as useMemo23, useState as useState32 } from "react";
9403
9403
  import { useRecordsClient as useRecordsClient26 } from "@ai-matrx/records/react";
9404
- import { Badge as Badge14, Button as Button29, Skeleton as Skeleton16, Switch as Switch2, cn as cn30 } from "@ai-matrx/design-system";
9405
- import { jsx as jsx36, jsxs as jsxs32 } from "react/jsx-runtime";
9404
+ import { BasicInput as BasicInput10, Button as Button29, Checkbox as Checkbox5, Label as Label5, Separator as Separator11, Skeleton as Skeleton16, cn as cn30 } from "@ai-matrx/design-system";
9405
+ import { Fragment as Fragment16, jsx as jsx36, jsxs as jsxs32 } from "react/jsx-runtime";
9406
+ var WEEKDAYS = ["monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday"];
9407
+ var CADENCE_WORDS = {
9408
+ instant: "the moment something arrives",
9409
+ hourly: "every hour",
9410
+ daily: "every day",
9411
+ weekly: "every week"
9412
+ };
9413
+ var CHANNEL_WORDS = {
9414
+ in_app: "in the app",
9415
+ email: "by email \u2014 only if an address is on their account",
9416
+ sms: "by text \u2014 only if a number is on their account"
9417
+ };
9418
+ function DigestScheduler({
9419
+ tableId,
9420
+ savedViewId,
9421
+ subjectName,
9422
+ filters,
9423
+ onScheduled,
9424
+ onClose,
9425
+ className
9426
+ }) {
9427
+ const client = useRecordsClient26();
9428
+ const host = useRecordsUi();
9429
+ const [views, setViews] = useState32(null);
9430
+ const [cadences, setCadences] = useState32([]);
9431
+ const [members, setMembers] = useState32(null);
9432
+ const [error, setError] = useState32(null);
9433
+ const [viewId, setViewId] = useState32(savedViewId ?? "");
9434
+ const [name, setName] = useState32(subjectName?.trim() ? `${subjectName.trim()} summary` : "");
9435
+ const [cadence, setCadence] = useState32("weekly");
9436
+ const [weekday, setWeekday] = useState32("monday");
9437
+ const [time, setTime] = useState32("08:00");
9438
+ const [channel, setChannel] = useState32("in_app");
9439
+ const [quiet, setQuiet] = useState32(false);
9440
+ const [quietFrom, setQuietFrom] = useState32("22:00");
9441
+ const [quietTo, setQuietTo] = useState32("07:00");
9442
+ const [recipients, setRecipients] = useState32([]);
9443
+ const [saving, setSaving] = useState32(false);
9444
+ const [outcomes, setOutcomes] = useState32(null);
9445
+ const [preview, setPreview] = useState32(null);
9446
+ const [previewing, setPreviewing] = useState32(false);
9447
+ const load = useCallback19(async () => {
9448
+ const [saved, offered] = await Promise.all([
9449
+ // The store's own door onto `platform.saved_view`, narrowed in SQL to
9450
+ // Tables this person can already open — never a grant on the table behind it.
9451
+ client.views({ table_id: tableId }),
9452
+ // The cadences are ASKED, never typed into this file, so a picker can
9453
+ // never offer a cadence the digest runner does not honour.
9454
+ client.subscriptionCadences()
9455
+ ]);
9456
+ if (!saved.ok) setError(saved.error);
9457
+ else setViews(saved.data);
9458
+ if (offered.ok) setCadences(offered.data.filter((c) => c !== "instant"));
9459
+ if (host.members) {
9460
+ const who = await host.members();
9461
+ setMembers(who.map((m) => ({ userId: m.userId, name: m.name ?? m.email ?? m.userId })));
9462
+ } else {
9463
+ setMembers([]);
9464
+ }
9465
+ }, [client, tableId, host]);
9466
+ useEffect22(() => {
9467
+ void load();
9468
+ }, [load]);
9469
+ const schedule = useMemo23(() => {
9470
+ if (cadence === "weekly") return `${weekday} ${time}`;
9471
+ if (cadence === "daily") return time;
9472
+ return null;
9473
+ }, [cadence, weekday, time]);
9474
+ const quietHours = useMemo23(
9475
+ () => quiet ? { start: quietFrom, end: quietTo } : null,
9476
+ [quiet, quietFrom, quietTo]
9477
+ );
9478
+ async function scheduleIt() {
9479
+ setSaving(true);
9480
+ setError(null);
9481
+ setOutcomes(null);
9482
+ let subject = viewId;
9483
+ if (subject === "" || subject === "new") {
9484
+ const declared = await client.viewDeclare({
9485
+ table_id: tableId,
9486
+ spec: {
9487
+ name: subjectName?.trim() ? subjectName.trim() : name.trim() || "Weekly summary",
9488
+ filters: filters ?? {}
9489
+ }
9490
+ });
9491
+ if (!declared.ok) {
9492
+ setSaving(false);
9493
+ setError(declared.error);
9494
+ return;
9495
+ }
9496
+ subject = declared.data;
9497
+ setViewId(declared.data);
9498
+ }
9499
+ const who = recipients.length > 0 ? recipients : [""];
9500
+ const done = [];
9501
+ for (const userId of who) {
9502
+ const label = members?.find((m) => m.userId === userId)?.name ?? (userId === "" ? "you" : userId);
9503
+ const written = await client.subscriptionDeclare({
9504
+ table_id: tableId,
9505
+ spec: {
9506
+ name: name.trim() === "" ? "Weekly summary" : name.trim(),
9507
+ saved_view_id: subject,
9508
+ cadence,
9509
+ schedule,
9510
+ channel,
9511
+ quiet_hours: quietHours,
9512
+ ...userId === "" ? {} : { recipient_user_id: userId }
9513
+ }
9514
+ });
9515
+ if (written.ok) done.push({ userId, who: label, ruleId: written.data });
9516
+ else done.push({ userId, who: label, refused: written.error });
9517
+ }
9518
+ setSaving(false);
9519
+ setOutcomes(done);
9520
+ if (done.some((d) => d.ruleId)) onScheduled?.();
9521
+ }
9522
+ async function showOne(ruleId) {
9523
+ setPreviewing(true);
9524
+ const answered = await client.subscriptionPreview({ rule_id: ruleId });
9525
+ setPreviewing(false);
9526
+ if (!answered.ok) {
9527
+ setError(answered.error);
9528
+ return;
9529
+ }
9530
+ setPreview(answered.data);
9531
+ }
9532
+ if (views === null && !error) return /* @__PURE__ */ jsx36(Skeleton16, { className: cn30("h-56 w-full", className) });
9533
+ return /* @__PURE__ */ jsxs32("section", { className: cn30("flex flex-col gap-3", className), children: [
9534
+ /* @__PURE__ */ jsxs32("header", { className: "flex items-center gap-2", children: [
9535
+ /* @__PURE__ */ jsx36("h3", { className: "text-sm font-medium", children: "Send this on a schedule" }),
9536
+ /* @__PURE__ */ jsx36("div", { className: "flex-1" }),
9537
+ /* @__PURE__ */ jsx36(Button29, { size: "sm", disabled: saving, onClick: () => void scheduleIt(), children: saving ? "Scheduling\u2026" : "Schedule it" }),
9538
+ onClose ? /* @__PURE__ */ jsx36(Button29, { size: "sm", variant: "ghost", onClick: onClose, children: "Close" }) : null
9539
+ ] }),
9540
+ error ? /* @__PURE__ */ jsx36(RefusalNotice, { error }) : null,
9541
+ /* @__PURE__ */ jsxs32("label", { className: "flex flex-col gap-1", children: [
9542
+ /* @__PURE__ */ jsx36(Label5, { className: "text-xs font-medium", children: "What to call it" }),
9543
+ /* @__PURE__ */ jsx36(
9544
+ BasicInput10,
9545
+ {
9546
+ value: name,
9547
+ placeholder: "Monday donor summary",
9548
+ onChange: (e) => setName(e.target.value)
9549
+ }
9550
+ )
9551
+ ] }),
9552
+ savedViewId ? null : /* @__PURE__ */ jsxs32("label", { className: "flex flex-col gap-1", children: [
9553
+ /* @__PURE__ */ jsx36(Label5, { className: "text-xs font-medium", children: "What it summarises" }),
9554
+ /* @__PURE__ */ jsxs32(
9555
+ "select",
9556
+ {
9557
+ className: "h-9 rounded border bg-background px-2 text-sm",
9558
+ value: viewId,
9559
+ onChange: (e) => setViewId(e.target.value),
9560
+ children: [
9561
+ /* @__PURE__ */ jsx36("option", { value: "new", children: subjectName?.trim() ? `What I am looking at now (${subjectName.trim()})` : "What I am looking at now" }),
9562
+ (views ?? []).map((v) => /* @__PURE__ */ jsx36("option", { value: v.view_id, children: v.name }, v.view_id))
9563
+ ]
9564
+ }
9565
+ ),
9566
+ /* @__PURE__ */ jsx36("span", { className: "text-xs text-muted-foreground", children: "The first choice saves what is on screen as a view, so the summary and the screen stay the same thing." })
9567
+ ] }),
9568
+ /* @__PURE__ */ jsxs32("div", { className: "grid grid-cols-2 gap-2", children: [
9569
+ /* @__PURE__ */ jsxs32("label", { className: "flex flex-col gap-1", children: [
9570
+ /* @__PURE__ */ jsx36(Label5, { className: "text-xs font-medium", children: "How often" }),
9571
+ /* @__PURE__ */ jsx36(
9572
+ "select",
9573
+ {
9574
+ className: "h-9 rounded border bg-background px-2 text-sm",
9575
+ value: cadence,
9576
+ onChange: (e) => setCadence(e.target.value),
9577
+ children: (cadences.length > 0 ? cadences : ["hourly", "daily", "weekly"]).map((c) => /* @__PURE__ */ jsx36("option", { value: c, children: CADENCE_WORDS[c] ?? c }, c))
9578
+ }
9579
+ )
9580
+ ] }),
9581
+ cadence === "weekly" ? /* @__PURE__ */ jsxs32("label", { className: "flex flex-col gap-1", children: [
9582
+ /* @__PURE__ */ jsx36(Label5, { className: "text-xs font-medium", children: "Which day" }),
9583
+ /* @__PURE__ */ jsx36(
9584
+ "select",
9585
+ {
9586
+ className: "h-9 rounded border bg-background px-2 text-sm",
9587
+ value: weekday,
9588
+ onChange: (e) => setWeekday(e.target.value),
9589
+ children: WEEKDAYS.map((d) => /* @__PURE__ */ jsx36("option", { value: d, children: d[0].toUpperCase() + d.slice(1) }, d))
9590
+ }
9591
+ )
9592
+ ] }) : null,
9593
+ cadence === "weekly" || cadence === "daily" ? /* @__PURE__ */ jsxs32("label", { className: "flex flex-col gap-1", children: [
9594
+ /* @__PURE__ */ jsx36(Label5, { className: "text-xs font-medium", children: "At" }),
9595
+ /* @__PURE__ */ jsx36(BasicInput10, { type: "time", value: time, onChange: (e) => setTime(e.target.value) })
9596
+ ] }) : null,
9597
+ /* @__PURE__ */ jsxs32("label", { className: "flex flex-col gap-1", children: [
9598
+ /* @__PURE__ */ jsx36(Label5, { className: "text-xs font-medium", children: "Where it arrives" }),
9599
+ /* @__PURE__ */ jsx36(
9600
+ "select",
9601
+ {
9602
+ className: "h-9 rounded border bg-background px-2 text-sm",
9603
+ value: channel,
9604
+ onChange: (e) => setChannel(e.target.value),
9605
+ children: Object.entries(CHANNEL_WORDS).map(([key, words]) => /* @__PURE__ */ jsx36("option", { value: key, children: words }, key))
9606
+ }
9607
+ )
9608
+ ] })
9609
+ ] }),
9610
+ /* @__PURE__ */ jsxs32("div", { className: "flex flex-col gap-1.5", children: [
9611
+ /* @__PURE__ */ jsx36(Label5, { className: "text-xs font-medium", children: "Who to send it to" }),
9612
+ members === null ? /* @__PURE__ */ jsx36(Skeleton16, { className: "h-8 w-full" }) : members.length === 0 ? /* @__PURE__ */ jsx36("p", { className: "text-xs text-muted-foreground", children: "Nobody else is offered here, so this will be addressed to you. Who the people are is the platform's answer and this screen reaches it through its host's `members` port, which is not bound here." }) : /* @__PURE__ */ jsxs32(Fragment16, { children: [
9613
+ /* @__PURE__ */ jsx36("div", { className: "flex flex-wrap gap-x-4 gap-y-1.5", children: members.map((m) => /* @__PURE__ */ jsxs32("label", { className: "flex items-center gap-1.5 text-xs", children: [
9614
+ /* @__PURE__ */ jsx36(
9615
+ Checkbox5,
9616
+ {
9617
+ checked: recipients.includes(m.userId),
9618
+ onCheckedChange: (on) => setRecipients(
9619
+ (prev) => on ? [...prev, m.userId] : prev.filter((u) => u !== m.userId)
9620
+ )
9621
+ }
9622
+ ),
9623
+ m.name
9624
+ ] }, m.userId)) }),
9625
+ /* @__PURE__ */ jsx36("p", { className: "text-xs text-muted-foreground", children: recipients.length === 0 ? "Nobody picked \u2014 it will be addressed to you." : `${recipients.length} ${recipients.length === 1 ? "person" : "people"}, and each one's summary holds only what that person may see.` })
9626
+ ] })
9627
+ ] }),
9628
+ /* @__PURE__ */ jsxs32("label", { className: "flex items-center gap-1.5 text-xs", children: [
9629
+ /* @__PURE__ */ jsx36(Checkbox5, { checked: quiet, onCheckedChange: (on) => setQuiet(Boolean(on)) }),
9630
+ "Not between certain hours"
9631
+ ] }),
9632
+ quiet ? /* @__PURE__ */ jsxs32("div", { className: "flex items-center gap-2", children: [
9633
+ /* @__PURE__ */ jsx36(
9634
+ BasicInput10,
9635
+ {
9636
+ type: "time",
9637
+ className: "h-8 w-28",
9638
+ value: quietFrom,
9639
+ onChange: (e) => setQuietFrom(e.target.value)
9640
+ }
9641
+ ),
9642
+ /* @__PURE__ */ jsx36("span", { className: "text-xs text-muted-foreground", children: "and" }),
9643
+ /* @__PURE__ */ jsx36(
9644
+ BasicInput10,
9645
+ {
9646
+ type: "time",
9647
+ className: "h-8 w-28",
9648
+ value: quietTo,
9649
+ onChange: (e) => setQuietTo(e.target.value)
9650
+ }
9651
+ ),
9652
+ /* @__PURE__ */ jsx36("span", { className: "text-xs text-muted-foreground", children: "A send inside these hours waits until they end \u2014 it is never dropped." })
9653
+ ] }) : null,
9654
+ outcomes ? /* @__PURE__ */ jsxs32(Fragment16, { children: [
9655
+ /* @__PURE__ */ jsx36(Separator11, {}),
9656
+ /* @__PURE__ */ jsx36("div", { className: "flex flex-col gap-1.5", children: outcomes.map((o) => /* @__PURE__ */ jsx36("div", { className: "text-xs", children: o.ruleId ? /* @__PURE__ */ jsxs32("span", { children: [
9657
+ o.who,
9658
+ " will get it ",
9659
+ CADENCE_WORDS[cadence] ?? cadence,
9660
+ schedule ? `, ${schedule}` : "",
9661
+ ".",
9662
+ " ",
9663
+ /* @__PURE__ */ jsx36(
9664
+ Button29,
9665
+ {
9666
+ size: "sm",
9667
+ variant: "ghost",
9668
+ disabled: previewing,
9669
+ onClick: () => void showOne(o.ruleId),
9670
+ children: "Send me one now"
9671
+ }
9672
+ )
9673
+ ] }) : o.refused ? /* @__PURE__ */ jsx36(RefusalNotice, { error: o.refused }) : null }, o.userId || "me")) })
9674
+ ] }) : null,
9675
+ preview ? /* @__PURE__ */ jsxs32("div", { className: "rounded-md border bg-muted/40 p-2.5 text-xs", children: [
9676
+ /* @__PURE__ */ jsx36("p", { className: "font-medium", children: preview.subject }),
9677
+ /* @__PURE__ */ jsx36("p", { className: "mt-1 text-muted-foreground", children: preview.body }),
9678
+ preview.incomplete ? /* @__PURE__ */ jsx36("p", { className: "mt-1 text-destructive", children: preview.incomplete }) : null,
9679
+ preview.entered.length > 0 ? /* @__PURE__ */ jsxs32("p", { className: "mt-1", children: [
9680
+ /* @__PURE__ */ jsx36("span", { className: "font-medium", children: "Arrived:" }),
9681
+ " ",
9682
+ preview.entered.map((e) => e.name).join(", ")
9683
+ ] }) : null,
9684
+ preview.changed.length > 0 ? /* @__PURE__ */ jsxs32("p", { className: "mt-1", children: [
9685
+ /* @__PURE__ */ jsx36("span", { className: "font-medium", children: "Changed:" }),
9686
+ " ",
9687
+ preview.changed.map((e) => e.name).join(", ")
9688
+ ] }) : null,
9689
+ /* @__PURE__ */ jsx36("p", { className: "mt-1.5 text-muted-foreground", children: "Nothing was sent and nothing was recorded \u2014 this is what the next one would say." }),
9690
+ /* @__PURE__ */ jsx36(Button29, { size: "sm", variant: "ghost", className: "mt-1", onClick: () => setPreview(null), children: "Close" })
9691
+ ] }) : null
9692
+ ] });
9693
+ }
9694
+
9695
+ // src/SubscriptionsPanel.tsx
9696
+ import { useCallback as useCallback20, useEffect as useEffect23, useState as useState33 } from "react";
9697
+ import { useRecordsClient as useRecordsClient27 } from "@ai-matrx/records/react";
9698
+ import { Badge as Badge14, Button as Button30, Skeleton as Skeleton17, Switch as Switch2, cn as cn31 } from "@ai-matrx/design-system";
9699
+ import { jsx as jsx37, jsxs as jsxs33 } from "react/jsx-runtime";
9406
9700
  function whenItFires(subscription) {
9407
9701
  if (subscription.cadence === "instant") return "as it happens";
9408
9702
  const every = subscription.cadence === "hourly" ? "an hourly summary" : subscription.cadence === "weekly" ? "a weekly summary" : "a daily summary";
9409
9703
  return subscription.schedule ? `${every}, ${subscription.schedule}` : every;
9410
9704
  }
9411
- var CHANNEL_WORDS = {
9705
+ var CHANNEL_WORDS2 = {
9412
9706
  in_app: "in the app",
9413
9707
  email: "by email \u2014 only if an address is on the account",
9414
9708
  sms: "by text \u2014 only if a number is on the account"
9415
9709
  };
9710
+ var DIGEST_SUGGESTION = "Email me a summary of this every Monday at 8 in the morning.";
9416
9711
  function SubscriptionsPanel({ tableId, className }) {
9417
- const client = useRecordsClient26();
9418
- const [rows, setRows] = useState32(null);
9419
- const [error, setError] = useState32(null);
9420
- const [busy, setBusy] = useState32(null);
9421
- const [preview, setPreview] = useState32(null);
9422
- const load = useCallback19(async () => {
9712
+ const client = useRecordsClient27();
9713
+ const [rows, setRows] = useState33(null);
9714
+ const [error, setError] = useState33(null);
9715
+ const [busy, setBusy] = useState33(null);
9716
+ const [preview, setPreview] = useState33(null);
9717
+ const [scheduling, setScheduling] = useState33(false);
9718
+ const host = useRecordsUi();
9719
+ const load = useCallback20(async () => {
9423
9720
  const answered = await client.subscriptions({ table_id: tableId });
9424
9721
  if (!answered.ok) {
9425
9722
  setError(answered.error);
@@ -9429,10 +9726,10 @@ function SubscriptionsPanel({ tableId, className }) {
9429
9726
  setError(null);
9430
9727
  setRows(answered.data);
9431
9728
  }, [client, tableId]);
9432
- useEffect22(() => {
9729
+ useEffect23(() => {
9433
9730
  void load();
9434
9731
  }, [load]);
9435
- const flip = useCallback19(
9732
+ const flip = useCallback20(
9436
9733
  async (subscription, on) => {
9437
9734
  setBusy(subscription.rule_id);
9438
9735
  const answered = await client.subscriptionMute({
@@ -9448,7 +9745,7 @@ function SubscriptionsPanel({ tableId, className }) {
9448
9745
  },
9449
9746
  [client, load]
9450
9747
  );
9451
- const showOne = useCallback19(
9748
+ const showOne = useCallback20(
9452
9749
  async (subscription) => {
9453
9750
  setBusy(subscription.rule_id);
9454
9751
  const answered = await client.subscriptionPreview({ rule_id: subscription.rule_id });
@@ -9462,25 +9759,57 @@ function SubscriptionsPanel({ tableId, className }) {
9462
9759
  },
9463
9760
  [client]
9464
9761
  );
9465
- if (rows === null) return /* @__PURE__ */ jsx36(Skeleton16, { className: cn30("h-32 w-full", className) });
9466
- return /* @__PURE__ */ jsxs32("section", { className: cn30("flex flex-col gap-3", className), children: [
9467
- /* @__PURE__ */ jsxs32("header", { className: "flex items-center gap-2", children: [
9468
- /* @__PURE__ */ jsx36("h3", { className: "text-sm font-medium", children: "Notifications" }),
9469
- /* @__PURE__ */ jsx36("span", { className: "text-xs text-muted-foreground", children: rows.length === 0 ? "none" : `${rows.filter((r) => !r.muted).length} on` })
9762
+ if (rows === null) return /* @__PURE__ */ jsx37(Skeleton17, { className: cn31("h-32 w-full", className) });
9763
+ return /* @__PURE__ */ jsxs33("section", { className: cn31("flex flex-col gap-3", className), children: [
9764
+ /* @__PURE__ */ jsxs33("header", { className: "flex items-center gap-2", children: [
9765
+ /* @__PURE__ */ jsx37("h3", { className: "text-sm font-medium", children: "Notifications" }),
9766
+ /* @__PURE__ */ jsx37("span", { className: "text-xs text-muted-foreground", children: rows.length === 0 ? "none" : `${rows.filter((r) => !r.muted).length} on` }),
9767
+ /* @__PURE__ */ jsx37("div", { className: "flex-1" }),
9768
+ rows.length > 0 ? /* @__PURE__ */ jsx37(
9769
+ Button30,
9770
+ {
9771
+ size: "sm",
9772
+ variant: scheduling ? "secondary" : "ghost",
9773
+ onClick: () => setScheduling((s) => !s),
9774
+ children: scheduling ? "Done" : "Schedule a summary"
9775
+ }
9776
+ ) : null
9470
9777
  ] }),
9471
- error ? /* @__PURE__ */ jsx36(RefusalNotice, { error }) : null,
9472
- rows.length === 0 ? /* @__PURE__ */ jsx36("p", { className: "text-xs text-muted-foreground", children: 'Nothing is telling you about this table. A form that says "tell me when somebody answers" makes one, and it appears here the moment it does.' }) : null,
9473
- /* @__PURE__ */ jsx36("ul", { className: "flex flex-col gap-2", children: rows.map((subscription) => /* @__PURE__ */ jsxs32("li", { className: "rounded-md border p-2.5", children: [
9474
- /* @__PURE__ */ jsxs32("div", { className: "flex items-start gap-2", children: [
9475
- /* @__PURE__ */ jsxs32("div", { className: "min-w-0 flex-1", children: [
9476
- /* @__PURE__ */ jsx36("p", { className: "truncate text-sm font-medium", children: subscription.name }),
9477
- /* @__PURE__ */ jsxs32("p", { className: "mt-0.5 text-xs text-muted-foreground", children: [
9478
- CHANNEL_WORDS[subscription.channel] ?? `on the ${subscription.channel} channel`,
9778
+ error ? /* @__PURE__ */ jsx37(RefusalNotice, { error }) : null,
9779
+ scheduling ? /* @__PURE__ */ jsx37(
9780
+ DigestScheduler,
9781
+ {
9782
+ tableId,
9783
+ onScheduled: () => {
9784
+ void load();
9785
+ },
9786
+ onClose: () => setScheduling(false)
9787
+ }
9788
+ ) : null,
9789
+ rows.length === 0 && !scheduling ? /* @__PURE__ */ jsx37(
9790
+ BuildOrAsk,
9791
+ {
9792
+ mayBuild: true,
9793
+ onBuild: () => setScheduling(true),
9794
+ ...host.onAskForOne ? {
9795
+ onAsk: () => host.onAskForOne?.({ kind: "digest", tableId, suggestion: DIGEST_SUGGESTION })
9796
+ } : {},
9797
+ suggestion: DIGEST_SUGGESTION,
9798
+ buildLabel: "Schedule a summary",
9799
+ children: "Nothing is telling you about this table. A summary arrives on a schedule you set and names what arrived, what left and what changed since the last one."
9800
+ }
9801
+ ) : null,
9802
+ /* @__PURE__ */ jsx37("ul", { className: "flex flex-col gap-2", children: rows.map((subscription) => /* @__PURE__ */ jsxs33("li", { className: "rounded-md border p-2.5", children: [
9803
+ /* @__PURE__ */ jsxs33("div", { className: "flex items-start gap-2", children: [
9804
+ /* @__PURE__ */ jsxs33("div", { className: "min-w-0 flex-1", children: [
9805
+ /* @__PURE__ */ jsx37("p", { className: "truncate text-sm font-medium", children: subscription.name }),
9806
+ /* @__PURE__ */ jsxs33("p", { className: "mt-0.5 text-xs text-muted-foreground", children: [
9807
+ CHANNEL_WORDS2[subscription.channel] ?? `on the ${subscription.channel} channel`,
9479
9808
  " \xB7 ",
9480
9809
  whenItFires(subscription)
9481
9810
  ] })
9482
9811
  ] }),
9483
- subscription.i_may_mute ? /* @__PURE__ */ jsx36(
9812
+ subscription.i_may_mute ? /* @__PURE__ */ jsx37(
9484
9813
  Switch2,
9485
9814
  {
9486
9815
  checked: !subscription.muted,
@@ -9488,27 +9817,27 @@ function SubscriptionsPanel({ tableId, className }) {
9488
9817
  "aria-label": `Tell me about ${subscription.name}`,
9489
9818
  onCheckedChange: (on) => void flip(subscription, on)
9490
9819
  }
9491
- ) : /* @__PURE__ */ jsx36(Badge14, { variant: "secondary", children: "someone else's" })
9820
+ ) : /* @__PURE__ */ jsx37(Badge14, { variant: "secondary", children: "someone else's" })
9492
9821
  ] }),
9493
- /* @__PURE__ */ jsx36("p", { className: "mt-1.5 text-xs text-muted-foreground", children: subscription.muted ? "Off \u2014 it stays here and tells nobody until you switch it back on." : subscription.mine ? "On, and addressed to you." : "On, and addressed to somebody else in this organization." }),
9494
- /* @__PURE__ */ jsxs32("div", { className: "mt-1.5 flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-muted-foreground", children: [
9495
- subscription.next_digest_at ? /* @__PURE__ */ jsxs32("span", { children: [
9822
+ /* @__PURE__ */ jsx37("p", { className: "mt-1.5 text-xs text-muted-foreground", children: subscription.muted ? "Off \u2014 it stays here and tells nobody until you switch it back on." : subscription.mine ? "On, and addressed to you." : "On, and addressed to somebody else in this organization." }),
9823
+ /* @__PURE__ */ jsxs33("div", { className: "mt-1.5 flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-muted-foreground", children: [
9824
+ subscription.next_digest_at ? /* @__PURE__ */ jsxs33("span", { children: [
9496
9825
  "Next summary ",
9497
9826
  new Date(subscription.next_digest_at).toLocaleString()
9498
- ] }) : subscription.cadence === "instant" ? null : subscription.muted ? /* @__PURE__ */ jsx36("span", { children: "No next summary while it is off." }) : null,
9499
- subscription.last_sent_at ? /* @__PURE__ */ jsxs32("span", { children: [
9827
+ ] }) : subscription.cadence === "instant" ? null : subscription.muted ? /* @__PURE__ */ jsx37("span", { children: "No next summary while it is off." }) : null,
9828
+ subscription.last_sent_at ? /* @__PURE__ */ jsxs33("span", { children: [
9500
9829
  "Last told you ",
9501
9830
  new Date(subscription.last_sent_at).toLocaleString()
9502
- ] }) : /* @__PURE__ */ jsx36("span", { children: "It has not told you anything yet." }),
9503
- subscription.quiet_hours ? /* @__PURE__ */ jsxs32("span", { children: [
9831
+ ] }) : /* @__PURE__ */ jsx37("span", { children: "It has not told you anything yet." }),
9832
+ subscription.quiet_hours ? /* @__PURE__ */ jsxs33("span", { children: [
9504
9833
  "Not between ",
9505
9834
  subscription.quiet_hours.start,
9506
9835
  " and ",
9507
9836
  subscription.quiet_hours.end,
9508
9837
  " \u2014 a send inside those hours waits until they end."
9509
9838
  ] }) : null,
9510
- subscription.cadence === "instant" ? null : /* @__PURE__ */ jsx36(
9511
- Button29,
9839
+ subscription.cadence === "instant" ? null : /* @__PURE__ */ jsx37(
9840
+ Button30,
9512
9841
  {
9513
9842
  size: "sm",
9514
9843
  variant: "ghost",
@@ -9518,66 +9847,66 @@ function SubscriptionsPanel({ tableId, className }) {
9518
9847
  }
9519
9848
  )
9520
9849
  ] }),
9521
- preview && preview.rule_id === subscription.rule_id ? /* @__PURE__ */ jsxs32("div", { className: "mt-2 rounded-md border bg-muted/40 p-2.5 text-xs", children: [
9522
- /* @__PURE__ */ jsx36("p", { className: "font-medium", children: preview.subject }),
9523
- /* @__PURE__ */ jsx36("p", { className: "mt-1 text-muted-foreground", children: preview.body }),
9850
+ preview && preview.rule_id === subscription.rule_id ? /* @__PURE__ */ jsxs33("div", { className: "mt-2 rounded-md border bg-muted/40 p-2.5 text-xs", children: [
9851
+ /* @__PURE__ */ jsx37("p", { className: "font-medium", children: preview.subject }),
9852
+ /* @__PURE__ */ jsx37("p", { className: "mt-1 text-muted-foreground", children: preview.body }),
9524
9853
  preview.incomplete ? (
9525
9854
  // Absent or honest: a subscription that cannot produce a summary
9526
9855
  // says which piece is missing instead of showing an empty one.
9527
- /* @__PURE__ */ jsx36("p", { className: "mt-1 text-destructive", children: preview.incomplete })
9856
+ /* @__PURE__ */ jsx37("p", { className: "mt-1 text-destructive", children: preview.incomplete })
9528
9857
  ) : null,
9529
- preview.entered.length > 0 ? /* @__PURE__ */ jsxs32("p", { className: "mt-1", children: [
9530
- /* @__PURE__ */ jsx36("span", { className: "font-medium", children: "Arrived:" }),
9858
+ preview.entered.length > 0 ? /* @__PURE__ */ jsxs33("p", { className: "mt-1", children: [
9859
+ /* @__PURE__ */ jsx37("span", { className: "font-medium", children: "Arrived:" }),
9531
9860
  " ",
9532
9861
  preview.entered.map((e) => e.name).join(", ")
9533
9862
  ] }) : null,
9534
- preview.left.length > 0 ? /* @__PURE__ */ jsxs32("p", { className: "mt-1", children: [
9535
- /* @__PURE__ */ jsx36("span", { className: "font-medium", children: "Left:" }),
9863
+ preview.left.length > 0 ? /* @__PURE__ */ jsxs33("p", { className: "mt-1", children: [
9864
+ /* @__PURE__ */ jsx37("span", { className: "font-medium", children: "Left:" }),
9536
9865
  " ",
9537
9866
  preview.left.map((e) => e.name).join(", ")
9538
9867
  ] }) : null,
9539
- preview.changed.length > 0 ? /* @__PURE__ */ jsxs32("p", { className: "mt-1", children: [
9540
- /* @__PURE__ */ jsx36("span", { className: "font-medium", children: "Changed:" }),
9868
+ preview.changed.length > 0 ? /* @__PURE__ */ jsxs33("p", { className: "mt-1", children: [
9869
+ /* @__PURE__ */ jsx37("span", { className: "font-medium", children: "Changed:" }),
9541
9870
  " ",
9542
9871
  preview.changed.map((e) => e.name).join(", ")
9543
9872
  ] }) : null,
9544
- /* @__PURE__ */ jsx36("p", { className: "mt-1.5 text-muted-foreground", children: "Nothing was sent and nothing was recorded \u2014 this is what the next one would say." }),
9545
- /* @__PURE__ */ jsx36(Button29, { size: "sm", variant: "ghost", className: "mt-1", onClick: () => setPreview(null), children: "Close" })
9873
+ /* @__PURE__ */ jsx37("p", { className: "mt-1.5 text-muted-foreground", children: "Nothing was sent and nothing was recorded \u2014 this is what the next one would say." }),
9874
+ /* @__PURE__ */ jsx37(Button30, { size: "sm", variant: "ghost", className: "mt-1", onClick: () => setPreview(null), children: "Close" })
9546
9875
  ] }) : null
9547
9876
  ] }, subscription.rule_id)) })
9548
9877
  ] });
9549
9878
  }
9550
9879
 
9551
9880
  // src/FormBuilder.tsx
9552
- import { useCallback as useCallback21, useEffect as useEffect24, useMemo as useMemo24, useState as useState34 } from "react";
9553
- import { useFields as useFields16, useRecordsClient as useRecordsClient27, useTable as useTable13 } from "@ai-matrx/records/react";
9881
+ import { useCallback as useCallback22, useEffect as useEffect25, useMemo as useMemo25, useState as useState35 } from "react";
9882
+ import { useFields as useFields16, useRecordsClient as useRecordsClient28, useTable as useTable13 } from "@ai-matrx/records/react";
9554
9883
  import { publicFormPath } from "@ai-matrx/records";
9555
9884
  import {
9556
- BasicInput as BasicInput10,
9885
+ BasicInput as BasicInput11,
9557
9886
  BasicTextarea as BasicTextarea5,
9558
- Button as Button31,
9559
- Checkbox as Checkbox5,
9560
- Label as Label5,
9561
- Skeleton as Skeleton18,
9562
- cn as cn32
9887
+ Button as Button32,
9888
+ Checkbox as Checkbox6,
9889
+ Label as Label6,
9890
+ Skeleton as Skeleton19,
9891
+ cn as cn33
9563
9892
  } from "@ai-matrx/design-system";
9564
9893
 
9565
9894
  // src/FormRunner.tsx
9566
- import { useCallback as useCallback20, useEffect as useEffect23, useMemo as useMemo23, useRef as useRef9, useState as useState33 } from "react";
9895
+ import { useCallback as useCallback21, useEffect as useEffect24, useMemo as useMemo24, useRef as useRef9, useState as useState34 } from "react";
9567
9896
  import { useFields as useFields15, useOptionalRecordsClient } from "@ai-matrx/records/react";
9568
- import { Button as Button30, Progress, Skeleton as Skeleton17, cn as cn31 } from "@ai-matrx/design-system";
9569
- import { Fragment as Fragment16, jsx as jsx37, jsxs as jsxs33 } from "react/jsx-runtime";
9897
+ import { Button as Button31, Progress, Skeleton as Skeleton18, cn as cn32 } from "@ai-matrx/design-system";
9898
+ import { Fragment as Fragment17, jsx as jsx38, jsxs as jsxs34 } from "react/jsx-runtime";
9570
9899
  function FormRunner(props) {
9571
9900
  if (props.fields && props.onSubmit) {
9572
- return /* @__PURE__ */ jsx37(FormStage, { ...props, fields: props.fields, onSubmit: props.onSubmit });
9901
+ return /* @__PURE__ */ jsx38(FormStage, { ...props, fields: props.fields, onSubmit: props.onSubmit });
9573
9902
  }
9574
- return /* @__PURE__ */ jsx37(ConnectedFormRunner, { ...props });
9903
+ return /* @__PURE__ */ jsx38(ConnectedFormRunner, { ...props });
9575
9904
  }
9576
9905
  function ConnectedFormRunner(props) {
9577
9906
  const client = useOptionalRecordsClient();
9578
9907
  const fields = useFields15(props.form.subject);
9579
9908
  const { form, className } = props;
9580
- const submit = useCallback20(
9909
+ const submit = useCallback21(
9581
9910
  async (values) => {
9582
9911
  if (!client) {
9583
9912
  return {
@@ -9600,7 +9929,7 @@ function ConnectedFormRunner(props) {
9600
9929
  },
9601
9930
  [client, form]
9602
9931
  );
9603
- const evaluate = useCallback20(
9932
+ const evaluate = useCallback21(
9604
9933
  async (expr, values) => {
9605
9934
  if (!client) return null;
9606
9935
  const answered = await client.ruleEval({ expr, values });
@@ -9609,9 +9938,9 @@ function ConnectedFormRunner(props) {
9609
9938
  },
9610
9939
  [client]
9611
9940
  );
9612
- if (fields.error) return /* @__PURE__ */ jsx37(RefusalNotice, { error: fields.error, className });
9613
- if (fields.loading) return /* @__PURE__ */ jsx37(Skeleton17, { className: cn31("h-40 w-full", className) });
9614
- return /* @__PURE__ */ jsx37(
9941
+ if (fields.error) return /* @__PURE__ */ jsx38(RefusalNotice, { error: fields.error, className });
9942
+ if (fields.loading) return /* @__PURE__ */ jsx38(Skeleton18, { className: cn32("h-40 w-full", className) });
9943
+ return /* @__PURE__ */ jsx38(
9615
9944
  FormStage,
9616
9945
  {
9617
9946
  ...props,
@@ -9634,16 +9963,16 @@ function FormStage({
9634
9963
  connected = false
9635
9964
  }) {
9636
9965
  const host = useRecordsUi();
9637
- const [answers, setAnswers] = useState33({});
9638
- const [at, setAt] = useState33(0);
9639
- const [error, setError] = useState33(null);
9640
- const [refusal, setRefusal] = useState33(null);
9641
- const [writing, setWriting] = useState33(false);
9642
- const [done, setDone] = useState33(null);
9643
- const [hidden, setHidden] = useState33({});
9966
+ const [answers, setAnswers] = useState34({});
9967
+ const [at, setAt] = useState34(0);
9968
+ const [error, setError] = useState34(null);
9969
+ const [refusal, setRefusal] = useState34(null);
9970
+ const [writing, setWriting] = useState34(false);
9971
+ const [done, setDone] = useState34(null);
9972
+ const [hidden, setHidden] = useState34({});
9644
9973
  const decoy = useRef9("");
9645
9974
  const stage = useRef9(null);
9646
- const questions = useMemo23(() => {
9975
+ const questions = useMemo24(() => {
9647
9976
  const byKey = new Map((fields ?? []).map((f) => [f.key, f]));
9648
9977
  return (form.questions ?? []).map((q) => {
9649
9978
  const field = byKey.get(q.field) ?? null;
@@ -9657,7 +9986,7 @@ function FormStage({
9657
9986
  };
9658
9987
  });
9659
9988
  }, [fields, form.questions]);
9660
- useEffect23(() => {
9989
+ useEffect24(() => {
9661
9990
  let cancelled = false;
9662
9991
  const conditional = questions.filter((q) => q.showIf);
9663
9992
  if (conditional.length === 0 || !evaluate) return;
@@ -9691,7 +10020,7 @@ function FormStage({
9691
10020
  const v = answers[q.key];
9692
10021
  return q.required && (v === void 0 || v === null || v === "");
9693
10022
  });
9694
- const submit = useCallback20(async () => {
10023
+ const submit = useCallback21(async () => {
9695
10024
  if (preview) {
9696
10025
  setDone("preview");
9697
10026
  return;
@@ -9728,51 +10057,51 @@ function FormStage({
9728
10057
  if (event.shiftKey) retreat();
9729
10058
  else advance();
9730
10059
  }
9731
- useEffect23(() => {
10060
+ useEffect24(() => {
9732
10061
  const input = stage.current?.querySelector(
9733
10062
  "input:not([tabindex='-1']), textarea, select, [role='combobox']"
9734
10063
  );
9735
10064
  input?.focus();
9736
10065
  }, [index, done]);
9737
10066
  if (done) {
9738
- return /* @__PURE__ */ jsxs33("section", { className: cn31("mx-auto max-w-xl py-10", centred && "text-center", className), children: [
9739
- /* @__PURE__ */ jsx37("h2", { className: "text-lg font-medium", children: form.thankYou?.title ?? "Thank you" }),
9740
- form.thankYou?.body ? /* @__PURE__ */ jsx37("p", { className: "mt-1 text-sm text-muted-foreground", children: form.thankYou.body }) : null,
9741
- /* @__PURE__ */ jsx37("p", { className: "mt-3 text-xs text-muted-foreground", children: done === "preview" ? "This was a preview, so nothing was written." : done === "sent" ? `Saved to ${form.name}, with this form stamped on it.` : done })
10067
+ return /* @__PURE__ */ jsxs34("section", { className: cn32("mx-auto max-w-xl py-10", centred && "text-center", className), children: [
10068
+ /* @__PURE__ */ jsx38("h2", { className: "text-lg font-medium", children: form.thankYou?.title ?? "Thank you" }),
10069
+ form.thankYou?.body ? /* @__PURE__ */ jsx38("p", { className: "mt-1 text-sm text-muted-foreground", children: form.thankYou.body }) : null,
10070
+ /* @__PURE__ */ jsx38("p", { className: "mt-3 text-xs text-muted-foreground", children: done === "preview" ? "This was a preview, so nothing was written." : done === "sent" ? `Saved to ${form.name}, with this form stamped on it.` : done })
9742
10071
  ] });
9743
10072
  }
9744
10073
  const unresolved = questions.filter((q) => !q.field);
9745
10074
  const unanswerable = !evaluate && questions.some((q) => q.showIf);
9746
10075
  const signedInPublic = connected && form.isPublic === true;
9747
- return /* @__PURE__ */ jsxs33(
10076
+ return /* @__PURE__ */ jsxs34(
9748
10077
  "section",
9749
10078
  {
9750
- className: cn31("mx-auto flex max-w-xl flex-col gap-3 py-4", centred && "text-center", className),
10079
+ className: cn32("mx-auto flex max-w-xl flex-col gap-3 py-4", centred && "text-center", className),
9751
10080
  onKeyDown,
9752
10081
  children: [
9753
- /* @__PURE__ */ jsxs33("header", { className: "flex items-center gap-3 text-xs text-muted-foreground", children: [
9754
- /* @__PURE__ */ jsx37(
10082
+ /* @__PURE__ */ jsxs34("header", { className: "flex items-center gap-3 text-xs text-muted-foreground", children: [
10083
+ /* @__PURE__ */ jsx38(
9755
10084
  Progress,
9756
10085
  {
9757
10086
  value: live.length === 0 ? 0 : (oneAtATime ? index + 1 : answered) / live.length * 100,
9758
10087
  className: "h-1 flex-1"
9759
10088
  }
9760
10089
  ),
9761
- /* @__PURE__ */ jsx37("span", { className: "tabular-nums", children: oneAtATime ? `${index + 1} of ${live.length}` : `${answered} of ${live.length}` })
10090
+ /* @__PURE__ */ jsx38("span", { className: "tabular-nums", children: oneAtATime ? `${index + 1} of ${live.length}` : `${answered} of ${live.length}` })
9762
10091
  ] }),
9763
- signedInPublic ? /* @__PURE__ */ jsx37("p", { className: "rounded border border-dashed px-2 py-1 text-left text-xs text-muted-foreground", children: "You are answering this signed in, so the record will carry your name. A stranger answers the same form at its public link, where the answer arrives through the anonymous door instead." }) : null,
9764
- unanswerable ? /* @__PURE__ */ jsx37("p", { className: "rounded border border-dashed px-2 py-1 text-left text-xs text-muted-foreground", children: "This form has questions that only appear in certain cases. Nothing here can work out which ones, so all of them are being shown rather than any being skipped." }) : null,
9765
- unresolved.length > 0 ? /* @__PURE__ */ jsxs33("p", { className: "rounded border border-dashed px-2 py-1 text-left text-xs text-destructive", children: [
10092
+ signedInPublic ? /* @__PURE__ */ jsx38("p", { className: "rounded border border-dashed px-2 py-1 text-left text-xs text-muted-foreground", children: "You are answering this signed in, so the record will carry your name. A stranger answers the same form at its public link, where the answer arrives through the anonymous door instead." }) : null,
10093
+ unanswerable ? /* @__PURE__ */ jsx38("p", { className: "rounded border border-dashed px-2 py-1 text-left text-xs text-muted-foreground", children: "This form has questions that only appear in certain cases. Nothing here can work out which ones, so all of them are being shown rather than any being skipped." }) : null,
10094
+ unresolved.length > 0 ? /* @__PURE__ */ jsxs34("p", { className: "rounded border border-dashed px-2 py-1 text-left text-xs text-destructive", children: [
9766
10095
  unresolved.map((q) => q.key).join(", "),
9767
10096
  " ",
9768
10097
  unresolved.length === 1 ? "is a question" : "are questions",
9769
10098
  " this form asks for and this table has no such Field. Add the Field, or take the question out \u2014 it is shown here rather than quietly dropped, because a dropped question is an answer nobody gave."
9770
10099
  ] }) : null,
9771
- form.intro && index === 0 ? /* @__PURE__ */ jsx37("p", { className: "text-sm text-muted-foreground", children: form.intro }) : null,
9772
- error ? /* @__PURE__ */ jsx37(RefusalNotice, { error, className: "text-left" }) : null,
9773
- refusal ? /* @__PURE__ */ jsx37("p", { role: "alert", className: "rounded border border-destructive/40 px-2 py-1 text-left text-xs text-destructive", children: refusal }) : null,
9774
- /* @__PURE__ */ jsxs33("div", { ref: stage, className: "flex flex-col gap-4 text-left", children: [
9775
- (oneAtATime ? current ? [current] : [] : live).map((q) => /* @__PURE__ */ jsx37(
10100
+ form.intro && index === 0 ? /* @__PURE__ */ jsx38("p", { className: "text-sm text-muted-foreground", children: form.intro }) : null,
10101
+ error ? /* @__PURE__ */ jsx38(RefusalNotice, { error, className: "text-left" }) : null,
10102
+ refusal ? /* @__PURE__ */ jsx38("p", { role: "alert", className: "rounded border border-destructive/40 px-2 py-1 text-left text-xs text-destructive", children: refusal }) : null,
10103
+ /* @__PURE__ */ jsxs34("div", { ref: stage, className: "flex flex-col gap-4 text-left", children: [
10104
+ (oneAtATime ? current ? [current] : [] : live).map((q) => /* @__PURE__ */ jsx38(
9776
10105
  Question,
9777
10106
  {
9778
10107
  question: q,
@@ -9782,11 +10111,11 @@ function FormStage({
9782
10111
  },
9783
10112
  q.key
9784
10113
  )),
9785
- live.length === 0 ? /* @__PURE__ */ jsx37("p", { className: "text-xs text-muted-foreground", children: "Every question in this form is hidden by its own condition right now, so there is nothing to answer yet." }) : null
10114
+ live.length === 0 ? /* @__PURE__ */ jsx38("p", { className: "text-xs text-muted-foreground", children: "Every question in this form is hidden by its own condition right now, so there is nothing to answer yet." }) : null
9786
10115
  ] }),
9787
- honeypotKey ? /* @__PURE__ */ jsxs33("div", { "aria-hidden": true, className: "pointer-events-none absolute left-[-9999px] h-px w-px overflow-hidden", children: [
9788
- /* @__PURE__ */ jsx37("label", { htmlFor: `hp-${honeypotKey}`, children: "Leave this field empty" }),
9789
- /* @__PURE__ */ jsx37(
10116
+ honeypotKey ? /* @__PURE__ */ jsxs34("div", { "aria-hidden": true, className: "pointer-events-none absolute left-[-9999px] h-px w-px overflow-hidden", children: [
10117
+ /* @__PURE__ */ jsx38("label", { htmlFor: `hp-${honeypotKey}`, children: "Leave this field empty" }),
10118
+ /* @__PURE__ */ jsx38(
9790
10119
  "input",
9791
10120
  {
9792
10121
  id: `hp-${honeypotKey}`,
@@ -9801,16 +10130,16 @@ function FormStage({
9801
10130
  }
9802
10131
  )
9803
10132
  ] }) : null,
9804
- /* @__PURE__ */ jsxs33("footer", { className: cn31("flex flex-wrap items-center gap-2", centred && "justify-center"), children: [
9805
- oneAtATime && index > 0 ? /* @__PURE__ */ jsx37(Button30, { size: "sm", variant: "ghost", onClick: retreat, children: "Back" }) : null,
9806
- oneAtATime && index < live.length - 1 ? /* @__PURE__ */ jsx37(Button30, { size: "sm", onClick: advance, children: "Next" }) : /* @__PURE__ */ jsx37(Button30, { size: "sm", disabled: writing || missing.length > 0, onClick: () => void submit(), children: writing ? "Sending\u2026" : form.submitLabel ?? "Submit" }),
9807
- missing.length > 0 && (!oneAtATime || index === live.length - 1) ? /* @__PURE__ */ jsxs33("span", { className: "text-xs text-muted-foreground", children: [
10133
+ /* @__PURE__ */ jsxs34("footer", { className: cn32("flex flex-wrap items-center gap-2", centred && "justify-center"), children: [
10134
+ oneAtATime && index > 0 ? /* @__PURE__ */ jsx38(Button31, { size: "sm", variant: "ghost", onClick: retreat, children: "Back" }) : null,
10135
+ oneAtATime && index < live.length - 1 ? /* @__PURE__ */ jsx38(Button31, { size: "sm", onClick: advance, children: "Next" }) : /* @__PURE__ */ jsx38(Button31, { size: "sm", disabled: writing || missing.length > 0, onClick: () => void submit(), children: writing ? "Sending\u2026" : form.submitLabel ?? "Submit" }),
10136
+ missing.length > 0 && (!oneAtATime || index === live.length - 1) ? /* @__PURE__ */ jsxs34("span", { className: "text-xs text-muted-foreground", children: [
9808
10137
  missing.map((q) => q.ask).join(", "),
9809
10138
  " still ",
9810
10139
  missing.length === 1 ? "needs" : "need",
9811
10140
  " an answer."
9812
10141
  ] }) : null,
9813
- preview ? /* @__PURE__ */ jsx37("span", { className: "text-xs text-muted-foreground", children: "Preview \u2014 nothing is written." }) : null
10142
+ preview ? /* @__PURE__ */ jsx38("span", { className: "text-xs text-muted-foreground", children: "Preview \u2014 nothing is written." }) : null
9814
10143
  ] })
9815
10144
  ]
9816
10145
  }
@@ -9822,16 +10151,16 @@ function Question({
9822
10151
  onChange,
9823
10152
  upload
9824
10153
  }) {
9825
- const [uploadError, setUploadError] = useState33(null);
10154
+ const [uploadError, setUploadError] = useState34(null);
9826
10155
  const field = question.field;
9827
10156
  if (!field) return null;
9828
10157
  const id = `form-${field.key}`;
9829
10158
  const isAttachment = editorKindFor(field) === "attachment";
9830
- return /* @__PURE__ */ jsxs33("div", { className: "flex flex-col gap-1.5", children: [
9831
- /* @__PURE__ */ jsx37(FieldLabel, { field: { ...field, label: question.ask, required: question.required }, htmlFor: id }),
9832
- question.help ? /* @__PURE__ */ jsx37("p", { className: "text-xs text-muted-foreground", children: question.help }) : null,
9833
- isAttachment && !upload ? /* @__PURE__ */ jsx37("p", { className: "rounded border border-dashed px-2 py-1 text-xs text-muted-foreground", children: NO_UPLOAD_REASON }) : isAttachment ? /* @__PURE__ */ jsxs33(Fragment16, { children: [
9834
- /* @__PURE__ */ jsx37(
10159
+ return /* @__PURE__ */ jsxs34("div", { className: "flex flex-col gap-1.5", children: [
10160
+ /* @__PURE__ */ jsx38(FieldLabel, { field: { ...field, label: question.ask, required: question.required }, htmlFor: id }),
10161
+ question.help ? /* @__PURE__ */ jsx38("p", { className: "text-xs text-muted-foreground", children: question.help }) : null,
10162
+ isAttachment && !upload ? /* @__PURE__ */ jsx38("p", { className: "rounded border border-dashed px-2 py-1 text-xs text-muted-foreground", children: NO_UPLOAD_REASON }) : isAttachment ? /* @__PURE__ */ jsxs34(Fragment17, { children: [
10163
+ /* @__PURE__ */ jsx38(
9835
10164
  "input",
9836
10165
  {
9837
10166
  id,
@@ -9849,31 +10178,31 @@ function Question({
9849
10178
  }
9850
10179
  }
9851
10180
  ),
9852
- uploadError ? /* @__PURE__ */ jsx37("p", { className: "text-xs text-destructive", children: uploadError }) : null
9853
- ] }) : /* @__PURE__ */ jsx37(FieldControl, { field, value, onChange, id })
10181
+ uploadError ? /* @__PURE__ */ jsx38("p", { className: "text-xs text-destructive", children: uploadError }) : null
10182
+ ] }) : /* @__PURE__ */ jsx38(FieldControl, { field, value, onChange, id })
9854
10183
  ] });
9855
10184
  }
9856
10185
 
9857
10186
  // src/FormBuilder.tsx
9858
- import { Fragment as Fragment17, jsx as jsx38, jsxs as jsxs34 } from "react/jsx-runtime";
10187
+ import { Fragment as Fragment18, jsx as jsx39, jsxs as jsxs35 } from "react/jsx-runtime";
9859
10188
  function FormBuilder({ tableId, seed, activeFormId, onActiveForm, className }) {
9860
- const client = useRecordsClient27();
10189
+ const client = useRecordsClient28();
9861
10190
  const host = useRecordsUi();
9862
10191
  const claimSeed = useSeedGuard();
9863
10192
  const table = useTable13(tableId);
9864
10193
  const rights = useTableRights(table.data);
9865
10194
  const fields = useFields16(tableId);
9866
- const [forms, setForms] = useState34(null);
9867
- const [error, setError] = useState34(null);
9868
- const [activeId, setActiveId] = useState34(activeFormId ?? null);
9869
- const [draft, setDraft] = useState34(null);
9870
- const [saving, setSaving] = useState34(false);
9871
- const [saved, setSaved] = useState34(null);
9872
- const [publishing, setPublishing] = useState34(false);
9873
- const [copied, setCopied] = useState34(false);
9874
- const [shownUrl, setShownUrl] = useState34(null);
10195
+ const [forms, setForms] = useState35(null);
10196
+ const [error, setError] = useState35(null);
10197
+ const [activeId, setActiveId] = useState35(activeFormId ?? null);
10198
+ const [draft, setDraft] = useState35(null);
10199
+ const [saving, setSaving] = useState35(false);
10200
+ const [saved, setSaved] = useState35(null);
10201
+ const [publishing, setPublishing] = useState35(false);
10202
+ const [copied, setCopied] = useState35(false);
10203
+ const [shownUrl, setShownUrl] = useState35(null);
9875
10204
  const publicOrigin = host.publicOrigin ?? (typeof window === "undefined" ? "" : window.location.origin);
9876
- const load = useCallback21(async () => {
10205
+ const load = useCallback22(async () => {
9877
10206
  const answered = await client.forms({ table_id: tableId });
9878
10207
  if (!answered.ok) {
9879
10208
  setError(answered.error);
@@ -9900,17 +10229,17 @@ function FormBuilder({ tableId, seed, activeFormId, onActiveForm, className }) {
9900
10229
  setError(null);
9901
10230
  setForms(mine);
9902
10231
  }, [client, tableId, seed, claimSeed]);
9903
- useEffect24(() => {
10232
+ useEffect25(() => {
9904
10233
  void load();
9905
10234
  }, [load]);
9906
- useEffect24(() => {
10235
+ useEffect25(() => {
9907
10236
  if (!forms || forms.length === 0) return;
9908
10237
  const chosen = forms.find((f) => f.id === (activeFormId ?? activeId)) ?? forms[0];
9909
10238
  if (chosen.id !== activeId) setActiveId(chosen.id);
9910
10239
  setDraft(chosen);
9911
10240
  onActiveForm?.(chosen);
9912
10241
  }, [forms, activeFormId]);
9913
- const byKey = useMemo24(() => new Map((fields.data ?? []).map((f) => [f.key, f])), [fields.data]);
10242
+ const byKey = useMemo25(() => new Map((fields.data ?? []).map((f) => [f.key, f])), [fields.data]);
9914
10243
  function patch(change) {
9915
10244
  setDraft((prev) => prev ? { ...prev, ...change } : prev);
9916
10245
  setSaved(null);
@@ -9970,26 +10299,26 @@ function FormBuilder({ tableId, seed, activeFormId, onActiveForm, className }) {
9970
10299
  setActiveId(written.data);
9971
10300
  await load();
9972
10301
  }
9973
- if (fields.error) return /* @__PURE__ */ jsx38(RefusalNotice, { error: fields.error, className });
10302
+ if (fields.error) return /* @__PURE__ */ jsx39(RefusalNotice, { error: fields.error, className });
9974
10303
  if (error) {
9975
- return /* @__PURE__ */ jsx38(
10304
+ return /* @__PURE__ */ jsx39(
9976
10305
  RefusalNotice,
9977
10306
  {
9978
10307
  error,
9979
10308
  className,
9980
- actions: /* @__PURE__ */ jsx38(Button31, { size: "sm", variant: "ghost", onClick: () => void load(), children: "Try again" })
10309
+ actions: /* @__PURE__ */ jsx39(Button32, { size: "sm", variant: "ghost", onClick: () => void load(), children: "Try again" })
9981
10310
  }
9982
10311
  );
9983
10312
  }
9984
- if (fields.loading || forms === null) return /* @__PURE__ */ jsx38(Skeleton18, { className: cn32("h-64 w-full", className) });
10313
+ if (fields.loading || forms === null) return /* @__PURE__ */ jsx39(Skeleton19, { className: cn33("h-64 w-full", className) });
9985
10314
  if (!rights.admin) {
9986
- return /* @__PURE__ */ jsx38("p", { className: cn32("text-xs text-muted-foreground", className), children: rights.why("structure") });
10315
+ return /* @__PURE__ */ jsx39("p", { className: cn33("text-xs text-muted-foreground", className), children: rights.why("structure") });
9987
10316
  }
9988
- return /* @__PURE__ */ jsxs34("div", { className: cn32("flex min-h-0 gap-3", className), children: [
9989
- /* @__PURE__ */ jsxs34("div", { className: "flex min-w-0 flex-1 flex-col gap-2 overflow-auto", children: [
9990
- /* @__PURE__ */ jsxs34("div", { className: "flex items-center gap-1 overflow-x-auto", role: "group", "aria-label": "Forms", children: [
9991
- forms.map((form) => /* @__PURE__ */ jsx38(
9992
- Button31,
10317
+ return /* @__PURE__ */ jsxs35("div", { className: cn33("flex min-h-0 gap-3", className), children: [
10318
+ /* @__PURE__ */ jsxs35("div", { className: "flex min-w-0 flex-1 flex-col gap-2 overflow-auto", children: [
10319
+ /* @__PURE__ */ jsxs35("div", { className: "flex items-center gap-1 overflow-x-auto", role: "group", "aria-label": "Forms", children: [
10320
+ forms.map((form) => /* @__PURE__ */ jsx39(
10321
+ Button32,
9993
10322
  {
9994
10323
  size: "sm",
9995
10324
  variant: form.id === activeId ? "secondary" : "ghost",
@@ -10002,14 +10331,14 @@ function FormBuilder({ tableId, seed, activeFormId, onActiveForm, className }) {
10002
10331
  },
10003
10332
  form.id
10004
10333
  )),
10005
- (fields.data ?? []).length > 0 ? /* @__PURE__ */ jsx38(Button31, { size: "sm", variant: "ghost", onClick: () => void create(`Form ${forms.length + 1}`), children: "New form" }) : null,
10006
- /* @__PURE__ */ jsxs34("span", { className: "ml-auto flex items-center gap-2", children: [
10007
- saved ? /* @__PURE__ */ jsx38("span", { className: "text-xs text-muted-foreground", children: saved }) : null,
10008
- /* @__PURE__ */ jsx38(Button31, { size: "sm", disabled: saving || !draft, onClick: () => void save(), children: saving ? "Saving\u2026" : "Save" })
10334
+ (fields.data ?? []).length > 0 ? /* @__PURE__ */ jsx39(Button32, { size: "sm", variant: "ghost", onClick: () => void create(`Form ${forms.length + 1}`), children: "New form" }) : null,
10335
+ /* @__PURE__ */ jsxs35("span", { className: "ml-auto flex items-center gap-2", children: [
10336
+ saved ? /* @__PURE__ */ jsx39("span", { className: "text-xs text-muted-foreground", children: saved }) : null,
10337
+ /* @__PURE__ */ jsx39(Button32, { size: "sm", disabled: saving || !draft, onClick: () => void save(), children: saving ? "Saving\u2026" : "Save" })
10009
10338
  ] })
10010
10339
  ] }),
10011
- !draft ? /* @__PURE__ */ jsx38("p", { className: "text-xs text-muted-foreground", children: (fields.data ?? []).length === 0 ? "This table has no fields yet, so there is nothing a form could ask for. Add a field first \u2014 every question is one of this table's own fields." : 'No form collects into this table yet. "New form" makes one, or you ask an agent for the whole thing in a sentence.' }) : /* @__PURE__ */ jsxs34(Fragment17, { children: [
10012
- /* @__PURE__ */ jsx38(
10340
+ !draft ? /* @__PURE__ */ jsx39("p", { className: "text-xs text-muted-foreground", children: (fields.data ?? []).length === 0 ? "This table has no fields yet, so there is nothing a form could ask for. Add a field first \u2014 every question is one of this table's own fields." : 'No form collects into this table yet. "New form" makes one, or you ask an agent for the whole thing in a sentence.' }) : /* @__PURE__ */ jsxs35(Fragment18, { children: [
10341
+ /* @__PURE__ */ jsx39(
10013
10342
  PublishRow,
10014
10343
  {
10015
10344
  url: `${publicOrigin}${publicFormPath(draft.id)}`,
@@ -10023,26 +10352,26 @@ function FormBuilder({ tableId, seed, activeFormId, onActiveForm, className }) {
10023
10352
  onCopy: (url) => void copyLink(url)
10024
10353
  }
10025
10354
  ),
10026
- /* @__PURE__ */ jsxs34("div", { className: "grid grid-cols-2 gap-2", children: [
10027
- /* @__PURE__ */ jsxs34("label", { className: "flex flex-col gap-1", children: [
10028
- /* @__PURE__ */ jsx38(Label5, { className: "text-xs font-medium", children: "Name" }),
10029
- /* @__PURE__ */ jsx38(BasicInput10, { value: draft.name, onChange: (e) => patch({ name: e.target.value }) })
10355
+ /* @__PURE__ */ jsxs35("div", { className: "grid grid-cols-2 gap-2", children: [
10356
+ /* @__PURE__ */ jsxs35("label", { className: "flex flex-col gap-1", children: [
10357
+ /* @__PURE__ */ jsx39(Label6, { className: "text-xs font-medium", children: "Name" }),
10358
+ /* @__PURE__ */ jsx39(BasicInput11, { value: draft.name, onChange: (e) => patch({ name: e.target.value }) })
10030
10359
  ] }),
10031
- /* @__PURE__ */ jsxs34("label", { className: "flex flex-col gap-1", children: [
10032
- /* @__PURE__ */ jsx38(Label5, { className: "text-xs font-medium", children: "Flow" }),
10033
- /* @__PURE__ */ jsx38(
10360
+ /* @__PURE__ */ jsxs35("label", { className: "flex flex-col gap-1", children: [
10361
+ /* @__PURE__ */ jsx39(Label6, { className: "text-xs font-medium", children: "Flow" }),
10362
+ /* @__PURE__ */ jsx39(
10034
10363
  "select",
10035
10364
  {
10036
10365
  className: "h-9 rounded border bg-background px-2 text-sm",
10037
10366
  value: draft.flow ?? "one-at-a-time",
10038
10367
  onChange: (e) => patch({ flow: e.target.value }),
10039
- children: FORM_FLOWS.map((flow) => /* @__PURE__ */ jsx38("option", { value: flow, children: flow === "one-at-a-time" ? "One question at a time" : "All on one page" }, flow))
10368
+ children: FORM_FLOWS.map((flow) => /* @__PURE__ */ jsx39("option", { value: flow, children: flow === "one-at-a-time" ? "One question at a time" : "All on one page" }, flow))
10040
10369
  }
10041
10370
  )
10042
10371
  ] }),
10043
- /* @__PURE__ */ jsxs34("label", { className: "col-span-2 flex flex-col gap-1", children: [
10044
- /* @__PURE__ */ jsx38(Label5, { className: "text-xs font-medium", children: "Intro" }),
10045
- /* @__PURE__ */ jsx38(
10372
+ /* @__PURE__ */ jsxs35("label", { className: "col-span-2 flex flex-col gap-1", children: [
10373
+ /* @__PURE__ */ jsx39(Label6, { className: "text-xs font-medium", children: "Intro" }),
10374
+ /* @__PURE__ */ jsx39(
10046
10375
  BasicTextarea5,
10047
10376
  {
10048
10377
  rows: 2,
@@ -10051,10 +10380,10 @@ function FormBuilder({ tableId, seed, activeFormId, onActiveForm, className }) {
10051
10380
  }
10052
10381
  )
10053
10382
  ] }),
10054
- /* @__PURE__ */ jsxs34("label", { className: "flex flex-col gap-1", children: [
10055
- /* @__PURE__ */ jsx38(Label5, { className: "text-xs font-medium", children: "Submit button" }),
10056
- /* @__PURE__ */ jsx38(
10057
- BasicInput10,
10383
+ /* @__PURE__ */ jsxs35("label", { className: "flex flex-col gap-1", children: [
10384
+ /* @__PURE__ */ jsx39(Label6, { className: "text-xs font-medium", children: "Submit button" }),
10385
+ /* @__PURE__ */ jsx39(
10386
+ BasicInput11,
10058
10387
  {
10059
10388
  value: draft.submitLabel ?? "",
10060
10389
  placeholder: "Submit",
@@ -10062,10 +10391,10 @@ function FormBuilder({ tableId, seed, activeFormId, onActiveForm, className }) {
10062
10391
  }
10063
10392
  )
10064
10393
  ] }),
10065
- /* @__PURE__ */ jsxs34("label", { className: "flex flex-col gap-1", children: [
10066
- /* @__PURE__ */ jsx38(Label5, { className: "text-xs font-medium", children: "Thank-you title" }),
10067
- /* @__PURE__ */ jsx38(
10068
- BasicInput10,
10394
+ /* @__PURE__ */ jsxs35("label", { className: "flex flex-col gap-1", children: [
10395
+ /* @__PURE__ */ jsx39(Label6, { className: "text-xs font-medium", children: "Thank-you title" }),
10396
+ /* @__PURE__ */ jsx39(
10397
+ BasicInput11,
10069
10398
  {
10070
10399
  value: draft.thankYou?.title ?? "",
10071
10400
  placeholder: "Thank you",
@@ -10076,10 +10405,10 @@ function FormBuilder({ tableId, seed, activeFormId, onActiveForm, className }) {
10076
10405
  )
10077
10406
  ] })
10078
10407
  ] }),
10079
- /* @__PURE__ */ jsxs34("div", { className: "flex flex-col gap-1", children: [
10080
- /* @__PURE__ */ jsxs34("div", { className: "flex items-center gap-2 text-xs text-muted-foreground", children: [
10081
- /* @__PURE__ */ jsx38("span", { className: "font-medium text-foreground", children: "Questions" }),
10082
- /* @__PURE__ */ jsxs34("span", { children: [
10408
+ /* @__PURE__ */ jsxs35("div", { className: "flex flex-col gap-1", children: [
10409
+ /* @__PURE__ */ jsxs35("div", { className: "flex items-center gap-2 text-xs text-muted-foreground", children: [
10410
+ /* @__PURE__ */ jsx39("span", { className: "font-medium text-foreground", children: "Questions" }),
10411
+ /* @__PURE__ */ jsxs35("span", { children: [
10083
10412
  draft.questions.length,
10084
10413
  " of this table's ",
10085
10414
  fields.data?.length ?? 0,
@@ -10090,10 +10419,10 @@ function FormBuilder({ tableId, seed, activeFormId, onActiveForm, className }) {
10090
10419
  const index = draft.questions.findIndex((q) => q.field === field.key);
10091
10420
  const asked = index >= 0;
10092
10421
  const question = asked ? draft.questions[index] : null;
10093
- return /* @__PURE__ */ jsxs34("div", { className: "rounded border px-2 py-1.5", children: [
10094
- /* @__PURE__ */ jsxs34("div", { className: "flex items-center gap-2", children: [
10095
- /* @__PURE__ */ jsx38(
10096
- Checkbox5,
10422
+ return /* @__PURE__ */ jsxs35("div", { className: "rounded border px-2 py-1.5", children: [
10423
+ /* @__PURE__ */ jsxs35("div", { className: "flex items-center gap-2", children: [
10424
+ /* @__PURE__ */ jsx39(
10425
+ Checkbox6,
10097
10426
  {
10098
10427
  checked: asked,
10099
10428
  "aria-label": `Ask for ${fieldName(field)}`,
@@ -10102,13 +10431,13 @@ function FormBuilder({ tableId, seed, activeFormId, onActiveForm, className }) {
10102
10431
  })
10103
10432
  }
10104
10433
  ),
10105
- /* @__PURE__ */ jsx38("span", { className: "text-xs font-medium", children: fieldName(field) }),
10106
- /* @__PURE__ */ jsx38("span", { className: "text-xs text-muted-foreground", children: field.key }),
10107
- field.required ? /* @__PURE__ */ jsx38("span", { className: "text-xs text-destructive", children: "required" }) : null
10434
+ /* @__PURE__ */ jsx39("span", { className: "text-xs font-medium", children: fieldName(field) }),
10435
+ /* @__PURE__ */ jsx39("span", { className: "text-xs text-muted-foreground", children: field.key }),
10436
+ field.required ? /* @__PURE__ */ jsx39("span", { className: "text-xs text-destructive", children: "required" }) : null
10108
10437
  ] }),
10109
- asked && question ? /* @__PURE__ */ jsxs34("div", { className: "mt-1.5 grid grid-cols-2 gap-2 pl-6", children: [
10110
- /* @__PURE__ */ jsx38(
10111
- BasicInput10,
10438
+ asked && question ? /* @__PURE__ */ jsxs35("div", { className: "mt-1.5 grid grid-cols-2 gap-2 pl-6", children: [
10439
+ /* @__PURE__ */ jsx39(
10440
+ BasicInput11,
10112
10441
  {
10113
10442
  className: "h-8 text-xs",
10114
10443
  placeholder: fieldName(field),
@@ -10117,8 +10446,8 @@ function FormBuilder({ tableId, seed, activeFormId, onActiveForm, className }) {
10117
10446
  onChange: (e) => patchQuestion(index, { ask: e.target.value || null })
10118
10447
  }
10119
10448
  ),
10120
- /* @__PURE__ */ jsx38(
10121
- BasicInput10,
10449
+ /* @__PURE__ */ jsx39(
10450
+ BasicInput11,
10122
10451
  {
10123
10452
  className: "h-8 text-xs",
10124
10453
  placeholder: "Help text",
@@ -10127,7 +10456,7 @@ function FormBuilder({ tableId, seed, activeFormId, onActiveForm, className }) {
10127
10456
  onChange: (e) => patchQuestion(index, { help: e.target.value || null })
10128
10457
  }
10129
10458
  ),
10130
- /* @__PURE__ */ jsx38(
10459
+ /* @__PURE__ */ jsx39(
10131
10460
  Condition,
10132
10461
  {
10133
10462
  question,
@@ -10138,7 +10467,7 @@ function FormBuilder({ tableId, seed, activeFormId, onActiveForm, className }) {
10138
10467
  ] }) : null
10139
10468
  ] }, field.id);
10140
10469
  }),
10141
- draft.questions.filter((q) => !byKey.has(q.field)).map((q) => /* @__PURE__ */ jsxs34("p", { className: "text-xs text-destructive", children: [
10470
+ draft.questions.filter((q) => !byKey.has(q.field)).map((q) => /* @__PURE__ */ jsxs35("p", { className: "text-xs text-destructive", children: [
10142
10471
  'This form asks for "',
10143
10472
  q.field,
10144
10473
  '" and this table has no such Field. It is shown here rather than quietly dropped.'
@@ -10146,9 +10475,9 @@ function FormBuilder({ tableId, seed, activeFormId, onActiveForm, className }) {
10146
10475
  ] })
10147
10476
  ] })
10148
10477
  ] }),
10149
- draft ? /* @__PURE__ */ jsxs34("aside", { className: "w-96 shrink-0 overflow-auto rounded border p-2", children: [
10150
- /* @__PURE__ */ jsx38("h4", { className: "px-1 text-sm font-medium", children: draft.name }),
10151
- /* @__PURE__ */ jsx38(FormRunner, { form: draft, preview: true })
10478
+ draft ? /* @__PURE__ */ jsxs35("aside", { className: "w-96 shrink-0 overflow-auto rounded border p-2", children: [
10479
+ /* @__PURE__ */ jsx39("h4", { className: "px-1 text-sm font-medium", children: draft.name }),
10480
+ /* @__PURE__ */ jsx39(FormRunner, { form: draft, preview: true })
10152
10481
  ] }) : null
10153
10482
  ] });
10154
10483
  }
@@ -10164,10 +10493,10 @@ function PublishRow({
10164
10493
  onCopy
10165
10494
  }) {
10166
10495
  const open = state === "open";
10167
- return /* @__PURE__ */ jsxs34("div", { className: "flex flex-col gap-1.5 rounded border px-2.5 py-2", children: [
10168
- /* @__PURE__ */ jsxs34("div", { className: "flex flex-wrap items-center gap-2", children: [
10169
- mayPublish ? /* @__PURE__ */ jsx38(
10170
- Button31,
10496
+ return /* @__PURE__ */ jsxs35("div", { className: "flex flex-col gap-1.5 rounded border px-2.5 py-2", children: [
10497
+ /* @__PURE__ */ jsxs35("div", { className: "flex flex-wrap items-center gap-2", children: [
10498
+ mayPublish ? /* @__PURE__ */ jsx39(
10499
+ Button32,
10171
10500
  {
10172
10501
  size: "sm",
10173
10502
  variant: open ? "ghost" : "default",
@@ -10176,8 +10505,8 @@ function PublishRow({
10176
10505
  children: busy ? "\u2026" : open ? "Unpublish" : "Publish"
10177
10506
  }
10178
10507
  ) : null,
10179
- state !== "draft" ? /* @__PURE__ */ jsxs34(Fragment17, { children: [
10180
- /* @__PURE__ */ jsx38(
10508
+ state !== "draft" ? /* @__PURE__ */ jsxs35(Fragment18, { children: [
10509
+ /* @__PURE__ */ jsx39(
10181
10510
  "a",
10182
10511
  {
10183
10512
  href: url,
@@ -10187,12 +10516,12 @@ function PublishRow({
10187
10516
  children: url
10188
10517
  }
10189
10518
  ),
10190
- /* @__PURE__ */ jsx38(Button31, { size: "sm", variant: "outline", onClick: () => onCopy(url), children: copied ? "Copied" : "Copy link" })
10519
+ /* @__PURE__ */ jsx39(Button32, { size: "sm", variant: "outline", onClick: () => onCopy(url), children: copied ? "Copied" : "Copy link" })
10191
10520
  ] }) : null
10192
10521
  ] }),
10193
- /* @__PURE__ */ jsx38("p", { className: "text-xs text-muted-foreground", children: FORM_STATE_WORDS[state] }),
10194
- !mayPublish ? /* @__PURE__ */ jsx38("p", { className: "text-xs text-muted-foreground", children: why }) : null,
10195
- shownUrl ? /* @__PURE__ */ jsxs34("p", { className: "break-all rounded border border-dashed px-2 py-1 text-xs", children: [
10522
+ /* @__PURE__ */ jsx39("p", { className: "text-xs text-muted-foreground", children: FORM_STATE_WORDS[state] }),
10523
+ !mayPublish ? /* @__PURE__ */ jsx39("p", { className: "text-xs text-muted-foreground", children: why }) : null,
10524
+ shownUrl ? /* @__PURE__ */ jsxs35("p", { className: "break-all rounded border border-dashed px-2 py-1 text-xs", children: [
10196
10525
  "This browser would not let the page copy for you, so here it is to copy by hand: ",
10197
10526
  shownUrl
10198
10527
  ] }) : null
@@ -10203,7 +10532,7 @@ function Condition({
10203
10532
  fieldKeys,
10204
10533
  onChange
10205
10534
  }) {
10206
- return /* @__PURE__ */ jsx38(
10535
+ return /* @__PURE__ */ jsx39(
10207
10536
  ConditionRow,
10208
10537
  {
10209
10538
  lead: "Ask only when",
@@ -10301,17 +10630,17 @@ function groupLabel(groups) {
10301
10630
 
10302
10631
  // src/chartFrame.tsx
10303
10632
  import {
10304
- useEffect as useEffect25,
10633
+ useEffect as useEffect26,
10305
10634
  useId,
10306
10635
  useRef as useRef10,
10307
- useState as useState35
10636
+ useState as useState36
10308
10637
  } from "react";
10309
- import { cn as cn33 } from "@ai-matrx/design-system";
10310
- import { jsx as jsx39, jsxs as jsxs35 } from "react/jsx-runtime";
10638
+ import { cn as cn34 } from "@ai-matrx/design-system";
10639
+ import { jsx as jsx40, jsxs as jsxs36 } from "react/jsx-runtime";
10311
10640
  function useMeasuredWidth(fallback = 480) {
10312
10641
  const ref = useRef10(null);
10313
- const [width, setWidth] = useState35(fallback);
10314
- useEffect25(() => {
10642
+ const [width, setWidth] = useState36(fallback);
10643
+ useEffect26(() => {
10315
10644
  const node = ref.current;
10316
10645
  if (!node) return;
10317
10646
  const apply = () => {
@@ -10342,13 +10671,13 @@ function ChartFrame({
10342
10671
  }) {
10343
10672
  const [ref, width] = useMeasuredWidth();
10344
10673
  const chartId = `records-chart-${useId().replace(/:/g, "")}`;
10345
- return /* @__PURE__ */ jsx39(
10674
+ return /* @__PURE__ */ jsx40(
10346
10675
  "div",
10347
10676
  {
10348
10677
  ref,
10349
10678
  "data-chart": chartId,
10350
10679
  style: { height, ...chartVariables(config) },
10351
- className: cn33(
10680
+ className: cn34(
10352
10681
  "w-full min-w-0 overflow-hidden text-xs [&_.recharts-cartesian-axis-tick_text]:fill-muted-foreground [&_.recharts-cartesian-grid_line]:stroke-border/50 [&_.recharts-curve.recharts-tooltip-cursor]:stroke-border [&_.recharts-layer]:outline-none [&_.recharts-rectangle.recharts-tooltip-cursor]:fill-muted [&_.recharts-sector]:outline-none [&_.recharts-surface]:outline-none",
10353
10682
  className
10354
10683
  ),
@@ -10364,27 +10693,27 @@ function ChartTooltipContent({
10364
10693
  className
10365
10694
  }) {
10366
10695
  if (!active || !payload || payload.length === 0) return null;
10367
- return /* @__PURE__ */ jsxs35(
10696
+ return /* @__PURE__ */ jsxs36(
10368
10697
  "div",
10369
10698
  {
10370
- className: cn33(
10699
+ className: cn34(
10371
10700
  "grid min-w-[8rem] items-start gap-1.5 rounded-lg border border-border/50 bg-background px-2.5 py-1.5 text-xs shadow-xl",
10372
10701
  className
10373
10702
  ),
10374
10703
  children: [
10375
- label ? /* @__PURE__ */ jsx39("div", { className: "font-medium", children: label }) : null,
10376
- /* @__PURE__ */ jsx39("div", { className: "grid gap-1.5", children: payload.map((entry, index) => {
10704
+ label ? /* @__PURE__ */ jsx40("div", { className: "font-medium", children: label }) : null,
10705
+ /* @__PURE__ */ jsx40("div", { className: "grid gap-1.5", children: payload.map((entry, index) => {
10377
10706
  const key = String(entry.dataKey ?? entry.name ?? index);
10378
- return /* @__PURE__ */ jsxs35("div", { className: "flex w-full items-center gap-2", children: [
10379
- /* @__PURE__ */ jsx39(
10707
+ return /* @__PURE__ */ jsxs36("div", { className: "flex w-full items-center gap-2", children: [
10708
+ /* @__PURE__ */ jsx40(
10380
10709
  "span",
10381
10710
  {
10382
10711
  className: "h-2.5 w-2.5 shrink-0 rounded-[2px] bg-[--color-swatch]",
10383
10712
  style: { "--color-swatch": entry.color }
10384
10713
  }
10385
10714
  ),
10386
- /* @__PURE__ */ jsx39("span", { className: "text-muted-foreground", children: config[key]?.label ?? key }),
10387
- /* @__PURE__ */ jsx39("span", { className: "ml-auto font-mono font-medium tabular-nums text-foreground", children: typeof entry.value === "number" ? entry.value.toLocaleString() : entry.value })
10715
+ /* @__PURE__ */ jsx40("span", { className: "text-muted-foreground", children: config[key]?.label ?? key }),
10716
+ /* @__PURE__ */ jsx40("span", { className: "ml-auto font-mono font-medium tabular-nums text-foreground", children: typeof entry.value === "number" ? entry.value.toLocaleString() : entry.value })
10388
10717
  ] }, key);
10389
10718
  }) })
10390
10719
  ]
@@ -10397,10 +10726,10 @@ function ChartLegendContent({
10397
10726
  className
10398
10727
  }) {
10399
10728
  if (!payload || payload.length === 0) return null;
10400
- return /* @__PURE__ */ jsx39("div", { className: cn33("flex flex-wrap items-center justify-center gap-3 pt-2", className), children: payload.map((entry, index) => {
10729
+ return /* @__PURE__ */ jsx40("div", { className: cn34("flex flex-wrap items-center justify-center gap-3 pt-2", className), children: payload.map((entry, index) => {
10401
10730
  const key = String(entry.dataKey ?? entry.value ?? index);
10402
- return /* @__PURE__ */ jsxs35("span", { className: "flex items-center gap-1.5 text-xs text-muted-foreground", children: [
10403
- /* @__PURE__ */ jsx39(
10731
+ return /* @__PURE__ */ jsxs36("span", { className: "flex items-center gap-1.5 text-xs text-muted-foreground", children: [
10732
+ /* @__PURE__ */ jsx40(
10404
10733
  "span",
10405
10734
  {
10406
10735
  className: "h-2 w-2 shrink-0 rounded-[2px] bg-[--color-swatch]",
@@ -10435,24 +10764,24 @@ function isSignatureField(field) {
10435
10764
  }
10436
10765
 
10437
10766
  // src/DocTemplate.tsx
10438
- import { useCallback as useCallback22, useEffect as useEffect26, useState as useState36 } from "react";
10439
- import { useFields as useFields17, useRecordsClient as useRecordsClient28, useTable as useTable14 } from "@ai-matrx/records/react";
10440
- import { BasicInput as BasicInput11, BasicTextarea as BasicTextarea6, Button as Button32, Label as Label6, Skeleton as Skeleton19, cn as cn34 } from "@ai-matrx/design-system";
10441
- import { jsx as jsx40, jsxs as jsxs36 } from "react/jsx-runtime";
10767
+ import { useCallback as useCallback23, useEffect as useEffect27, useState as useState37 } from "react";
10768
+ import { useFields as useFields17, useRecordsClient as useRecordsClient29, useTable as useTable14 } from "@ai-matrx/records/react";
10769
+ import { BasicInput as BasicInput12, BasicTextarea as BasicTextarea6, Button as Button33, Label as Label7, Skeleton as Skeleton20, cn as cn35 } from "@ai-matrx/design-system";
10770
+ import { jsx as jsx41, jsxs as jsxs37 } from "react/jsx-runtime";
10442
10771
  function DocTemplate({ tableId, seed, activeTemplateId, onActiveTemplate, className }) {
10443
- const client = useRecordsClient28();
10772
+ const client = useRecordsClient29();
10444
10773
  const claimSeed = useSeedGuard();
10445
10774
  const table = useTable14(tableId);
10446
10775
  const rights = useTableRights(table.data);
10447
10776
  const fields = useFields17(tableId);
10448
- const [templates, setTemplates] = useState36(null);
10449
- const [error, setError] = useState36(null);
10450
- const [activeId, setActiveId] = useState36(activeTemplateId ?? null);
10451
- const [draftName, setDraftName] = useState36("");
10452
- const [draftBody, setDraftBody] = useState36("");
10453
- const [unresolved, setUnresolved] = useState36([]);
10454
- const [saving, setSaving] = useState36(false);
10455
- const load = useCallback22(async () => {
10777
+ const [templates, setTemplates] = useState37(null);
10778
+ const [error, setError] = useState37(null);
10779
+ const [activeId, setActiveId] = useState37(activeTemplateId ?? null);
10780
+ const [draftName, setDraftName] = useState37("");
10781
+ const [draftBody, setDraftBody] = useState37("");
10782
+ const [unresolved, setUnresolved] = useState37([]);
10783
+ const [saving, setSaving] = useState37(false);
10784
+ const load = useCallback23(async () => {
10456
10785
  const held = await client.docTemplates({ table_id: tableId });
10457
10786
  if (!held.ok) {
10458
10787
  setError(held.error);
@@ -10483,10 +10812,10 @@ function DocTemplate({ tableId, seed, activeTemplateId, onActiveTemplate, classN
10483
10812
  setError(null);
10484
10813
  setTemplates(rows);
10485
10814
  }, [client, tableId, seed, fields.data]);
10486
- useEffect26(() => {
10815
+ useEffect27(() => {
10487
10816
  void load();
10488
10817
  }, [load]);
10489
- useEffect26(() => {
10818
+ useEffect27(() => {
10490
10819
  if (!templates || templates.length === 0) return;
10491
10820
  const chosen = templates.find((t) => t.id === (activeTemplateId ?? activeId)) ?? templates[0];
10492
10821
  if (chosen.id !== activeId) setActiveId(chosen.id);
@@ -10494,7 +10823,7 @@ function DocTemplate({ tableId, seed, activeTemplateId, onActiveTemplate, classN
10494
10823
  setDraftBody(chosen.body);
10495
10824
  onActiveTemplate?.(chosen);
10496
10825
  }, [templates, activeTemplateId]);
10497
- useEffect26(() => {
10826
+ useEffect27(() => {
10498
10827
  let cancelled = false;
10499
10828
  if (draftBody.trim() === "") {
10500
10829
  setUnresolved([]);
@@ -10524,25 +10853,25 @@ function DocTemplate({ tableId, seed, activeTemplateId, onActiveTemplate, classN
10524
10853
  setActiveId(saved.data);
10525
10854
  await load();
10526
10855
  }
10527
- if (fields.error) return /* @__PURE__ */ jsx40(RefusalNotice, { error: fields.error, className });
10856
+ if (fields.error) return /* @__PURE__ */ jsx41(RefusalNotice, { error: fields.error, className });
10528
10857
  if (error) {
10529
- return /* @__PURE__ */ jsx40(
10858
+ return /* @__PURE__ */ jsx41(
10530
10859
  RefusalNotice,
10531
10860
  {
10532
10861
  error,
10533
10862
  className,
10534
- actions: /* @__PURE__ */ jsx40(Button32, { size: "sm", variant: "ghost", onClick: () => void load(), children: "Try again" })
10863
+ actions: /* @__PURE__ */ jsx41(Button33, { size: "sm", variant: "ghost", onClick: () => void load(), children: "Try again" })
10535
10864
  }
10536
10865
  );
10537
10866
  }
10538
- if (templates === null || fields.loading) return /* @__PURE__ */ jsx40(Skeleton19, { className: cn34("h-64 w-full", className) });
10867
+ if (templates === null || fields.loading) return /* @__PURE__ */ jsx41(Skeleton20, { className: cn35("h-64 w-full", className) });
10539
10868
  if (!rights.admin) {
10540
- return /* @__PURE__ */ jsx40("p", { className: cn34("text-xs text-muted-foreground", className), children: rights.why("structure") });
10869
+ return /* @__PURE__ */ jsx41("p", { className: cn35("text-xs text-muted-foreground", className), children: rights.why("structure") });
10541
10870
  }
10542
- return /* @__PURE__ */ jsxs36("div", { className: cn34("flex flex-col gap-2", className), children: [
10543
- /* @__PURE__ */ jsxs36("div", { className: "flex items-center gap-1 overflow-x-auto", role: "group", "aria-label": "Templates", children: [
10544
- templates.map((t) => /* @__PURE__ */ jsxs36(
10545
- Button32,
10871
+ return /* @__PURE__ */ jsxs37("div", { className: cn35("flex flex-col gap-2", className), children: [
10872
+ /* @__PURE__ */ jsxs37("div", { className: "flex items-center gap-1 overflow-x-auto", role: "group", "aria-label": "Templates", children: [
10873
+ templates.map((t) => /* @__PURE__ */ jsxs37(
10874
+ Button33,
10546
10875
  {
10547
10876
  size: "sm",
10548
10877
  variant: t.id === activeId ? "secondary" : "ghost",
@@ -10554,7 +10883,7 @@ function DocTemplate({ tableId, seed, activeTemplateId, onActiveTemplate, classN
10554
10883
  },
10555
10884
  children: [
10556
10885
  t.name,
10557
- /* @__PURE__ */ jsxs36("span", { className: "ml-1 text-[10px] text-muted-foreground", children: [
10886
+ /* @__PURE__ */ jsxs37("span", { className: "ml-1 text-[10px] text-muted-foreground", children: [
10558
10887
  "v",
10559
10888
  t.template_version
10560
10889
  ] })
@@ -10562,8 +10891,8 @@ function DocTemplate({ tableId, seed, activeTemplateId, onActiveTemplate, classN
10562
10891
  },
10563
10892
  t.id
10564
10893
  )),
10565
- /* @__PURE__ */ jsx40(
10566
- Button32,
10894
+ /* @__PURE__ */ jsx41(
10895
+ Button33,
10567
10896
  {
10568
10897
  size: "sm",
10569
10898
  variant: "ghost",
@@ -10575,16 +10904,16 @@ function DocTemplate({ tableId, seed, activeTemplateId, onActiveTemplate, classN
10575
10904
  children: "New template"
10576
10905
  }
10577
10906
  ),
10578
- /* @__PURE__ */ jsx40(Button32, { size: "sm", className: "ml-auto", disabled: saving, onClick: () => void save(), children: saving ? "Saving\u2026" : "Save" })
10907
+ /* @__PURE__ */ jsx41(Button33, { size: "sm", className: "ml-auto", disabled: saving, onClick: () => void save(), children: saving ? "Saving\u2026" : "Save" })
10579
10908
  ] }),
10580
- /* @__PURE__ */ jsxs36("label", { className: "flex flex-col gap-1", children: [
10581
- /* @__PURE__ */ jsx40(Label6, { className: "text-xs font-medium", children: "Name" }),
10582
- /* @__PURE__ */ jsx40(BasicInput11, { value: draftName, onChange: (e) => setDraftName(e.target.value), placeholder: "Agreement" })
10909
+ /* @__PURE__ */ jsxs37("label", { className: "flex flex-col gap-1", children: [
10910
+ /* @__PURE__ */ jsx41(Label7, { className: "text-xs font-medium", children: "Name" }),
10911
+ /* @__PURE__ */ jsx41(BasicInput12, { value: draftName, onChange: (e) => setDraftName(e.target.value), placeholder: "Agreement" })
10583
10912
  ] }),
10584
- /* @__PURE__ */ jsxs36("div", { className: "flex flex-wrap items-center gap-1", children: [
10585
- /* @__PURE__ */ jsx40("span", { className: "text-xs text-muted-foreground", children: "Insert" }),
10586
- (fields.data ?? []).map((field) => /* @__PURE__ */ jsx40(
10587
- Button32,
10913
+ /* @__PURE__ */ jsxs37("div", { className: "flex flex-wrap items-center gap-1", children: [
10914
+ /* @__PURE__ */ jsx41("span", { className: "text-xs text-muted-foreground", children: "Insert" }),
10915
+ (fields.data ?? []).map((field) => /* @__PURE__ */ jsx41(
10916
+ Button33,
10588
10917
  {
10589
10918
  size: "sm",
10590
10919
  variant: "ghost",
@@ -10595,7 +10924,7 @@ function DocTemplate({ tableId, seed, activeTemplateId, onActiveTemplate, classN
10595
10924
  field.id
10596
10925
  ))
10597
10926
  ] }),
10598
- /* @__PURE__ */ jsx40(
10927
+ /* @__PURE__ */ jsx41(
10599
10928
  BasicTextarea6,
10600
10929
  {
10601
10930
  rows: 10,
@@ -10605,28 +10934,28 @@ function DocTemplate({ tableId, seed, activeTemplateId, onActiveTemplate, classN
10605
10934
  onChange: (e) => setDraftBody(e.target.value)
10606
10935
  }
10607
10936
  ),
10608
- unresolved.length > 0 ? /* @__PURE__ */ jsx40("ul", { className: "flex flex-col gap-0.5 rounded border border-dashed px-2 py-1 text-xs", children: unresolved.map((token) => /* @__PURE__ */ jsxs36("li", { className: "text-destructive", children: [
10609
- /* @__PURE__ */ jsx40("code", { children: token.raw }),
10937
+ unresolved.length > 0 ? /* @__PURE__ */ jsx41("ul", { className: "flex flex-col gap-0.5 rounded border border-dashed px-2 py-1 text-xs", children: unresolved.map((token) => /* @__PURE__ */ jsxs37("li", { className: "text-destructive", children: [
10938
+ /* @__PURE__ */ jsx41("code", { children: token.raw }),
10610
10939
  " \u2014 ",
10611
10940
  token.why
10612
- ] }, token.raw)) }) : draftBody.trim() !== "" ? /* @__PURE__ */ jsx40("p", { className: "text-xs text-muted-foreground", children: "Every token in this body points at a Field this table can answer." }) : null
10941
+ ] }, token.raw)) }) : draftBody.trim() !== "" ? /* @__PURE__ */ jsx41("p", { className: "text-xs text-muted-foreground", children: "Every token in this body points at a Field this table can answer." }) : null
10613
10942
  ] });
10614
10943
  }
10615
10944
 
10616
10945
  // src/DocRender.tsx
10617
- import { useCallback as useCallback23, useEffect as useEffect27, useRef as useRef11, useState as useState37 } from "react";
10618
- import { useRecordsClient as useRecordsClient29 } from "@ai-matrx/records/react";
10619
- import { Button as Button33, Skeleton as Skeleton20, cn as cn35 } from "@ai-matrx/design-system";
10620
- import { jsx as jsx41, jsxs as jsxs37 } from "react/jsx-runtime";
10946
+ import { useCallback as useCallback24, useEffect as useEffect28, useRef as useRef11, useState as useState38 } from "react";
10947
+ import { useRecordsClient as useRecordsClient30 } from "@ai-matrx/records/react";
10948
+ import { Button as Button34, Skeleton as Skeleton21, cn as cn36 } from "@ai-matrx/design-system";
10949
+ import { jsx as jsx42, jsxs as jsxs38 } from "react/jsx-runtime";
10621
10950
  function DocRender({ templateId, recordId, filename, onRendered, className }) {
10622
- const client = useRecordsClient29();
10623
- const [preview, setPreview] = useState37(null);
10624
- const [renders, setRenders] = useState37(null);
10625
- const [showing, setShowing] = useState37(null);
10626
- const [error, setError] = useState37(null);
10627
- const [busy, setBusy] = useState37(null);
10951
+ const client = useRecordsClient30();
10952
+ const [preview, setPreview] = useState38(null);
10953
+ const [renders, setRenders] = useState38(null);
10954
+ const [showing, setShowing] = useState38(null);
10955
+ const [error, setError] = useState38(null);
10956
+ const [busy, setBusy] = useState38(null);
10628
10957
  const paper = useRef11(null);
10629
- const load = useCallback23(async () => {
10958
+ const load = useCallback24(async () => {
10630
10959
  const [body, held] = await Promise.all([
10631
10960
  client.docRenderBody({ template_id: templateId, record_id: recordId }),
10632
10961
  client.docRenders({ record_id: recordId })
@@ -10643,7 +10972,7 @@ function DocRender({ templateId, recordId, filename, onRendered, className }) {
10643
10972
  setPreview(body.data);
10644
10973
  setRenders(held.data.filter((r) => r.template_id === templateId));
10645
10974
  }, [client, templateId, recordId]);
10646
- useEffect27(() => {
10975
+ useEffect28(() => {
10647
10976
  void load();
10648
10977
  }, [load]);
10649
10978
  async function freeze() {
@@ -10680,22 +11009,22 @@ function DocRender({ templateId, recordId, filename, onRendered, className }) {
10680
11009
  }
10681
11010
  }
10682
11011
  if (error) {
10683
- return /* @__PURE__ */ jsx41(
11012
+ return /* @__PURE__ */ jsx42(
10684
11013
  RefusalNotice,
10685
11014
  {
10686
11015
  error,
10687
11016
  className,
10688
- actions: /* @__PURE__ */ jsx41(Button33, { size: "sm", variant: "ghost", onClick: () => void load(), children: "Try again" })
11017
+ actions: /* @__PURE__ */ jsx42(Button34, { size: "sm", variant: "ghost", onClick: () => void load(), children: "Try again" })
10689
11018
  }
10690
11019
  );
10691
11020
  }
10692
- if (preview === null || renders === null) return /* @__PURE__ */ jsx41(Skeleton20, { className: cn35("h-64 w-full", className) });
11021
+ if (preview === null || renders === null) return /* @__PURE__ */ jsx42(Skeleton21, { className: cn36("h-64 w-full", className) });
10693
11022
  const frozen = showing ? renders.find((r) => r.id === showing) ?? null : null;
10694
11023
  const text = frozen ? frozen.body : preview;
10695
- return /* @__PURE__ */ jsxs37("div", { className: cn35("flex flex-col gap-2", className), children: [
10696
- /* @__PURE__ */ jsxs37("div", { className: "flex items-center gap-1 text-xs", children: [
10697
- /* @__PURE__ */ jsx41(
10698
- Button33,
11024
+ return /* @__PURE__ */ jsxs38("div", { className: cn36("flex flex-col gap-2", className), children: [
11025
+ /* @__PURE__ */ jsxs38("div", { className: "flex items-center gap-1 text-xs", children: [
11026
+ /* @__PURE__ */ jsx42(
11027
+ Button34,
10699
11028
  {
10700
11029
  size: "sm",
10701
11030
  variant: frozen ? "ghost" : "secondary",
@@ -10704,8 +11033,8 @@ function DocRender({ templateId, recordId, filename, onRendered, className }) {
10704
11033
  children: "As it reads now"
10705
11034
  }
10706
11035
  ),
10707
- renders.map((render) => /* @__PURE__ */ jsx41(
10708
- Button33,
11036
+ renders.map((render) => /* @__PURE__ */ jsx42(
11037
+ Button34,
10709
11038
  {
10710
11039
  size: "sm",
10711
11040
  variant: render.id === showing ? "secondary" : "ghost",
@@ -10716,33 +11045,33 @@ function DocRender({ templateId, recordId, filename, onRendered, className }) {
10716
11045
  },
10717
11046
  render.id
10718
11047
  )),
10719
- /* @__PURE__ */ jsxs37("span", { className: "ml-auto flex items-center gap-1", children: [
10720
- /* @__PURE__ */ jsx41(Button33, { size: "sm", disabled: busy !== null, onClick: () => void freeze(), children: busy === "freeze" ? "Freezing\u2026" : "Freeze this version" }),
10721
- /* @__PURE__ */ jsx41(Button33, { size: "sm", variant: "ghost", disabled: busy !== null, onClick: () => void toPdf(), children: busy === "pdf" ? "Making the PDF\u2026" : "PDF" })
11048
+ /* @__PURE__ */ jsxs38("span", { className: "ml-auto flex items-center gap-1", children: [
11049
+ /* @__PURE__ */ jsx42(Button34, { size: "sm", disabled: busy !== null, onClick: () => void freeze(), children: busy === "freeze" ? "Freezing\u2026" : "Freeze this version" }),
11050
+ /* @__PURE__ */ jsx42(Button34, { size: "sm", variant: "ghost", disabled: busy !== null, onClick: () => void toPdf(), children: busy === "pdf" ? "Making the PDF\u2026" : "PDF" })
10722
11051
  ] })
10723
11052
  ] }),
10724
- /* @__PURE__ */ jsx41("div", { ref: paper, className: "whitespace-pre-wrap rounded border bg-background p-4 text-sm text-foreground", children: text }),
10725
- /* @__PURE__ */ jsx41("p", { className: "text-xs text-muted-foreground", children: frozen ? `Frozen ${new Date(frozen.rendered_at).toLocaleString()} from template v${frozen.template_version}. Its text and its SHA-256 (${frozen.content_hash.slice(0, 12)}\u2026) are what any signature seals.` : "This is how the template reads against this record right now. Nothing has been frozen, so there is no version for anyone to sign yet." })
11053
+ /* @__PURE__ */ jsx42("div", { ref: paper, className: "whitespace-pre-wrap rounded border bg-background p-4 text-sm text-foreground", children: text }),
11054
+ /* @__PURE__ */ jsx42("p", { className: "text-xs text-muted-foreground", children: frozen ? `Frozen ${new Date(frozen.rendered_at).toLocaleString()} from template v${frozen.template_version}. Its text and its SHA-256 (${frozen.content_hash.slice(0, 12)}\u2026) are what any signature seals.` : "This is how the template reads against this record right now. Nothing has been frozen, so there is no version for anyone to sign yet." })
10726
11055
  ] });
10727
11056
  }
10728
11057
 
10729
11058
  // src/SignBlock.tsx
10730
- import { useCallback as useCallback24, useEffect as useEffect28, useState as useState38 } from "react";
10731
- import { useFields as useFields18, useRecordsClient as useRecordsClient30 } from "@ai-matrx/records/react";
10732
- import { BasicInput as BasicInput12, Button as Button34, Skeleton as Skeleton21, cn as cn36 } from "@ai-matrx/design-system";
11059
+ import { useCallback as useCallback25, useEffect as useEffect29, useState as useState39 } from "react";
11060
+ import { useFields as useFields18, useRecordsClient as useRecordsClient31 } from "@ai-matrx/records/react";
11061
+ import { BasicInput as BasicInput13, Button as Button35, Skeleton as Skeleton22, cn as cn37 } from "@ai-matrx/design-system";
10733
11062
  import { useTable as useTable15 } from "@ai-matrx/records/react";
10734
- import { jsx as jsx42, jsxs as jsxs38 } from "react/jsx-runtime";
11063
+ import { jsx as jsx43, jsxs as jsxs39 } from "react/jsx-runtime";
10735
11064
  function SignBlock({ tableId, recordId, render, className }) {
10736
- const client = useRecordsClient30();
11065
+ const client = useRecordsClient31();
10737
11066
  const table = useTable15(tableId);
10738
11067
  const rights = useTableRights(table.data);
10739
11068
  const fields = useFields18(tableId);
10740
- const [signatures, setSignatures] = useState38(null);
10741
- const [verdicts, setVerdicts] = useState38({});
10742
- const [error, setError] = useState38(null);
10743
- const [name, setName] = useState38("");
10744
- const [busy, setBusy] = useState38(false);
10745
- const load = useCallback24(async () => {
11069
+ const [signatures, setSignatures] = useState39(null);
11070
+ const [verdicts, setVerdicts] = useState39({});
11071
+ const [error, setError] = useState39(null);
11072
+ const [name, setName] = useState39("");
11073
+ const [busy, setBusy] = useState39(false);
11074
+ const load = useCallback25(async () => {
10746
11075
  const held = await client.docSignatures({ record_id: recordId });
10747
11076
  if (!held.ok) {
10748
11077
  setError(held.error);
@@ -10757,7 +11086,7 @@ function SignBlock({ tableId, recordId, render, className }) {
10757
11086
  }
10758
11087
  setVerdicts(answers);
10759
11088
  }, [client, recordId]);
10760
- useEffect28(() => {
11089
+ useEffect29(() => {
10761
11090
  void load();
10762
11091
  }, [load]);
10763
11092
  async function sign(field) {
@@ -10777,58 +11106,58 @@ function SignBlock({ tableId, recordId, render, className }) {
10777
11106
  setName("");
10778
11107
  await load();
10779
11108
  }
10780
- if (fields.error) return /* @__PURE__ */ jsx42(RefusalNotice, { error: fields.error, className });
11109
+ if (fields.error) return /* @__PURE__ */ jsx43(RefusalNotice, { error: fields.error, className });
10781
11110
  if (error) {
10782
- return /* @__PURE__ */ jsx42(
11111
+ return /* @__PURE__ */ jsx43(
10783
11112
  RefusalNotice,
10784
11113
  {
10785
11114
  error,
10786
11115
  className,
10787
- actions: /* @__PURE__ */ jsx42(Button34, { size: "sm", variant: "ghost", onClick: () => void load(), children: "Try again" })
11116
+ actions: /* @__PURE__ */ jsx43(Button35, { size: "sm", variant: "ghost", onClick: () => void load(), children: "Try again" })
10788
11117
  }
10789
11118
  );
10790
11119
  }
10791
- if (fields.loading || signatures === null) return /* @__PURE__ */ jsx42(Skeleton21, { className: cn36("h-32 w-full", className) });
11120
+ if (fields.loading || signatures === null) return /* @__PURE__ */ jsx43(Skeleton22, { className: cn37("h-32 w-full", className) });
10792
11121
  const signable = (fields.data ?? []).filter(isSignatureField);
10793
- return /* @__PURE__ */ jsxs38("div", { className: cn36("flex flex-col gap-2", className), children: [
10794
- /* @__PURE__ */ jsxs38("div", { className: "flex items-center gap-2 text-xs text-muted-foreground", children: [
10795
- /* @__PURE__ */ jsx42("span", { className: "font-medium text-foreground", children: "Signatures" }),
10796
- /* @__PURE__ */ jsxs38("span", { children: [
11122
+ return /* @__PURE__ */ jsxs39("div", { className: cn37("flex flex-col gap-2", className), children: [
11123
+ /* @__PURE__ */ jsxs39("div", { className: "flex items-center gap-2 text-xs text-muted-foreground", children: [
11124
+ /* @__PURE__ */ jsx43("span", { className: "font-medium text-foreground", children: "Signatures" }),
11125
+ /* @__PURE__ */ jsxs39("span", { children: [
10797
11126
  signatures.length,
10798
11127
  " on this record"
10799
11128
  ] })
10800
11129
  ] }),
10801
- signatures.length > 0 ? /* @__PURE__ */ jsx42("ul", { className: "flex flex-col gap-1", children: signatures.map((signature) => {
11130
+ signatures.length > 0 ? /* @__PURE__ */ jsx43("ul", { className: "flex flex-col gap-1", children: signatures.map((signature) => {
10802
11131
  const verdict = verdicts[signature.id];
10803
11132
  const intact = typeof verdict === "object" && verdict !== null ? verdict.intact : void 0;
10804
- return /* @__PURE__ */ jsxs38("li", { className: "rounded border px-2 py-1.5 text-xs", children: [
10805
- /* @__PURE__ */ jsxs38("div", { className: "flex items-center gap-2", children: [
10806
- /* @__PURE__ */ jsx42("span", { className: "font-medium", children: signature.signer_name }),
10807
- /* @__PURE__ */ jsx42("span", { className: "text-muted-foreground", children: signature.field_key }),
10808
- /* @__PURE__ */ jsx42("span", { className: "text-muted-foreground", children: new Date(signature.signed_at).toLocaleString() }),
10809
- /* @__PURE__ */ jsxs38("span", { className: "ml-auto font-mono text-[10px] text-muted-foreground", children: [
11133
+ return /* @__PURE__ */ jsxs39("li", { className: "rounded border px-2 py-1.5 text-xs", children: [
11134
+ /* @__PURE__ */ jsxs39("div", { className: "flex items-center gap-2", children: [
11135
+ /* @__PURE__ */ jsx43("span", { className: "font-medium", children: signature.signer_name }),
11136
+ /* @__PURE__ */ jsx43("span", { className: "text-muted-foreground", children: signature.field_key }),
11137
+ /* @__PURE__ */ jsx43("span", { className: "text-muted-foreground", children: new Date(signature.signed_at).toLocaleString() }),
11138
+ /* @__PURE__ */ jsxs39("span", { className: "ml-auto font-mono text-[10px] text-muted-foreground", children: [
10810
11139
  "v",
10811
11140
  signature.document_version,
10812
11141
  " \xB7 ",
10813
11142
  signature.document_hash.slice(0, 12)
10814
11143
  ] })
10815
11144
  ] }),
10816
- /* @__PURE__ */ jsx42("p", { className: cn36("mt-0.5", intact === false ? "text-destructive" : "text-muted-foreground"), children: intact === true ? "The store says what was signed is still exactly what is there." : intact === false ? typeof verdict === "object" && verdict !== null && "reason" in verdict ? String(verdict.reason) : "The store says what is there no longer matches what was signed." : String(verdict ?? "") })
11145
+ /* @__PURE__ */ jsx43("p", { className: cn37("mt-0.5", intact === false ? "text-destructive" : "text-muted-foreground"), children: intact === true ? "The store says what was signed is still exactly what is there." : intact === false ? typeof verdict === "object" && verdict !== null && "reason" in verdict ? String(verdict.reason) : "The store says what is there no longer matches what was signed." : String(verdict ?? "") })
10817
11146
  ] }, signature.id);
10818
11147
  }) }) : null,
10819
- !render ? /* @__PURE__ */ jsx42("p", { className: "text-xs text-muted-foreground", children: 'Nothing has been frozen yet, so there is no version to sign. A signature seals one exact document version and its hash \u2014 "the document as it reads today" is not something anyone can agree to.' }) : signable.length === 0 ? /* @__PURE__ */ jsxs38("p", { className: "text-xs text-muted-foreground", children: [
11148
+ !render ? /* @__PURE__ */ jsx43("p", { className: "text-xs text-muted-foreground", children: 'Nothing has been frozen yet, so there is no version to sign. A signature seals one exact document version and its hash \u2014 "the document as it reads today" is not something anyone can agree to.' }) : signable.length === 0 ? /* @__PURE__ */ jsxs39("p", { className: "text-xs text-muted-foreground", children: [
10820
11149
  "This table has no signature Field, so there is nowhere to write a signature. A signature is a Value on a text Field whose format is ",
10821
- /* @__PURE__ */ jsx42("code", { children: "signature" }),
11150
+ /* @__PURE__ */ jsx43("code", { children: "signature" }),
10822
11151
  " \u2014 add one and the signing appears."
10823
- ] }) : !rights.write ? /* @__PURE__ */ jsx42("p", { className: "text-xs text-muted-foreground", children: rights.why("write") }) : /* @__PURE__ */ jsx42("div", { className: "flex flex-col gap-1", children: signable.map((field) => {
11152
+ ] }) : !rights.write ? /* @__PURE__ */ jsx43("p", { className: "text-xs text-muted-foreground", children: rights.why("write") }) : /* @__PURE__ */ jsx43("div", { className: "flex flex-col gap-1", children: signable.map((field) => {
10824
11153
  const already = signatures.find((s) => s.field_key === field.key);
10825
11154
  if (already) {
10826
- return /* @__PURE__ */ jsxs38("p", { className: "text-xs text-muted-foreground", children: [
11155
+ return /* @__PURE__ */ jsxs39("p", { className: "text-xs text-muted-foreground", children: [
10827
11156
  fieldName(field),
10828
11157
  " is signed, and a signature is immutable once made. A further agreement is a further Field with its own signature, or a further version with its own seal."
10829
11158
  ] }, field.id);
10830
11159
  }
10831
- return /* @__PURE__ */ jsxs38(
11160
+ return /* @__PURE__ */ jsxs39(
10832
11161
  "form",
10833
11162
  {
10834
11163
  className: "flex items-center gap-1",
@@ -10837,8 +11166,8 @@ function SignBlock({ tableId, recordId, render, className }) {
10837
11166
  if (name.trim() !== "") void sign(field);
10838
11167
  },
10839
11168
  children: [
10840
- /* @__PURE__ */ jsx42(
10841
- BasicInput12,
11169
+ /* @__PURE__ */ jsx43(
11170
+ BasicInput13,
10842
11171
  {
10843
11172
  className: "h-8 w-56 text-xs",
10844
11173
  "aria-label": `Sign ${fieldName(field)}`,
@@ -10847,8 +11176,8 @@ function SignBlock({ tableId, recordId, render, className }) {
10847
11176
  onChange: (e) => setName(e.target.value)
10848
11177
  }
10849
11178
  ),
10850
- /* @__PURE__ */ jsx42(Button34, { size: "sm", type: "submit", disabled: busy || name.trim() === "", children: "Sign" }),
10851
- /* @__PURE__ */ jsxs38("span", { className: "text-xs text-muted-foreground", children: [
11179
+ /* @__PURE__ */ jsx43(Button35, { size: "sm", type: "submit", disabled: busy || name.trim() === "", children: "Sign" }),
11180
+ /* @__PURE__ */ jsxs39("span", { className: "text-xs text-muted-foreground", children: [
10852
11181
  "seals version ",
10853
11182
  render.template_version,
10854
11183
  " \xB7 ",
@@ -10864,32 +11193,32 @@ function SignBlock({ tableId, recordId, render, className }) {
10864
11193
  }
10865
11194
 
10866
11195
  // src/NotifyRuleEditor.tsx
10867
- import { useCallback as useCallback25, useEffect as useEffect29, useState as useState39 } from "react";
10868
- import { useRecordsClient as useRecordsClient31, useTable as useTable16 } from "@ai-matrx/records/react";
10869
- import { Button as Button35, Skeleton as Skeleton22, cn as cn37 } from "@ai-matrx/design-system";
10870
- import { jsx as jsx43, jsxs as jsxs39 } from "react/jsx-runtime";
10871
- var CADENCE_WORDS = {
11196
+ import { useCallback as useCallback26, useEffect as useEffect30, useState as useState40 } from "react";
11197
+ import { useRecordsClient as useRecordsClient32, useTable as useTable16 } from "@ai-matrx/records/react";
11198
+ import { Button as Button36, Skeleton as Skeleton23, cn as cn38 } from "@ai-matrx/design-system";
11199
+ import { jsx as jsx44, jsxs as jsxs40 } from "react/jsx-runtime";
11200
+ var CADENCE_WORDS2 = {
10872
11201
  instant: "as it happens",
10873
11202
  hourly: "hourly summary",
10874
11203
  daily: "daily summary",
10875
11204
  weekly: "weekly summary"
10876
11205
  };
10877
- var CHANNEL_WORDS2 = {
11206
+ var CHANNEL_WORDS3 = {
10878
11207
  in_app: "in the app",
10879
11208
  email: "by email",
10880
11209
  sms: "by text"
10881
11210
  };
10882
11211
  function NotifyRuleEditor({ tableId, seed, className }) {
10883
- const client = useRecordsClient31();
11212
+ const client = useRecordsClient32();
10884
11213
  const host = useRecordsUi();
10885
11214
  const table = useTable16(tableId);
10886
11215
  const rights = useTableRights(table.data);
10887
- const [subscriptions, setSubscriptions] = useState39(null);
10888
- const [cadences, setCadences] = useState39([]);
10889
- const [views, setViews] = useState39(null);
10890
- const [error, setError] = useState39(null);
10891
- const [busy, setBusy] = useState39(false);
10892
- const load = useCallback25(async () => {
11216
+ const [subscriptions, setSubscriptions] = useState40(null);
11217
+ const [cadences, setCadences] = useState40([]);
11218
+ const [views, setViews] = useState40(null);
11219
+ const [error, setError] = useState40(null);
11220
+ const [busy, setBusy] = useState40(false);
11221
+ const load = useCallback26(async () => {
10893
11222
  const [held, offered] = await Promise.all([
10894
11223
  // THE PERSON'S OWN DOOR, not the notifier's. It answers what is addressed
10895
11224
  // to them plus — only where they hold admin on this Table — anyone's over
@@ -10909,10 +11238,10 @@ function NotifyRuleEditor({ tableId, seed, className }) {
10909
11238
  if (host.savedViews) setViews(await host.savedViews());
10910
11239
  else setViews(null);
10911
11240
  }, [client, host, tableId]);
10912
- useEffect29(() => {
11241
+ useEffect30(() => {
10913
11242
  void load();
10914
11243
  }, [load]);
10915
- const write = useCallback25(
11244
+ const write = useCallback26(
10916
11245
  async (spec) => {
10917
11246
  const declared = await client.subscriptionDeclare({
10918
11247
  table_id: tableId,
@@ -10937,7 +11266,7 @@ function NotifyRuleEditor({ tableId, seed, className }) {
10937
11266
  },
10938
11267
  [client, tableId]
10939
11268
  );
10940
- useEffect29(() => {
11269
+ useEffect30(() => {
10941
11270
  if (!subscriptions || !seed || seed.length === 0) return;
10942
11271
  const missing = seed.filter((s) => !subscriptions.some((held) => held.name === s.name));
10943
11272
  if (missing.length === 0) return;
@@ -10951,7 +11280,7 @@ function NotifyRuleEditor({ tableId, seed, className }) {
10951
11280
  async function subscribe(viewId, viewName, cadence, channel, schedule, quiet) {
10952
11281
  setBusy(true);
10953
11282
  const ok = await write({
10954
- name: `${viewName} \u2014 ${CADENCE_WORDS[cadence] ?? cadence}`,
11283
+ name: `${viewName} \u2014 ${CADENCE_WORDS2[cadence] ?? cadence}`,
10955
11284
  savedViewId: viewId,
10956
11285
  cadence,
10957
11286
  channel,
@@ -10972,45 +11301,45 @@ function NotifyRuleEditor({ tableId, seed, className }) {
10972
11301
  await load();
10973
11302
  }
10974
11303
  if (error) {
10975
- return /* @__PURE__ */ jsx43(
11304
+ return /* @__PURE__ */ jsx44(
10976
11305
  RefusalNotice,
10977
11306
  {
10978
11307
  error,
10979
11308
  className,
10980
- actions: /* @__PURE__ */ jsx43(Button35, { size: "sm", variant: "ghost", onClick: () => void load(), children: "Try again" })
11309
+ actions: /* @__PURE__ */ jsx44(Button36, { size: "sm", variant: "ghost", onClick: () => void load(), children: "Try again" })
10981
11310
  }
10982
11311
  );
10983
11312
  }
10984
- if (subscriptions === null) return /* @__PURE__ */ jsx43(Skeleton22, { className: cn37("h-40 w-full", className) });
10985
- return /* @__PURE__ */ jsxs39("div", { className: cn37("flex flex-col gap-2", className), children: [
10986
- /* @__PURE__ */ jsxs39("div", { className: "flex items-center gap-2 text-xs text-muted-foreground", children: [
10987
- /* @__PURE__ */ jsx43("span", { className: "font-medium text-foreground", children: "Tell me when" }),
10988
- /* @__PURE__ */ jsxs39("span", { children: [
11313
+ if (subscriptions === null) return /* @__PURE__ */ jsx44(Skeleton23, { className: cn38("h-40 w-full", className) });
11314
+ return /* @__PURE__ */ jsxs40("div", { className: cn38("flex flex-col gap-2", className), children: [
11315
+ /* @__PURE__ */ jsxs40("div", { className: "flex items-center gap-2 text-xs text-muted-foreground", children: [
11316
+ /* @__PURE__ */ jsx44("span", { className: "font-medium text-foreground", children: "Tell me when" }),
11317
+ /* @__PURE__ */ jsxs40("span", { children: [
10989
11318
  subscriptions.length,
10990
11319
  " subscription",
10991
11320
  subscriptions.length === 1 ? "" : "s"
10992
11321
  ] })
10993
11322
  ] }),
10994
- subscriptions.length === 0 ? /* @__PURE__ */ jsx43("p", { className: "text-xs text-muted-foreground", children: "Nothing is telling anyone about this yet. A subscription is a Rule over a saved view, so what you are told about is exactly what that view holds." }) : /* @__PURE__ */ jsx43("ul", { className: "flex flex-col gap-1", children: subscriptions.map((held) => /* @__PURE__ */ jsxs39("li", { className: "flex items-center gap-2 rounded border px-2 py-1.5 text-xs", children: [
10995
- /* @__PURE__ */ jsx43("span", { className: "truncate font-medium", children: held.name }),
10996
- /* @__PURE__ */ jsx43("span", { className: "text-muted-foreground", children: CADENCE_WORDS[held.cadence] ?? held.cadence }),
10997
- /* @__PURE__ */ jsx43("span", { className: "text-muted-foreground", children: CHANNEL_WORDS2[held.channel] ?? held.channel }),
10998
- held.schedule ? /* @__PURE__ */ jsx43("span", { className: "text-muted-foreground", children: held.schedule }) : null,
10999
- held.next_digest_at ? /* @__PURE__ */ jsxs39("span", { className: "text-muted-foreground", children: [
11323
+ subscriptions.length === 0 ? /* @__PURE__ */ jsx44("p", { className: "text-xs text-muted-foreground", children: "Nothing is telling anyone about this yet. A subscription is a Rule over a saved view, so what you are told about is exactly what that view holds." }) : /* @__PURE__ */ jsx44("ul", { className: "flex flex-col gap-1", children: subscriptions.map((held) => /* @__PURE__ */ jsxs40("li", { className: "flex items-center gap-2 rounded border px-2 py-1.5 text-xs", children: [
11324
+ /* @__PURE__ */ jsx44("span", { className: "truncate font-medium", children: held.name }),
11325
+ /* @__PURE__ */ jsx44("span", { className: "text-muted-foreground", children: CADENCE_WORDS2[held.cadence] ?? held.cadence }),
11326
+ /* @__PURE__ */ jsx44("span", { className: "text-muted-foreground", children: CHANNEL_WORDS3[held.channel] ?? held.channel }),
11327
+ held.schedule ? /* @__PURE__ */ jsx44("span", { className: "text-muted-foreground", children: held.schedule }) : null,
11328
+ held.next_digest_at ? /* @__PURE__ */ jsxs40("span", { className: "text-muted-foreground", children: [
11000
11329
  "next ",
11001
11330
  new Date(held.next_digest_at).toLocaleString()
11002
11331
  ] }) : null,
11003
- held.quiet_hours ? /* @__PURE__ */ jsxs39("span", { className: "text-muted-foreground", children: [
11332
+ held.quiet_hours ? /* @__PURE__ */ jsxs40("span", { className: "text-muted-foreground", children: [
11004
11333
  "quiet ",
11005
11334
  held.quiet_hours.start,
11006
11335
  "\u2013",
11007
11336
  held.quiet_hours.end
11008
11337
  ] }) : null,
11009
- held.recipient_user_id === null ? /* @__PURE__ */ jsx43("span", { className: "text-destructive", children: "This one has nobody to tell, so it fires at nobody." }) : null,
11010
- held.saved_view_id === null ? /* @__PURE__ */ jsx43("span", { className: "text-destructive", children: "This one names no view, so the store never admits a record to it." }) : null,
11011
- held.muted ? /* @__PURE__ */ jsx43("span", { className: "text-muted-foreground", children: "Switched off \u2014 it tells nobody until somebody switches it back on." }) : null,
11012
- rights.write ? /* @__PURE__ */ jsx43(
11013
- Button35,
11338
+ held.recipient_user_id === null ? /* @__PURE__ */ jsx44("span", { className: "text-destructive", children: "This one has nobody to tell, so it fires at nobody." }) : null,
11339
+ held.saved_view_id === null ? /* @__PURE__ */ jsx44("span", { className: "text-destructive", children: "This one names no view, so the store never admits a record to it." }) : null,
11340
+ held.muted ? /* @__PURE__ */ jsx44("span", { className: "text-muted-foreground", children: "Switched off \u2014 it tells nobody until somebody switches it back on." }) : null,
11341
+ rights.write ? /* @__PURE__ */ jsx44(
11342
+ Button36,
11014
11343
  {
11015
11344
  size: "sm",
11016
11345
  variant: "ghost",
@@ -11021,7 +11350,7 @@ function NotifyRuleEditor({ tableId, seed, className }) {
11021
11350
  }
11022
11351
  ) : null
11023
11352
  ] }, held.rule_id)) }),
11024
- views === null ? /* @__PURE__ */ jsx43("p", { className: "rounded border border-dashed px-2 py-1 text-xs text-muted-foreground", children: NO_SAVED_VIEWS_REASON }) : views.length === 0 ? /* @__PURE__ */ jsx43("p", { className: "text-xs text-muted-foreground", children: "This organization has no saved views yet, so there is nothing to subscribe to. Save a view first." }) : rights.write ? /* @__PURE__ */ jsxs39(
11353
+ views === null ? /* @__PURE__ */ jsx44("p", { className: "rounded border border-dashed px-2 py-1 text-xs text-muted-foreground", children: NO_SAVED_VIEWS_REASON }) : views.length === 0 ? /* @__PURE__ */ jsx44("p", { className: "text-xs text-muted-foreground", children: "This organization has no saved views yet, so there is nothing to subscribe to. Save a view first." }) : rights.write ? /* @__PURE__ */ jsxs40(
11025
11354
  "form",
11026
11355
  {
11027
11356
  className: "flex flex-wrap items-center gap-1",
@@ -11039,10 +11368,10 @@ function NotifyRuleEditor({ tableId, seed, className }) {
11039
11368
  if (view) void subscribe(view, name, cadence, channel, schedule, quiet);
11040
11369
  },
11041
11370
  children: [
11042
- /* @__PURE__ */ jsx43("select", { name: "view", "aria-label": "View to be told about", className: "h-7 rounded border bg-background px-1 text-xs", children: views.map((view) => /* @__PURE__ */ jsx43("option", { value: view.id, children: view.name }, view.id)) }),
11043
- /* @__PURE__ */ jsx43("select", { name: "cadence", "aria-label": "How often", className: "h-7 rounded border bg-background px-1 text-xs", children: (cadences.length > 0 ? cadences : ["instant"]).map((cadence) => /* @__PURE__ */ jsx43("option", { value: cadence, children: CADENCE_WORDS[cadence] ?? cadence }, cadence)) }),
11044
- /* @__PURE__ */ jsx43("select", { name: "channel", "aria-label": "Where it arrives", className: "h-7 rounded border bg-background px-1 text-xs", children: ["in_app", "email", "sms"].map((channel) => /* @__PURE__ */ jsx43("option", { value: channel, children: CHANNEL_WORDS2[channel] }, channel)) }),
11045
- /* @__PURE__ */ jsx43(
11371
+ /* @__PURE__ */ jsx44("select", { name: "view", "aria-label": "View to be told about", className: "h-7 rounded border bg-background px-1 text-xs", children: views.map((view) => /* @__PURE__ */ jsx44("option", { value: view.id, children: view.name }, view.id)) }),
11372
+ /* @__PURE__ */ jsx44("select", { name: "cadence", "aria-label": "How often", className: "h-7 rounded border bg-background px-1 text-xs", children: (cadences.length > 0 ? cadences : ["instant"]).map((cadence) => /* @__PURE__ */ jsx44("option", { value: cadence, children: CADENCE_WORDS2[cadence] ?? cadence }, cadence)) }),
11373
+ /* @__PURE__ */ jsx44("select", { name: "channel", "aria-label": "Where it arrives", className: "h-7 rounded border bg-background px-1 text-xs", children: ["in_app", "email", "sms"].map((channel) => /* @__PURE__ */ jsx44("option", { value: channel, children: CHANNEL_WORDS3[channel] }, channel)) }),
11374
+ /* @__PURE__ */ jsx44(
11046
11375
  "input",
11047
11376
  {
11048
11377
  name: "schedule",
@@ -11051,8 +11380,8 @@ function NotifyRuleEditor({ tableId, seed, className }) {
11051
11380
  className: "h-7 w-28 rounded border bg-background px-1 text-xs"
11052
11381
  }
11053
11382
  ),
11054
- /* @__PURE__ */ jsx43("span", { className: "text-xs text-muted-foreground", children: "not between" }),
11055
- /* @__PURE__ */ jsx43(
11383
+ /* @__PURE__ */ jsx44("span", { className: "text-xs text-muted-foreground", children: "not between" }),
11384
+ /* @__PURE__ */ jsx44(
11056
11385
  "input",
11057
11386
  {
11058
11387
  name: "quiet_start",
@@ -11061,8 +11390,8 @@ function NotifyRuleEditor({ tableId, seed, className }) {
11061
11390
  className: "h-7 rounded border bg-background px-1 text-xs"
11062
11391
  }
11063
11392
  ),
11064
- /* @__PURE__ */ jsx43("span", { className: "text-xs text-muted-foreground", children: "and" }),
11065
- /* @__PURE__ */ jsx43(
11393
+ /* @__PURE__ */ jsx44("span", { className: "text-xs text-muted-foreground", children: "and" }),
11394
+ /* @__PURE__ */ jsx44(
11066
11395
  "input",
11067
11396
  {
11068
11397
  name: "quiet_end",
@@ -11071,16 +11400,16 @@ function NotifyRuleEditor({ tableId, seed, className }) {
11071
11400
  className: "h-7 rounded border bg-background px-1 text-xs"
11072
11401
  }
11073
11402
  ),
11074
- /* @__PURE__ */ jsx43(Button35, { size: "sm", type: "submit", disabled: busy, children: "Tell me" }),
11075
- /* @__PURE__ */ jsx43("p", { className: "w-full text-xs text-muted-foreground", children: "A send inside your quiet hours is held until they end \u2014 it is never dropped. Leave the schedule blank for the store's own default of 08:00." })
11403
+ /* @__PURE__ */ jsx44(Button36, { size: "sm", type: "submit", disabled: busy, children: "Tell me" }),
11404
+ /* @__PURE__ */ jsx44("p", { className: "w-full text-xs text-muted-foreground", children: "A send inside your quiet hours is held until they end \u2014 it is never dropped. Leave the schedule blank for the store's own default of 08:00." })
11076
11405
  ]
11077
11406
  }
11078
- ) : /* @__PURE__ */ jsx43("p", { className: "text-xs text-muted-foreground", children: rights.why("write") })
11407
+ ) : /* @__PURE__ */ jsx44("p", { className: "text-xs text-muted-foreground", children: rights.why("write") })
11079
11408
  ] });
11080
11409
  }
11081
11410
 
11082
11411
  // src/ChartBlock.tsx
11083
- import { useMemo as useMemo25 } from "react";
11412
+ import { useMemo as useMemo26 } from "react";
11084
11413
  import {
11085
11414
  Bar,
11086
11415
  BarChart,
@@ -11099,8 +11428,8 @@ import {
11099
11428
  measureKey
11100
11429
  } from "@ai-matrx/records";
11101
11430
  import { mapPgError } from "@ai-matrx/records/core";
11102
- import { cn as cn38 } from "@ai-matrx/design-system";
11103
- import { Fragment as Fragment18, jsx as jsx44, jsxs as jsxs40 } from "react/jsx-runtime";
11431
+ import { cn as cn39 } from "@ai-matrx/design-system";
11432
+ import { Fragment as Fragment19, jsx as jsx45, jsxs as jsxs41 } from "react/jsx-runtime";
11104
11433
  function pretty(n) {
11105
11434
  return Number.isInteger(n) ? n.toLocaleString() : n.toLocaleString(void 0, { maximumFractionDigits: 2 });
11106
11435
  }
@@ -11120,7 +11449,7 @@ function ChartBlock({ block, subject, className }) {
11120
11449
  const measures = block.measures && block.measures.length > 0 ? block.measures : [{ op: "count" }];
11121
11450
  const primary = measureKey(measures[0]);
11122
11451
  const series = [primary];
11123
- const points = useMemo25(() => {
11452
+ const points = useMemo26(() => {
11124
11453
  if (kind === "stuck") return [];
11125
11454
  const rows = block.rows ?? [];
11126
11455
  return rows.map((row) => ({
@@ -11132,7 +11461,7 @@ function ChartBlock({ block, subject, className }) {
11132
11461
  n: row.row_count
11133
11462
  }));
11134
11463
  }, [block.rows, kind, series.join("|")]);
11135
- const config = useMemo25(() => {
11464
+ const config = useMemo26(() => {
11136
11465
  const out = {};
11137
11466
  series.forEach((key, index) => {
11138
11467
  out[key] = {
@@ -11156,18 +11485,18 @@ function ChartBlock({ block, subject, className }) {
11156
11485
  label: point.label === "All records" ? block.title : `${block.title} \xB7 ${point.label}`
11157
11486
  });
11158
11487
  } : null;
11159
- return /* @__PURE__ */ jsxs40("figure", { className: cn38("flex min-w-0 flex-col gap-1.5 rounded border p-2", className), children: [
11160
- /* @__PURE__ */ jsxs40("figcaption", { className: "flex items-baseline gap-2 text-xs", children: [
11161
- /* @__PURE__ */ jsx44("span", { className: "truncate font-medium", children: block.title || CHART_KIND_LABEL[kind] }),
11162
- /* @__PURE__ */ jsx44("span", { className: "ml-auto shrink-0 text-muted-foreground", children: kind === "stuck" ? `${block.days ?? 14} days` : primary.replace("_", " of ") }),
11163
- typeof block.ms === "number" ? /* @__PURE__ */ jsxs40("span", { className: "shrink-0 tabular-nums text-muted-foreground/70", title: "how long the store took", children: [
11488
+ return /* @__PURE__ */ jsxs41("figure", { className: cn39("flex min-w-0 flex-col gap-1.5 rounded border p-2", className), children: [
11489
+ /* @__PURE__ */ jsxs41("figcaption", { className: "flex items-baseline gap-2 text-xs", children: [
11490
+ /* @__PURE__ */ jsx45("span", { className: "truncate font-medium", children: block.title || CHART_KIND_LABEL[kind] }),
11491
+ /* @__PURE__ */ jsx45("span", { className: "ml-auto shrink-0 text-muted-foreground", children: kind === "stuck" ? `${block.days ?? 14} days` : primary.replace("_", " of ") }),
11492
+ typeof block.ms === "number" ? /* @__PURE__ */ jsxs41("span", { className: "shrink-0 tabular-nums text-muted-foreground/70", title: "how long the store took", children: [
11164
11493
  pretty(block.ms),
11165
11494
  " ms"
11166
11495
  ] }) : null
11167
11496
  ] }),
11168
- block.refused ? /* @__PURE__ */ jsx44(RefusalNotice, { error: asRefusal(block) }) : needs ? /* @__PURE__ */ jsx44("p", { className: "px-1 py-3 text-xs text-muted-foreground", children: needs }) : kind === "stuck" ? /* @__PURE__ */ jsx44(StuckList, { rows: block.rows ?? [] }) : points.length === 0 ? /* @__PURE__ */ jsx44("p", { className: "px-1 py-3 text-xs text-muted-foreground", children: "The store answered this question with no groups at all, so there is nothing to draw yet." }) : /* @__PURE__ */ jsx44(Fragment18, { children: kind === "number" ? /* @__PURE__ */ jsx44(BigNumber, { points, measure: primary, onDrill: drill }) : kind === "table" ? /* @__PURE__ */ jsx44(GroupTable, { points, series, onDrill: drill }) : /* @__PURE__ */ jsxs40(Fragment18, { children: [
11169
- /* @__PURE__ */ jsx44(Drawing, { kind, points, series, config, onDrill: drill }),
11170
- /* @__PURE__ */ jsx44(Values, { points, measure: primary, config, onDrill: drill })
11497
+ block.refused ? /* @__PURE__ */ jsx45(RefusalNotice, { error: asRefusal(block) }) : needs ? /* @__PURE__ */ jsx45("p", { className: "px-1 py-3 text-xs text-muted-foreground", children: needs }) : kind === "stuck" ? /* @__PURE__ */ jsx45(StuckList, { rows: block.rows ?? [] }) : points.length === 0 ? /* @__PURE__ */ jsx45("p", { className: "px-1 py-3 text-xs text-muted-foreground", children: "The store answered this question with no groups at all, so there is nothing to draw yet." }) : /* @__PURE__ */ jsx45(Fragment19, { children: kind === "number" ? /* @__PURE__ */ jsx45(BigNumber, { points, measure: primary, onDrill: drill }) : kind === "table" ? /* @__PURE__ */ jsx45(GroupTable, { points, series, onDrill: drill }) : /* @__PURE__ */ jsxs41(Fragment19, { children: [
11498
+ /* @__PURE__ */ jsx45(Drawing, { kind, points, series, config, onDrill: drill }),
11499
+ /* @__PURE__ */ jsx45(Values, { points, measure: primary, config, onDrill: drill })
11171
11500
  ] }) })
11172
11501
  ] });
11173
11502
  }
@@ -11178,16 +11507,16 @@ function BigNumber({
11178
11507
  }) {
11179
11508
  const total = points.reduce((sum, p) => sum + (p.values[measure] ?? 0), 0);
11180
11509
  const records = points.reduce((sum, p) => sum + p.n, 0);
11181
- const body = /* @__PURE__ */ jsxs40(Fragment18, { children: [
11182
- /* @__PURE__ */ jsx44("span", { className: "text-2xl font-semibold tabular-nums", children: pretty(total) }),
11183
- /* @__PURE__ */ jsxs40("span", { className: "text-xs text-muted-foreground", children: [
11510
+ const body = /* @__PURE__ */ jsxs41(Fragment19, { children: [
11511
+ /* @__PURE__ */ jsx45("span", { className: "text-2xl font-semibold tabular-nums", children: pretty(total) }),
11512
+ /* @__PURE__ */ jsxs41("span", { className: "text-xs text-muted-foreground", children: [
11184
11513
  "from ",
11185
11514
  records.toLocaleString(),
11186
11515
  " records"
11187
11516
  ] })
11188
11517
  ] });
11189
- if (!onDrill || !points[0]) return /* @__PURE__ */ jsx44("div", { className: "flex items-baseline gap-2 px-1 py-2", children: body });
11190
- return /* @__PURE__ */ jsx44(
11518
+ if (!onDrill || !points[0]) return /* @__PURE__ */ jsx45("div", { className: "flex items-baseline gap-2 px-1 py-2", children: body });
11519
+ return /* @__PURE__ */ jsx45(
11191
11520
  "button",
11192
11521
  {
11193
11522
  type: "button",
@@ -11203,19 +11532,19 @@ function Values({
11203
11532
  config,
11204
11533
  onDrill
11205
11534
  }) {
11206
- return /* @__PURE__ */ jsx44("ul", { className: "flex flex-wrap gap-x-2 gap-y-0.5", children: points.map((point) => {
11207
- const text = /* @__PURE__ */ jsxs40(Fragment18, { children: [
11208
- /* @__PURE__ */ jsx44(
11535
+ return /* @__PURE__ */ jsx45("ul", { className: "flex flex-wrap gap-x-2 gap-y-0.5", children: points.map((point) => {
11536
+ const text = /* @__PURE__ */ jsxs41(Fragment19, { children: [
11537
+ /* @__PURE__ */ jsx45(
11209
11538
  "span",
11210
11539
  {
11211
11540
  className: "h-2 w-2 shrink-0 rounded-[2px]",
11212
11541
  style: { background: String(config[point.label]?.color ?? SERIES_COLORS[0]) }
11213
11542
  }
11214
11543
  ),
11215
- /* @__PURE__ */ jsx44("span", { className: "truncate", children: point.label }),
11216
- /* @__PURE__ */ jsx44("span", { className: "tabular-nums text-muted-foreground", children: pretty(point.values[measure] ?? 0) })
11544
+ /* @__PURE__ */ jsx45("span", { className: "truncate", children: point.label }),
11545
+ /* @__PURE__ */ jsx45("span", { className: "tabular-nums text-muted-foreground", children: pretty(point.values[measure] ?? 0) })
11217
11546
  ] });
11218
- return /* @__PURE__ */ jsx44("li", { className: "min-w-0", children: onDrill ? /* @__PURE__ */ jsx44(
11547
+ return /* @__PURE__ */ jsx45("li", { className: "min-w-0", children: onDrill ? /* @__PURE__ */ jsx45(
11219
11548
  "button",
11220
11549
  {
11221
11550
  type: "button",
@@ -11223,7 +11552,7 @@ function Values({
11223
11552
  onClick: () => onDrill(point),
11224
11553
  children: text
11225
11554
  }
11226
- ) : /* @__PURE__ */ jsx44("span", { className: "flex min-w-0 items-center gap-1.5 px-1 py-0.5 text-xs", children: text }) }, point.label);
11555
+ ) : /* @__PURE__ */ jsx45("span", { className: "flex min-w-0 items-center gap-1.5 px-1 py-0.5 text-xs", children: text }) }, point.label);
11227
11556
  }) });
11228
11557
  }
11229
11558
  function GroupTable({
@@ -11231,8 +11560,8 @@ function GroupTable({
11231
11560
  series,
11232
11561
  onDrill
11233
11562
  }) {
11234
- return /* @__PURE__ */ jsx44("table", { className: "w-full text-xs", children: /* @__PURE__ */ jsx44("tbody", { children: points.map((point) => /* @__PURE__ */ jsxs40("tr", { className: "border-t first:border-t-0", children: [
11235
- /* @__PURE__ */ jsx44("td", { className: "min-w-0 truncate py-0.5 pr-2", children: onDrill ? /* @__PURE__ */ jsx44(
11563
+ return /* @__PURE__ */ jsx45("table", { className: "w-full text-xs", children: /* @__PURE__ */ jsx45("tbody", { children: points.map((point) => /* @__PURE__ */ jsxs41("tr", { className: "border-t first:border-t-0", children: [
11564
+ /* @__PURE__ */ jsx45("td", { className: "min-w-0 truncate py-0.5 pr-2", children: onDrill ? /* @__PURE__ */ jsx45(
11236
11565
  "button",
11237
11566
  {
11238
11567
  type: "button",
@@ -11241,29 +11570,29 @@ function GroupTable({
11241
11570
  children: point.label
11242
11571
  }
11243
11572
  ) : point.label }),
11244
- series.map((key) => /* @__PURE__ */ jsx44("td", { className: "py-0.5 pl-2 text-right tabular-nums", children: pretty(point.values[key] ?? 0) }, key)),
11245
- /* @__PURE__ */ jsx44("td", { className: "py-0.5 pl-2 text-right tabular-nums text-muted-foreground", children: point.n })
11573
+ series.map((key) => /* @__PURE__ */ jsx45("td", { className: "py-0.5 pl-2 text-right tabular-nums", children: pretty(point.values[key] ?? 0) }, key)),
11574
+ /* @__PURE__ */ jsx45("td", { className: "py-0.5 pl-2 text-right tabular-nums text-muted-foreground", children: point.n })
11246
11575
  ] }, point.label)) }) });
11247
11576
  }
11248
11577
  function StuckList({ rows }) {
11249
11578
  if (rows.length === 0) {
11250
- return /* @__PURE__ */ jsx44("p", { className: "px-1 py-3 text-xs text-muted-foreground", children: "Nothing has sat still this long. This block goes quiet when the work is moving." });
11579
+ return /* @__PURE__ */ jsx45("p", { className: "px-1 py-3 text-xs text-muted-foreground", children: "Nothing has sat still this long. This block goes quiet when the work is moving." });
11251
11580
  }
11252
- return /* @__PURE__ */ jsxs40("table", { className: "w-full table-fixed text-xs", children: [
11253
- /* @__PURE__ */ jsxs40("colgroup", { children: [
11254
- /* @__PURE__ */ jsx44("col", { className: "w-[45%]" }),
11255
- /* @__PURE__ */ jsx44("col", { className: "w-[33%]" }),
11256
- /* @__PURE__ */ jsx44("col", { className: "w-[22%]" })
11581
+ return /* @__PURE__ */ jsxs41("table", { className: "w-full table-fixed text-xs", children: [
11582
+ /* @__PURE__ */ jsxs41("colgroup", { children: [
11583
+ /* @__PURE__ */ jsx45("col", { className: "w-[45%]" }),
11584
+ /* @__PURE__ */ jsx45("col", { className: "w-[33%]" }),
11585
+ /* @__PURE__ */ jsx45("col", { className: "w-[22%]" })
11257
11586
  ] }),
11258
- /* @__PURE__ */ jsx44("thead", { className: "sr-only", children: /* @__PURE__ */ jsxs40("tr", { children: [
11259
- /* @__PURE__ */ jsx44("th", { children: "Record" }),
11260
- /* @__PURE__ */ jsx44("th", { children: "Where it is" }),
11261
- /* @__PURE__ */ jsx44("th", { children: "Days unchanged" })
11587
+ /* @__PURE__ */ jsx45("thead", { className: "sr-only", children: /* @__PURE__ */ jsxs41("tr", { children: [
11588
+ /* @__PURE__ */ jsx45("th", { children: "Record" }),
11589
+ /* @__PURE__ */ jsx45("th", { children: "Where it is" }),
11590
+ /* @__PURE__ */ jsx45("th", { children: "Days unchanged" })
11262
11591
  ] }) }),
11263
- /* @__PURE__ */ jsx44("tbody", { children: rows.map((row) => /* @__PURE__ */ jsxs40("tr", { className: "border-t first:border-t-0", children: [
11264
- /* @__PURE__ */ jsx44("td", { className: "truncate py-0.5 pr-2", title: row.title ?? void 0, children: row.title ?? row.record_id.slice(0, 8) }),
11265
- /* @__PURE__ */ jsx44("td", { className: "truncate py-0.5 pr-2 text-muted-foreground", title: row.state ?? void 0, children: row.state ?? "\u2014" }),
11266
- /* @__PURE__ */ jsxs40(
11592
+ /* @__PURE__ */ jsx45("tbody", { children: rows.map((row) => /* @__PURE__ */ jsxs41("tr", { className: "border-t first:border-t-0", children: [
11593
+ /* @__PURE__ */ jsx45("td", { className: "truncate py-0.5 pr-2", title: row.title ?? void 0, children: row.title ?? row.record_id.slice(0, 8) }),
11594
+ /* @__PURE__ */ jsx45("td", { className: "truncate py-0.5 pr-2 text-muted-foreground", title: row.state ?? void 0, children: row.state ?? "\u2014" }),
11595
+ /* @__PURE__ */ jsxs41(
11267
11596
  "td",
11268
11597
  {
11269
11598
  className: "whitespace-nowrap py-0.5 text-right tabular-nums",
@@ -11313,12 +11642,12 @@ function Drawing({
11313
11642
  axisLine: false,
11314
11643
  tickFormatter: compactTick
11315
11644
  };
11316
- const tooltip = /* @__PURE__ */ jsx44(Tooltip, { content: /* @__PURE__ */ jsx44(ChartTooltipContent, { config }), cursor: false });
11317
- const legend = series.length > 1 ? /* @__PURE__ */ jsx44(Legend, { content: /* @__PURE__ */ jsx44(ChartLegendContent, { config }) }) : null;
11645
+ const tooltip = /* @__PURE__ */ jsx45(Tooltip, { content: /* @__PURE__ */ jsx45(ChartTooltipContent, { config }), cursor: false });
11646
+ const legend = series.length > 1 ? /* @__PURE__ */ jsx45(Legend, { content: /* @__PURE__ */ jsx45(ChartLegendContent, { config }) }) : null;
11318
11647
  if (kind === "donut") {
11319
- return /* @__PURE__ */ jsx44(ChartFrame, { config, height: HEIGHT, children: (width) => /* @__PURE__ */ jsxs40(PieChart, { width, height: HEIGHT, children: [
11648
+ return /* @__PURE__ */ jsx45(ChartFrame, { config, height: HEIGHT, children: (width) => /* @__PURE__ */ jsxs41(PieChart, { width, height: HEIGHT, children: [
11320
11649
  tooltip,
11321
- /* @__PURE__ */ jsx44(
11650
+ /* @__PURE__ */ jsx45(
11322
11651
  Pie,
11323
11652
  {
11324
11653
  data,
@@ -11328,19 +11657,19 @@ function Drawing({
11328
11657
  outerRadius: 58,
11329
11658
  isAnimationActive: false,
11330
11659
  ...click ? { onClick: click, className: "cursor-pointer" } : {},
11331
- children: points.map((point) => /* @__PURE__ */ jsx44(Cell, { fill: String(config[point.label]?.color ?? SERIES_COLORS[0]) }, point.label))
11660
+ children: points.map((point) => /* @__PURE__ */ jsx45(Cell, { fill: String(config[point.label]?.color ?? SERIES_COLORS[0]) }, point.label))
11332
11661
  }
11333
11662
  )
11334
11663
  ] }) });
11335
11664
  }
11336
11665
  if (kind === "line") {
11337
- return /* @__PURE__ */ jsx44(ChartFrame, { config, height: HEIGHT, children: (width) => /* @__PURE__ */ jsxs40(LineChart, { width, height: HEIGHT, data, margin: { top: 6, right: 6, bottom: 0, left: 0 }, children: [
11338
- /* @__PURE__ */ jsx44(CartesianGrid, { vertical: false }),
11339
- /* @__PURE__ */ jsx44(XAxis, { dataKey: "label", ...axis }),
11340
- /* @__PURE__ */ jsx44(YAxis, { width: 44, ...axis }),
11666
+ return /* @__PURE__ */ jsx45(ChartFrame, { config, height: HEIGHT, children: (width) => /* @__PURE__ */ jsxs41(LineChart, { width, height: HEIGHT, data, margin: { top: 6, right: 6, bottom: 0, left: 0 }, children: [
11667
+ /* @__PURE__ */ jsx45(CartesianGrid, { vertical: false }),
11668
+ /* @__PURE__ */ jsx45(XAxis, { dataKey: "label", ...axis }),
11669
+ /* @__PURE__ */ jsx45(YAxis, { width: 44, ...axis }),
11341
11670
  tooltip,
11342
11671
  legend,
11343
- series.map((key) => /* @__PURE__ */ jsx44(
11672
+ series.map((key) => /* @__PURE__ */ jsx45(
11344
11673
  Line2,
11345
11674
  {
11346
11675
  type: "monotone",
@@ -11356,7 +11685,7 @@ function Drawing({
11356
11685
  ] }) });
11357
11686
  }
11358
11687
  const horizontal = kind === "bar";
11359
- return /* @__PURE__ */ jsx44(ChartFrame, { config, height: HEIGHT, children: (width) => /* @__PURE__ */ jsxs40(
11688
+ return /* @__PURE__ */ jsx45(ChartFrame, { config, height: HEIGHT, children: (width) => /* @__PURE__ */ jsxs41(
11360
11689
  BarChart,
11361
11690
  {
11362
11691
  width,
@@ -11365,17 +11694,17 @@ function Drawing({
11365
11694
  layout: horizontal ? "vertical" : "horizontal",
11366
11695
  margin: { top: 6, right: 6, bottom: 0, left: 0 },
11367
11696
  children: [
11368
- /* @__PURE__ */ jsx44(CartesianGrid, { vertical: horizontal, horizontal: !horizontal }),
11369
- horizontal ? /* @__PURE__ */ jsxs40(Fragment18, { children: [
11370
- /* @__PURE__ */ jsx44(XAxis, { type: "number", ...axis }),
11371
- /* @__PURE__ */ jsx44(YAxis, { type: "category", dataKey: "label", width: 88, ...axis })
11372
- ] }) : /* @__PURE__ */ jsxs40(Fragment18, { children: [
11373
- /* @__PURE__ */ jsx44(XAxis, { dataKey: "label", ...axis }),
11374
- /* @__PURE__ */ jsx44(YAxis, { width: 44, ...axis })
11697
+ /* @__PURE__ */ jsx45(CartesianGrid, { vertical: horizontal, horizontal: !horizontal }),
11698
+ horizontal ? /* @__PURE__ */ jsxs41(Fragment19, { children: [
11699
+ /* @__PURE__ */ jsx45(XAxis, { type: "number", ...axis }),
11700
+ /* @__PURE__ */ jsx45(YAxis, { type: "category", dataKey: "label", width: 88, ...axis })
11701
+ ] }) : /* @__PURE__ */ jsxs41(Fragment19, { children: [
11702
+ /* @__PURE__ */ jsx45(XAxis, { dataKey: "label", ...axis }),
11703
+ /* @__PURE__ */ jsx45(YAxis, { width: 44, ...axis })
11375
11704
  ] }),
11376
11705
  tooltip,
11377
11706
  legend,
11378
- series.map((key, index) => /* @__PURE__ */ jsx44(
11707
+ series.map((key, index) => /* @__PURE__ */ jsx45(
11379
11708
  Bar,
11380
11709
  {
11381
11710
  dataKey: key,
@@ -11383,7 +11712,7 @@ function Drawing({
11383
11712
  isAnimationActive: false,
11384
11713
  fill: `var(--color-${key})`,
11385
11714
  ...click ? { onClick: click, className: "cursor-pointer" } : {},
11386
- children: series.length === 1 ? points.map((point) => /* @__PURE__ */ jsx44(Cell, { fill: String(config[point.label]?.color ?? SERIES_COLORS[index]) }, point.label)) : null
11715
+ children: series.length === 1 ? points.map((point) => /* @__PURE__ */ jsx45(Cell, { fill: String(config[point.label]?.color ?? SERIES_COLORS[index]) }, point.label)) : null
11387
11716
  },
11388
11717
  key
11389
11718
  ))
@@ -11393,24 +11722,25 @@ function Drawing({
11393
11722
  }
11394
11723
 
11395
11724
  // src/DashboardCanvas.tsx
11396
- import { useCallback as useCallback26, useEffect as useEffect30, useMemo as useMemo26, useState as useState40 } from "react";
11397
- import { useFields as useFields19, useRecordsClient as useRecordsClient32, useTable as useTable17 } from "@ai-matrx/records/react";
11398
- import { BasicInput as BasicInput13, Button as Button36, Skeleton as Skeleton23, cn as cn39 } from "@ai-matrx/design-system";
11399
- import { Fragment as Fragment19, jsx as jsx45, jsxs as jsxs41 } from "react/jsx-runtime";
11725
+ import { useCallback as useCallback27, useEffect as useEffect31, useMemo as useMemo27, useState as useState41 } from "react";
11726
+ import { useFields as useFields19, useRecordsClient as useRecordsClient33, useTable as useTable17 } from "@ai-matrx/records/react";
11727
+ import { BasicInput as BasicInput14, Button as Button37, Skeleton as Skeleton24, cn as cn40 } from "@ai-matrx/design-system";
11728
+ import { Fragment as Fragment20, jsx as jsx46, jsxs as jsxs42 } from "react/jsx-runtime";
11400
11729
  function DashboardCanvas({ tableId, activeDashboardId, filter, className }) {
11401
- const client = useRecordsClient32();
11730
+ const client = useRecordsClient33();
11402
11731
  const host = useRecordsUi();
11403
11732
  const table = useTable17(tableId);
11404
11733
  const rights = useTableRights(table.data);
11405
11734
  const fields = useFields19(tableId);
11406
- const [boards, setBoards] = useState40(null);
11407
- const [error, setError] = useState40(null);
11408
- const [activeId, setActiveId] = useState40(activeDashboardId ?? null);
11409
- const [run, setRun] = useState40(null);
11410
- const [running, setRunning] = useState40(false);
11411
- const [question, setQuestion] = useState40("");
11412
- const [asking, setAsking] = useState40(false);
11413
- const load = useCallback26(async () => {
11735
+ const [boards, setBoards] = useState41(null);
11736
+ const [error, setError] = useState41(null);
11737
+ const [activeId, setActiveId] = useState41(activeDashboardId ?? null);
11738
+ const [run, setRun] = useState41(null);
11739
+ const [running, setRunning] = useState41(false);
11740
+ const [question, setQuestion] = useState41("");
11741
+ const [asking, setAsking] = useState41(false);
11742
+ const [scheduling, setScheduling] = useState41(false);
11743
+ const load = useCallback27(async () => {
11414
11744
  const answered = await client.dashboards({ table_id: tableId });
11415
11745
  if (!answered.ok) {
11416
11746
  setError(answered.error);
@@ -11419,17 +11749,17 @@ function DashboardCanvas({ tableId, activeDashboardId, filter, className }) {
11419
11749
  setError(null);
11420
11750
  setBoards(answered.data.map(dashboardFromSummary));
11421
11751
  }, [client, tableId]);
11422
- useEffect30(() => {
11752
+ useEffect31(() => {
11423
11753
  void load();
11424
11754
  }, [load]);
11425
- useEffect30(() => {
11755
+ useEffect31(() => {
11426
11756
  if (!boards || boards.length === 0) return;
11427
11757
  const chosen = boards.find((d) => d.id === (activeDashboardId ?? activeId)) ?? boards[0];
11428
11758
  if (chosen.id !== activeId) setActiveId(chosen.id);
11429
11759
  }, [boards, activeDashboardId]);
11430
- const board = useMemo26(() => boards?.find((d) => d.id === activeId) ?? null, [boards, activeId]);
11760
+ const board = useMemo27(() => boards?.find((d) => d.id === activeId) ?? null, [boards, activeId]);
11431
11761
  const filterKey = JSON.stringify(filter ?? {});
11432
- useEffect30(() => {
11762
+ useEffect31(() => {
11433
11763
  if (!activeId) {
11434
11764
  setRun(null);
11435
11765
  return;
@@ -11447,7 +11777,7 @@ function DashboardCanvas({ tableId, activeDashboardId, filter, className }) {
11447
11777
  cancelled = true;
11448
11778
  };
11449
11779
  }, [client, activeId, filterKey]);
11450
- const declare2 = useCallback26(
11780
+ const declare2 = useCallback27(
11451
11781
  async (next, blocks) => {
11452
11782
  const written = await client.dashboardDeclare(
11453
11783
  dashboardDeclareArgs({ ...next, blocks }, tableId)
@@ -11493,7 +11823,7 @@ function DashboardCanvas({ tableId, activeDashboardId, filter, className }) {
11493
11823
  ]
11494
11824
  );
11495
11825
  }
11496
- const refresh = useCallback26(async () => {
11826
+ const refresh = useCallback27(async () => {
11497
11827
  if (!activeId) return;
11498
11828
  setRunning(true);
11499
11829
  const again = await client.dashboardRun({
@@ -11534,20 +11864,20 @@ function DashboardCanvas({ tableId, activeDashboardId, filter, className }) {
11534
11864
  }
11535
11865
  }
11536
11866
  if (error) {
11537
- return /* @__PURE__ */ jsx45(
11867
+ return /* @__PURE__ */ jsx46(
11538
11868
  RefusalNotice,
11539
11869
  {
11540
11870
  error,
11541
11871
  className,
11542
- actions: /* @__PURE__ */ jsx45(Button36, { size: "sm", variant: "ghost", onClick: () => void load(), children: "Try again" })
11872
+ actions: /* @__PURE__ */ jsx46(Button37, { size: "sm", variant: "ghost", onClick: () => void load(), children: "Try again" })
11543
11873
  }
11544
11874
  );
11545
11875
  }
11546
- if (boards === null) return /* @__PURE__ */ jsx45(Skeleton23, { className: cn39("h-64 w-full", className) });
11547
- return /* @__PURE__ */ jsxs41("div", { className: cn39("flex min-h-0 flex-col gap-2", className), children: [
11548
- /* @__PURE__ */ jsxs41("div", { className: "flex flex-wrap items-center gap-1", role: "group", "aria-label": "Dashboards", children: [
11549
- boards.map((d) => /* @__PURE__ */ jsx45(
11550
- Button36,
11876
+ if (boards === null) return /* @__PURE__ */ jsx46(Skeleton24, { className: cn40("h-64 w-full", className) });
11877
+ return /* @__PURE__ */ jsxs42("div", { className: cn40("flex min-h-0 flex-col gap-2", className), children: [
11878
+ /* @__PURE__ */ jsxs42("div", { className: "flex flex-wrap items-center gap-1", role: "group", "aria-label": "Dashboards", children: [
11879
+ boards.map((d) => /* @__PURE__ */ jsx46(
11880
+ Button37,
11551
11881
  {
11552
11882
  size: "sm",
11553
11883
  variant: d.id === activeId ? "secondary" : "ghost",
@@ -11557,10 +11887,10 @@ function DashboardCanvas({ tableId, activeDashboardId, filter, className }) {
11557
11887
  },
11558
11888
  d.id
11559
11889
  )),
11560
- rights.admin ? /* @__PURE__ */ jsx45(Button36, { size: "sm", variant: "ghost", onClick: () => void create(), children: "New dashboard" }) : null,
11561
- board ? /* @__PURE__ */ jsxs41("span", { className: "ml-auto flex items-center gap-1", children: [
11562
- /* @__PURE__ */ jsx45(
11563
- Button36,
11890
+ rights.admin ? /* @__PURE__ */ jsx46(Button37, { size: "sm", variant: "ghost", onClick: () => void create(), children: "New dashboard" }) : null,
11891
+ board ? /* @__PURE__ */ jsxs42("span", { className: "ml-auto flex items-center gap-1", children: [
11892
+ /* @__PURE__ */ jsx46(
11893
+ Button37,
11564
11894
  {
11565
11895
  size: "sm",
11566
11896
  variant: "ghost",
@@ -11569,13 +11899,31 @@ function DashboardCanvas({ tableId, activeDashboardId, filter, className }) {
11569
11899
  children: running ? "Asking" : "Refresh"
11570
11900
  }
11571
11901
  ),
11572
- rights.admin ? /* @__PURE__ */ jsx45(Button36, { size: "sm", variant: "ghost", onClick: () => void remove(board), children: "Delete" }) : null
11902
+ /* @__PURE__ */ jsx46(
11903
+ Button37,
11904
+ {
11905
+ size: "sm",
11906
+ variant: scheduling ? "secondary" : "ghost",
11907
+ onClick: () => setScheduling((v) => !v),
11908
+ children: scheduling ? "Done" : "Send on a schedule"
11909
+ }
11910
+ ),
11911
+ rights.admin ? /* @__PURE__ */ jsx46(Button37, { size: "sm", variant: "ghost", onClick: () => void remove(board), children: "Delete" }) : null
11573
11912
  ] }) : null
11574
11913
  ] }),
11575
- board ? /* @__PURE__ */ jsxs41("div", { className: "flex flex-col gap-1", children: [
11576
- /* @__PURE__ */ jsxs41("div", { className: "flex items-center gap-1", children: [
11577
- /* @__PURE__ */ jsx45(
11578
- BasicInput13,
11914
+ board && scheduling ? /* @__PURE__ */ jsx46(
11915
+ DigestScheduler,
11916
+ {
11917
+ tableId,
11918
+ subjectName: board.name,
11919
+ ...filter ? { filters: filter } : {},
11920
+ onClose: () => setScheduling(false)
11921
+ }
11922
+ ) : null,
11923
+ board ? /* @__PURE__ */ jsxs42("div", { className: "flex flex-col gap-1", children: [
11924
+ /* @__PURE__ */ jsxs42("div", { className: "flex items-center gap-1", children: [
11925
+ /* @__PURE__ */ jsx46(
11926
+ BasicInput14,
11579
11927
  {
11580
11928
  value: question,
11581
11929
  "aria-label": "Ask for a change to this dashboard",
@@ -11588,15 +11936,15 @@ function DashboardCanvas({ tableId, activeDashboardId, filter, className }) {
11588
11936
  className: "h-8 min-w-0 flex-1 text-xs"
11589
11937
  }
11590
11938
  ),
11591
- /* @__PURE__ */ jsx45(Button36, { size: "sm", disabled: !host.onReask || asking || question.trim() === "", onClick: () => void reask(), children: asking ? "Asking" : "Ask" })
11939
+ /* @__PURE__ */ jsx46(Button37, { size: "sm", disabled: !host.onReask || asking || question.trim() === "", onClick: () => void reask(), children: asking ? "Asking" : "Ask" })
11592
11940
  ] }),
11593
- !host.onReask ? /* @__PURE__ */ jsx45("p", { className: "text-xs text-muted-foreground", children: NO_REASK_REASON }) : null
11941
+ !host.onReask ? /* @__PURE__ */ jsx46("p", { className: "text-xs text-muted-foreground", children: NO_REASK_REASON }) : null
11594
11942
  ] }) : null,
11595
- !board ? /* @__PURE__ */ jsx45("p", { className: "text-xs text-muted-foreground", children: rights.admin ? "No dashboard looks at this table yet. \u201CNew dashboard\u201D makes one, or ask an agent for the one you want and it writes the whole thing." : rights.why("structure") }) : run === null ? /* @__PURE__ */ jsx45(Skeleton23, { className: "h-64 w-full" }) : run.blocks.length === 0 ? /* @__PURE__ */ jsx45("p", { className: "text-xs text-muted-foreground", children: "This dashboard has no blocks yet. Each one is a single question the store answers \u2014 grouped, bucketed and measured inside the read door." }) : /* @__PURE__ */ jsxs41(Fragment19, { children: [
11596
- /* @__PURE__ */ jsx45("div", { className: "grid grid-cols-1 gap-2 sm:grid-cols-2 xl:grid-cols-3", children: run.blocks.map((block, index) => /* @__PURE__ */ jsxs41("div", { className: "flex min-w-0 flex-col gap-1", children: [
11597
- /* @__PURE__ */ jsx45(ChartBlock, { block, subject: tableId }),
11598
- rights.admin && board.blocks[index] ? /* @__PURE__ */ jsxs41("div", { className: "flex items-center gap-1 text-xs", children: [
11599
- /* @__PURE__ */ jsx45(
11943
+ !board ? /* @__PURE__ */ jsx46("p", { className: "text-xs text-muted-foreground", children: rights.admin ? "No dashboard looks at this table yet. \u201CNew dashboard\u201D makes one, or ask an agent for the one you want and it writes the whole thing." : rights.why("structure") }) : run === null ? /* @__PURE__ */ jsx46(Skeleton24, { className: "h-64 w-full" }) : run.blocks.length === 0 ? /* @__PURE__ */ jsx46("p", { className: "text-xs text-muted-foreground", children: "This dashboard has no blocks yet. Each one is a single question the store answers \u2014 grouped, bucketed and measured inside the read door." }) : /* @__PURE__ */ jsxs42(Fragment20, { children: [
11944
+ /* @__PURE__ */ jsx46("div", { className: "grid grid-cols-1 gap-2 sm:grid-cols-2 xl:grid-cols-3", children: run.blocks.map((block, index) => /* @__PURE__ */ jsxs42("div", { className: "flex min-w-0 flex-col gap-1", children: [
11945
+ /* @__PURE__ */ jsx46(ChartBlock, { block, subject: tableId }),
11946
+ rights.admin && board.blocks[index] ? /* @__PURE__ */ jsxs42("div", { className: "flex items-center gap-1 text-xs", children: [
11947
+ /* @__PURE__ */ jsx46(
11600
11948
  "select",
11601
11949
  {
11602
11950
  "aria-label": `Shape of ${block.title}`,
@@ -11608,11 +11956,11 @@ function DashboardCanvas({ tableId, activeDashboardId, filter, className }) {
11608
11956
  (b, i) => i === index ? { ...b, kind: e.target.value } : b
11609
11957
  )
11610
11958
  ),
11611
- children: CHART_KINDS.map((kind) => /* @__PURE__ */ jsx45("option", { value: kind, children: CHART_KIND_LABEL[kind] }, kind))
11959
+ children: CHART_KINDS.map((kind) => /* @__PURE__ */ jsx46("option", { value: kind, children: CHART_KIND_LABEL[kind] }, kind))
11612
11960
  }
11613
11961
  ),
11614
- /* @__PURE__ */ jsx45(
11615
- Button36,
11962
+ /* @__PURE__ */ jsx46(
11963
+ Button37,
11616
11964
  {
11617
11965
  size: "sm",
11618
11966
  variant: "ghost",
@@ -11625,17 +11973,17 @@ function DashboardCanvas({ tableId, activeDashboardId, filter, className }) {
11625
11973
  )
11626
11974
  ] }) : null
11627
11975
  ] }, `${block.title}-${index}`)) }),
11628
- !host.openRecords ? /* @__PURE__ */ jsx45("p", { className: "text-xs text-muted-foreground", children: NO_OPEN_RECORDS_REASON }) : null
11976
+ !host.openRecords ? /* @__PURE__ */ jsx46("p", { className: "text-xs text-muted-foreground", children: NO_OPEN_RECORDS_REASON }) : null
11629
11977
  ] })
11630
11978
  ] });
11631
11979
  }
11632
11980
 
11633
11981
  // src/FormsPanel.tsx
11634
- import { useCallback as useCallback27, useEffect as useEffect31, useState as useState41 } from "react";
11635
- import { useRecordsClient as useRecordsClient33, useTable as useTable18 } from "@ai-matrx/records/react";
11982
+ import { useCallback as useCallback28, useEffect as useEffect32, useState as useState42 } from "react";
11983
+ import { useRecordsClient as useRecordsClient34, useTable as useTable18 } from "@ai-matrx/records/react";
11636
11984
  import { publicFormPath as publicFormPath2 } from "@ai-matrx/records";
11637
- import { Badge as Badge15, Button as Button37, Skeleton as Skeleton24, cn as cn40 } from "@ai-matrx/design-system";
11638
- import { Fragment as Fragment20, jsx as jsx46, jsxs as jsxs42 } from "react/jsx-runtime";
11985
+ import { Badge as Badge15, Button as Button38, Skeleton as Skeleton25, cn as cn41 } from "@ai-matrx/design-system";
11986
+ import { Fragment as Fragment21, jsx as jsx47, jsxs as jsxs43 } from "react/jsx-runtime";
11639
11987
  var WHAT_A_FORM_IS = "A form asks for this table's own fields, and its answers land here as ordinary records stamped with the form they came through.";
11640
11988
  var NO_ADMIN = "This table has no forms. Making one needs the admin level on it, because a form decides what people with no account may add here.";
11641
11989
  function formSuggestion(tableName2) {
@@ -11643,16 +11991,16 @@ function formSuggestion(tableName2) {
11643
11991
  return `Make me a form that collects new ${subject} entries and tells me when somebody answers.`;
11644
11992
  }
11645
11993
  function FormsPanel({ tableId, className }) {
11646
- const client = useRecordsClient33();
11994
+ const client = useRecordsClient34();
11647
11995
  const host = useRecordsUi();
11648
11996
  const table = useTable18(tableId);
11649
11997
  const rights = useTableRights(table.data);
11650
- const [forms, setForms] = useState41(null);
11651
- const [error, setError] = useState41(null);
11652
- const [busy, setBusy] = useState41(null);
11653
- const [copied, setCopied] = useState41(null);
11654
- const [building, setBuilding] = useState41(false);
11655
- const load = useCallback27(async () => {
11998
+ const [forms, setForms] = useState42(null);
11999
+ const [error, setError] = useState42(null);
12000
+ const [busy, setBusy] = useState42(null);
12001
+ const [copied, setCopied] = useState42(null);
12002
+ const [building, setBuilding] = useState42(false);
12003
+ const load = useCallback28(async () => {
11656
12004
  const answered = await client.forms({ table_id: tableId });
11657
12005
  if (!answered.ok) {
11658
12006
  setError(answered.error);
@@ -11662,10 +12010,10 @@ function FormsPanel({ tableId, className }) {
11662
12010
  setError(null);
11663
12011
  setForms(answered.data);
11664
12012
  }, [client, tableId]);
11665
- useEffect31(() => {
12013
+ useEffect32(() => {
11666
12014
  void load();
11667
12015
  }, [load]);
11668
- const toggle = useCallback27(
12016
+ const toggle = useCallback28(
11669
12017
  async (form) => {
11670
12018
  setBusy(form.form_id);
11671
12019
  const wanted = form.published_at === null || form.closed_at !== null;
@@ -11679,8 +12027,8 @@ function FormsPanel({ tableId, className }) {
11679
12027
  },
11680
12028
  [client, load]
11681
12029
  );
11682
- const [shown, setShown] = useState41(null);
11683
- const copy = useCallback27(async (url, formId) => {
12030
+ const [shown, setShown] = useState42(null);
12031
+ const copy = useCallback28(async (url, formId) => {
11684
12032
  try {
11685
12033
  await navigator.clipboard.writeText(url);
11686
12034
  setCopied(formId);
@@ -11690,17 +12038,17 @@ function FormsPanel({ tableId, className }) {
11690
12038
  setShown(url);
11691
12039
  }
11692
12040
  }, []);
11693
- if (forms === null) return /* @__PURE__ */ jsx46(Skeleton24, { className: cn40("h-32 w-full", className) });
12041
+ if (forms === null) return /* @__PURE__ */ jsx47(Skeleton25, { className: cn41("h-32 w-full", className) });
11694
12042
  const origin = host.publicOrigin ?? (typeof window === "undefined" ? "" : window.location.origin);
11695
- return /* @__PURE__ */ jsxs42("section", { className: cn40("flex flex-col gap-3", className), children: [
11696
- /* @__PURE__ */ jsxs42("header", { className: "flex items-center gap-2", children: [
11697
- /* @__PURE__ */ jsx46("h3", { className: "text-sm font-medium", children: "Forms" }),
11698
- /* @__PURE__ */ jsx46("span", { className: "text-xs text-muted-foreground", children: forms.length === 0 ? "none yet" : `${forms.length}` }),
11699
- /* @__PURE__ */ jsx46("div", { className: "flex-1" }),
11700
- rights.structure && forms.length > 0 ? /* @__PURE__ */ jsx46(Button37, { size: "sm", variant: building ? "secondary" : "ghost", onClick: () => setBuilding((b) => !b), children: building ? "Done building" : "Build a form" }) : null
12043
+ return /* @__PURE__ */ jsxs43("section", { className: cn41("flex flex-col gap-3", className), children: [
12044
+ /* @__PURE__ */ jsxs43("header", { className: "flex items-center gap-2", children: [
12045
+ /* @__PURE__ */ jsx47("h3", { className: "text-sm font-medium", children: "Forms" }),
12046
+ /* @__PURE__ */ jsx47("span", { className: "text-xs text-muted-foreground", children: forms.length === 0 ? "none yet" : `${forms.length}` }),
12047
+ /* @__PURE__ */ jsx47("div", { className: "flex-1" }),
12048
+ rights.structure && forms.length > 0 ? /* @__PURE__ */ jsx47(Button38, { size: "sm", variant: building ? "secondary" : "ghost", onClick: () => setBuilding((b) => !b), children: building ? "Done building" : "Build a form" }) : null
11701
12049
  ] }),
11702
- error ? /* @__PURE__ */ jsx46(RefusalNotice, { error }) : null,
11703
- building ? /* @__PURE__ */ jsx46(
12050
+ error ? /* @__PURE__ */ jsx47(RefusalNotice, { error }) : null,
12051
+ building ? /* @__PURE__ */ jsx47(
11704
12052
  FormBuilder,
11705
12053
  {
11706
12054
  tableId,
@@ -11709,7 +12057,7 @@ function FormsPanel({ tableId, className }) {
11709
12057
  }
11710
12058
  }
11711
12059
  ) : null,
11712
- forms.length === 0 && !building ? /* @__PURE__ */ jsx46(
12060
+ forms.length === 0 && !building ? /* @__PURE__ */ jsx47(
11713
12061
  BuildOrAsk,
11714
12062
  {
11715
12063
  mayBuild: rights.structure,
@@ -11727,36 +12075,36 @@ function FormsPanel({ tableId, className }) {
11727
12075
  children: WHAT_A_FORM_IS
11728
12076
  }
11729
12077
  ) : null,
11730
- /* @__PURE__ */ jsx46("ul", { className: "flex flex-col gap-2", children: forms.map((form) => {
12078
+ /* @__PURE__ */ jsx47("ul", { className: "flex flex-col gap-2", children: forms.map((form) => {
11731
12079
  const url = `${origin}${publicFormPath2(form.form_id)}`;
11732
- return /* @__PURE__ */ jsxs42("li", { className: "rounded-md border p-2.5", children: [
11733
- /* @__PURE__ */ jsxs42("div", { className: "flex items-center gap-2", children: [
11734
- /* @__PURE__ */ jsx46("span", { className: "min-w-0 flex-1 truncate text-sm font-medium", children: form.title ?? form.slug }),
11735
- /* @__PURE__ */ jsx46(Badge15, { variant: form.state === "open" ? "default" : "secondary", children: form.state })
12080
+ return /* @__PURE__ */ jsxs43("li", { className: "rounded-md border p-2.5", children: [
12081
+ /* @__PURE__ */ jsxs43("div", { className: "flex items-center gap-2", children: [
12082
+ /* @__PURE__ */ jsx47("span", { className: "min-w-0 flex-1 truncate text-sm font-medium", children: form.title ?? form.slug }),
12083
+ /* @__PURE__ */ jsx47(Badge15, { variant: form.state === "open" ? "default" : "secondary", children: form.state })
11736
12084
  ] }),
11737
- /* @__PURE__ */ jsx46("p", { className: "mt-0.5 text-xs text-muted-foreground", children: FORM_STATE_WORDS[form.state] }),
11738
- /* @__PURE__ */ jsxs42("p", { className: "mt-1.5 text-xs", children: [
11739
- /* @__PURE__ */ jsx46("span", { className: "font-medium", children: form.in_table }),
12085
+ /* @__PURE__ */ jsx47("p", { className: "mt-0.5 text-xs text-muted-foreground", children: FORM_STATE_WORDS[form.state] }),
12086
+ /* @__PURE__ */ jsxs43("p", { className: "mt-1.5 text-xs", children: [
12087
+ /* @__PURE__ */ jsx47("span", { className: "font-medium", children: form.in_table }),
11740
12088
  " in the table",
11741
- form.held > 0 ? /* @__PURE__ */ jsxs42(Fragment20, { children: [
12089
+ form.held > 0 ? /* @__PURE__ */ jsxs43(Fragment21, { children: [
11742
12090
  " \xB7 ",
11743
- /* @__PURE__ */ jsx46("span", { className: "font-medium", children: form.held }),
12091
+ /* @__PURE__ */ jsx47("span", { className: "font-medium", children: form.held }),
11744
12092
  " waiting for someone"
11745
12093
  ] }) : null,
11746
- form.rejected > 0 ? /* @__PURE__ */ jsxs42(Fragment20, { children: [
12094
+ form.rejected > 0 ? /* @__PURE__ */ jsxs43(Fragment21, { children: [
11747
12095
  " \xB7 ",
11748
- /* @__PURE__ */ jsx46("span", { className: "font-medium", children: form.rejected }),
12096
+ /* @__PURE__ */ jsx47("span", { className: "font-medium", children: form.rejected }),
11749
12097
  " turned away"
11750
12098
  ] }) : null,
11751
- form.submission_cap !== null ? /* @__PURE__ */ jsxs42("span", { className: "text-muted-foreground", children: [
12099
+ form.submission_cap !== null ? /* @__PURE__ */ jsxs43("span", { className: "text-muted-foreground", children: [
11752
12100
  " \xB7 stops at ",
11753
12101
  form.submission_cap
11754
12102
  ] }) : null
11755
12103
  ] }),
11756
- /* @__PURE__ */ jsxs42("div", { className: "mt-2 flex flex-wrap items-center gap-1.5", children: [
11757
- form.published_at ? /* @__PURE__ */ jsx46(Button37, { size: "sm", variant: "outline", onClick: () => void copy(url, form.form_id), children: copied === form.form_id ? "Copied" : "Copy link" }) : null,
11758
- rights.structure ? /* @__PURE__ */ jsx46(
11759
- Button37,
12104
+ /* @__PURE__ */ jsxs43("div", { className: "mt-2 flex flex-wrap items-center gap-1.5", children: [
12105
+ form.published_at ? /* @__PURE__ */ jsx47(Button38, { size: "sm", variant: "outline", onClick: () => void copy(url, form.form_id), children: copied === form.form_id ? "Copied" : "Copy link" }) : null,
12106
+ rights.structure ? /* @__PURE__ */ jsx47(
12107
+ Button38,
11760
12108
  {
11761
12109
  size: "sm",
11762
12110
  variant: "ghost",
@@ -11766,25 +12114,25 @@ function FormsPanel({ tableId, className }) {
11766
12114
  }
11767
12115
  ) : null
11768
12116
  ] }),
11769
- shown && shown === url ? /* @__PURE__ */ jsxs42("p", { className: "mt-1.5 break-all rounded border border-dashed px-2 py-1 text-xs", children: [
12117
+ shown && shown === url ? /* @__PURE__ */ jsxs43("p", { className: "mt-1.5 break-all rounded border border-dashed px-2 py-1 text-xs", children: [
11770
12118
  "This browser would not let the page copy for you, so here it is to copy by hand:",
11771
12119
  " ",
11772
12120
  url
11773
12121
  ] }) : null
11774
12122
  ] }, form.form_id);
11775
12123
  }) }),
11776
- !rights.known ? /* @__PURE__ */ jsx46("p", { className: "text-xs text-muted-foreground", children: "Still asking what you may do with this table \u2014 nothing is offered until it answers." }) : null
12124
+ !rights.known ? /* @__PURE__ */ jsx47("p", { className: "text-xs text-muted-foreground", children: "Still asking what you may do with this table \u2014 nothing is offered until it answers." }) : null
11777
12125
  ] });
11778
12126
  }
11779
12127
 
11780
12128
  // src/BookingBuilder.tsx
11781
- import { useCallback as useCallback28, useEffect as useEffect32, useMemo as useMemo27, useState as useState42 } from "react";
11782
- import { useFields as useFields20, useRecordsClient as useRecordsClient34, useTable as useTable19 } from "@ai-matrx/records/react";
12129
+ import { useCallback as useCallback29, useEffect as useEffect33, useMemo as useMemo28, useState as useState43 } from "react";
12130
+ import { useFields as useFields20, useRecordsClient as useRecordsClient35, useTable as useTable19 } from "@ai-matrx/records/react";
11783
12131
  import {
11784
12132
  bookingPath
11785
12133
  } from "@ai-matrx/records";
11786
- import { BasicInput as BasicInput14, BasicTextarea as BasicTextarea7, Button as Button38, Checkbox as Checkbox6, Label as Label7, Skeleton as Skeleton25, cn as cn41 } from "@ai-matrx/design-system";
11787
- import { Fragment as Fragment21, jsx as jsx47, jsxs as jsxs43 } from "react/jsx-runtime";
12134
+ import { BasicInput as BasicInput15, BasicTextarea as BasicTextarea7, Button as Button39, Checkbox as Checkbox7, Label as Label8, Skeleton as Skeleton26, cn as cn42 } from "@ai-matrx/design-system";
12135
+ import { Fragment as Fragment22, jsx as jsx48, jsxs as jsxs44 } from "react/jsx-runtime";
11788
12136
  var STORE_ANSWERS_THESE = ["slot", "status", "booked_with"];
11789
12137
  var DAYS = [
11790
12138
  { weekday: 1, label: "Mon" },
@@ -11808,36 +12156,36 @@ function draftWindows(availability) {
11808
12156
  });
11809
12157
  }
11810
12158
  function BookingBuilder({ tableId, bookingId, onSaved, onClose, className }) {
11811
- const client = useRecordsClient34();
12159
+ const client = useRecordsClient35();
11812
12160
  const host = useRecordsUi();
11813
12161
  const table = useTable19(tableId);
11814
12162
  const rights = useTableRights(table.data);
11815
12163
  const fields = useFields20(tableId);
11816
- const [existing, setExisting] = useState42(null);
11817
- const [loaded, setLoaded] = useState42(false);
11818
- const [error, setError] = useState42(null);
11819
- const [saving, setSaving] = useState42(false);
11820
- const [publishing, setPublishing] = useState42(false);
11821
- const [copied, setCopied] = useState42(false);
11822
- const [title, setTitle] = useState42("");
11823
- const [minutes, setMinutes] = useState42(30);
11824
- const [buffer, setBuffer] = useState42(0);
11825
- const [lead, setLead] = useState42(120);
11826
- const [perDay, setPerDay] = useState42(8);
11827
- const [days, setDays] = useState42(30);
11828
- const [windows, setWindows] = useState42(() => draftWindows(null));
11829
- const [asked, setAsked] = useState42([]);
11830
- const [confirmation, setConfirmation] = useState42("");
11831
- const [offer, setOffer] = useState42(null);
11832
- const [formId, setFormId] = useState42(bookingId ?? null);
12164
+ const [existing, setExisting] = useState43(null);
12165
+ const [loaded, setLoaded] = useState43(false);
12166
+ const [error, setError] = useState43(null);
12167
+ const [saving, setSaving] = useState43(false);
12168
+ const [publishing, setPublishing] = useState43(false);
12169
+ const [copied, setCopied] = useState43(false);
12170
+ const [title, setTitle] = useState43("");
12171
+ const [minutes, setMinutes] = useState43(30);
12172
+ const [buffer, setBuffer] = useState43(0);
12173
+ const [lead, setLead] = useState43(120);
12174
+ const [perDay, setPerDay] = useState43(8);
12175
+ const [days, setDays] = useState43(30);
12176
+ const [windows, setWindows] = useState43(() => draftWindows(null));
12177
+ const [asked, setAsked] = useState43([]);
12178
+ const [confirmation, setConfirmation] = useState43("");
12179
+ const [offer, setOffer] = useState43(null);
12180
+ const [formId, setFormId] = useState43(bookingId ?? null);
11833
12181
  const publicOrigin = host.publicOrigin ?? (typeof window === "undefined" ? "" : window.location.origin);
11834
- const askable = useMemo27(
12182
+ const askable = useMemo28(
11835
12183
  () => (fields.data ?? []).filter(
11836
12184
  (f) => !STORE_ANSWERS_THESE.includes(f.key)
11837
12185
  ),
11838
12186
  [fields.data]
11839
12187
  );
11840
- const load = useCallback28(async () => {
12188
+ const load = useCallback29(async () => {
11841
12189
  const answered = await client.bookings({ table_id: tableId });
11842
12190
  if (!answered.ok) {
11843
12191
  setError(answered.error);
@@ -11849,18 +12197,18 @@ function BookingBuilder({ tableId, bookingId, onSaved, onClose, className }) {
11849
12197
  setLoaded(true);
11850
12198
  if (mine) setFormId(mine.form_id);
11851
12199
  }, [client, tableId, bookingId]);
11852
- useEffect32(() => {
12200
+ useEffect33(() => {
11853
12201
  void load();
11854
12202
  }, [load]);
11855
- useEffect32(() => {
12203
+ useEffect33(() => {
11856
12204
  if (title !== "" || !table.data) return;
11857
12205
  setTitle(existing?.title ?? `Book a ${minutes}-minute ${table.data.name} appointment`);
11858
12206
  }, [table.data, existing]);
11859
- useEffect32(() => {
12207
+ useEffect33(() => {
11860
12208
  if (!existing) return;
11861
12209
  setMinutes(existing.slot_minutes);
11862
12210
  }, [existing]);
11863
- const availability = useCallback28(
12211
+ const availability = useCallback29(
11864
12212
  () => ({
11865
12213
  slot_minutes: minutes,
11866
12214
  buffer_minutes: buffer,
@@ -11909,68 +12257,68 @@ function BookingBuilder({ tableId, bookingId, onSaved, onClose, className }) {
11909
12257
  await load();
11910
12258
  onSaved?.({ form_id: formId });
11911
12259
  }
11912
- if (!loaded || fields.loading) return /* @__PURE__ */ jsx47(Skeleton25, { className: cn41("h-64 w-full", className) });
12260
+ if (!loaded || fields.loading) return /* @__PURE__ */ jsx48(Skeleton26, { className: cn42("h-64 w-full", className) });
11913
12261
  if (!rights.structure) {
11914
- return /* @__PURE__ */ jsx47("p", { className: cn41("text-xs text-muted-foreground", className), children: rights.why("structure") ?? "Making a booking page needs the admin level on this table, because it lets people with no account take time in your calendar." });
12262
+ return /* @__PURE__ */ jsx48("p", { className: cn42("text-xs text-muted-foreground", className), children: rights.why("structure") ?? "Making a booking page needs the admin level on this table, because it lets people with no account take time in your calendar." });
11915
12263
  }
11916
12264
  const url = formId ? `${publicOrigin}${bookingPath(formId)}` : null;
11917
12265
  const shown = offer ?? null;
11918
- return /* @__PURE__ */ jsxs43("section", { className: cn41("flex flex-col gap-3", className), children: [
11919
- /* @__PURE__ */ jsxs43("header", { className: "flex items-center gap-2", children: [
11920
- /* @__PURE__ */ jsx47("h3", { className: "text-sm font-medium", children: existing ? "Booking page" : "New booking page" }),
11921
- /* @__PURE__ */ jsx47("div", { className: "flex-1" }),
11922
- /* @__PURE__ */ jsx47(Button38, { size: "sm", disabled: saving, onClick: () => void save(), children: saving ? "Saving\u2026" : "Save" }),
11923
- onClose ? /* @__PURE__ */ jsx47(Button38, { size: "sm", variant: "ghost", onClick: onClose, children: "Close" }) : null
12266
+ return /* @__PURE__ */ jsxs44("section", { className: cn42("flex flex-col gap-3", className), children: [
12267
+ /* @__PURE__ */ jsxs44("header", { className: "flex items-center gap-2", children: [
12268
+ /* @__PURE__ */ jsx48("h3", { className: "text-sm font-medium", children: existing ? "Booking page" : "New booking page" }),
12269
+ /* @__PURE__ */ jsx48("div", { className: "flex-1" }),
12270
+ /* @__PURE__ */ jsx48(Button39, { size: "sm", disabled: saving, onClick: () => void save(), children: saving ? "Saving\u2026" : "Save" }),
12271
+ onClose ? /* @__PURE__ */ jsx48(Button39, { size: "sm", variant: "ghost", onClick: onClose, children: "Close" }) : null
11924
12272
  ] }),
11925
- error ? /* @__PURE__ */ jsx47(RefusalNotice, { error }) : null,
11926
- /* @__PURE__ */ jsxs43("label", { className: "flex flex-col gap-1", children: [
11927
- /* @__PURE__ */ jsx47(Label7, { className: "text-xs font-medium", children: "What it is called" }),
11928
- /* @__PURE__ */ jsx47(BasicInput14, { value: title, onChange: (e) => setTitle(e.target.value) })
12273
+ error ? /* @__PURE__ */ jsx48(RefusalNotice, { error }) : null,
12274
+ /* @__PURE__ */ jsxs44("label", { className: "flex flex-col gap-1", children: [
12275
+ /* @__PURE__ */ jsx48(Label8, { className: "text-xs font-medium", children: "What it is called" }),
12276
+ /* @__PURE__ */ jsx48(BasicInput15, { value: title, onChange: (e) => setTitle(e.target.value) })
11929
12277
  ] }),
11930
- /* @__PURE__ */ jsxs43("div", { className: "grid grid-cols-2 gap-2", children: [
11931
- /* @__PURE__ */ jsxs43("label", { className: "flex flex-col gap-1", children: [
11932
- /* @__PURE__ */ jsx47(Label7, { className: "text-xs font-medium", children: "How long one appointment is" }),
11933
- /* @__PURE__ */ jsx47(
12278
+ /* @__PURE__ */ jsxs44("div", { className: "grid grid-cols-2 gap-2", children: [
12279
+ /* @__PURE__ */ jsxs44("label", { className: "flex flex-col gap-1", children: [
12280
+ /* @__PURE__ */ jsx48(Label8, { className: "text-xs font-medium", children: "How long one appointment is" }),
12281
+ /* @__PURE__ */ jsx48(
11934
12282
  "select",
11935
12283
  {
11936
12284
  className: "h-9 rounded border bg-background px-2 text-sm",
11937
12285
  value: minutes,
11938
12286
  onChange: (e) => setMinutes(Number(e.target.value)),
11939
- children: LENGTHS.map((n) => /* @__PURE__ */ jsxs43("option", { value: n, children: [
12287
+ children: LENGTHS.map((n) => /* @__PURE__ */ jsxs44("option", { value: n, children: [
11940
12288
  n,
11941
12289
  " minutes"
11942
12290
  ] }, n))
11943
12291
  }
11944
12292
  )
11945
12293
  ] }),
11946
- /* @__PURE__ */ jsxs43("label", { className: "flex flex-col gap-1", children: [
11947
- /* @__PURE__ */ jsx47(Label7, { className: "text-xs font-medium", children: "Gap kept after each one" }),
11948
- /* @__PURE__ */ jsx47(
12294
+ /* @__PURE__ */ jsxs44("label", { className: "flex flex-col gap-1", children: [
12295
+ /* @__PURE__ */ jsx48(Label8, { className: "text-xs font-medium", children: "Gap kept after each one" }),
12296
+ /* @__PURE__ */ jsx48(
11949
12297
  "select",
11950
12298
  {
11951
12299
  className: "h-9 rounded border bg-background px-2 text-sm",
11952
12300
  value: buffer,
11953
12301
  onChange: (e) => setBuffer(Number(e.target.value)),
11954
- children: [0, 5, 10, 15, 30].map((n) => /* @__PURE__ */ jsx47("option", { value: n, children: n === 0 ? "No gap \u2014 back to back" : `${n} minutes` }, n))
12302
+ children: [0, 5, 10, 15, 30].map((n) => /* @__PURE__ */ jsx48("option", { value: n, children: n === 0 ? "No gap \u2014 back to back" : `${n} minutes` }, n))
11955
12303
  }
11956
12304
  )
11957
12305
  ] }),
11958
- /* @__PURE__ */ jsxs43("label", { className: "flex flex-col gap-1", children: [
11959
- /* @__PURE__ */ jsx47(Label7, { className: "text-xs font-medium", children: "Earliest somebody may book" }),
11960
- /* @__PURE__ */ jsx47(
12306
+ /* @__PURE__ */ jsxs44("label", { className: "flex flex-col gap-1", children: [
12307
+ /* @__PURE__ */ jsx48(Label8, { className: "text-xs font-medium", children: "Earliest somebody may book" }),
12308
+ /* @__PURE__ */ jsx48(
11961
12309
  "select",
11962
12310
  {
11963
12311
  className: "h-9 rounded border bg-background px-2 text-sm",
11964
12312
  value: lead,
11965
12313
  onChange: (e) => setLead(Number(e.target.value)),
11966
- children: [0, 60, 120, 240, 1440].map((n) => /* @__PURE__ */ jsx47("option", { value: n, children: n === 0 ? "Right away" : n === 1440 ? "A day from now" : `${n / 60} hour${n === 60 ? "" : "s"} from now` }, n))
12314
+ children: [0, 60, 120, 240, 1440].map((n) => /* @__PURE__ */ jsx48("option", { value: n, children: n === 0 ? "Right away" : n === 1440 ? "A day from now" : `${n / 60} hour${n === 60 ? "" : "s"} from now` }, n))
11967
12315
  }
11968
12316
  )
11969
12317
  ] }),
11970
- /* @__PURE__ */ jsxs43("label", { className: "flex flex-col gap-1", children: [
11971
- /* @__PURE__ */ jsx47(Label7, { className: "text-xs font-medium", children: "Most in one day" }),
11972
- /* @__PURE__ */ jsx47(
11973
- BasicInput14,
12318
+ /* @__PURE__ */ jsxs44("label", { className: "flex flex-col gap-1", children: [
12319
+ /* @__PURE__ */ jsx48(Label8, { className: "text-xs font-medium", children: "Most in one day" }),
12320
+ /* @__PURE__ */ jsx48(
12321
+ BasicInput15,
11974
12322
  {
11975
12323
  type: "number",
11976
12324
  min: 1,
@@ -11979,15 +12327,15 @@ function BookingBuilder({ tableId, bookingId, onSaved, onClose, className }) {
11979
12327
  }
11980
12328
  )
11981
12329
  ] }),
11982
- /* @__PURE__ */ jsxs43("label", { className: "flex flex-col gap-1", children: [
11983
- /* @__PURE__ */ jsx47(Label7, { className: "text-xs font-medium", children: "How far ahead the page offers" }),
11984
- /* @__PURE__ */ jsx47(
12330
+ /* @__PURE__ */ jsxs44("label", { className: "flex flex-col gap-1", children: [
12331
+ /* @__PURE__ */ jsx48(Label8, { className: "text-xs font-medium", children: "How far ahead the page offers" }),
12332
+ /* @__PURE__ */ jsx48(
11985
12333
  "select",
11986
12334
  {
11987
12335
  className: "h-9 rounded border bg-background px-2 text-sm",
11988
12336
  value: days,
11989
12337
  onChange: (e) => setDays(Number(e.target.value)),
11990
- children: [7, 14, 30, 60, 90].map((n) => /* @__PURE__ */ jsxs43("option", { value: n, children: [
12338
+ children: [7, 14, 30, 60, 90].map((n) => /* @__PURE__ */ jsxs44("option", { value: n, children: [
11991
12339
  n,
11992
12340
  " days"
11993
12341
  ] }, n))
@@ -11995,12 +12343,12 @@ function BookingBuilder({ tableId, bookingId, onSaved, onClose, className }) {
11995
12343
  )
11996
12344
  ] })
11997
12345
  ] }),
11998
- /* @__PURE__ */ jsxs43("div", { className: "flex flex-col gap-1.5", children: [
11999
- /* @__PURE__ */ jsx47(Label7, { className: "text-xs font-medium", children: "Hours you are free" }),
12000
- windows.map((w, i) => /* @__PURE__ */ jsxs43("div", { className: "flex items-center gap-2", children: [
12001
- /* @__PURE__ */ jsxs43("label", { className: "flex w-20 items-center gap-1.5 text-xs", children: [
12002
- /* @__PURE__ */ jsx47(
12003
- Checkbox6,
12346
+ /* @__PURE__ */ jsxs44("div", { className: "flex flex-col gap-1.5", children: [
12347
+ /* @__PURE__ */ jsx48(Label8, { className: "text-xs font-medium", children: "Hours you are free" }),
12348
+ windows.map((w, i) => /* @__PURE__ */ jsxs44("div", { className: "flex items-center gap-2", children: [
12349
+ /* @__PURE__ */ jsxs44("label", { className: "flex w-20 items-center gap-1.5 text-xs", children: [
12350
+ /* @__PURE__ */ jsx48(
12351
+ Checkbox7,
12004
12352
  {
12005
12353
  checked: w.on,
12006
12354
  onCheckedChange: (on) => setWindows((prev) => prev.map((x, j) => i === j ? { ...x, on: Boolean(on) } : x))
@@ -12008,9 +12356,9 @@ function BookingBuilder({ tableId, bookingId, onSaved, onClose, className }) {
12008
12356
  ),
12009
12357
  DAYS[i]?.label
12010
12358
  ] }),
12011
- w.on ? /* @__PURE__ */ jsxs43(Fragment21, { children: [
12012
- /* @__PURE__ */ jsx47(
12013
- BasicInput14,
12359
+ w.on ? /* @__PURE__ */ jsxs44(Fragment22, { children: [
12360
+ /* @__PURE__ */ jsx48(
12361
+ BasicInput15,
12014
12362
  {
12015
12363
  type: "time",
12016
12364
  className: "h-8 w-28",
@@ -12018,9 +12366,9 @@ function BookingBuilder({ tableId, bookingId, onSaved, onClose, className }) {
12018
12366
  onChange: (e) => setWindows((prev) => prev.map((x, j) => i === j ? { ...x, from: e.target.value } : x))
12019
12367
  }
12020
12368
  ),
12021
- /* @__PURE__ */ jsx47("span", { className: "text-xs text-muted-foreground", children: "to" }),
12022
- /* @__PURE__ */ jsx47(
12023
- BasicInput14,
12369
+ /* @__PURE__ */ jsx48("span", { className: "text-xs text-muted-foreground", children: "to" }),
12370
+ /* @__PURE__ */ jsx48(
12371
+ BasicInput15,
12024
12372
  {
12025
12373
  type: "time",
12026
12374
  className: "h-8 w-28",
@@ -12028,14 +12376,14 @@ function BookingBuilder({ tableId, bookingId, onSaved, onClose, className }) {
12028
12376
  onChange: (e) => setWindows((prev) => prev.map((x, j) => i === j ? { ...x, to: e.target.value } : x))
12029
12377
  }
12030
12378
  )
12031
- ] }) : /* @__PURE__ */ jsx47("span", { className: "text-xs text-muted-foreground", children: "not taking bookings" })
12379
+ ] }) : /* @__PURE__ */ jsx48("span", { className: "text-xs text-muted-foreground", children: "not taking bookings" })
12032
12380
  ] }, w.weekday))
12033
12381
  ] }),
12034
- /* @__PURE__ */ jsxs43("div", { className: "flex flex-col gap-1.5", children: [
12035
- /* @__PURE__ */ jsx47(Label7, { className: "text-xs font-medium", children: "What to ask the person booking" }),
12036
- askable.length === 0 ? /* @__PURE__ */ jsx47("p", { className: "text-xs text-muted-foreground", children: "This table has no fields yet, so there is nothing a booking page could ask for. Add a field first \u2014 every question is one of this table's own fields." }) : /* @__PURE__ */ jsx47("div", { className: "flex flex-wrap gap-x-4 gap-y-1.5", children: askable.map((f) => /* @__PURE__ */ jsxs43("label", { className: "flex items-center gap-1.5 text-xs", children: [
12037
- /* @__PURE__ */ jsx47(
12038
- Checkbox6,
12382
+ /* @__PURE__ */ jsxs44("div", { className: "flex flex-col gap-1.5", children: [
12383
+ /* @__PURE__ */ jsx48(Label8, { className: "text-xs font-medium", children: "What to ask the person booking" }),
12384
+ askable.length === 0 ? /* @__PURE__ */ jsx48("p", { className: "text-xs text-muted-foreground", children: "This table has no fields yet, so there is nothing a booking page could ask for. Add a field first \u2014 every question is one of this table's own fields." }) : /* @__PURE__ */ jsx48("div", { className: "flex flex-wrap gap-x-4 gap-y-1.5", children: askable.map((f) => /* @__PURE__ */ jsxs44("label", { className: "flex items-center gap-1.5 text-xs", children: [
12385
+ /* @__PURE__ */ jsx48(
12386
+ Checkbox7,
12039
12387
  {
12040
12388
  checked: asked.includes(f.key),
12041
12389
  onCheckedChange: (on) => setAsked((prev) => on ? [...prev, f.key] : prev.filter((k) => k !== f.key))
@@ -12043,11 +12391,11 @@ function BookingBuilder({ tableId, bookingId, onSaved, onClose, className }) {
12043
12391
  ),
12044
12392
  fieldName(f)
12045
12393
  ] }, f.key)) }),
12046
- /* @__PURE__ */ jsx47("p", { className: "text-xs text-muted-foreground", children: "The time, whether it is booked or cancelled, and who it is with are filled in by the store \u2014 a booking page cannot ask a visitor for any of the three." })
12394
+ /* @__PURE__ */ jsx48("p", { className: "text-xs text-muted-foreground", children: "The time, whether it is booked or cancelled, and who it is with are filled in by the store \u2014 a booking page cannot ask a visitor for any of the three." })
12047
12395
  ] }),
12048
- /* @__PURE__ */ jsxs43("label", { className: "flex flex-col gap-1", children: [
12049
- /* @__PURE__ */ jsx47(Label7, { className: "text-xs font-medium", children: "What they see after booking" }),
12050
- /* @__PURE__ */ jsx47(
12396
+ /* @__PURE__ */ jsxs44("label", { className: "flex flex-col gap-1", children: [
12397
+ /* @__PURE__ */ jsx48(Label8, { className: "text-xs font-medium", children: "What they see after booking" }),
12398
+ /* @__PURE__ */ jsx48(
12051
12399
  BasicTextarea7,
12052
12400
  {
12053
12401
  rows: 2,
@@ -12057,7 +12405,7 @@ function BookingBuilder({ tableId, bookingId, onSaved, onClose, className }) {
12057
12405
  }
12058
12406
  )
12059
12407
  ] }),
12060
- shown ? /* @__PURE__ */ jsxs43("p", { className: "rounded-md border bg-muted/40 p-2.5 text-xs", children: [
12408
+ shown ? /* @__PURE__ */ jsxs44("p", { className: "rounded-md border bg-muted/40 p-2.5 text-xs", children: [
12061
12409
  "Saved. The page offers ",
12062
12410
  shown.slot_minutes,
12063
12411
  "-minute appointments in ",
@@ -12076,10 +12424,10 @@ function BookingBuilder({ tableId, bookingId, onSaved, onClose, className }) {
12076
12424
  ).join(", "),
12077
12425
  "."
12078
12426
  ] }) : null,
12079
- url ? /* @__PURE__ */ jsxs43("div", { className: "flex flex-wrap items-center gap-2 rounded-md border p-2.5", children: [
12080
- /* @__PURE__ */ jsx47("span", { className: "min-w-0 flex-1 break-all text-xs", children: url }),
12081
- /* @__PURE__ */ jsx47(
12082
- Button38,
12427
+ url ? /* @__PURE__ */ jsxs44("div", { className: "flex flex-wrap items-center gap-2 rounded-md border p-2.5", children: [
12428
+ /* @__PURE__ */ jsx48("span", { className: "min-w-0 flex-1 break-all text-xs", children: url }),
12429
+ /* @__PURE__ */ jsx48(
12430
+ Button39,
12083
12431
  {
12084
12432
  size: "sm",
12085
12433
  variant: "outline",
@@ -12092,8 +12440,8 @@ function BookingBuilder({ tableId, bookingId, onSaved, onClose, className }) {
12092
12440
  children: copied ? "Copied" : "Copy link"
12093
12441
  }
12094
12442
  ),
12095
- /* @__PURE__ */ jsx47(
12096
- Button38,
12443
+ /* @__PURE__ */ jsx48(
12444
+ Button39,
12097
12445
  {
12098
12446
  size: "sm",
12099
12447
  disabled: publishing,
@@ -12101,17 +12449,17 @@ function BookingBuilder({ tableId, bookingId, onSaved, onClose, className }) {
12101
12449
  children: publishing ? "\u2026" : existing?.published_at && !existing.closed_at ? "Unpublish" : "Publish"
12102
12450
  }
12103
12451
  ),
12104
- /* @__PURE__ */ jsx47("p", { className: "w-full text-xs text-muted-foreground", children: existing?.published_at && !existing.closed_at ? "Open. Anyone with this link can book a time." : "Not open yet \u2014 the link does not take bookings until you publish it." })
12452
+ /* @__PURE__ */ jsx48("p", { className: "w-full text-xs text-muted-foreground", children: existing?.published_at && !existing.closed_at ? "Open. Anyone with this link can book a time." : "Not open yet \u2014 the link does not take bookings until you publish it." })
12105
12453
  ] }) : null
12106
12454
  ] });
12107
12455
  }
12108
12456
 
12109
12457
  // src/BookingSlots.tsx
12110
- import { useCallback as useCallback29, useEffect as useEffect33, useMemo as useMemo28, useState as useState43 } from "react";
12111
- import { useRecordsClient as useRecordsClient35, useMyLevels as useMyLevels2 } from "@ai-matrx/records/react";
12458
+ import { useCallback as useCallback30, useEffect as useEffect34, useMemo as useMemo29, useState as useState44 } from "react";
12459
+ import { useRecordsClient as useRecordsClient36, useMyLevels as useMyLevels2 } from "@ai-matrx/records/react";
12112
12460
  import { bookingPath as bookingPath2 } from "@ai-matrx/records";
12113
- import { Badge as Badge16, Button as Button39, Skeleton as Skeleton26, cn as cn42 } from "@ai-matrx/design-system";
12114
- import { Fragment as Fragment22, jsx as jsx48, jsxs as jsxs44 } from "react/jsx-runtime";
12461
+ import { Badge as Badge16, Button as Button40, Skeleton as Skeleton27, cn as cn43 } from "@ai-matrx/design-system";
12462
+ import { Fragment as Fragment23, jsx as jsx49, jsxs as jsxs45 } from "react/jsx-runtime";
12115
12463
  var WHAT_A_BOOKING_PAGE_IS = "A booking page offers times you are free and writes each appointment into this table as an ordinary record.";
12116
12464
  function bookingSuggestion(tableName2) {
12117
12465
  const subject = tableName2?.trim() ? tableName2.trim() : "appointments";
@@ -12125,15 +12473,15 @@ var STATE_WORDS = {
12125
12473
  full: "Every time you offered is taken."
12126
12474
  };
12127
12475
  function BookingSlots({ tableId, className }) {
12128
- const client = useRecordsClient35();
12476
+ const client = useRecordsClient36();
12129
12477
  const host = useRecordsUi();
12130
- const [pages, setPages] = useState43(null);
12131
- const [error, setError] = useState43(null);
12132
- const [busy, setBusy] = useState43(null);
12133
- const [copied, setCopied] = useState43(null);
12134
- const [shown, setShown] = useState43(null);
12135
- const [building, setBuilding] = useState43(false);
12136
- const load = useCallback29(async () => {
12478
+ const [pages, setPages] = useState44(null);
12479
+ const [error, setError] = useState44(null);
12480
+ const [busy, setBusy] = useState44(null);
12481
+ const [copied, setCopied] = useState44(null);
12482
+ const [shown, setShown] = useState44(null);
12483
+ const [building, setBuilding] = useState44(false);
12484
+ const load = useCallback30(async () => {
12137
12485
  const answered = await client.bookings(tableId ? { table_id: tableId } : {});
12138
12486
  if (!answered.ok) {
12139
12487
  setError(answered.error);
@@ -12143,15 +12491,15 @@ function BookingSlots({ tableId, className }) {
12143
12491
  setError(null);
12144
12492
  setPages(answered.data);
12145
12493
  }, [client, tableId]);
12146
- useEffect33(() => {
12494
+ useEffect34(() => {
12147
12495
  void load();
12148
12496
  }, [load]);
12149
- const subjectIds = useMemo28(
12497
+ const subjectIds = useMemo29(
12150
12498
  () => Array.from(new Set((pages ?? []).map((p) => p.table_id))),
12151
12499
  [pages]
12152
12500
  );
12153
12501
  const levels = useMyLevels2(subjectIds);
12154
- const toggle = useCallback29(
12502
+ const toggle = useCallback30(
12155
12503
  async (page) => {
12156
12504
  setBusy(page.form_id);
12157
12505
  const wanted = page.published_at === null || page.closed_at !== null;
@@ -12165,7 +12513,7 @@ function BookingSlots({ tableId, className }) {
12165
12513
  },
12166
12514
  [client, load]
12167
12515
  );
12168
- const copy = useCallback29(async (url, formId) => {
12516
+ const copy = useCallback30(async (url, formId) => {
12169
12517
  try {
12170
12518
  await navigator.clipboard.writeText(url);
12171
12519
  setCopied(formId);
@@ -12175,18 +12523,18 @@ function BookingSlots({ tableId, className }) {
12175
12523
  setShown(url);
12176
12524
  }
12177
12525
  }, []);
12178
- if (pages === null) return /* @__PURE__ */ jsx48(Skeleton26, { className: cn42("h-32 w-full", className) });
12526
+ if (pages === null) return /* @__PURE__ */ jsx49(Skeleton27, { className: cn43("h-32 w-full", className) });
12179
12527
  if (pages.length === 0 && !tableId && !error && !building) return null;
12180
12528
  const origin = host.publicOrigin ?? (typeof window === "undefined" ? "" : window.location.origin);
12181
- return /* @__PURE__ */ jsxs44("section", { className: cn42("flex flex-col gap-3", className), "data-testid": "booking-slots", children: [
12182
- /* @__PURE__ */ jsxs44("header", { className: "flex items-center gap-2", children: [
12183
- /* @__PURE__ */ jsx48("h3", { className: "text-sm font-medium", children: "Bookings" }),
12184
- /* @__PURE__ */ jsx48("span", { className: "text-xs text-muted-foreground", children: pages.length === 0 ? "none yet" : `${pages.length}` }),
12185
- /* @__PURE__ */ jsx48("div", { className: "flex-1" }),
12186
- tableId && pages.length > 0 ? /* @__PURE__ */ jsx48(Button39, { size: "sm", variant: building ? "secondary" : "ghost", onClick: () => setBuilding((b) => !b), children: building ? "Done building" : "Build a booking page" }) : null
12529
+ return /* @__PURE__ */ jsxs45("section", { className: cn43("flex flex-col gap-3", className), "data-testid": "booking-slots", children: [
12530
+ /* @__PURE__ */ jsxs45("header", { className: "flex items-center gap-2", children: [
12531
+ /* @__PURE__ */ jsx49("h3", { className: "text-sm font-medium", children: "Bookings" }),
12532
+ /* @__PURE__ */ jsx49("span", { className: "text-xs text-muted-foreground", children: pages.length === 0 ? "none yet" : `${pages.length}` }),
12533
+ /* @__PURE__ */ jsx49("div", { className: "flex-1" }),
12534
+ tableId && pages.length > 0 ? /* @__PURE__ */ jsx49(Button40, { size: "sm", variant: building ? "secondary" : "ghost", onClick: () => setBuilding((b) => !b), children: building ? "Done building" : "Build a booking page" }) : null
12187
12535
  ] }),
12188
- error ? /* @__PURE__ */ jsx48(RefusalNotice, { error }) : null,
12189
- building && tableId ? /* @__PURE__ */ jsx48(
12536
+ error ? /* @__PURE__ */ jsx49(RefusalNotice, { error }) : null,
12537
+ building && tableId ? /* @__PURE__ */ jsx49(
12190
12538
  BookingBuilder,
12191
12539
  {
12192
12540
  tableId,
@@ -12196,7 +12544,7 @@ function BookingSlots({ tableId, className }) {
12196
12544
  onClose: () => setBuilding(false)
12197
12545
  }
12198
12546
  ) : null,
12199
- pages.length === 0 && !building ? tableId ? /* @__PURE__ */ jsx48(
12547
+ pages.length === 0 && !building ? tableId ? /* @__PURE__ */ jsx49(
12200
12548
  BuildOrAsk,
12201
12549
  {
12202
12550
  mayBuild: true,
@@ -12212,47 +12560,47 @@ function BookingSlots({ tableId, className }) {
12212
12560
  buildLabel: "Build the booking page",
12213
12561
  children: WHAT_A_BOOKING_PAGE_IS
12214
12562
  }
12215
- ) : /* @__PURE__ */ jsxs44("p", { className: "text-xs text-muted-foreground", children: [
12563
+ ) : /* @__PURE__ */ jsxs45("p", { className: "text-xs text-muted-foreground", children: [
12216
12564
  WHAT_A_BOOKING_PAGE_IS,
12217
12565
  " Open the table the appointments should land in to make one."
12218
12566
  ] }) : null,
12219
- /* @__PURE__ */ jsx48("ul", { className: "flex flex-col gap-2", children: pages.map((page) => {
12567
+ /* @__PURE__ */ jsx49("ul", { className: "flex flex-col gap-2", children: pages.map((page) => {
12220
12568
  const url = `${origin}${bookingPath2(page.form_id)}`;
12221
12569
  const mayOpen = levels.data?.[page.table_id] === "admin";
12222
12570
  const open = page.published_at !== null && page.closed_at === null;
12223
- return /* @__PURE__ */ jsxs44("li", { className: "rounded-md border p-2.5", "data-testid": "booking-page", children: [
12224
- /* @__PURE__ */ jsxs44("div", { className: "flex items-center gap-2", children: [
12225
- /* @__PURE__ */ jsx48("span", { className: "min-w-0 flex-1 truncate text-sm font-medium", children: page.title ?? page.slug }),
12226
- /* @__PURE__ */ jsx48(Badge16, { variant: page.state === "open" ? "default" : "secondary", children: page.state })
12571
+ return /* @__PURE__ */ jsxs45("li", { className: "rounded-md border p-2.5", "data-testid": "booking-page", children: [
12572
+ /* @__PURE__ */ jsxs45("div", { className: "flex items-center gap-2", children: [
12573
+ /* @__PURE__ */ jsx49("span", { className: "min-w-0 flex-1 truncate text-sm font-medium", children: page.title ?? page.slug }),
12574
+ /* @__PURE__ */ jsx49(Badge16, { variant: page.state === "open" ? "default" : "secondary", children: page.state })
12227
12575
  ] }),
12228
- /* @__PURE__ */ jsx48("p", { className: "mt-0.5 text-xs text-muted-foreground", children: STATE_WORDS[page.state] ?? page.state }),
12229
- /* @__PURE__ */ jsxs44("p", { className: "mt-1.5 text-xs", children: [
12230
- /* @__PURE__ */ jsx48("span", { className: "font-medium", children: page.upcoming }),
12576
+ /* @__PURE__ */ jsx49("p", { className: "mt-0.5 text-xs text-muted-foreground", children: STATE_WORDS[page.state] ?? page.state }),
12577
+ /* @__PURE__ */ jsxs45("p", { className: "mt-1.5 text-xs", children: [
12578
+ /* @__PURE__ */ jsx49("span", { className: "font-medium", children: page.upcoming }),
12231
12579
  " coming up",
12232
12580
  " \xB7 ",
12233
- /* @__PURE__ */ jsx48("span", { className: "font-medium", children: page.booked }),
12581
+ /* @__PURE__ */ jsx49("span", { className: "font-medium", children: page.booked }),
12234
12582
  " booked in all",
12235
- page.held > 0 ? /* @__PURE__ */ jsxs44(Fragment22, { children: [
12583
+ page.held > 0 ? /* @__PURE__ */ jsxs45(Fragment23, { children: [
12236
12584
  " \xB7 ",
12237
- /* @__PURE__ */ jsx48("span", { className: "font-medium", children: page.held }),
12585
+ /* @__PURE__ */ jsx49("span", { className: "font-medium", children: page.held }),
12238
12586
  " being held right now"
12239
12587
  ] }) : null,
12240
- page.cancelled > 0 ? /* @__PURE__ */ jsxs44(Fragment22, { children: [
12588
+ page.cancelled > 0 ? /* @__PURE__ */ jsxs45(Fragment23, { children: [
12241
12589
  " \xB7 ",
12242
- /* @__PURE__ */ jsx48("span", { className: "font-medium", children: page.cancelled }),
12590
+ /* @__PURE__ */ jsx49("span", { className: "font-medium", children: page.cancelled }),
12243
12591
  " cancelled"
12244
12592
  ] }) : null,
12245
- /* @__PURE__ */ jsxs44("span", { className: "text-muted-foreground", children: [
12593
+ /* @__PURE__ */ jsxs45("span", { className: "text-muted-foreground", children: [
12246
12594
  " \xB7 ",
12247
12595
  page.slot_minutes,
12248
12596
  " minutes each"
12249
12597
  ] })
12250
12598
  ] }),
12251
- /* @__PURE__ */ jsx48("p", { className: "mt-1 text-xs text-muted-foreground", children: nextInWords(page) }),
12252
- /* @__PURE__ */ jsxs44("div", { className: "mt-2 flex flex-wrap items-center gap-1.5", children: [
12253
- /* @__PURE__ */ jsx48(Button39, { size: "sm", variant: "outline", onClick: () => void copy(url, page.form_id), children: copied === page.form_id ? "Copied" : "Copy link" }),
12254
- mayOpen ? /* @__PURE__ */ jsx48(
12255
- Button39,
12599
+ /* @__PURE__ */ jsx49("p", { className: "mt-1 text-xs text-muted-foreground", children: nextInWords(page) }),
12600
+ /* @__PURE__ */ jsxs45("div", { className: "mt-2 flex flex-wrap items-center gap-1.5", children: [
12601
+ /* @__PURE__ */ jsx49(Button40, { size: "sm", variant: "outline", onClick: () => void copy(url, page.form_id), children: copied === page.form_id ? "Copied" : "Copy link" }),
12602
+ mayOpen ? /* @__PURE__ */ jsx49(
12603
+ Button40,
12256
12604
  {
12257
12605
  size: "sm",
12258
12606
  variant: "ghost",
@@ -12262,13 +12610,13 @@ function BookingSlots({ tableId, className }) {
12262
12610
  }
12263
12611
  ) : null
12264
12612
  ] }),
12265
- shown && shown === url ? /* @__PURE__ */ jsxs44("p", { className: "mt-1.5 break-all rounded border border-dashed px-2 py-1 text-xs", children: [
12613
+ shown && shown === url ? /* @__PURE__ */ jsxs45("p", { className: "mt-1.5 break-all rounded border border-dashed px-2 py-1 text-xs", children: [
12266
12614
  "This browser would not let the page copy for you, so here it is to copy by hand: ",
12267
12615
  url
12268
12616
  ] }) : null
12269
12617
  ] }, page.form_id);
12270
12618
  }) }),
12271
- pages.length > 0 && !levels.loading && levels.data && subjectIds.every((id) => levels.data?.[id] !== "admin") ? /* @__PURE__ */ jsx48("p", { className: "text-xs text-muted-foreground", children: NO_ADMIN2 }) : null
12619
+ pages.length > 0 && !levels.loading && levels.data && subjectIds.every((id) => levels.data?.[id] !== "admin") ? /* @__PURE__ */ jsx49("p", { className: "text-xs text-muted-foreground", children: NO_ADMIN2 }) : null
12272
12620
  ] });
12273
12621
  }
12274
12622
  function nextInWords(page) {
@@ -12300,18 +12648,18 @@ function nextInWords(page) {
12300
12648
  }
12301
12649
 
12302
12650
  // src/CaptureSheet.tsx
12303
- import { useCallback as useCallback31, useEffect as useEffect35, useRef as useRef13, useState as useState45 } from "react";
12304
- import { useFields as useFields21, useRecordsClient as useRecordsClient37, useTable as useTable20 } from "@ai-matrx/records/react";
12305
- import { Button as Button41, Input as Input4, Skeleton as Skeleton28, Textarea as Textarea3, cn as cn44 } from "@ai-matrx/design-system";
12651
+ import { useCallback as useCallback32, useEffect as useEffect36, useRef as useRef13, useState as useState46 } from "react";
12652
+ import { useFields as useFields21, useRecordsClient as useRecordsClient38, useTable as useTable20 } from "@ai-matrx/records/react";
12653
+ import { Button as Button42, Input as Input4, Skeleton as Skeleton29, Textarea as Textarea3, cn as cn45 } from "@ai-matrx/design-system";
12306
12654
 
12307
12655
  // src/CaptureRun.tsx
12308
- import { useCallback as useCallback30, useEffect as useEffect34, useMemo as useMemo29, useRef as useRef12, useState as useState44 } from "react";
12656
+ import { useCallback as useCallback31, useEffect as useEffect35, useMemo as useMemo30, useRef as useRef12, useState as useState45 } from "react";
12309
12657
  import {
12310
12658
  openCaptureQueue
12311
12659
  } from "@ai-matrx/records";
12312
- import { useRecordsClient as useRecordsClient36 } from "@ai-matrx/records/react";
12313
- import { Button as Button40, Input as Input3, Skeleton as Skeleton27, Textarea as Textarea2, cn as cn43 } from "@ai-matrx/design-system";
12314
- import { jsx as jsx49, jsxs as jsxs45 } from "react/jsx-runtime";
12660
+ import { useRecordsClient as useRecordsClient37 } from "@ai-matrx/records/react";
12661
+ import { Button as Button41, Input as Input3, Skeleton as Skeleton28, Textarea as Textarea2, cn as cn44 } from "@ai-matrx/design-system";
12662
+ import { jsx as jsx50, jsxs as jsxs46 } from "react/jsx-runtime";
12315
12663
  function controlFor(field) {
12316
12664
  if (!field) return "text";
12317
12665
  const label = `${field.key} ${field.label ?? ""}`.toLowerCase();
@@ -12348,21 +12696,21 @@ function whereWeAre(timeoutMs = 4e3) {
12348
12696
  });
12349
12697
  }
12350
12698
  function CaptureRun({ sheetId, face: given, className }) {
12351
- const client = useRecordsClient36();
12699
+ const client = useRecordsClient37();
12352
12700
  const host = useRecordsUi();
12353
- const [face, setFace] = useState44(given);
12354
- const [loadFailed, setLoadFailed] = useState44(null);
12355
- const [at, setAt] = useState44(0);
12356
- const [answers, setAnswers] = useState44({});
12357
- const [files, setFiles] = useState44({});
12358
- const [missing, setMissing] = useState44(null);
12359
- const [done, setDone] = useState44(null);
12360
- const [counts, setCounts] = useState44({ waiting: 0, sending: 0, refused: 0, landed: 0 });
12361
- const [items, setItems] = useState44([]);
12362
- const [lastSynced, setLastSynced] = useState44(null);
12363
- const [sending, setSending] = useState44(false);
12701
+ const [face, setFace] = useState45(given);
12702
+ const [loadFailed, setLoadFailed] = useState45(null);
12703
+ const [at, setAt] = useState45(0);
12704
+ const [answers, setAnswers] = useState45({});
12705
+ const [files, setFiles] = useState45({});
12706
+ const [missing, setMissing] = useState45(null);
12707
+ const [done, setDone] = useState45(null);
12708
+ const [counts, setCounts] = useState45({ waiting: 0, sending: 0, refused: 0, landed: 0 });
12709
+ const [items, setItems] = useState45([]);
12710
+ const [lastSynced, setLastSynced] = useState45(null);
12711
+ const [sending, setSending] = useState45(false);
12364
12712
  const queueRef = useRef12(null);
12365
- useEffect34(() => {
12713
+ useEffect35(() => {
12366
12714
  const q2 = openCaptureQueue({
12367
12715
  onChange: (c, all) => {
12368
12716
  setCounts(c);
@@ -12400,7 +12748,7 @@ function CaptureRun({ sheetId, face: given, className }) {
12400
12748
  void q2.sync().then(() => void q2.lastSyncedAt().then(setLastSynced));
12401
12749
  return () => q2.dispose();
12402
12750
  }, [client, host]);
12403
- useEffect34(() => {
12751
+ useEffect35(() => {
12404
12752
  if (given !== void 0) return;
12405
12753
  let cancelled = false;
12406
12754
  void client.captureOpen({ sheet_id: sheetId }).then((res) => {
@@ -12412,7 +12760,7 @@ function CaptureRun({ sheetId, face: given, className }) {
12412
12760
  cancelled = true;
12413
12761
  };
12414
12762
  }, [client, given, sheetId]);
12415
- const questions = useMemo29(() => {
12763
+ const questions = useMemo30(() => {
12416
12764
  const asked = face?.presentation?.questions ?? [];
12417
12765
  if (asked.length > 0) return asked;
12418
12766
  return (face?.fields ?? []).map((f) => ({
@@ -12422,11 +12770,11 @@ function CaptureRun({ sheetId, face: given, className }) {
12422
12770
  required: f.required
12423
12771
  }));
12424
12772
  }, [face]);
12425
- const fieldOf = useCallback30(
12773
+ const fieldOf = useCallback31(
12426
12774
  (key) => (face?.fields ?? []).find((f) => f.key === key),
12427
12775
  [face]
12428
12776
  );
12429
- const sync = useCallback30(async () => {
12777
+ const sync = useCallback31(async () => {
12430
12778
  const q2 = queueRef.current;
12431
12779
  if (!q2) return;
12432
12780
  setSending(true);
@@ -12439,7 +12787,7 @@ function CaptureRun({ sheetId, face: given, className }) {
12439
12787
  setSending(false);
12440
12788
  }
12441
12789
  }, []);
12442
- const answered = useCallback30(
12790
+ const answered = useCallback31(
12443
12791
  (key) => {
12444
12792
  if (files[key]) return true;
12445
12793
  const v = answers[key];
@@ -12484,24 +12832,24 @@ function CaptureRun({ sheetId, face: given, className }) {
12484
12832
  if (online) await sync();
12485
12833
  }
12486
12834
  if (loadFailed) {
12487
- return /* @__PURE__ */ jsx49("section", { className: cn43("mx-auto w-full max-w-sm p-4", className), children: /* @__PURE__ */ jsx49("p", { className: "text-sm text-destructive", "data-testid": "capture-refusal", children: loadFailed }) });
12835
+ return /* @__PURE__ */ jsx50("section", { className: cn44("mx-auto w-full max-w-sm p-4", className), children: /* @__PURE__ */ jsx50("p", { className: "text-sm text-destructive", "data-testid": "capture-refusal", children: loadFailed }) });
12488
12836
  }
12489
12837
  if (face === void 0) {
12490
- return /* @__PURE__ */ jsx49(Skeleton27, { className: cn43("mx-auto h-64 w-full max-w-sm", className) });
12838
+ return /* @__PURE__ */ jsx50(Skeleton28, { className: cn44("mx-auto h-64 w-full max-w-sm", className) });
12491
12839
  }
12492
12840
  if (face === null) {
12493
- return /* @__PURE__ */ jsx49("section", { className: cn43("mx-auto w-full max-w-sm p-4", className), children: /* @__PURE__ */ jsx49("p", { className: "text-sm", "data-testid": "capture-refusal", children: "This capture sheet is not here, or it is not yours to open. If somebody sent you this link, ask them to check it." }) });
12841
+ return /* @__PURE__ */ jsx50("section", { className: cn44("mx-auto w-full max-w-sm p-4", className), children: /* @__PURE__ */ jsx50("p", { className: "text-sm", "data-testid": "capture-refusal", children: "This capture sheet is not here, or it is not yours to open. If somebody sent you this link, ask them to check it." }) });
12494
12842
  }
12495
12843
  const waiting = counts.waiting + counts.sending;
12496
- const queueLine = /* @__PURE__ */ jsxs45("div", { className: "flex items-center gap-2 text-[11px] text-muted-foreground", "data-testid": "capture-queue-line", children: [
12497
- /* @__PURE__ */ jsx49("span", { className: "tabular-nums", "data-testid": "capture-waiting", children: waiting === 0 ? "Nothing waiting" : `${waiting} waiting` }),
12498
- counts.refused > 0 ? /* @__PURE__ */ jsxs45("span", { className: "text-destructive tabular-nums", "data-testid": "capture-refused", children: [
12844
+ const queueLine = /* @__PURE__ */ jsxs46("div", { className: "flex items-center gap-2 text-[11px] text-muted-foreground", "data-testid": "capture-queue-line", children: [
12845
+ /* @__PURE__ */ jsx50("span", { className: "tabular-nums", "data-testid": "capture-waiting", children: waiting === 0 ? "Nothing waiting" : `${waiting} waiting` }),
12846
+ counts.refused > 0 ? /* @__PURE__ */ jsxs46("span", { className: "text-destructive tabular-nums", "data-testid": "capture-refused", children: [
12499
12847
  counts.refused,
12500
12848
  " to fix"
12501
12849
  ] }) : null,
12502
- /* @__PURE__ */ jsx49("span", { className: "ml-auto", "data-testid": "capture-last-synced", children: lastSynced ? `Last synced ${new Date(lastSynced).toLocaleTimeString()}` : "Not synced yet" }),
12503
- waiting > 0 || counts.refused > 0 ? /* @__PURE__ */ jsx49(
12504
- Button40,
12850
+ /* @__PURE__ */ jsx50("span", { className: "ml-auto", "data-testid": "capture-last-synced", children: lastSynced ? `Last synced ${new Date(lastSynced).toLocaleTimeString()}` : "Not synced yet" }),
12851
+ waiting > 0 || counts.refused > 0 ? /* @__PURE__ */ jsx50(
12852
+ Button41,
12505
12853
  {
12506
12854
  size: "sm",
12507
12855
  variant: "ghost",
@@ -12513,11 +12861,11 @@ function CaptureRun({ sheetId, face: given, className }) {
12513
12861
  }
12514
12862
  ) : null
12515
12863
  ] });
12516
- const queuePanel = items.filter((i) => i.state !== "landed").length > 0 ? /* @__PURE__ */ jsx49("ul", { className: "flex flex-col gap-1 rounded border p-2", "data-testid": "capture-queue", children: items.filter((i) => i.state !== "landed").map((i) => /* @__PURE__ */ jsxs45("li", { className: "flex items-start gap-2 text-[11px]", children: [
12517
- /* @__PURE__ */ jsx49("span", { className: "tabular-nums text-muted-foreground", children: new Date(i.captured_at).toLocaleTimeString() }),
12518
- /* @__PURE__ */ jsx49("span", { className: cn43("flex-1", i.state === "refused" && "text-destructive"), children: i.state === "refused" ? i.last_error ?? "This one was refused." : i.last_error ?? "Waiting for a signal." }),
12519
- i.state === "refused" ? /* @__PURE__ */ jsx49(
12520
- Button40,
12864
+ const queuePanel = items.filter((i) => i.state !== "landed").length > 0 ? /* @__PURE__ */ jsx50("ul", { className: "flex flex-col gap-1 rounded border p-2", "data-testid": "capture-queue", children: items.filter((i) => i.state !== "landed").map((i) => /* @__PURE__ */ jsxs46("li", { className: "flex items-start gap-2 text-[11px]", children: [
12865
+ /* @__PURE__ */ jsx50("span", { className: "tabular-nums text-muted-foreground", children: new Date(i.captured_at).toLocaleTimeString() }),
12866
+ /* @__PURE__ */ jsx50("span", { className: cn44("flex-1", i.state === "refused" && "text-destructive"), children: i.state === "refused" ? i.last_error ?? "This one was refused." : i.last_error ?? "Waiting for a signal." }),
12867
+ i.state === "refused" ? /* @__PURE__ */ jsx50(
12868
+ Button41,
12521
12869
  {
12522
12870
  size: "sm",
12523
12871
  variant: "ghost",
@@ -12528,19 +12876,19 @@ function CaptureRun({ sheetId, face: given, className }) {
12528
12876
  ) : null
12529
12877
  ] }, i.client_key)) }) : null;
12530
12878
  if (!face.may_capture) {
12531
- return /* @__PURE__ */ jsxs45("section", { className: cn43("mx-auto flex w-full max-w-sm flex-col gap-3 p-4", className), children: [
12532
- /* @__PURE__ */ jsx49("h1", { className: "text-lg font-medium", children: face.title }),
12533
- /* @__PURE__ */ jsx49("p", { className: "text-sm", "data-testid": "capture-refusal", children: face.message }),
12879
+ return /* @__PURE__ */ jsxs46("section", { className: cn44("mx-auto flex w-full max-w-sm flex-col gap-3 p-4", className), children: [
12880
+ /* @__PURE__ */ jsx50("h1", { className: "text-lg font-medium", children: face.title }),
12881
+ /* @__PURE__ */ jsx50("p", { className: "text-sm", "data-testid": "capture-refusal", children: face.message }),
12534
12882
  queueLine,
12535
12883
  queuePanel
12536
12884
  ] });
12537
12885
  }
12538
12886
  if (done) {
12539
12887
  const thanks = face.presentation?.thank_you ?? {};
12540
- return /* @__PURE__ */ jsxs45("section", { className: cn43("mx-auto flex w-full max-w-sm flex-col gap-3 p-4", className), children: [
12541
- /* @__PURE__ */ jsx49("h2", { className: "text-lg font-medium", "data-testid": "capture-thankyou", children: thanks.title ?? "Logged" }),
12542
- /* @__PURE__ */ jsx49("p", { className: "text-sm text-muted-foreground", children: done.queued ? "There is no signal, so this one is on the phone and goes up the moment there is. Nothing is lost." : thanks.body ?? "Ready for the next one." }),
12543
- /* @__PURE__ */ jsx49(Button40, { className: "h-12", onClick: () => setDone(null), "data-testid": "capture-next", children: "Next one" }),
12888
+ return /* @__PURE__ */ jsxs46("section", { className: cn44("mx-auto flex w-full max-w-sm flex-col gap-3 p-4", className), children: [
12889
+ /* @__PURE__ */ jsx50("h2", { className: "text-lg font-medium", "data-testid": "capture-thankyou", children: thanks.title ?? "Logged" }),
12890
+ /* @__PURE__ */ jsx50("p", { className: "text-sm text-muted-foreground", children: done.queued ? "There is no signal, so this one is on the phone and goes up the moment there is. Nothing is lost." : thanks.body ?? "Ready for the next one." }),
12891
+ /* @__PURE__ */ jsx50(Button41, { className: "h-12", onClick: () => setDone(null), "data-testid": "capture-next", children: "Next one" }),
12544
12892
  queueLine,
12545
12893
  queuePanel
12546
12894
  ] });
@@ -12549,14 +12897,14 @@ function CaptureRun({ sheetId, face: given, className }) {
12549
12897
  const field = fieldOf(q.field);
12550
12898
  const control = controlFor(field);
12551
12899
  const last = at >= questions.length - 1;
12552
- return /* @__PURE__ */ jsxs45("section", { className: cn43("mx-auto flex w-full max-w-sm flex-col gap-3 p-4", className), children: [
12553
- /* @__PURE__ */ jsxs45("div", { className: "flex items-center gap-2 text-[11px] text-muted-foreground", children: [
12554
- /* @__PURE__ */ jsxs45("span", { className: "tabular-nums", "data-testid": "capture-progress", children: [
12900
+ return /* @__PURE__ */ jsxs46("section", { className: cn44("mx-auto flex w-full max-w-sm flex-col gap-3 p-4", className), children: [
12901
+ /* @__PURE__ */ jsxs46("div", { className: "flex items-center gap-2 text-[11px] text-muted-foreground", children: [
12902
+ /* @__PURE__ */ jsxs46("span", { className: "tabular-nums", "data-testid": "capture-progress", children: [
12555
12903
  at + 1,
12556
12904
  " of ",
12557
12905
  questions.length
12558
12906
  ] }),
12559
- /* @__PURE__ */ jsx49("div", { className: "h-1 flex-1 overflow-hidden rounded bg-muted", children: /* @__PURE__ */ jsx49(
12907
+ /* @__PURE__ */ jsx50("div", { className: "h-1 flex-1 overflow-hidden rounded bg-muted", children: /* @__PURE__ */ jsx50(
12560
12908
  "div",
12561
12909
  {
12562
12910
  className: "h-full bg-primary transition-[width] duration-200",
@@ -12564,7 +12912,7 @@ function CaptureRun({ sheetId, face: given, className }) {
12564
12912
  }
12565
12913
  ) })
12566
12914
  ] }),
12567
- /* @__PURE__ */ jsxs45(
12915
+ /* @__PURE__ */ jsxs46(
12568
12916
  "label",
12569
12917
  {
12570
12918
  className: "text-xl font-medium leading-snug",
@@ -12572,13 +12920,13 @@ function CaptureRun({ sheetId, face: given, className }) {
12572
12920
  "data-testid": "capture-ask",
12573
12921
  children: [
12574
12922
  q.ask ?? q.field,
12575
- q.required ? /* @__PURE__ */ jsx49("span", { className: "text-muted-foreground", children: " *" }) : null
12923
+ q.required ? /* @__PURE__ */ jsx50("span", { className: "text-muted-foreground", children: " *" }) : null
12576
12924
  ]
12577
12925
  }
12578
12926
  ),
12579
- q.help ? /* @__PURE__ */ jsx49("p", { className: "-mt-1 text-sm text-muted-foreground", children: q.help }) : null,
12580
- control === "photo" || control === "voice" ? !host.upload ? /* @__PURE__ */ jsx49("p", { className: "rounded border border-dashed px-2 py-2 text-xs text-muted-foreground", children: NO_UPLOAD_REASON }) : /* @__PURE__ */ jsxs45("div", { className: "flex flex-col gap-1", children: [
12581
- /* @__PURE__ */ jsx49(
12927
+ q.help ? /* @__PURE__ */ jsx50("p", { className: "-mt-1 text-sm text-muted-foreground", children: q.help }) : null,
12928
+ control === "photo" || control === "voice" ? !host.upload ? /* @__PURE__ */ jsx50("p", { className: "rounded border border-dashed px-2 py-2 text-xs text-muted-foreground", children: NO_UPLOAD_REASON }) : /* @__PURE__ */ jsxs46("div", { className: "flex flex-col gap-1", children: [
12929
+ /* @__PURE__ */ jsx50(
12582
12930
  "input",
12583
12931
  {
12584
12932
  id: `capture-${q.field}`,
@@ -12596,11 +12944,11 @@ function CaptureRun({ sheetId, face: given, className }) {
12596
12944
  }
12597
12945
  }
12598
12946
  ),
12599
- files[q.field] ? /* @__PURE__ */ jsxs45("p", { className: "text-[11px] text-muted-foreground", "data-testid": `capture-have-${q.field}`, children: [
12947
+ files[q.field] ? /* @__PURE__ */ jsxs46("p", { className: "text-[11px] text-muted-foreground", "data-testid": `capture-have-${q.field}`, children: [
12600
12948
  files[q.field].name,
12601
12949
  " is on the phone. It goes up with the capture."
12602
12950
  ] }) : null
12603
- ] }) : control === "number" ? /* @__PURE__ */ jsx49(
12951
+ ] }) : control === "number" ? /* @__PURE__ */ jsx50(
12604
12952
  Input3,
12605
12953
  {
12606
12954
  id: `capture-${q.field}`,
@@ -12616,7 +12964,7 @@ function CaptureRun({ sheetId, face: given, className }) {
12616
12964
  setMissing(null);
12617
12965
  }
12618
12966
  }
12619
- ) : /* @__PURE__ */ jsx49(
12967
+ ) : /* @__PURE__ */ jsx50(
12620
12968
  Textarea2,
12621
12969
  {
12622
12970
  id: `capture-${q.field}`,
@@ -12631,10 +12979,10 @@ function CaptureRun({ sheetId, face: given, className }) {
12631
12979
  }
12632
12980
  }
12633
12981
  ),
12634
- missing ? /* @__PURE__ */ jsx49("p", { className: "text-sm text-destructive", "data-testid": "capture-missing", children: missing }) : null,
12635
- /* @__PURE__ */ jsxs45("div", { className: "flex items-center gap-2", children: [
12636
- at > 0 ? /* @__PURE__ */ jsx49(
12637
- Button40,
12982
+ missing ? /* @__PURE__ */ jsx50("p", { className: "text-sm text-destructive", "data-testid": "capture-missing", children: missing }) : null,
12983
+ /* @__PURE__ */ jsxs46("div", { className: "flex items-center gap-2", children: [
12984
+ at > 0 ? /* @__PURE__ */ jsx50(
12985
+ Button41,
12638
12986
  {
12639
12987
  variant: "ghost",
12640
12988
  className: "h-12 px-3",
@@ -12643,16 +12991,16 @@ function CaptureRun({ sheetId, face: given, className }) {
12643
12991
  children: "Back"
12644
12992
  }
12645
12993
  ) : null,
12646
- last ? /* @__PURE__ */ jsx49(
12647
- Button40,
12994
+ last ? /* @__PURE__ */ jsx50(
12995
+ Button41,
12648
12996
  {
12649
12997
  className: "h-12 flex-1 text-base",
12650
12998
  onClick: () => void capture(),
12651
12999
  "data-testid": "capture-submit",
12652
13000
  children: "Capture"
12653
13001
  }
12654
- ) : /* @__PURE__ */ jsx49(
12655
- Button40,
13002
+ ) : /* @__PURE__ */ jsx50(
13003
+ Button41,
12656
13004
  {
12657
13005
  className: "h-12 flex-1 text-base",
12658
13006
  onClick: () => setAt(at + 1),
@@ -12667,7 +13015,7 @@ function CaptureRun({ sheetId, face: given, className }) {
12667
13015
  }
12668
13016
 
12669
13017
  // src/CaptureSheet.tsx
12670
- import { Fragment as Fragment23, jsx as jsx50, jsxs as jsxs46 } from "react/jsx-runtime";
13018
+ import { Fragment as Fragment24, jsx as jsx51, jsxs as jsxs47 } from "react/jsx-runtime";
12671
13019
  var CAPTURE_MODES = ["reading", "photo", "voice"];
12672
13020
  var IN_MEMORY_QUEUE_REASON = "No capture queue is bound, so anything that cannot be sent right now is held in this page's memory and is lost if the page closes. Bind `captureQueue` on <RecordsUiProvider> with your host's own durable store (IndexedDB in a browser) and a queued capture survives the tab, the reload and the flight. It is said here rather than discovered later.";
12673
13021
  function mintClientKey() {
@@ -12687,8 +13035,8 @@ function CaptureSheet({
12687
13035
  attachmentField,
12688
13036
  className
12689
13037
  }) {
12690
- if (sheetId) return /* @__PURE__ */ jsx50(CaptureRun, { sheetId, className });
12691
- return /* @__PURE__ */ jsx50(
13038
+ if (sheetId) return /* @__PURE__ */ jsx51(CaptureRun, { sheetId, className });
13039
+ return /* @__PURE__ */ jsx51(
12692
13040
  AdHocCaptureSheet,
12693
13041
  {
12694
13042
  tableId,
@@ -12706,21 +13054,21 @@ function AdHocCaptureSheet({
12706
13054
  attachmentField,
12707
13055
  className
12708
13056
  }) {
12709
- const client = useRecordsClient37();
13057
+ const client = useRecordsClient38();
12710
13058
  const host = useRecordsUi();
12711
13059
  const table = useTable20(tableId);
12712
13060
  const fields = useFields21(tableId);
12713
- const [mode, setMode] = useState45("reading");
12714
- const [reading, setReading] = useState45("");
12715
- const [note, setNote] = useState45("");
12716
- const [fileId, setFileId] = useState45(null);
12717
- const [pending, setPending] = useState45(null);
12718
- const [saved, setSaved] = useState45([]);
12719
- const [error, setError] = useState45(null);
12720
- const [uploadError, setUploadError] = useState45(null);
12721
- const [flushing, setFlushing] = useState45(false);
13061
+ const [mode, setMode] = useState46("reading");
13062
+ const [reading, setReading] = useState46("");
13063
+ const [note, setNote] = useState46("");
13064
+ const [fileId, setFileId] = useState46(null);
13065
+ const [pending, setPending] = useState46(null);
13066
+ const [saved, setSaved] = useState46([]);
13067
+ const [error, setError] = useState46(null);
13068
+ const [uploadError, setUploadError] = useState46(null);
13069
+ const [flushing, setFlushing] = useState46(false);
12722
13070
  const queue = useRef13([]);
12723
- const keep = useCallback31(
13071
+ const keep = useCallback32(
12724
13072
  async (next) => {
12725
13073
  queue.current = next;
12726
13074
  setPending(next);
@@ -12728,7 +13076,7 @@ function AdHocCaptureSheet({
12728
13076
  },
12729
13077
  [host]
12730
13078
  );
12731
- useEffect35(() => {
13079
+ useEffect36(() => {
12732
13080
  let cancelled = false;
12733
13081
  void (async () => {
12734
13082
  const held = host.captureQueue ? await host.captureQueue.load() : [];
@@ -12740,7 +13088,7 @@ function AdHocCaptureSheet({
12740
13088
  cancelled = true;
12741
13089
  };
12742
13090
  }, [host]);
12743
- const resolved = useCallback31(() => {
13091
+ const resolved = useCallback32(() => {
12744
13092
  const all = fields.data ?? [];
12745
13093
  return {
12746
13094
  reading: readingField ?? all.find((f) => f.type === "range")?.key ?? null,
@@ -12748,7 +13096,7 @@ function AdHocCaptureSheet({
12748
13096
  attachment: attachmentField ?? all.find((f) => f.format === "file" || f.format === "image")?.key ?? null
12749
13097
  };
12750
13098
  }, [attachmentField, fields.data, noteField, readingField, table.data]);
12751
- const flush = useCallback31(async () => {
13099
+ const flush = useCallback32(async () => {
12752
13100
  if (flushing || queue.current.length === 0) return;
12753
13101
  setFlushing(true);
12754
13102
  const left = [];
@@ -12800,14 +13148,14 @@ function AdHocCaptureSheet({
12800
13148
  await keep([...queue.current, item]);
12801
13149
  await flush();
12802
13150
  }
12803
- if (table.error) return /* @__PURE__ */ jsx50(RefusalNotice, { error: table.error, className });
12804
- if (fields.error) return /* @__PURE__ */ jsx50(RefusalNotice, { error: fields.error, className });
12805
- if (fields.loading || pending === null) return /* @__PURE__ */ jsx50(Skeleton28, { className: cn44("h-64 w-full", className) });
13151
+ if (table.error) return /* @__PURE__ */ jsx51(RefusalNotice, { error: table.error, className });
13152
+ if (fields.error) return /* @__PURE__ */ jsx51(RefusalNotice, { error: fields.error, className });
13153
+ if (fields.loading || pending === null) return /* @__PURE__ */ jsx51(Skeleton29, { className: cn45("h-64 w-full", className) });
12806
13154
  const keys = resolved();
12807
- return /* @__PURE__ */ jsxs46("section", { className: cn44("mx-auto flex w-full max-w-sm flex-col gap-2", className), children: [
12808
- /* @__PURE__ */ jsxs46("header", { className: "flex items-center gap-1 text-xs text-muted-foreground", children: [
12809
- CAPTURE_MODES.map((m) => /* @__PURE__ */ jsx50(
12810
- Button41,
13155
+ return /* @__PURE__ */ jsxs47("section", { className: cn45("mx-auto flex w-full max-w-sm flex-col gap-2", className), children: [
13156
+ /* @__PURE__ */ jsxs47("header", { className: "flex items-center gap-1 text-xs text-muted-foreground", children: [
13157
+ CAPTURE_MODES.map((m) => /* @__PURE__ */ jsx51(
13158
+ Button42,
12811
13159
  {
12812
13160
  size: "sm",
12813
13161
  variant: m === mode ? "default" : "ghost",
@@ -12817,10 +13165,10 @@ function AdHocCaptureSheet({
12817
13165
  },
12818
13166
  m
12819
13167
  )),
12820
- /* @__PURE__ */ jsx50("span", { className: "ml-auto tabular-nums", children: pending.length > 0 ? `${pending.length} waiting` : `${saved.length} saved` })
13168
+ /* @__PURE__ */ jsx51("span", { className: "ml-auto tabular-nums", children: pending.length > 0 ? `${pending.length} waiting` : `${saved.length} saved` })
12821
13169
  ] }),
12822
- error ? /* @__PURE__ */ jsx50(RefusalNotice, { error }) : null,
12823
- mode === "reading" ? keys.reading ? /* @__PURE__ */ jsx50(
13170
+ error ? /* @__PURE__ */ jsx51(RefusalNotice, { error }) : null,
13171
+ mode === "reading" ? keys.reading ? /* @__PURE__ */ jsx51(
12824
13172
  Input4,
12825
13173
  {
12826
13174
  "aria-label": "Reading",
@@ -12830,22 +13178,22 @@ function AdHocCaptureSheet({
12830
13178
  value: reading,
12831
13179
  onChange: (e) => setReading(e.target.value)
12832
13180
  }
12833
- ) : /* @__PURE__ */ jsxs46("p", { className: "rounded border border-dashed px-2 py-1 text-xs text-muted-foreground", children: [
13181
+ ) : /* @__PURE__ */ jsxs47("p", { className: "rounded border border-dashed px-2 py-1 text-xs text-muted-foreground", children: [
12834
13182
  table.data?.name ?? "This table",
12835
13183
  " has no number Field for a reading to go in, so this mode would have nowhere to put what you typed. Add one, or name it with ",
12836
- /* @__PURE__ */ jsx50("code", { children: "readingField" }),
13184
+ /* @__PURE__ */ jsx51("code", { children: "readingField" }),
12837
13185
  "."
12838
13186
  ] }) : null,
12839
- mode !== "reading" ? !host.upload ? /* @__PURE__ */ jsx50("p", { className: "rounded border border-dashed px-2 py-1 text-xs text-muted-foreground", children: NO_UPLOAD_REASON }) : !keys.attachment ? /* @__PURE__ */ jsxs46("p", { className: "rounded border border-dashed px-2 py-1 text-xs text-muted-foreground", children: [
13187
+ mode !== "reading" ? !host.upload ? /* @__PURE__ */ jsx51("p", { className: "rounded border border-dashed px-2 py-1 text-xs text-muted-foreground", children: NO_UPLOAD_REASON }) : !keys.attachment ? /* @__PURE__ */ jsxs47("p", { className: "rounded border border-dashed px-2 py-1 text-xs text-muted-foreground", children: [
12840
13188
  table.data?.name ?? "This table",
12841
13189
  " has no Field a file id can go in, so a ",
12842
13190
  mode,
12843
13191
  " capture would upload the bytes and then have nowhere to record them. Add one, or name it with",
12844
13192
  " ",
12845
- /* @__PURE__ */ jsx50("code", { children: "attachmentField" }),
13193
+ /* @__PURE__ */ jsx51("code", { children: "attachmentField" }),
12846
13194
  "."
12847
- ] }) : /* @__PURE__ */ jsxs46(Fragment23, { children: [
12848
- /* @__PURE__ */ jsx50(
13195
+ ] }) : /* @__PURE__ */ jsxs47(Fragment24, { children: [
13196
+ /* @__PURE__ */ jsx51(
12849
13197
  "input",
12850
13198
  {
12851
13199
  "aria-label": mode === "photo" ? "Take a photo" : "Record a voice note",
@@ -12864,10 +13212,10 @@ function AdHocCaptureSheet({
12864
13212
  }
12865
13213
  }
12866
13214
  ),
12867
- fileId ? /* @__PURE__ */ jsx50("p", { className: "text-[11px] text-muted-foreground", children: "Attached." }) : null,
12868
- uploadError ? /* @__PURE__ */ jsx50("p", { className: "text-xs text-destructive", children: uploadError }) : null
13215
+ fileId ? /* @__PURE__ */ jsx51("p", { className: "text-[11px] text-muted-foreground", children: "Attached." }) : null,
13216
+ uploadError ? /* @__PURE__ */ jsx51("p", { className: "text-xs text-destructive", children: uploadError }) : null
12869
13217
  ] }) : null,
12870
- /* @__PURE__ */ jsx50(
13218
+ /* @__PURE__ */ jsx51(
12871
13219
  Textarea3,
12872
13220
  {
12873
13221
  "aria-label": "Note",
@@ -12878,12 +13226,12 @@ function AdHocCaptureSheet({
12878
13226
  onChange: (e) => setNote(e.target.value)
12879
13227
  }
12880
13228
  ),
12881
- /* @__PURE__ */ jsx50(Button41, { className: "h-12", onClick: () => void capture(), children: "Capture" }),
12882
- pending.length > 0 ? /* @__PURE__ */ jsxs46("div", { className: "flex flex-col gap-1 rounded border p-2", children: [
12883
- /* @__PURE__ */ jsxs46("div", { className: "flex items-center gap-2 text-xs", children: [
12884
- /* @__PURE__ */ jsx50("span", { className: "font-medium", children: "Waiting to send" }),
12885
- /* @__PURE__ */ jsx50(
12886
- Button41,
13229
+ /* @__PURE__ */ jsx51(Button42, { className: "h-12", onClick: () => void capture(), children: "Capture" }),
13230
+ pending.length > 0 ? /* @__PURE__ */ jsxs47("div", { className: "flex flex-col gap-1 rounded border p-2", children: [
13231
+ /* @__PURE__ */ jsxs47("div", { className: "flex items-center gap-2 text-xs", children: [
13232
+ /* @__PURE__ */ jsx51("span", { className: "font-medium", children: "Waiting to send" }),
13233
+ /* @__PURE__ */ jsx51(
13234
+ Button42,
12887
13235
  {
12888
13236
  size: "sm",
12889
13237
  variant: "ghost",
@@ -12894,7 +13242,7 @@ function AdHocCaptureSheet({
12894
13242
  }
12895
13243
  )
12896
13244
  ] }),
12897
- /* @__PURE__ */ jsx50("ul", { className: "flex flex-col gap-0.5", children: pending.map((item) => /* @__PURE__ */ jsxs46("li", { className: "text-[11px] text-muted-foreground", children: [
13245
+ /* @__PURE__ */ jsx51("ul", { className: "flex flex-col gap-0.5", children: pending.map((item) => /* @__PURE__ */ jsxs47("li", { className: "text-[11px] text-muted-foreground", children: [
12898
13246
  new Date(item.capturedAt).toLocaleTimeString(),
12899
13247
  " \xB7 ",
12900
13248
  item.attempts,
@@ -12902,23 +13250,23 @@ function AdHocCaptureSheet({
12902
13250
  item.lastRefusal ? ` \xB7 ${item.lastRefusal}` : ""
12903
13251
  ] }, item.clientKey)) })
12904
13252
  ] }) : null,
12905
- !host.captureQueue ? /* @__PURE__ */ jsx50("p", { className: "rounded border border-dashed px-2 py-1 text-[11px] text-muted-foreground", children: IN_MEMORY_QUEUE_REASON }) : null
13253
+ !host.captureQueue ? /* @__PURE__ */ jsx51("p", { className: "rounded border border-dashed px-2 py-1 text-[11px] text-muted-foreground", children: IN_MEMORY_QUEUE_REASON }) : null
12906
13254
  ] });
12907
13255
  }
12908
13256
 
12909
13257
  // src/PortalShell.tsx
12910
- import { useCallback as useCallback32, useEffect as useEffect36, useState as useState46 } from "react";
12911
- import { useRecordsClient as useRecordsClient38 } from "@ai-matrx/records/react";
12912
- import { Button as Button42, Skeleton as Skeleton29, cn as cn45 } from "@ai-matrx/design-system";
12913
- import { jsx as jsx51, jsxs as jsxs47 } from "react/jsx-runtime";
13258
+ import { useCallback as useCallback33, useEffect as useEffect37, useState as useState47 } from "react";
13259
+ import { useRecordsClient as useRecordsClient39 } from "@ai-matrx/records/react";
13260
+ import { Button as Button43, Skeleton as Skeleton30, cn as cn46 } from "@ai-matrx/design-system";
13261
+ import { jsx as jsx52, jsxs as jsxs48 } from "react/jsx-runtime";
12914
13262
  function PortalShell({ tableId, form, resourceType = "record", className }) {
12915
- const client = useRecordsClient38();
12916
- const [card, setCard] = useState46(null);
12917
- const [reach, setReach] = useState46(null);
12918
- const [error, setError] = useState46(null);
12919
- const [open, setOpen] = useState46(null);
12920
- const [sending, setSending] = useState46(false);
12921
- const load = useCallback32(async () => {
13263
+ const client = useRecordsClient39();
13264
+ const [card, setCard] = useState47(null);
13265
+ const [reach, setReach] = useState47(null);
13266
+ const [error, setError] = useState47(null);
13267
+ const [open, setOpen] = useState47(null);
13268
+ const [sending, setSending] = useState47(false);
13269
+ const load = useCallback33(async () => {
12922
13270
  const who = await client.externalPrincipalCard();
12923
13271
  if (!who.ok) {
12924
13272
  setError(who.error);
@@ -12933,23 +13281,23 @@ function PortalShell({ tableId, form, resourceType = "record", className }) {
12933
13281
  }
12934
13282
  setReach(reached.data.map((row) => row.resource_id));
12935
13283
  }, [client, resourceType]);
12936
- useEffect36(() => {
13284
+ useEffect37(() => {
12937
13285
  void load();
12938
13286
  }, [load]);
12939
13287
  if (error) {
12940
- return /* @__PURE__ */ jsx51(RefusalNotice, { error, className });
13288
+ return /* @__PURE__ */ jsx52(RefusalNotice, { error, className });
12941
13289
  }
12942
- if (!card || reach === null) return /* @__PURE__ */ jsx51(Skeleton29, { className: cn45("h-64 w-full", className) });
13290
+ if (!card || reach === null) return /* @__PURE__ */ jsx52(Skeleton30, { className: cn46("h-64 w-full", className) });
12943
13291
  if (!card.external) {
12944
- return /* @__PURE__ */ jsx51("p", { className: cn45("rounded border border-dashed px-2 py-1 text-xs text-muted-foreground", className), children: card.explanation || "You are a member of an organization here, so this portal is not yours \u2014 a portal is the scoped view an OUTSIDER gets. You have the full application instead." });
13292
+ return /* @__PURE__ */ jsx52("p", { className: cn46("rounded border border-dashed px-2 py-1 text-xs text-muted-foreground", className), children: card.explanation || "You are a member of an organization here, so this portal is not yours \u2014 a portal is the scoped view an OUTSIDER gets. You have the full application instead." });
12945
13293
  }
12946
13294
  if (sending && form) {
12947
- return /* @__PURE__ */ jsxs47("section", { className: cn45("flex min-h-0 flex-col gap-2", className), children: [
12948
- /* @__PURE__ */ jsxs47("header", { className: "flex items-center gap-2 text-xs text-muted-foreground", children: [
12949
- /* @__PURE__ */ jsx51("span", { className: "font-medium text-foreground", children: form.name }),
12950
- /* @__PURE__ */ jsx51(Button42, { size: "sm", variant: "ghost", className: "ml-auto h-6 px-2 text-[11px]", onClick: () => setSending(false), children: "Back" })
13295
+ return /* @__PURE__ */ jsxs48("section", { className: cn46("flex min-h-0 flex-col gap-2", className), children: [
13296
+ /* @__PURE__ */ jsxs48("header", { className: "flex items-center gap-2 text-xs text-muted-foreground", children: [
13297
+ /* @__PURE__ */ jsx52("span", { className: "font-medium text-foreground", children: form.name }),
13298
+ /* @__PURE__ */ jsx52(Button43, { size: "sm", variant: "ghost", className: "ml-auto h-6 px-2 text-[11px]", onClick: () => setSending(false), children: "Back" })
12951
13299
  ] }),
12952
- /* @__PURE__ */ jsx51(
13300
+ /* @__PURE__ */ jsx52(
12953
13301
  FormRunner,
12954
13302
  {
12955
13303
  form,
@@ -12962,38 +13310,38 @@ function PortalShell({ tableId, form, resourceType = "record", className }) {
12962
13310
  ] });
12963
13311
  }
12964
13312
  if (open) {
12965
- return /* @__PURE__ */ jsx51("section", { className: cn45("flex min-h-0 flex-col gap-2", className), children: /* @__PURE__ */ jsx51(Peek, { tableId, recordId: open, onClose: () => setOpen(null) }) });
13313
+ return /* @__PURE__ */ jsx52("section", { className: cn46("flex min-h-0 flex-col gap-2", className), children: /* @__PURE__ */ jsx52(Peek, { tableId, recordId: open, onClose: () => setOpen(null) }) });
12966
13314
  }
12967
- return /* @__PURE__ */ jsxs47("section", { className: cn45("flex min-h-0 flex-col gap-2", className), children: [
12968
- /* @__PURE__ */ jsxs47("header", { className: "flex items-center gap-2 text-xs text-muted-foreground", children: [
12969
- /* @__PURE__ */ jsx51("span", { className: "font-medium text-foreground", children: "Shared with you" }),
12970
- /* @__PURE__ */ jsx51("span", { className: "tabular-nums", children: reach.length }),
12971
- form ? /* @__PURE__ */ jsx51(Button42, { size: "sm", className: "ml-auto h-7 px-2 text-[11px]", onClick: () => setSending(true), children: form.submitLabel ?? form.name }) : null
13315
+ return /* @__PURE__ */ jsxs48("section", { className: cn46("flex min-h-0 flex-col gap-2", className), children: [
13316
+ /* @__PURE__ */ jsxs48("header", { className: "flex items-center gap-2 text-xs text-muted-foreground", children: [
13317
+ /* @__PURE__ */ jsx52("span", { className: "font-medium text-foreground", children: "Shared with you" }),
13318
+ /* @__PURE__ */ jsx52("span", { className: "tabular-nums", children: reach.length }),
13319
+ form ? /* @__PURE__ */ jsx52(Button43, { size: "sm", className: "ml-auto h-7 px-2 text-[11px]", onClick: () => setSending(true), children: form.submitLabel ?? form.name }) : null
12972
13320
  ] }),
12973
13321
  reach.length === 0 ? (
12974
13322
  // EMPTY, AND IT SAYS WHICH EMPTY. "Nobody has shared anything with you"
12975
13323
  // and "the store would not tell us" are different sentences, and a
12976
13324
  // person acts on them differently.
12977
- /* @__PURE__ */ jsxs47("p", { className: "rounded border border-dashed px-2 py-1 text-xs text-muted-foreground", children: [
13325
+ /* @__PURE__ */ jsxs48("p", { className: "rounded border border-dashed px-2 py-1 text-xs text-muted-foreground", children: [
12978
13326
  "Nothing has been shared with you yet. ",
12979
13327
  card.explanation,
12980
13328
  " When somebody shares a record with you it appears here \u2014 this list is exactly what Visibility gave you, one record at a time, never an organization's list."
12981
13329
  ] })
12982
- ) : /* @__PURE__ */ jsx51("ul", { className: "flex min-h-0 flex-col gap-1 overflow-y-auto", children: reach.map((id) => /* @__PURE__ */ jsx51("li", { children: /* @__PURE__ */ jsx51(
13330
+ ) : /* @__PURE__ */ jsx52("ul", { className: "flex min-h-0 flex-col gap-1 overflow-y-auto", children: reach.map((id) => /* @__PURE__ */ jsx52("li", { children: /* @__PURE__ */ jsx52(
12983
13331
  "button",
12984
13332
  {
12985
13333
  type: "button",
12986
13334
  className: "w-full truncate rounded border px-2 py-1.5 text-left text-xs hover:bg-muted",
12987
13335
  onClick: () => setOpen(id),
12988
- children: /* @__PURE__ */ jsx51(PortalRow, { tableId, recordId: id })
13336
+ children: /* @__PURE__ */ jsx52(PortalRow, { tableId, recordId: id })
12989
13337
  }
12990
13338
  ) }, id)) })
12991
13339
  ] });
12992
13340
  }
12993
13341
  function PortalRow({ tableId, recordId }) {
12994
- const client = useRecordsClient38();
12995
- const [title, setTitle] = useState46(null);
12996
- useEffect36(() => {
13342
+ const client = useRecordsClient39();
13343
+ const [title, setTitle] = useState47(null);
13344
+ useEffect37(() => {
12997
13345
  let cancelled = false;
12998
13346
  void client.recordRead({ record_id: recordId }).then((answered) => {
12999
13347
  if (cancelled) return;
@@ -13009,22 +13357,22 @@ function PortalRow({ tableId, recordId }) {
13009
13357
  cancelled = true;
13010
13358
  };
13011
13359
  }, [client, recordId, tableId]);
13012
- return /* @__PURE__ */ jsx51("span", { children: title ?? "\u2026" });
13360
+ return /* @__PURE__ */ jsx52("span", { children: title ?? "\u2026" });
13013
13361
  }
13014
13362
 
13015
13363
  // src/PublicViewPage.tsx
13016
- import { useCallback as useCallback33, useEffect as useEffect37, useState as useState47 } from "react";
13017
- import { useRecordsClient as useRecordsClient39 } from "@ai-matrx/records/react";
13018
- import { Skeleton as Skeleton30, cn as cn46 } from "@ai-matrx/design-system";
13019
- import { jsx as jsx52, jsxs as jsxs48 } from "react/jsx-runtime";
13364
+ import { useCallback as useCallback34, useEffect as useEffect38, useState as useState48 } from "react";
13365
+ import { useRecordsClient as useRecordsClient40 } from "@ai-matrx/records/react";
13366
+ import { Skeleton as Skeleton31, cn as cn47 } from "@ai-matrx/design-system";
13367
+ import { jsx as jsx53, jsxs as jsxs49 } from "react/jsx-runtime";
13020
13368
  function PublicViewPage({ slug, className }) {
13021
- const client = useRecordsClient39();
13022
- const [binding, setBinding] = useState47(null);
13023
- const [rows, setRows] = useState47(null);
13024
- const [fields, setFields] = useState47(null);
13025
- const [error, setError] = useState47(null);
13026
- const [gap, setGap] = useState47(null);
13027
- const load = useCallback33(async () => {
13369
+ const client = useRecordsClient40();
13370
+ const [binding, setBinding] = useState48(null);
13371
+ const [rows, setRows] = useState48(null);
13372
+ const [fields, setFields] = useState48(null);
13373
+ const [error, setError] = useState48(null);
13374
+ const [gap, setGap] = useState48(null);
13375
+ const load = useCallback34(async () => {
13028
13376
  const notice = await client.worldPublishGapNotice();
13029
13377
  if (notice.ok) setGap(notice.data);
13030
13378
  const resolved = await client.resolvePublishBinding({ slug });
@@ -13057,48 +13405,48 @@ function PublicViewPage({ slug, className }) {
13057
13405
  }
13058
13406
  setRows([{ id: found.resource_id, document: read.data.document, level: "viewer", hidden: read.data.hidden }]);
13059
13407
  }, [client, slug]);
13060
- useEffect37(() => {
13408
+ useEffect38(() => {
13061
13409
  void load();
13062
13410
  }, [load]);
13063
- if (error) return /* @__PURE__ */ jsx52(RefusalNotice, { error, className });
13064
- if (binding === null) return /* @__PURE__ */ jsx52(Skeleton30, { className: cn46("h-64 w-full", className) });
13411
+ if (error) return /* @__PURE__ */ jsx53(RefusalNotice, { error, className });
13412
+ if (binding === null) return /* @__PURE__ */ jsx53(Skeleton31, { className: cn47("h-64 w-full", className) });
13065
13413
  if (binding === "none") {
13066
- return /* @__PURE__ */ jsxs48("main", { className: cn46("mx-auto max-w-2xl py-16 text-center", className), children: [
13067
- /* @__PURE__ */ jsx52("h1", { className: "text-lg font-medium", children: "Nothing here" }),
13068
- /* @__PURE__ */ jsx52("p", { className: "mt-1 text-sm text-muted-foreground", children: "There is no public page at this address." }),
13069
- gap ? /* @__PURE__ */ jsx52("p", { className: "mt-6 text-xs text-muted-foreground", children: gap }) : null
13414
+ return /* @__PURE__ */ jsxs49("main", { className: cn47("mx-auto max-w-2xl py-16 text-center", className), children: [
13415
+ /* @__PURE__ */ jsx53("h1", { className: "text-lg font-medium", children: "Nothing here" }),
13416
+ /* @__PURE__ */ jsx53("p", { className: "mt-1 text-sm text-muted-foreground", children: "There is no public page at this address." }),
13417
+ gap ? /* @__PURE__ */ jsx53("p", { className: "mt-6 text-xs text-muted-foreground", children: gap }) : null
13070
13418
  ] });
13071
13419
  }
13072
- return /* @__PURE__ */ jsxs48("main", { className: cn46("mx-auto flex max-w-2xl flex-col gap-3 py-8", className), children: [
13073
- /* @__PURE__ */ jsxs48("header", { className: "flex items-baseline gap-2 text-xs text-muted-foreground", children: [
13074
- /* @__PURE__ */ jsx52("span", { className: "font-medium text-foreground", children: binding.namespace ?? "Published" }),
13075
- /* @__PURE__ */ jsx52("span", { children: binding.render_mode === "list" ? `${rows?.length ?? 0} items` : "Shared publicly" })
13420
+ return /* @__PURE__ */ jsxs49("main", { className: cn47("mx-auto flex max-w-2xl flex-col gap-3 py-8", className), children: [
13421
+ /* @__PURE__ */ jsxs49("header", { className: "flex items-baseline gap-2 text-xs text-muted-foreground", children: [
13422
+ /* @__PURE__ */ jsx53("span", { className: "font-medium text-foreground", children: binding.namespace ?? "Published" }),
13423
+ /* @__PURE__ */ jsx53("span", { children: binding.render_mode === "list" ? `${rows?.length ?? 0} items` : "Shared publicly" })
13076
13424
  ] }),
13077
- rows === null ? /* @__PURE__ */ jsx52(Skeleton30, { className: "h-40 w-full" }) : rows.length === 0 ? /* @__PURE__ */ jsx52("p", { className: "text-sm text-muted-foreground", children: "This page has nothing on it yet." }) : /* @__PURE__ */ jsx52("ol", { className: "flex flex-col gap-2", children: rows.map((row) => /* @__PURE__ */ jsx52("li", { className: "rounded border p-3", children: /* @__PURE__ */ jsx52(PublicRow, { row, fields }) }, row.id)) }),
13078
- /* @__PURE__ */ jsx52("footer", { className: "mt-4 border-t pt-3 text-xs text-muted-foreground", children: binding.notice || gap || "Nothing was scanned before this was listed." })
13425
+ rows === null ? /* @__PURE__ */ jsx53(Skeleton31, { className: "h-40 w-full" }) : rows.length === 0 ? /* @__PURE__ */ jsx53("p", { className: "text-sm text-muted-foreground", children: "This page has nothing on it yet." }) : /* @__PURE__ */ jsx53("ol", { className: "flex flex-col gap-2", children: rows.map((row) => /* @__PURE__ */ jsx53("li", { className: "rounded border p-3", children: /* @__PURE__ */ jsx53(PublicRow, { row, fields }) }, row.id)) }),
13426
+ /* @__PURE__ */ jsx53("footer", { className: "mt-4 border-t pt-3 text-xs text-muted-foreground", children: binding.notice || gap || "Nothing was scanned before this was listed." })
13079
13427
  ] });
13080
13428
  }
13081
13429
  function PublicRow({ row, fields }) {
13082
13430
  const document2 = row.document ?? {};
13083
13431
  if (!fields) {
13084
13432
  const first = Object.entries(document2).filter(([key]) => !key.startsWith("_"));
13085
- return /* @__PURE__ */ jsx52("dl", { className: "grid grid-cols-[auto_1fr] gap-x-3 gap-y-1 text-sm", children: first.map(([key, value]) => /* @__PURE__ */ jsxs48("div", { className: "contents", children: [
13086
- /* @__PURE__ */ jsx52("dt", { className: "text-xs text-muted-foreground", children: key }),
13087
- /* @__PURE__ */ jsx52("dd", { className: "text-sm", children: String(value ?? "") })
13433
+ return /* @__PURE__ */ jsx53("dl", { className: "grid grid-cols-[auto_1fr] gap-x-3 gap-y-1 text-sm", children: first.map(([key, value]) => /* @__PURE__ */ jsxs49("div", { className: "contents", children: [
13434
+ /* @__PURE__ */ jsx53("dt", { className: "text-xs text-muted-foreground", children: key }),
13435
+ /* @__PURE__ */ jsx53("dd", { className: "text-sm", children: String(value ?? "") })
13088
13436
  ] }, key)) });
13089
13437
  }
13090
- return /* @__PURE__ */ jsx52("dl", { className: "grid grid-cols-[auto_1fr] gap-x-3 gap-y-1", children: fields.map((field) => /* @__PURE__ */ jsxs48("div", { className: "contents", children: [
13091
- /* @__PURE__ */ jsx52("dt", { className: "text-xs text-muted-foreground", children: field.label }),
13092
- /* @__PURE__ */ jsx52("dd", { className: "text-sm", children: /* @__PURE__ */ jsx52(RecordValue, { field, value: document2[field.key], document: row.document }) })
13438
+ return /* @__PURE__ */ jsx53("dl", { className: "grid grid-cols-[auto_1fr] gap-x-3 gap-y-1", children: fields.map((field) => /* @__PURE__ */ jsxs49("div", { className: "contents", children: [
13439
+ /* @__PURE__ */ jsx53("dt", { className: "text-xs text-muted-foreground", children: field.label }),
13440
+ /* @__PURE__ */ jsx53("dd", { className: "text-sm", children: /* @__PURE__ */ jsx53(RecordValue, { field, value: document2[field.key], document: row.document }) })
13093
13441
  ] }, field.key)) });
13094
13442
  }
13095
13443
 
13096
13444
  // src/EmbedFrame.tsx
13097
- import { useCallback as useCallback34, useEffect as useEffect38, useState as useState48 } from "react";
13098
- import { useRecordsClient as useRecordsClient40 } from "@ai-matrx/records/react";
13099
- import { Button as Button43, Input as Input5, Skeleton as Skeleton31, Textarea as Textarea4, cn as cn47 } from "@ai-matrx/design-system";
13445
+ import { useCallback as useCallback35, useEffect as useEffect39, useState as useState49 } from "react";
13446
+ import { useRecordsClient as useRecordsClient41 } from "@ai-matrx/records/react";
13447
+ import { Button as Button44, Input as Input5, Skeleton as Skeleton32, Textarea as Textarea4, cn as cn48 } from "@ai-matrx/design-system";
13100
13448
  import { useTable as useTable21 } from "@ai-matrx/records/react";
13101
- import { Fragment as Fragment24, jsx as jsx53, jsxs as jsxs49 } from "react/jsx-runtime";
13449
+ import { Fragment as Fragment25, jsx as jsx54, jsxs as jsxs50 } from "react/jsx-runtime";
13102
13450
  function EmbedFrame({
13103
13451
  tableId,
13104
13452
  formId,
@@ -13107,18 +13455,18 @@ function EmbedFrame({
13107
13455
  embedUrl,
13108
13456
  className
13109
13457
  }) {
13110
- const client = useRecordsClient40();
13458
+ const client = useRecordsClient41();
13111
13459
  const host = useRecordsUi();
13112
13460
  const table = useTable21(tableId);
13113
13461
  const rights = useTableRights(table.data);
13114
- const [origins, setOrigins] = useState48("");
13115
- const [secret, setSecret] = useState48(null);
13116
- const [tokenId, setTokenId] = useState48(null);
13117
- const [error, setError] = useState48(null);
13118
- const [busy, setBusy] = useState48(false);
13462
+ const [origins, setOrigins] = useState49("");
13463
+ const [secret, setSecret] = useState49(null);
13464
+ const [tokenId, setTokenId] = useState49(null);
13465
+ const [error, setError] = useState49(null);
13466
+ const [busy, setBusy] = useState49(false);
13119
13467
  const mode = formId ? "write" : "read";
13120
13468
  const parsed = origins.split(/[\s,]+/).map((o) => o.trim()).filter((o) => o.length > 0);
13121
- const issue = useCallback34(async () => {
13469
+ const issue = useCallback35(async () => {
13122
13470
  setBusy(true);
13123
13471
  setError(null);
13124
13472
  const minted = await client.anonTokenIssue({
@@ -13137,7 +13485,7 @@ function EmbedFrame({
13137
13485
  setTokenId(minted.data.token_id);
13138
13486
  host.notify?.success("Embed token issued. Copy it now \u2014 it is never shown again.");
13139
13487
  }, [client, formId, host, mode, parsed, recordId, savedViewId]);
13140
- const revoke = useCallback34(async () => {
13488
+ const revoke = useCallback35(async () => {
13141
13489
  if (!tokenId) return;
13142
13490
  setBusy(true);
13143
13491
  const done = await client.anonTokenRevoke({ token_id: tokenId });
@@ -13149,24 +13497,24 @@ function EmbedFrame({
13149
13497
  setSecret(null);
13150
13498
  setTokenId(null);
13151
13499
  }, [client, tokenId]);
13152
- if (table.error) return /* @__PURE__ */ jsx53(RefusalNotice, { error: table.error, className });
13153
- if (table.loading) return /* @__PURE__ */ jsx53(Skeleton31, { className: cn47("h-48 w-full", className) });
13500
+ if (table.error) return /* @__PURE__ */ jsx54(RefusalNotice, { error: table.error, className });
13501
+ if (table.loading) return /* @__PURE__ */ jsx54(Skeleton32, { className: cn48("h-48 w-full", className) });
13154
13502
  if (!rights.admin) {
13155
- return /* @__PURE__ */ jsxs49("p", { className: cn47("rounded border border-dashed px-2 py-1 text-xs text-muted-foreground", className), children: [
13503
+ return /* @__PURE__ */ jsxs50("p", { className: cn48("rounded border border-dashed px-2 py-1 text-xs text-muted-foreground", className), children: [
13156
13504
  rights.why("structure"),
13157
13505
  " Issuing an embed opens a way in from somebody else's website, so the store asks for the admin level on this table before it will mint one."
13158
13506
  ] });
13159
13507
  }
13160
13508
  const snippet = secret ? `<iframe src="${embedUrl}#t=${secret}" style="width:100%;height:640px;border:0" loading="lazy"></iframe>` : null;
13161
- return /* @__PURE__ */ jsxs49("section", { className: cn47("flex min-h-0 flex-col gap-2", className), children: [
13162
- /* @__PURE__ */ jsxs49("header", { className: "flex items-center gap-2 text-xs text-muted-foreground", children: [
13163
- /* @__PURE__ */ jsx53("span", { className: "font-medium text-foreground", children: "Embed" }),
13164
- /* @__PURE__ */ jsx53("span", { children: mode === "write" ? "a form people can send" : "a read-only view" })
13509
+ return /* @__PURE__ */ jsxs50("section", { className: cn48("flex min-h-0 flex-col gap-2", className), children: [
13510
+ /* @__PURE__ */ jsxs50("header", { className: "flex items-center gap-2 text-xs text-muted-foreground", children: [
13511
+ /* @__PURE__ */ jsx54("span", { className: "font-medium text-foreground", children: "Embed" }),
13512
+ /* @__PURE__ */ jsx54("span", { children: mode === "write" ? "a form people can send" : "a read-only view" })
13165
13513
  ] }),
13166
- error ? /* @__PURE__ */ jsx53(RefusalNotice, { error }) : null,
13167
- /* @__PURE__ */ jsxs49("label", { className: "flex flex-col gap-1 text-xs", children: [
13168
- /* @__PURE__ */ jsx53("span", { className: "text-muted-foreground", children: "Sites this embed works on" }),
13169
- /* @__PURE__ */ jsx53(
13514
+ error ? /* @__PURE__ */ jsx54(RefusalNotice, { error }) : null,
13515
+ /* @__PURE__ */ jsxs50("label", { className: "flex flex-col gap-1 text-xs", children: [
13516
+ /* @__PURE__ */ jsx54("span", { className: "text-muted-foreground", children: "Sites this embed works on" }),
13517
+ /* @__PURE__ */ jsx54(
13170
13518
  Input5,
13171
13519
  {
13172
13520
  "aria-label": "Allowed origins",
@@ -13176,18 +13524,18 @@ function EmbedFrame({
13176
13524
  onChange: (e) => setOrigins(e.target.value)
13177
13525
  }
13178
13526
  ),
13179
- /* @__PURE__ */ jsxs49("span", { className: "text-[11px] text-muted-foreground", children: [
13527
+ /* @__PURE__ */ jsxs50("span", { className: "text-[11px] text-muted-foreground", children: [
13180
13528
  `Name every site, scheme and host and port. An empty list is refused rather than quietly meaning "anywhere": a token that works from any page on the internet includes an attacker's. The match is exact, so `,
13181
- /* @__PURE__ */ jsx53("code", { children: "example.com.evil.test" }),
13529
+ /* @__PURE__ */ jsx54("code", { children: "example.com.evil.test" }),
13182
13530
  " will not pass."
13183
13531
  ] })
13184
13532
  ] }),
13185
- !secret ? /* @__PURE__ */ jsx53(Button43, { size: "sm", className: "self-start", disabled: busy || parsed.length === 0, onClick: () => void issue(), children: busy ? "Issuing\u2026" : "Issue embed" }) : /* @__PURE__ */ jsxs49(Fragment24, { children: [
13186
- /* @__PURE__ */ jsx53("p", { className: "rounded border px-2 py-1 text-xs text-muted-foreground", children: "Copy this now. Only its digest is stored, so nobody \u2014 including us \u2014 can show it to you again; if it is lost, issue a new one and replace the old." }),
13187
- /* @__PURE__ */ jsx53(Textarea4, { "aria-label": "Embed snippet", readOnly: true, rows: 3, className: "font-mono text-[11px]", value: snippet ?? "" }),
13188
- /* @__PURE__ */ jsxs49("div", { className: "flex gap-2", children: [
13189
- /* @__PURE__ */ jsx53(
13190
- Button43,
13533
+ !secret ? /* @__PURE__ */ jsx54(Button44, { size: "sm", className: "self-start", disabled: busy || parsed.length === 0, onClick: () => void issue(), children: busy ? "Issuing\u2026" : "Issue embed" }) : /* @__PURE__ */ jsxs50(Fragment25, { children: [
13534
+ /* @__PURE__ */ jsx54("p", { className: "rounded border px-2 py-1 text-xs text-muted-foreground", children: "Copy this now. Only its digest is stored, so nobody \u2014 including us \u2014 can show it to you again; if it is lost, issue a new one and replace the old." }),
13535
+ /* @__PURE__ */ jsx54(Textarea4, { "aria-label": "Embed snippet", readOnly: true, rows: 3, className: "font-mono text-[11px]", value: snippet ?? "" }),
13536
+ /* @__PURE__ */ jsxs50("div", { className: "flex gap-2", children: [
13537
+ /* @__PURE__ */ jsx54(
13538
+ Button44,
13191
13539
  {
13192
13540
  size: "sm",
13193
13541
  variant: "outline",
@@ -13195,19 +13543,19 @@ function EmbedFrame({
13195
13543
  children: "Copy"
13196
13544
  }
13197
13545
  ),
13198
- /* @__PURE__ */ jsx53(Button43, { size: "sm", variant: "ghost", disabled: busy, onClick: () => void revoke(), children: "Revoke" })
13546
+ /* @__PURE__ */ jsx54(Button44, { size: "sm", variant: "ghost", disabled: busy, onClick: () => void revoke(), children: "Revoke" })
13199
13547
  ] })
13200
13548
  ] })
13201
13549
  ] });
13202
13550
  }
13203
13551
  function useEmbedHandshake(args) {
13204
- const client = useRecordsClient40();
13205
- const [binding, setBinding] = useState48(null);
13206
- const [error, setError] = useState48(null);
13207
- const [loading, setLoading] = useState48(true);
13552
+ const client = useRecordsClient41();
13553
+ const [binding, setBinding] = useState49(null);
13554
+ const [error, setError] = useState49(null);
13555
+ const [loading, setLoading] = useState49(true);
13208
13556
  const origin = args.origin ?? (typeof location === "undefined" ? "" : location.origin);
13209
13557
  const { secret, requiredMode } = args;
13210
- useEffect38(() => {
13558
+ useEffect39(() => {
13211
13559
  let cancelled = false;
13212
13560
  setLoading(true);
13213
13561
  setError(null);
@@ -13230,7 +13578,7 @@ function useEmbedHandshake(args) {
13230
13578
 
13231
13579
  // src/RecordsMount.tsx
13232
13580
  import { RecordsProvider } from "@ai-matrx/records/react";
13233
- import { jsx as jsx54 } from "react/jsx-runtime";
13581
+ import { jsx as jsx55 } from "react/jsx-runtime";
13234
13582
  var STORE_DECIDES_REASON = "This host offers exactly what the record store says this person may do: the level comes back with the table on the read door, so a control they cannot use is never drawn in the first place.";
13235
13583
  function storeDecidesRights(_table) {
13236
13584
  return NO_RIGHTS;
@@ -13243,7 +13591,7 @@ function RecordsMount({
13243
13591
  }) {
13244
13592
  const bound = { ...host ?? {} };
13245
13593
  void letTheStoreDecideRights;
13246
- return /* @__PURE__ */ jsx54(RecordsProvider, { config, children: /* @__PURE__ */ jsx54(RecordsUiProvider, { value: bound, children: /* @__PURE__ */ jsx54(RecordLabelProvider, { children }) }) });
13594
+ return /* @__PURE__ */ jsx55(RecordsProvider, { config, children: /* @__PURE__ */ jsx55(RecordsUiProvider, { value: bound, children: /* @__PURE__ */ jsx55(RecordLabelProvider, { children }) }) });
13247
13595
  }
13248
13596
  function personActor(userId) {
13249
13597
  return userId ? { actor: "user", user_id: userId } : { actor: "user" };
@@ -13262,9 +13610,9 @@ function recordsDataSource(client, fallbackSchema = "custom") {
13262
13610
  }
13263
13611
 
13264
13612
  // src/TablesHome.tsx
13265
- import { useCallback as useCallback35, useEffect as useEffect39, useState as useState49 } from "react";
13266
- import { useRecordsClient as useRecordsClient41, useTables as useTables3 } from "@ai-matrx/records/react";
13267
- import { BasicInput as BasicInput15, Button as Button44, Skeleton as Skeleton32, cn as cn48 } from "@ai-matrx/design-system";
13613
+ import { useCallback as useCallback36, useEffect as useEffect40, useState as useState50 } from "react";
13614
+ import { useRecordsClient as useRecordsClient42, useTables as useTables3 } from "@ai-matrx/records/react";
13615
+ import { BasicInput as BasicInput16, Button as Button45, Skeleton as Skeleton33, cn as cn49 } from "@ai-matrx/design-system";
13268
13616
 
13269
13617
  // src/createTable.ts
13270
13618
  function tokenFor(name) {
@@ -13374,7 +13722,7 @@ function declaredType(field) {
13374
13722
  }
13375
13723
 
13376
13724
  // src/TablesHome.tsx
13377
- import { Fragment as Fragment25, jsx as jsx55, jsxs as jsxs50 } from "react/jsx-runtime";
13725
+ import { Fragment as Fragment26, jsx as jsx56, jsxs as jsxs51 } from "react/jsx-runtime";
13378
13726
  var TABLE_LANES = ["mine", "organization", "system", "community", "app"];
13379
13727
  var LANE_TITLE = {
13380
13728
  mine: "Mine",
@@ -13399,15 +13747,15 @@ function laneFor(table) {
13399
13747
  return "organization";
13400
13748
  }
13401
13749
  function TablesHome({ onOpenTable, className }) {
13402
- const client = useRecordsClient41();
13750
+ const client = useRecordsClient42();
13403
13751
  const tables = useTables3();
13404
- const [creating, setCreating] = useState49(false);
13405
- const [name, setName] = useState49("");
13406
- const [busy, setBusy] = useState49(false);
13407
- const [error, setError] = useState49(null);
13408
- const [importInto, setImportInto] = useState49(null);
13409
- const [boards, setBoards] = useState49(null);
13410
- useEffect39(() => {
13752
+ const [creating, setCreating] = useState50(false);
13753
+ const [name, setName] = useState50("");
13754
+ const [busy, setBusy] = useState50(false);
13755
+ const [error, setError] = useState50(null);
13756
+ const [importInto, setImportInto] = useState50(null);
13757
+ const [boards, setBoards] = useState50(null);
13758
+ useEffect40(() => {
13411
13759
  let cancelled = false;
13412
13760
  void client.dashboards({}).then((result) => {
13413
13761
  if (cancelled) return;
@@ -13417,7 +13765,7 @@ function TablesHome({ onOpenTable, className }) {
13417
13765
  cancelled = true;
13418
13766
  };
13419
13767
  }, [client]);
13420
- const create = useCallback35(
13768
+ const create = useCallback36(
13421
13769
  async (mode) => {
13422
13770
  const trimmed = name.trim();
13423
13771
  if (!trimmed) return;
@@ -13437,10 +13785,10 @@ function TablesHome({ onOpenTable, className }) {
13437
13785
  },
13438
13786
  [client, name, onOpenTable, tables]
13439
13787
  );
13440
- return /* @__PURE__ */ jsxs50("div", { className: cn48("flex min-h-0 flex-col gap-4", className), children: [
13441
- /* @__PURE__ */ jsx55("div", { className: "flex items-center gap-2", children: creating ? /* @__PURE__ */ jsxs50(Fragment25, { children: [
13442
- /* @__PURE__ */ jsx55(
13443
- BasicInput15,
13788
+ return /* @__PURE__ */ jsxs51("div", { className: cn49("flex min-h-0 flex-col gap-4", className), children: [
13789
+ /* @__PURE__ */ jsx56("div", { className: "flex items-center gap-2", children: creating ? /* @__PURE__ */ jsxs51(Fragment26, { children: [
13790
+ /* @__PURE__ */ jsx56(
13791
+ BasicInput16,
13444
13792
  {
13445
13793
  autoFocus: true,
13446
13794
  value: name,
@@ -13453,9 +13801,9 @@ function TablesHome({ onOpenTable, className }) {
13453
13801
  className: "h-8 max-w-xs"
13454
13802
  }
13455
13803
  ),
13456
- /* @__PURE__ */ jsx55(Button44, { size: "sm", disabled: busy || name.trim().length === 0, onClick: () => void create("open"), children: busy ? "Declaring\u2026" : "Create" }),
13457
- /* @__PURE__ */ jsx55(
13458
- Button44,
13804
+ /* @__PURE__ */ jsx56(Button45, { size: "sm", disabled: busy || name.trim().length === 0, onClick: () => void create("open"), children: busy ? "Declaring\u2026" : "Create" }),
13805
+ /* @__PURE__ */ jsx56(
13806
+ Button45,
13459
13807
  {
13460
13808
  size: "sm",
13461
13809
  variant: "outline",
@@ -13464,37 +13812,37 @@ function TablesHome({ onOpenTable, className }) {
13464
13812
  children: "Create and import a file"
13465
13813
  }
13466
13814
  ),
13467
- /* @__PURE__ */ jsx55(Button44, { size: "sm", variant: "ghost", onClick: () => setCreating(false), children: "Cancel" })
13468
- ] }) : /* @__PURE__ */ jsx55(Button44, { size: "sm", onClick: () => setCreating(true), children: "New table" }) }),
13469
- error ? /* @__PURE__ */ jsx55(RefusalNotice, { error }) : null,
13470
- importInto ? /* @__PURE__ */ jsxs50("div", { className: "rounded-md border p-3", children: [
13471
- /* @__PURE__ */ jsx55(ImportWizard, { tableId: importInto, onDone: () => setImportInto(null) }),
13472
- /* @__PURE__ */ jsx55(Button44, { size: "sm", variant: "ghost", className: "mt-2", onClick: () => onOpenTable?.(importInto), children: "Open the table" })
13815
+ /* @__PURE__ */ jsx56(Button45, { size: "sm", variant: "ghost", onClick: () => setCreating(false), children: "Cancel" })
13816
+ ] }) : /* @__PURE__ */ jsx56(Button45, { size: "sm", onClick: () => setCreating(true), children: "New table" }) }),
13817
+ error ? /* @__PURE__ */ jsx56(RefusalNotice, { error }) : null,
13818
+ importInto ? /* @__PURE__ */ jsxs51("div", { className: "rounded-md border p-3", children: [
13819
+ /* @__PURE__ */ jsx56(ImportWizard, { tableId: importInto, onDone: () => setImportInto(null) }),
13820
+ /* @__PURE__ */ jsx56(Button45, { size: "sm", variant: "ghost", className: "mt-2", onClick: () => onOpenTable?.(importInto), children: "Open the table" })
13473
13821
  ] }) : null,
13474
- tables.error ? /* @__PURE__ */ jsx55(RefusalNotice, { error: tables.error }) : null,
13475
- tables.loading && !tables.data ? /* @__PURE__ */ jsxs50("div", { className: "space-y-2", children: [
13476
- /* @__PURE__ */ jsx55(Skeleton32, { className: "h-6 w-40" }),
13477
- /* @__PURE__ */ jsx55(Skeleton32, { className: "h-16 w-full" })
13822
+ tables.error ? /* @__PURE__ */ jsx56(RefusalNotice, { error: tables.error }) : null,
13823
+ tables.loading && !tables.data ? /* @__PURE__ */ jsxs51("div", { className: "space-y-2", children: [
13824
+ /* @__PURE__ */ jsx56(Skeleton33, { className: "h-6 w-40" }),
13825
+ /* @__PURE__ */ jsx56(Skeleton33, { className: "h-16 w-full" })
13478
13826
  ] }) : null,
13479
- /* @__PURE__ */ jsx55(BookingSlots, {}),
13480
- boards && boards.length > 0 ? /* @__PURE__ */ jsxs50("section", { className: "space-y-1.5", children: [
13481
- /* @__PURE__ */ jsxs50("div", { className: "flex items-baseline gap-2", children: [
13482
- /* @__PURE__ */ jsx55("h2", { className: "text-sm font-medium", children: "Dashboards" }),
13483
- /* @__PURE__ */ jsx55("span", { className: "text-xs opacity-60", children: boards.length })
13827
+ /* @__PURE__ */ jsx56(BookingSlots, {}),
13828
+ boards && boards.length > 0 ? /* @__PURE__ */ jsxs51("section", { className: "space-y-1.5", children: [
13829
+ /* @__PURE__ */ jsxs51("div", { className: "flex items-baseline gap-2", children: [
13830
+ /* @__PURE__ */ jsx56("h2", { className: "text-sm font-medium", children: "Dashboards" }),
13831
+ /* @__PURE__ */ jsx56("span", { className: "text-xs opacity-60", children: boards.length })
13484
13832
  ] }),
13485
- /* @__PURE__ */ jsx55("ul", { className: "grid gap-1.5 sm:grid-cols-2 lg:grid-cols-3", children: boards.map((board) => /* @__PURE__ */ jsx55("li", { children: /* @__PURE__ */ jsxs50(
13833
+ /* @__PURE__ */ jsx56("ul", { className: "grid gap-1.5 sm:grid-cols-2 lg:grid-cols-3", children: boards.map((board) => /* @__PURE__ */ jsx56("li", { children: /* @__PURE__ */ jsxs51(
13486
13834
  "button",
13487
13835
  {
13488
13836
  type: "button",
13489
13837
  disabled: !board.table_id,
13490
13838
  onClick: () => board.table_id ? onOpenTable?.(board.table_id, board.dashboard_id) : void 0,
13491
- className: cn48(
13839
+ className: cn49(
13492
13840
  "w-full rounded-md border px-3 py-2 text-left transition-colors",
13493
13841
  onOpenTable && board.table_id ? "hover:bg-accent" : "cursor-default"
13494
13842
  ),
13495
13843
  children: [
13496
- /* @__PURE__ */ jsx55("span", { className: "block truncate text-sm", children: board.name }),
13497
- /* @__PURE__ */ jsxs50("span", { className: "block truncate text-xs opacity-60", children: [
13844
+ /* @__PURE__ */ jsx56("span", { className: "block truncate text-sm", children: board.name }),
13845
+ /* @__PURE__ */ jsxs51("span", { className: "block truncate text-xs opacity-60", children: [
13498
13846
  board.block_count,
13499
13847
  " block",
13500
13848
  board.block_count === 1 ? "" : "s",
@@ -13510,23 +13858,23 @@ function TablesHome({ onOpenTable, className }) {
13510
13858
  ] }) : null,
13511
13859
  tables.data ? TABLE_LANES.map((lane) => {
13512
13860
  const rows = tables.data.filter((t) => laneFor(t) === lane);
13513
- return /* @__PURE__ */ jsxs50("section", { className: "space-y-1.5", children: [
13514
- /* @__PURE__ */ jsxs50("div", { className: "flex items-baseline gap-2", children: [
13515
- /* @__PURE__ */ jsx55("h2", { className: "text-sm font-medium", children: LANE_TITLE[lane] }),
13516
- /* @__PURE__ */ jsx55("span", { className: "text-xs opacity-60", children: rows.length })
13861
+ return /* @__PURE__ */ jsxs51("section", { className: "space-y-1.5", children: [
13862
+ /* @__PURE__ */ jsxs51("div", { className: "flex items-baseline gap-2", children: [
13863
+ /* @__PURE__ */ jsx56("h2", { className: "text-sm font-medium", children: LANE_TITLE[lane] }),
13864
+ /* @__PURE__ */ jsx56("span", { className: "text-xs opacity-60", children: rows.length })
13517
13865
  ] }),
13518
- rows.length === 0 ? /* @__PURE__ */ jsx55("p", { className: "text-xs opacity-70", children: LANE_EMPTY[lane] }) : /* @__PURE__ */ jsx55("ul", { className: "grid gap-1.5 sm:grid-cols-2 lg:grid-cols-3", children: rows.map((table) => /* @__PURE__ */ jsx55("li", { children: /* @__PURE__ */ jsxs50(
13866
+ rows.length === 0 ? /* @__PURE__ */ jsx56("p", { className: "text-xs opacity-70", children: LANE_EMPTY[lane] }) : /* @__PURE__ */ jsx56("ul", { className: "grid gap-1.5 sm:grid-cols-2 lg:grid-cols-3", children: rows.map((table) => /* @__PURE__ */ jsx56("li", { children: /* @__PURE__ */ jsxs51(
13519
13867
  "button",
13520
13868
  {
13521
13869
  type: "button",
13522
13870
  onClick: () => onOpenTable?.(table.id),
13523
- className: cn48(
13871
+ className: cn49(
13524
13872
  "w-full rounded-md border px-3 py-2 text-left transition-colors",
13525
13873
  onOpenTable ? "hover:bg-accent" : "cursor-default"
13526
13874
  ),
13527
13875
  children: [
13528
- /* @__PURE__ */ jsx55("span", { className: "block truncate text-sm", children: tableName(table) }),
13529
- /* @__PURE__ */ jsxs50("span", { className: "block truncate text-xs opacity-60", children: [
13876
+ /* @__PURE__ */ jsx56("span", { className: "block truncate text-sm", children: tableName(table) }),
13877
+ /* @__PURE__ */ jsxs51("span", { className: "block truncate text-xs opacity-60", children: [
13530
13878
  table.fields.length,
13531
13879
  " field",
13532
13880
  table.fields.length === 1 ? "" : "s",
@@ -13541,10 +13889,10 @@ function TablesHome({ onOpenTable, className }) {
13541
13889
  }
13542
13890
 
13543
13891
  // src/TablePage.tsx
13544
- import { useCallback as useCallback36, useEffect as useEffect40, useState as useState50 } from "react";
13545
- import { useRecordsClient as useRecordsClient42, useTable as useTable22 } from "@ai-matrx/records/react";
13546
- import { Button as Button45, Separator as Separator11, Skeleton as Skeleton33, cn as cn49 } from "@ai-matrx/design-system";
13547
- import { Fragment as Fragment26, jsx as jsx56, jsxs as jsxs51 } from "react/jsx-runtime";
13892
+ import { useCallback as useCallback37, useEffect as useEffect41, useState as useState51 } from "react";
13893
+ import { useRecordsClient as useRecordsClient43, useTable as useTable22 } from "@ai-matrx/records/react";
13894
+ import { Button as Button46, Separator as Separator12, Skeleton as Skeleton34, cn as cn50 } from "@ai-matrx/design-system";
13895
+ import { Fragment as Fragment27, jsx as jsx57, jsxs as jsxs52 } from "react/jsx-runtime";
13548
13896
  var TABLE_NOT_REACHABLE = "This table is not in the organization you are working in, so there is nothing here to show. Switch to the organization that owns it and open it again \u2014 or it may have been deleted.";
13549
13897
  var DEFAULT_VIEW_NAME = "All records";
13550
13898
  var VIEW_NOT_SAVED_YET = "This table has no saved view yet, so the layout you pick here is not being kept. Save one from the bar above and it is remembered.";
@@ -13575,29 +13923,29 @@ function TablePage({
13575
13923
  activeRecordId,
13576
13924
  className
13577
13925
  }) {
13578
- const client = useRecordsClient42();
13926
+ const client = useRecordsClient43();
13579
13927
  const table = useTable22(tableId);
13580
13928
  const rights = useTableRights(table.data);
13581
- const organizationId = useRecordsClient42().config.organizationId;
13582
- const [view, setView] = useState50(null);
13929
+ const organizationId = useRecordsClient43().config.organizationId;
13930
+ const [view, setView] = useState51(null);
13583
13931
  const opening = openingRail(activeRecordId);
13584
- const [asking, setAsking] = useState50(null);
13585
- const [surface, setSurface] = useState50({
13932
+ const [asking, setAsking] = useState51(null);
13933
+ const [surface, setSurface] = useState51({
13586
13934
  main: activeDashboardId ? "dashboards" : "records",
13587
13935
  rail: opening.rail
13588
13936
  });
13589
13937
  const { main, rail } = surface;
13590
13938
  const setRail = (next) => setSurface((now) => ({ ...now, rail: next }));
13591
- const [openRecord, setOpenRecord] = useState50(opening.record);
13939
+ const [openRecord, setOpenRecord] = useState51(opening.record);
13592
13940
  const viewVersion = useRecordVersion(view?.id ?? null);
13593
13941
  const press = (pressed) => setSurface((now) => chooseSurface(now, pressed));
13594
13942
  const show = (next) => press({ rail: next });
13595
- useEffect40(() => {
13943
+ useEffect41(() => {
13596
13944
  if (!activeRecordId) return;
13597
13945
  setOpenRecord(activeRecordId);
13598
13946
  setSurface((now) => ({ ...now, rail: "record" }));
13599
13947
  }, [activeRecordId]);
13600
- const patchView = useCallback36(
13948
+ const patchView = useCallback37(
13601
13949
  async (patch) => {
13602
13950
  const current = view;
13603
13951
  if (!current) return;
@@ -13613,19 +13961,19 @@ function TablePage({
13613
13961
  },
13614
13962
  [client, view, viewVersion]
13615
13963
  );
13616
- if (table.error) return /* @__PURE__ */ jsx56(RefusalNotice, { error: table.error });
13617
- if (table.loading) return /* @__PURE__ */ jsx56(Skeleton33, { className: "h-40 w-full" });
13964
+ if (table.error) return /* @__PURE__ */ jsx57(RefusalNotice, { error: table.error });
13965
+ if (table.loading) return /* @__PURE__ */ jsx57(Skeleton34, { className: "h-40 w-full" });
13618
13966
  if (!table.data) {
13619
- return /* @__PURE__ */ jsxs51("div", { className: cn49("flex flex-col items-start gap-2 rounded-md border border-dashed p-6", className), children: [
13620
- /* @__PURE__ */ jsx56("p", { className: "text-sm font-medium", children: "This table is not here" }),
13621
- /* @__PURE__ */ jsx56("p", { className: "max-w-prose text-xs text-muted-foreground", children: TABLE_NOT_REACHABLE }),
13622
- onLeave ? /* @__PURE__ */ jsx56(Button45, { size: "sm", variant: "outline", onClick: onLeave, children: leaveLabel }) : null
13967
+ return /* @__PURE__ */ jsxs52("div", { className: cn50("flex flex-col items-start gap-2 rounded-md border border-dashed p-6", className), children: [
13968
+ /* @__PURE__ */ jsx57("p", { className: "text-sm font-medium", children: "This table is not here" }),
13969
+ /* @__PURE__ */ jsx57("p", { className: "max-w-prose text-xs text-muted-foreground", children: TABLE_NOT_REACHABLE }),
13970
+ onLeave ? /* @__PURE__ */ jsx57(Button46, { size: "sm", variant: "outline", onClick: onLeave, children: leaveLabel }) : null
13623
13971
  ] });
13624
13972
  }
13625
- return /* @__PURE__ */ jsxs51("div", { className: cn49("flex min-h-0 gap-4", className), children: [
13626
- /* @__PURE__ */ jsxs51("div", { className: "flex min-w-0 flex-1 flex-col gap-2", children: [
13627
- /* @__PURE__ */ jsxs51("div", { className: "flex flex-wrap items-center gap-1.5", children: [
13628
- /* @__PURE__ */ jsx56(
13973
+ return /* @__PURE__ */ jsxs52("div", { className: cn50("flex min-h-0 gap-4", className), children: [
13974
+ /* @__PURE__ */ jsxs52("div", { className: "flex min-w-0 flex-1 flex-col gap-2", children: [
13975
+ /* @__PURE__ */ jsxs52("div", { className: "flex flex-wrap items-center gap-1.5", children: [
13976
+ /* @__PURE__ */ jsx57(
13629
13977
  ViewBar,
13630
13978
  {
13631
13979
  tableId,
@@ -13634,10 +13982,10 @@ function TablePage({
13634
13982
  className: "min-w-0 flex-1"
13635
13983
  }
13636
13984
  ),
13637
- rights.write && view && view.layout !== "grid" ? /* @__PURE__ */ jsx56(Button45, { size: "sm", onClick: () => show("new-record"), children: "New record" }) : null,
13638
- rights.structure ? /* @__PURE__ */ jsxs51(Fragment26, { children: [
13639
- /* @__PURE__ */ jsx56(
13640
- Button45,
13985
+ rights.write && view && view.layout !== "grid" ? /* @__PURE__ */ jsx57(Button46, { size: "sm", onClick: () => show("new-record"), children: "New record" }) : null,
13986
+ rights.structure ? /* @__PURE__ */ jsxs52(Fragment27, { children: [
13987
+ /* @__PURE__ */ jsx57(
13988
+ Button46,
13641
13989
  {
13642
13990
  size: "sm",
13643
13991
  variant: surfaceChosen(surface, { rail: "field" }) ? "secondary" : "outline",
@@ -13646,8 +13994,8 @@ function TablePage({
13646
13994
  children: "Add field"
13647
13995
  }
13648
13996
  ),
13649
- /* @__PURE__ */ jsx56(
13650
- Button45,
13997
+ /* @__PURE__ */ jsx57(
13998
+ Button46,
13651
13999
  {
13652
14000
  size: "sm",
13653
14001
  variant: surfaceChosen(surface, { rail: "settings" }) ? "secondary" : "ghost",
@@ -13656,8 +14004,8 @@ function TablePage({
13656
14004
  children: "Settings"
13657
14005
  }
13658
14006
  ),
13659
- /* @__PURE__ */ jsx56(
13660
- Button45,
14007
+ /* @__PURE__ */ jsx57(
14008
+ Button46,
13661
14009
  {
13662
14010
  size: "sm",
13663
14011
  variant: surfaceChosen(surface, { rail: "import" }) ? "secondary" : "ghost",
@@ -13667,8 +14015,8 @@ function TablePage({
13667
14015
  }
13668
14016
  )
13669
14017
  ] }) : null,
13670
- /* @__PURE__ */ jsx56(
13671
- Button45,
14018
+ /* @__PURE__ */ jsx57(
14019
+ Button46,
13672
14020
  {
13673
14021
  size: "sm",
13674
14022
  variant: surfaceChosen(surface, { main: "dashboards" }) ? "secondary" : "ghost",
@@ -13677,8 +14025,8 @@ function TablePage({
13677
14025
  children: "Dashboards"
13678
14026
  }
13679
14027
  ),
13680
- /* @__PURE__ */ jsx56(
13681
- Button45,
14028
+ /* @__PURE__ */ jsx57(
14029
+ Button46,
13682
14030
  {
13683
14031
  size: "sm",
13684
14032
  variant: surfaceChosen(surface, { rail: "forms" }) ? "secondary" : "ghost",
@@ -13687,8 +14035,8 @@ function TablePage({
13687
14035
  children: "Forms"
13688
14036
  }
13689
14037
  ),
13690
- /* @__PURE__ */ jsx56(
13691
- Button45,
14038
+ /* @__PURE__ */ jsx57(
14039
+ Button46,
13692
14040
  {
13693
14041
  size: "sm",
13694
14042
  variant: surfaceChosen(surface, { rail: "bookings" }) ? "secondary" : "ghost",
@@ -13697,8 +14045,8 @@ function TablePage({
13697
14045
  children: "Bookings"
13698
14046
  }
13699
14047
  ),
13700
- /* @__PURE__ */ jsx56(
13701
- Button45,
14048
+ /* @__PURE__ */ jsx57(
14049
+ Button46,
13702
14050
  {
13703
14051
  size: "sm",
13704
14052
  variant: surfaceChosen(surface, { rail: "checklists" }) ? "secondary" : "ghost",
@@ -13707,8 +14055,8 @@ function TablePage({
13707
14055
  children: "Checklists"
13708
14056
  }
13709
14057
  ),
13710
- /* @__PURE__ */ jsx56(
13711
- Button45,
14058
+ /* @__PURE__ */ jsx57(
14059
+ Button46,
13712
14060
  {
13713
14061
  size: "sm",
13714
14062
  variant: surfaceChosen(surface, { rail: "notifications" }) ? "secondary" : "ghost",
@@ -13717,8 +14065,8 @@ function TablePage({
13717
14065
  children: "Notifications"
13718
14066
  }
13719
14067
  ),
13720
- /* @__PURE__ */ jsx56(
13721
- Button45,
14068
+ /* @__PURE__ */ jsx57(
14069
+ Button46,
13722
14070
  {
13723
14071
  size: "sm",
13724
14072
  variant: surfaceChosen(surface, { rail: "portals" }) ? "secondary" : "ghost",
@@ -13727,8 +14075,8 @@ function TablePage({
13727
14075
  children: "Portals"
13728
14076
  }
13729
14077
  ),
13730
- /* @__PURE__ */ jsx56(
13731
- Button45,
14078
+ /* @__PURE__ */ jsx57(
14079
+ Button46,
13732
14080
  {
13733
14081
  size: "sm",
13734
14082
  variant: surfaceChosen(surface, { rail: "inbox" }) ? "secondary" : "ghost",
@@ -13737,7 +14085,7 @@ function TablePage({
13737
14085
  children: "Inbox"
13738
14086
  }
13739
14087
  ),
13740
- /* @__PURE__ */ jsx56(
14088
+ /* @__PURE__ */ jsx57(
13741
14089
  ShareControl,
13742
14090
  {
13743
14091
  kind: "table",
@@ -13747,10 +14095,10 @@ function TablePage({
13747
14095
  may: rights.share
13748
14096
  }
13749
14097
  ),
13750
- /* @__PURE__ */ jsx56(ExportMenu, { tableId })
14098
+ /* @__PURE__ */ jsx57(ExportMenu, { tableId })
13751
14099
  ] }),
13752
- main === "dashboards" ? /* @__PURE__ */ jsx56(DashboardCanvas, { tableId, activeDashboardId: activeDashboardId ?? null }) : /* @__PURE__ */ jsxs51(Fragment26, { children: [
13753
- /* @__PURE__ */ jsx56(
14100
+ main === "dashboards" ? /* @__PURE__ */ jsx57(DashboardCanvas, { tableId, activeDashboardId: activeDashboardId ?? null }) : /* @__PURE__ */ jsxs52(Fragment27, { children: [
14101
+ /* @__PURE__ */ jsx57(
13754
14102
  ViewSwitcher,
13755
14103
  {
13756
14104
  view: view ?? defaultView(tableId),
@@ -13767,18 +14115,18 @@ function TablePage({
13767
14115
  }
13768
14116
  }
13769
14117
  ),
13770
- view ? null : /* @__PURE__ */ jsx56("p", { className: "text-xs text-muted-foreground", children: VIEW_NOT_SAVED_YET })
14118
+ view ? null : /* @__PURE__ */ jsx57("p", { className: "text-xs text-muted-foreground", children: VIEW_NOT_SAVED_YET })
13771
14119
  ] })
13772
14120
  ] }),
13773
- rail === "none" ? null : /* @__PURE__ */ jsxs51("aside", { className: "w-[26rem] shrink-0 overflow-y-auto rounded-md border p-3", children: [
13774
- rail === "settings" ? /* @__PURE__ */ jsx56(
14121
+ rail === "none" ? null : /* @__PURE__ */ jsxs52("aside", { className: "w-[26rem] shrink-0 overflow-y-auto rounded-md border p-3", children: [
14122
+ rail === "settings" ? /* @__PURE__ */ jsx57(
13775
14123
  TableSettings,
13776
14124
  {
13777
14125
  tableId,
13778
14126
  ...onLeave ? { onDeleted: onLeave } : {}
13779
14127
  }
13780
14128
  ) : null,
13781
- rail === "inbox" ? /* @__PURE__ */ jsx56(
14129
+ rail === "inbox" ? /* @__PURE__ */ jsx57(
13782
14130
  ActionInbox,
13783
14131
  {
13784
14132
  tableId,
@@ -13788,9 +14136,9 @@ function TablePage({
13788
14136
  }
13789
14137
  }
13790
14138
  ) : null,
13791
- rail === "forms" ? /* @__PURE__ */ jsx56(FormsPanel, { tableId }) : null,
13792
- rail === "bookings" ? /* @__PURE__ */ jsx56(BookingSlots, { tableId }) : null,
13793
- rail === "checklists" ? /* @__PURE__ */ jsx56(
14139
+ rail === "forms" ? /* @__PURE__ */ jsx57(FormsPanel, { tableId }) : null,
14140
+ rail === "bookings" ? /* @__PURE__ */ jsx57(BookingSlots, { tableId }) : null,
14141
+ rail === "checklists" ? /* @__PURE__ */ jsx57(
13794
14142
  ChecklistsPanel,
13795
14143
  {
13796
14144
  tableId,
@@ -13800,11 +14148,11 @@ function TablePage({
13800
14148
  }
13801
14149
  }
13802
14150
  ) : null,
13803
- rail === "notifications" ? /* @__PURE__ */ jsx56(SubscriptionsPanel, { tableId }) : null,
13804
- rail === "portals" ? /* @__PURE__ */ jsx56(PortalsPanel, { tableId }) : null,
13805
- rail === "import" ? /* @__PURE__ */ jsx56(ImportWizard, { tableId, onDone: () => setRail("none") }) : null,
13806
- rail === "field" ? /* @__PURE__ */ jsx56(FieldEditor, { tableId, onSaved: () => setRail("none"), onCancel: () => setRail("none") }) : null,
13807
- rail === "new-record" ? /* @__PURE__ */ jsx56(
14151
+ rail === "notifications" ? /* @__PURE__ */ jsx57(SubscriptionsPanel, { tableId }) : null,
14152
+ rail === "portals" ? /* @__PURE__ */ jsx57(PortalsPanel, { tableId }) : null,
14153
+ rail === "import" ? /* @__PURE__ */ jsx57(ImportWizard, { tableId, onDone: () => setRail("none") }) : null,
14154
+ rail === "field" ? /* @__PURE__ */ jsx57(FieldEditor, { tableId, onSaved: () => setRail("none"), onCancel: () => setRail("none") }) : null,
14155
+ rail === "new-record" ? /* @__PURE__ */ jsx57(
13808
14156
  RecordForm,
13809
14157
  {
13810
14158
  tableId,
@@ -13815,7 +14163,7 @@ function TablePage({
13815
14163
  onCancel: () => setRail("none")
13816
14164
  }
13817
14165
  ) : null,
13818
- rail === "who-changed" && asking ? /* @__PURE__ */ jsx56(
14166
+ rail === "who-changed" && asking ? /* @__PURE__ */ jsx57(
13819
14167
  FieldHistoryPanel,
13820
14168
  {
13821
14169
  tableId,
@@ -13828,10 +14176,10 @@ function TablePage({
13828
14176
  }
13829
14177
  }
13830
14178
  ) : null,
13831
- rail === "record" && openRecord ? /* @__PURE__ */ jsxs51("div", { className: "space-y-3", children: [
13832
- /* @__PURE__ */ jsx56(Peek, { tableId, recordId: openRecord, onClose: () => setRail("none") }),
13833
- /* @__PURE__ */ jsx56(Separator11, {}),
13834
- /* @__PURE__ */ jsx56(
14179
+ rail === "record" && openRecord ? /* @__PURE__ */ jsxs52("div", { className: "space-y-3", children: [
14180
+ /* @__PURE__ */ jsx57(Peek, { tableId, recordId: openRecord, onClose: () => setRail("none") }),
14181
+ /* @__PURE__ */ jsx57(Separator12, {}),
14182
+ /* @__PURE__ */ jsx57(
13835
14183
  HistoryPanel,
13836
14184
  {
13837
14185
  tableId,
@@ -13842,10 +14190,10 @@ function TablePage({
13842
14190
  }
13843
14191
  }
13844
14192
  ),
13845
- /* @__PURE__ */ jsx56(Separator11, {}),
13846
- /* @__PURE__ */ jsx56(ChecklistRunner, { tableId, recordId: openRecord }),
13847
- /* @__PURE__ */ jsx56(Separator11, {}),
13848
- /* @__PURE__ */ jsx56(CommentThread, { tableId, recordId: openRecord })
14193
+ /* @__PURE__ */ jsx57(Separator12, {}),
14194
+ /* @__PURE__ */ jsx57(ChecklistRunner, { tableId, recordId: openRecord }),
14195
+ /* @__PURE__ */ jsx57(Separator12, {}),
14196
+ /* @__PURE__ */ jsx57(CommentThread, { tableId, recordId: openRecord })
13849
14197
  ] }) : null
13850
14198
  ] })
13851
14199
  ] });
@@ -13876,6 +14224,7 @@ export {
13876
14224
  DEFAULT_FIELDS,
13877
14225
  DEFAULT_VIEW_NAME,
13878
14226
  DashboardCanvas,
14227
+ DigestScheduler,
13879
14228
  DocRender,
13880
14229
  DocTemplate,
13881
14230
  EMPTY_PRESENTATION,