@ai-matrx/records-ui 0.113.38 → 0.114.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
@@ -41,7 +41,9 @@ var NEEDS = {
41
41
  // custom.share_grant — passing your access on is an administrator's right
42
42
  share: "admin",
43
43
  // custom.field_declare / field_update / field_retire / table settings
44
- structure: "admin"
44
+ structure: "admin",
45
+ // custom.field_declare / field_update / field_retire — the same rung; a lock takes it away, nothing else does
46
+ schema: "admin"
45
47
  };
46
48
  function levelNeededFor(what) {
47
49
  return NEEDS[what];
@@ -59,7 +61,8 @@ function sentenceFor(what, level) {
59
61
  organise: "organise this",
60
62
  remove: "delete this",
61
63
  share: "pass this on to somebody else",
62
- structure: "change the columns of this table"
64
+ structure: "change the columns of this table",
65
+ schema: "change the columns of this table"
63
66
  };
64
67
  return `You are ${have} here, and it takes ${need} to ${doing[what]}. Ask somebody who is Admin on it to move you up.`;
65
68
  }
@@ -75,6 +78,7 @@ function whatYouMayDo(level, known = true) {
75
78
  remove: may("remove"),
76
79
  share: may("share"),
77
80
  structure: may("structure"),
81
+ schema: may("schema"),
78
82
  why: (what) => known ? sentenceFor(what, level) : NOT_YET
79
83
  };
80
84
  }
@@ -86,15 +90,16 @@ function whatYouMayDoWithTable(table, level, known) {
86
90
  return {
87
91
  ...base2,
88
92
  structure: false,
89
- why: (what) => what === "structure" ? KERNEL_REASON : base2.why(what)
93
+ schema: false,
94
+ why: (what) => what === "structure" || what === "schema" ? KERNEL_REASON : base2.why(what)
90
95
  };
91
96
  }
92
97
  function tableIsLocked(table) {
93
98
  return table?.metadata?.["locked"] === true;
94
99
  }
95
- var DATABASE_LOCKED_REASON = "This database is locked, so its columns and settings are read-only. Unlock it to change them.";
100
+ var DATABASE_LOCKED_REASON = "This database is locked, so its columns cannot be added, renamed, changed or deleted. Unlock it to change them.";
96
101
  function lockedStructure(base2) {
97
- return { ...base2, structure: false, why: (what) => what === "structure" ? DATABASE_LOCKED_REASON : base2.why(what) };
102
+ return { ...base2, schema: false, why: (what) => what === "schema" ? DATABASE_LOCKED_REASON : base2.why(what) };
98
103
  }
99
104
 
100
105
  // src/host.tsx
