@ai-matrx/records-ui 0.115.66 → 0.115.67

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.
@@ -1 +1 @@
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, findTableEnd, mapInlineCodeSpans, rowCells, tableStartsAt } from \"@ai-matrx/content-ir/source\";\nimport { kindValueOf } from \"./kindValues\";\nimport { humanizeIdentifier } from \"@ai-matrx/kit/text-case\";\nimport { parseDelimited as readDelimited } from \"@ai-matrx/kit/delimited\";\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\n/** A table row's cells, read by THE GFM rule (`@ai-matrx/content-ir/source`) and reduced to words. */\nfunction splitPipes(line: string): string[] {\n return rowCells(line).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/** CSV/TSV rows with the all-blank lines dropped — THE one reader (kit `parseDelimited`). */\nexport function parseDelimited(text: string, delimiter: \",\" | \"\\t\"): string[][] {\n return readDelimited(text, { delimiter, skipEmptyLines: \"greedy\" }).data;\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 (tableStartsAt(lines, i)) {\n const headers = uniqueLabels(splitPipes(line));\n const j = findTableEnd(lines, i);\n const matrix: string[][] = [];\n for (let r = i + 2; r < j; r += 1) matrix.push(splitPipes(lines[r]!));\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":["humanizeIdentifier","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, findTableEnd, mapInlineCodeSpans, rowCells, tableStartsAt } from \"@ai-matrx/content-ir/source\";\nimport { kindValueOf } from \"./kindValues\";\nimport { humanizeIdentifier } from \"@ai-matrx/kit/text-case\";\nimport { parseDelimited as readDelimited } from \"@ai-matrx/kit/delimited\";\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\n/** A table row's cells, read by THE GFM rule (`@ai-matrx/content-ir/source`) and reduced to words. */\nfunction splitPipes(line: string): string[] {\n return rowCells(line).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/** CSV/TSV rows with the all-blank lines dropped — THE one reader (kit `parseDelimited`). */\nexport function parseDelimited(text: string, delimiter: \",\" | \"\\t\"): string[][] {\n return readDelimited(text, { delimiter, skipEmptyLines: \"greedy\" }).data;\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 (tableStartsAt(lines, i)) {\n const headers = uniqueLabels(splitPipes(line));\n const j = findTableEnd(lines, i);\n const matrix: string[][] = [];\n for (let r = i + 2; r < j; r += 1) matrix.push(splitPipes(lines[r]!));\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(\n headers: readonly string[],\n cells: ReadonlyArray<ReadonlyArray<string>>,\n name: string | null = null,\n /** What the rows came out of, in a word: \"table\" (a rendered table), \"sheet\" (a workbook's sheet). */\n from: string = \"table\",\n): 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 ${from}`;\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":["humanizeIdentifier","s","i"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-matrx/records-ui",
3
- "version": "0.115.66",
3
+ "version": "0.115.67",
4
4
  "description": "The thirty-one canonical screens of the AI Matrx record store: grid, generated record form, field editor, table settings, relation picker, peek, views, import/export, custom fields, action inbox, history, comments, forms, charts, documents, record chat, AI field fill, checklists, bookings, offline capture, portals, public pages and embeds. Built on @ai-matrx/design-system's data table, carrying @ai-matrx/alchemy's menu, and reading and writing ONLY through @ai-matrx/records.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -96,13 +96,13 @@
96
96
  "@tanstack/react-query": ">=5.100.0"
97
97
  },
98
98
  "dependencies": {
99
- "@ai-matrx/content-ir": "latest",
100
- "@ai-matrx/alchemy": "latest",
101
- "@ai-matrx/design-system": "latest",
99
+ "@ai-matrx/print": "latest",
102
100
  "@ai-matrx/realtime": "latest",
103
101
  "@ai-matrx/kit": "latest",
102
+ "@ai-matrx/design-system": "latest",
103
+ "@ai-matrx/alchemy": "latest",
104
104
  "@ai-matrx/records": "latest",
105
- "@ai-matrx/print": "latest"
105
+ "@ai-matrx/content-ir": "latest"
106
106
  },
107
107
  "devDependencies": {
108
108
  "@arethetypeswrong/cli": "^0.18.5",
@@ -164,5 +164,5 @@
164
164
  "test:record": "RECORDS_UI_RECORD=1 vitest run demo/deterministic.test.tsx",
165
165
  "check:consumer-flags": "tsc --noEmit -p tsconfig.consumer.json"
166
166
  },
167
- "gitHead": "8be5f4123bbfed265b68952540090a837edafb91"
167
+ "gitHead": "784b31cb22beaccb4285669ea256f4962173683d"
168
168
  }