@ai-matrx/records-ui 0.103.0 → 0.103.2

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/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # Changelog — @ai-matrx/records-ui
2
2
 
3
+ ## 0.103.2
4
+
5
+ - Republish of 0.103.1 (its publish stopped on two suites fixed on main after its tag: the form runner's date placeholder and a formula clause). No change of its own.
6
+
7
+ **Consumer action:** none.
8
+
9
+ ## 0.103.1
10
+
11
+ - `ConditionGroup` takes `maxDepth`: how many levels of groups a person may add below the whole condition (Notion's advanced filter is 2). A deeper saved question still draws; only "Add a group" stops. Left out, no limit (unchanged). Test: `src/condition-group.test.tsx`.
12
+
13
+ **Consumer action:** none.
14
+
3
15
  ## 0.103.0
4
16
 
5
17
  - **Notion's calculation row on the embedded grid** (Spaces N6): every column's footer cell says "Calculate" on hover and offers Notion's list in Notion's words — Count all · Count unique values · Count empty · Count not empty · Percent empty · Percent not empty · Sum · Average · Median · Min · Max · Range — narrowed to what the column can mean and to the store's `custom.agg_operations`. The pick is reported through `onPresentationChange` as `summaries` (unchanged door). The band stays plain until one is picked.
package/dist/index.cjs CHANGED
@@ -1696,6 +1696,7 @@ function PickChips({ chips }) {
1696
1696
  ) });
1697
1697
  }
1698
1698
  function PickOrAdd({
1699
+ triggerSize = "field",
1699
1700
  triggerLabel,
1700
1701
  triggerText,
1701
1702
  triggerId,
@@ -1754,7 +1755,7 @@ function PickOrAdd({
1754
1755
  }
1755
1756
  ) : null;
1756
1757
  return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_design_system5.Popover, { open, onOpenChange, children: [
1757
- anchored ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_design_system5.PopoverAnchor, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "flex min-h-6 w-full min-w-0 flex-wrap items-center gap-1", "aria-label": triggerLabel, children }) }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_design_system5.PopoverTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_design_system5.Button, { id: triggerId, type: "button", size: "sm", variant: "outline", "aria-label": triggerLabel, children: triggerText }) }),
1758
+ anchored ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_design_system5.PopoverAnchor, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { "data-pick-anchor": "", className: "flex min-h-6 w-full min-w-0 flex-wrap items-center gap-1", "aria-label": triggerLabel, children }) }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_design_system5.PopoverTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_design_system5.Button, { id: triggerId, type: "button", size: "sm", variant: "outline", className: triggerSize === "field" ? "h-9" : void 0, "aria-label": triggerLabel, children: triggerText }) }),
1758
1759
  /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1759
1760
  import_design_system5.PopoverContent,