@@ -2880,6 +2885,7 @@ function TableDetails({
2880
2885
  tableShapeChanged2(tableId);
2881
2886
  onSaved?.();
2882
2887
  };
2888
+ const locked = table.metadata?.["locked"] === true;
2883
2889
  return /* @__PURE__ */ jsxs8("div", { className: "flex flex-col gap-2", "data-table-details": "", children: [
2884
2890
  /* @__PURE__ */ jsx10("span", { className: "font-medium", children: "About this table" }),
2885
2891
  /* @__PURE__ */ jsxs8("div", { className: "flex flex-col gap-1", children: [
@@ -2933,9 +2939,9 @@ function TableDetails({
2933
2939
  size: "sm",
2934
2940
  variant: "outline",
2935
2941
  disabled: busy,
2936
- onClick: () => void write(lockPatch(table, true), { metadata: table.metadata ?? {} }),
2942
+ onClick: () => void write(lockPatch(table, !locked), { metadata: table.metadata ?? {} }),
2937
2943
  "data-table-lock-on": "",
2938
- children: "Lock"
2944
+ children: locked ? "Unlock" : "Lock"
2939
2945
  }
2940
2946
  )
2941
2947
  ] }),
@@ -6828,10 +6834,8 @@ var RELATIVE_DAY_TERMS = [
6828
6834
  { kind: "past_days", label: "is in the past \u2026 days", takesDays: true }
6829
6835
  ];
6830
6836
  function relativeDayNode(fieldId, kind, days) {
6831
- const zone2 = getPersonTimeZone();
6832
6837
  const window2 = { relative: kind };
6833
6838
  if (RELATIVE_DAY_TERMS.find((t) => t.kind === kind)?.takesDays) window2["n"] = days ?? 7;
6834
- if (zone2) window2["tz"] = zone2;
6835
6839
  return { op: "within", args: [{ field: fieldId }, { const: window2 }] };
6836
6840
  }
6837
6841
  function relativeDayOf(expr) {
@@ -8501,8 +8505,8 @@ function FieldEditor(props) {
8501
8505
  const entityMutation = useEntityFieldMutation();
8502
8506
  const mutation = onEntity ? { saving: entityMutation.saving, error: entityMutation.error } : { saving: tableMutation.saving, error: tableMutation.error };
8503
8507
  const client = useRecordsClient11();
8504
- const mayShape = onEntity ? entityRights.data?.may_declare === true : tableRights2.structure;
8505
- const whyNot = onEntity ? entityRights.data?.reason ?? null : tableRights2.why("structure");
8508
+ const mayShape = onEntity ? entityRights.data?.may_declare === true : tableRights2.schema;
8509
+ const whyNot = onEntity ? entityRights.data?.reason ?? null : tableRights2.why("schema");
8506
8510
  const storeLabel = onEntity ? entityRights.data?.label ?? null : table.data?.name ?? null;
8507
8511
  const tableLabel = props.tableLabel ?? storeLabel;
8508
8512
  const offered = onEntity ? STANDARD_TABLE_FIELD_CHOICES : FIELD_TYPE_CHOICES;
@@ -15527,7 +15531,7 @@ function useMergedGridControls(args) {
15527
15531
  },
15528
15532
  [enabled, fieldOf2, choiceOptions, rows, choiceCounts]
15529
15533
  );
15530
- const copyText14 = useCallback17(
15534
+ const copyText16 = useCallback17(
15531
15535
  (text, what) => {
15532
15536
  void writeClipboardText(text).then(
15533
15537
  (landed) => landed ? notify?.success(`${what} ${what.endsWith("cells") ? "are" : "is"} copied.`) : notify?.error("The clipboard refused the copy. Select the cells and press Cmd-C.")
@@ -15536,7 +15540,7 @@ function useMergedGridControls(args) {
15536
15540
  [notify]
15537
15541
  );
15538
15542
  const ctx = useMemo21(() => {
15539
- const mayStructure = rights.structure && editable;
15543
+ const mayStructure = rights.schema && editable;
15540
15544
  const summaries = args.summaries;
15541
15545
  const firstKey = fieldKeys[0];
15542
15546
  return {
@@ -15565,7 +15569,7 @@ function useMergedGridControls(args) {
15565
15569
  id: "col-duplicate",
15566
15570
  label: "Duplicate property",
15567
15571
  icon: "copy-plus",
15568
- disabledReason: mayStructure ? void 0 : editable ? rights.why("structure") : "Editing is off here.",
15572
+ disabledReason: mayStructure ? void 0 : editable ? rights.why("schema") : "Editing is off here.",
15569
15573
  // DUPLICATE PROPERTY (Notion N5): one door declares the copy right after this column and copies
15570
15574
  // every record's value into it (`custom.field_duplicate`); nothing to fill in, as in Notion.
15571
15575
  onSelect: () => {
@@ -15602,14 +15606,14 @@ function useMergedGridControls(args) {
15602
15606
  id: "table-row-label",
15603
15607
  label: "What names each record\u2026",
15604
15608
  icon: "tag",
15605
- disabledReason: mayStructure ? void 0 : editable ? rights.why("structure") : "Editing is off here.",
15609
+ disabledReason: mayStructure ? void 0 : editable ? rights.why("schema") : "Editing is off here.",
15606
15610
  onSelect: () => setLabelling(true)
15607
15611
  },
15608
15612
  {
15609
15613
  id: "table-row-actions",
15610
15614
  label: ROW_ACTIONS_MENU_LABEL,
15611
15615
  icon: "zap",
15612
- disabledReason: mayStructure ? void 0 : editable ? rights.why("structure") : "Editing is off here.",
15616
+ disabledReason: mayStructure ? void 0 : editable ? rights.why("schema") : "Editing is off here.",
15613
15617
  onSelect: () => setRowActionsOpen(true)
15614
15618
  },
15615
15619
  // CLEAN THE WHOLE TABLE AT ONCE (the Sheet's CellCleanupButton): every text column, one review.
@@ -15639,7 +15643,7 @@ function useMergedGridControls(args) {
15639
15643
  mayAddRecord: editable && rights.write,
15640
15644
  whyNotAdd: editable ? rights.why("write") : "Editing is off here.",
15641
15645
  mayStructure,
15642
- whyNotStructure: editable ? rights.why("structure") : "Editing is off here.",
15646
+ whyNotStructure: editable ? rights.why("schema") : "Editing is off here.",
15643
15647
  cleanable: (field2) => isTextColumn(field2),
15644
15648
  typeable: (columnId2) => {
15645
15649
  const field2 = fields.find((f) => f.key === columnId2);
@@ -15647,7 +15651,7 @@ function useMergedGridControls(args) {
15647
15651
  },
15648
15652
  sortedBy: query.sort ? { id: query.sort.id, direction: query.sort.direction } : null,
15649
15653
  actions: {
15650
- copyText: copyText14,
15654
+ copyText: copyText16,
15651
15655
  pasteInto: (rowId, columnId2) => {
15652
15656
  const field2 = fields.find((f) => f.key === columnId2);
15653
15657
  if (!field2) return;
@@ -15716,10 +15720,10 @@ function useMergedGridControls(args) {
15716
15720
  ...hrefForRecord && table ? {
15717
15721
  copyRecordLink: (rowId) => {
15718
15722
  const href = hrefForRecord({ table, recordId: rowId });
15719
- if (href) copyText14(new URL(href, window.location.origin).toString(), "The link");
15723
+ if (href) copyText16(new URL(href, window.location.origin).toString(), "The link");
15720
15724
  }
15721
15725
  } : {},
15722
- ...editable && rights.structure ? {
15726
+ ...editable && rights.schema ? {
15723
15727
  saveAsTemplate: (rowId) => {
15724
15728
  const row = rows.find((r) => r.id === rowId);
15725
15729
  if (row) setDialog({ kind: "template", row });
@@ -15816,7 +15820,7 @@ function useMergedGridControls(args) {
15816
15820
  fieldKeys,
15817
15821
  canWrite,
15818
15822
  client,
15819
- copyText14,
15823
+ copyText16,
15820
15824
  editable,
15821
15825
  editing,
15822
15826
  fields,
@@ -24407,7 +24411,7 @@ function GridBody({
24407
24411
  const offersRowActions = !embedded && (canWrite || Boolean(host.talkToRecord) || Boolean(!readOnly && host.runAgentAction && tableActions2.agent.length > 0));
24408
24412
  const hasTalk = Boolean(host.talkToRecord);
24409
24413
  const hasAgent = Boolean(host.runAgentAction);
24410
- const mayManageActions = !readOnly && rights.structure;
24414
+ const mayManageActions = !readOnly && rights.schema;
24411
24415
  const actionsHeader = useMemo35(
24412
24416
  () => /* @__PURE__ */ jsx77(ActionsHeaderControls, { ...mayManageActions ? { onAddColumn: () => addColumnNow.current() } : {} }),
24413
24417
  [mayManageActions]
@@ -24490,9 +24494,9 @@ function GridBody({
24490
24494
  (key) => holdByContent(headerHeld.current, `filter:${key}`, filterFor(key))
24491
24495
  );
24492
24496
  const pinnedPlus = offersRowActions && mayManageActions;
24493
- return merged && rights.structure && !readOnly && !pinnedPlus ? [...drawn, addColumnDef] : drawn;
24497
+ return merged && rights.schema && !readOnly && !pinnedPlus ? [...drawn, addColumnDef] : drawn;
24494
24498
  },
24495
- [bodyColumns, controls.headerMenuFor, controls.filterFor, merged, rights.structure, readOnly, addColumnDef, offersRowActions, mayManageActions]
24499
+ [bodyColumns, controls.headerMenuFor, controls.filterFor, merged, rights.schema, readOnly, addColumnDef, offersRowActions, mayManageActions]
24496
24500
  );
24497
24501
  const columnsHeld = useRef36(null);
24498
24502
  const columns = columnsHeld.current !== null && sameColumns(columnsHeld.current, drawnColumns) ? columnsHeld.current : drawnColumns;
@@ -24790,7 +24794,7 @@ function GridBody({
24790
24794
  }
24791
24795
  ) : null,
24792
24796
  viewId && viewOrder?.onPlaced && hasSeveralRows ? /* @__PURE__ */ jsx77(Button44, { size: "sm", variant: "ghost", onClick: () => setReordering(true), "data-records-reorder-open": "", children: "Reorder" }) : null,
24793
- !readOnly && rights.structure && onAddField ? /* @__PURE__ */ jsx77(AddFieldButton, { onClick: onAddField }) : null,
24797
+ !readOnly && rights.schema && onAddField ? /* @__PURE__ */ jsx77(AddFieldButton, { onClick: onAddField }) : null,
24794
24798
  embedded ? null : /* @__PURE__ */ jsx77(
24795
24799
  GridLayoutMenu,
24796
24800
  {
@@ -25107,7 +25111,7 @@ function GridBody({
25107
25111
  className: "flex flex-col items-start gap-2 rounded-md border border-dashed p-6",
25108
25112
  children: [
25109
25113
  /* @__PURE__ */ jsx77("p", { className: "text-sm font-medium", children: NO_COLUMNS_YET }),
25110
- rights.structure && onAddField ? /* @__PURE__ */ jsx77(Button44, { size: "sm", onClick: onAddField, children: "Add your first column" }) : /* @__PURE__ */ jsx77("p", { className: "max-w-prose text-xs text-muted-foreground", children: rights.why("structure") })
25114
+ rights.schema && onAddField ? /* @__PURE__ */ jsx77(Button44, { size: "sm", onClick: onAddField, children: "Add your first column" }) : /* @__PURE__ */ jsx77("p", { className: "max-w-prose text-xs text-muted-foreground", children: rights.why("schema") })
25111
25115
  ]
25112
25116
  }
25113
25117
  ) : /* @__PURE__ */ jsxs70(Fragment26, { children: [
@@ -25696,10 +25700,11 @@ function ConnectedRecordsTableCard({
25696
25700
  }
25697
25701
 
25698
25702
  // src/ExportMenu.tsx
25703
+ import { copyText as copyText2 } from "@ai-matrx/kit/clipboard";
25699
25704
  import { downloadFile as downloadFile2 } from "@ai-matrx/kit/download";
25700
25705
  import { useEffect as useEffect54, useState as useState65 } from "react";
25701
25706
  import { useFields as useFields10, useRecords as useRecords6, useRecordsClient as useRecordsClient41, useTable as useTable8 } from "@ai-matrx/records/react";
25702
- import { exportCsv, exportXlsx, wholeValueOf as wholeValueOf5, withWholeValues } from "@ai-matrx/records/core";
25707
+ import { exportCsv, exportMarkdown, exportXlsx, wholeValueOf as wholeValueOf5, withWholeValues } from "@ai-matrx/records/core";
25703
25708
 
25704
25709
  // src/viewExport.ts
25705
25710
  import { formatCount as formatCount4 } from "@ai-matrx/kit/format";
@@ -25877,6 +25882,31 @@ function ExportMenu({ tableId, rows, label, className, view }) {
25877
25882
  children: "XLSX"
25878
25883
  }
25879
25884
  ),
25885
+ /* @__PURE__ */ jsx80(
25886
+ Button46,
25887
+ {
25888
+ size: "sm",
25889
+ variant: "outline",
25890
+ onClick: () => void exported().then((rows2) => download(`${name}.md`, "text/markdown;charset=utf-8", exportMarkdown(columns, rows2))).catch(
25891
+ (thrown) => setFailure((0, refusals_exports.refusalFromThrown)(thrown, "The Markdown could not be written.", "The CSV beside this button carries the same rows."))
25892
+ ),
25893
+ children: "Markdown"
25894
+ }
25895
+ ),
25896
+ /* @__PURE__ */ jsx80(
25897
+ Button46,
25898
+ {
25899
+ size: "sm",
25900
+ variant: "outline",
25901
+ onClick: () => void exported().then(async (rows2) => {
25902
+ if (await copyText2(exportMarkdown(columns, rows2))) host.notify?.success("Markdown copied");
25903
+ else host.notify?.error("Couldn\u2019t copy the Markdown");
25904
+ }).catch(
25905
+ (thrown) => setFailure((0, refusals_exports.refusalFromThrown)(thrown, "The Markdown could not be written.", "The CSV beside this button carries the same rows."))
25906
+ ),
25907
+ children: "Copy Markdown"
25908
+ }
25909
+ ),
25880
25910
  /* @__PURE__ */ jsx80(
25881
25911
  AlchemyMenu,
25882
25912
  {
@@ -26197,14 +26227,14 @@ function fieldsForNewColumns(columns) {
26197
26227
  }
26198
26228
 
26199
26229
  // src/BookingSlots.tsx
26200
- import { copyText as copyText3 } from "@ai-matrx/kit/clipboard";
26230
+ import { copyText as copyText4 } from "@ai-matrx/kit/clipboard";
26201
26231
  import { useCallback as useCallback31, useEffect as useEffect58, useMemo as useMemo39, useState as useState69 } from "react";
26202
26232
  import { useRecordsClient as useRecordsClient45, useMyLevels as useMyLevels2 } from "@ai-matrx/records/react";
26203
26233
  import { bookingPath as bookingPath2 } from "@ai-matrx/records";
26204
26234
  import { Badge as Badge11, Button as Button51, Skeleton as Skeleton7, cn as cn44 } from "@ai-matrx/design-system";
26205
26235
 
26206
26236
  // src/BookingBuilder.tsx
26207
- import { copyText as copyText2 } from "@ai-matrx/kit/clipboard";
26237
+ import { copyText as copyText3 } from "@ai-matrx/kit/clipboard";
26208
26238
  import { ErrorBox as ErrorBox3 } from "@ai-matrx/design-system";
26209
26239
  import { useCallback as useCallback29, useEffect as useEffect56, useMemo as useMemo38, useRef as useRef37, useState as useState67 } from "react";
26210
26240
  import { useFields as useFields11, useRecordsClient as useRecordsClient43, useTable as useTable9 } from "@ai-matrx/records/react";
@@ -26683,7 +26713,7 @@ function BookingBuilder({ tableId, bookingId, onSaved, onClose, startNew, classN
26683
26713
  size: "sm",
26684
26714
  variant: "outline",
26685
26715
  onClick: () => {
26686
- void copyText2(url).then((landed) => {
26716
+ void copyText3(url).then((landed) => {
26687
26717
  setCopied(landed);
26688
26718
  if (landed) window.setTimeout(() => setCopied(false), 1600);
26689
26719
  });
@@ -26813,7 +26843,7 @@ var ACTION_NO_ACCESS_REASON = "You don't have access";
26813
26843
  var ACTION_PLATFORM_TABLE_REASON = "Platform tables can't be changed here";
26814
26844
  function rightsReason(rights, what, table) {
26815
26845
  if (!rights.known) return ACTION_CHECKING_ACCESS_REASON;
26816
- if (what === "structure" && table?.is_kernel) return ACTION_PLATFORM_TABLE_REASON;
26846
+ if ((what === "structure" || what === "schema") && table?.is_kernel) return ACTION_PLATFORM_TABLE_REASON;
26817
26847
  if (rights[what]) return null;
26818
26848
  if (rights.level === null) return ACTION_NO_ACCESS_REASON;
26819
26849
  return `Needs ${LEVEL_WORD[levelNeededFor(what)]} access`;
@@ -26875,7 +26905,7 @@ function tableActions(input) {
26875
26905
  const url = tableLink(table.id, host.origin);
26876
26906
  const favorite = host.isFavorite === true;
26877
26907
  const openInNewTab = host.openInNewTab;
26878
- const copyText14 = host.copyText;
26908
+ const copyText16 = host.copyText;
26879
26909
  const openBuiltOn = host.openBuiltOn;
26880
26910
  const built = [
26881
26911
  action({ id: "open", label: "Open", icon: "folder-open", group: "open", verb: "open" }, "read", input, bind(host.open, table)),
@@ -26889,7 +26919,7 @@ function tableActions(input) {
26889
26919
  { id: "copy-link", label: "Copy link", icon: "link", group: "open", verb: "copy-link" },
26890
26920
  "read",
26891
26921
  input,
26892
- copyText14 ? () => copyText14(url, table) : void 0
26922
+ copyText16 ? () => copyText16(url, table) : void 0
26893
26923
  ),
26894
26924
  action({ id: "rename", label: "Rename\u2026", icon: "pencil", group: "organize", verb: "rename" }, "structure", input, bind(host.rename, table)),
26895
26925
  action({ id: "duplicate", label: "Duplicate", icon: "copy", group: "organize", verb: "duplicate" }, "read", input, bind(host.duplicate, table)),
@@ -26909,7 +26939,7 @@ function tableActions(input) {
26909
26939
  action({ id: "share", label: "Share\u2026", icon: "share-2", group: "share", verb: "share" }, "share", input, bind(host.share, table)),
26910
26940
  action({ id: "export", label: "Export\u2026", icon: "download", group: "share", verb: "export" }, "read", input, bind(host.export, table)),
26911
26941
  action({ id: "import", label: "Import\u2026", icon: "upload", group: "share", verb: "import" }, "structure", input, bind(host.import, table)),
26912
- action({ id: "add-column", label: ADD_FIELD_OPEN_LABEL, icon: "columns-3", group: "manage", rank: 1 }, "structure", input, bind(host.addColumn, table)),
26942
+ action({ id: "add-column", label: ADD_FIELD_OPEN_LABEL, icon: "columns-3", group: "manage", rank: 1 }, "schema", input, bind(host.addColumn, table)),
26913
26943
  action({ id: "settings", label: "Settings", icon: "settings", group: "manage", verb: "settings" }, "structure", input, bind(host.settings, table)),
26914
26944
  action({ id: "history", label: "History", icon: "history", group: "manage", verb: "history" }, "read", input, bind(host.history, table)),
26915
26945
  // custom.view_designate asks editor on the table — `canDesignate` in TablePage.
@@ -27299,7 +27329,7 @@ function BookingSlots({ tableId, activeBookingId, className }) {
27299
27329
  [client, load]
27300
27330
  );
27301
27331
  const copy = useCallback31(async (url, formId) => {
27302
- if (await copyText3(url)) {
27332
+ if (await copyText4(url)) {
27303
27333
  setCopied(formId);
27304
27334
  setShown(null);
27305
27335
  window.setTimeout(() => setCopied(null), 1600);
@@ -29612,26 +29642,6 @@ function TableSettingsPanel({
29612
29642
  if (table.error) return /* @__PURE__ */ jsx99(RefusalNotice, { error: table.error, className });
29613
29643
  if (fields.error) return /* @__PURE__ */ jsx99(RefusalNotice, { error: fields.error, className });
29614
29644
  if (!rights.structure) {
29615
- if (tableIsLocked(table.data)) {
29616
- return /* @__PURE__ */ jsxs90("div", { className: cn50("flex flex-col items-start gap-2 text-xs text-muted-foreground", className), "data-table-locked": "", children: [
29617
- /* @__PURE__ */ jsx99("p", { children: DATABASE_LOCKED_REASON }),
29618
- /* @__PURE__ */ jsx99(
29619
- Button61,
29620
- {
29621
- size: "sm",
29622
- variant: "outline",
29623
- onClick: () => {
29624
- void saveTableLock(client, tableId, false).then((done) => {
29625
- if (done.ok) table.reload();
29626
- else ui.notify?.error(done.says);
29627
- });
29628
- },
29629
- "data-table-unlock": "",
29630
- children: "Unlock database"
29631
- }
29632
- )
29633
- ] });
29634
- }
29635
29645
  return /* @__PURE__ */ jsx99("p", { className: cn50("text-xs text-muted-foreground", className), children: rights.why("structure") });
29636
29646
  }
29637
29647
  const rows = [...fields.data ?? []].sort((a, b) => (a.sort ?? 0) - (b.sort ?? 0));
@@ -29731,8 +29741,26 @@ function TableSettingsPanel({
29731
29741
  /* @__PURE__ */ jsxs90("div", { className: "flex items-center gap-2", children: [
29732
29742
  /* @__PURE__ */ jsx99("span", { className: "font-medium", children: "Fields" }),
29733
29743
  /* @__PURE__ */ jsx99("span", { className: "text-muted-foreground", children: rows.length }),
29734
- /* @__PURE__ */ jsx99(Button61, { size: "sm", variant: "outline", className: "ml-auto", onClick: () => setEditing("new"), children: "Add a field" })
29744
+ /* @__PURE__ */ jsx99(Button61, { size: "sm", variant: "outline", className: "ml-auto", disabled: !rights.schema, onClick: () => setEditing("new"), children: "Add a field" })
29735
29745
  ] }),
29746
+ tableIsLocked(table.data) ? /* @__PURE__ */ jsxs90("div", { className: "flex flex-wrap items-center gap-2 text-muted-foreground", "data-table-locked": "", children: [
29747
+ /* @__PURE__ */ jsx99("span", { children: DATABASE_LOCKED_REASON }),
29748
+ /* @__PURE__ */ jsx99(
29749
+ Button61,
29750
+ {
29751
+ size: "sm",
29752
+ variant: "outline",
29753
+ onClick: () => {
29754
+ void saveTableLock(client, tableId, false).then((done) => {
29755
+ if (done.ok) table.reload();
29756
+ else ui.notify?.error(done.says);
29757
+ });
29758
+ },
29759
+ "data-table-unlock": "",
29760
+ children: "Unlock database"
29761
+ }
29762
+ )
29763
+ ] }) : null,
29736
29764
  shape.error ? /* @__PURE__ */ jsx99(RefusalNotice, { error: shape.error }) : null,
29737
29765
  askingToRemove ? /* @__PURE__ */ jsxs90("div", { className: "flex flex-wrap items-center gap-2 rounded border border-destructive/40 px-2 py-1.5", children: [
29738
29766
  /* @__PURE__ */ jsxs90("span", { className: "text-destructive", children: [
@@ -29751,7 +29779,7 @@ function TableSettingsPanel({
29751
29779
  ),
29752
29780
  /* @__PURE__ */ jsx99(Button61, { size: "sm", variant: "ghost", onClick: () => setAskingToRemove(null), children: "Keep it" })
29753
29781
  ] }) : null,
29754
- /* @__PURE__ */ jsx99(
29782
+ /* @__PURE__ */ jsx99("fieldset", { disabled: !rights.schema, className: "min-w-0 border-0 p-0", "data-columns-locked": rights.schema ? void 0 : "", children: /* @__PURE__ */ jsx99(
29755
29783
  ColumnsAtOnce,
29756
29784
  {
29757
29785
  rows,
@@ -29764,7 +29792,7 @@ function TableSettingsPanel({
29764
29792
  table.reload();
29765
29793
  }
29766
29794
  }
29767
- ),
29795
+ ) }),
29768
29796
  /* @__PURE__ */ jsx99(Separator8, {}),
29769
29797
  /* @__PURE__ */ jsx99(StageRulesSection, { tableId }),
29770
29798
  /* @__PURE__ */ jsx99(RowActionsSection, { tableId }),
@@ -30036,7 +30064,7 @@ function RecordFieldsInPlace({
30036
30064
  }
30037
30065
 
30038
30066
  // src/ShareControl.tsx
30039
- import { copyText as copyText4 } from "@ai-matrx/kit/clipboard";
30067
+ import { copyText as copyText5 } from "@ai-matrx/kit/clipboard";
30040
30068
  import { useEffect as useEffect69, useState as useState82 } from "react";
30041
30069
  import { Button as Button62, Popover as Popover11, PopoverContent as PopoverContent11, PopoverTrigger as PopoverTrigger11 } from "@ai-matrx/design-system";
30042
30070
  import { Fragment as Fragment37, jsx as jsx102, jsxs as jsxs93 } from "react/jsx-runtime";
@@ -30099,7 +30127,7 @@ function ShareForSomebodyElse({
30099
30127
  }) {
30100
30128
  const [copied, setCopied] = useState82(null);
30101
30129
  const copy = async () => {
30102
- if (await copyText4(window.location.href)) {
30130
+ if (await copyText5(window.location.href)) {
30103
30131
  setCopied("yes");
30104
30132
  } else {
30105
30133
  setCopied("no");
@@ -30820,7 +30848,7 @@ function CardDates({ record, fields }) {
30820
30848
  }
30821
30849
 
30822
30850
  // src/RecordActionMenu.tsx
30823
- import { copyText as copyText5 } from "@ai-matrx/kit/clipboard";
30851
+ import { copyText as copyText6 } from "@ai-matrx/kit/clipboard";
30824
30852
  import { useCallback as useCallback38, useMemo as useMemo49, useState as useState87 } from "react";
30825
30853
  import { useFields as useFields16, useRecordsClient as useRecordsClient62, useTable as useTable16 } from "@ai-matrx/records/react";
30826
30854
  import {
@@ -30861,7 +30889,7 @@ function recordActions(input) {
30861
30889
  const origin = (host.origin ?? DEFAULT_ACTION_ORIGIN).replace(/\/+$/, "");
30862
30890
  const url = host.href ? /^https?:/.test(host.href) ? host.href : `${origin}${host.href}` : recordLink(record.tableId, record.id, host.origin);
30863
30891
  const openInNewTab = host.openInNewTab;
30864
- const copyText14 = host.copyText;
30892
+ const copyText16 = host.copyText;
30865
30893
  const built = [
30866
30894
  action2({ id: "open", label: "Open", icon: "folder-open", group: "open", verb: "open" }, "read", input, bind2(host.open, record)),
30867
30895
  action2(
@@ -30874,7 +30902,7 @@ function recordActions(input) {
30874
30902
  { id: "copy-link", label: "Copy link", icon: "link", group: "open", verb: "copy-link" },
30875
30903
  "read",
30876
30904
  input,
30877
- copyText14 ? () => copyText14(url, record) : void 0
30905
+ copyText16 ? () => copyText16(url, record) : void 0
30878
30906
  ),
30879
30907
  action2({ id: "duplicate", label: "Duplicate", icon: "copy", group: "organize", verb: "duplicate" }, "organise", input, bind2(host.duplicate, record)),
30880
30908
  action2(
@@ -30927,7 +30955,7 @@ function useRecordActionHost({ tableId, row, onOpenRecord, onChanged, onMerge })
30927
30955
  window.open(url, "_blank", "noopener,noreferrer");
30928
30956
  },
30929
30957
  copyText: async (url) => {
30930
- if (await copyText5(url)) {
30958
+ if (await copyText6(url)) {
30931
30959
  said2("Link copied");
30932
30960
  } else {
30933
30961
  notify?.error("Couldn\u2019t copy the link");
@@ -40079,7 +40107,7 @@ function clientNoun(table) {
40079
40107
  }
40080
40108
 
40081
40109
  // src/PortalsPanel.tsx
40082
- import { copyText as copyText6 } from "@ai-matrx/kit/clipboard";
40110
+ import { copyText as copyText7 } from "@ai-matrx/kit/clipboard";
40083
40111
  import { useCallback as useCallback50, useEffect as useEffect96, useMemo as useMemo65, useState as useState110 } from "react";
40084
40112
  import { useRecordsClient as useRecordsClient78 } from "@ai-matrx/records/react";
40085
40113
  import {
@@ -40685,7 +40713,7 @@ function CopyLink({ url, what }) {
40685
40713
  variant: "outline",
40686
40714
  onClick: () => {
40687
40715
  void (async () => {
40688
- if (await copyText6(url)) {
40716
+ if (await copyText7(url)) {
40689
40717
  setCopied(true);
40690
40718
  setShown(false);
40691
40719
  window.setTimeout(() => setCopied(false), 1600);
@@ -41988,7 +42016,7 @@ function SubscriptionsPanel({ tableId, activeRuleId, className }) {
41988
42016
  }
41989
42017
 
41990
42018
  // src/FormBuilder.tsx
41991
- import { copyText as copyText7 } from "@ai-matrx/kit/clipboard";
42019
+ import { copyText as copyText8 } from "@ai-matrx/kit/clipboard";
41992
42020
  import { ErrorBox as ErrorBox7 } from "@ai-matrx/design-system";
41993
42021
  import { useCallback as useCallback55, useEffect as useEffect101, useMemo as useMemo68, useRef as useRef56, useState as useState116 } from "react";
41994
42022
  import { DndContext, KeyboardSensor, PointerSensor, closestCenter, useSensor, useSensors } from "@dnd-kit/core";
@@ -42236,7 +42264,7 @@ function ThemeEditor({
42236
42264
  }
42237
42265
  var EMBED_LABELS = { inline: "Inline", popup: "Popup", slider: "Slider" };
42238
42266
  function EmbedSnippets({ url, label }) {
42239
- const { copyText: copyText14 } = useClipboard();
42267
+ const { copyText: copyText16 } = useClipboard();
42240
42268
  const [kind, setKind] = useState114("inline");
42241
42269
  const [copied, setCopied] = useState114(false);
42242
42270
  const snippet = formEmbedSnippet(kind, url, { label });
@@ -42250,7 +42278,7 @@ function EmbedSnippets({ url, label }) {
42250
42278
  size: "sm",
42251
42279
  variant: "outline",
42252
42280
  className: "ml-auto",
42253
- onClick: () => void copyText14(snippet).then((ok) => {
42281
+ onClick: () => void copyText16(snippet).then((ok) => {
42254
42282
  setCopied(Boolean(ok));
42255
42283
  window.setTimeout(() => setCopied(false), 1500);
42256
42284
  }),
@@ -43176,7 +43204,7 @@ function FormBuilder({ tableId, seed, activeFormId, onActiveForm, startWithOne,
43176
43204
  await load();
43177
43205
  }
43178
43206
  async function copyLink(url) {
43179
- if (await copyText7(url)) {
43207
+ if (await copyText8(url)) {
43180
43208
  setCopied(true);
43181
43209
  setShownUrl(null);
43182
43210
  window.setTimeout(() => setCopied(false), 1600);
@@ -45986,7 +46014,7 @@ function TargetControl({
45986
46014
  }
45987
46015
 
45988
46016
  // src/QuestionPanel.tsx
45989
- import { copyText as copyText8 } from "@ai-matrx/kit/clipboard";
46017
+ import { copyText as copyText9 } from "@ai-matrx/kit/clipboard";
45990
46018
  import { useEffect as useEffect108, useMemo as useMemo72, useRef as useRef59, useState as useState124 } from "react";
45991
46019
  import { Badge as Badge24, BasicInput as BasicInput33, Button as Button97, Checkbox as Checkbox16, Label as Label26, Skeleton as Skeleton34, cn as cn88 } from "@ai-matrx/design-system";
45992
46020
  import { useFields as useFields31, useRecordsClient as useRecordsClient89 } from "@ai-matrx/records/react";
@@ -46158,7 +46186,7 @@ function QuestionPanel({ tableId, activeQuestionId, draft, className }) {
46158
46186
  const url = new URL(window.location.href);
46159
46187
  url.searchParams.set("view", "questions");
46160
46188
  url.searchParams.set("item", current.id);
46161
- void copyText8(url.toString());
46189
+ void copyText9(url.toString());
46162
46190
  };
46163
46191
  if (fields.loading || hidden === null) {
46164
46192
  return /* @__PURE__ */ jsxs139("div", { className: cn88("flex flex-col gap-2 p-3", className), "data-testid": "question-panel", children: [
@@ -47637,7 +47665,7 @@ function onePerKey3(all) {
47637
47665
  }
47638
47666
 
47639
47667
  // src/FormsPanel.tsx
47640
- import { copyText as copyText9 } from "@ai-matrx/kit/clipboard";
47668
+ import { copyText as copyText10 } from "@ai-matrx/kit/clipboard";
47641
47669
  import { useCallback as useCallback62, useEffect as useEffect110, useState as useState127 } from "react";
47642
47670
  import { useRecordsClient as useRecordsClient92, useTable as useTable34 } from "@ai-matrx/records/react";
47643
47671
  import { publicFormPath as publicFormPath2 } from "@ai-matrx/records";
@@ -47696,7 +47724,7 @@ function FormsPanel({ tableId, activeFormId, className }) {
47696
47724
  );
47697
47725
  const [shown4, setShown] = useState127(null);
47698
47726
  const copy = useCallback62(async (url, formId) => {
47699
- if (await copyText9(url)) {
47727
+ if (await copyText10(url)) {
47700
47728
  setCopied(formId);
47701
47729
  setShown(null);
47702
47730
  window.setTimeout(() => setCopied(null), 1600);
@@ -48659,7 +48687,7 @@ function PublicRow({ row, fields }) {
48659
48687
  }
48660
48688
 
48661
48689
  // src/EmbedFrame.tsx
48662
- import { copyText as copyText10 } from "@ai-matrx/kit/clipboard";
48690
+ import { copyText as copyText11 } from "@ai-matrx/kit/clipboard";
48663
48691
  import { useCallback as useCallback67, useEffect as useEffect115, useState as useState132 } from "react";
48664
48692
  import { useRecordsClient as useRecordsClient97 } from "@ai-matrx/records/react";
48665
48693
  import { Button as Button104, Input as Input9, Skeleton as Skeleton42, Textarea as Textarea9, cn as cn95 } from "@ai-matrx/design-system";
@@ -48757,7 +48785,7 @@ function EmbedFrame({
48757
48785
  {
48758
48786
  size: "sm",
48759
48787
  variant: "outline",
48760
- onClick: () => void copyText10(snippet ?? ""),
48788
+ onClick: () => void copyText11(snippet ?? ""),
48761
48789
  children: "Copy"
48762
48790
  }
48763
48791
  ),
@@ -48795,7 +48823,7 @@ function useEmbedHandshake(args) {
48795
48823
  }
48796
48824
 
48797
48825
  // src/TablePage.tsx
48798
- import { copyText as copyText13 } from "@ai-matrx/kit/clipboard";
48826
+ import { copyText as copyText15 } from "@ai-matrx/kit/clipboard";
48799
48827
  import { useCallback as useCallback69, useEffect as useEffect118, useMemo as useMemo78, useRef as useRef62, useState as useState140 } from "react";
48800
48828
  import { archivedByLine as archivedByLine2 } from "@ai-matrx/records";
48801
48829
  import { tableShapeChanged as tableShapeChanged14, useFields as useFields37, useRecordsClient as useRecordsClient99, useSeededAnswer, useTable as useTable37, useTableArchive as useTableArchive2 } from "@ai-matrx/records/react";
@@ -48838,15 +48866,23 @@ function subscribeCalendarAction(args) {
48838
48866
  }
48839
48867
 
48840
48868
  // src/CalendarSubscribe.tsx
48841
- import { copyText as copyText11 } from "@ai-matrx/kit/clipboard";
48869
+ import { copyText as copyText12 } from "@ai-matrx/kit/clipboard";
48842
48870
  import { useState as useState133 } from "react";
48871
+
48872
+ // src/asRefusal.ts
48873
+ function asRefusal3(e, fallback) {
48874
+ if (e instanceof Error && e.message.trim() !== "") return e;
48875
+ return { message: fallback };
48876
+ }
48877
+
48878
+ // src/CalendarSubscribe.tsx
48843
48879
  import { Button as Button105, Dialog as Dialog15, DialogContent as DialogContent15, DialogHeader as DialogHeader15, DialogTitle as DialogTitle15 } from "@ai-matrx/design-system";
48844
48880
  import { jsx as jsx159, jsxs as jsxs148 } from "react/jsx-runtime";
48845
48881
  function CalendarLinkOnce({ url }) {
48846
48882
  const notify = useRecordsUi().notify;
48847
48883
  const [copied, setCopied] = useState133(false);
48848
48884
  const copy = async () => {
48849
- if (await copyText11(url)) {
48885
+ if (await copyText12(url)) {
48850
48886
  setCopied(true);
48851
48887
  notify?.success("Link copied");
48852
48888
  } else notify?.error("Couldn\u2019t copy the link");
@@ -48898,7 +48934,7 @@ function CalendarSubscribeDialog({
48898
48934
  });
48899
48935
  setUrl(port.urlFor(made.token));
48900
48936
  } catch (e) {
48901
- setError(e instanceof Error ? e.message : "The link could not be made");
48937
+ setError(asRefusal3(e, "The link could not be made"));
48902
48938
  } finally {
48903
48939
  setBusy(false);
48904
48940
  }
@@ -48908,13 +48944,14 @@ function CalendarSubscribeDialog({
48908
48944
  /* @__PURE__ */ jsx159("p", { className: "text-sm text-muted-foreground", children: "Shows the rows this view shows, read as you." }),
48909
48945
  url ? /* @__PURE__ */ jsx159(CalendarLinkOnce, { url }) : /* @__PURE__ */ jsxs148("div", { className: "flex flex-col gap-2", children: [
48910
48946
  /* @__PURE__ */ jsx159(Button105, { size: "sm", onClick: () => void make(), disabled: busy, "data-calendar-make": "", children: busy ? "Making\u2026" : "Make link" }),
48911
- error ? /* @__PURE__ */ jsx159("p", { role: "alert", className: "text-sm text-destructive", "data-calendar-error": "", children: error }) : null
48947
+ error ? /* @__PURE__ */ jsx159(RefusalNotice, { error }) : null
48912
48948
  ] })
48913
48949
  ] }) });
48914
48950
  }
48915
48951
 
48916
48952
  // src/viewShare.ts
48917
- import { exportCsv as exportCsv2 } from "@ai-matrx/records/core";
48953
+ import { copyText as copyText13 } from "@ai-matrx/kit/clipboard";
48954
+ import { exportCsv as exportCsv2, exportMarkdown as exportMarkdown2 } from "@ai-matrx/records/core";
48918
48955
  var NO_VIEW_REASON = "No saved view yet";
48919
48956
  function viewLink(tableId, viewId, origin) {
48920
48957
  return `${tableLink(tableId, origin)}?view=${encodeURIComponent(viewId)}`;
@@ -48953,6 +48990,56 @@ async function downloadViewCsv(args) {
48953
48990
  download(`${name}.csv`, "text/csv;charset=utf-8", exportCsv2(columns, rows));
48954
48991
  host.notify?.success(read.cut ? cutWords(read.rows.length) : "CSV downloaded");
48955
48992
  }
48993
+ function downloadMarkdownAction(args) {
48994
+ return {
48995
+ id: "download-markdown",
48996
+ label: "Download Markdown",
48997
+ icon: "file-text",
48998
+ group: "share",
48999
+ ...args.disabledReason ? { disabledReason: args.disabledReason } : {},
49000
+ run: args.run
49001
+ };
49002
+ }
49003
+ function copyMarkdownAction(args) {
49004
+ return {
49005
+ id: "copy-markdown",
49006
+ label: "Copy as Markdown",
49007
+ icon: "copy",
49008
+ group: "share",
49009
+ ...args.disabledReason ? { disabledReason: args.disabledReason } : {},
49010
+ run: args.run
49011
+ };
49012
+ }
49013
+ async function readViewMarkdown(args) {
49014
+ const { client, host, tableId, fields, view, labels } = args;
49015
+ const read = await readViewRows(client, tableId, fields, view, args.ceiling);
49016
+ if ("error" in read) return null;
49017
+ const columns = viewColumns(fields, view.presentation);
49018
+ const rows = await rowsForFile({ client, host, fields: columns, records: read.rows, labels });
49019
+ return { markdown: exportMarkdown2(columns, rows), cut: read.cut, count: read.rows.length };
49020
+ }
49021
+ async function downloadViewMarkdown(args) {
49022
+ const table = await readViewMarkdown(args);
49023
+ if (!table) {
49024
+ args.host.notify?.error("The Markdown could not be written");
49025
+ return;
49026
+ }
49027
+ const name = fileNameOf(args.tableName) || "records";
49028
+ download(`${name}.md`, "text/markdown;charset=utf-8", table.markdown);
49029
+ args.host.notify?.success(table.cut ? cutWords(table.count) : "Markdown downloaded");
49030
+ }
49031
+ async function copyViewMarkdown(args) {
49032
+ const table = await readViewMarkdown(args);
49033
+ if (!table) {
49034
+ args.host.notify?.error("The Markdown could not be written");
49035
+ return;
49036
+ }
49037
+ if (await copyText13(table.markdown)) {
49038
+ args.host.notify?.success(table.cut ? cutWords(table.count) : "Markdown copied");
49039
+ } else {
49040
+ args.host.notify?.error("Couldn\u2019t copy the Markdown");
49041
+ }
49042
+ }
48956
49043
 
48957
49044
  // src/RecordPanel.tsx
48958
49045
  import { Fragment as Fragment65, useState as useState134 } from "react";
@@ -49114,7 +49201,7 @@ function BreakDownDialog({
49114
49201
  }
49115
49202
 
49116
49203
  // src/TableReference.tsx
49117
- import { copyText as copyText12 } from "@ai-matrx/kit/clipboard";
49204
+ import { copyText as copyText14 } from "@ai-matrx/kit/clipboard";
49118
49205
  import { useCallback as useCallback68, useMemo as useMemo76, useState as useState136 } from "react";
49119
49206
  import { useContentTransferCapabilities as useContentTransferCapabilities2 } from "@ai-matrx/alchemy/react/workspace";
49120
49207
  import { Button as Button108, Dialog as Dialog17, DialogContent as DialogContent17, DialogHeader as DialogHeader17, DialogTitle as DialogTitle17 } from "@ai-matrx/design-system";
@@ -49152,7 +49239,7 @@ function TableReferenceDialog({
49152
49239
  async (choice) => {
49153
49240
  if (!port) return;
49154
49241
  try {
49155
- if (!await copyText12(await port.build(choice.reference))) throw new Error("clipboard refused");
49242
+ if (!await copyText14(await port.build(choice.reference))) throw new Error("clipboard refused");
49156
49243
  setCopied(choice.id);
49157
49244
  notify?.success("Reference copied");
49158
49245
  } catch {
@@ -50391,7 +50478,7 @@ function TablePage({
50391
50478
  viewId: view?.id ?? null,
50392
50479
  origin: actionOrigin,
50393
50480
  copy: async (text) => {
50394
- if (await copyText13(text)) {
50481
+ if (await copyText15(text)) {
50395
50482
  recordsUi.notify?.success("Link copied");
50396
50483
  } else {
50397
50484
  recordsUi.notify?.error("Couldn\u2019t copy the link");
@@ -50411,6 +50498,30 @@ function TablePage({
50411
50498
  labels: viewLabels
50412
50499
  })
50413
50500
  }),
50501
+ downloadMarkdownAction({
50502
+ ...mayDo.known && !mayDo.read ? { disabledReason: "Needs Viewer access" } : !allFields.data ? { disabledReason: "Reading this table\u2026" } : {},
50503
+ run: () => downloadViewMarkdown({
50504
+ client,
50505
+ host: recordsUi,
50506
+ tableId,
50507
+ tableName: table.data?.name ?? "",
50508
+ fields: allFields.data ?? [],
50509
+ view: viewReading,
50510
+ labels: viewLabels
50511
+ })
50512
+ }),
50513
+ copyMarkdownAction({
50514
+ ...mayDo.known && !mayDo.read ? { disabledReason: "Needs Viewer access" } : !allFields.data ? { disabledReason: "Reading this table\u2026" } : {},
50515
+ run: () => copyViewMarkdown({
50516
+ client,
50517
+ host: recordsUi,
50518
+ tableId,
50519
+ tableName: table.data?.name ?? "",
50520
+ fields: allFields.data ?? [],
50521
+ view: viewReading,
50522
+ labels: viewLabels
50523
+ })
50524
+ }),
50414
50525
  // THE PRIVATE CALENDAR LINK (CAL-FEED-UI): only where the host bound the doors.
50415
50526
  ...recordsUi.calendarFeed && organizationId ? [
50416
50527
  subscribeCalendarAction({
@@ -50424,7 +50535,7 @@ function TablePage({
50424
50535
  window.open(url, "_blank", "noopener,noreferrer");
50425
50536
  },
50426
50537
  copyText: async (url) => {
50427
- if (await copyText13(url)) {
50538
+ if (await copyText15(url)) {
50428
50539
  recordsUi.notify?.success("Link copied");
50429
50540
  } else {
50430
50541
  recordsUi.notify?.error("Couldn\u2019t copy the link");
@@ -51689,7 +51800,7 @@ function CalendarLinks({ port }) {
51689
51800
  setRows(await port.list());
51690
51801
  setError(null);
51691
51802
  } catch (e) {
51692
- setError(e instanceof Error ? e.message : "The links could not be read");
51803
+ setError(asRefusal3(e, "The links could not be read"));
51693
51804
  }
51694
51805
  }, [port]);
51695
51806
  useEffect122(() => {
@@ -51700,12 +51811,12 @@ function CalendarLinks({ port }) {
51700
51811
  await run();
51701
51812
  setError(null);
51702
51813
  } catch (e) {
51703
- setError(e instanceof Error ? e.message : "That didn\u2019t work");
51814
+ setError(asRefusal3(e, "That didn\u2019t work"));
51704
51815
  }
51705
51816
  };
51706
51817
  if (rows === null && !error) return /* @__PURE__ */ jsx172("p", { className: "text-sm text-muted-foreground", "data-calendar-links-loading": "", children: "Reading your links\u2026" });
51707
51818
  return /* @__PURE__ */ jsxs160("div", { className: "flex flex-col gap-2", "data-calendar-links": "", children: [
51708
- error ? /* @__PURE__ */ jsx172("p", { role: "alert", className: "text-sm text-destructive", "data-calendar-error": "", children: error }) : null,
51819
+ error ? /* @__PURE__ */ jsx172(RefusalNotice, { error }) : null,
51709
51820
  rows && rows.length === 0 ? /* @__PURE__ */ jsx172("p", { className: "text-sm text-muted-foreground", children: "No calendar links yet." }) : null,
51710
51821
  rows?.map((row) => /* @__PURE__ */ jsxs160("div", { className: "flex flex-col gap-2 rounded-md border px-3 py-2", "data-calendar-link-row": row.id, children: [
51711
51822
  /* @__PURE__ */ jsxs160("div", { className: "flex flex-wrap items-center justify-between gap-2", children: [