@ai-matrx/records-ui 0.9.0 → 0.10.3

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
@@ -710,7 +710,7 @@ function PersonPicker({ field, value: value2, onChange, disabled, id, className
710
710
  }
711
711
 
712
712
  // src/gridEditing.tsx
713
- import { useCallback as useCallback3, useMemo as useMemo5, useRef as useRef2, useState as useState5 } from "react";
713
+ import { useCallback as useCallback3, useEffect as useEffect5, useMemo as useMemo5, useRef as useRef2, useState as useState5 } from "react";
714
714
  import { asWriteConflict } from "@ai-matrx/records/core";
715
715
  import { useRecordsClient as useRecordsClient5 } from "@ai-matrx/records/react";
716
716
  import { Button as Button4, cn as cn6 } from "@ai-matrx/design-system";
@@ -732,27 +732,49 @@ var TITLE = {
732
732
  store_not_served: "The store is not being served",
733
733
  internal: "The store said something this package does not recognise"
734
734
  };
735
+ var MACHINE_IDENTITY = [
736
+ /\b[A-Z]{2,4}-\d{1,3}\b/,
737
+ // FLD-11, REC-29, AGT-7
738
+ /\b(?:custom|platform|iam|history|public)\.[a-z_]+/,
739
+ // a database object
740
+ /\b(?:config|data|document|spec)\.[a-z_]+/,
741
+ // a path inside a document
742
+ /\b[a-z]+(?:_[a-z]+)+\b/,
743
+ // any snake_case identifier
744
+ // A SQLSTATE: five characters of digits and capitals, at least one a digit —
745
+ // so `23514` and `PT409` match and an emphatic `NEVER` does not.
746
+ /\b(?=[0-9A-Z]{5}\b)(?=[0-9A-Z]*\d)[0-9A-Z]{5}\b/
747
+ ];
748
+ function hintIsMachineIdentity(hint) {
749
+ return MACHINE_IDENTITY.some((shape) => shape.test(hint));
750
+ }
751
+ function hintForAPerson(hint) {
752
+ const said = (hint ?? "").trim();
753
+ if (said === "") return null;
754
+ return hintIsMachineIdentity(said) ? null : said;
755
+ }
735
756
  function RefusalNotice({
736
757
  error,
737
758
  className,
738
759
  actions
739
760
  }) {
761
+ const hint = hintForAPerson(error.hint);
762
+ const forEngineers = [
763
+ error.sqlstate ? `The store's code for this was ${error.sqlstate}.` : null,
764
+ hint === null && error.hint ? error.hint : null
765
+ ].filter(Boolean).join(" ");
740
766
  return /* @__PURE__ */ jsxs4(
741
767
  Alert,
742
768
  {
743
769
  variant: "destructive",
744
770
  className: cn4("text-xs", className),
745
- ...error.sqlstate ? { title: `The store's code for this was ${error.sqlstate}` } : {},
771
+ ...forEngineers ? { title: forEngineers } : {},
746
772
  children: [
747
773
  /* @__PURE__ */ jsx5(AlertTitle, { className: "text-xs font-medium", children: TITLE[error.code] ?? "The store refused this" }),
748
774
  /* @__PURE__ */ jsxs4(AlertDescription, { className: "space-y-1 text-xs", children: [
749
775
  /* @__PURE__ */ jsx5("p", { children: error.message }),
750
- error.hint ? /* @__PURE__ */ jsx5("p", { className: "opacity-80", children: error.hint }) : null,
751
- error.sqlstate ? /* @__PURE__ */ jsxs4("p", { className: "sr-only", "data-sqlstate": error.sqlstate, children: [
752
- "The store's code for this was ",
753
- error.sqlstate,
754
- "."
755
- ] }) : null,
776
+ hint ? /* @__PURE__ */ jsx5("p", { className: "opacity-80", children: hint }) : null,
777
+ forEngineers ? /* @__PURE__ */ jsx5("p", { className: "sr-only", ...error.sqlstate ? { "data-sqlstate": error.sqlstate } : {}, children: forEngineers }) : null,
756
778
  actions
757
779
  ] })
758
780
  ]
@@ -760,7 +782,14 @@ function RefusalNotice({
760
782
  );
761
783
  }
762
784
  function RefusalLine({ error, className }) {
763
- return /* @__PURE__ */ jsx5("p", { className: cn4("text-xs text-destructive", className), title: error.hint ?? void 0, children: error.message });
785
+ const hint = hintForAPerson(error.hint);
786
+ return /* @__PURE__ */ jsxs4("p", { className: cn4("text-xs text-destructive", className), title: error.hint ?? void 0, children: [
787
+ error.message,
788
+ hint ? /* @__PURE__ */ jsxs4("span", { className: "opacity-80", children: [
789
+ " ",
790
+ hint
791
+ ] }) : null
792
+ ] });
764
793
  }
765
794
 
766
795
  // src/values.tsx
@@ -940,6 +969,8 @@ async function versionOf(client, cache, recordId) {
940
969
  function useGridEditing({ tableId, fields, rows, reload, canWrite }) {
941
970
  const client = useRecordsClient5();
942
971
  const [editing, setEditing] = useState5(null);
972
+ const [draft, setDraft] = useState5(null);
973
+ const startedFrom = useRef2(null);
943
974
  const [optimistic, setOptimistic] = useState5({});
944
975
  const [saving, setSaving] = useState5({});
945
976
  const [refusals, setRefusals] = useState5({});
@@ -971,15 +1002,31 @@ function useGridEditing({ tableId, fields, rows, reload, canWrite }) {
971
1002
  (rowId, key) => refusals[cellKey(rowId, key)] ?? null,
972
1003
  [refusals]
973
1004
  );
1005
+ const shownValue = useCallback3(
1006
+ (rowId, key) => {
1007
+ const held = optimistic[cellKey(rowId, key)];
1008
+ if (held !== void 0) return held;
1009
+ return rows.find((r) => r.id === rowId)?.document?.[key];
1010
+ },
1011
+ [optimistic, rows]
1012
+ );
974
1013
  const begin = useCallback3(
975
1014
  (address) => {
976
1015
  if (!canWrite) return;
1016
+ if (editing && editing.rowId === address.rowId && editing.key === address.key) return;
1017
+ const current = shownValue(address.rowId, address.key);
1018
+ startedFrom.current = current;
1019
+ setDraft(current);
977
1020
  setEditing(address);
978
1021
  void versionOf(client, versions.current, address.rowId);
979
1022
  },
980
- [canWrite, client]
1023
+ [canWrite, client, editing, shownValue]
981
1024
  );
982
- const cancel = useCallback3(() => setEditing(null), []);
1025
+ const cancel = useCallback3(() => {
1026
+ setEditing(null);
1027
+ setDraft(null);
1028
+ startedFrom.current = null;
1029
+ }, []);
983
1030
  const writeCellNow = useCallback3(
984
1031
  async (rowId, key, value2) => {
985
1032
  const id = cellKey(rowId, key);
@@ -1041,21 +1088,31 @@ function useGridEditing({ tableId, fields, rows, reload, canWrite }) {
1041
1088
  [client, reload]
1042
1089
  );
1043
1090
  const commit = useCallback3(
1044
- (value2, move = null) => {
1091
+ (move = null) => {
1045
1092
  const open = editing;
1046
1093
  if (!open) return;
1047
- const row = rows.find((r) => r.id === open.rowId);
1048
- const unchanged = row !== void 0 && sameValue(row.document?.[open.key], value2);
1049
- if (!unchanged) void writeCell(open.rowId, open.key, value2);
1094
+ const value2 = draft;
1095
+ if (!sameValue(startedFrom.current, value2)) void writeCell(open.rowId, open.key, value2);
1050
1096
  if (!move) {
1051
1097
  setEditing(null);
1098
+ setDraft(null);
1099
+ startedFrom.current = null;
1052
1100
  return;
1053
1101
  }
1054
1102
  const next = step(open, order, rowIds, move);
1103
+ if (!next) {
1104
+ setEditing(null);
1105
+ setDraft(null);
1106
+ startedFrom.current = null;
1107
+ return;
1108
+ }
1109
+ const starting = shownValue(next.rowId, next.key);
1110
+ startedFrom.current = starting;
1111
+ setDraft(starting);
1055
1112
  setEditing(next);
1056
- if (next) void versionOf(client, versions.current, next.rowId);
1113
+ void versionOf(client, versions.current, next.rowId);
1057
1114
  },
1058
- [editing, rows, writeCell, order, rowIds, client]
1115
+ [editing, draft, writeCell, order, rowIds, client, shownValue]
1059
1116
  );
1060
1117
  const retry = useCallback3(
1061
1118
  (rowId, key) => {
@@ -1110,12 +1167,18 @@ function useGridEditing({ tableId, fields, rows, reload, canWrite }) {
1110
1167
  }
1111
1168
  reload();
1112
1169
  const first = order[0];
1113
- if (first) setEditing({ rowId: written.data, key: first });
1170
+ if (first) {
1171
+ startedFrom.current = void 0;
1172
+ setDraft(void 0);
1173
+ setEditing({ rowId: written.data, key: first });
1174
+ }
1114
1175
  return null;
1115
1176
  }, [client, tableId, reload, order]);
1116
1177
  return {
1117
1178
  valueFor,
1118
1179
  editing,
1180
+ draft,
1181
+ type: setDraft,
1119
1182
  stateOf,
1120
1183
  refusalOf,
1121
1184
  begin,
@@ -1178,27 +1241,40 @@ function GridCell({
1178
1241
  OpenCell,
1179
1242
  {
1180
1243
  field,
1181
- value: value2,
1182
- onCommit: (next, move) => editing.commit(next, move),
1244
+ draft: editing.draft,
1245
+ onType: editing.type,
1246
+ onCommit: (move) => editing.commit(move),
1183
1247
  onCancel: editing.cancel
1184
1248
  }
1185
1249
  );
1186
1250
  }
1187
- return /* @__PURE__ */ jsxs5("span", { "data-matrx-cell-control": "", className: "flex min-w-0 flex-col gap-0.5", children: [
1251
+ const openThisCell = (event) => {
1252
+ event.preventDefault();
1253
+ event.stopPropagation();
1254
+ editing.begin({ rowId: row.id, key: field.key });
1255
+ };
1256
+ return /* @__PURE__ */ jsxs5("span", { "data-matrx-cell-control": "", className: "flex w-full min-w-0 flex-col gap-0.5", children: [
1188
1257
  /* @__PURE__ */ jsx8(
1189
1258
  "span",
1190
1259
  {
1191
1260
  role: editable ? "button" : void 0,
1192
1261
  tabIndex: editable ? 0 : void 0,
1193
1262
  "aria-label": editable ? `Edit ${fieldName(field)}` : void 0,
1194
- title: editable ? `${fieldName(field)} \u2014 press Enter to edit` : void 0,
1263
+ title: editable ? `${fieldName(field)} \u2014 click or press Enter to edit` : `${fieldName(field)} is worked out by the system, so it cannot be typed in`,
1195
1264
  className: cn6(
1196
- "block min-w-0 truncate rounded px-1 py-0.5",
1265
+ // `w-full` so the whole cell is the control. A gap beside it is bare
1266
+ // table cell, and a click there is a row click.
1267
+ "block w-full min-w-0 truncate rounded px-1 py-0.5",
1197
1268
  editable ? "cursor-text hover:bg-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring" : "",
1198
1269
  state === "saving" ? "opacity-60" : "",
1199
1270
  state === "refused" ? "ring-1 ring-destructive" : ""
1200
1271
  ),
1201
- onClick: editable ? () => editing.begin({ rowId: row.id, key: field.key }) : void 0,
1272
+ onClick: editable ? openThisCell : void 0,
1273
+ onDoubleClick: (event) => {
1274
+ event.preventDefault();
1275
+ event.stopPropagation();
1276
+ if (editable) editing.begin({ rowId: row.id, key: field.key });
1277
+ },
1202
1278
  onKeyDown: editable ? (event) => {
1203
1279
  if (event.key === "Enter" || event.key === "F2") {
1204
1280
  event.preventDefault();
@@ -1241,11 +1317,11 @@ function closesOnBlur(field) {
1241
1317
  }
1242
1318
  function OpenCell({
1243
1319
  field,
1244
- value: value2,
1320
+ draft,
1321
+ onType,
1245
1322
  onCommit,
1246
1323
  onCancel
1247
1324
  }) {
1248
- const [draft, setDraft] = useState5(value2);
1249
1325
  const multiline = editorKindFor(field) === "long_text";
1250
1326
  const blurCommits = closesOnBlur(field);
1251
1327
  const done = useRef2(false);
@@ -1254,22 +1330,24 @@ function OpenCell({
1254
1330
  done.current = true;
1255
1331
  run();
1256
1332
  };
1333
+ const host = useRef2(null);
1334
+ useEffect5(() => {
1335
+ const control = host.current?.querySelector("input, textarea, [role='combobox'], button");
1336
+ if (control && document.activeElement !== control) control.focus();
1337
+ }, [field.id]);
1257
1338
  const blur = blurCommits ? {
1258
1339
  onBlur: (event) => {
1259
1340
  if (event.currentTarget.contains(event.relatedTarget)) return;
1260
- once(() => onCommit(draft, null));
1341
+ once(() => onCommit(null));
1261
1342
  }
1262
1343
  } : {};
1263
1344
  return /* @__PURE__ */ jsxs5(
1264
1345
  "span",
1265
1346
  {
1347
+ ref: host,
1266
1348
  "data-matrx-cell-control": "",
1267
- className: "flex min-w-[10rem] flex-col gap-1",
1268
- ref: (node) => {
1269
- if (!node) return;
1270
- const control = node.querySelector("input, textarea, [role='combobox'], button");
1271
- if (control && document.activeElement !== control) control.focus();
1272
- },
1349
+ className: "flex w-full min-w-[10rem] flex-col gap-1",
1350
+ onDoubleClick: (event) => event.stopPropagation(),
1273
1351
  onKeyDown: (event) => {
1274
1352
  if (event.key === "Escape") {
1275
1353
  event.preventDefault();
@@ -1280,20 +1358,20 @@ function OpenCell({
1280
1358
  if (event.key === "Tab") {
1281
1359
  event.preventDefault();
1282
1360
  event.stopPropagation();
1283
- once(() => onCommit(draft, event.shiftKey ? "previous" : "next"));
1361
+ once(() => onCommit(event.shiftKey ? "previous" : "next"));
1284
1362
  return;
1285
1363
  }
1286
1364
  if (event.key === "Enter" && (!multiline || event.metaKey || event.ctrlKey)) {
1287
1365
  event.preventDefault();
1288
1366
  event.stopPropagation();
1289
- once(() => onCommit(draft, null));
1367
+ once(() => onCommit(null));
1290
1368
  }
1291
1369
  },
1292
1370
  ...blur,
1293
1371
  children: [
1294
- /* @__PURE__ */ jsx8(FieldControl, { field, value: draft, onChange: setDraft, id: `cell-${field.id}` }),
1372
+ /* @__PURE__ */ jsx8(FieldControl, { field, value: draft, onChange: onType, id: `cell-${field.id}` }),
1295
1373
  blurCommits ? /* @__PURE__ */ jsx8("span", { className: "text-[10px] text-muted-foreground", children: multiline ? "\u2318\u23CE saves \xB7 Esc cancels \xB7 Tab next" : "\u23CE saves \xB7 Esc cancels \xB7 Tab next" }) : /* @__PURE__ */ jsxs5("span", { className: "flex items-center gap-1", children: [
1296
- /* @__PURE__ */ jsx8(Button4, { size: "sm", onClick: () => once(() => onCommit(draft, null)), children: "Save" }),
1374
+ /* @__PURE__ */ jsx8(Button4, { size: "sm", onClick: () => once(() => onCommit(null)), children: "Save" }),
1297
1375
  /* @__PURE__ */ jsx8(Button4, { size: "sm", variant: "ghost", onClick: () => once(onCancel), children: "Cancel" })
1298
1376
  ] })
1299
1377
  ]
@@ -1386,6 +1464,8 @@ function Grid({
1386
1464
  tableId: `records-grid-${tableId}`,
1387
1465
  stickyHeader: true,
1388
1466
  pageSize,
1467
+ detail: { enabled: false },
1468
+ window: { enabled: false },
1389
1469
  ...onOpenRecord ? { onRowOpen: (row) => onOpenRecord(row.id) } : {},
1390
1470
  ...canWrite ? {
1391
1471
  rowActions: (row) => /* @__PURE__ */ jsx9(
@@ -1727,7 +1807,7 @@ import { useFields as useFields5, useRecord as useRecord2, useTable as useTable3
1727
1807
  import { Button as Button9, Separator as Separator3, cn as cn11 } from "@ai-matrx/design-system";
1728
1808
 
1729
1809
  // src/RecordForm.tsx
1730
- import { useEffect as useEffect6, useMemo as useMemo8, useState as useState10 } from "react";
1810
+ import { useEffect as useEffect7, useMemo as useMemo8, useState as useState10 } from "react";
1731
1811
  import {
1732
1812
  useFields as useFields4,
1733
1813
  useRecord,
@@ -1737,7 +1817,7 @@ import { predictWriteRefusals } from "@ai-matrx/records/core";
1737
1817
  import { Button as Button8, Separator as Separator2, cn as cn10 } from "@ai-matrx/design-system";
1738
1818
 
1739
1819
  // src/systemTable.ts
1740
- import { useEffect as useEffect5, useMemo as useMemo7, useState as useState9 } from "react";
1820
+ import { useEffect as useEffect6, useMemo as useMemo7, useState as useState9 } from "react";
1741
1821
  import { useRecordsClient as useRecordsClient6 } from "@ai-matrx/records/react";
1742
1822
  var inFlight = /* @__PURE__ */ new Map();
1743
1823
  async function ensureSystemTable(client, spec) {
@@ -1878,7 +1958,7 @@ function useSystemTable(spec) {
1878
1958
  const [state, setState] = useState9({ tableId: null, loading: true, error: null });
1879
1959
  const slug = spec.slug;
1880
1960
  const stable = useMemo7(() => spec, [slug]);
1881
- useEffect5(() => {
1961
+ useEffect6(() => {
1882
1962
  let cancelled = false;
1883
1963
  setState({ tableId: null, loading: true, error: null });
1884
1964
  void ensureSystemTable(client, stable).then((result) => {
@@ -1896,7 +1976,7 @@ function useSystemTable(spec) {
1896
1976
  function useRecordVersion(recordId) {
1897
1977
  const client = useRecordsClient6();
1898
1978
  const [version, setVersion] = useState9(null);
1899
- useEffect5(() => {
1979
+ useEffect6(() => {
1900
1980
  let cancelled = false;
1901
1981
  setVersion(null);
1902
1982
  if (!recordId) return;
@@ -1933,7 +2013,7 @@ function RecordForm({
1933
2013
  const [touched, setTouched] = useState10(false);
1934
2014
  const loaded = useRecordVersion(recordId ?? null);
1935
2015
  const loadedVersion = loaded.version;
1936
- useEffect6(() => {
2016
+ useEffect7(() => {
1937
2017
  const document3 = existing.data?.document;
1938
2018
  if (document3) setDraft({ ...document3 });
1939
2019
  }, [existing.data?.record_id, loadedVersion]);
@@ -2208,7 +2288,6 @@ function FieldEditor({ tableId, field, onSaved, onCancel, onRemoved, className }
2208
2288
  [fields.data, field?.id]
2209
2289
  );
2210
2290
  const relations = useMemo10(() => others.filter((f) => f.type === "relation"), [others]);
2211
- const manyRelations = useMemo10(() => relations.filter((f) => f.multi), [relations]);
2212
2291
  const choice = fieldTypeChoice(type);
2213
2292
  const editing = Boolean(field);
2214
2293
  const derivedKey = field?.key ?? keyFor(label);
@@ -2216,7 +2295,9 @@ function FieldEditor({ tableId, field, onSaved, onCancel, onRemoved, className }
2216
2295
  return /* @__PURE__ */ jsx14("p", { className: cn12("text-xs text-muted-foreground", className), children: rights.reason });
2217
2296
  }
2218
2297
  const options = optionText.split("\n").map((word) => word.trim()).filter((word) => word !== "");
2298
+ const missing = editing ? null : whatIsMissing({ type, label, options, via, pick, agg, of, relations, formulaFields });
2219
2299
  const save = async () => {
2300
+ if (missing) return;
2220
2301
  if (editing && field) {
2221
2302
  const patch = {
2222
2303
  label,
@@ -2322,7 +2403,6 @@ function FieldEditor({ tableId, field, onSaved, onCancel, onRemoved, className }
2322
2403
  formulaFields,
2323
2404
  setFormulaFields,
2324
2405
  relations,
2325
- manyRelations,
2326
2406
  others,
2327
2407
  existingOptionsTable: Boolean(field?.options_table_id ?? field?.config?.["options_table_id"])
2328
2408
  }
@@ -2437,8 +2517,17 @@ function FieldEditor({ tableId, field, onSaved, onCancel, onRemoved, className }
2437
2517
  ] }, `${rule.kind}-${index}`))
2438
2518
  ] }),
2439
2519
  mutation.error ? /* @__PURE__ */ jsx14(RefusalNotice, { error: mutation.error }) : null,
2520
+ missing ? /* @__PURE__ */ jsx14("p", { className: "text-[11px] text-destructive", children: missing }) : null,
2440
2521
  /* @__PURE__ */ jsxs11("div", { className: "flex flex-wrap items-center gap-2", children: [
2441
- /* @__PURE__ */ jsx14(Button10, { size: "sm", disabled: mutation.saving || label.trim() === "", onClick: () => void save(), children: mutation.saving ? "Saving\u2026" : field ? "Save field" : "Add field" }),
2522
+ /* @__PURE__ */ jsx14(
2523
+ Button10,
2524
+ {
2525
+ size: "sm",
2526
+ disabled: mutation.saving || missing !== null,
2527
+ onClick: () => void save(),
2528
+ children: mutation.saving ? "Saving\u2026" : field ? "Save field" : "Add field"
2529
+ }
2530
+ ),
2442
2531
  onCancel ? /* @__PURE__ */ jsx14(Button10, { size: "sm", variant: "ghost", onClick: onCancel, children: "Cancel" }) : null,
2443
2532
  field ? /* @__PURE__ */ jsx14("div", { className: "ml-auto flex items-center gap-2", children: askingToRemove ? /* @__PURE__ */ jsxs11(Fragment4, { children: [
2444
2533
  /* @__PURE__ */ jsxs11("span", { className: "text-destructive", children: [
@@ -2452,6 +2541,34 @@ function FieldEditor({ tableId, field, onSaved, onCancel, onRemoved, className }
2452
2541
  ] })
2453
2542
  ] });
2454
2543
  }
2544
+ function whatIsMissing(state) {
2545
+ const { type, label, options, via, pick, agg, of, relations, formulaFields } = state;
2546
+ if (label.trim() === "") return "Give the column a name.";
2547
+ if (type === "select" || type === "multi_select") {
2548
+ if (options.length === 0) return "Type the choices this column offers, one per line.";
2549
+ return null;
2550
+ }
2551
+ if (type === "lookup" || type === "rollup") {
2552
+ if (relations.length === 0) {
2553
+ return type === "rollup" ? "A total is worked out across the records a column points at, and this table has no column that points at records yet. Add a Person or File column that can hold more than one, then come back." : "A borrowed value is read from a record a column points at, and this table has no column that points at another record yet. Add a Person or File column, then come back.";
2554
+ }
2555
+ const chosen = relations.find((relation) => relation.key === via);
2556
+ if (!chosen) {
2557
+ return type === "rollup" ? "Choose the column whose records this one adds up." : "Choose the column whose record this one reads from.";
2558
+ }
2559
+ if (type === "rollup" && !chosen.multi) {
2560
+ return `${fieldName(chosen)} points at one record at a time, so there is nothing to total. Tick "Can hold more than one" on ${fieldName(chosen)}, or use Borrowed value to read its one value.`;
2561
+ }
2562
+ if (type === "lookup" && pick.trim() === "") {
2563
+ return `Say which value to read from the record ${fieldName(chosen)} points at.`;
2564
+ }
2565
+ if (type === "rollup" && agg !== "count" && of.trim() === "") {
2566
+ return "Say which value of those records to work out.";
2567
+ }
2568
+ return null;
2569
+ }
2570
+ return null;
2571
+ }
2455
2572
  function multiApplies(type) {
2456
2573
  return type === "member" || type === "attachment" || type === "text";
2457
2574
  }
@@ -2517,16 +2634,19 @@ function TypeQuestions(props) {
2517
2634
  ] });
2518
2635
  }
2519
2636
  if (type === "lookup" || type === "rollup") {
2520
- const usable = type === "rollup" ? props.manyRelations : props.relations;
2637
+ const usable = props.relations;
2521
2638
  if (usable.length === 0) {
2522
- return /* @__PURE__ */ jsx14("p", { className: "text-[11px] text-muted-foreground", children: type === "rollup" ? "This kind of column works something out from records this one points at, and this table has no column yet that points at several records. Add a Person or File column that can hold more than one first." : "This kind of column reads a value from a record this one points at, and this table has no column yet that points at another record. Add a Person or File column first." });
2639
+ return /* @__PURE__ */ jsx14("p", { className: "text-[11px] text-muted-foreground", children: type === "rollup" ? "This kind of column works something out from the records another column points at, and this table has no column that points at records yet. Add a Person or File column that can hold more than one first." : "This kind of column reads a value from a record another column points at, and this table has no column that points at another record yet. Add a Person or File column first." });
2523
2640
  }
2524
2641
  return /* @__PURE__ */ jsxs11("div", { className: "grid gap-3 sm:grid-cols-2", children: [
2525
2642
  /* @__PURE__ */ jsxs11("div", { className: "flex flex-col gap-1", children: [
2526
2643
  /* @__PURE__ */ jsx14(Label2, { className: "text-xs", children: type === "rollup" ? "Across the records in" : "Read it from" }),
2527
2644
  /* @__PURE__ */ jsxs11(Select2, { value: props.via, onValueChange: props.setVia, children: [
2528
2645
  /* @__PURE__ */ jsx14(SelectTrigger2, { size: "sm", "aria-label": type === "rollup" ? "Across the records in" : "Read it from", children: /* @__PURE__ */ jsx14(SelectValue2, { placeholder: "Choose a column" }) }),
2529
- /* @__PURE__ */ jsx14(SelectContent2, { children: usable.map((f) => /* @__PURE__ */ jsx14(SelectItem2, { value: f.key, children: fieldName(f) }, f.id)) })
2646
+ /* @__PURE__ */ jsx14(SelectContent2, { children: usable.map((f) => /* @__PURE__ */ jsxs11(SelectItem2, { value: f.key, children: [
2647
+ fieldName(f),
2648
+ type === "rollup" && !f.multi ? " \u2014 holds one" : ""
2649
+ ] }, f.id)) })
2530
2650
  ] })
2531
2651
  ] }),
2532
2652
  type === "lookup" ? /* @__PURE__ */ jsxs11("label", { className: "flex flex-col gap-1", children: [
@@ -2978,7 +3098,7 @@ function parseSorts(raw) {
2978
3098
  }
2979
3099
 
2980
3100
  // src/ViewSwitcher.tsx
2981
- import { useEffect as useEffect7, useMemo as useMemo11, useState as useState16 } from "react";
3101
+ import { useEffect as useEffect8, useMemo as useMemo11, useState as useState16 } from "react";
2982
3102
  import { useFields as useFields9, useRecords as useRecords3, useRecordsClient as useRecordsClient8 } from "@ai-matrx/records/react";
2983
3103
  import { Button as Button14, Skeleton as Skeleton3, cn as cn15 } from "@ai-matrx/design-system";
2984
3104
  import { jsx as jsx18, jsxs as jsxs15 } from "react/jsx-runtime";
@@ -2991,7 +3111,7 @@ function useViewRecords(view, pageSize = 200) {
2991
3111
  error: null
2992
3112
  });
2993
3113
  const ruleId = view.ruleId ?? null;
2994
- useEffect7(() => {
3114
+ useEffect8(() => {
2995
3115
  if (!ruleId) return;
2996
3116
  let cancelled = false;
2997
3117
  setRuled({ rows: [], loading: true, error: null });
@@ -3041,7 +3161,7 @@ function ViewSwitcher({
3041
3161
  }) {
3042
3162
  const [layout, setLayout] = useState16(view.layout);
3043
3163
  const [local, setLocal] = useState16({});
3044
- useEffect7(() => {
3164
+ useEffect8(() => {
3045
3165
  setLayout(view.layout);
3046
3166
  setLocal({});
3047
3167
  }, [view.layout, view.name, view.subject]);
@@ -3318,7 +3438,7 @@ function Card({
3318
3438
  }
3319
3439
 
3320
3440
  // src/ViewBar.tsx
3321
- import { useCallback as useCallback5, useEffect as useEffect8, useState as useState17 } from "react";
3441
+ import { useCallback as useCallback5, useEffect as useEffect9, useState as useState17 } from "react";
3322
3442
  import { useRecordsClient as useRecordsClient9 } from "@ai-matrx/records/react";
3323
3443
  import { Button as Button15, Input, Skeleton as Skeleton4, cn as cn16 } from "@ai-matrx/design-system";
3324
3444
 
@@ -3380,10 +3500,10 @@ function ViewBar({ tableId, activeViewId, onActiveView, seed, className }) {
3380
3500
  setError(null);
3381
3501
  setViews(mine);
3382
3502
  }, [client, viewTableId, tableId, seed]);
3383
- useEffect8(() => {
3503
+ useEffect9(() => {
3384
3504
  void load();
3385
3505
  }, [load]);
3386
- useEffect8(() => {
3506
+ useEffect9(() => {
3387
3507
  if (!views || views.length === 0) return;
3388
3508
  const chosen = views.find((v) => v.id === (activeViewId ?? active)) ?? views.find((v) => v.isDefault) ?? views[0];
3389
3509
  if (chosen.id !== active) setActive(chosen.id);
@@ -3561,7 +3681,7 @@ function ProposalRow({
3561
3681
  }
3562
3682
 
3563
3683
  // src/ActionInbox.tsx
3564
- import { useCallback as useCallback6, useEffect as useEffect9, useMemo as useMemo12, useState as useState19 } from "react";
3684
+ import { useCallback as useCallback6, useEffect as useEffect10, useMemo as useMemo12, useState as useState19 } from "react";
3565
3685
  import { useRecordsClient as useRecordsClient11 } from "@ai-matrx/records/react";
3566
3686
  import { Badge as Badge6, Button as Button17, Skeleton as Skeleton5, cn as cn18 } from "@ai-matrx/design-system";
3567
3687
  import { jsx as jsx21, jsxs as jsxs18 } from "react/jsx-runtime";
@@ -3640,7 +3760,7 @@ function ActionInbox({ tableId, includeSettled = false, seed, onOpenRecord, clas
3640
3760
  setError(null);
3641
3761
  setActions(held);
3642
3762
  }, [client, queueTableId, seed]);
3643
- useEffect9(() => {
3763
+ useEffect10(() => {
3644
3764
  void load();
3645
3765
  }, [load]);
3646
3766
  const shown = useMemo12(() => {
@@ -3755,7 +3875,7 @@ function parseChanges(raw) {
3755
3875
  }
3756
3876
 
3757
3877
  // src/HistoryPanel.tsx
3758
- import { useCallback as useCallback7, useEffect as useEffect10, useState as useState20 } from "react";
3878
+ import { useCallback as useCallback7, useEffect as useEffect11, useState as useState20 } from "react";
3759
3879
  import {
3760
3880
  useFields as useFields10,
3761
3881
  useRecordsClient as useRecordsClient12,
@@ -3802,7 +3922,7 @@ function HistoryPanel({ tableId, recordId, className }) {
3802
3922
  );
3803
3923
  setRead(read2.ok ? read2.data : { record_id: recordId, document: {}, hidden: {}, computed: [] });
3804
3924
  }, [client, recordId]);
3805
- useEffect10(() => {
3925
+ useEffect11(() => {
3806
3926
  void load();
3807
3927
  }, [load]);
3808
3928
  if (error) return /* @__PURE__ */ jsx22(RefusalNotice, { error, className });
@@ -3862,7 +3982,7 @@ function HistoryPanel({ tableId, recordId, className }) {
3862
3982
  }
3863
3983
 
3864
3984
  // src/CommentThread.tsx
3865
- import { useCallback as useCallback8, useEffect as useEffect11, useState as useState21 } from "react";
3985
+ import { useCallback as useCallback8, useEffect as useEffect12, useState as useState21 } from "react";
3866
3986
  import { useRecordsClient as useRecordsClient13, useTable as useTable9 } from "@ai-matrx/records/react";
3867
3987
  import { Button as Button19, Skeleton as Skeleton7, Textarea, cn as cn20 } from "@ai-matrx/design-system";
3868
3988
  import { jsx as jsx23, jsxs as jsxs20 } from "react/jsx-runtime";
@@ -3907,7 +4027,7 @@ function CommentThread({ tableId, recordId, className }) {
3907
4027
  result.data.rows.filter((row) => row.document?.["subject"] === recordId).map(toComment).sort((a, b) => a.at.localeCompare(b.at))
3908
4028
  );
3909
4029
  }, [client, commentTableId, recordId]);
3910
- useEffect11(() => {
4030
+ useEffect12(() => {
3911
4031
  void load();
3912
4032
  }, [load]);
3913
4033
  async function post() {
@@ -4077,7 +4197,7 @@ function submissionStamp(args) {
4077
4197
  }
4078
4198
 
4079
4199
  // src/FormBuilder.tsx
4080
- import { useCallback as useCallback10, useEffect as useEffect13, useMemo as useMemo14, useState as useState23 } from "react";
4200
+ import { useCallback as useCallback10, useEffect as useEffect14, useMemo as useMemo14, useState as useState23 } from "react";
4081
4201
  import { useFields as useFields12, useRecordsClient as useRecordsClient15, useTable as useTable10 } from "@ai-matrx/records/react";
4082
4202
  import {
4083
4203
  BasicInput as BasicInput5,
@@ -4090,7 +4210,7 @@ import {
4090
4210
  } from "@ai-matrx/design-system";
4091
4211
 
4092
4212
  // src/FormRunner.tsx
4093
- import { useCallback as useCallback9, useEffect as useEffect12, useMemo as useMemo13, useRef as useRef4, useState as useState22 } from "react";
4213
+ import { useCallback as useCallback9, useEffect as useEffect13, useMemo as useMemo13, useRef as useRef4, useState as useState22 } from "react";
4094
4214
  import { useFields as useFields11, useRecordsClient as useRecordsClient14 } from "@ai-matrx/records/react";
4095
4215
  import { doorExists, DOORS } from "@ai-matrx/records/core";
4096
4216
  import { Button as Button20, Progress, Skeleton as Skeleton8, cn as cn21 } from "@ai-matrx/design-system";
@@ -4120,7 +4240,7 @@ function FormRunner({ form, preview = false, onSubmitted, className }) {
4120
4240
  };
4121
4241
  });
4122
4242
  }, [fields.data, form.questions]);
4123
- useEffect12(() => {
4243
+ useEffect13(() => {
4124
4244
  let cancelled = false;
4125
4245
  const conditional = questions.filter((q) => q.showIf);
4126
4246
  if (conditional.length === 0) return;
@@ -4195,7 +4315,7 @@ function FormRunner({ form, preview = false, onSubmitted, className }) {
4195
4315
  if (event.shiftKey) retreat();
4196
4316
  else advance();
4197
4317
  }
4198
- useEffect12(() => {
4318
+ useEffect13(() => {
4199
4319
  const input = stage.current?.querySelector("input, textarea, select, [role='combobox']");
4200
4320
  input?.focus();
4201
4321
  }, [index, done]);
@@ -4361,10 +4481,10 @@ function FormBuilder({ tableId, seed, activeFormId, onActiveForm, className }) {
4361
4481
  setError(null);
4362
4482
  setForms(mine);
4363
4483
  }, [client, formTableId, tableId, seed]);
4364
- useEffect13(() => {
4484
+ useEffect14(() => {
4365
4485
  void load();
4366
4486
  }, [load]);
4367
- useEffect13(() => {
4487
+ useEffect14(() => {
4368
4488
  if (!forms || forms.length === 0) return;
4369
4489
  const chosen = forms.find((f) => f.id === (activeFormId ?? activeId)) ?? forms[0];
4370
4490
  if (chosen.id !== activeId) setActiveId(chosen.id);
@@ -4736,7 +4856,7 @@ function isSignatureField(field) {
4736
4856
  }
4737
4857
 
4738
4858
  // src/DocTemplate.tsx
4739
- import { useCallback as useCallback11, useEffect as useEffect14, useState as useState24 } from "react";
4859
+ import { useCallback as useCallback11, useEffect as useEffect15, useState as useState24 } from "react";
4740
4860
  import { useFields as useFields13, useRecordsClient as useRecordsClient16, useTable as useTable11 } from "@ai-matrx/records/react";
4741
4861
  import { BasicInput as BasicInput6, BasicTextarea as BasicTextarea4, Button as Button22, Label as Label4, Skeleton as Skeleton10, cn as cn23 } from "@ai-matrx/design-system";
4742
4862
  import { jsx as jsx26, jsxs as jsxs23 } from "react/jsx-runtime";
@@ -4784,10 +4904,10 @@ function DocTemplate({ tableId, seed, activeTemplateId, onActiveTemplate, classN
4784
4904
  setError(null);
4785
4905
  setTemplates(rows);
4786
4906
  }, [client, tableId, seed, fields.data]);
4787
- useEffect14(() => {
4907
+ useEffect15(() => {
4788
4908
  void load();
4789
4909
  }, [load]);
4790
- useEffect14(() => {
4910
+ useEffect15(() => {
4791
4911
  if (!templates || templates.length === 0) return;
4792
4912
  const chosen = templates.find((t) => t.id === (activeTemplateId ?? activeId)) ?? templates[0];
4793
4913
  if (chosen.id !== activeId) setActiveId(chosen.id);
@@ -4795,7 +4915,7 @@ function DocTemplate({ tableId, seed, activeTemplateId, onActiveTemplate, classN
4795
4915
  setDraftBody(chosen.body);
4796
4916
  onActiveTemplate?.(chosen);
4797
4917
  }, [templates, activeTemplateId]);
4798
- useEffect14(() => {
4918
+ useEffect15(() => {
4799
4919
  let cancelled = false;
4800
4920
  if (draftBody.trim() === "") {
4801
4921
  setUnresolved([]);
@@ -4915,7 +5035,7 @@ function DocTemplate({ tableId, seed, activeTemplateId, onActiveTemplate, classN
4915
5035
  }
4916
5036
 
4917
5037
  // src/DocRender.tsx
4918
- import { useCallback as useCallback12, useEffect as useEffect15, useRef as useRef5, useState as useState25 } from "react";
5038
+ import { useCallback as useCallback12, useEffect as useEffect16, useRef as useRef5, useState as useState25 } from "react";
4919
5039
  import { useRecordsClient as useRecordsClient17 } from "@ai-matrx/records/react";
4920
5040
  import { Button as Button23, Skeleton as Skeleton11, cn as cn24 } from "@ai-matrx/design-system";
4921
5041
  import { jsx as jsx27, jsxs as jsxs24 } from "react/jsx-runtime";
@@ -4944,7 +5064,7 @@ function DocRender({ templateId, recordId, filename, onRendered, className }) {
4944
5064
  setPreview(body.data);
4945
5065
  setRenders(held.data.filter((r) => r.template_id === templateId));
4946
5066
  }, [client, templateId, recordId]);
4947
- useEffect15(() => {
5067
+ useEffect16(() => {
4948
5068
  void load();
4949
5069
  }, [load]);
4950
5070
  async function freeze() {
@@ -5028,7 +5148,7 @@ function DocRender({ templateId, recordId, filename, onRendered, className }) {
5028
5148
  }
5029
5149
 
5030
5150
  // src/SignBlock.tsx
5031
- import { useCallback as useCallback13, useEffect as useEffect16, useState as useState26 } from "react";
5151
+ import { useCallback as useCallback13, useEffect as useEffect17, useState as useState26 } from "react";
5032
5152
  import { useFields as useFields14, useRecordsClient as useRecordsClient18 } from "@ai-matrx/records/react";
5033
5153
  import { BasicInput as BasicInput7, Button as Button24, Skeleton as Skeleton12, cn as cn25 } from "@ai-matrx/design-system";
5034
5154
  import { useTable as useTable12 } from "@ai-matrx/records/react";
@@ -5058,7 +5178,7 @@ function SignBlock({ tableId, recordId, render, className }) {
5058
5178
  }
5059
5179
  setVerdicts(answers);
5060
5180
  }, [client, recordId]);
5061
- useEffect16(() => {
5181
+ useEffect17(() => {
5062
5182
  void load();
5063
5183
  }, [load]);
5064
5184
  async function sign(field) {
@@ -5165,7 +5285,7 @@ function SignBlock({ tableId, recordId, render, className }) {
5165
5285
  }
5166
5286
 
5167
5287
  // src/NotifyRuleEditor.tsx
5168
- import { useCallback as useCallback14, useEffect as useEffect17, useState as useState27 } from "react";
5288
+ import { useCallback as useCallback14, useEffect as useEffect18, useState as useState27 } from "react";
5169
5289
  import { useRecordsClient as useRecordsClient19, useTable as useTable13 } from "@ai-matrx/records/react";
5170
5290
  import { Button as Button25, Skeleton as Skeleton13, cn as cn26 } from "@ai-matrx/design-system";
5171
5291
  import { jsx as jsx29, jsxs as jsxs26 } from "react/jsx-runtime";
@@ -5196,7 +5316,7 @@ function NotifyRuleEditor({ tableId, seed, className }) {
5196
5316
  if (host.savedViews) setViews(await host.savedViews());
5197
5317
  else setViews(null);
5198
5318
  }, [client, host]);
5199
- useEffect17(() => {
5319
+ useEffect18(() => {
5200
5320
  void load();
5201
5321
  }, [load]);
5202
5322
  const write = useCallback14(
@@ -5231,7 +5351,7 @@ function NotifyRuleEditor({ tableId, seed, className }) {
5231
5351
  },
5232
5352
  [client, tableId]
5233
5353
  );
5234
- useEffect17(() => {
5354
+ useEffect18(() => {
5235
5355
  if (!subscriptions || !seed || seed.length === 0) return;
5236
5356
  const missing = seed.filter((s) => !subscriptions.some((held) => held.name === s.name));
5237
5357
  if (missing.length === 0) return;
@@ -5320,7 +5440,7 @@ function NotifyRuleEditor({ tableId, seed, className }) {
5320
5440
  }
5321
5441
 
5322
5442
  // src/ChartBlock.tsx
5323
- import { useEffect as useEffect18, useState as useState28 } from "react";
5443
+ import { useEffect as useEffect19, useState as useState28 } from "react";
5324
5444
  import { useRecordsClient as useRecordsClient20 } from "@ai-matrx/records/react";
5325
5445
  import { measureKey } from "@ai-matrx/records";
5326
5446
  import { Skeleton as Skeleton14, cn as cn27 } from "@ai-matrx/design-system";
@@ -5341,7 +5461,7 @@ function ChartBlock({ spec, className }) {
5341
5461
  f: spec.filter ?? {},
5342
5462
  l: spec.limit ?? 50
5343
5463
  });
5344
- useEffect18(() => {
5464
+ useEffect19(() => {
5345
5465
  if (needs) return;
5346
5466
  let cancelled = false;
5347
5467
  setRows(null);
@@ -5488,7 +5608,7 @@ function Drawing({ kind, rows, measure }) {
5488
5608
  }
5489
5609
 
5490
5610
  // src/DashboardCanvas.tsx
5491
- import { useCallback as useCallback15, useEffect as useEffect19, useState as useState29 } from "react";
5611
+ import { useCallback as useCallback15, useEffect as useEffect20, useState as useState29 } from "react";
5492
5612
  import { useFields as useFields15, useRecordsClient as useRecordsClient21, useTable as useTable14 } from "@ai-matrx/records/react";
5493
5613
  import { Button as Button26, Skeleton as Skeleton15, cn as cn28 } from "@ai-matrx/design-system";
5494
5614
  import { jsx as jsx31, jsxs as jsxs28 } from "react/jsx-runtime";
@@ -5535,10 +5655,10 @@ function DashboardCanvas({ tableId, seed, activeDashboardId, className }) {
5535
5655
  setError(null);
5536
5656
  setBoards(mine);
5537
5657
  }, [client, boardTableId, tableId, seed]);
5538
- useEffect19(() => {
5658
+ useEffect20(() => {
5539
5659
  void load();
5540
5660
  }, [load]);
5541
- useEffect19(() => {
5661
+ useEffect20(() => {
5542
5662
  if (!boards || boards.length === 0) return;
5543
5663
  const chosen = boards.find((d) => d.id === (activeDashboardId ?? activeId)) ?? boards[0];
5544
5664
  if (chosen.id !== activeId) setActiveId(chosen.id);
@@ -5675,7 +5795,7 @@ function DashboardCanvas({ tableId, seed, activeDashboardId, className }) {
5675
5795
  }
5676
5796
 
5677
5797
  // src/RecordChat.tsx
5678
- import { useEffect as useEffect20, useMemo as useMemo15, useState as useState30 } from "react";
5798
+ import { useEffect as useEffect21, useMemo as useMemo15, useState as useState30 } from "react";
5679
5799
  import { useFields as useFields16, useRecordsClient as useRecordsClient22, useTable as useTable15 } from "@ai-matrx/records/react";
5680
5800
  import { Skeleton as Skeleton16, cn as cn29 } from "@ai-matrx/design-system";
5681
5801
  import { jsx as jsx32, jsxs as jsxs29 } from "react/jsx-runtime";
@@ -5687,7 +5807,7 @@ function RecordChat({ tableId, recordId, className }) {
5687
5807
  const fields = useFields16(tableId);
5688
5808
  const [read, setRead] = useState30(null);
5689
5809
  const [error, setError] = useState30(null);
5690
- useEffect20(() => {
5810
+ useEffect21(() => {
5691
5811
  let cancelled = false;
5692
5812
  setRead(null);
5693
5813
  setError(null);
@@ -5808,7 +5928,7 @@ function noticeText(notice) {
5808
5928
  }
5809
5929
 
5810
5930
  // src/EnrichPanel.tsx
5811
- import { useCallback as useCallback16, useEffect as useEffect21, useMemo as useMemo16, useState as useState31 } from "react";
5931
+ import { useCallback as useCallback16, useEffect as useEffect22, useMemo as useMemo16, useState as useState31 } from "react";
5812
5932
  import { useFields as useFields17, useRecordsClient as useRecordsClient23, useTable as useTable16 } from "@ai-matrx/records/react";
5813
5933
  import { Badge as Badge8, Button as Button27, Skeleton as Skeleton17, cn as cn30 } from "@ai-matrx/design-system";
5814
5934
  import { jsx as jsx33, jsxs as jsxs30 } from "react/jsx-runtime";
@@ -5832,7 +5952,7 @@ function EnrichPanel({ tableId, recordId, className }) {
5832
5952
  setError(null);
5833
5953
  setDocument(answered.data.document);
5834
5954
  }, [client, recordId]);
5835
- useEffect21(() => {
5955
+ useEffect22(() => {
5836
5956
  void load();
5837
5957
  }, [load]);
5838
5958
  const agentFields = useMemo16(() => {
@@ -5971,7 +6091,7 @@ function describe(field, document2) {
5971
6091
  }
5972
6092
 
5973
6093
  // src/ChecklistRunner.tsx
5974
- import { useCallback as useCallback17, useEffect as useEffect22, useMemo as useMemo17, useState as useState32 } from "react";
6094
+ import { useCallback as useCallback17, useEffect as useEffect23, useMemo as useMemo17, useState as useState32 } from "react";
5975
6095
  import { useRecordsClient as useRecordsClient24, useTable as useTable17 } from "@ai-matrx/records/react";
5976
6096
  import { Badge as Badge9, Button as Button28, Skeleton as Skeleton18, cn as cn31 } from "@ai-matrx/design-system";
5977
6097
  import { jsx as jsx34, jsxs as jsxs31 } from "react/jsx-runtime";
@@ -6003,7 +6123,7 @@ function ChecklistRunner({ tableId, seed, className }) {
6003
6123
  const [busy, setBusy] = useState32(false);
6004
6124
  const [note, setNote] = useState32(null);
6005
6125
  const runTableId = home.tableId;
6006
- useEffect22(() => {
6126
+ useEffect23(() => {
6007
6127
  let cancelled = false;
6008
6128
  void client.workStates().then((answered) => {
6009
6129
  if (cancelled || !answered.ok) return;
@@ -6025,10 +6145,10 @@ function ChecklistRunner({ tableId, seed, className }) {
6025
6145
  setRuns(held);
6026
6146
  return held;
6027
6147
  }, [client, runTableId, tableId]);
6028
- useEffect22(() => {
6148
+ useEffect23(() => {
6029
6149
  void loadRuns();
6030
6150
  }, [loadRuns]);
6031
- useEffect22(() => {
6151
+ useEffect23(() => {
6032
6152
  let cancelled = false;
6033
6153
  if (!runTableId || !seed || seed.length === 0 || runs === null) return;
6034
6154
  const missing = seed.filter((spec) => !runs.some((run) => run.name === spec.name));
@@ -6060,7 +6180,7 @@ function ChecklistRunner({ tableId, seed, className }) {
6060
6180
  }
6061
6181
  setTurns(answered.data);
6062
6182
  }, [client, tableId]);
6063
- useEffect22(() => {
6183
+ useEffect23(() => {
6064
6184
  void loadTurns();
6065
6185
  }, [loadTurns, active?.id, active?.stepIds.length]);
6066
6186
  async function move(step2, to) {
@@ -6209,7 +6329,7 @@ function toRun(row) {
6209
6329
  }
6210
6330
 
6211
6331
  // src/BookingSlots.tsx
6212
- import { useCallback as useCallback18, useEffect as useEffect23, useMemo as useMemo18, useState as useState33 } from "react";
6332
+ import { useCallback as useCallback18, useEffect as useEffect24, useMemo as useMemo18, useState as useState33 } from "react";
6213
6333
  import { useRecordsClient as useRecordsClient25, useTable as useTable18 } from "@ai-matrx/records/react";
6214
6334
  import { Button as Button29, Skeleton as Skeleton19, cn as cn32 } from "@ai-matrx/design-system";
6215
6335
  import { jsx as jsx35, jsxs as jsxs32 } from "react/jsx-runtime";
@@ -6231,7 +6351,7 @@ function BookingSlots({
6231
6351
  const [refused, setRefused] = useState33(null);
6232
6352
  const me = client.config.actor.user_id ?? "anonymous";
6233
6353
  const slug = `records_ui_slots_${tableId.replace(/-/g, "").slice(0, 16)}`;
6234
- useEffect23(() => {
6354
+ useEffect24(() => {
6235
6355
  let cancelled = false;
6236
6356
  void (async () => {
6237
6357
  const tables = await client.tableList();
@@ -6284,7 +6404,7 @@ function BookingSlots({
6284
6404
  setError(null);
6285
6405
  setHolds(answered.data);
6286
6406
  }, [client, slotTableId]);
6287
- useEffect23(() => {
6407
+ useEffect24(() => {
6288
6408
  void loadHolds();
6289
6409
  }, [loadHolds]);
6290
6410
  const slots = useMemo18(() => {
@@ -6416,7 +6536,7 @@ function whenText(at) {
6416
6536
  }
6417
6537
 
6418
6538
  // src/CaptureSheet.tsx
6419
- import { useCallback as useCallback19, useEffect as useEffect24, useRef as useRef6, useState as useState34 } from "react";
6539
+ import { useCallback as useCallback19, useEffect as useEffect25, useRef as useRef6, useState as useState34 } from "react";
6420
6540
  import { useFields as useFields18, useRecordsClient as useRecordsClient26, useTable as useTable19 } from "@ai-matrx/records/react";
6421
6541
  import { Button as Button30, Input as Input2, Skeleton as Skeleton20, Textarea as Textarea2, cn as cn33 } from "@ai-matrx/design-system";
6422
6542
  import { Fragment as Fragment10, jsx as jsx36, jsxs as jsxs33 } from "react/jsx-runtime";
@@ -6460,7 +6580,7 @@ function CaptureSheet({
6460
6580
  },
6461
6581
  [host]
6462
6582
  );
6463
- useEffect24(() => {
6583
+ useEffect25(() => {
6464
6584
  let cancelled = false;
6465
6585
  void (async () => {
6466
6586
  const held = host.captureQueue ? await host.captureQueue.load() : [];
@@ -6639,7 +6759,7 @@ function CaptureSheet({
6639
6759
  }
6640
6760
 
6641
6761
  // src/PortalShell.tsx
6642
- import { useCallback as useCallback20, useEffect as useEffect25, useState as useState35 } from "react";
6762
+ import { useCallback as useCallback20, useEffect as useEffect26, useState as useState35 } from "react";
6643
6763
  import { useRecordsClient as useRecordsClient27 } from "@ai-matrx/records/react";
6644
6764
  import { Button as Button31, Skeleton as Skeleton21, cn as cn34 } from "@ai-matrx/design-system";
6645
6765
  import { jsx as jsx37, jsxs as jsxs34 } from "react/jsx-runtime";
@@ -6665,7 +6785,7 @@ function PortalShell({ tableId, form, resourceType = "record", className }) {
6665
6785
  }
6666
6786
  setReach(reached.data.map((row) => row.resource_id));
6667
6787
  }, [client, resourceType]);
6668
- useEffect25(() => {
6788
+ useEffect26(() => {
6669
6789
  void load();
6670
6790
  }, [load]);
6671
6791
  if (error) {
@@ -6725,7 +6845,7 @@ function PortalShell({ tableId, form, resourceType = "record", className }) {
6725
6845
  function PortalRow({ tableId, recordId }) {
6726
6846
  const client = useRecordsClient27();
6727
6847
  const [title, setTitle] = useState35(null);
6728
- useEffect25(() => {
6848
+ useEffect26(() => {
6729
6849
  let cancelled = false;
6730
6850
  void client.recordRead({ record_id: recordId }).then((answered) => {
6731
6851
  if (cancelled) return;
@@ -6745,7 +6865,7 @@ function PortalRow({ tableId, recordId }) {
6745
6865
  }
6746
6866
 
6747
6867
  // src/PublicViewPage.tsx
6748
- import { useCallback as useCallback21, useEffect as useEffect26, useState as useState36 } from "react";
6868
+ import { useCallback as useCallback21, useEffect as useEffect27, useState as useState36 } from "react";
6749
6869
  import { useRecordsClient as useRecordsClient28 } from "@ai-matrx/records/react";
6750
6870
  import { Skeleton as Skeleton22, cn as cn35 } from "@ai-matrx/design-system";
6751
6871
  import { jsx as jsx38, jsxs as jsxs35 } from "react/jsx-runtime";
@@ -6789,7 +6909,7 @@ function PublicViewPage({ slug, className }) {
6789
6909
  }
6790
6910
  setRows([{ id: found.resource_id, document: read.data.document, level: "viewer", hidden: read.data.hidden }]);
6791
6911
  }, [client, slug]);
6792
- useEffect26(() => {
6912
+ useEffect27(() => {
6793
6913
  void load();
6794
6914
  }, [load]);
6795
6915
  if (error) return /* @__PURE__ */ jsx38(RefusalNotice, { error, className });
@@ -6826,7 +6946,7 @@ function PublicRow({ row, fields }) {
6826
6946
  }
6827
6947
 
6828
6948
  // src/EmbedFrame.tsx
6829
- import { useCallback as useCallback22, useEffect as useEffect27, useState as useState37 } from "react";
6949
+ import { useCallback as useCallback22, useEffect as useEffect28, useState as useState37 } from "react";
6830
6950
  import { useRecordsClient as useRecordsClient29 } from "@ai-matrx/records/react";
6831
6951
  import { Button as Button32, Input as Input3, Skeleton as Skeleton23, Textarea as Textarea3, cn as cn36 } from "@ai-matrx/design-system";
6832
6952
  import { useTable as useTable20 } from "@ai-matrx/records/react";
@@ -6939,7 +7059,7 @@ function useEmbedHandshake(args) {
6939
7059
  const [loading, setLoading] = useState37(true);
6940
7060
  const origin = args.origin ?? (typeof location === "undefined" ? "" : location.origin);
6941
7061
  const { secret, requiredMode } = args;
6942
- useEffect27(() => {
7062
+ useEffect28(() => {
6943
7063
  let cancelled = false;
6944
7064
  setLoading(true);
6945
7065
  setError(null);
@@ -7447,6 +7567,8 @@ export {
7447
7567
  formDocument,
7448
7568
  formFromRecord,
7449
7569
  groupLabel,
7570
+ hintForAPerson,
7571
+ hintIsMachineIdentity,
7450
7572
  humanize,
7451
7573
  idsOf,
7452
7574
  isId,
@@ -7479,6 +7601,7 @@ export {
7479
7601
  useViewRecords,
7480
7602
  viewDocument,
7481
7603
  viewFromRecord,
7482
- viewPatchDocument
7604
+ viewPatchDocument,
7605
+ whatIsMissing
7483
7606
  };
7484
7607
  //# sourceMappingURL=index.js.map