1760
1761
  {
@@ -4133,6 +4134,7 @@ function PickOne({
4133
4134
  return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: (0, import_design_system12.cn)("inline-flex min-w-0", className), "data-records-pick-one": "search", "data-pick-value": value, ...attrs ?? {}, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
4134
4135
  PickOrAdd,
4135
4136
  {
4137
+ triggerSize: "compact",
4136
4138
  triggerLabel: label,
4137
4139
  triggerText: current?.label ?? placeholder ?? "",
4138
4140
  triggerId: id,
@@ -5218,7 +5220,7 @@ function TypedWordsControl({ field: field2, value, onChange, disabled, id }) {
5218
5220
  disabled,
5219
5221
  autoComplete: "off",
5220
5222
  "aria-invalid": refusal4 ? true : void 0,
5221
- placeholder: helper === "time" ? "2:30 PM" : helper === "datetime" ? `${(0, import_records12.typedDateText)("2026-10-03")} 2:30 PM` : helper ? (0, import_records12.typedDateText)("2026-10-03") : void 0,
5223
+ placeholder: helper ? typedDatePlaceholder(helper) : void 0,
5222
5224
  onChange: (e) => retype(e.target.value),
5223
5225
  onBlur: () => {
5224
5226
  if (!refusal4) return;
@@ -5232,6 +5234,12 @@ function TypedWordsControl({ field: field2, value, onChange, disabled, id }) {
5232
5234
  refusal4 ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { "data-records-typed-refusal": "", "data-records-cell-note": "", role: "status", title: why, className: "text-[11px] leading-snug text-amber-700 dark:text-amber-400", children: refusal4 }) : null
5233
5235
  ] });
5234
5236
  }
5237
+ function typedDatePlaceholder(helper) {
5238
+ const time = "h:mm AM";
5239
+ if (helper === "time") return time;
5240
+ const pattern = (0, import_records12.typedDateText)("2026-11-22").replace("2026", "YYYY").replace("11", "MM").replace("22", "DD");
5241
+ return helper === "datetime" ? `${pattern} ${time}` : pattern;
5242
+ }
5235
5243
  function CheckboxControl({ field: field2, value, onChange, disabled, id }) {
5236
5244
  const unanswered = value === null || value === void 0;
5237
5245
  return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex items-center gap-2", children: [
@@ -6885,16 +6893,16 @@ function cellMarkSentence(field2, document2, value) {
6885
6893
  const kept = keptAsideFor(document2, field2.key);
6886
6894
  return kept ? keptAsideReason(field2, kept) : null;
6887
6895
  }
6888
- var WORKED_OUT_ERROR_WORD = "#ERROR";
6889
- function WorkedOutError({ field: field2, sentence: sentence2 }) {
6896
+ function WorkedOutError({ field: field2, sentence: sentence2, floated = false }) {
6890
6897
  return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
6891
6898
  "span",
6892
6899
  {
6893
6900
  "data-records-worked-out-error": field2.key,
6901
+ role: "img",
6894
6902
  title: sentence2,
6895
- "aria-label": `Error. ${sentence2}`,
6896
- className: "cursor-help font-medium text-amber-700 dark:text-amber-300",
6897
- children: WORKED_OUT_ERROR_WORD
6903
+ "aria-label": sentence2,
6904
+ className: (floated ? "absolute right-1 top-1/2 z-10 flex -translate-y-1/2 " : "inline-flex align-middle ") + "h-4 w-4 shrink-0 cursor-help items-center justify-center rounded-full bg-destructive text-[10px] font-bold leading-none text-destructive-foreground",
6905
+ children: "!"
6898
6906
  }
6899
6907
  );
6900
6908
  }
@@ -7123,7 +7131,7 @@ function RecordValue({
7123
7131
  if (withheld && (value === null || value === void 0)) return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(WithheldValue, { field: field2, notice: withheld });
7124
7132
  if (value === null || value === void 0) {
7125
7133
  const failed = workedOutErrorFor(document2, field2.key);
7126
- if (failed) return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(WorkedOutError, { field: field2, sentence: failed });
7134
+ if (failed) return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(WorkedOutError, { field: field2, sentence: failed, floated: Boolean(inCell) });
7127
7135
  }
7128
7136
  if (inCell) {
7129
7137
  const kept = keptAsideFor(document2, field2.key);
@@ -7746,6 +7754,22 @@ function GridCell({
7746
7754
  const open = Boolean(
7747
7755
  editing && editing.editing?.rowId === row.id && editing.editing.key === field2.key
7748
7756
  );
7757
+ const closedControl = (0, import_react38.useRef)(null);
7758
+ const wasOpen = (0, import_react38.useRef)(false);
7759
+ (0, import_react38.useEffect)(() => {
7760
+ if (open) {
7761
+ wasOpen.current = true;
7762
+ return;
7763
+ }
7764
+ if (!wasOpen.current) return;
7765
+ wasOpen.current = false;
7766
+ if (!clickOpens) return;
7767
+ const frame = requestAnimationFrame(() => {
7768
+ const active = document.activeElement;
7769
+ if (!active || active === document.body) closedControl.current?.focus({ preventScroll: true });
7770
+ });
7771
+ return () => cancelAnimationFrame(frame);
7772
+ }, [open, clickOpens]);
7749
7773
  const wasActive = (0, import_react38.useRef)(false);
7750
7774
  const singlePress = (0, import_react38.useRef)(null);
7751
7775
  const cancelSinglePress = () => {
@@ -7889,9 +7913,13 @@ function GridCell({
7889
7913
  /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
7890
7914
  "span",
7891
7915
  {
7916
+ ref: closedControl,
7892
7917
  role: editable ? "button" : void 0,
7893
7918
  "aria-label": editable ? `Edit ${fieldName(field2)}` : void 0,
7894
- title: cellMark ? cellMark : editable ? `${fieldName(field2)} \u2014 double-click or press Enter to edit` : fieldIsEditable(field2) ? (
7919
+ title: cellMark ? cellMark : editable ? (
7920
+ // The words match the gesture (WALK3-FIX): one click opens a record-page field.
7921
+ clickOpens ? `${fieldName(field2)} \u2014 click to edit` : `${fieldName(field2)} \u2014 double-click or press Enter to edit`
7922
+ ) : fieldIsEditable(field2) ? (
7895
7923
  // NOT A DEAD CONTROL AND NOT A SILENT ONE: the cell is plain
7896
7924
  // text, and hovering it says exactly why, in the level's own
7897
7925
  // words. The editor was never offered, so nothing is refused
@@ -9151,6 +9179,7 @@ function ChoiceValue({ field: field2, value, onChange }) {
9151
9179
  return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: "inline-flex min-w-0", "data-records-condition-choice": "", "data-pick-value": value, children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
9152
9180
  PickOrAdd,
9153
9181
  {
9182
+ triggerSize: "compact",
9154
9183
  triggerLabel: `Choice for ${field2.label || field2.key}`,
9155
9184
  triggerText: value || "Choose",
9156
9185
  open,
@@ -9374,6 +9403,7 @@ function ConditionGroup({
9374
9403
  onChange,
9375
9404
  emptyLabel = "always",
9376
9405
  allowNesting = true,
9406
+ maxDepth,
9377
9407
  depth = 0,
9378
9408
  onRemove,
9379
9409
  className
@@ -9444,6 +9474,7 @@ function ConditionGroup({
9444
9474
  onChange: (next) => replaceAt(index, next),
9445
9475
  onRemove: () => replaceAt(index, null),
9446
9476
  emptyLabel,
9477
+ maxDepth,
9447
9478
  depth: depth + 1
9448
9479
  }
9449
9480
  ) }, key);
@@ -9487,7 +9518,7 @@ function ConditionGroup({
9487
9518
  children: "Add another condition"
9488
9519
  }
9489
9520
  ),
9490
- allowNesting ? /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
9521
+ allowNesting && (maxDepth === void 0 || depth < maxDepth) ? /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
9491
9522
  import_design_system29.Button,
9492
9523
  {
9493
9524
  size: "sm",
@@ -11137,6 +11168,7 @@ function FieldEditor(props) {
11137
11168
  const [formulaWritten, setFormulaWritten] = (0, import_react59.useState)(() => startingFormulaText.trim() !== "" || Boolean(field2));
11138
11169
  const [formulaOk, setFormulaOk] = (0, import_react59.useState)(null);
11139
11170
  const [askingToRemove, setAskingToRemove] = (0, import_react59.useState)(false);
11171
+ const [attempted, setAttempted] = (0, import_react59.useState)(false);
11140
11172
  const [showsAs, setShowsAs] = (0, import_react59.useState)(() => field2 ? startingChoice(field2) : { format: null, retype: null });
11141
11173
  const [backfillError, setBackfillError] = (0, import_react59.useState)(null);
11142
11174
  const [notLanded, setNotLanded] = (0, import_react59.useState)(false);
@@ -11186,6 +11218,8 @@ function FieldEditor(props) {
11186
11218
  const missing = editing ? formulaMissing ?? (listsChoices ? whatTheChoiceListIsMissing(choiceList) : null) : formulaMissing ?? (listsChoices ? whatTheChoiceListIsMissing(newChoices) : null) ?? (type === "formula" && formulaWritten ? null : whatIsMissing(
11187
11219
  type === "count" ? { type: "rollup", label, options, via, pick, agg: "count", of, relations: totalSources, formulaFields, relationTarget, allowedTypes, noun } : { type, label, options, via, pick, agg, of, relations: sourcesFor(type), formulaFields, relationTarget, allowedTypes, noun }
11188
11220
  ));
11221
+ const missingShown = missing && (attempted || formulaMissing) ? missing : null;
11222
+ const removeConsequence = field2 ? `${archiveConfirmSentence(`the field \u201C${fieldName(field2)}\u201D`)} Its values stay in every record and come back with it.${linkReaders.length > 0 ? ` ${readersSentence(linkReaders.length, farTable.data?.name ?? null)}` : ""}` : "";
11189
11223
  const save = async () => {
11190
11224
  if (missing) return;
11191
11225
  if (editing && field2) {
@@ -11333,8 +11367,8 @@ function FieldEditor(props) {
11333
11367
  const remove = async () => {
11334
11368
  if (!field2) return;
11335
11369
  const gone = await tableMutation.retire({ field_id: field2.id });
11336
- setAskingToRemove(false);
11337
11370
  if (gone) (onRemoved ?? onSaved)?.();
11371
+ else setAskingToRemove(false);
11338
11372
  };
11339
11373
  return /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { className: (0, import_design_system34.cn)("flex min-w-0 flex-col gap-3 text-xs", className), "data-records-field-editor": "", children: [
11340
11374
  /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { className: "flex items-center gap-2", children: [
@@ -11351,7 +11385,12 @@ function FieldEditor(props) {
11351
11385
  autoFocus: true,
11352
11386
  "aria-label": "Field name",
11353
11387
  placeholder: "Day rate",
11354
- onChange: (e) => setLabel(e.target.value)
11388
+ onChange: (e) => setLabel(e.target.value),
11389
+ onBlur: (e) => {
11390
+ const to = e.relatedTarget;
11391
+ if (to?.textContent === "Cancel") return;
11392
+ setAttempted(true);
11393
+ }
11355
11394
  }
11356
11395
  )
11357
11396
  ] }),
@@ -11639,7 +11678,6 @@ function FieldEditor(props) {
11639
11678
  ] }),
11640
11679
  editing && field2 && !onEntity && rulesChanged(savedRules, editedRules) ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(FieldRulePreview, { field: field2, tableId, rules: editedRules }) : null,
11641
11680
  mutation.error ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(RefusalNotice, { error: mutation.error }) : null,
11642
- missing ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("p", { id: "field-editor-missing", className: "text-[11px] text-muted-foreground", "data-field-editor-missing": "", children: missing }) : null,
11643
11681
  notLanded ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
11644
11682
  RefusalNotice,
11645
11683
  {
@@ -11684,27 +11722,61 @@ function FieldEditor(props) {
11684
11722
  /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("p", { className: "text-[11px]", children: "The column is made. The records already in the table were not numbered:" }),
11685
11723
  /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(RefusalNotice, { error: backfillError })
11686
11724
  ] }) : null,
11687
- /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { className: "flex flex-wrap items-center gap-2", children: [
11725
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { className: "flex min-w-0 flex-nowrap items-center gap-2", "data-field-editor-footer": "", children: [
11688
11726
  /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
11689
11727
  import_design_system34.Button,
11690
11728
  {
11691
11729
  size: "sm",
11692
- disabled: mutation.saving || missing !== null,
11693
- ...missing ? { "aria-describedby": "field-editor-missing" } : {},
11694
- onClick: () => void save(),
11730
+ disabled: mutation.saving || Boolean(formulaMissing),
11731
+ ...missingShown ? { "aria-describedby": "field-editor-missing" } : {},
11732
+ onClick: () => {
11733
+ if (missing) {
11734
+ setAttempted(true);
11735
+ return;
11736
+ }
11737
+ void save();
11738
+ },
11695
11739
  children: mutation.saving ? "Saving\u2026" : field2 ? EDIT_FIELD_SAVE_LABEL : onEntity ? ADD_STANDARD_FIELD_SAVE_LABEL : ADD_FIELD_SAVE_LABEL
11696
11740
  }
11697
11741
  ),
11698
11742
  onCancel ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_design_system34.Button, { size: "sm", variant: "ghost", onClick: onCancel, children: "Cancel" }) : null,
11699
- field2 ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("div", { className: "ml-auto flex items-center gap-2", children: askingToRemove ? /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_jsx_runtime40.Fragment, { children: [
11700
- /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("span", { className: "text-destructive", children: [
11701
- archiveConfirmSentence(`the field \u201C${fieldName(field2)}\u201D`),
11702
- " Its values stay in every record and come back with it.",
11703
- linkReaders.length > 0 ? ` ${readersSentence(linkReaders.length, farTable.data?.name ?? null)}` : ""
11704
- ] }),
11705
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_design_system34.Button, { size: "sm", variant: "destructive", disabled: mutation.saving, onClick: () => void remove(), children: "Remove it" }),
11706
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_design_system34.Button, { size: "sm", variant: "ghost", onClick: () => setAskingToRemove(false), children: "Keep it" })
11707
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_design_system34.Button, { size: "sm", variant: "outline", onClick: () => setAskingToRemove(true), children: "Remove this field" }) }) : null
11743
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
11744
+ "span",
11745
+ {
11746
+ id: "field-editor-missing",
11747
+ "data-field-editor-missing": missingShown ? "" : void 0,
11748
+ title: missingShown ?? void 0,
11749
+ className: "min-w-0 flex-1 truncate text-[11px] text-muted-foreground",
11750
+ children: missingShown
11751
+ }
11752
+ ),
11753
+ field2 ? (
11754
+ /* WALK3-FIX (2026-10-07): the confirm appears IN PLACE. Both states share one grid cell,
11755
+ the idle one drawn invisibly under the asking one and the other way round, so the
11756
+ footer is always as wide and as tall as its larger state: opening the confirm moved
11757
+ the dialog card 0.2278 and closing it 0.1303. The consequence is the tooltip of
11758
+ "Remove it" (interface-text: the slot holds a label, never a paragraph). */
11759
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { className: "ml-auto grid shrink-0 items-center", "data-field-editor-remove": askingToRemove ? "asking" : "idle", children: [
11760
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("div", { className: (0, import_design_system34.cn)("col-start-1 row-start-1 flex items-center justify-end gap-2", askingToRemove && "invisible"), "aria-hidden": askingToRemove || void 0, children: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_design_system34.Button, { size: "sm", variant: "outline", tabIndex: askingToRemove ? -1 : void 0, onClick: () => setAskingToRemove(true), children: "Remove this field" }) }),
11761
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { className: (0, import_design_system34.cn)("col-start-1 row-start-1 flex items-center justify-end gap-2", !askingToRemove && "invisible"), "aria-hidden": !askingToRemove || void 0, children: [
11762
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("span", { className: "whitespace-nowrap text-destructive", "data-field-editor-remove-ask": "", children: "Archive this field?" }),
11763
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
11764
+ import_design_system34.Button,
11765
+ {
11766
+ size: "sm",
11767
+ variant: "destructive",
11768
+ tabIndex: askingToRemove ? void 0 : -1,
11769
+ disabled: mutation.saving,
11770
+ title: removeConsequence,
11771
+ "aria-description": removeConsequence,
11772
+ onClick: () => void remove(),
11773
+ children: "Remove it"
11774
+ }
11775
+ ),
11776
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_design_system34.Button, { size: "sm", variant: "ghost", tabIndex: askingToRemove ? void 0 : -1, onClick: () => setAskingToRemove(false), children: "Keep it" })
11777
+ ] })
11778
+ ] })
11779
+ ) : null
11708
11780
  ] })
11709
11781
  ] });
11710
11782
  }
@@ -14527,6 +14599,13 @@ function useMergedGridControls(args) {
14527
14599
  const tableVersion = useTableVersion(client, tableId, table);
14528
14600
  const mutation = (0, import_react81.useFieldMutation)();
14529
14601
  const [dialog, setDialog] = (0, import_react80.useState)(null);
14602
+ const lastColumnDialog = (0, import_react80.useRef)(null);
14603
+ const insertEpoch = (0, import_react80.useRef)(0);
14604
+ if (dialog?.kind === "settings" || dialog?.kind === "insert") {
14605
+ if (dialog !== lastColumnDialog.current) insertEpoch.current += 1;
14606
+ lastColumnDialog.current = dialog;
14607
+ }
14608
+ const columnDialog = dialog?.kind === "settings" || dialog?.kind === "insert" ? dialog : lastColumnDialog.current;
14530
14609
  const [renameTo, setRenameTo] = (0, import_react80.useState)("");
14531
14610
  const [deleteBusy, setDeleteBusy] = (0, import_react80.useState)(false);
14532
14611
  const [labelling, setLabelling] = (0, import_react80.useState)(false);
@@ -15016,27 +15095,27 @@ function useMergedGridControls(args) {
15016
15095
  },
15017
15096
  children: /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)(import_design_system46.DialogContent, { className: "max-h-[85vh] overflow-y-auto sm:max-w-2xl", children: [
15018
15097
  /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)(import_design_system46.DialogHeader, { children: [
15019
- /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(import_design_system46.DialogTitle, { children: dialog?.kind === "settings" ? `Column settings \xB7 ${fieldName(dialog.field)}` : dialog?.kind === "insert" && dialog.anchor ? `Add column ${dialog.side === "left" ? "left" : "right"} of ${fieldOf2(dialog.anchor) ? fieldName(fieldOf2(dialog.anchor)) : dialog.anchor}` : ADD_FIELD_OPEN_LABEL }),
15098
+ /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(import_design_system46.DialogTitle, { children: columnDialog?.kind === "settings" ? `Column settings \xB7 ${fieldName(columnDialog.field)}` : columnDialog?.kind === "insert" && columnDialog.anchor ? `Add column ${columnDialog.side === "left" ? "left" : "right"} of ${fieldOf2(columnDialog.anchor) ? fieldName(fieldOf2(columnDialog.anchor)) : columnDialog.anchor}` : ADD_FIELD_OPEN_LABEL }),
15020
15099
  /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(import_design_system46.DialogDescription, { className: "sr-only", children: "Name, type and look" })
15021
15100
  ] }),
15022
- dialog?.kind === "settings" ? /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
15101
+ columnDialog?.kind === "settings" ? /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
15023
15102
  FieldEditor,
15024
15103
  {
15025
15104
  tableId,
15026
- field: dialog.field,
15105
+ field: columnDialog.field,
15027
15106
  onSaved: () => {
15028
- notify?.success(`${fieldName(dialog.field)} is saved.`);
15107
+ notify?.success(`${fieldName(columnDialog.field)} is saved.`);
15029
15108
  close();
15030
15109
  },
15031
15110
  onRemoved: () => {
15032
- if (args.onColumnRetired) args.onColumnRetired(dialog.field);
15033
- else notify?.success(`${fieldName(dialog.field)} is gone from the table. It can be brought back from Archived.`);
15111
+ if (args.onColumnRetired) args.onColumnRetired(columnDialog.field);
15112
+ else notify?.success(`${fieldName(columnDialog.field)} is gone from the table. It can be brought back from Archived.`);
15034
15113
  close();
15035
15114
  },
15036
15115
  onCancel: close
15037
15116
  },
15038
- dialog.field.id
15039
- ) : dialog?.kind === "insert" ? /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
15117
+ `${columnDialog.field.id}-${insertEpoch.current}`
15118
+ ) : columnDialog?.kind === "insert" ? /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
15040
15119
  FieldEditor,
15041
15120
  {
15042
15121
  tableId,
@@ -15045,10 +15124,11 @@ function useMergedGridControls(args) {
15045
15124
  close();
15046
15125
  },
15047
15126
  onSaved: () => {
15048
- pendingInsert.current = { before: new Set(fieldKeys), anchor: dialog.anchor, side: dialog.side };
15127
+ pendingInsert.current = { before: new Set(fieldKeys), anchor: columnDialog.anchor, side: columnDialog.side };
15049
15128
  close();
15050
15129
  }
15051
- }
15130
+ },
15131
+ `insert-${insertEpoch.current}`
15052
15132
  ) : null
15053
15133
  ] })
15054
15134
  }
@@ -20551,7 +20631,7 @@ function UndoRedoPair({ face, onUndo, onRedo }) {
20551
20631
  )
20552
20632
  ] });
