@ai-matrx/records-ui 0.93.140 → 0.93.142
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 +12 -0
- package/dist/index.cjs +42 -29
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -8
- package/dist/index.d.ts +1 -8
- package/dist/index.js +42 -29
- package/dist/index.js.map +1 -1
- package/dist/pickers.cjs +23 -26
- package/dist/pickers.cjs.map +1 -1
- package/dist/pickers.js +23 -26
- package/dist/pickers.js.map +1 -1
- package/dist/table-shape.cjs +3 -9
- package/dist/table-shape.cjs.map +1 -1
- package/dist/table-shape.js +3 -9
- package/dist/table-shape.js.map +1 -1
- package/package.json +1 -1
package/dist/table-shape.cjs
CHANGED
|
@@ -41,6 +41,7 @@ var import_source = require("@ai-matrx/content-ir/source");
|
|
|
41
41
|
|
|
42
42
|
// src/kindValues.ts
|
|
43
43
|
var import_directives = require("@ai-matrx/content-ir/directives");
|
|
44
|
+
var import_text_case = require("@ai-matrx/kit/text-case");
|
|
44
45
|
var KIND_KEY = "__kind";
|
|
45
46
|
var WHOLE_FENCE = /^\s*```(?:matrx|json)?[ \t]*\r?\n?([\s\S]*?)\r?\n?[ \t]*```\s*$/;
|
|
46
47
|
function asObject(value) {
|
|
@@ -74,15 +75,8 @@ function kindValueOf(value) {
|
|
|
74
75
|
return parsed ? fromObject(parsed) : null;
|
|
75
76
|
}
|
|
76
77
|
|
|
77
|
-
// src/names.ts
|
|
78
|
-
var import_uuid = require("@ai-matrx/kit/uuid");
|
|
79
|
-
function humanize(token) {
|
|
80
|
-
const words = token.replace(/[_\-.]+/g, " ").replace(/([a-z0-9])([A-Z])/g, "$1 $2").trim();
|
|
81
|
-
if (words === "") return token;
|
|
82
|
-
return words.charAt(0).toUpperCase() + words.slice(1);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
78
|
// src/tableShape.ts
|
|
79
|
+
var import_text_case2 = require("@ai-matrx/kit/text-case");
|
|
86
80
|
var FEWEST_LIST_ITEMS = 2;
|
|
87
81
|
var FEWEST_KEY_VALUE_PAIRS = 2;
|
|
88
82
|
var LONGEST_TEXT_READ = 2e6;
|
|
@@ -330,7 +324,7 @@ function isPlainObject(v) {
|
|
|
330
324
|
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
331
325
|
}
|
|
332
326
|
function labelOfKey(key) {
|
|
333
|
-
return /[\s]/.test(key) || /^[A-Z]/.test(key) ? key :
|
|
327
|
+
return /[\s]/.test(key) || /^[A-Z]/.test(key) ? key : (0, import_text_case2.humanizeIdentifier)(key);
|
|
334
328
|
}
|
|
335
329
|
function shapeFromObjects(objects, kind, from, name) {
|
|
336
330
|
if (objects.length === 0) return null;
|
package/dist/table-shape.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/tableShape.ts","../src/kindValues.ts","../src/names.ts"],"sourcesContent":["// src/tableShape.ts — @ai-matrx/records-ui · lane SAVE-AS-TABLE-EVERYWHERE\n//\n// WHAT IN THIS TEXT IS ALREADY A TABLE. Arman, 2026-09-29: anywhere information appears in a shape\n// the system understands — a markdown table, a list or even a few bullet points, key:value lines,\n// a CSV or TSV paste, a kind value, JSON rows — the SAME \"Save to a table\" must exist. This module\n// is the one reader of those shapes: every menu asks it whether to offer the action, and the one\n// screen (`SaveToTable`) asks it for the rows. It is pure (no React, no store) so a guard, a server\n// render and a menu predicate read the same answer.\n//\n// IT IS A PARSE, NOT AN IMPORTER. It turns words into headers and rows and says what it read, in a\n// sentence. What a column IS in a table that already exists is the store's (`custom.io_import_plan`);\n// `guessColumn` only proposes the types of a NEW table's columns, and the person changes any of it\n// before anything is made.\n\nimport { codeSpanText, mapInlineCodeSpans } from \"@ai-matrx/content-ir/source\";\nimport { kindValueOf } from \"./kindValues\";\nimport { humanize } from \"./names\";\n\n/** What kind of thing the rows were read from. */\nexport type TableShapeKind = \"table\" | \"csv\" | \"tsv\" | \"list\" | \"key_value\" | \"kind\" | \"rows\";\n\n/** Another honest way to read a list: every item split at the same delimiter. */\nexport interface TableShapeSplit {\n /** The delimiter, exactly as it sits in the items. */\n delimiter: string;\n /** The choice in words (\"Split at the dash\"). */\n label: string;\n headers: string[];\n rows: Array<Record<string, string>>;\n}\n\nexport interface TableShape {\n kind: TableShapeKind;\n /** Column labels, unique and never blank, in the order the text has them. */\n headers: string[];\n /** Every row, keyed by label. A cell the text left empty is \"\". */\n rows: Array<Record<string, string>>;\n /** One sentence a person reads: what was found and how big it is. */\n said: string;\n /** A name the text itself suggests (the heading above it), or null. */\n name: string | null;\n /** For a list: the other readings on offer. Empty for everything else. */\n splits: TableShapeSplit[];\n /** Where the shape sits in the text, as character offsets (a kind value / JSON value has none). */\n at: { start: number; end: number } | null;\n}\n\n/** The fewest items a plain list needs before it reads as rows (\"even a few bullet points\"). */\nexport const FEWEST_LIST_ITEMS = 2;\n/** The fewest `Key: value` lines that read as one record. */\nexport const FEWEST_KEY_VALUE_PAIRS = 2;\n/** Past this many characters a text is not read at all — a paste that big is a file, and the file import reads files. */\nexport const LONGEST_TEXT_READ = 2_000_000;\n\n// ── cells ──────────────────────────────────────────────────────────────────\n\n/** A markdown cell as the words it shows: emphasis, code ticks and link syntax removed (code spans by THE rule, `findInlineCodeSpans`). */\nexport function cellWords(raw: string): string {\n return mapInlineCodeSpans(raw, (_range, span) => codeSpanText(span))\n .replace(/<br\\s*\\/?>/gi, \"\\n\")\n .replace(/!\\[([^\\]]*)\\]\\([^)]*\\)/g, \"$1\")\n .replace(/\\[([^\\]]+)\\]\\(([^)]*)\\)/g, \"$1\")\n .replace(/\\*\\*([^*]+)\\*\\*/g, \"$1\")\n .replace(/__([^_]+)__/g, \"$1\")\n .replace(/(^|[^\\w*])\\*([^*\\n]+)\\*(?=[^\\w*]|$)/g, \"$1$2\")\n .replace(/(^|[^\\w_])_([^_\\n]+)_(?=[^\\w_]|$)/g, \"$1$2\")\n .replace(/~~([^~]+)~~/g, \"$1\")\n .replace(/\\\\\\|/g, \"|\")\n .trim();\n}\n\n/** Labels made unique and never blank: a second \"Name\" is \"Name 2\", a blank is \"Column 3\". */\nexport function uniqueLabels(raw: readonly string[]): string[] {\n const seen = new Map<string, number>();\n return raw.map((label, index) => {\n const base = label.trim() || `Column ${index + 1}`;\n const key = base.toLowerCase();\n const count = (seen.get(key) ?? 0) + 1;\n seen.set(key, count);\n return count === 1 ? base : `${base} ${count}`;\n });\n}\n\nfunction rowsOf(headers: string[], matrix: string[][]): Array<Record<string, string>> {\n return matrix\n .filter((cells) => cells.some((c) => c.trim() !== \"\"))\n .map((cells) => {\n const row: Record<string, string> = {};\n headers.forEach((h, i) => {\n row[h] = (cells[i] ?? \"\").trim();\n });\n return row;\n });\n}\n\nfunction plural(n: number, one: string, many: string): string {\n return `${n} ${n === 1 ? one : many}`;\n}\n\nfunction sizeWords(rows: number, columns: number): string {\n return `${plural(rows, \"row\", \"rows\")} and ${plural(columns, \"column\", \"columns\")}`;\n}\n\n// ── the name the text suggests ─────────────────────────────────────────────\n\nconst HEADING = /^\\s{0,3}#{1,6}\\s+(.+?)\\s*#*\\s*$/;\nconst BOLD_LINE = /^\\s*\\*\\*([^*]+)\\*\\*:?\\s*$/;\nconst LEAD_IN = /^\\s*([^|\\-*+#>`].{1,80}?):\\s*$/;\n\n/**\n * The name the text gives what follows: the line right above it when that is a heading, a bold\n * line or a lead-in ending in a colon (\"Exercises:\"), else the nearest heading within a few lines.\n */\nfunction nameAbove(lines: string[], lineIndex: number): string | null {\n let looked = 0;\n for (let i = lineIndex - 1; i >= 0 && looked < 4; i -= 1) {\n const line = lines[i] ?? \"\";\n if (line.trim() === \"\") continue;\n looked += 1;\n const m = HEADING.exec(line) ?? (looked === 1 ? (BOLD_LINE.exec(line) ?? LEAD_IN.exec(line)) : null);\n if (m) return cellWords(m[1]!).replace(/:$/, \"\").trim() || null;\n }\n return null;\n}\n\n// ── markdown tables ────────────────────────────────────────────────────────\n\nconst SEPARATOR = /^\\s*\\|?\\s*:?-{2,}:?\\s*(\\|\\s*:?-{2,}:?\\s*)*\\|?\\s*$/;\n\nfunction splitPipes(line: string): string[] {\n let text = line.trim();\n if (text.startsWith(\"|\")) text = text.slice(1);\n if (text.endsWith(\"|\") && !text.endsWith(\"\\\\|\")) text = text.slice(0, -1);\n const cells: string[] = [];\n let cell = \"\";\n for (let i = 0; i < text.length; i += 1) {\n const ch = text[i];\n if (ch === \"\\\\\" && text[i + 1] === \"|\") {\n cell += \"\\\\|\";\n i += 1;\n } else if (ch === \"|\") {\n cells.push(cell);\n cell = \"\";\n } else cell += ch;\n }\n cells.push(cell);\n return cells.map(cellWords);\n}\n\n// ── lists and key:value lines ──────────────────────────────────────────────\n\nconst LIST_ITEM = /^(\\s*)(?:[-*+•]|\\d{1,3}[.)])\\s+(?:\\[[ xX]\\]\\s+)?(.*)$/;\n/** `Key: value` — the key short, the kind of words a column is named with. */\nconst KEY_VALUE = /^\\s*(?:\\*\\*|__)?([^:*_|]{1,40}?)(?::(?:\\*\\*|__)|(?:\\*\\*|__):|:)\\s+(.+)$/;\n\ninterface ListItem {\n depth: number;\n text: string;\n children: string[];\n}\n\nfunction keyValueOf(text: string): { key: string; value: string } | null {\n const m = KEY_VALUE.exec(text);\n if (!m) return null;\n const key = cellWords(m[1]!);\n const value = cellWords(m[2]!);\n if (!key || !value) return null;\n // A key is a column's name: a few words, not a clause, not a time of day (\"10:30\").\n if (key.split(/\\s+/).length > 5 || /^\\d+$/.test(key) || /https?$/i.test(key)) return null;\n return { key, value };\n}\n\n/** Records from key:value pairs: a new record starts each time the first key comes round again. */\nfunction recordsFromPairs(pairs: Array<{ key: string; value: string }>): Array<Map<string, string>> {\n const records: Array<Map<string, string>> = [];\n let current = new Map<string, string>();\n const first = pairs[0]?.key.toLowerCase();\n for (const pair of pairs) {\n const k = pair.key.toLowerCase();\n if (current.size > 0 && (k === first || [...current.keys()].some((c) => c.toLowerCase() === k))) {\n records.push(current);\n current = new Map();\n }\n current.set(pair.key, pair.value);\n }\n if (current.size > 0) records.push(current);\n return records;\n}\n\nfunction shapeFromRecords(\n records: Array<Map<string, string>>,\n base: Omit<TableShape, \"headers\" | \"rows\" | \"said\" | \"kind\" | \"splits\">,\n from: string,\n): TableShape {\n const order: string[] = [];\n for (const r of records) for (const k of r.keys()) if (!order.some((o) => o.toLowerCase() === k.toLowerCase())) order.push(k);\n const headers = uniqueLabels(order);\n const rows = records.map((r) => {\n const row: Record<string, string> = {};\n order.forEach((k, i) => {\n const hit = [...r.entries()].find(([rk]) => rk.toLowerCase() === k.toLowerCase());\n row[headers[i]!] = hit ? hit[1] : \"\";\n });\n return row;\n });\n return {\n ...base,\n kind: \"key_value\",\n headers,\n rows,\n splits: [],\n said:\n rows.length === 1\n ? `One record with ${plural(headers.length, \"value\", \"values\")} from ${from}`\n : `${sizeWords(rows.length, headers.length)} from ${from}`,\n };\n}\n\nconst SPLITS: Array<{ delimiter: string; label: string }> = [\n { delimiter: \" — \", label: \"Split at the dash\" },\n { delimiter: \" – \", label: \"Split at the dash\" },\n { delimiter: \" - \", label: \"Split at the hyphen\" },\n { delimiter: \": \", label: \"Split at the colon\" },\n { delimiter: \" | \", label: \"Split at the bar\" },\n { delimiter: \"\\t\", label: \"Split at the tab\" },\n { delimiter: \", \", label: \"Split at each comma\" },\n];\n\nfunction listSplits(items: string[]): TableShapeSplit[] {\n const out: TableShapeSplit[] = [];\n for (const { delimiter, label } of SPLITS) {\n if (!items.every((t) => t.includes(delimiter))) continue;\n if (out.some((s) => s.label === label)) continue;\n const every = delimiter === \", \";\n const parts = items.map((t) => (every ? t.split(delimiter) : [t.slice(0, t.indexOf(delimiter)), t.slice(t.indexOf(delimiter) + delimiter.length)]));\n const width = Math.max(...parts.map((p) => p.length));\n const headers = uniqueLabels(width === 2 ? [\"Item\", \"Detail\"] : Array.from({ length: width }, (_, i) => (i === 0 ? \"Item\" : `Detail ${i}`)));\n out.push({ delimiter, label, headers, rows: rowsOf(headers, parts.map((p) => p.map((c) => cellWords(c)))) });\n }\n return out;\n}\n\nfunction shapeFromList(items: ListItem[], name: string | null, at: TableShape[\"at\"]): TableShape | null {\n const top = items.filter((i) => i.depth === 0);\n if (top.length === 0) return null;\n // (1) Every item names a record and its children are that record's values.\n const withChildren = top.filter((i) => i.children.length > 0);\n if (withChildren.length === top.length && top.length >= 1) {\n const childPairs = top.map((i) => i.children.map(keyValueOf));\n if (childPairs.every((ps) => ps.length > 0 && ps.every((p) => p !== null))) {\n const lead = \"Name\";\n const records = top.map((item, idx) => {\n const r = new Map<string, string>();\n r.set(lead, cellWords(item.text).replace(/:$/, \"\"));\n for (const p of childPairs[idx]!) r.set(p!.key, p!.value);\n return r;\n });\n return shapeFromRecords(records, { name, at }, \"a list\");\n }\n }\n // (2) Every item is `Key: value` — one record, or several when the keys come round again.\n const pairs = top.map((i) => keyValueOf(i.text));\n if (pairs.length >= FEWEST_KEY_VALUE_PAIRS && pairs.every((p) => p !== null)) {\n const records = recordsFromPairs(pairs as Array<{ key: string; value: string }>);\n const sameKeys = records.every((r) => r.size === records[0]!.size);\n if (records.length === 1 || (sameKeys && records[0]!.size >= 2)) return shapeFromRecords(records, { name, at }, \"a list\");\n }\n // (3) A plain list — one column, or split, and the children kept beside their item.\n if (top.length < FEWEST_LIST_ITEMS) return null;\n const texts = top.map((i) => cellWords(i.text));\n const anyChildren = top.some((i) => i.children.length > 0);\n const headers = anyChildren ? [\"Item\", \"Details\"] : [\"Item\"];\n const rows = top.map((item, idx) => {\n const row: Record<string, string> = { Item: texts[idx]! };\n if (anyChildren) row.Details = item.children.map(cellWords).join(\"; \");\n return row;\n });\n return {\n kind: \"list\",\n headers,\n rows,\n name,\n at,\n splits: anyChildren ? [] : listSplits(texts),\n said: `${plural(rows.length, \"item\", \"items\")} from a list`,\n };\n}\n\n// ── CSV / TSV ──────────────────────────────────────────────────────────────\n\n/** RFC-4180 enough: quotes, doubled quotes and newlines inside quotes. */\nexport function parseDelimited(text: string, delimiter: \",\" | \"\\t\"): string[][] {\n const rows: string[][] = [];\n let row: string[] = [];\n let cell = \"\";\n let quoted = false;\n for (let i = 0; i < text.length; i += 1) {\n const ch = text[i];\n if (quoted) {\n if (ch === '\"') {\n if (text[i + 1] === '\"') {\n cell += '\"';\n i += 1;\n } else quoted = false;\n } else cell += ch;\n continue;\n }\n if (ch === '\"' && cell.trim() === \"\") quoted = true;\n else if (ch === delimiter) {\n row.push(cell);\n cell = \"\";\n } else if (ch === \"\\n\" || ch === \"\\r\") {\n if (ch === \"\\r\" && text[i + 1] === \"\\n\") i += 1;\n row.push(cell);\n rows.push(row);\n row = [];\n cell = \"\";\n } else cell += ch;\n }\n if (cell.length > 0 || row.length > 0) {\n row.push(cell);\n rows.push(row);\n }\n return rows.filter((r) => r.some((c) => c.trim() !== \"\"));\n}\n\n/** A whole text that is delimited rows: every line the same width, at least two columns. */\nfunction delimitedShape(text: string, delimiter: \",\" | \"\\t\", fenced: boolean, at: TableShape[\"at\"], name: string | null): TableShape | null {\n const matrix = parseDelimited(text.trim(), delimiter);\n if (matrix.length < 2) return null;\n const width = matrix[0]!.length;\n if (width < 2) return null;\n const same = matrix.filter((r) => r.length === width).length;\n if (same / matrix.length < 0.9) return null;\n if (!fenced) {\n // Prose has commas too. A CSV cell is short; a sentence is not.\n const cells = matrix.flat();\n const long = cells.filter((c) => c.trim().length > 80).length;\n if (delimiter === \",\" && (long / cells.length > 0.2 || matrix.length < 2)) return null;\n }\n const headers = uniqueLabels(matrix[0]!.map((c) => c.trim()));\n const rows = rowsOf(headers, matrix.slice(1));\n if (rows.length === 0) return null;\n const kind = delimiter === \"\\t\" ? \"tsv\" : \"csv\";\n return {\n kind,\n headers,\n rows,\n name,\n at,\n splits: [],\n said: `${sizeWords(rows.length, headers.length)} from ${kind === \"tsv\" ? \"pasted spreadsheet cells\" : \"CSV\"}`,\n };\n}\n\n// ── JSON and kind values ───────────────────────────────────────────────────\n\nfunction cellOf(value: unknown): string {\n if (value === null || value === undefined) return \"\";\n if (typeof value === \"string\") return value;\n if (typeof value === \"number\" || typeof value === \"boolean\") return String(value);\n if (Array.isArray(value) && value.every((v) => typeof v !== \"object\" || v === null)) return value.map(cellOf).join(\", \");\n // A nested object (a kind value included) is kept whole, `__kind` and all — it is part of the data.\n return JSON.stringify(value);\n}\n\nfunction isPlainObject(v: unknown): v is Record<string, unknown> {\n return typeof v === \"object\" && v !== null && !Array.isArray(v);\n}\n\n/** A key as a column's label: `body_area` → \"Body area\"; a key already in words stays as written. */\nfunction labelOfKey(key: string): string {\n return /[\\s]/.test(key) || /^[A-Z]/.test(key) ? key : humanize(key);\n}\n\nfunction shapeFromObjects(objects: Array<Record<string, unknown>>, kind: TableShapeKind, from: string, name: string | null): TableShape | null {\n if (objects.length === 0) return null;\n const keys: string[] = [];\n for (const o of objects) for (const k of Object.keys(o)) if (k !== \"__kind\" && !keys.includes(k)) keys.push(k);\n if (keys.length === 0) return null;\n const headers = uniqueLabels(keys.map(labelOfKey));\n const rows = objects.map((o) => {\n const row: Record<string, string> = {};\n keys.forEach((k, i) => {\n row[headers[i]!] = cellOf(o[k]);\n });\n return row;\n });\n return { kind, headers, rows, name, at: null, splits: [], said: `${sizeWords(rows.length, headers.length)} from ${from}` };\n}\n\n/**\n * THE ROWS A VALUE HOLDS — a kind value (`__kind`, or a Matrx directive read by the one decoder),\n * a JSON array of objects, or an object whose largest member is one. Null when it holds none.\n */\nexport function shapeOfValue(value: unknown, name: string | null = null): TableShape | null {\n let v: unknown = value;\n if (typeof v === \"string\") {\n const t = v.trim();\n const fence = /^```(?:json|matrx)?\\s*\\n?([\\s\\S]*?)\\n?```$/.exec(t);\n const body = fence ? fence[1]!.trim() : t;\n if (!(body.startsWith(\"{\") || body.startsWith(\"[\"))) return null;\n try {\n v = JSON.parse(body);\n } catch {\n return null;\n }\n }\n if (Array.isArray(v)) {\n const objects = v.filter(isPlainObject);\n if (objects.length === 0 || objects.length < v.length) return null;\n return shapeFromObjects(objects, \"rows\", \"JSON rows\", name);\n }\n if (!isPlainObject(v)) return null;\n const kv = kindValueOf(v);\n if (kv) {\n const label = kv.kind.replace(/^directive_v\\d+_/, \"\").replace(/_/g, \" \");\n if (kv.items.length > 0) return shapeFromObjects(kv.items, \"kind\", `a ${label}`, name);\n const scalars = Object.fromEntries(Object.entries(v).filter(([k, x]) => k !== \"__kind\" && (typeof x !== \"object\" || x === null)));\n const shape = shapeFromObjects([scalars], \"kind\", `a ${label}`, name);\n if (shape) return shape;\n }\n // An object carrying one list of records: {\"exercises\": [{…}, {…}]} — the largest one.\n let best: { key: string; items: Array<Record<string, unknown>> } | null = null;\n for (const [key, member] of Object.entries(v)) {\n if (Array.isArray(member) && member.length > 0 && member.every(isPlainObject)) {\n if (!best || member.length > best.items.length) best = { key, items: member };\n }\n }\n if (best) return shapeFromObjects(best.items, kv ? \"kind\" : \"rows\", \"JSON rows\", name ?? labelOfKey(best.key));\n return null;\n}\n\n// ── the document reader ────────────────────────────────────────────────────\n\n/**\n * EVERY SHAPE IN A TEXT, top to bottom: markdown tables, lists, runs of `Key: value` lines,\n * fenced CSV / TSV / JSON blocks — and when the whole text is one CSV or TSV paste, that.\n * An empty array means the text holds nothing that reads as rows, and a menu offers nothing.\n */\nexport function readTableShapes(text: string): TableShape[] {\n if (typeof text !== \"string\" || text.trim() === \"\" || text.length > LONGEST_TEXT_READ) return [];\n const whole = text.trim();\n\n // A whole value (a kind value / JSON rows pasted or stored as the content).\n if (whole.startsWith(\"{\") || whole.startsWith(\"[\") || /^```(?:json|matrx)/.test(whole)) {\n const s = shapeOfValue(whole);\n if (s) return [{ ...s, at: { start: 0, end: text.length } }];\n }\n // A whole paste of spreadsheet cells or CSV (no markdown at all).\n if (!/^\\s*[|#>*-]/m.test(whole)) {\n if (whole.includes(\"\\t\")) {\n const s = delimitedShape(whole, \"\\t\", false, { start: 0, end: text.length }, null);\n if (s) return [s];\n }\n const s = delimitedShape(whole, \",\", false, { start: 0, end: text.length }, null);\n if (s) return [s];\n }\n\n const lines = text.split(/\\r?\\n/);\n const offsets: number[] = [];\n let pos = 0;\n for (const line of lines) {\n offsets.push(pos);\n pos += line.length + 1;\n }\n const endOf = (i: number) => (offsets[i] ?? 0) + (lines[i]?.length ?? 0);\n const shapes: TableShape[] = [];\n let i = 0;\n while (i < lines.length) {\n const line = lines[i] ?? \"\";\n\n // Fenced blocks: CSV / TSV / JSON are read; any other fence is skipped whole.\n const fence = /^\\s*```\\s*([\\w-]*)\\s*$/.exec(line);\n if (fence) {\n let j = i + 1;\n while (j < lines.length && !/^\\s*```\\s*$/.test(lines[j] ?? \"\")) j += 1;\n const body = lines.slice(i + 1, j).join(\"\\n\");\n const lang = fence[1]!.toLowerCase();\n const at = { start: offsets[i]!, end: endOf(Math.min(j, lines.length - 1)) };\n const name = nameAbove(lines, i);\n let s: TableShape | null = null;\n if (lang === \"csv\") s = delimitedShape(body, \",\", true, at, name);\n else if (lang === \"tsv\") s = delimitedShape(body, \"\\t\", true, at, name);\n else if (lang === \"json\" || lang === \"matrx\") s = shapeOfValue(body, name);\n if (s) shapes.push({ ...s, at });\n i = j + 1;\n continue;\n }\n\n // A markdown table: a row of pipes, then the separator row.\n if (line.includes(\"|\") && SEPARATOR.test(lines[i + 1] ?? \"\") && !SEPARATOR.test(line)) {\n const headers = uniqueLabels(splitPipes(line));\n let j = i + 2;\n const matrix: string[][] = [];\n while (j < lines.length && (lines[j] ?? \"\").includes(\"|\") && (lines[j] ?? \"\").trim() !== \"\") {\n matrix.push(splitPipes(lines[j]!));\n j += 1;\n }\n const rows = rowsOf(headers, matrix);\n if (headers.length >= 1 && rows.length >= 1) {\n shapes.push({\n kind: \"table\",\n headers,\n rows,\n name: nameAbove(lines, i),\n at: { start: offsets[i]!, end: endOf(j - 1) },\n splits: [],\n said: `${sizeWords(rows.length, headers.length)} from a table`,\n });\n }\n i = j;\n continue;\n }\n\n // A list: consecutive items (a single blank line between items is still the same list).\n const item = LIST_ITEM.exec(line);\n if (item) {\n const baseIndent = item[1]!.length;\n const items: ListItem[] = [];\n let j = i;\n while (j < lines.length) {\n const l = lines[j] ?? \"\";\n const m = LIST_ITEM.exec(l);\n if (m) {\n const depth = m[1]!.length > baseIndent ? 1 : 0;\n if (depth === 0) items.push({ depth: 0, text: m[2]!, children: [] });\n else if (items.length > 0) items[items.length - 1]!.children.push(m[2]!);\n j += 1;\n continue;\n }\n if (l.trim() === \"\" && LIST_ITEM.test(lines[j + 1] ?? \"\")) {\n j += 1;\n continue;\n }\n // A wrapped line of the item above it.\n if (l.trim() !== \"\" && /^\\s{2,}\\S/.test(l) && items.length > 0) {\n items[items.length - 1]!.text += ` ${l.trim()}`;\n j += 1;\n continue;\n }\n break;\n }\n const s = shapeFromList(items, nameAbove(lines, i), { start: offsets[i]!, end: endOf(j - 1) });\n if (s) shapes.push(s);\n i = j;\n continue;\n }\n\n // A run of `Key: value` lines outside a list (a form result, a record written out).\n if (keyValueOf(line)) {\n let j = i;\n const pairs: Array<{ key: string; value: string }> = [];\n while (j < lines.length) {\n const l = lines[j] ?? \"\";\n const p = keyValueOf(l);\n if (p) {\n pairs.push(p);\n j += 1;\n continue;\n }\n if (l.trim() === \"\" && keyValueOf(lines[j + 1] ?? \"\")) {\n j += 1;\n continue;\n }\n break;\n }\n if (pairs.length >= FEWEST_KEY_VALUE_PAIRS) {\n const records = recordsFromPairs(pairs);\n const sameKeys = records.every((r) => r.size === records[0]!.size);\n if (records.length === 1 || (sameKeys && records[0]!.size >= 2)) {\n shapes.push(shapeFromRecords(records, { name: nameAbove(lines, i), at: { start: offsets[i]!, end: endOf(j - 1) } }, \"key and value lines\"));\n } else {\n // Keys that come round in no fixed order are a conversation: who, and what they said.\n const headers = [\"Name\", \"Text\"];\n shapes.push({\n kind: \"key_value\",\n headers,\n rows: pairs.map((p) => ({ Name: p.key, Text: p.value })),\n name: nameAbove(lines, i),\n at: { start: offsets[i]!, end: endOf(j - 1) },\n splits: [],\n said: `${plural(pairs.length, \"line\", \"lines\")}, each a name and what was said`,\n });\n }\n }\n i = Math.max(j, i + 1);\n continue;\n }\n i += 1;\n }\n return shapes;\n}\n\n/** The first shape in a text, or null — what a one-shape menu item acts on. */\nexport function readTableShape(text: string): TableShape | null {\n return readTableShapes(text)[0] ?? null;\n}\n\n/** True when the text holds anything that reads as rows — every menu's \"offer it?\" predicate. */\nexport function hasTableShape(text: string): boolean {\n return readTableShapes(text).length > 0;\n}\n\n/** Headers + string rows — the shape a table renderer already holds, read as a TableShape. */\nexport function shapeOfGrid(headers: readonly string[], cells: ReadonlyArray<ReadonlyArray<string>>, name: string | null = null): TableShape | null {\n const labels = uniqueLabels(headers.map((h) => cellWords(String(h ?? \"\"))));\n const rows = rowsOf(labels, cells.map((r) => r.map((c) => cellWords(String(c ?? \"\")))));\n if (labels.length === 0) return null;\n // Column names with no rows under them are still a table to make (the file importer's header-only\n // file: \"the table will be created with these columns and no rows yet\").\n const said = rows.length === 0 ? `${plural(labels.length, \"column\", \"columns\")} and no rows yet` : `${sizeWords(rows.length, labels.length)} from a table`;\n return { kind: \"table\", headers: labels, rows, name, at: null, splits: [], said };\n}\n\n// ── what a NEW table's columns should be ───────────────────────────────────\n\n/** A type word `custom.field_declare` takes, from `FIELD_TYPE_CHOICES`. */\nexport type GuessedFieldType =\n | \"text\"\n | \"long_text\"\n | \"number\"\n | \"currency\"\n | \"percent\"\n | \"datetime\"\n | \"checkbox\"\n | \"email\"\n | \"url\"\n | \"phone\"\n | \"select\";\n\nexport interface ColumnGuess {\n type: GuessedFieldType;\n /** A choice column's words, in the order the text first used them. */\n options?: string[];\n /** A money column's symbol. */\n unit?: string;\n /** A date column that never carries a time of day. */\n kind?: \"date\" | \"datetime\";\n}\n\n/** The most words a choice column's list may hold before it reads as text. */\nexport const MOST_GUESSED_CHOICES = 12;\n\nconst NUMBER = /^[+-]?(?:\\d{1,3}(?:,\\d{3})+|\\d+)(?:\\.\\d+)?$/;\nconst MONEY = /^([$€£¥])\\s?([+-]?(?:\\d{1,3}(?:,\\d{3})+|\\d+)(?:\\.\\d+)?)$/;\nconst PERCENT = /^[+-]?\\d+(?:\\.\\d+)?\\s?%$/;\nconst ISO_DATE = /^\\d{4}-\\d{2}-\\d{2}(?:[T ]\\d{2}:\\d{2}(?::\\d{2}(?:\\.\\d+)?)?(?:Z|[+-]\\d{2}:?\\d{2})?)?$/;\nconst SLASH_DATE = /^\\d{1,2}\\/\\d{1,2}\\/\\d{2,4}$/;\nconst WORD_DATE = /^(?:\\d{1,2}\\s+)?(?:jan|feb|mar|apr|may|jun|jul|aug|sep|sept|oct|nov|dec)[a-z]*\\.?\\s+\\d{1,2}(?:st|nd|rd|th)?,?\\s+\\d{4}$|^\\d{1,2}\\s+(?:jan|feb|mar|apr|may|jun|jul|aug|sep|sept|oct|nov|dec)[a-z]*\\.?\\s+\\d{4}$/i;\nconst EMAIL = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]{2,}$/;\nconst URL_WORD = /^https?:\\/\\/\\S+$/i;\nconst PHONE = /^\\+?[\\d\\s().-]{7,20}$/;\nconst YES = new Set([\"yes\", \"true\", \"y\", \"✓\", \"✔\", \"checked\", \"done\"]);\nconst NO = new Set([\"no\", \"false\", \"n\", \"✗\", \"✘\", \"unchecked\", \"\"]);\n\nfunction isDate(v: string): boolean {\n return ISO_DATE.test(v) || SLASH_DATE.test(v) || WORD_DATE.test(v);\n}\n\n/**\n * WHAT A NEW TABLE'S COLUMN SHOULD HOLD, proposed from every value in it. A column is a number, a\n * date, a yes/no, an email… only when EVERY non-blank value reads as one — one value that does not\n * (\"10-15\" among the Reps) keeps the column text, because a guess that loses a person's words is\n * worse than a text column. A few short words that repeat are a Choice, with those words as its\n * choices. It is a proposal the person sees and changes before anything is made.\n */\nexport function guessColumn(values: ReadonlyArray<string>): ColumnGuess {\n const filled = values.map((v) => String(v ?? \"\").trim()).filter((v) => v !== \"\");\n if (filled.length === 0) return { type: \"text\" };\n const every = (test: (v: string) => boolean) => filled.every(test);\n if (every((v) => YES.has(v.toLowerCase()) || NO.has(v.toLowerCase())) && filled.some((v) => YES.has(v.toLowerCase())) && filled.some((v) => !/^\\d+$/.test(v))) {\n return { type: \"checkbox\" };\n }\n if (every((v) => NUMBER.test(v))) return { type: \"number\" };\n if (every((v) => MONEY.test(v))) {\n const symbols = new Set(filled.map((v) => MONEY.exec(v)![1]!));\n if (symbols.size === 1) return { type: \"currency\", unit: [...symbols][0]! };\n }\n if (every((v) => PERCENT.test(v))) return { type: \"percent\" };\n if (every(isDate)) return { type: \"datetime\", kind: every((v) => !/\\d:\\d/.test(v)) ? \"date\" : \"datetime\" };\n if (every((v) => EMAIL.test(v))) return { type: \"email\" };\n if (every((v) => URL_WORD.test(v))) return { type: \"url\" };\n if (every((v) => PHONE.test(v) && (v.match(/\\d/g)?.length ?? 0) >= 7)) return { type: \"phone\" };\n if (filled.some((v) => v.length > 120 || v.includes(\"\\n\"))) return { type: \"long_text\" };\n const distinct: string[] = [];\n for (const v of filled) if (!distinct.some((d) => d.toLowerCase() === v.toLowerCase())) distinct.push(v);\n const short = every((v) => v.length <= 40 && v.split(/\\s+/).length <= 5);\n // Counts and ranges (\"10\", \"10-15\") repeat too, and they are measurements, not a list to pick from.\n const words = filled.filter((v) => !/\\d/.test(v)).length >= filled.length / 2;\n if (filled.length >= 3 && short && words && distinct.length < filled.length && distinct.length <= MOST_GUESSED_CHOICES) {\n return { type: \"select\", options: distinct };\n }\n return { type: \"text\" };\n}\n\n// ── what a selection over RENDERED content holds ───────────────────────────\n\nconst BLOCK_TAGS = new Set([\"P\", \"DIV\", \"SECTION\", \"ARTICLE\", \"H1\", \"H2\", \"H3\", \"H4\", \"H5\", \"H6\", \"BLOCKQUOTE\", \"PRE\", \"HEADER\", \"FOOTER\", \"BR\", \"HR\", \"DL\", \"DT\", \"DD\"]);\n\n/**\n * THE WORDS OF A SELECTION, WITH ITS SHAPE KEPT (VERIFIER-30 #1). A browser selection over a rendered\n * answer flattens to text in which a table's header cells come one per line and a list's bullets are\n * gone, so `readTableShapes` found nothing in exactly the places a person selects rows. This walks the\n * selected DOM (a `Range.cloneContents()` fragment, or any node) and writes it back as the shapes it\n * was drawn from: a table's rows as a markdown table (the first row its heading), a list's items as\n * bullets (nested items indented), every other block on its own line.\n */\nexport function shapeTextOfNode(node: Node): string {\n const out: string[] = [];\n let line = \"\";\n let rows: string[][] = [];\n const flushLine = () => {\n if (line.trim() !== \"\") out.push(line.trim());\n line = \"\";\n };\n const flushRows = () => {\n if (rows.length === 0) return;\n flushLine();\n const width = Math.max(...rows.map((r) => r.length));\n const pad = (r: string[]) => [...r, ...Array.from({ length: width - r.length }, () => \"\")];\n const cell = (c: string) => c.replace(/\\|/g, \"\\\\|\").replace(/\\s+/g, \" \").trim();\n out.push(`| ${pad(rows[0]!).map(cell).join(\" | \")} |`);\n out.push(`|${Array.from({ length: width }, () => \"---\").join(\"|\")}|`);\n for (const r of rows.slice(1)) out.push(`| ${pad(r).map(cell).join(\" | \")} |`);\n rows = [];\n };\n const textOf = (n: Node): string => (n.textContent ?? \"\").replace(/\\s+/g, \" \").trim();\n const walk = (n: Node, depth: number): void => {\n if (n.nodeType === 3) {\n line += n.textContent ?? \"\";\n return;\n }\n if (n.nodeType !== 1 && n.nodeType !== 11) return;\n const el = n as Element;\n const tag = n.nodeType === 1 ? el.tagName.toUpperCase() : \"#fragment\";\n if (tag === \"TR\") {\n const cells = Array.from(el.children).filter((c) => /^(TD|TH)$/i.test(c.tagName));\n rows.push(cells.map((c) => textOf(c)));\n return;\n }\n if (tag === \"TABLE\" || tag === \"THEAD\" || tag === \"TBODY\" || tag === \"TFOOT\") {\n for (const child of Array.from(n.childNodes)) walk(child, depth);\n if (tag === \"TABLE\") flushRows();\n return;\n }\n flushRows();\n if (tag === \"LI\") {\n flushLine();\n const own: string[] = [];\n const nested: Node[] = [];\n for (const child of Array.from(n.childNodes)) {\n if (child.nodeType === 1 && /^(UL|OL)$/i.test((child as Element).tagName)) nested.push(child);\n else own.push(child.textContent ?? \"\");\n }\n const words = own.join(\"\").replace(/\\s+/g, \" \").trim();\n if (words) out.push(`${\" \".repeat(depth)}- ${words}`);\n for (const list of nested) walk(list, depth + 1);\n return;\n }\n if (tag === \"UL\" || tag === \"OL\") {\n flushLine();\n for (const child of Array.from(n.childNodes)) walk(child, depth);\n flushLine();\n return;\n }\n const block = BLOCK_TAGS.has(tag);\n if (block) flushLine();\n for (const child of Array.from(n.childNodes)) walk(child, depth);\n if (block) flushLine();\n };\n walk(node, 0);\n flushRows();\n flushLine();\n return out.join(\"\\n\");\n}\n","// src/kindValues.ts — @ai-matrx/records-ui · lane REFERENCE-CARRY\n//\n// A CELL THAT HOLDS A KIND. A value is a kind value when it names its own shape — `__kind` — or\n// when it is a stored Matrx directive written before the Kind Directives merge (the retired 4-key\n// shell `{\"matrx_version\": 1, \"kind\": \"reference\", \"type\": \"scope\", \"items\": [...]}`, usually inside\n// a ```matrx fence). Before this, a text cell holding either printed the raw fence: Arman's\n// Department table, 2026-09-28 — Team Leader read ```matrx {\"matrx_version\":1,…}```.\n//\n// THE SHELL IS UNDERSTOOD IN ONE PLACE. Whether an object is a directive — the current shell, or\n// the retired one stored before the merge — is `@ai-matrx/content-ir/directives`' `tryDecodeDirective`\n// alone (its containment guard keeps the retired shell there). This module only takes the object out\n// of its fence and hands it, untouched, to the host's `renderKind` port, which draws it through the\n// kind system. New values are stored as `__kind` JSON, never as a fence.\n\nimport { tryDecodeDirective } from \"@ai-matrx/content-ir/directives\";\n\n/** What a cell holds when it holds a kind. */\nexport interface KindValue {\n /** The parsed object, exactly as stored (`__kind` kept — it is part of the data). */\n value: Record<string, unknown>;\n /**\n * The kind's own name: `__kind`, or for a directive the slug the one decoder reads\n * (`directive_v1_reference_scope`, the retired shell included).\n */\n kind: string;\n /** True when the one decoder read this as a Matrx directive. */\n directive: boolean;\n /** The items a directive carries (a reference's chips), when it carries any. */\n items: Record<string, unknown>[];\n}\n\nconst KIND_KEY = \"__kind\";\n/** One fenced block and nothing else: ```matrx {…}``` or ```json {…}```. */\nconst WHOLE_FENCE = /^\\s*```(?:matrx|json)?[ \\t]*\\r?\\n?([\\s\\S]*?)\\r?\\n?[ \\t]*```\\s*$/;\n\nfunction asObject(value: unknown): Record<string, unknown> | null {\n return typeof value === \"object\" && value !== null && !Array.isArray(value) ? (value as Record<string, unknown>) : null;\n}\n\nfunction parseJsonObject(text: string): Record<string, unknown> | null {\n const trimmed = text.trim();\n if (!trimmed.startsWith(\"{\") || !trimmed.endsWith(\"}\")) return null;\n try {\n return asObject(JSON.parse(trimmed));\n } catch {\n return null;\n }\n}\n\nfunction fromObject(obj: Record<string, unknown>): KindValue | null {\n // THE ONE DECODER decides what a directive is — the current two-key shell and, for stored\n // content only, the retired 4-key one. A directive it cannot honour is not drawn as one.\n const directive = tryDecodeDirective(obj);\n if (directive) return { value: obj, kind: directive.slug, directive: true, items: [...directive.items] };\n const named = obj[KIND_KEY];\n if (typeof named === \"string\" && named.trim() !== \"\") {\n const items = Array.isArray(obj.items) ? obj.items.filter((i): i is Record<string, unknown> => asObject(i) !== null) : [];\n return { value: obj, kind: named.trim(), directive: false, items };\n }\n return null;\n}\n/**\n * The kind value a cell holds, or null. An object with `__kind`, a string that is such an object's\n * JSON, or a string that is ONE fenced block of it (```matrx / ```json), the retired directive shell\n * included. Text that merely mentions a fence among other words is text, and stays text.\n */\nexport function kindValueOf(value: unknown): KindValue | null {\n const obj = asObject(value);\n if (obj) return fromObject(obj);\n if (typeof value !== \"string\" || value.length > 200_000) return null;\n const fenced = WHOLE_FENCE.exec(value);\n const parsed = parseJsonObject(fenced ? fenced[1]! : value);\n return parsed ? fromObject(parsed) : null;\n}\n\n/** The words an item names itself by (a reference's label, a title, a name). */\nexport function kindItemWords(item: Record<string, unknown>): string | null {\n for (const key of [\"label\", \"title\", \"name\", \"display_name\"]) {\n const v = item[key];\n if (typeof v === \"string\" && v.trim() !== \"\") return v.trim();\n }\n return null;\n}\n\n/**\n * A kind value in words, for a sentence, a card, a copy or an export: the items' own names when it\n * carries named items (\"Arman Sadeghi, Tahir Mahfooz\"), else what kind of thing it is.\n */\nexport function kindValueWords(kv: KindValue): string {\n const named = kv.items.map(kindItemWords).filter((w): w is string => w !== null);\n if (named.length > 0) return named.join(\", \");\n return kindLabel(kv.kind);\n}\n\n/** A kind's name the way a person reads it: `directive_v1_reference_scope` → \"Reference scope\". */\nexport function kindLabel(kind: string): string {\n const words = kind.replace(/^directive_v\\d+_/, \"\").replace(/_v\\d+$/, \"\").replace(/_/g, \" \").trim();\n return words ? words.charAt(0).toUpperCase() + words.slice(1) : \"Structured value\";\n}\n","// src/names.ts — @ai-matrx/records-ui\n//\n// WHAT A PERSON READS, NEVER WHAT THE MACHINE STORES.\n//\n// Measured on the real screens (independent verdict, 19 September): a table row\n// read `Saved views · 9 fields · records_ui_view`, a relation cell read a bare\n// `8f2c…` identifier, a header read `stage` because the Field's label was\n// blank, and the type word on a column tooltip read `long_text`. Airtable,\n// Notion and Linear never show a person an internal name; the verdict called\n// this one of the three things a first-time user feels within a minute.\n//\n// So every place this package prints something a person reads goes through one\n// of these three functions. They are deliberately dumb: they choose the best\n// human string that IS AVAILABLE and, when none is, say what the thing is\n// rather than printing the token. No screen in this package formats a name of\n// its own any more.\n\nimport type { Field, ReadRow, RecordDocument, Table } from \"@ai-matrx/records\";\nimport { isUuidShape } from \"@ai-matrx/kit/uuid\";\n\n/**\n * A token (`records_ui_view`, `long_text`, `v78_widget2`) as a person would\n * write it. Used ONLY as a last resort, when the thing carries no label at all\n * — a raw token on a screen is the defect, and a humanised token is the least\n * bad honest answer, because it still names the same thing.\n */\nexport function humanize(token: string): string {\n const words = token\n .replace(/[_\\-.]+/g, \" \")\n .replace(/([a-z0-9])([A-Z])/g, \"$1 $2\")\n .trim();\n if (words === \"\") return token;\n return words.charAt(0).toUpperCase() + words.slice(1);\n}\n\n/** The name of one Field, as a header, a form label or a peek's term. */\nexport function fieldName(field: Field): string {\n const label = (field.label ?? \"\").trim();\n // A LABEL THAT IS ONLY A MACHINE KEY is not a name (merged-grid review 2026-09-26): a table made\n // by an import or an agent kept `label: \"job_no\"`, and the header read `job_no`. A key joined by\n // underscores is written the way a person writes it (\"Job no\"). A single lowercase word the\n // owner typed (\"mobile\", \"title\") is their own words and is shown as they wrote it.\n if (label !== \"\" && label === field.key && /^[a-z0-9]+(?:[_.-][a-z0-9]+)+$/.test(label)) return humanize(label);\n if (label !== \"\") return label;\n const name = (field.name ?? \"\").trim();\n if (name !== \"\") return name;\n return humanize(field.key);\n}\n\n/** The name of one Table, as a card, a heading or a breadcrumb. */\nexport function tableName(table: Table): string {\n const name = (table.name ?? \"\").trim();\n if (name !== \"\") return name;\n const singular = (table.label_singular ?? \"\").trim();\n if (singular !== \"\") return singular;\n return humanize(table.slug ?? \"\");\n}\n\n/**\n * The name of one RECORD, for a chip, a card, a relation cell or a peek title.\n *\n * `titleKey` is the Table's own `title_field` when the caller knows the Table —\n * that is the store's answer to \"what makes this record a chip\" (REC-2) — and\n * the conventional keys are tried after it. A record with nothing readable is\n * NOT shown as a truncated uuid: it says it has no name yet, because \"8f2c1b0a\"\n * tells a person nothing and looks like a bug.\n */\nexport function recordName(\n document: RecordDocument | undefined,\n titleKey?: string | null,\n fallback = \"Untitled\",\n): string {\n const data = (document ?? {}) as Record<string, unknown>;\n const keys = [titleKey, \"title\", \"name\", \"label\", \"full_name\", \"subject\", \"summary\"].filter(\n (key): key is string => typeof key === \"string\" && key !== \"\",\n );\n for (const key of keys) {\n const value = data[key];\n // AN ID IS NEVER A NAME, AND THIS FUNCTION USED TO RETURN ONE (TAILS-2, 2026-09-21).\n // A Table's title column may be a RELATION, and a relation's stored value IS the other\n // record's id (FLD-6 / REL-1) — so `recordName` handed every caller a bare uuid for\n // every Table titled that way, which is how the board's unplaced list came to read\n // \"11 records are not in any of these columns — ca5f1ac7-…\". A caller that can resolve\n // the id resolves it (`scalarText` with the label lookup); a caller that cannot is\n // better off reading the next column, and then the fallback, than a hex number.\n if (typeof value === \"string\" && value.trim() !== \"\" && !looksLikeId(value.trim())) {\n return value.trim();\n }\n if (typeof value === \"number\") return String(value);\n }\n return fallback;\n}\n\n\n/**\n * Is this string the store's id for something, rather than words a person wrote?\n *\n * The same question `custom._card_words` asks on the database, with the same regex, so the\n * screen and the store agree about what is a name (TAILS-2, 2026-09-21).\n */\nexport function looksLikeId(value: unknown): boolean {\n return typeof value === \"string\" && isUuidShape(value.trim());\n}\n\n/** The same question for a row the read door returned. */\nexport function rowName(row: ReadRow | undefined, titleKey?: string | null, fallback = \"Untitled\"): string {\n return recordName(row?.document, titleKey, fallback);\n}\n\n// ═══════════════════════════════════════════════════════════════════════════════════════\n// NAMING A RECORD WHOSE TABLE YOU ALREADY HOLD — ONE CALL, NOT A COPY PER SCREEN.\n// ═══════════════════════════════════════════════════════════════════════════════════════\n//\n// 🚨 WHY THESE TWO EXIST (lane RELATION-DISPLAY-2, 2026-09-21).\n//\n// `check:one-reference-builder` shipped with a baseline of SIX, and its own header said the\n// number was not zero because \"moving six surfaces onto the store door is real work\". Three\n// of the six were not asking the store's question at all. `Grid.tsx` names the row a person\n// is looking at for the Talk action; `Peek.tsx` names the record being peeked at;\n// `PortalsPanel.tsx` names the rows of the portal's own client Table; and this repo's\n// `TryEverythingScreen.tsx` does the same in two places. NONE of them is resolving a\n// RELATION — they hold the Table and they hold the document, and there is nothing to ask a\n// door about. What they were each doing by hand was the two-line join between them:\n//\n// recordName(row.document, table.title_field)\n//\n// Written out five times in four files, which is how a Table whose title column is `cname`\n// came to read \"Untitled\" on one screen and correctly on the next. It is the same question\n// as the store's, one rung down the ladder, and the same one answer should serve it — so it\n// lives HERE, in the file the guard exempts BY CONSTRUCTION as the primitive itself, and\n// every screen calls it instead of re-deriving it.\n//\n// WHAT THESE ARE NOT. They are not a second resolver and must never become one. They take a\n// Table a caller ALREADY HAS and a document it ALREADY HAS. The moment a caller is holding\n// an ID and wants words, that is the store's question and the answer is\n// `client.relationWords` / `client.relationWordsMany` — which ask the read-access ladder\n// BEFORE they read a name, apply that column's own display spec, and return the withheld\n// sentence for a record the reader may not open. Nothing in here can do any of that, and\n// nothing in here should try.\n\n/** A Table as far as naming is concerned — its own declared title column (REC-2). */\ntype TitledTable = Pick<Table, \"title_field\"> | null | undefined;\n\n/**\n * THE NAME OF A RECORD WHOSE TABLE YOU ALREADY HOLD.\n *\n * `table` is the Table the record belongs to; its `title_field` is the store's answer to\n * \"what makes this record a chip\" (REC-2) and is tried first, then the conventional keys,\n * then the fallback. A Table that has not loaded yet is `null` and the conventional keys\n * still answer — a name arriving a tick late is better than a screen that shows nothing.\n */\nexport function recordNameIn(\n table: TitledTable,\n document: RecordDocument | undefined,\n fallback = \"Untitled\",\n): string {\n return recordName(document, table?.title_field ?? null, fallback);\n}\n\n/**\n * THE NAME A CHANGE NOTICE USES — the record as it is AFTER the change (merged-grid tails,\n * 2026-09-26). Typing a first name into a row's label column read \"Changed Title on Untitled\":\n * the notice named the row from the document held before the write. The row is called what it is\n * called now; only when the change emptied its name does the notice use the name it had.\n */\nexport function changedRecordName(\n table: TitledTable,\n document: RecordDocument | undefined,\n key: string,\n after: unknown,\n): string {\n const now = recordName({ ...((document ?? {}) as Record<string, unknown>), [key]: after } as RecordDocument, table?.title_field ?? null, \"\");\n return now !== \"\" ? now : recordNameIn(table, document);\n}\n\n/** The same, for a row the read door returned. */\nexport function rowNameIn(table: TitledTable, row: ReadRow | undefined, fallback = \"Untitled\"): string {\n return recordName(row?.document, table?.title_field ?? null, fallback);\n}\n\n/**\n * WHAT A RELATION CHIP SAYS WHEN THE RECORD IT POINTS AT IS NOT YOURS.\n *\n * A relation joins two records in two tables, and holding one end says nothing\n * about the other: a Job shared with you may point at a Client nobody shared.\n * The chip then has three possible answers and only one of them is honest —\n * the target's TITLE is a leak, a bare id is a bug with a hex number in it, and\n * a permanent \"Loading…\" is a lie that never resolves.\n *\n * This is the SAME sentence `platform.relation_withheld_label()` answers on the\n * database, word for word, so a person reads one wording whether the screen or\n * the store worked it out. Changing it changes both (RELATION-DECLARE,\n * 2026-09-20).\n */\nexport const WITHHELD_RECORD_LABEL = \"A record you have not been given access to\";\n\n/**\n * THE PLURAL A PERSON READS, NEVER A MACHINE'S \"…ss\" (VERIFIER-16 M3 / VERIFIER-17 M3).\n *\n * A table the store or the mover never gave a real plural carries `name + \"s\"` — so a table\n * already named in the plural reads \"Customerss\", \"Status optionss\". That word was made by a\n * machine appending one letter, and it says nothing the name does not; it is never shown.\n * Returns the table's own plural when a person (or the example library) wrote one, and `null`\n * when the only plural on record is that appended letter.\n */\nexport function realPlural(table: { name?: string | null; label_singular?: string | null; label_plural?: string | null }): string | null {\n const plural = (table.label_plural ?? \"\").trim();\n if (plural === \"\") return null;\n for (const base of [table.name, table.label_singular]) {\n const b = (base ?? \"\").trim();\n if (b !== \"\" && plural === `${b}s`) return null;\n }\n return plural;\n}\n\n/**\n * ONE RECORD'S NOUN, IN A SENTENCE (\"Book a 30-minute visit\") — lane HANDOVER, 2026-09-28.\n *\n * Cedar Ridge Physical Therapy's booking page was titled \"Book a 30-minute Visits appointment\":\n * the table's NAME (a plural, a heading) was put where one record's noun goes. The noun is the\n * table's own singular (\"Visit\"), else its name, else the fallback; a Title-case word is written\n * the way a sentence writes it (\"visit\"), an acronym is kept (\"MRI scan\").\n */\nexport function singularNoun(\n table: { name?: string | null; label_singular?: string | null } | null | undefined,\n fallback = \"record\",\n): string {\n const word = (table?.label_singular ?? \"\").trim() || (table?.name ?? \"\").trim() || fallback;\n return word\n .split(/\\s+/)\n .map((w) => (/^[A-Z][a-z'’-]*$/.test(w) ? w.toLowerCase() : w))\n .join(\" \");\n}\n\n/**\n * A plural for a sentence (\"Check in ran on 2 appointments\"): the table's real plural, else the\n * singular with an \"s\" only when that does not double a final \"s\".\n */\nexport function pluralNoun(table: { name?: string | null; label_singular?: string | null; label_plural?: string | null } | null | undefined, fallback = \"record\"): string {\n const real = table ? realPlural(table) : null;\n if (real) return real;\n const singular = (table?.label_singular ?? \"\").trim() || fallback;\n return /s$/i.test(singular) ? singular : `${singular}s`;\n}\n\n\n// ═══════════════════════════════════════════════════════════════════════════════════════\n// WHAT A CARD IS TITLED BY (lane UI-FIX-18, VERIFIER-18 H5).\n// ═══════════════════════════════════════════════════════════════════════════════════════\n//\n// MEASURED 2026-09-24 on production: *ZZZ UI-QUICK-1 Rincon-style Jobs* (title column `title`)\n// grouped by Stage titled every kanban card \"false\" or \"true\". The board took the FIRST Field\n// the read door answered that was not the group column — here the *Urgent* checkbox — as the\n// title. A card is named by what names its record: the Table's own `title_field` first (REC-2),\n// then the conventional name keys, then the first words column in declared order. A yes/no\n// column, the column the board is grouped by and the store's own stamps never title a card.\n\nconst CONVENTIONAL_TITLE_KEYS = [\"title\", \"name\", \"label\", \"full_name\", \"subject\", \"summary\"];\n\nfunction canTitle(field: Field, excluded: ReadonlySet<string>): boolean {\n if (excluded.has(field.key)) return false;\n if (field.type === \"boolean\") return false;\n if (field.type === \"formula\" && (field.config ?? {})[\"system\"]) return false;\n return true;\n}\n\n/**\n * THE FIELD A CARD IS TITLED BY — kanban, calendar, gallery and pipeline alike.\n *\n * Order: the Table's `title_field` → a conventional name key (`title`, `name`, …) → the first\n * text column in declared `sort` order → the first other column that can title (never a\n * checkbox, never the group column, never a system stamp). `undefined` when nothing can, and\n * the card falls back to `rowName`, which says \"Untitled\" rather than \"false\".\n */\nexport function cardTitleField(\n fields: readonly Field[],\n titleKey: string | null | undefined,\n exclude: readonly (string | null | undefined)[] = [],\n): Field | undefined {\n const excluded = new Set(exclude.filter((k): k is string => typeof k === \"string\" && k !== \"\"));\n const ordered = [...fields].sort((a, b) => (a.sort ?? 100) - (b.sort ?? 100) || a.key.localeCompare(b.key));\n const byKey = (key: string) => ordered.find((f) => f.key === key && canTitle(f, excluded));\n if (titleKey) {\n const declared = byKey(titleKey);\n if (declared) return declared;\n }\n for (const key of CONVENTIONAL_TITLE_KEYS) {\n const found = byKey(key);\n if (found) return found;\n }\n return (\n ordered.find((f) => f.type === \"text\" && canTitle(f, excluded)) ?? ordered.find((f) => canTitle(f, excluded))\n );\n}\n"],"mappings":"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","names":["s","i"]}
|
|
1
|
+
{"version":3,"sources":["../src/tableShape.ts","../src/kindValues.ts"],"sourcesContent":["// src/tableShape.ts — @ai-matrx/records-ui · lane SAVE-AS-TABLE-EVERYWHERE\n//\n// WHAT IN THIS TEXT IS ALREADY A TABLE. Arman, 2026-09-29: anywhere information appears in a shape\n// the system understands — a markdown table, a list or even a few bullet points, key:value lines,\n// a CSV or TSV paste, a kind value, JSON rows — the SAME \"Save to a table\" must exist. This module\n// is the one reader of those shapes: every menu asks it whether to offer the action, and the one\n// screen (`SaveToTable`) asks it for the rows. It is pure (no React, no store) so a guard, a server\n// render and a menu predicate read the same answer.\n//\n// IT IS A PARSE, NOT AN IMPORTER. It turns words into headers and rows and says what it read, in a\n// sentence. What a column IS in a table that already exists is the store's (`custom.io_import_plan`);\n// `guessColumn` only proposes the types of a NEW table's columns, and the person changes any of it\n// before anything is made.\n\nimport { codeSpanText, mapInlineCodeSpans } from \"@ai-matrx/content-ir/source\";\nimport { kindValueOf } from \"./kindValues\";\nimport { humanizeIdentifier } from \"@ai-matrx/kit/text-case\";\n\n/** What kind of thing the rows were read from. */\nexport type TableShapeKind = \"table\" | \"csv\" | \"tsv\" | \"list\" | \"key_value\" | \"kind\" | \"rows\";\n\n/** Another honest way to read a list: every item split at the same delimiter. */\nexport interface TableShapeSplit {\n /** The delimiter, exactly as it sits in the items. */\n delimiter: string;\n /** The choice in words (\"Split at the dash\"). */\n label: string;\n headers: string[];\n rows: Array<Record<string, string>>;\n}\n\nexport interface TableShape {\n kind: TableShapeKind;\n /** Column labels, unique and never blank, in the order the text has them. */\n headers: string[];\n /** Every row, keyed by label. A cell the text left empty is \"\". */\n rows: Array<Record<string, string>>;\n /** One sentence a person reads: what was found and how big it is. */\n said: string;\n /** A name the text itself suggests (the heading above it), or null. */\n name: string | null;\n /** For a list: the other readings on offer. Empty for everything else. */\n splits: TableShapeSplit[];\n /** Where the shape sits in the text, as character offsets (a kind value / JSON value has none). */\n at: { start: number; end: number } | null;\n}\n\n/** The fewest items a plain list needs before it reads as rows (\"even a few bullet points\"). */\nexport const FEWEST_LIST_ITEMS = 2;\n/** The fewest `Key: value` lines that read as one record. */\nexport const FEWEST_KEY_VALUE_PAIRS = 2;\n/** Past this many characters a text is not read at all — a paste that big is a file, and the file import reads files. */\nexport const LONGEST_TEXT_READ = 2_000_000;\n\n// ── cells ──────────────────────────────────────────────────────────────────\n\n/** A markdown cell as the words it shows: emphasis, code ticks and link syntax removed (code spans by THE rule, `findInlineCodeSpans`). */\nexport function cellWords(raw: string): string {\n return mapInlineCodeSpans(raw, (_range, span) => codeSpanText(span))\n .replace(/<br\\s*\\/?>/gi, \"\\n\")\n .replace(/!\\[([^\\]]*)\\]\\([^)]*\\)/g, \"$1\")\n .replace(/\\[([^\\]]+)\\]\\(([^)]*)\\)/g, \"$1\")\n .replace(/\\*\\*([^*]+)\\*\\*/g, \"$1\")\n .replace(/__([^_]+)__/g, \"$1\")\n .replace(/(^|[^\\w*])\\*([^*\\n]+)\\*(?=[^\\w*]|$)/g, \"$1$2\")\n .replace(/(^|[^\\w_])_([^_\\n]+)_(?=[^\\w_]|$)/g, \"$1$2\")\n .replace(/~~([^~]+)~~/g, \"$1\")\n .replace(/\\\\\\|/g, \"|\")\n .trim();\n}\n\n/** Labels made unique and never blank: a second \"Name\" is \"Name 2\", a blank is \"Column 3\". */\nexport function uniqueLabels(raw: readonly string[]): string[] {\n const seen = new Map<string, number>();\n return raw.map((label, index) => {\n const base = label.trim() || `Column ${index + 1}`;\n const key = base.toLowerCase();\n const count = (seen.get(key) ?? 0) + 1;\n seen.set(key, count);\n return count === 1 ? base : `${base} ${count}`;\n });\n}\n\nfunction rowsOf(headers: string[], matrix: string[][]): Array<Record<string, string>> {\n return matrix\n .filter((cells) => cells.some((c) => c.trim() !== \"\"))\n .map((cells) => {\n const row: Record<string, string> = {};\n headers.forEach((h, i) => {\n row[h] = (cells[i] ?? \"\").trim();\n });\n return row;\n });\n}\n\nfunction plural(n: number, one: string, many: string): string {\n return `${n} ${n === 1 ? one : many}`;\n}\n\nfunction sizeWords(rows: number, columns: number): string {\n return `${plural(rows, \"row\", \"rows\")} and ${plural(columns, \"column\", \"columns\")}`;\n}\n\n// ── the name the text suggests ─────────────────────────────────────────────\n\nconst HEADING = /^\\s{0,3}#{1,6}\\s+(.+?)\\s*#*\\s*$/;\nconst BOLD_LINE = /^\\s*\\*\\*([^*]+)\\*\\*:?\\s*$/;\nconst LEAD_IN = /^\\s*([^|\\-*+#>`].{1,80}?):\\s*$/;\n\n/**\n * The name the text gives what follows: the line right above it when that is a heading, a bold\n * line or a lead-in ending in a colon (\"Exercises:\"), else the nearest heading within a few lines.\n */\nfunction nameAbove(lines: string[], lineIndex: number): string | null {\n let looked = 0;\n for (let i = lineIndex - 1; i >= 0 && looked < 4; i -= 1) {\n const line = lines[i] ?? \"\";\n if (line.trim() === \"\") continue;\n looked += 1;\n const m = HEADING.exec(line) ?? (looked === 1 ? (BOLD_LINE.exec(line) ?? LEAD_IN.exec(line)) : null);\n if (m) return cellWords(m[1]!).replace(/:$/, \"\").trim() || null;\n }\n return null;\n}\n\n// ── markdown tables ────────────────────────────────────────────────────────\n\nconst SEPARATOR = /^\\s*\\|?\\s*:?-{2,}:?\\s*(\\|\\s*:?-{2,}:?\\s*)*\\|?\\s*$/;\n\nfunction splitPipes(line: string): string[] {\n let text = line.trim();\n if (text.startsWith(\"|\")) text = text.slice(1);\n if (text.endsWith(\"|\") && !text.endsWith(\"\\\\|\")) text = text.slice(0, -1);\n const cells: string[] = [];\n let cell = \"\";\n for (let i = 0; i < text.length; i += 1) {\n const ch = text[i];\n if (ch === \"\\\\\" && text[i + 1] === \"|\") {\n cell += \"\\\\|\";\n i += 1;\n } else if (ch === \"|\") {\n cells.push(cell);\n cell = \"\";\n } else cell += ch;\n }\n cells.push(cell);\n return cells.map(cellWords);\n}\n\n// ── lists and key:value lines ──────────────────────────────────────────────\n\nconst LIST_ITEM = /^(\\s*)(?:[-*+•]|\\d{1,3}[.)])\\s+(?:\\[[ xX]\\]\\s+)?(.*)$/;\n/** `Key: value` — the key short, the kind of words a column is named with. */\nconst KEY_VALUE = /^\\s*(?:\\*\\*|__)?([^:*_|]{1,40}?)(?::(?:\\*\\*|__)|(?:\\*\\*|__):|:)\\s+(.+)$/;\n\ninterface ListItem {\n depth: number;\n text: string;\n children: string[];\n}\n\nfunction keyValueOf(text: string): { key: string; value: string } | null {\n const m = KEY_VALUE.exec(text);\n if (!m) return null;\n const key = cellWords(m[1]!);\n const value = cellWords(m[2]!);\n if (!key || !value) return null;\n // A key is a column's name: a few words, not a clause, not a time of day (\"10:30\").\n if (key.split(/\\s+/).length > 5 || /^\\d+$/.test(key) || /https?$/i.test(key)) return null;\n return { key, value };\n}\n\n/** Records from key:value pairs: a new record starts each time the first key comes round again. */\nfunction recordsFromPairs(pairs: Array<{ key: string; value: string }>): Array<Map<string, string>> {\n const records: Array<Map<string, string>> = [];\n let current = new Map<string, string>();\n const first = pairs[0]?.key.toLowerCase();\n for (const pair of pairs) {\n const k = pair.key.toLowerCase();\n if (current.size > 0 && (k === first || [...current.keys()].some((c) => c.toLowerCase() === k))) {\n records.push(current);\n current = new Map();\n }\n current.set(pair.key, pair.value);\n }\n if (current.size > 0) records.push(current);\n return records;\n}\n\nfunction shapeFromRecords(\n records: Array<Map<string, string>>,\n base: Omit<TableShape, \"headers\" | \"rows\" | \"said\" | \"kind\" | \"splits\">,\n from: string,\n): TableShape {\n const order: string[] = [];\n for (const r of records) for (const k of r.keys()) if (!order.some((o) => o.toLowerCase() === k.toLowerCase())) order.push(k);\n const headers = uniqueLabels(order);\n const rows = records.map((r) => {\n const row: Record<string, string> = {};\n order.forEach((k, i) => {\n const hit = [...r.entries()].find(([rk]) => rk.toLowerCase() === k.toLowerCase());\n row[headers[i]!] = hit ? hit[1] : \"\";\n });\n return row;\n });\n return {\n ...base,\n kind: \"key_value\",\n headers,\n rows,\n splits: [],\n said:\n rows.length === 1\n ? `One record with ${plural(headers.length, \"value\", \"values\")} from ${from}`\n : `${sizeWords(rows.length, headers.length)} from ${from}`,\n };\n}\n\nconst SPLITS: Array<{ delimiter: string; label: string }> = [\n { delimiter: \" — \", label: \"Split at the dash\" },\n { delimiter: \" – \", label: \"Split at the dash\" },\n { delimiter: \" - \", label: \"Split at the hyphen\" },\n { delimiter: \": \", label: \"Split at the colon\" },\n { delimiter: \" | \", label: \"Split at the bar\" },\n { delimiter: \"\\t\", label: \"Split at the tab\" },\n { delimiter: \", \", label: \"Split at each comma\" },\n];\n\nfunction listSplits(items: string[]): TableShapeSplit[] {\n const out: TableShapeSplit[] = [];\n for (const { delimiter, label } of SPLITS) {\n if (!items.every((t) => t.includes(delimiter))) continue;\n if (out.some((s) => s.label === label)) continue;\n const every = delimiter === \", \";\n const parts = items.map((t) => (every ? t.split(delimiter) : [t.slice(0, t.indexOf(delimiter)), t.slice(t.indexOf(delimiter) + delimiter.length)]));\n const width = Math.max(...parts.map((p) => p.length));\n const headers = uniqueLabels(width === 2 ? [\"Item\", \"Detail\"] : Array.from({ length: width }, (_, i) => (i === 0 ? \"Item\" : `Detail ${i}`)));\n out.push({ delimiter, label, headers, rows: rowsOf(headers, parts.map((p) => p.map((c) => cellWords(c)))) });\n }\n return out;\n}\n\nfunction shapeFromList(items: ListItem[], name: string | null, at: TableShape[\"at\"]): TableShape | null {\n const top = items.filter((i) => i.depth === 0);\n if (top.length === 0) return null;\n // (1) Every item names a record and its children are that record's values.\n const withChildren = top.filter((i) => i.children.length > 0);\n if (withChildren.length === top.length && top.length >= 1) {\n const childPairs = top.map((i) => i.children.map(keyValueOf));\n if (childPairs.every((ps) => ps.length > 0 && ps.every((p) => p !== null))) {\n const lead = \"Name\";\n const records = top.map((item, idx) => {\n const r = new Map<string, string>();\n r.set(lead, cellWords(item.text).replace(/:$/, \"\"));\n for (const p of childPairs[idx]!) r.set(p!.key, p!.value);\n return r;\n });\n return shapeFromRecords(records, { name, at }, \"a list\");\n }\n }\n // (2) Every item is `Key: value` — one record, or several when the keys come round again.\n const pairs = top.map((i) => keyValueOf(i.text));\n if (pairs.length >= FEWEST_KEY_VALUE_PAIRS && pairs.every((p) => p !== null)) {\n const records = recordsFromPairs(pairs as Array<{ key: string; value: string }>);\n const sameKeys = records.every((r) => r.size === records[0]!.size);\n if (records.length === 1 || (sameKeys && records[0]!.size >= 2)) return shapeFromRecords(records, { name, at }, \"a list\");\n }\n // (3) A plain list — one column, or split, and the children kept beside their item.\n if (top.length < FEWEST_LIST_ITEMS) return null;\n const texts = top.map((i) => cellWords(i.text));\n const anyChildren = top.some((i) => i.children.length > 0);\n const headers = anyChildren ? [\"Item\", \"Details\"] : [\"Item\"];\n const rows = top.map((item, idx) => {\n const row: Record<string, string> = { Item: texts[idx]! };\n if (anyChildren) row.Details = item.children.map(cellWords).join(\"; \");\n return row;\n });\n return {\n kind: \"list\",\n headers,\n rows,\n name,\n at,\n splits: anyChildren ? [] : listSplits(texts),\n said: `${plural(rows.length, \"item\", \"items\")} from a list`,\n };\n}\n\n// ── CSV / TSV ──────────────────────────────────────────────────────────────\n\n/** RFC-4180 enough: quotes, doubled quotes and newlines inside quotes. */\nexport function parseDelimited(text: string, delimiter: \",\" | \"\\t\"): string[][] {\n const rows: string[][] = [];\n let row: string[] = [];\n let cell = \"\";\n let quoted = false;\n for (let i = 0; i < text.length; i += 1) {\n const ch = text[i];\n if (quoted) {\n if (ch === '\"') {\n if (text[i + 1] === '\"') {\n cell += '\"';\n i += 1;\n } else quoted = false;\n } else cell += ch;\n continue;\n }\n if (ch === '\"' && cell.trim() === \"\") quoted = true;\n else if (ch === delimiter) {\n row.push(cell);\n cell = \"\";\n } else if (ch === \"\\n\" || ch === \"\\r\") {\n if (ch === \"\\r\" && text[i + 1] === \"\\n\") i += 1;\n row.push(cell);\n rows.push(row);\n row = [];\n cell = \"\";\n } else cell += ch;\n }\n if (cell.length > 0 || row.length > 0) {\n row.push(cell);\n rows.push(row);\n }\n return rows.filter((r) => r.some((c) => c.trim() !== \"\"));\n}\n\n/** A whole text that is delimited rows: every line the same width, at least two columns. */\nfunction delimitedShape(text: string, delimiter: \",\" | \"\\t\", fenced: boolean, at: TableShape[\"at\"], name: string | null): TableShape | null {\n const matrix = parseDelimited(text.trim(), delimiter);\n if (matrix.length < 2) return null;\n const width = matrix[0]!.length;\n if (width < 2) return null;\n const same = matrix.filter((r) => r.length === width).length;\n if (same / matrix.length < 0.9) return null;\n if (!fenced) {\n // Prose has commas too. A CSV cell is short; a sentence is not.\n const cells = matrix.flat();\n const long = cells.filter((c) => c.trim().length > 80).length;\n if (delimiter === \",\" && (long / cells.length > 0.2 || matrix.length < 2)) return null;\n }\n const headers = uniqueLabels(matrix[0]!.map((c) => c.trim()));\n const rows = rowsOf(headers, matrix.slice(1));\n if (rows.length === 0) return null;\n const kind = delimiter === \"\\t\" ? \"tsv\" : \"csv\";\n return {\n kind,\n headers,\n rows,\n name,\n at,\n splits: [],\n said: `${sizeWords(rows.length, headers.length)} from ${kind === \"tsv\" ? \"pasted spreadsheet cells\" : \"CSV\"}`,\n };\n}\n\n// ── JSON and kind values ───────────────────────────────────────────────────\n\nfunction cellOf(value: unknown): string {\n if (value === null || value === undefined) return \"\";\n if (typeof value === \"string\") return value;\n if (typeof value === \"number\" || typeof value === \"boolean\") return String(value);\n if (Array.isArray(value) && value.every((v) => typeof v !== \"object\" || v === null)) return value.map(cellOf).join(\", \");\n // A nested object (a kind value included) is kept whole, `__kind` and all — it is part of the data.\n return JSON.stringify(value);\n}\n\nfunction isPlainObject(v: unknown): v is Record<string, unknown> {\n return typeof v === \"object\" && v !== null && !Array.isArray(v);\n}\n\n/** A key as a column's label: `body_area` → \"Body area\"; a key already in words stays as written. */\nfunction labelOfKey(key: string): string {\n return /[\\s]/.test(key) || /^[A-Z]/.test(key) ? key : humanizeIdentifier(key);\n}\n\nfunction shapeFromObjects(objects: Array<Record<string, unknown>>, kind: TableShapeKind, from: string, name: string | null): TableShape | null {\n if (objects.length === 0) return null;\n const keys: string[] = [];\n for (const o of objects) for (const k of Object.keys(o)) if (k !== \"__kind\" && !keys.includes(k)) keys.push(k);\n if (keys.length === 0) return null;\n const headers = uniqueLabels(keys.map(labelOfKey));\n const rows = objects.map((o) => {\n const row: Record<string, string> = {};\n keys.forEach((k, i) => {\n row[headers[i]!] = cellOf(o[k]);\n });\n return row;\n });\n return { kind, headers, rows, name, at: null, splits: [], said: `${sizeWords(rows.length, headers.length)} from ${from}` };\n}\n\n/**\n * THE ROWS A VALUE HOLDS — a kind value (`__kind`, or a Matrx directive read by the one decoder),\n * a JSON array of objects, or an object whose largest member is one. Null when it holds none.\n */\nexport function shapeOfValue(value: unknown, name: string | null = null): TableShape | null {\n let v: unknown = value;\n if (typeof v === \"string\") {\n const t = v.trim();\n const fence = /^```(?:json|matrx)?\\s*\\n?([\\s\\S]*?)\\n?```$/.exec(t);\n const body = fence ? fence[1]!.trim() : t;\n if (!(body.startsWith(\"{\") || body.startsWith(\"[\"))) return null;\n try {\n v = JSON.parse(body);\n } catch {\n return null;\n }\n }\n if (Array.isArray(v)) {\n const objects = v.filter(isPlainObject);\n if (objects.length === 0 || objects.length < v.length) return null;\n return shapeFromObjects(objects, \"rows\", \"JSON rows\", name);\n }\n if (!isPlainObject(v)) return null;\n const kv = kindValueOf(v);\n if (kv) {\n const label = kv.kind.replace(/^directive_v\\d+_/, \"\").replace(/_/g, \" \");\n if (kv.items.length > 0) return shapeFromObjects(kv.items, \"kind\", `a ${label}`, name);\n const scalars = Object.fromEntries(Object.entries(v).filter(([k, x]) => k !== \"__kind\" && (typeof x !== \"object\" || x === null)));\n const shape = shapeFromObjects([scalars], \"kind\", `a ${label}`, name);\n if (shape) return shape;\n }\n // An object carrying one list of records: {\"exercises\": [{…}, {…}]} — the largest one.\n let best: { key: string; items: Array<Record<string, unknown>> } | null = null;\n for (const [key, member] of Object.entries(v)) {\n if (Array.isArray(member) && member.length > 0 && member.every(isPlainObject)) {\n if (!best || member.length > best.items.length) best = { key, items: member };\n }\n }\n if (best) return shapeFromObjects(best.items, kv ? \"kind\" : \"rows\", \"JSON rows\", name ?? labelOfKey(best.key));\n return null;\n}\n\n// ── the document reader ────────────────────────────────────────────────────\n\n/**\n * EVERY SHAPE IN A TEXT, top to bottom: markdown tables, lists, runs of `Key: value` lines,\n * fenced CSV / TSV / JSON blocks — and when the whole text is one CSV or TSV paste, that.\n * An empty array means the text holds nothing that reads as rows, and a menu offers nothing.\n */\nexport function readTableShapes(text: string): TableShape[] {\n if (typeof text !== \"string\" || text.trim() === \"\" || text.length > LONGEST_TEXT_READ) return [];\n const whole = text.trim();\n\n // A whole value (a kind value / JSON rows pasted or stored as the content).\n if (whole.startsWith(\"{\") || whole.startsWith(\"[\") || /^```(?:json|matrx)/.test(whole)) {\n const s = shapeOfValue(whole);\n if (s) return [{ ...s, at: { start: 0, end: text.length } }];\n }\n // A whole paste of spreadsheet cells or CSV (no markdown at all).\n if (!/^\\s*[|#>*-]/m.test(whole)) {\n if (whole.includes(\"\\t\")) {\n const s = delimitedShape(whole, \"\\t\", false, { start: 0, end: text.length }, null);\n if (s) return [s];\n }\n const s = delimitedShape(whole, \",\", false, { start: 0, end: text.length }, null);\n if (s) return [s];\n }\n\n const lines = text.split(/\\r?\\n/);\n const offsets: number[] = [];\n let pos = 0;\n for (const line of lines) {\n offsets.push(pos);\n pos += line.length + 1;\n }\n const endOf = (i: number) => (offsets[i] ?? 0) + (lines[i]?.length ?? 0);\n const shapes: TableShape[] = [];\n let i = 0;\n while (i < lines.length) {\n const line = lines[i] ?? \"\";\n\n // Fenced blocks: CSV / TSV / JSON are read; any other fence is skipped whole.\n const fence = /^\\s*```\\s*([\\w-]*)\\s*$/.exec(line);\n if (fence) {\n let j = i + 1;\n while (j < lines.length && !/^\\s*```\\s*$/.test(lines[j] ?? \"\")) j += 1;\n const body = lines.slice(i + 1, j).join(\"\\n\");\n const lang = fence[1]!.toLowerCase();\n const at = { start: offsets[i]!, end: endOf(Math.min(j, lines.length - 1)) };\n const name = nameAbove(lines, i);\n let s: TableShape | null = null;\n if (lang === \"csv\") s = delimitedShape(body, \",\", true, at, name);\n else if (lang === \"tsv\") s = delimitedShape(body, \"\\t\", true, at, name);\n else if (lang === \"json\" || lang === \"matrx\") s = shapeOfValue(body, name);\n if (s) shapes.push({ ...s, at });\n i = j + 1;\n continue;\n }\n\n // A markdown table: a row of pipes, then the separator row.\n if (line.includes(\"|\") && SEPARATOR.test(lines[i + 1] ?? \"\") && !SEPARATOR.test(line)) {\n const headers = uniqueLabels(splitPipes(line));\n let j = i + 2;\n const matrix: string[][] = [];\n while (j < lines.length && (lines[j] ?? \"\").includes(\"|\") && (lines[j] ?? \"\").trim() !== \"\") {\n matrix.push(splitPipes(lines[j]!));\n j += 1;\n }\n const rows = rowsOf(headers, matrix);\n if (headers.length >= 1 && rows.length >= 1) {\n shapes.push({\n kind: \"table\",\n headers,\n rows,\n name: nameAbove(lines, i),\n at: { start: offsets[i]!, end: endOf(j - 1) },\n splits: [],\n said: `${sizeWords(rows.length, headers.length)} from a table`,\n });\n }\n i = j;\n continue;\n }\n\n // A list: consecutive items (a single blank line between items is still the same list).\n const item = LIST_ITEM.exec(line);\n if (item) {\n const baseIndent = item[1]!.length;\n const items: ListItem[] = [];\n let j = i;\n while (j < lines.length) {\n const l = lines[j] ?? \"\";\n const m = LIST_ITEM.exec(l);\n if (m) {\n const depth = m[1]!.length > baseIndent ? 1 : 0;\n if (depth === 0) items.push({ depth: 0, text: m[2]!, children: [] });\n else if (items.length > 0) items[items.length - 1]!.children.push(m[2]!);\n j += 1;\n continue;\n }\n if (l.trim() === \"\" && LIST_ITEM.test(lines[j + 1] ?? \"\")) {\n j += 1;\n continue;\n }\n // A wrapped line of the item above it.\n if (l.trim() !== \"\" && /^\\s{2,}\\S/.test(l) && items.length > 0) {\n items[items.length - 1]!.text += ` ${l.trim()}`;\n j += 1;\n continue;\n }\n break;\n }\n const s = shapeFromList(items, nameAbove(lines, i), { start: offsets[i]!, end: endOf(j - 1) });\n if (s) shapes.push(s);\n i = j;\n continue;\n }\n\n // A run of `Key: value` lines outside a list (a form result, a record written out).\n if (keyValueOf(line)) {\n let j = i;\n const pairs: Array<{ key: string; value: string }> = [];\n while (j < lines.length) {\n const l = lines[j] ?? \"\";\n const p = keyValueOf(l);\n if (p) {\n pairs.push(p);\n j += 1;\n continue;\n }\n if (l.trim() === \"\" && keyValueOf(lines[j + 1] ?? \"\")) {\n j += 1;\n continue;\n }\n break;\n }\n if (pairs.length >= FEWEST_KEY_VALUE_PAIRS) {\n const records = recordsFromPairs(pairs);\n const sameKeys = records.every((r) => r.size === records[0]!.size);\n if (records.length === 1 || (sameKeys && records[0]!.size >= 2)) {\n shapes.push(shapeFromRecords(records, { name: nameAbove(lines, i), at: { start: offsets[i]!, end: endOf(j - 1) } }, \"key and value lines\"));\n } else {\n // Keys that come round in no fixed order are a conversation: who, and what they said.\n const headers = [\"Name\", \"Text\"];\n shapes.push({\n kind: \"key_value\",\n headers,\n rows: pairs.map((p) => ({ Name: p.key, Text: p.value })),\n name: nameAbove(lines, i),\n at: { start: offsets[i]!, end: endOf(j - 1) },\n splits: [],\n said: `${plural(pairs.length, \"line\", \"lines\")}, each a name and what was said`,\n });\n }\n }\n i = Math.max(j, i + 1);\n continue;\n }\n i += 1;\n }\n return shapes;\n}\n\n/** The first shape in a text, or null — what a one-shape menu item acts on. */\nexport function readTableShape(text: string): TableShape | null {\n return readTableShapes(text)[0] ?? null;\n}\n\n/** True when the text holds anything that reads as rows — every menu's \"offer it?\" predicate. */\nexport function hasTableShape(text: string): boolean {\n return readTableShapes(text).length > 0;\n}\n\n/** Headers + string rows — the shape a table renderer already holds, read as a TableShape. */\nexport function shapeOfGrid(headers: readonly string[], cells: ReadonlyArray<ReadonlyArray<string>>, name: string | null = null): TableShape | null {\n const labels = uniqueLabels(headers.map((h) => cellWords(String(h ?? \"\"))));\n const rows = rowsOf(labels, cells.map((r) => r.map((c) => cellWords(String(c ?? \"\")))));\n if (labels.length === 0) return null;\n // Column names with no rows under them are still a table to make (the file importer's header-only\n // file: \"the table will be created with these columns and no rows yet\").\n const said = rows.length === 0 ? `${plural(labels.length, \"column\", \"columns\")} and no rows yet` : `${sizeWords(rows.length, labels.length)} from a table`;\n return { kind: \"table\", headers: labels, rows, name, at: null, splits: [], said };\n}\n\n// ── what a NEW table's columns should be ───────────────────────────────────\n\n/** A type word `custom.field_declare` takes, from `FIELD_TYPE_CHOICES`. */\nexport type GuessedFieldType =\n | \"text\"\n | \"long_text\"\n | \"number\"\n | \"currency\"\n | \"percent\"\n | \"datetime\"\n | \"checkbox\"\n | \"email\"\n | \"url\"\n | \"phone\"\n | \"select\";\n\nexport interface ColumnGuess {\n type: GuessedFieldType;\n /** A choice column's words, in the order the text first used them. */\n options?: string[];\n /** A money column's symbol. */\n unit?: string;\n /** A date column that never carries a time of day. */\n kind?: \"date\" | \"datetime\";\n}\n\n/** The most words a choice column's list may hold before it reads as text. */\nexport const MOST_GUESSED_CHOICES = 12;\n\nconst NUMBER = /^[+-]?(?:\\d{1,3}(?:,\\d{3})+|\\d+)(?:\\.\\d+)?$/;\nconst MONEY = /^([$€£¥])\\s?([+-]?(?:\\d{1,3}(?:,\\d{3})+|\\d+)(?:\\.\\d+)?)$/;\nconst PERCENT = /^[+-]?\\d+(?:\\.\\d+)?\\s?%$/;\nconst ISO_DATE = /^\\d{4}-\\d{2}-\\d{2}(?:[T ]\\d{2}:\\d{2}(?::\\d{2}(?:\\.\\d+)?)?(?:Z|[+-]\\d{2}:?\\d{2})?)?$/;\nconst SLASH_DATE = /^\\d{1,2}\\/\\d{1,2}\\/\\d{2,4}$/;\nconst WORD_DATE = /^(?:\\d{1,2}\\s+)?(?:jan|feb|mar|apr|may|jun|jul|aug|sep|sept|oct|nov|dec)[a-z]*\\.?\\s+\\d{1,2}(?:st|nd|rd|th)?,?\\s+\\d{4}$|^\\d{1,2}\\s+(?:jan|feb|mar|apr|may|jun|jul|aug|sep|sept|oct|nov|dec)[a-z]*\\.?\\s+\\d{4}$/i;\nconst EMAIL = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]{2,}$/;\nconst URL_WORD = /^https?:\\/\\/\\S+$/i;\nconst PHONE = /^\\+?[\\d\\s().-]{7,20}$/;\nconst YES = new Set([\"yes\", \"true\", \"y\", \"✓\", \"✔\", \"checked\", \"done\"]);\nconst NO = new Set([\"no\", \"false\", \"n\", \"✗\", \"✘\", \"unchecked\", \"\"]);\n\nfunction isDate(v: string): boolean {\n return ISO_DATE.test(v) || SLASH_DATE.test(v) || WORD_DATE.test(v);\n}\n\n/**\n * WHAT A NEW TABLE'S COLUMN SHOULD HOLD, proposed from every value in it. A column is a number, a\n * date, a yes/no, an email… only when EVERY non-blank value reads as one — one value that does not\n * (\"10-15\" among the Reps) keeps the column text, because a guess that loses a person's words is\n * worse than a text column. A few short words that repeat are a Choice, with those words as its\n * choices. It is a proposal the person sees and changes before anything is made.\n */\nexport function guessColumn(values: ReadonlyArray<string>): ColumnGuess {\n const filled = values.map((v) => String(v ?? \"\").trim()).filter((v) => v !== \"\");\n if (filled.length === 0) return { type: \"text\" };\n const every = (test: (v: string) => boolean) => filled.every(test);\n if (every((v) => YES.has(v.toLowerCase()) || NO.has(v.toLowerCase())) && filled.some((v) => YES.has(v.toLowerCase())) && filled.some((v) => !/^\\d+$/.test(v))) {\n return { type: \"checkbox\" };\n }\n if (every((v) => NUMBER.test(v))) return { type: \"number\" };\n if (every((v) => MONEY.test(v))) {\n const symbols = new Set(filled.map((v) => MONEY.exec(v)![1]!));\n if (symbols.size === 1) return { type: \"currency\", unit: [...symbols][0]! };\n }\n if (every((v) => PERCENT.test(v))) return { type: \"percent\" };\n if (every(isDate)) return { type: \"datetime\", kind: every((v) => !/\\d:\\d/.test(v)) ? \"date\" : \"datetime\" };\n if (every((v) => EMAIL.test(v))) return { type: \"email\" };\n if (every((v) => URL_WORD.test(v))) return { type: \"url\" };\n if (every((v) => PHONE.test(v) && (v.match(/\\d/g)?.length ?? 0) >= 7)) return { type: \"phone\" };\n if (filled.some((v) => v.length > 120 || v.includes(\"\\n\"))) return { type: \"long_text\" };\n const distinct: string[] = [];\n for (const v of filled) if (!distinct.some((d) => d.toLowerCase() === v.toLowerCase())) distinct.push(v);\n const short = every((v) => v.length <= 40 && v.split(/\\s+/).length <= 5);\n // Counts and ranges (\"10\", \"10-15\") repeat too, and they are measurements, not a list to pick from.\n const words = filled.filter((v) => !/\\d/.test(v)).length >= filled.length / 2;\n if (filled.length >= 3 && short && words && distinct.length < filled.length && distinct.length <= MOST_GUESSED_CHOICES) {\n return { type: \"select\", options: distinct };\n }\n return { type: \"text\" };\n}\n\n// ── what a selection over RENDERED content holds ───────────────────────────\n\nconst BLOCK_TAGS = new Set([\"P\", \"DIV\", \"SECTION\", \"ARTICLE\", \"H1\", \"H2\", \"H3\", \"H4\", \"H5\", \"H6\", \"BLOCKQUOTE\", \"PRE\", \"HEADER\", \"FOOTER\", \"BR\", \"HR\", \"DL\", \"DT\", \"DD\"]);\n\n/**\n * THE WORDS OF A SELECTION, WITH ITS SHAPE KEPT (VERIFIER-30 #1). A browser selection over a rendered\n * answer flattens to text in which a table's header cells come one per line and a list's bullets are\n * gone, so `readTableShapes` found nothing in exactly the places a person selects rows. This walks the\n * selected DOM (a `Range.cloneContents()` fragment, or any node) and writes it back as the shapes it\n * was drawn from: a table's rows as a markdown table (the first row its heading), a list's items as\n * bullets (nested items indented), every other block on its own line.\n */\nexport function shapeTextOfNode(node: Node): string {\n const out: string[] = [];\n let line = \"\";\n let rows: string[][] = [];\n const flushLine = () => {\n if (line.trim() !== \"\") out.push(line.trim());\n line = \"\";\n };\n const flushRows = () => {\n if (rows.length === 0) return;\n flushLine();\n const width = Math.max(...rows.map((r) => r.length));\n const pad = (r: string[]) => [...r, ...Array.from({ length: width - r.length }, () => \"\")];\n const cell = (c: string) => c.replace(/\\|/g, \"\\\\|\").replace(/\\s+/g, \" \").trim();\n out.push(`| ${pad(rows[0]!).map(cell).join(\" | \")} |`);\n out.push(`|${Array.from({ length: width }, () => \"---\").join(\"|\")}|`);\n for (const r of rows.slice(1)) out.push(`| ${pad(r).map(cell).join(\" | \")} |`);\n rows = [];\n };\n const textOf = (n: Node): string => (n.textContent ?? \"\").replace(/\\s+/g, \" \").trim();\n const walk = (n: Node, depth: number): void => {\n if (n.nodeType === 3) {\n line += n.textContent ?? \"\";\n return;\n }\n if (n.nodeType !== 1 && n.nodeType !== 11) return;\n const el = n as Element;\n const tag = n.nodeType === 1 ? el.tagName.toUpperCase() : \"#fragment\";\n if (tag === \"TR\") {\n const cells = Array.from(el.children).filter((c) => /^(TD|TH)$/i.test(c.tagName));\n rows.push(cells.map((c) => textOf(c)));\n return;\n }\n if (tag === \"TABLE\" || tag === \"THEAD\" || tag === \"TBODY\" || tag === \"TFOOT\") {\n for (const child of Array.from(n.childNodes)) walk(child, depth);\n if (tag === \"TABLE\") flushRows();\n return;\n }\n flushRows();\n if (tag === \"LI\") {\n flushLine();\n const own: string[] = [];\n const nested: Node[] = [];\n for (const child of Array.from(n.childNodes)) {\n if (child.nodeType === 1 && /^(UL|OL)$/i.test((child as Element).tagName)) nested.push(child);\n else own.push(child.textContent ?? \"\");\n }\n const words = own.join(\"\").replace(/\\s+/g, \" \").trim();\n if (words) out.push(`${\" \".repeat(depth)}- ${words}`);\n for (const list of nested) walk(list, depth + 1);\n return;\n }\n if (tag === \"UL\" || tag === \"OL\") {\n flushLine();\n for (const child of Array.from(n.childNodes)) walk(child, depth);\n flushLine();\n return;\n }\n const block = BLOCK_TAGS.has(tag);\n if (block) flushLine();\n for (const child of Array.from(n.childNodes)) walk(child, depth);\n if (block) flushLine();\n };\n walk(node, 0);\n flushRows();\n flushLine();\n return out.join(\"\\n\");\n}\n","// src/kindValues.ts — @ai-matrx/records-ui · lane REFERENCE-CARRY\n//\n// A CELL THAT HOLDS A KIND. A value is a kind value when it names its own shape — `__kind` — or\n// when it is a stored Matrx directive written before the Kind Directives merge (the retired 4-key\n// shell `{\"matrx_version\": 1, \"kind\": \"reference\", \"type\": \"scope\", \"items\": [...]}`, usually inside\n// a ```matrx fence). Before this, a text cell holding either printed the raw fence: Arman's\n// Department table, 2026-09-28 — Team Leader read ```matrx {\"matrx_version\":1,…}```.\n//\n// THE SHELL IS UNDERSTOOD IN ONE PLACE. Whether an object is a directive — the current shell, or\n// the retired one stored before the merge — is `@ai-matrx/content-ir/directives`' `tryDecodeDirective`\n// alone (its containment guard keeps the retired shell there). This module only takes the object out\n// of its fence and hands it, untouched, to the host's `renderKind` port, which draws it through the\n// kind system. New values are stored as `__kind` JSON, never as a fence.\n\nimport { tryDecodeDirective } from \"@ai-matrx/content-ir/directives\";\nimport { humanizeIdentifier } from \"@ai-matrx/kit/text-case\";\n\n/** What a cell holds when it holds a kind. */\nexport interface KindValue {\n /** The parsed object, exactly as stored (`__kind` kept — it is part of the data). */\n value: Record<string, unknown>;\n /**\n * The kind's own name: `__kind`, or for a directive the slug the one decoder reads\n * (`directive_v1_reference_scope`, the retired shell included).\n */\n kind: string;\n /** True when the one decoder read this as a Matrx directive. */\n directive: boolean;\n /** The items a directive carries (a reference's chips), when it carries any. */\n items: Record<string, unknown>[];\n}\n\nconst KIND_KEY = \"__kind\";\n/** One fenced block and nothing else: ```matrx {…}``` or ```json {…}```. */\nconst WHOLE_FENCE = /^\\s*```(?:matrx|json)?[ \\t]*\\r?\\n?([\\s\\S]*?)\\r?\\n?[ \\t]*```\\s*$/;\n\nfunction asObject(value: unknown): Record<string, unknown> | null {\n return typeof value === \"object\" && value !== null && !Array.isArray(value) ? (value as Record<string, unknown>) : null;\n}\n\nfunction parseJsonObject(text: string): Record<string, unknown> | null {\n const trimmed = text.trim();\n if (!trimmed.startsWith(\"{\") || !trimmed.endsWith(\"}\")) return null;\n try {\n return asObject(JSON.parse(trimmed));\n } catch {\n return null;\n }\n}\n\nfunction fromObject(obj: Record<string, unknown>): KindValue | null {\n // THE ONE DECODER decides what a directive is — the current two-key shell and, for stored\n // content only, the retired 4-key one. A directive it cannot honour is not drawn as one.\n const directive = tryDecodeDirective(obj);\n if (directive) return { value: obj, kind: directive.slug, directive: true, items: [...directive.items] };\n const named = obj[KIND_KEY];\n if (typeof named === \"string\" && named.trim() !== \"\") {\n const items = Array.isArray(obj.items) ? obj.items.filter((i): i is Record<string, unknown> => asObject(i) !== null) : [];\n return { value: obj, kind: named.trim(), directive: false, items };\n }\n return null;\n}\n/**\n * The kind value a cell holds, or null. An object with `__kind`, a string that is such an object's\n * JSON, or a string that is ONE fenced block of it (```matrx / ```json), the retired directive shell\n * included. Text that merely mentions a fence among other words is text, and stays text.\n */\nexport function kindValueOf(value: unknown): KindValue | null {\n const obj = asObject(value);\n if (obj) return fromObject(obj);\n if (typeof value !== \"string\" || value.length > 200_000) return null;\n const fenced = WHOLE_FENCE.exec(value);\n const parsed = parseJsonObject(fenced ? fenced[1]! : value);\n return parsed ? fromObject(parsed) : null;\n}\n\n/** The words an item names itself by (a reference's label, a title, a name). */\nexport function kindItemWords(item: Record<string, unknown>): string | null {\n for (const key of [\"label\", \"title\", \"name\", \"display_name\"]) {\n const v = item[key];\n if (typeof v === \"string\" && v.trim() !== \"\") return v.trim();\n }\n return null;\n}\n\n/**\n * A kind value in words, for a sentence, a card, a copy or an export: the items' own names when it\n * carries named items (\"Arman Sadeghi, Tahir Mahfooz\"), else what kind of thing it is.\n */\nexport function kindValueWords(kv: KindValue): string {\n const named = kv.items.map(kindItemWords).filter((w): w is string => w !== null);\n if (named.length > 0) return named.join(\", \");\n return kindLabel(kv.kind);\n}\n\n/** A kind's name the way a person reads it: `directive_v1_reference_scope` → \"Reference Scope\". */\nexport function kindLabel(kind: string): string {\n return humanizeIdentifier(kind.replace(/^directive_v\\d+_/, \"\").replace(/_v\\d+$/, \"\")) || \"Structured value\";\n}\n"],"mappings":"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","names":["import_text_case","s","i"]}
|
package/dist/table-shape.js
CHANGED
|
@@ -5,6 +5,7 @@ import { codeSpanText, mapInlineCodeSpans } from "@ai-matrx/content-ir/source";
|
|
|
5
5
|
|
|
6
6
|
// src/kindValues.ts
|
|
7
7
|
import { tryDecodeDirective } from "@ai-matrx/content-ir/directives";
|
|
8
|
+
import { humanizeIdentifier } from "@ai-matrx/kit/text-case";
|
|
8
9
|
var KIND_KEY = "__kind";
|
|
9
10
|
var WHOLE_FENCE = /^\s*```(?:matrx|json)?[ \t]*\r?\n?([\s\S]*?)\r?\n?[ \t]*```\s*$/;
|
|
10
11
|
function asObject(value) {
|
|
@@ -38,15 +39,8 @@ function kindValueOf(value) {
|
|
|
38
39
|
return parsed ? fromObject(parsed) : null;
|
|
39
40
|
}
|
|
40
41
|
|
|
41
|
-
// src/names.ts
|
|
42
|
-
import { isUuidShape } from "@ai-matrx/kit/uuid";
|
|
43
|
-
function humanize(token) {
|
|
44
|
-
const words = token.replace(/[_\-.]+/g, " ").replace(/([a-z0-9])([A-Z])/g, "$1 $2").trim();
|
|
45
|
-
if (words === "") return token;
|
|
46
|
-
return words.charAt(0).toUpperCase() + words.slice(1);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
42
|
// src/tableShape.ts
|
|
43
|
+
import { humanizeIdentifier as humanizeIdentifier2 } from "@ai-matrx/kit/text-case";
|
|
50
44
|
var FEWEST_LIST_ITEMS = 2;
|
|
51
45
|
var FEWEST_KEY_VALUE_PAIRS = 2;
|
|
52
46
|
var LONGEST_TEXT_READ = 2e6;
|
|
@@ -294,7 +288,7 @@ function isPlainObject(v) {
|
|
|
294
288
|
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
295
289
|
}
|
|
296
290
|
function labelOfKey(key) {
|
|
297
|
-
return /[\s]/.test(key) || /^[A-Z]/.test(key) ? key :
|
|
291
|
+
return /[\s]/.test(key) || /^[A-Z]/.test(key) ? key : humanizeIdentifier2(key);
|
|
298
292
|
}
|
|
299
293
|
function shapeFromObjects(objects, kind, from, name) {
|
|
300
294
|
if (objects.length === 0) return null;
|