@ai-matrx/records-ui 0.101.11 → 0.101.15

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,23 @@
1
1
  # Changelog — @ai-matrx/records-ui
2
2
 
3
+ ## 0.101.15
4
+
5
+ Automatic changed-only republish (docs/metadata drift since the last tag — see
6
+ `git diff npm/records-ui/v0.101.14..npm/records-ui/v0.101.15 -- apps/shared/records-ui`).
7
+ No source changes intended and no consumer action required.
8
+
9
+ ## 0.101.14
10
+
11
+ Automatic changed-only republish (docs/metadata drift since the last tag — see
12
+ `git diff npm/records-ui/v0.101.11..npm/records-ui/v0.101.14 -- apps/shared/records-ui`).
13
+ No source changes intended and no consumer action required.
14
+
15
+ ## 0.101.12
16
+
17
+ Automatic changed-only republish (docs/metadata drift since the last tag — see
18
+ `git diff npm/records-ui/v0.101.11..npm/records-ui/v0.101.12 -- apps/shared/records-ui`).
19
+ No source changes intended and no consumer action required.
20
+
3
21
  ## 0.101.11
4
22
 
5
23
  - **The Action Inbox key hints answer to the inbox's width, not the screen's.** In a ~360px window on
package/dist/index.cjs CHANGED
@@ -7696,6 +7696,7 @@ function containsPattern(text) {
7696
7696
  var BLANK_VALUE = "__empty__";
7697
7697
  var field = (f) => ({ field: f.id });
7698
7698
  var constant = (value) => ({ const: value });
7699
+ var withinWindow = (f, window2) => ({ op: "within", args: [field(f), constant(window2)] });
7699
7700
  var op = (name, ...args) => ({ op: name, args });
7700
7701
  function clauseFor(f, value, now = /* @__PURE__ */ new Date()) {
7701
7702
  const name = fieldName(f);
@@ -7725,12 +7726,12 @@ function clauseFor(f, value, now = /* @__PURE__ */ new Date()) {
7725
7726
  const kind = String(editorKindFor(f));
7726
7727
  if (kind === "datetime" || kind === "date") {
7727
7728
  if (words2.length === 0) return { rule: value.negated ? present : not(present) };
7728
- if (words2.length > 1 || blank || value.negated) {
7729
- return `${name} is asked one ticked day at a time. Use the date range for more.`;
7730
- }
7731
- const day = words2[0];
7732
7729
  const holdsDays = String((f.config ?? {})["kind"] ?? "") === "date";
7733
- return { flat: { [f.key]: dayWindow(day, holdsDays) } };
7730
+ if (words2.length === 1 && !blank && !value.negated) return { flat: { [f.key]: dayWindow(words2[0], holdsDays) } };
7731
+ const leaves = words2.map((day) => withinWindow(f, dayWindow(day, holdsDays)));
7732
+ const anyDay = leaves.length === 1 ? leaves[0] : op("or", ...leaves);
7733
+ if (!value.negated) return { rule: blank ? op("or", not(present), anyDay) : anyDay };
7734
+ return { rule: blank ? op("and", present, not(anyDay)) : not(anyDay) };
7734
7735
  }
7735
7736
  const asStored = (w) => holdsNumbers(f) || kind === "rating" ? Number.isFinite(Number(w)) ? Number(w) : w : kind === "checkbox" && (w === "true" || w === "false") ? w === "true" : w;
7736
7737
  const stored = words2.map(asStored);
@@ -7813,7 +7814,7 @@ function storeQuestion(fields, base, columnFilters, now = /* @__PURE__ */ new Da
7813
7814
  const value = flat[key];
7814
7815
  if (!f) continue;
7815
7816
  if (value !== null && typeof value === "object") {
7816
- refused.push(`${fieldName(f)}'s date range cannot be asked together with the other filters yet, so it is left off.`);
7817
+ rules.push(withinWindow(f, value));
7817
7818
  continue;
7818
7819
  }
7819
7820
  rules.push(value === null ? op("not", op("present", field(f))) : op("eq", field(f), constant(value)));
@@ -13732,6 +13733,44 @@ function replanPastedBlock(plan, args, change = {}) {
13732
13733
  droppedLines: (plan.droppedLines ?? 0) + dropped
13733
13734
  });
13734
13735
  }
13736
+ function storedValueAgrees(field2, stored, written, options) {
13737
+ const empty = (v) => v === void 0 || v === null || v === "";
13738
+ if (empty(stored) || empty(written)) return empty(stored) && empty(written);
13739
+ if (field2 !== void 0) {
13740
+ if (isDateField(field2)) {
13741
+ if (hasTimePart(written)) {
13742
+ const a = new Date(stored).getTime();
13743
+ const b = new Date(written).getTime();
13744
+ if (!Number.isNaN(a) && !Number.isNaN(b)) return a === b;
13745
+ } else {
13746
+ const dayOf = (v) => typeof v === "string" && /^\d{4}-\d{2}-\d{2}/.test(v.trim()) ? v.trim().slice(0, 10) : calendarDayOf(v);
13747
+ const a = dayOf(stored);
13748
+ const b = dayOf(written);
13749
+ if (a !== null && b !== null) return a === b;
13750
+ }
13751
+ }
13752
+ const kind = editorKindFor(field2);
13753
+ if (kind === "number" || kind === "currency" || kind === "percent") {
13754
+ const a = typeof stored === "number" ? stored : Number(String(stored).trim());
13755
+ const b = typeof written === "number" ? written : Number(String(written).trim());
13756
+ if (Number.isFinite(a) && Number.isFinite(b)) return a === b;
13757
+ }
13758
+ if (options !== void 0 && typeof stored === "string" && typeof written === "string") {
13759
+ const option = (v) => options.byWord.get(v.trim().toLowerCase()) ?? v.trim().toLowerCase();
13760
+ return option(stored) === option(written);
13761
+ }
13762
+ }
13763
+ return JSON.stringify(stored) === JSON.stringify(written);
13764
+ }
13765
+ function columnsTheGridTurnedAway(turnedAway, columnKeys, columnName, already) {
13766
+ const said = /* @__PURE__ */ new Map();
13767
+ for (const refused of turnedAway) {
13768
+ if (!columnKeys.includes(refused.columnId)) continue;
13769
+ if (already.some((r) => r.column === columnName(refused.columnId))) continue;
13770
+ said.set(refused.columnId, refused.reason);
13771
+ }
13772
+ return [...said].map(([columnId, why]) => ({ fromLine: 0, column: columnName(columnId), raw: "", why }));
13773
+ }
13735
13774
 
13736
13775
  // src/gridControls.tsx
13737
13776
  var import_jsx_runtime48 = require("react/jsx-runtime");
@@ -13840,26 +13879,26 @@ function useMergedGridControls(args) {
13840
13879
  );
13841
13880
  const choiceKeys = (0, import_react77.useMemo)(() => enabled ? fields.filter(isChoiceField).map((f) => f.key) : [], [enabled, fields]);
13842
13881
  const choiceOptions = useChoiceOptions(fields, choiceKeys);
13843
- const searchTyped = query.search.trim() !== "";
13844
- const countQuestionKey = JSON.stringify([choiceKeys, query.columnFilters, args.filter ?? null]);
13882
+ const searchWords = (args.search ?? "").trim();
13883
+ const countQuestionKey = JSON.stringify([choiceKeys, query.columnFilters, args.filter ?? null, searchWords]);
13845
13884
  const rowsKey = rows.map((row) => `${row.id}:${row.version ?? ""}`).join(",");
13846
13885
  const fieldsRef = (0, import_react77.useRef)(fields);
13847
13886
  fieldsRef.current = fields;
13848
13887
  const [choiceCounts, setChoiceCounts] = (0, import_react77.useState)({});
13849
13888
  (0, import_react77.useEffect)(() => {
13850
- if (!enabled || searchTyped || choiceKeys.length === 0) {
13889
+ if (!enabled || choiceKeys.length === 0) {
13851
13890
  setChoiceCounts({});
13852
13891
  return;
13853
13892
  }
13854
13893
  let cancelled = false;
13855
- const [keys, filters, base] = JSON.parse(countQuestionKey);
13894
+ const [keys, filters, base, searched] = JSON.parse(countQuestionKey);
13856
13895
  for (const key of keys) {
13857
13896
  const { [key]: _own, ...others } = filters;
13858
13897
  const asked3 = storeQuestion(fieldsRef.current, base ?? void 0, others);
13859
13898
  void askOnce(
13860
13899
  client,
13861
- `value_counts:${tableId}:${key}:${JSON.stringify(asked3.filter ?? null)}`,
13862
- () => client.recordAggregate({ table_id: tableId, groupBy: [key], measures: [{ op: "count" }], ...asked3.filter ? { filter: asked3.filter } : {}, limit: 500 }),
13900
+ `value_counts:${tableId}:${key}:${searched}:${JSON.stringify(asked3.filter ?? null)}`,
13901
+ () => client.recordAggregate({ table_id: tableId, groupBy: [key], measures: [{ op: "count" }], ...asked3.filter ? { filter: asked3.filter } : {}, ...searched ? { search: searched } : {}, limit: 500 }),
13863
13902
  VALUE_COUNTS_FRESH_MS
13864
13903
  ).then((answer) => {
13865
13904
  if (cancelled || !answer.ok) return;
@@ -13875,7 +13914,7 @@ function useMergedGridControls(args) {
13875
13914
  return () => {
13876
13915
  cancelled = true;
13877
13916
  };
13878
- }, [enabled, searchTyped, countQuestionKey, client, tableId, rowsKey]);
13917
+ }, [enabled, countQuestionKey, client, tableId, rowsKey]);
13879
13918
  const filterFor = (0, import_react77.useCallback)(
13880
13919
  (key) => {
13881
13920
  if (!enabled) return void 0;
@@ -15503,12 +15542,7 @@ function PastePreview({
15503
15542
  plan.refusals.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)("div", { "data-matrx-paste-refusals": true, className: "flex flex-col gap-1", children: [
15504
15543
  /* @__PURE__ */ (0, import_jsx_runtime51.jsx)("p", { className: "text-xs font-medium", children: plan.refusals.length === 1 ? "One cell will be left alone:" : `${plan.refusals.length} cells will be left alone:` }),
15505
15544
  /* @__PURE__ */ (0, import_jsx_runtime51.jsx)("ul", { className: "flex flex-col gap-1", children: plan.refusals.slice(0, 12).map((refusal3, index) => /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)("li", { className: "text-xs text-muted-foreground", children: [
15506
- /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)("span", { className: "font-medium", children: [
15507
- "Row ",
15508
- refusal3.fromLine,
15509
- ", ",
15510
- refusal3.column
15511
- ] }),
15545
+ /* @__PURE__ */ (0, import_jsx_runtime51.jsx)("span", { className: "font-medium", children: refusal3.fromLine > 0 ? `Row ${refusal3.fromLine}, ${refusal3.column}` : refusal3.column }),
15512
15546
  " \u2014 ",
15513
15547
  refusal3.why
15514
15548
  ] }, index)) }),
@@ -19192,7 +19226,8 @@ async function askColumnValues(inputs, args) {
19192
19226
  const field2 = inputs.fields.find((f) => f.key === args.columnId);
19193
19227
  const kind = valueListKind(field2);
19194
19228
  if (kind === "none") return noValueList(args.columnId);
19195
- if (kind === "loaded" || args.search.trim() !== "") return null;
19229
+ if (kind === "loaded") return null;
19230
+ const searched = args.search.trim();
19196
19231
  const { [args.columnId]: _own, ...others } = inputs.columnFilters;
19197
19232
  const asked3 = storeQuestion(inputs.fields, inputs.filter, others);
19198
19233
  const limit = Math.min(Math.max(args.limit, 1), MOST_VALUES);
@@ -19201,6 +19236,7 @@ async function askColumnValues(inputs, args) {
19201
19236
  ...kind === "days" ? { bucket: { key: field2.key, by: "day" } } : { groupBy: [field2.key] },
19202
19237
  measures: [{ op: "count" }],
19203
19238
  ...asked3.filter ? { filter: asked3.filter } : {},
19239
+ ...searched ? { search: searched } : {},
19204
19240
  limit: MOST_VALUES
19205
19241
  });
19206
19242
  if (!answer.ok) throw new Error(refusalSentence(answer.error));
@@ -19212,7 +19248,8 @@ async function askColumnValues(inputs, args) {
19212
19248
  const unique = await inputs.client.recordAggregate({
19213
19249
  table_id: inputs.tableId,
19214
19250
  measures: [{ op: "unique", key: field2.key }],
19215
- ...asked3.filter ? { filter: asked3.filter } : {}
19251
+ ...asked3.filter ? { filter: asked3.filter } : {},
19252
+ ...searched ? { search: searched } : {}
19216
19253
  });
19217
19254
  if (unique.ok) {
19218
19255
  const n = Number(unique.data?.[0]?.measures?.[`unique_${field2.key}`]);
@@ -21280,19 +21317,9 @@ function GridBody({
21280
21317
  pastRightEdge: sheetPlan.pastRightEdge,
21281
21318
  ...headed ? { header: { unmatched: headed.unmatched } } : {}
21282
21319
  });
21283
- const notWritable = /* @__PURE__ */ new Map();
21284
- for (const refused of sheetPlan.refusals) {
21285
- if (built.refusals.some((r) => r.column === columnLabel(fieldList, refused.columnId))) continue;
21286
- notWritable.set(refused.columnId, refused.reason);
21287
- }
21288
- for (const [columnId, why] of notWritable) {
21289
- built.refusals.push({
21290
- fromLine: 0,
21291
- column: columnLabel(fieldList, columnId),
21292
- raw: "",
21293
- why
21294
- });
21295
- }
21320
+ built.refusals.push(
21321
+ ...columnsTheGridTurnedAway(sheetPlan.refusals, columnKeys, (id) => columnLabel(fieldList, id), built.refusals)
21322
+ );
21296
21323
  if (merged && built.cells > 0 && built.creates === 0 && built.refusals.length === 0 && built.pastRightEdge === 0 && (built.header?.unmatched.length ?? 0) === 0 && built.unmatchedOptions.length === 0) {
21297
21324
  const outcome = await pasteAtOnce.current(built);
21298
21325
  if (outcome.refusals.length > 0 || outcome.missing > 0) {
@@ -21393,20 +21420,21 @@ function GridBody({
21393
21420
  const reread = await client.listByIds({ table_id: tableId, ids: expected.map((e) => e.recordId) });
21394
21421
  let readBack = 0;
21395
21422
  let missing = 0;
21423
+ const fieldOf2 = new Map((fields.data ?? []).map((f) => [f.key, f]));
21396
21424
  const refusals = [];
21397
21425
  if (!reread.ok) refusals.push(`The rows were written and this could not read them back to check: ${refusalSentence(reread.error)}`);
21398
21426
  const seen = new Map((reread.ok ? reread.data : []).map((r) => [r.id, r.document ?? {}]));
21399
21427
  for (const entry of expected) {
21400
21428
  const stored = seen.get(entry.recordId);
21401
21429
  for (const [key, value] of Object.entries(entry.document)) {
21402
- if (stored !== void 0 && JSON.stringify(stored[key]) === JSON.stringify(value)) readBack += 1;
21430
+ if (stored !== void 0 && storedValueAgrees(fieldOf2.get(key), stored[key], value, choiceOptions.current.get(key))) readBack += 1;
21403
21431
  else missing += 1;
21404
21432
  }
21405
21433
  }
21406
21434
  records.reload();
21407
21435
  return { written: lines, readBack, missing, refusals };
21408
21436
  },
21409
- [rows, client, tableId, editing, undo, records, versions]
21437
+ [rows, client, tableId, editing, undo, records, versions, fields.data]
21410
21438
  );
21411
21439
  const pasteAtOnce = (0, import_react107.useRef)(applyPasteAsOne);
21412
21440
  pasteAtOnce.current = applyPasteAsOne;
@@ -21481,6 +21509,7 @@ function GridBody({
21481
21509
  }
21482
21510
  let readBack = 0;
21483
21511
  let missing = 0;
21512
+ const fieldOf2 = new Map((fields.data ?? []).map((f) => [f.key, f]));
21484
21513
  const seen = /* @__PURE__ */ new Map();
21485
21514
  const READ_BACK_PAGE = 500;
21486
21515
  for (let offset = 0; ; offset += READ_BACK_PAGE) {
@@ -21500,7 +21529,7 @@ function GridBody({
21500
21529
  for (const entry of expected) {
21501
21530
  const stored = seen.get(entry.recordId);
21502
21531
  for (const [key, value] of Object.entries(entry.document)) {
21503
- if (stored !== void 0 && JSON.stringify(stored[key]) === JSON.stringify(value)) {
21532
+ if (stored !== void 0 && storedValueAgrees(fieldOf2.get(key), stored[key], value, choiceOptions.current.get(key))) {
21504
21533
  readBack += 1;
21505
21534
  } else {
21506
21535
  missing += 1;
@@ -21509,7 +21538,7 @@ function GridBody({
21509
21538
  }
21510
21539
  records.reload();
21511
21540
  return { written, readBack, missing, refusals };
21512
- }, [pastePlan, client, tableId, records, merged, applyPasteAsOne, versions]);
21541
+ }, [pastePlan, client, tableId, records, merged, applyPasteAsOne, versions, fields.data]);
21513
21542
  const columnIds = (0, import_react107.useMemo)(() => ordered.map((field2) => field2.key), [ordered]);
21514
21543
  const lookWrites = (0, import_react107.useRef)(null);
21515
21544
  const writeLook = (0, import_react107.useCallback)(
@@ -21631,6 +21660,7 @@ function GridBody({
21631
21660
  writeLook,
21632
21661
  query,
21633
21662
  ...filter ? { filter } : {},
21663
+ search: searchTerm,
21634
21664
  setQuery,
21635
21665
  duplicate: (row) => void duplicate(row),
21636
21666
  askDelete: setDeleting,
@@ -23132,6 +23162,7 @@ var import_records40 = require("@ai-matrx/records");
23132
23162
  var import_design_system67 = require("@ai-matrx/design-system");
23133
23163
 
23134
23164
  // src/newTableColumns.ts
23165
+ var import_templates = require("@ai-matrx/records/templates");
23135
23166
  var NEW_COLUMN_TYPES = [
23136
23167
  { type: "text", words: "Text" },
23137
23168
  { type: "rich_text", words: "Formatted text" },
@@ -23149,7 +23180,80 @@ var NEW_COLUMN_TYPES = [
23149
23180
  ];
23150
23181
  var MOST_NEW_COLUMNS = 40;
23151
23182
  var BULLET = /^\s*(?:[-*•]|\d+[.)])\s+/;
23152
- function columnNamesFrom(text) {
23183
+ var TYPE_WORD_ALIASES = {
23184
+ choice: "select",
23185
+ dropdown: "select",
23186
+ status: "select",
23187
+ list: "select",
23188
+ choices: "multi_select",
23189
+ tags: "multi_select",
23190
+ money: "currency",
23191
+ boolean: "checkbox",
23192
+ bool: "checkbox",
23193
+ "yes/no": "checkbox",
23194
+ "yes / no": "checkbox",
23195
+ link: "url",
23196
+ integer: "number",
23197
+ decimal: "number",
23198
+ float: "number",
23199
+ string: "text",
23200
+ long_text: "rich_text",
23201
+ "formatted text": "rich_text",
23202
+ "date and time": "datetime",
23203
+ "date time": "datetime"
23204
+ };
23205
+ function typeFromWord(raw) {
23206
+ const word = raw.trim().toLowerCase();
23207
+ if (word === "") return void 0;
23208
+ const type = TYPE_WORD_ALIASES[word] ?? NEW_COLUMN_TYPES.find((t) => t.type === word.replace(/[\s-]+/g, "_"))?.type;
23209
+ if (!type) return void 0;
23210
+ return import_templates.RESOLVABLE_FIELD_TYPE_WORDS.includes(type === "date" ? "datetime" : type) ? type : void 0;
23211
+ }
23212
+ function maskBrackets(text) {
23213
+ let depth = 0;
23214
+ let out = "";
23215
+ for (const ch of text) {
23216
+ if (ch === "(" || ch === "[") {
23217
+ depth++;
23218
+ out += depth === 1 ? ch : "\0";
23219
+ } else if ((ch === ")" || ch === "]") && depth > 0) {
23220
+ depth--;
23221
+ out += depth === 0 ? ch : "\0";
23222
+ } else out += depth > 0 ? "\0" : ch;
23223
+ }
23224
+ return out;
23225
+ }
23226
+ function splitOutsideBrackets(text, sep) {
23227
+ const masked = maskBrackets(text);
23228
+ const parts = [];
23229
+ let from = 0;
23230
+ const re = new RegExp(sep.source, sep.flags.includes("g") ? sep.flags : sep.flags + "g");
23231
+ for (const m of masked.matchAll(re)) {
23232
+ if (m[0] === "") continue;
23233
+ parts.push(text.slice(from, m.index));
23234
+ from = m.index + m[0].length;
23235
+ }
23236
+ parts.push(text.slice(from));
23237
+ return parts;
23238
+ }
23239
+ function specFromPart(part) {
23240
+ const clean = (t) => t.replace(/^["'\s]+|["'.\s]+$/g, "").replace(/\s+/g, " ");
23241
+ const m = /^(.*?)\s*[(\[]([^()\[\]]*)[)\]]\s*$/.exec(part.trim());
23242
+ if (m) {
23243
+ const inner2 = m[2];
23244
+ const colon = inner2.indexOf(":");
23245
+ const type = typeFromWord(colon >= 0 ? inner2.slice(0, colon) : inner2);
23246
+ const name = clean(m[1]);
23247
+ if (type && name !== "") {
23248
+ const options = colon >= 0 ? [...new Set(inner2.slice(colon + 1).split(/\s*[,;|]\s*/).map(clean).filter((o) => o !== ""))] : void 0;
23249
+ return { label: name, type, ...options && options.length > 0 && (type === "select" || type === "multi_select") ? { options } : {} };
23250
+ }
23251
+ }
23252
+ const withoutPairs = part.replace(/\([^()\[\]]*\)|\[[^()\[\]]*\]/g, "");
23253
+ const label = /[()\[\]]/.test(withoutPairs) ? part.replace(/[()\[\]]/g, " ") : part;
23254
+ return { label: clean(label) };
23255
+ }
23256
+ function specsFrom(text) {
23153
23257
  const trimmed = text.trim();
23154
23258
  if (trimmed === "") return [];
23155
23259
  let parts;
@@ -23160,23 +23264,28 @@ function columnNamesFrom(text) {
23160
23264
  parts = trimmed.split(/\r?\n/).map((line) => line.replace(BULLET, ""));
23161
23265
  } else {
23162
23266
  let line = trimmed;
23163
- const colon = line.indexOf(":");
23267
+ const masked = maskBrackets(line);
23268
+ const colon = masked.indexOf(":");
23164
23269
  if (colon >= 0) line = line.slice(colon + 1);
23165
23270
  else {
23166
- const withAt = line.toLowerCase().lastIndexOf(" with ");
23271
+ const withAt = masked.toLowerCase().lastIndexOf(" with ");
23167
23272
  if (withAt >= 0) line = line.slice(withAt + " with ".length);
23168
23273
  }
23169
- parts = line.split(/\s*[,;|]\s*|\s+and\s+|\s*&\s*/i);
23274
+ parts = splitOutsideBrackets(line, /\s*[,;|]\s*|\s+and\s+|\s*&\s*/i);
23170
23275
  }
23171
- const names = parts.map((p) => p.replace(/^["'\s]+|["'.\s]+$/g, "").replace(/\s+/g, " ")).filter((p) => p !== "").map((p) => p === p.toLowerCase() ? p[0].toUpperCase() + p.slice(1) : p);
23276
+ const specs = parts.map(specFromPart).filter((c) => c.label !== "").map((c) => c.label === c.label.toLowerCase() ? { ...c, label: c.label[0].toUpperCase() + c.label.slice(1) } : c);
23172
23277
  const seen = /* @__PURE__ */ new Set();
23173
- const once = names.filter((n) => {
23174
- const k = n.toLowerCase();
23278
+ const once = specs.filter((c) => {
23279
+ const k = c.label.toLowerCase();
23175
23280
  if (seen.has(k)) return false;
23176
23281
  seen.add(k);
23177
23282
  return true;
23178
23283
  });
23179
- return uniqueLabels(once).slice(0, MOST_NEW_COLUMNS);
23284
+ const labels = uniqueLabels(once.map((c) => c.label));
23285
+ return once.map((c, i) => ({ ...c, label: labels[i] })).slice(0, MOST_NEW_COLUMNS);
23286
+ }
23287
+ function columnNamesFrom(text) {
23288
+ return specsFrom(text).map((c) => c.label);
23180
23289
  }
23181
23290
  var STATUS_CHOICES = ["Not started", "In progress", "Done"];
23182
23291
  var PRIORITY_CHOICES = ["Low", "Medium", "High"];
@@ -23201,7 +23310,9 @@ function columnFromName(label) {
23201
23310
  return { label, type: "text" };
23202
23311
  }
23203
23312
  function columnsFrom(text) {
23204
- return columnNamesFrom(text).map(columnFromName);
23313
+ return specsFrom(text).map(
23314
+ (c) => c.type ? { label: c.label, type: c.type, ...c.options && c.options.length > 0 ? { options: c.options } : {} } : columnFromName(c.label)
23315
+ );
23205
23316
  }
23206
23317
  function fieldsForNewColumns(columns) {
23207
23318
  const keys = /* @__PURE__ */ new Set();
@@ -27032,8 +27143,15 @@ function bodyFieldOf(fields) {
27032
27143
  var BODY_COLUMN_NAME = "Notes";
27033
27144
  var BODY_SAVE_PAUSE_MS = 900;
27034
27145
  function RecordBody({ tableId, recordId, className }) {
27146
+ const host = useRecordsUi();
27035
27147
  const fields = (0, import_react150.useFields)(tableId);
27148
+ const hostRecord = (0, import_react150.useRecord)(recordId, true, { tableId });
27149
+ const hostMay = useRecordRights(recordId);
27036
27150
  const field2 = bodyFieldOf(fields.data ?? []);
27151
+ const hostBody = host.renderRecordBody && hostRecord.data ? host.renderRecordBody({ tableId, recordId, record: hostRecord.data, readOnly: !hostMay.write }) : null;
27152
+ if (hostBody !== null && hostBody !== void 0) {
27153
+ return /* @__PURE__ */ (0, import_jsx_runtime90.jsx)("section", { className: (0, import_design_system80.cn)("flex min-w-0 flex-col gap-1.5", className), "data-record-body": "host", children: hostBody });
27154
+ }
27037
27155
  if (fields.loading && !fields.data) return null;
27038
27156
  return /* @__PURE__ */ (0, import_jsx_runtime90.jsx)("section", { className: (0, import_design_system80.cn)("flex min-w-0 flex-col gap-1.5", className), "data-record-body": field2?.key ?? "", children: field2 ? /* @__PURE__ */ (0, import_jsx_runtime90.jsx)(BodyEditor, { tableId, recordId, field: field2 }, `${recordId}:${field2.key}`) : /* @__PURE__ */ (0, import_jsx_runtime90.jsx)(AddBody, { tableId }) });
27039
27157
  }