20553
20633
  }
20554
- function UndoNotice({ undo }) {
20634
+ function UndoNotice({ undo, placement = "frame" }) {
20555
20635
  const notice = undo.notice;
20556
20636
  const dismiss = undo.dismiss;
20557
20637
  (0, import_react105.useEffect)(() => {
@@ -20565,7 +20645,8 @@ function UndoNotice({ undo }) {
20565
20645
  {
20566
20646
  role: "status",
20567
20647
  "data-records-undo-notice": notice.refusal ? "refused" : "done",
20568
- className: "pointer-events-auto absolute bottom-3 left-1/2 z-30 flex max-w-[min(36rem,calc(100%-2rem))] -translate-x-1/2 items-center gap-2 rounded-md border border-border bg-popover px-3 py-1.5 text-xs text-popover-foreground shadow-lg",
20648
+ "data-records-undo-placement": placement,
20649
+ className: `pointer-events-auto ${placement === "viewport" ? "fixed bottom-4 z-50" : "absolute bottom-3 z-30"} left-1/2 flex max-w-[min(36rem,calc(100%-2rem))] -translate-x-1/2 items-center gap-2 rounded-md border border-border bg-popover px-3 py-1.5 text-xs text-popover-foreground shadow-lg`,
20569
20650
  children: [
20570
20651
  /* @__PURE__ */ (0, import_jsx_runtime66.jsx)("span", { children: notice.says }),
20571
20652
  notice.canUndo && undo.canUndo ? /* @__PURE__ */ (0, import_jsx_runtime66.jsx)(import_design_system57.Button, { size: "sm", variant: "outline", className: "ml-auto h-6", "data-records-undo-now": "", onClick: () => void undo.undo(), children: "Undo" }) : null,
@@ -21910,6 +21991,11 @@ function GridBody({
21910
21991
  page: query.page
21911
21992
  });
21912
21993
  }, [reportQuery, query.sort, query.columnFilters, query.search, query.page, openingKey]);
21994
+ const liveKeys = (0, import_react113.useMemo)(() => fields.data ? new Set(fields.data.map((f) => f.key)) : null, [fields.data]);
21995
+ (0, import_react113.useEffect)(() => {
21996
+ if (!liveKeys) return;
21997
+ setQuery((held) => withoutGoneColumns(held, liveKeys, openingSort));
21998
+ }, [liveKeys]);
21913
21999
  const resetEpoch = address?.resetEpoch ?? 0;
21914
22000
  const resetSeen = (0, import_react113.useRef)(resetEpoch);
21915
22001
  (0, import_react113.useEffect)(() => {
@@ -21963,7 +22049,9 @@ function GridBody({
21963
22049
  page: classicInCard ? 0 : page,
21964
22050
  ...readFilter ? { filter: readFilter } : {}
21965
22051
  });
21966
- const pageAsked = !storePaged || Boolean(fields.data && !table.loading);
22052
+ const pageAskedFor = (0, import_react113.useRef)(null);
22053
+ if (fields.data && !table.loading) pageAskedFor.current = tableId;
22054
+ const pageAsked = !storePaged || Boolean(fields.data) && pageAskedFor.current === tableId;
21967
22055
  const serverRead = (0, import_react114.useRecordPage)(storePaged && pageAsked ? tableId : null, {
21968
22056
  pageSize: query.pageSize,
21969
22057
  page: Math.max(0, query.page - 1),
@@ -23415,7 +23503,7 @@ function GridBody({
23415
23503
  }, [rowsAnswered, rowMemoryKey, rows.length, embedded, tableId]);
23416
23504
  if (table.error) return /* @__PURE__ */ (0, import_jsx_runtime70.jsx)(RefusalNotice, { error: table.error, className });
23417
23505
  if (fields.error) return /* @__PURE__ */ (0, import_jsx_runtime70.jsx)(RefusalNotice, { error: fields.error, className });
23418
- const nothingToShowYet = table.loading || fields.loading || !rights.known || records.loading && rows.length === 0 || // The page read has neither answered nor refused: nothing has been said yet, so nothing is empty.
23506
+ const nothingToShowYet = table.loading && !table.data || fields.loading && !fields.data || !rights.known || records.loading && rows.length === 0 || // The page read has neither answered nor refused: nothing has been said yet, so nothing is empty.
23419
23507
  !records.data && !records.error || countedButNotReturned || manual && hand.loading && rows.length === 0;
23420
23508
  const refreshing = !nothingToShowYet && (records.loading || manual && hand.loading);
23421
23509
  const noColumnsYet = !nothingToShowYet && (fields.data ?? []).length === 0;
@@ -23870,6 +23958,15 @@ function withHeaderMenus(columns, menuFor, filterFor) {
23870
23958
  return filter ? { ...withMenu, ...filter } : withMenu;
23871
23959
  });
23872
23960
  }
23961
+ function withoutGoneColumns(query, liveKeys, openingSort) {
23962
+ const sortGone = Boolean(query.sort && !liveKeys.has(query.sort.id));
23963
+ const goneFilters = Object.keys(query.columnFilters ?? {}).filter((key) => !liveKeys.has(key));
23964
+ if (!sortGone && goneFilters.length === 0) return query;
23965
+ const columnFilters = { ...query.columnFilters };
23966
+ for (const key of goneFilters) delete columnFilters[key];
23967
+ const fallback = openingSort && liveKeys.has(openingSort.id) ? openingSort : null;
23968
+ return { ...query, sort: sortGone ? fallback : query.sort, columnFilters, page: 1 };
23969
+ }
23873
23970
 
23874
23971
  // src/RecordsTableCard.tsx
23875
23972
  var import_data_table13 = require("@ai-matrx/design-system/data-table");
@@ -29027,7 +29124,7 @@ function RecordFieldsInPlace({
29027
29124
  /* @__PURE__ */ (0, import_jsx_runtime94.jsx)(
29028
29125
  "dd",
29029
29126
  {
29030
- className: "min-w-0 rounded hover:bg-muted/50 [&_[data-matrx-cell-editor]_input:not([type=checkbox]):not([type=radio])]:!h-7",
29127
+ className: "min-w-0 rounded hover:bg-muted/50 [&_[data-matrx-cell-editor]_input:not([type=checkbox]):not([type=radio])]:!h-7 [&_[data-pick-anchor]]:!w-fit",
29031
29128
  "data-records-in-place-field": field2.key,
29032
29129
  children: /* @__PURE__ */ (0, import_jsx_runtime94.jsx)(
29033
29130
  GridCell,
@@ -29045,7 +29142,7 @@ function RecordFieldsInPlace({
29045
29142
  )
29046
29143
  ] }, field2.id);
29047
29144
  }) }),
29048
- /* @__PURE__ */ (0, import_jsx_runtime94.jsx)(UndoNotice, { undo })
29145
+ /* @__PURE__ */ (0, import_jsx_runtime94.jsx)(UndoNotice, { undo, placement: "viewport" })
29049
29146
  ]
29050
29147
  }
29051
29148
  );
@@ -29271,6 +29368,7 @@ function Peek({ tableId, recordId, onClose, className, onPage = false }) {
29271
29368
  if (record.error) return /* @__PURE__ */ (0, import_jsx_runtime97.jsx)(RefusalNotice, { error: record.error, className });
29272
29369
  if (fields.error) return /* @__PURE__ */ (0, import_jsx_runtime97.jsx)(RefusalNotice, { error: fields.error, className });
29273
29370
  const document2 = record.data?.document;
29371
+ const computedShown = computedLinesWorthShowing(record.data?.computed, document2);
29274
29372
  const title = document2 ? recordNameIn(table.data, document2) : "";
29275
29373
  const bodyKey = onPage ? bodyFieldOf(fields.data ?? [])?.key : void 0;
29276
29374
  const shownFields = (fields.data ?? []).filter((field2) => !onPage || !drawnBelowOnPage(field2, bodyKey));
@@ -29370,15 +29468,20 @@ function Peek({ tableId, recordId, onClose, className, onPage = false }) {
29370
29468
  /* @__PURE__ */ (0, import_jsx_runtime97.jsx)(import_design_system85.Separator, {}),
29371
29469
  /* @__PURE__ */ (0, import_jsx_runtime97.jsx)("p", { className: "text-muted-foreground", children: "Nothing here can say which columns the system worked out for itself: that part of the record is not readable from your seat. Everything above is the record as it stands." })
29372
29470
  ] }) : null,
29373
- record.data?.computed?.length ? /* @__PURE__ */ (0, import_jsx_runtime97.jsxs)(import_jsx_runtime97.Fragment, { children: [
29471
+ computedShown.length ? /* @__PURE__ */ (0, import_jsx_runtime97.jsxs)(import_jsx_runtime97.Fragment, { children: [
29374
29472
  /* @__PURE__ */ (0, import_jsx_runtime97.jsx)(import_design_system85.Separator, {}),
29375
29473
  /* @__PURE__ */ (0, import_jsx_runtime97.jsxs)("div", { className: "flex flex-col gap-1", children: [
29376
29474
  /* @__PURE__ */ (0, import_jsx_runtime97.jsx)("span", { className: "font-medium", children: "Worked out by the system" }),
29377
- record.data.computed.map((row, i) => /* @__PURE__ */ (0, import_jsx_runtime97.jsx)("p", { className: "text-muted-foreground", children: computedInPlainWords(row, (fields.data ?? []).find((f) => f.key === row.field_key)) }, i))
29475
+ computedShown.map((row, i) => /* @__PURE__ */ (0, import_jsx_runtime97.jsx)("p", { className: "text-muted-foreground", children: computedInPlainWords(row, (fields.data ?? []).find((f) => f.key === row.field_key)) }, i))
29378
29476
  ] })
29379
29477
  ] }) : null
29380
29478
  ] });
29381
29479
  }
29480
+ function computedLinesWorthShowing(rows, document2) {
29481
+ return (rows ?? []).filter(
29482
+ (row) => !((row.value === null || row.value === void 0 || row.value === "") && workedOutErrorFor(document2, row.field_key))
29483
+ );
29484
+ }
29382
29485
  function computedInPlainWords(row, field2) {
29383
29486
  const name = field2 ? fieldName(field2) : (0, import_text_case12.humanizeIdentifier)(row.field_key);
29384
29487
  const shown4 = row.value === null || row.value === void 0 || row.value === "" ? "nothing" : field2 ? scalarText(field2, row.value) : String(row.value);
@@ -36474,7 +36577,8 @@ function HistoryPanel({ tableId, recordId, onAskAboutField, className }) {
36474
36577
  ref: listRef,
36475
36578
  role: "listbox",
36476
36579
  "aria-label": "Versions of this record, newest first",
36477
- className: "flex min-h-0 flex-col divide-y overflow-y-auto rounded border",
36580
+ "data-records-history-list": "",
36581
+ className: "flex max-h-[9.25rem] min-h-0 flex-col divide-y overflow-y-auto rounded border",
36478
36582
  onKeyDown: (event) => {
36479
36583
  const at = entries.findIndex((e) => e.version === open);
36480
36584
  if (event.key === "ArrowDown" || event.key === "ArrowUp") {
@@ -47796,7 +47900,7 @@ function TablePage({
47796
47900
  if (hostFromLink || opened.layout || layoutChosen) return;
47797
47901
  setHostLayout(face.hostLayout);
47798
47902
  }, [face.kind, face.kind === "designated" ? face.hostLayout : null, layoutChosen]);
47799
- const faceUnknown = !hostFromLink && !opened.layout && (views === void 0 || Array.isArray(views) && views.length > 0 && (view === null || looks === void 0));
47903
+ const faceUnknown = !hostFromLink && !opened.layout && (views === void 0 || looks === void 0 || Array.isArray(views) && views.length > 0 && view === null);
47800
47904
  const shownLayout = layoutFromLink ?? shown4?.layout ?? "grid";
47801
47905
  const shownFilter = filter ?? (shown4?.where ? shown4.where : null) ?? (shown4?.filters && Object.keys(shown4.filters).length > 0 ? shown4.filters : null);
47802
47906
  const press = (pressed) => {