@ai-matrx/records-ui 0.57.0 → 0.58.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
@@ -1931,7 +1931,7 @@ function OpenCell({
1931
1931
  }
1932
1932
 
1933
1933
  // src/Grid.tsx
1934
- import { useCallback as useCallback6, useMemo as useMemo8, useState as useState9 } from "react";
1934
+ import { useCallback as useCallback6, useMemo as useMemo8, useRef as useRef4, useState as useState9 } from "react";
1935
1935
  import {
1936
1936
  useFields as useFields2,
1937
1937
  useRecords,
@@ -2539,9 +2539,18 @@ function EnrichPanel({ tableId, fieldId, className }) {
2539
2539
  import { useState as useState8 } from "react";
2540
2540
  import { Badge as Badge4, Button as Button6, Separator } from "@ai-matrx/design-system";
2541
2541
  import { Fragment as Fragment4, jsx as jsx11, jsxs as jsxs7 } from "react/jsx-runtime";
2542
- function PastePreview({ plan, noun, nounPlural, onConfirm, onCancel }) {
2542
+ function PastePreview({
2543
+ plan,
2544
+ noun,
2545
+ nounPlural,
2546
+ onConfirm,
2547
+ onCancel,
2548
+ onAddOptions
2549
+ }) {
2543
2550
  const [busy, setBusy] = useState8(false);
2544
2551
  const [outcome, setOutcome] = useState8(null);
2552
+ const [adding, setAdding] = useState8(null);
2553
+ const [addFailed, setAddFailed] = useState8(null);
2545
2554
  const nothingToDo = plan.cells === 0;
2546
2555
  return /* @__PURE__ */ jsx11(
2547
2556
  "div",
@@ -2580,6 +2589,37 @@ function PastePreview({ plan, noun, nounPlural, onConfirm, onCancel }) {
2580
2589
  " more of the same kinds."
2581
2590
  ] }) : null
2582
2591
  ] }) : null,
2592
+ onAddOptions && plan.unmatchedOptions.length > 0 ? /* @__PURE__ */ jsxs7("div", { "data-matrx-paste-add-options": true, className: "flex flex-col gap-2", children: [
2593
+ plan.unmatchedOptions.map((group) => /* @__PURE__ */ jsxs7("div", { className: "flex flex-wrap items-center gap-2", children: [
2594
+ /* @__PURE__ */ jsxs7("p", { className: "text-xs", children: [
2595
+ /* @__PURE__ */ jsx11("span", { className: "font-medium", children: group.column }),
2596
+ " does not offer",
2597
+ " ",
2598
+ group.words.map((w) => `\u201C${w}\u201D`).join(", "),
2599
+ "."
2600
+ ] }),
2601
+ /* @__PURE__ */ jsx11(
2602
+ Button6,
2603
+ {
2604
+ type: "button",
2605
+ size: "sm",
2606
+ variant: "outline",
2607
+ disabled: adding !== null || busy,
2608
+ onClick: () => {
2609
+ setAddFailed(null);
2610
+ setAdding(group.fieldKey);
2611
+ void onAddOptions(group.fieldKey, group.words).catch(
2612
+ (err) => setAddFailed(
2613
+ `Those options were not added: ${err instanceof Error ? err.message : String(err)}`
2614
+ )
2615
+ ).finally(() => setAdding(null));
2616
+ },
2617
+ children: adding === group.fieldKey ? "Adding\u2026" : group.words.length === 1 ? `Add \u201C${group.words[0]}\u201D as an option` : `Add these ${group.words.length} options`
2618
+ }
2619
+ )
2620
+ ] }, group.fieldKey)),
2621
+ addFailed ? /* @__PURE__ */ jsx11("p", { className: "text-xs text-destructive", children: addFailed }) : null
2622
+ ] }) : null,
2583
2623
  plan.pastRightEdge > 0 ? /* @__PURE__ */ jsxs7("p", { className: "text-xs text-muted-foreground", children: [
2584
2624
  plan.pastRightEdge,
2585
2625
  " ",
@@ -2671,9 +2711,26 @@ function confirmLabel(plan, noun, nounPlural) {
2671
2711
 
2672
2712
  // src/pasteValues.ts
2673
2713
  import { predictValueRefusals } from "@ai-matrx/records/core";
2714
+ import { optionKey as optionKey2 } from "@ai-matrx/records";
2715
+ function choiceOptionsOf(options) {
2716
+ const byWord = /* @__PURE__ */ new Map();
2717
+ const labels = [];
2718
+ for (const option of options) {
2719
+ const id = option.id;
2720
+ const key = optionKey2(option);
2721
+ const label = recordName(option.data, null, "");
2722
+ if (label) {
2723
+ labels.push(label);
2724
+ if (!byWord.has(label.toLowerCase())) byWord.set(label.toLowerCase(), id);
2725
+ }
2726
+ if (key && !byWord.has(String(key).toLowerCase())) byWord.set(String(key).toLowerCase(), id);
2727
+ if (id && !byWord.has(id.toLowerCase())) byWord.set(id.toLowerCase(), id);
2728
+ }
2729
+ return { byWord, labels };
2730
+ }
2674
2731
  var TRUE_WORDS = /* @__PURE__ */ new Set(["true", "yes", "y", "1", "\u2713", "x", "checked", "on"]);
2675
2732
  var FALSE_WORDS = /* @__PURE__ */ new Set(["false", "no", "n", "0", "", "unchecked", "off"]);
2676
- function valueFromPastedText(field, raw) {
2733
+ function valueFromPastedText(field, raw, options) {
2677
2734
  const label = fieldName(field);
2678
2735
  const text = raw.trim();
2679
2736
  const kind = editorKindFor(field);
@@ -2733,10 +2790,30 @@ function valueFromPastedText(field, raw) {
2733
2790
  // stop agreeing with it. So the cell is left alone and the person is told
2734
2791
  // which control does hold it.
2735
2792
  case "select":
2736
- case "multi_select":
2737
- return {
2738
- refusal: `${label} is a list, and a pasted word is not one of its options. Open the cell and pick the option \u2014 or add \u201C${raw}\u201D as an option first, and paste again.`
2739
- };
2793
+ case "multi_select": {
2794
+ if (!options) {
2795
+ return {
2796
+ refusal: `${label} is a list and its options have not been read yet, so nothing can be matched to \u201C${raw}\u201D. Reopen the paste once the column's choices have loaded.`
2797
+ };
2798
+ }
2799
+ const wanted = kind === "multi_select" ? text.split(/[,;]/).map((w) => w.trim()).filter((w) => w.length > 0) : [text];
2800
+ if (wanted.length === 0) return { value: kind === "multi_select" ? [] : null };
2801
+ const matched = [];
2802
+ const unmatched = [];
2803
+ for (const word of wanted) {
2804
+ const hit = options.byWord.get(word.toLowerCase());
2805
+ if (hit === void 0) unmatched.push(word);
2806
+ else matched.push(hit);
2807
+ }
2808
+ if (unmatched.length > 0) {
2809
+ const offered = options.labels.length > 0 ? options.labels.join(", ") : "it has none yet";
2810
+ return {
2811
+ refusal: `${label} does not have ${unmatched.map((w) => `\u201C${w}\u201D`).join(" or ")} among its options. It offers: ${offered}.`,
2812
+ unmatched
2813
+ };
2814
+ }
2815
+ return { value: kind === "multi_select" ? matched : matched[0] };
2816
+ }
2740
2817
  case "member":
2741
2818
  return {
2742
2819
  refusal: `${label} names a person in this organization, so it is chosen rather than typed. Open the cell and pick them.`
@@ -2763,6 +2840,7 @@ function planPastedBlock(args) {
2763
2840
  const byKey = new Map(args.fields.map((f) => [f.key, f]));
2764
2841
  const rows = [];
2765
2842
  const refusals = [];
2843
+ const unmatched = /* @__PURE__ */ new Map();
2766
2844
  let cells = 0;
2767
2845
  const lines = [
2768
2846
  ...args.onExisting.map((r) => ({ recordId: r.recordId, raw: r.raw })),
@@ -2778,8 +2856,15 @@ function planPastedBlock(args) {
2778
2856
  const field = byKey.get(key);
2779
2857
  const raw = line.raw[c];
2780
2858
  if (field === void 0 || raw === void 0) continue;
2781
- const judged = valueFromPastedText(field, raw);
2859
+ const judged = valueFromPastedText(field, raw, args.options?.get(key));
2782
2860
  if ("refusal" in judged) {
2861
+ if ("unmatched" in judged) {
2862
+ const seen = unmatched.get(key) ?? { column: fieldName(field), words: [] };
2863
+ for (const word of judged.unmatched) {
2864
+ if (!seen.words.some((w) => w.toLowerCase() === word.toLowerCase())) seen.words.push(word);
2865
+ }
2866
+ unmatched.set(key, seen);
2867
+ }
2783
2868
  built.push({ key, raw, value: null, refusal: judged.refusal });
2784
2869
  refusals.push({ fromLine, column: fieldName(field), raw, why: judged.refusal });
2785
2870
  continue;
@@ -2815,7 +2900,12 @@ function planPastedBlock(args) {
2815
2900
  creates: keepers.filter((r) => r.recordId === null).length,
2816
2901
  cells,
2817
2902
  refusals,
2818
- pastRightEdge: args.pastRightEdge ?? 0
2903
+ pastRightEdge: args.pastRightEdge ?? 0,
2904
+ unmatchedOptions: [...unmatched.entries()].map(([fieldKey, seen]) => ({
2905
+ fieldKey,
2906
+ column: seen.column,
2907
+ words: seen.words
2908
+ }))
2819
2909
  };
2820
2910
  }
2821
2911
  function documentOf(row) {
@@ -2897,13 +2987,29 @@ function Grid({
2897
2987
  [client, host, refreshEnrich]
2898
2988
  );
2899
2989
  const [pastePlan, setPastePlan] = useState9(null);
2990
+ const choiceOptions = useRef4(/* @__PURE__ */ new Map());
2991
+ const loadChoiceOptions = useCallback6(
2992
+ async (fieldList) => {
2993
+ const wanted = fieldList.filter(
2994
+ (f) => (editorKindFor(f) === "select" || editorKindFor(f) === "multi_select") && !choiceOptions.current.has(f.key)
2995
+ );
2996
+ await Promise.all(
2997
+ wanted.map(async (field) => {
2998
+ const got = await client.fieldOptions({ field_id: field.id });
2999
+ if (got.ok) choiceOptions.current.set(field.key, choiceOptionsOf(got.data ?? []));
3000
+ })
3001
+ );
3002
+ },
3003
+ [client]
3004
+ );
2900
3005
  const pasteableKeys = useMemo8(
2901
3006
  () => ordered.filter((f) => fieldIsEditable(f)).map((f) => f.key),
2902
3007
  [ordered]
2903
3008
  );
2904
3009
  const receivePastePlan = useCallback6(
2905
- (sheetPlan) => {
3010
+ async (sheetPlan) => {
2906
3011
  const fieldList = fields.data ?? [];
3012
+ await loadChoiceOptions(fieldList);
2907
3013
  const byRecord = /* @__PURE__ */ new Map();
2908
3014
  const order = [];
2909
3015
  const take = (rowId, columnId, value) => {
@@ -2931,6 +3037,7 @@ function Grid({
2931
3037
  );
2932
3038
  const built = planPastedBlock({
2933
3039
  fields: fieldList,
3040
+ options: choiceOptions.current,
2934
3041
  columnKeys,
2935
3042
  onExisting: touched.map((recordId) => ({
2936
3043
  recordId,
@@ -2954,7 +3061,37 @@ function Grid({
2954
3061
  }
2955
3062
  setPastePlan({ plan: built });
2956
3063
  },
2957
- [fields.data, rows]
3064
+ [fields.data, loadChoiceOptions, rows]
3065
+ );
3066
+ const addPastedOptions = useCallback6(
3067
+ async (fieldKey, words) => {
3068
+ const field = (fields.data ?? []).find((f) => f.key === fieldKey);
3069
+ if (!field) throw new Error(`This table has no column called ${fieldKey}.`);
3070
+ const existing = choiceOptions.current.get(fieldKey)?.labels ?? [];
3071
+ const written = await client.fieldUpdate({
3072
+ field_id: field.id,
3073
+ patch: { options: [...existing, ...words] }
3074
+ });
3075
+ if (!written.ok) throw new Error(written.error.message);
3076
+ choiceOptions.current.delete(fieldKey);
3077
+ await loadChoiceOptions(fields.data ?? []);
3078
+ setPastePlan(
3079
+ (held) => held ? {
3080
+ plan: planPastedBlock({
3081
+ fields: fields.data ?? [],
3082
+ options: choiceOptions.current,
3083
+ columnKeys: held.plan.rows[0]?.cells.map((c) => c.key) ?? [],
3084
+ onExisting: held.plan.rows.filter((r) => r.recordId !== null).map((r) => ({
3085
+ recordId: r.recordId,
3086
+ raw: r.cells.map((c) => c.raw)
3087
+ })),
3088
+ onNew: held.plan.rows.filter((r) => r.recordId === null).map((r) => r.cells.map((c) => c.raw)),
3089
+ pastRightEdge: held.plan.pastRightEdge
3090
+ })
3091
+ } : held
3092
+ );
3093
+ },
3094
+ [client, fields.data, loadChoiceOptions]
2958
3095
  );
2959
3096
  const applyPaste = useCallback6(async () => {
2960
3097
  const held = pastePlan;
@@ -2962,39 +3099,56 @@ function Grid({
2962
3099
  const refusals = [];
2963
3100
  let written = 0;
2964
3101
  const expected = [];
2965
- for (const row of held.plan.rows) {
2966
- const document2 = documentOf(row);
2967
- if (Object.keys(document2).length === 0) continue;
2968
- if (row.recordId === null) {
2969
- const made = await client.recordWrite({ table_id: tableId, data: document2 });
2970
- if (!made.ok) {
2971
- refusals.push(`Row ${row.fromLine}: ${made.error.message}`);
2972
- continue;
2973
- }
2974
- written += 1;
2975
- expected.push({ recordId: made.data, document: document2 });
2976
- } else {
2977
- const done = await client.recordUpdate({ record_id: row.recordId, patch: document2 });
2978
- if (!done.ok) {
2979
- refusals.push(`Row ${row.fromLine}: ${done.error.message}`);
2980
- continue;
3102
+ const creates = held.plan.rows.filter((row) => row.recordId === null).map((row) => ({ row, document: documentOf(row) })).filter((c) => Object.keys(c.document).length > 0);
3103
+ const updates = held.plan.rows.filter((row) => row.recordId !== null).map((row) => ({ row, document: documentOf(row) })).filter((c) => Object.keys(c.document).length > 0);
3104
+ for (let at = 0; at < creates.length; at += pageSize) {
3105
+ const chunk = creates.slice(at, at + pageSize);
3106
+ const made = await client.recordWriteMany({
3107
+ table_id: tableId,
3108
+ rows: chunk.map((c) => c.document)
3109
+ });
3110
+ if (!made.ok) {
3111
+ refusals.push(
3112
+ `Rows ${chunk[0].row.fromLine}-${chunk[chunk.length - 1].row.fromLine}: ${made.error.message}`
3113
+ );
3114
+ continue;
3115
+ }
3116
+ chunk.forEach((c, index) => {
3117
+ const id = made.data[index];
3118
+ if (id === void 0) {
3119
+ refusals.push(`Row ${c.row.fromLine}: the store wrote the batch and named no id for this line.`);
3120
+ return;
2981
3121
  }
2982
3122
  written += 1;
2983
- expected.push({ recordId: row.recordId, document: document2 });
3123
+ expected.push({ recordId: id, document: c.document });
3124
+ });
3125
+ }
3126
+ for (const { row, document: document2 } of updates) {
3127
+ const done = await client.recordUpdate({ record_id: row.recordId, patch: document2 });
3128
+ if (!done.ok) {
3129
+ refusals.push(`Row ${row.fromLine}: ${done.error.message}`);
3130
+ continue;
2984
3131
  }
3132
+ written += 1;
3133
+ expected.push({ recordId: row.recordId, document: document2 });
2985
3134
  }
2986
3135
  let readBack = 0;
2987
3136
  let missing = 0;
2988
3137
  const seen = /* @__PURE__ */ new Map();
2989
- const reread = await client.list({ table_id: tableId, limit: 500, offset: 0 });
2990
- if (reread.ok) {
3138
+ const READ_BACK_PAGE = 500;
3139
+ for (let offset = 0; ; offset += READ_BACK_PAGE) {
3140
+ const reread = await client.list({ table_id: tableId, limit: READ_BACK_PAGE, offset });
3141
+ if (!reread.ok) {
3142
+ refusals.push(
3143
+ `The rows were written and this could not read them back to check: ${reread.error.message}`
3144
+ );
3145
+ break;
3146
+ }
2991
3147
  for (const row of reread.data.rows) {
2992
3148
  seen.set(row.id, row.document ?? {});
2993
3149
  }
2994
- } else {
2995
- refusals.push(
2996
- `The rows were written and this could not read them back to check: ${reread.error.message}`
2997
- );
3150
+ if (reread.data.rows.length < READ_BACK_PAGE) break;
3151
+ if (expected.every((e) => seen.has(e.recordId))) break;
2998
3152
  }
2999
3153
  for (const entry of expected) {
3000
3154
  const stored = seen.get(entry.recordId);
@@ -3267,6 +3421,7 @@ function Grid({
3267
3421
  noun: table.data?.label_singular ?? "record",
3268
3422
  nounPlural: table.data?.label_plural ?? `${table.data?.label_singular ?? "record"}s`,
3269
3423
  onConfirm: applyPaste,
3424
+ onAddOptions: addPastedOptions,
3270
3425
  onCancel: () => setPastePlan(null)
3271
3426
  }
3272
3427
  ) : null,
@@ -5973,7 +6128,7 @@ import { useFields as useFields11, useRecords as useRecords4, useRecordsClient a
5973
6128
  import { Button as Button18, Skeleton as Skeleton7, cn as cn19 } from "@ai-matrx/design-system";
5974
6129
 
5975
6130
  // src/Pipeline.tsx
5976
- import { useCallback as useCallback9, useEffect as useEffect12, useMemo as useMemo16, useRef as useRef4, useState as useState21 } from "react";
6131
+ import { useCallback as useCallback9, useEffect as useEffect12, useMemo as useMemo16, useRef as useRef5, useState as useState21 } from "react";
5977
6132
  import {
5978
6133
  mayDrag,
5979
6134
  useFields as useFields10,
@@ -6021,7 +6176,7 @@ function PipelineBoard({
6021
6176
  const [waiting, setWaiting] = useState21(/* @__PURE__ */ new Map());
6022
6177
  const [dragging, setDragging] = useState21(null);
6023
6178
  const [over, setOver] = useState21(null);
6024
- const alive = useRef4(true);
6179
+ const alive = useRef5(true);
6025
6180
  useEffect12(() => {
6026
6181
  alive.current = true;
6027
6182
  return () => {
@@ -6772,9 +6927,9 @@ import { useRecordsClient as useRecordsClient17 } from "@ai-matrx/records/react"
6772
6927
  import { Button as Button19, Input as Input2, Skeleton as Skeleton8, cn as cn20 } from "@ai-matrx/design-system";
6773
6928
 
6774
6929
  // src/seedOnce.ts
6775
- import { useCallback as useCallback10, useRef as useRef5 } from "react";
6930
+ import { useCallback as useCallback10, useRef as useRef6 } from "react";
6776
6931
  function useSeedGuard() {
6777
- const claimed = useRef5(/* @__PURE__ */ new Set());
6932
+ const claimed = useRef6(/* @__PURE__ */ new Set());
6778
6933
  return useCallback10((name) => {
6779
6934
  if (claimed.current.has(name)) return false;
6780
6935
  claimed.current.add(name);
@@ -7013,7 +7168,7 @@ function ProposalRow({
7013
7168
  }
7014
7169
 
7015
7170
  // src/ActionInbox.tsx
7016
- import { useCallback as useCallback13, useEffect as useEffect16, useMemo as useMemo19, useRef as useRef6, useState as useState26 } from "react";
7171
+ import { useCallback as useCallback13, useEffect as useEffect16, useMemo as useMemo19, useRef as useRef7, useState as useState26 } from "react";
7017
7172
  import { useRecordsClient as useRecordsClient20 } from "@ai-matrx/records/react";
7018
7173
  import { Badge as Badge9, Button as Button22, Skeleton as Skeleton10, cn as cn23 } from "@ai-matrx/design-system";
7019
7174
 
@@ -7763,7 +7918,7 @@ function ActionInbox({ tableId, includeSettled = false, onOpenRecord, className
7763
7918
  const [outcome, setOutcome] = useState26({});
7764
7919
  const [busy, setBusy] = useState26(null);
7765
7920
  const [cursor, setCursor] = useState26(0);
7766
- const listRef = useRef6(null);
7921
+ const listRef = useRef7(null);
7767
7922
  const load = useCallback13(async () => {
7768
7923
  const result = await client.workInbox({ limit: 200, includeDecided: includeSettled });
7769
7924
  if (!result.ok) {
@@ -7911,7 +8066,7 @@ function ActionInbox({ tableId, includeSettled = false, onOpenRecord, className
7911
8066
  }
7912
8067
 
7913
8068
  // src/HistoryPanel.tsx
7914
- import { useCallback as useCallback14, useEffect as useEffect17, useRef as useRef7, useState as useState27 } from "react";
8069
+ import { useCallback as useCallback14, useEffect as useEffect17, useRef as useRef8, useState as useState27 } from "react";
7915
8070
  import {
7916
8071
  useFields as useFields12,
7917
8072
  useRecordsClient as useRecordsClient21,
@@ -7929,7 +8084,7 @@ function HistoryPanel({ tableId, recordId, onAskAboutField, className }) {
7929
8084
  const [open, setOpen] = useState27(null);
7930
8085
  const [pending, setPending] = useState27({ phase: "idle" });
7931
8086
  const [said, setSaid] = useState27(null);
7932
- const listRef = useRef7(null);
8087
+ const listRef = useRef8(null);
7933
8088
  const load = useCallback14(async () => {
7934
8089
  const answered = await client.recordHistory({ record_id: recordId });
7935
8090
  if (!answered.ok) {
@@ -8220,7 +8375,7 @@ function say(value, field) {
8220
8375
  }
8221
8376
 
8222
8377
  // src/CommentThread.tsx
8223
- import { useCallback as useCallback15, useEffect as useEffect18, useMemo as useMemo20, useRef as useRef8, useState as useState28 } from "react";
8378
+ import { useCallback as useCallback15, useEffect as useEffect18, useMemo as useMemo20, useRef as useRef9, useState as useState28 } from "react";
8224
8379
  import {
8225
8380
  useFields as useFields13,
8226
8381
  useRecordsClient as useRecordsClient22,
@@ -8243,7 +8398,7 @@ function CommentThread({ tableId, recordId, fieldKey, className }) {
8243
8398
  const [people, setPeople] = useState28([]);
8244
8399
  const [mentionQuery, setMentionQuery] = useState28(null);
8245
8400
  const [picked, setPicked] = useState28([]);
8246
- const box = useRef8(null);
8401
+ const box = useRef9(null);
8247
8402
  const load = useCallback15(async () => {
8248
8403
  const answered = await client.commentThread({ record_id: recordId, include_resolved: showResolved });
8249
8404
  if (!answered.ok) {
@@ -10023,7 +10178,7 @@ import {
10023
10178
  } from "@ai-matrx/design-system";
10024
10179
 
10025
10180
  // src/FormRunner.tsx
10026
- import { useCallback as useCallback21, useEffect as useEffect24, useMemo as useMemo24, useRef as useRef9, useState as useState34 } from "react";
10181
+ import { useCallback as useCallback21, useEffect as useEffect24, useMemo as useMemo24, useRef as useRef10, useState as useState34 } from "react";
10027
10182
  import { useFields as useFields15, useOptionalRecordsClient } from "@ai-matrx/records/react";
10028
10183
  import { Button as Button31, Progress, Skeleton as Skeleton18, cn as cn32 } from "@ai-matrx/design-system";
10029
10184
  import { Fragment as Fragment17, jsx as jsx38, jsxs as jsxs34 } from "react/jsx-runtime";
@@ -10101,8 +10256,8 @@ function FormStage({
10101
10256
  const [writing, setWriting] = useState34(false);
10102
10257
  const [done, setDone] = useState34(null);
10103
10258
  const [hidden, setHidden] = useState34({});
10104
- const decoy = useRef9("");
10105
- const stage = useRef9(null);
10259
+ const decoy = useRef10("");
10260
+ const stage = useRef10(null);
10106
10261
  const questions = useMemo24(() => {
10107
10262
  const byKey = new Map((fields ?? []).map((f) => [f.key, f]));
10108
10263
  return (form.questions ?? []).map((q) => {
@@ -10763,13 +10918,13 @@ function groupLabel(groups) {
10763
10918
  import {
10764
10919
  useEffect as useEffect26,
10765
10920
  useId,
10766
- useRef as useRef10,
10921
+ useRef as useRef11,
10767
10922
  useState as useState36
10768
10923
  } from "react";
10769
10924
  import { cn as cn34 } from "@ai-matrx/design-system";
10770
10925
  import { jsx as jsx40, jsxs as jsxs36 } from "react/jsx-runtime";
10771
10926
  function useMeasuredWidth(fallback = 480) {
10772
- const ref = useRef10(null);
10927
+ const ref = useRef11(null);
10773
10928
  const [width, setWidth] = useState36(fallback);
10774
10929
  useEffect26(() => {
10775
10930
  const node = ref.current;
@@ -11074,7 +11229,7 @@ function DocTemplate({ tableId, seed, activeTemplateId, onActiveTemplate, classN
11074
11229
  }
11075
11230
 
11076
11231
  // src/DocRender.tsx
11077
- import { useCallback as useCallback24, useEffect as useEffect28, useRef as useRef11, useState as useState38 } from "react";
11232
+ import { useCallback as useCallback24, useEffect as useEffect28, useRef as useRef12, useState as useState38 } from "react";
11078
11233
  import { useRecordsClient as useRecordsClient30 } from "@ai-matrx/records/react";
11079
11234
  import { Button as Button34, Skeleton as Skeleton21, cn as cn36 } from "@ai-matrx/design-system";
11080
11235
  import { jsx as jsx42, jsxs as jsxs38 } from "react/jsx-runtime";
@@ -11085,7 +11240,7 @@ function DocRender({ templateId, recordId, filename, onRendered, className }) {
11085
11240
  const [showing, setShowing] = useState38(null);
11086
11241
  const [error, setError] = useState38(null);
11087
11242
  const [busy, setBusy] = useState38(null);
11088
- const paper = useRef11(null);
11243
+ const paper = useRef12(null);
11089
11244
  const load = useCallback24(async () => {
11090
11245
  const [body, held] = await Promise.all([
11091
11246
  client.docRenderBody({ template_id: templateId, record_id: recordId }),
@@ -12784,12 +12939,12 @@ function nextInWords(page) {
12784
12939
  }
12785
12940
 
12786
12941
  // src/CaptureSheet.tsx
12787
- import { useCallback as useCallback32, useEffect as useEffect36, useRef as useRef13, useState as useState46 } from "react";
12942
+ import { useCallback as useCallback32, useEffect as useEffect36, useRef as useRef14, useState as useState46 } from "react";
12788
12943
  import { useFields as useFields21, useRecordsClient as useRecordsClient38, useTable as useTable20 } from "@ai-matrx/records/react";
12789
12944
  import { Button as Button42, Input as Input4, Skeleton as Skeleton29, Textarea as Textarea3, cn as cn45 } from "@ai-matrx/design-system";
12790
12945
 
12791
12946
  // src/CaptureRun.tsx
12792
- import { useCallback as useCallback31, useEffect as useEffect35, useMemo as useMemo30, useRef as useRef12, useState as useState45 } from "react";
12947
+ import { useCallback as useCallback31, useEffect as useEffect35, useMemo as useMemo30, useRef as useRef13, useState as useState45 } from "react";
12793
12948
  import {
12794
12949
  openCaptureQueue
12795
12950
  } from "@ai-matrx/records";
@@ -12845,7 +13000,7 @@ function CaptureRun({ sheetId, face: given, className }) {
12845
13000
  const [items, setItems] = useState45([]);
12846
13001
  const [lastSynced, setLastSynced] = useState45(null);
12847
13002
  const [sending, setSending] = useState45(false);
12848
- const queueRef = useRef12(null);
13003
+ const queueRef = useRef13(null);
12849
13004
  useEffect35(() => {
12850
13005
  const q2 = openCaptureQueue({
12851
13006
  onChange: (c, all) => {
@@ -13203,7 +13358,7 @@ function AdHocCaptureSheet({
13203
13358
  const [error, setError] = useState46(null);
13204
13359
  const [uploadError, setUploadError] = useState46(null);
13205
13360
  const [flushing, setFlushing] = useState46(false);
13206
- const queue = useRef13([]);
13361
+ const queue = useRef14([]);
13207
13362
  const keep = useCallback32(
13208
13363
  async (next) => {
13209
13364
  queue.current = next;