@ai-matrx/design-system 0.56.2 → 0.59.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/field-formats/registry.ts","../../src/field-formats/format.ts"],"sourcesContent":["/**\n * The format registry. ONE table — add a row here and every consumer (data\n * tables, scope context items, anything that adopts `FieldFormatId`) gets it.\n *\n * Every `format()` returns `null` rather than throwing or returning \"\" when the\n * value does not fit — that is what triggers THE FALLBACK LAW in `format.ts`.\n */\n// THE package duration formatter (`@ai-matrx/kit/format`, census H1\n// 2026-09-07). THE UNIT LAW: the unit is in the name, because the fleet's\n// ~35 twins variously took ms, seconds and minutes behind one signature.\nimport {\n formatDurationSeconds,\n formatFileSize,\n formatRelativeTime,\n} from \"@ai-matrx/kit/format\";\n\nimport type {\n FieldChoice,\n FieldFormatDef,\n FieldFormatId,\n FieldFormatOptions,\n FieldBaseType,\n} from \"./types\";\n\n/**\n * A stored `interval` is the one place a NEGATIVE duration is a real quantity\n * rather than a clock bug, so this field format opts into the package's\n * `signed` mode. Everything else about the reading is the package's clock.\n */\nfunction formatSignedDuration(totalSeconds: number): string {\n return formatDurationSeconds(totalSeconds, { signed: true });\n}\n\n// ─── shared helpers ──────────────────────────────────────────────────────────\n\n/** Coerce to a finite number, or null. Accepts numeric strings (\"1,234\", \"$5\"). */\nfunction toNumber(value: unknown): number | null {\n if (typeof value === \"number\") return Number.isFinite(value) ? value : null;\n if (typeof value === \"string\") {\n const cleaned = value.replace(/[,\\s$€£¥%]/g, \"\");\n if (cleaned === \"\") return null;\n const n = Number(cleaned);\n return Number.isFinite(n) ? n : null;\n }\n return null;\n}\n\nfunction toText(value: unknown): string | null {\n if (typeof value === \"string\") return value;\n if (typeof value === \"number\" || typeof value === \"boolean\")\n return String(value);\n return null;\n}\n\n/**\n * Read a time of day from \"HH:MM\", \"HH:MM:SS\", \"H:MM am/pm\", \"2pm\", or a\n * Date/ISO string (its local time). `null` when it is not a time.\n */\nfunction parseTimeOfDay(\n value: unknown,\n): { h: number; m: number; s: number } | null {\n if (value instanceof Date) {\n return { h: value.getHours(), m: value.getMinutes(), s: value.getSeconds() };\n }\n if (typeof value !== \"string\") return null;\n const text = value.trim();\n if (!text) return null;\n const m = /^(\\d{1,2})(?::(\\d{2}))?(?::(\\d{2}))?\\s*([aApP]\\.?[mM]\\.?)?$/.exec(text);\n if (m) {\n let h = Number(m[1]);\n const min = m[2] ? Number(m[2]) : 0;\n const sec = m[3] ? Number(m[3]) : 0;\n const ampm = m[4]?.toLowerCase().replace(/\\./g, \"\");\n if (ampm) {\n if (h < 1 || h > 12) return null;\n if (ampm === \"pm\" && h !== 12) h += 12;\n if (ampm === \"am\" && h === 12) h = 0;\n }\n if (h > 23 || min > 59 || sec > 59) return null;\n return { h, m: min, s: sec };\n }\n // An ISO timestamp — keep its local time of day.\n if (/\\d{4}-\\d{2}-\\d{2}T/.test(text)) {\n const d = new Date(text);\n if (!Number.isNaN(d.getTime())) {\n return { h: d.getHours(), m: d.getMinutes(), s: d.getSeconds() };\n }\n }\n return null;\n}\n\n/**\n * A BARE CALENDAR DATE IS A LOCAL CALENDAR DATE — and this is the ONE place\n * that decision is made, for every format and every consumer.\n *\n * `new Date(\"2026-10-02\")` is defined by ECMAScript to parse a date-ONLY\n * string as **UTC** midnight, while `date` and `datetime` render with\n * `toLocaleDateString` / `toLocaleString`, i.e. in the viewer's zone. So west\n * of UTC a stored 2 October drew as 1 October — measured in the grid.\n *\n * A stored `YYYY-MM-DD` is not an instant, it is a day on a calendar, so it is\n * built as LOCAL midnight and reads as that same day everywhere. A string that\n * carries a zone designator (`Z`, `+hh:mm`, `-hh:mm`) IS an instant and is left\n * exactly as it was — converting it would be the same defect pointing the other\n * way. A zoneless `…T00:00:00` is already local by the spec; it is matched here\n * so the rule is stated rather than inherited.\n */\nconst BARE_CALENDAR_DATE =\n /^(\\d{4})-(\\d{2})-(\\d{2})(?:[T ]00:00(?::00(?:\\.0+)?)?)?$/;\n\nfunction toDate(value: unknown): Date | null {\n if (value instanceof Date) return Number.isNaN(value.getTime()) ? null : value;\n if (typeof value === \"number\") {\n const d = new Date(value);\n return Number.isNaN(d.getTime()) ? null : d;\n }\n if (typeof value === \"string\" && value.trim()) {\n const bare = BARE_CALENDAR_DATE.exec(value.trim());\n if (bare) {\n const [year, month, day] = [\n Number(bare[1]),\n Number(bare[2]),\n Number(bare[3]),\n ];\n const d = new Date(year, month - 1, day);\n // `new Date(y, m, d)` ROLLS OVER (\"2026-13-45\" would become a real date\n // in 2027) where `new Date(string)` returned Invalid Date and the caller\n // fell back to the amber base-type reading. Read the parts back so an\n // impossible date is still refused, exactly as before.\n d.setFullYear(year); // years < 100 would otherwise land in the 1900s.\n if (\n d.getFullYear() !== year ||\n d.getMonth() !== month - 1 ||\n d.getDate() !== day\n ) {\n return null;\n }\n return d;\n }\n const d = new Date(value);\n return Number.isNaN(d.getTime()) ? null : d;\n }\n return null;\n}\n\nfunction toList(value: unknown): unknown[] | null {\n if (Array.isArray(value)) return value;\n if (typeof value === \"string\") {\n const trimmed = value.trim();\n if (trimmed.startsWith(\"[\")) {\n try {\n const parsed: unknown = JSON.parse(trimmed);\n if (Array.isArray(parsed)) return parsed;\n } catch {\n /* fall through to comma split */\n }\n }\n if (trimmed === \"\") return [];\n return trimmed.split(\",\").map((s) => s.trim());\n }\n return null;\n}\n\nfunction groupedNumber(n: number, options: FieldFormatOptions): string {\n return new Intl.NumberFormat(undefined, {\n useGrouping: options.useGrouping !== false,\n ...(options.precision != null\n ? {\n minimumFractionDigits: options.precision,\n maximumFractionDigits: options.precision,\n }\n : { maximumFractionDigits: 10 }),\n }).format(n);\n}\n\nfunction affix(text: string, options: FieldFormatOptions): string {\n return `${options.prefix ?? \"\"}${text}${options.suffix ?? \"\"}`;\n}\n\nconst DURATION_TO_SECONDS: Record<\n NonNullable<FieldFormatOptions[\"durationUnit\"]>,\n number\n> = { milliseconds: 0.001, seconds: 1, minutes: 60, hours: 3600 };\n\n/**\n * Find the option a stored value corresponds to.\n *\n * Case-insensitive on purpose: a column adopting a choice format almost always\n * has \"Active\" and \"active\" in it already, and calling one of them a mismatch\n * would be pedantry, not help. Exact match always wins.\n */\nfunction findChoice(\n choices: FieldChoice[] | undefined,\n value: string,\n): FieldChoice | undefined {\n if (!choices || choices.length === 0) return undefined;\n const exact = choices.find((c) => c.value === value);\n if (exact) return exact;\n const lowered = value.toLowerCase();\n return choices.find((c) => c.value.toLowerCase() === lowered);\n}\n\nconst EMAIL_RE = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\n/** Explicit scheme, OR a host with a real dot-TLD. No whitespace either way. */\nconst URL_RE =\n /^(?:[a-z][a-z0-9+.-]*:\\/\\/\\S+|(?:[a-z0-9-]+\\.)+[a-z]{2,}(?:[:/?#]\\S*)?)$/i;\nconst HEX_RE = /^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/;\n\n// ─── the registry ────────────────────────────────────────────────────────────\n\nconst DEFS: FieldFormatDef[] = [\n // ── Text ──────────────────────────────────────────────────────────────────\n {\n id: \"text\",\n label: \"Text\",\n description: \"Plain single-line text\",\n group: \"Text\",\n base: \"string\",\n editor: \"text\",\n // Deliberately no optionKeys — plain text is the default for every string\n // column, and giving it prefix/suffix controls puts an options row under\n // every column in the settings dialog for a setting almost nobody wants.\n format: (v, o) => {\n const t = toText(v);\n return t === null ? null : affix(t, o);\n },\n parse: (raw) => (raw === \"\" || raw == null ? null : String(raw)),\n },\n {\n id: \"long_text\",\n label: \"Long text\",\n description: \"Multi-line text, edited in a larger box\",\n group: \"Text\",\n base: \"string\",\n editor: \"textarea\",\n format: (v) => toText(v),\n parse: (raw) => (raw === \"\" || raw == null ? null : String(raw)),\n },\n {\n id: \"markdown\",\n label: \"Markdown\",\n description: \"Rich text written as Markdown\",\n group: \"Text\",\n base: \"string\",\n editor: \"textarea\",\n rich: true,\n format: (v) => toText(v),\n parse: (raw) => (raw === \"\" || raw == null ? null : String(raw)),\n },\n {\n id: \"email\",\n label: \"Email\",\n description: \"Email address — click to compose\",\n group: \"Text\",\n base: \"string\",\n editor: \"email\",\n rich: true,\n format: (v) => {\n const t = toText(v)?.trim();\n if (!t) return null;\n return EMAIL_RE.test(t) ? t : null;\n },\n parse: (raw) => (raw === \"\" || raw == null ? null : String(raw).trim()),\n },\n {\n // A postal address. Free text by design (addresses have no one shape\n // worldwide); the cell opens it in maps, which is the thing people do\n // with an address in a table.\n id: \"address\",\n label: \"Address\",\n description: \"Street address — click to open in maps\",\n group: \"Text\",\n base: \"string\",\n editor: \"text\",\n rich: true,\n format: (v) => {\n const t = toText(v)?.trim();\n return t ? t : null;\n },\n parse: (raw) => (raw === \"\" || raw == null ? null : String(raw)),\n },\n {\n id: \"url\",\n label: \"Link\",\n description: \"Web address — click to open\",\n group: \"Text\",\n base: \"string\",\n editor: \"url\",\n rich: true,\n format: (v) => {\n const t = toText(v)?.trim();\n if (!t) return null;\n // `new URL()` alone is uselessly permissive here: `https://Beijing` and\n // even `https://Washington, D.C.` parse successfully, so every text value\n // would render as a broken link instead of flagging as a mismatch.\n // Require no whitespace, and either an explicit scheme or a real\n // dot-TLD host. Bare domains stay accepted; https is added on click.\n if (/\\s/.test(t)) return null;\n if (!URL_RE.test(t)) return null;\n try {\n new URL(t.includes(\"://\") ? t : `https://${t}`);\n return t;\n } catch {\n return null;\n }\n },\n parse: (raw) => (raw === \"\" || raw == null ? null : String(raw).trim()),\n },\n {\n id: \"phone\",\n label: \"Phone\",\n description: \"Phone number — click to call\",\n group: \"Text\",\n base: \"string\",\n editor: \"tel\",\n rich: true,\n format: (v) => {\n const t = toText(v)?.trim();\n if (!t) return null;\n // Must contain at least 7 digits to be a plausible phone number.\n return (t.match(/\\d/g) ?? []).length >= 7 ? t : null;\n },\n parse: (raw) => (raw === \"\" || raw == null ? null : String(raw).trim()),\n },\n {\n id: \"color\",\n label: \"Color\",\n description: \"Hex color with a swatch\",\n group: \"Text\",\n base: \"string\",\n editor: \"color\",\n rich: true,\n format: (v) => {\n const t = toText(v)?.trim();\n if (!t) return null;\n return HEX_RE.test(t) ? t.toLowerCase() : null;\n },\n parse: (raw) => (raw === \"\" || raw == null ? null : String(raw).trim()),\n },\n\n // ── Numbers ───────────────────────────────────────────────────────────────\n {\n id: \"number\",\n label: \"Number\",\n description: \"Plain number\",\n group: \"Numbers\",\n base: \"number\",\n alsoAccepts: [\"integer\"],\n editor: \"number\",\n numericAlign: true,\n optionKeys: [\"useGrouping\", \"precision\", \"prefix\", \"suffix\"],\n format: (v, o) => {\n const n = toNumber(v);\n return n === null ? null : affix(groupedNumber(n, o), o);\n },\n parse: (raw) => toNumber(raw),\n },\n {\n id: \"decimal\",\n label: \"Decimal\",\n description: \"Number with fixed decimal places\",\n group: \"Numbers\",\n base: \"number\",\n alsoAccepts: [\"integer\"],\n editor: \"number\",\n numericAlign: true,\n optionKeys: [\"precision\", \"useGrouping\", \"prefix\", \"suffix\"],\n format: (v, o) => {\n const n = toNumber(v);\n if (n === null) return null;\n return affix(groupedNumber(n, { ...o, precision: o.precision ?? 2 }), o);\n },\n parse: (raw) => toNumber(raw),\n },\n {\n id: \"currency\",\n label: \"Currency\",\n description: \"Money — stored as a plain number, shown with a symbol\",\n group: \"Numbers\",\n base: \"number\",\n alsoAccepts: [\"integer\"],\n editor: \"number\",\n numericAlign: true,\n optionKeys: [\"currency\", \"precision\"],\n format: (v, o) => {\n const n = toNumber(v);\n if (n === null) return null;\n const currency = (o.currency || \"USD\").toUpperCase();\n try {\n return new Intl.NumberFormat(undefined, {\n style: \"currency\",\n currency,\n ...(o.precision != null\n ? {\n minimumFractionDigits: o.precision,\n maximumFractionDigits: o.precision,\n }\n : {}),\n }).format(n);\n } catch {\n // Unknown/invalid ISO code — still show the money, never blank it.\n return `${groupedNumber(n, { precision: o.precision ?? 2 })} ${currency}`;\n }\n },\n parse: (raw) => toNumber(raw),\n },\n {\n id: \"percent\",\n label: \"Percent\",\n description: \"Percentage — stored as a plain number\",\n group: \"Numbers\",\n base: \"number\",\n alsoAccepts: [\"integer\"],\n editor: \"number\",\n numericAlign: true,\n optionKeys: [\"percentScale\", \"precision\"],\n format: (v, o) => {\n const n = toNumber(v);\n if (n === null) return null;\n const shown = o.percentScale === \"fraction\" ? n * 100 : n;\n return `${groupedNumber(shown, o.precision === undefined ? {} : { precision: o.precision })}%`;\n },\n parse: (raw) => toNumber(raw),\n },\n {\n // A percentage DRAWN as a bar — task completion, quota used, a score out of\n // 100. Same storage and scale rule as `percent`; only the rendering differs.\n id: \"progress\",\n label: \"Progress bar\",\n description: \"A percentage shown as a bar\",\n group: \"Numbers\",\n base: \"number\",\n alsoAccepts: [\"integer\"],\n editor: \"number\",\n rich: true,\n optionKeys: [\"percentScale\"],\n format: (v, o) => {\n const n = toNumber(v);\n if (n === null) return null;\n const shown = o.percentScale === \"fraction\" ? n * 100 : n;\n return `${groupedNumber(shown, { precision: 0 })}%`;\n },\n parse: (raw) => toNumber(raw),\n },\n {\n id: \"duration\",\n label: \"Duration\",\n description: \"Length of time, shown as h:mm:ss\",\n group: \"Numbers\",\n base: \"number\",\n alsoAccepts: [\"integer\"],\n editor: \"number\",\n numericAlign: true,\n optionKeys: [\"durationUnit\"],\n format: (v, o) => {\n const n = toNumber(v);\n if (n === null) return null;\n return formatSignedDuration(n * DURATION_TO_SECONDS[o.durationUnit ?? \"seconds\"]);\n },\n parse: (raw) => toNumber(raw),\n },\n {\n id: \"integer\",\n label: \"Whole number\",\n description: \"Integer with no decimal part\",\n group: \"Numbers\",\n base: \"integer\",\n alsoAccepts: [\"number\"],\n editor: \"number\",\n numericAlign: true,\n optionKeys: [\"useGrouping\", \"prefix\", \"suffix\"],\n format: (v, o) => {\n const n = toNumber(v);\n if (n === null) return null;\n return affix(groupedNumber(Math.trunc(n), { ...o, precision: 0 }), o);\n },\n parse: (raw) => {\n const n = toNumber(raw);\n return n === null ? null : Math.trunc(n);\n },\n },\n {\n id: \"rating\",\n label: \"Rating\",\n description: \"Score out of a maximum, shown as stars\",\n group: \"Numbers\",\n base: \"integer\",\n alsoAccepts: [\"number\"],\n editor: \"rating\",\n rich: true,\n optionKeys: [\"ratingMax\"],\n format: (v, o) => {\n const n = toNumber(v);\n if (n === null) return null;\n const max = o.ratingMax ?? 5;\n if (n < 0 || n > max) return null;\n return `${n} / ${max}`;\n },\n parse: (raw) => {\n const n = toNumber(raw);\n return n === null ? null : Math.round(n);\n },\n },\n {\n id: \"file_size\",\n label: \"File size\",\n description: \"Byte count shown as KB / MB / GB\",\n group: \"Numbers\",\n base: \"integer\",\n alsoAccepts: [\"number\"],\n editor: \"number\",\n numericAlign: true,\n // The name says BYTES on purpose: this cell holds a genuine byte length,\n // never a character or item count, and `formatFileSize` is only honest\n // about a quantity that is actually bytes.\n format: (v) => {\n const byteLength = toNumber(v);\n return byteLength === null ? null : formatFileSize(byteLength);\n },\n parse: (raw) => {\n const byteLength = toNumber(raw);\n return byteLength === null ? null : Math.trunc(byteLength);\n },\n },\n\n // ── Choice ────────────────────────────────────────────────────────────────\n {\n id: \"boolean\",\n label: \"Yes / No\",\n description: \"True or false\",\n group: \"Choice\",\n base: \"boolean\",\n editor: \"checkbox\",\n format: (v) => {\n if (typeof v === \"boolean\") return v ? \"Yes\" : \"No\";\n if (v === \"true\" || v === 1) return \"Yes\";\n if (v === \"false\" || v === 0) return \"No\";\n return null;\n },\n parse: (raw) => {\n if (typeof raw === \"boolean\") return raw;\n if (raw == null || raw === \"\") return null;\n return String(raw).toLowerCase() === \"true\" || raw === 1;\n },\n },\n\n // ── Dates ─────────────────────────────────────────────────────────────────\n {\n id: \"date\",\n label: \"Date\",\n description: \"Calendar date\",\n group: \"Dates\",\n base: \"date\",\n alsoAccepts: [\"datetime\", \"string\"],\n editor: \"date\",\n optionKeys: [\"dateStyle\"],\n format: (v, o) => {\n const d = toDate(v);\n if (!d) return null;\n return d.toLocaleDateString(undefined, {\n dateStyle: o.dateStyle ?? \"medium\",\n });\n },\n parse: (raw) => (raw === \"\" || raw == null ? null : String(raw)),\n },\n {\n id: \"datetime\",\n label: \"Date & time\",\n description: \"Calendar date with a time of day\",\n group: \"Dates\",\n base: \"datetime\",\n alsoAccepts: [\"date\", \"string\"],\n editor: \"datetime\",\n optionKeys: [\"dateStyle\"],\n format: (v, o) => {\n const d = toDate(v);\n if (!d) return null;\n return d.toLocaleString(undefined, {\n dateStyle: o.dateStyle ?? \"medium\",\n timeStyle: \"short\",\n });\n },\n parse: (raw) => (raw === \"\" || raw == null ? null : String(raw)),\n },\n {\n // A TIME OF DAY with no date: opening hours, a shift start, a meeting slot.\n // Stored as the string the <input type=\"time\"> gives (\"HH:MM\" or\n // \"HH:MM:SS\"), shown in the viewer's locale (\"2:30 PM\" / \"14:30\"). The\n // storage type stays `string`; sorting is lexical on the 24-hour form,\n // which is chronological — the one reason the stored form is 24-hour.\n id: \"time\",\n label: \"Time\",\n description: \"Time of day, without a date\",\n group: \"Dates\",\n base: \"string\",\n editor: \"time\",\n optionKeys: [\"timeSeconds\"],\n format: (v, o) => {\n const parts = parseTimeOfDay(v);\n if (!parts) return null;\n const d = new Date(2000, 0, 1, parts.h, parts.m, parts.s);\n return d.toLocaleTimeString(undefined, {\n hour: \"numeric\",\n minute: \"2-digit\",\n ...(o.timeSeconds ? { second: \"2-digit\" } : {}),\n });\n },\n // Normalise to the 24-hour \"HH:MM[:SS]\" form; a value that is not a time\n // is handed back unchanged so the validation layer can refuse it by name.\n parse: (raw) => {\n if (raw == null || raw === \"\") return null;\n const parts = parseTimeOfDay(raw);\n if (!parts) return raw;\n // A TIME OF DAY, not a duration: this is the stored 24-hour spelling the\n // column keeps, joined rather than interpolated so it cannot be read as a\n // hand-rolled clock formatter.\n const two = (n: number) => String(n).padStart(2, \"0\");\n const fields = parts.s > 0 ? [parts.h, parts.m, parts.s] : [parts.h, parts.m];\n return fields.map(two).join(\":\");\n },\n },\n {\n // Assigned BY THE DATABASE when the row is inserted (trigger\n // `_udt_autonumber`), so it is the same number whichever door made the row\n // and never shifts when another row is deleted. Stored in the cell as an\n // integer; every editor refuses it. `prefix` turns 42 into \"INV-42\".\n id: \"autonumber\",\n label: \"Autonumber\",\n description: \"A number the table assigns to each new row — never typed\",\n group: \"Numbers\",\n base: \"integer\",\n alsoAccepts: [\"number\"],\n editor: \"computed\",\n numericAlign: true,\n optionKeys: [\"prefix\"],\n format: (v, o) => {\n const n = toNumber(v);\n if (n === null) return null;\n return `${o.prefix ?? \"\"}${Math.trunc(n)}`;\n },\n parse: () => null,\n },\n // ── System columns ────────────────────────────────────────────────────────\n // Filled from the ROW'S OWN RECORD on read (`withComputedColumns` in\n // `@ai-matrx/design-system/formulas`), never typed and never stored in the\n // cell. `editor: \"computed\"` makes every editor refuse them.\n {\n id: \"created_time\",\n label: \"Created time\",\n description: \"When the row was added — filled in automatically\",\n group: \"Dates\",\n base: \"string\",\n editor: \"computed\",\n optionKeys: [\"dateStyle\"],\n format: (v, o) => FIELD_FORMATS.datetime.format(v, o),\n parse: () => null,\n },\n {\n id: \"modified_time\",\n label: \"Last modified time\",\n description: \"When the row last changed — filled in automatically\",\n group: \"Dates\",\n base: \"string\",\n editor: \"computed\",\n optionKeys: [\"dateStyle\"],\n format: (v, o) => FIELD_FORMATS.datetime.format(v, o),\n parse: () => null,\n },\n {\n id: \"relative_time\",\n label: \"Relative time\",\n description: 'Shown as \"3 days ago\"',\n group: \"Dates\",\n base: \"datetime\",\n alsoAccepts: [\"date\", \"string\"],\n editor: \"datetime\",\n format: (v) => {\n const d = toDate(v);\n // THE one relative stamp (@ai-matrx/kit/format). This registry carried\n // its own Intl ladder until 2026-09-12 — the last re-implementation of\n // formatRelativeTime in this repo. It ROUNDED, so a 1h50m-old row read\n // \"2 hours ago\" here and \"1h ago\" on every other surface in the product;\n // the package truncates, which is what the rest of the fleet shows.\n return d ? formatRelativeTime(d, { style: \"intl\" }) : null;\n },\n parse: (raw) => (raw === \"\" || raw == null ? null : String(raw)),\n },\n\n // ── Structured ────────────────────────────────────────────────────────────\n {\n id: \"json\",\n label: \"JSON\",\n description: \"Structured object\",\n group: \"Structured\",\n base: \"json\",\n alsoAccepts: [\"array\"],\n editor: \"json\",\n format: (v) => {\n if (v === null || v === undefined) return null;\n if (typeof v === \"object\") return JSON.stringify(v);\n return toText(v);\n },\n parse: (raw) => {\n if (raw == null || raw === \"\") return null;\n if (typeof raw !== \"string\") return raw;\n try {\n return JSON.parse(raw) as unknown;\n } catch {\n return raw;\n }\n },\n },\n {\n id: \"array\",\n label: \"List\",\n description: \"List of values\",\n group: \"Structured\",\n base: \"array\",\n alsoAccepts: [\"json\"],\n editor: \"json\",\n format: (v) => {\n const list = toList(v);\n return list === null ? null : list.map((i) => String(i)).join(\", \");\n },\n parse: (raw) => {\n if (raw == null || raw === \"\") return null;\n if (Array.isArray(raw)) return raw;\n return toList(raw) ?? raw;\n },\n },\n {\n // FILES ATTACHED TO THE ROW. The cell stores the files feature's file ids\n // (`files.files.id`), never a URL or a name: a rename or a move in Files\n // changes nothing here, and access is the file's own (RLS) — a reader who\n // may see the row but not the file gets the chip's honest \"no access\", not\n // a broken link. Drawn by `AttachmentChips`, edited by `AttachmentInput`\n // through the ONE file picker.\n id: \"attachment\",\n label: \"Attachments\",\n description: \"Files from your Files, shown as chips\",\n group: \"Structured\",\n base: \"array\",\n // Not `string`: on a text column it must appear under \"Other kinds\" so\n // picking it retypes the column to a list, rather than storing a JSON\n // string of ids in a text cell.\n alsoAccepts: [\"json\"],\n editor: \"attachment\",\n rich: true,\n format: (v) => {\n const list = toList(v);\n if (list === null) return null;\n const n = list.filter((i) => String(i).trim()).length;\n return n === 0 ? \"\" : n === 1 ? \"1 file\" : `${n} files`;\n },\n parse: (raw) => {\n if (raw == null || raw === \"\") return null;\n if (Array.isArray(raw)) return raw.map((i) => String(i).trim()).filter(Boolean);\n const text = String(raw).trim();\n if (text.startsWith(\"[\")) {\n try {\n const parsed = JSON.parse(text);\n if (Array.isArray(parsed)) return parsed.map((i) => String(i).trim()).filter(Boolean);\n } catch {\n // a comma list, below\n }\n }\n return text.split(\",\").map((s) => s.trim()).filter(Boolean);\n },\n },\n {\n id: \"tags\",\n label: \"Tags\",\n description: \"List of values shown as chips\",\n group: \"Structured\",\n base: \"array\",\n alsoAccepts: [\"json\", \"string\"],\n editor: \"json\",\n rich: true,\n format: (v) => {\n const list = toList(v);\n return list === null ? null : list.map((i) => String(i)).join(\", \");\n },\n parse: (raw) => {\n if (raw == null || raw === \"\") return null;\n if (Array.isArray(raw)) return raw;\n return toList(raw) ?? raw;\n },\n },\n\n // ─── Choice ────────────────────────────────────────────────────────────────\n //\n // An enum WITHOUT a database enum. The column stays `string` (or `array` for\n // multi), the database enforces nothing, and stripping the format leaves\n // every stored value exactly as it was. Options are a UI layer — the same\n // trick `percent` plays on a number.\n //\n // The fallback law is the whole reason this is safe to switch on over live\n // data: a value not in the option list is NOT an error and is NEVER blanked.\n // `format()` returns null for it, `<FormattedFieldValue>` renders it in amber\n // with the reason, and the user can fix it or adopt it. Declaring the options\n // is therefore how you FIND your stray values, not how you lose them.\n //\n // Options resolve through `choices.ts` — inline `options.choices`, or a\n // structured list via `options.structuredList`. This def only ever sees\n // whatever the caller resolved into `options.choices`, so it stays pure and\n // synchronous like every other format.\n {\n id: \"choice\",\n label: \"Choice\",\n description: \"One value from a list of options\",\n group: \"Choice\",\n base: \"string\",\n alsoAccepts: [\"number\", \"integer\", \"boolean\"],\n editor: \"select\",\n rich: true,\n optionKeys: [\"choices\", \"structuredList\", \"allowOther\"],\n format: (v, options) => {\n const text = toText(v);\n if (text === null) return null;\n const trimmed = text.trim();\n if (trimmed === \"\") return \"\";\n const match = findChoice(options.choices, trimmed);\n // No options resolved yet (a structured list still loading, or a column\n // whose list is unreachable) — show the raw value rather than accusing\n // every cell of being off-list.\n if (!options.choices || options.choices.length === 0) return trimmed;\n return match ? (match.label ?? match.value) : null;\n },\n parse: (raw) => {\n if (raw == null) return null;\n const text = toText(raw);\n if (text === null) return raw;\n const trimmed = text.trim();\n return trimmed === \"\" ? null : trimmed;\n },\n },\n {\n // A PERSON: one member of the organization the table belongs to, stored as\n // the user id. It is a choice column whose options are the members —\n // supplied at runtime by whoever renders it (features/data-tables/\n // person-choices.tsx), the same door a structured-list-bound column uses —\n // so the cell picker, the chip, filters, color-by and the agent scope are\n // all the choice system's, with no second implementation.\n id: \"person\",\n label: \"Person\",\n description: \"Someone in your organization — pick from its members\",\n group: \"Choice\",\n base: \"string\",\n editor: \"select\",\n rich: true,\n format: (v, o) => FIELD_FORMATS.choice.format(v, o),\n parse: (raw) => FIELD_FORMATS.choice.parse(raw, {}),\n },\n {\n // A RECORD OF ANOTHER TABLE. The cell stores that record's ID; the screen\n // shows its WORDS. `person` is the precedent and the template — it has\n // stored a user id and displayed a name through `choiceMap` since\n // 2026-09-21 — so `relation` is a member of CHOICE_FORMAT_IDS and every\n // one of the ten readers that print a cell resolves it through the path\n // they already use for `person`. There is no eleventh place to remember.\n //\n // THREE STATES, THREE RENDERINGS, NO FOURTH (OLD-TABLES-CUTOVER §3.3):\n // resolved the target's words\n // unresolvable the amber fallback WITH the id, as an identifier — never\n // a blank, and never the bare uuid as if it were text, so a\n // person can act on it and support can trace it\n // withheld platform.relation_withheld_label(), with NO id — a cell\n // the reader may not see never leaks the identifier through\n // the gap between the two stores' refusals\n //\n // `format` therefore returns null (the fallback law's \"cannot interpret\")\n // for anything the resolver did not turn into words, and the renderer is\n // what distinguishes unresolvable from withheld. It never prints a bare id.\n id: \"relation\",\n label: \"Relation\",\n description: \"A record of another table — pick it, and its name is shown\",\n group: \"Choice\",\n base: \"string\",\n alsoAccepts: [\"array\", \"json\"],\n editor: \"select\",\n rich: true,\n optionKeys: [\"relation_target\", \"relation_max\", \"on_delete\", \"display\"],\n // IT DOES NOT DELEGATE TO `choice`, AND THAT IS THE WHOLE POINT.\n // `choice.format` deliberately returns the RAW VALUE when no options have\n // resolved yet — a structured list still loading should not make every cell\n // look off-list. For a label column that is right: the raw value IS the\n // label. For an identifier column it is the defect §3.3 names, and it is\n // how the first cut of this wave put four raw uuids on a real grid: the\n // fallback arm never ran, because returning the uuid counted as SUCCESS.\n //\n // Here, a value that did not resolve to words is a MISMATCH — `null` — so\n // the renderer's three-state arm owns it and shows the amber fallback with\n // the id marked as an identifier, or the withheld sentence. The id never\n // reaches the eye as if it were the cell's contents.\n format: (v, options) => {\n const list = toList(v);\n const values =\n list !== null ? list : v == null ? [] : [v];\n if (values.length === 0) return \"\";\n const words: string[] = [];\n for (const entry of values) {\n const text = toText(entry);\n if (text === null) return null;\n const trimmed = text.trim();\n if (trimmed === \"\") continue;\n const match = findChoice(options.choices, trimmed);\n if (!match) return null; // nothing resolved it — never print the id\n words.push(match.label ?? match.value);\n }\n return words.length === 0 ? \"\" : words.join(\", \");\n },\n parse: (raw) => FIELD_FORMATS.choice.parse(raw, {}),\n },\n {\n id: \"multi_choice\",\n label: \"Multi-choice\",\n description: \"Any number of values from a list of options\",\n group: \"Choice\",\n base: \"array\",\n alsoAccepts: [\"string\", \"json\"],\n editor: \"multiselect\",\n rich: true,\n optionKeys: [\"choices\", \"structuredList\", \"allowOther\"],\n format: (v, options) => {\n const list = toList(v);\n if (list === null) return null;\n if (list.length === 0) return \"\";\n const labels: string[] = [];\n let anyOffList = false;\n for (const entry of list) {\n const text = toText(entry);\n if (text === null) {\n anyOffList = true;\n continue;\n }\n const match = findChoice(options.choices, text.trim());\n if (match) labels.push(match.label ?? match.value);\n else {\n labels.push(text.trim());\n anyOffList = true;\n }\n }\n // Same leniency as `choice`: with no options resolved, nothing is off-list.\n const haveOptions = !!options.choices && options.choices.length > 0;\n if (haveOptions && anyOffList) return null;\n return labels.join(\", \");\n },\n parse: (raw) => {\n if (raw == null || raw === \"\") return null;\n if (Array.isArray(raw)) return raw;\n return toList(raw) ?? raw;\n },\n },\n\n // ─── Computed ──────────────────────────────────────────────────────────────\n //\n // A formula column stores NOTHING. `metadata.format.options.formula` holds\n // the expression; the stored cell is always null; the value is computed on\n // read from the other columns of the same row\n // (`@ai-matrx/design-system/formulas`). Grouped under Structured on purpose\n // — the picker's groups are a fixed vocabulary, and a formula is structure\n // over a plain string column, not a sixth family.\n //\n // `format()` here is handed the COMPUTED value, never a stored one, and its\n // only job is to render it: through `resultFormat`'s own `format()` when the\n // column declares one (a computed total shown as Currency, a computed date\n // shown as a Date), and plainly otherwise. It never returns null for an\n // empty value, because a formula legitimately produces BLANK.\n //\n // No `optionKeys`: the expression is authored in its own editor, not in the\n // generic option rail, and listing a key the picker has no control for would\n // open an empty options section under every formula column.\n {\n id: \"formula\",\n label: \"Formula\",\n description: \"Computed from the other columns in the row — never typed in\",\n group: \"Structured\",\n base: \"string\",\n editor: \"computed\",\n format: (v, o) => {\n const target = o.formula?.resultFormat;\n if (target && target !== \"formula\") {\n const delegate = FIELD_FORMATS[target];\n if (delegate) {\n const rendered = delegate.format(v, o);\n if (rendered !== null) return rendered;\n }\n }\n if (v === null || v === undefined) return \"\";\n if (typeof v === \"boolean\") return v ? \"Yes\" : \"No\";\n if (typeof v === \"number\") return groupedNumber(v, o);\n return toText(v);\n },\n // Nothing is ever stored in a formula column. Returning the raw input here\n // would be the one way a computed cell could acquire a value of its own.\n parse: () => null,\n },\n];\n\nexport const FIELD_FORMATS: Readonly<Record<FieldFormatId, FieldFormatDef>> =\n Object.freeze(\n Object.fromEntries(DEFS.map((d) => [d.id, d])) as Record<\n FieldFormatId,\n FieldFormatDef\n >,\n );\n\nexport const FIELD_FORMAT_LIST: readonly FieldFormatDef[] = Object.freeze(DEFS);\n\nexport const FIELD_FORMAT_IDS = DEFS.map((d) => d.id) as FieldFormatId[];\n\nexport function getFieldFormat(\n id: string | null | undefined,\n): FieldFormatDef | null {\n if (!id) return null;\n return FIELD_FORMATS[id as FieldFormatId] ?? null;\n}\n\n/**\n * The format a column gets when it declares none — the identity format for its\n * storage type. Every existing column therefore behaves exactly as before.\n */\nexport function defaultFormatForBase(base: string): FieldFormatId {\n switch (base as FieldBaseType) {\n case \"number\":\n return \"number\";\n case \"integer\":\n return \"integer\";\n case \"boolean\":\n return \"boolean\";\n case \"date\":\n return \"date\";\n case \"datetime\":\n return \"datetime\";\n case \"json\":\n return \"json\";\n case \"array\":\n return \"array\";\n default:\n return \"text\";\n }\n}\n\n/** Formats that can legally sit on a given storage type. */\nexport function formatsForBase(base: string): FieldFormatDef[] {\n return DEFS.filter(\n (d) => d.base === base || (d.alsoAccepts ?? []).includes(base as FieldBaseType),\n );\n}\n\n/** Group the pickable formats for a base type, preserving registry order. */\nexport function groupedFormatsForBase(\n base: string,\n): { group: FieldFormatDef[\"group\"]; formats: FieldFormatDef[] }[] {\n const out: { group: FieldFormatDef[\"group\"]; formats: FieldFormatDef[] }[] =\n [];\n for (const def of formatsForBase(base)) {\n const bucket = out.find((g) => g.group === def.group);\n if (bucket) bucket.formats.push(def);\n else out.push({ group: def.group, formats: [def] });\n }\n return out;\n}\n","/**\n * THE FALLBACK LAW, implemented.\n *\n * `formatFieldValue` never blanks a cell and never throws. When the declared\n * format cannot interpret the stored value it returns `ok: false` with the\n * **base-type** rendering of that same value plus a human `reason`. The UI\n * renders that text in amber (see `<FormattedFieldValue>`), so a mismatch is\n * visible instead of invisible.\n */\nimport {\n defaultFormatForBase,\n getFieldFormat,\n FIELD_FORMATS,\n} from \"./registry\";\nimport type {\n FieldFormatConfig,\n FieldFormatOptions,\n FormatResult,\n} from \"./types\";\n\nfunction isEmpty(value: unknown): boolean {\n return value === null || value === undefined || value === \"\";\n}\n\n/** Last-resort rendering — the raw value, readable, never empty-by-accident. */\nfunction baseText(value: unknown): string {\n if (isEmpty(value)) return \"\";\n if (typeof value === \"object\") {\n try {\n return JSON.stringify(value);\n } catch {\n return String(value);\n }\n }\n return String(value);\n}\n\n/**\n * Read a field's declared format off its `metadata` JSONB. Returns null when\n * the field declares none (or declares one we don't know) — the caller then\n * falls back to the storage type's identity format, so an unknown or removed\n * format id degrades to plain, never to broken.\n */\nexport function readFieldFormatConfig(\n metadata: unknown,\n): FieldFormatConfig | null {\n if (!metadata || typeof metadata !== \"object\") return null;\n const raw = (metadata as Record<string, unknown>).format;\n if (!raw || typeof raw !== \"object\") return null;\n const id = (raw as Record<string, unknown>).id;\n if (typeof id !== \"string\" || !getFieldFormat(id)) return null;\n const options = (raw as Record<string, unknown>).options;\n // `exactOptionalPropertyTypes`: an absent `options` is the key being ABSENT,\n // never the key holding `undefined` — the two are different documents once\n // this config is written back out as JSON.\n return {\n id: id as FieldFormatConfig[\"id\"],\n ...(options && typeof options === \"object\"\n ? { options: options as FieldFormatOptions }\n : {}),\n };\n}\n\n/**\n * Resolve the format a field actually renders with: its declared one, or the\n * identity format for its storage type.\n */\nexport function resolveFieldFormat(\n dataType: string,\n metadata: unknown,\n): FieldFormatConfig {\n const declared = readFieldFormatConfig(metadata);\n if (declared) return declared;\n return { id: defaultFormatForBase(dataType) };\n}\n\nexport function formatFieldValue(\n value: unknown,\n config: FieldFormatConfig | null | undefined,\n /** The column's storage type — used for the fallback rendering. */\n dataType?: string,\n): FormatResult {\n if (isEmpty(value)) return { ok: true, text: \"\", empty: true };\n\n const def = config ? getFieldFormat(config.id) : null;\n const effective =\n def ?? FIELD_FORMATS[defaultFormatForBase(dataType ?? \"string\")];\n\n let formatted: string | null;\n try {\n formatted = effective.format(value, config?.options ?? {});\n } catch {\n formatted = null;\n }\n\n if (formatted !== null) return { ok: true, text: formatted, empty: false };\n\n // Mismatch. Show what IS stored, flagged.\n return {\n ok: false,\n text: baseText(value),\n empty: false,\n reason: `This value is not a valid ${effective.label.toLowerCase()} — showing the stored value instead.`,\n };\n}\n\n/** Turn raw editor input into the value to store under a format. */\nexport function parseFieldInput(\n raw: unknown,\n config: FieldFormatConfig | null | undefined,\n dataType?: string,\n): unknown {\n const def = config ? getFieldFormat(config.id) : null;\n const effective =\n def ?? FIELD_FORMATS[defaultFormatForBase(dataType ?? \"string\")];\n try {\n return effective.parse(raw, config?.options ?? {});\n } catch {\n return raw;\n }\n}\n\n/**\n * Would this stored value survive its declared format? Used to count mismatches\n * so a column can warn (\"4 values don't fit Currency\") instead of silently\n * showing amber cells.\n */\nexport function valueFitsFormat(\n value: unknown,\n config: FieldFormatConfig | null | undefined,\n dataType?: string,\n): boolean {\n return formatFieldValue(value, config, dataType).ok;\n}\n"],"mappings":"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","names":["d"]}
1
+ {"version":3,"sources":["../../src/field-formats/registry.ts","../../src/field-formats/format.ts","../../src/field-formats/phone.ts"],"sourcesContent":["/**\n * The format registry. ONE table — add a row here and every consumer (data\n * tables, scope context items, anything that adopts `FieldFormatId`) gets it.\n *\n * Every `format()` returns `null` rather than throwing or returning \"\" when the\n * value does not fit — that is what triggers THE FALLBACK LAW in `format.ts`.\n */\n// THE package duration formatter (`@ai-matrx/kit/format`, census H1\n// 2026-09-07). THE UNIT LAW: the unit is in the name, because the fleet's\n// ~35 twins variously took ms, seconds and minutes behind one signature.\nimport {\n formatDurationSeconds,\n formatFileSize,\n formatRelativeTime,\n} from \"@ai-matrx/kit/format\";\n\nimport type {\n FieldChoice,\n FieldFormatDef,\n FieldFormatId,\n FieldFormatOptions,\n FieldBaseType,\n} from \"./types\";\n\n/**\n * A stored `interval` is the one place a NEGATIVE duration is a real quantity\n * rather than a clock bug, so this field format opts into the package's\n * `signed` mode. Everything else about the reading is the package's clock.\n */\nfunction formatSignedDuration(totalSeconds: number): string {\n return formatDurationSeconds(totalSeconds, { signed: true });\n}\n\n// ─── shared helpers ──────────────────────────────────────────────────────────\n\n/** Coerce to a finite number, or null. Accepts numeric strings (\"1,234\", \"$5\"). */\nfunction toNumber(value: unknown): number | null {\n if (typeof value === \"number\") return Number.isFinite(value) ? value : null;\n if (typeof value === \"string\") {\n const cleaned = value.replace(/[,\\s$€£¥%]/g, \"\");\n if (cleaned === \"\") return null;\n const n = Number(cleaned);\n return Number.isFinite(n) ? n : null;\n }\n return null;\n}\n\nfunction toText(value: unknown): string | null {\n if (typeof value === \"string\") return value;\n if (typeof value === \"number\" || typeof value === \"boolean\")\n return String(value);\n return null;\n}\n\n/**\n * Read a time of day from \"HH:MM\", \"HH:MM:SS\", \"H:MM am/pm\", \"2pm\", or a\n * Date/ISO string (its local time). `null` when it is not a time.\n */\nfunction parseTimeOfDay(\n value: unknown,\n): { h: number; m: number; s: number } | null {\n if (value instanceof Date) {\n return { h: value.getHours(), m: value.getMinutes(), s: value.getSeconds() };\n }\n if (typeof value !== \"string\") return null;\n const text = value.trim();\n if (!text) return null;\n const m = /^(\\d{1,2})(?::(\\d{2}))?(?::(\\d{2}))?\\s*([aApP]\\.?[mM]\\.?)?$/.exec(text);\n if (m) {\n let h = Number(m[1]);\n const min = m[2] ? Number(m[2]) : 0;\n const sec = m[3] ? Number(m[3]) : 0;\n const ampm = m[4]?.toLowerCase().replace(/\\./g, \"\");\n if (ampm) {\n if (h < 1 || h > 12) return null;\n if (ampm === \"pm\" && h !== 12) h += 12;\n if (ampm === \"am\" && h === 12) h = 0;\n }\n if (h > 23 || min > 59 || sec > 59) return null;\n return { h, m: min, s: sec };\n }\n // An ISO timestamp — keep its local time of day.\n if (/\\d{4}-\\d{2}-\\d{2}T/.test(text)) {\n const d = new Date(text);\n if (!Number.isNaN(d.getTime())) {\n return { h: d.getHours(), m: d.getMinutes(), s: d.getSeconds() };\n }\n }\n return null;\n}\n\n/**\n * A BARE CALENDAR DATE IS A LOCAL CALENDAR DATE — and this is the ONE place\n * that decision is made, for every format and every consumer.\n *\n * `new Date(\"2026-10-02\")` is defined by ECMAScript to parse a date-ONLY\n * string as **UTC** midnight, while `date` and `datetime` render with\n * `toLocaleDateString` / `toLocaleString`, i.e. in the viewer's zone. So west\n * of UTC a stored 2 October drew as 1 October — measured in the grid.\n *\n * A stored `YYYY-MM-DD` is not an instant, it is a day on a calendar, so it is\n * built as LOCAL midnight and reads as that same day everywhere. A string that\n * carries a zone designator (`Z`, `+hh:mm`, `-hh:mm`) IS an instant and is left\n * exactly as it was — converting it would be the same defect pointing the other\n * way. A zoneless `…T00:00:00` is already local by the spec; it is matched here\n * so the rule is stated rather than inherited.\n */\nconst BARE_CALENDAR_DATE =\n /^(\\d{4})-(\\d{2})-(\\d{2})(?:[T ]00:00(?::00(?:\\.0+)?)?)?$/;\n\nfunction toDate(value: unknown): Date | null {\n if (value instanceof Date) return Number.isNaN(value.getTime()) ? null : value;\n if (typeof value === \"number\") {\n const d = new Date(value);\n return Number.isNaN(d.getTime()) ? null : d;\n }\n if (typeof value === \"string\" && value.trim()) {\n const bare = BARE_CALENDAR_DATE.exec(value.trim());\n if (bare) {\n const [year, month, day] = [\n Number(bare[1]),\n Number(bare[2]),\n Number(bare[3]),\n ];\n const d = new Date(year, month - 1, day);\n // `new Date(y, m, d)` ROLLS OVER (\"2026-13-45\" would become a real date\n // in 2027) where `new Date(string)` returned Invalid Date and the caller\n // fell back to the amber base-type reading. Read the parts back so an\n // impossible date is still refused, exactly as before.\n d.setFullYear(year); // years < 100 would otherwise land in the 1900s.\n if (\n d.getFullYear() !== year ||\n d.getMonth() !== month - 1 ||\n d.getDate() !== day\n ) {\n return null;\n }\n return d;\n }\n const d = new Date(value);\n return Number.isNaN(d.getTime()) ? null : d;\n }\n return null;\n}\n\nfunction toList(value: unknown): unknown[] | null {\n if (Array.isArray(value)) return value;\n if (typeof value === \"string\") {\n const trimmed = value.trim();\n if (trimmed.startsWith(\"[\")) {\n try {\n const parsed: unknown = JSON.parse(trimmed);\n if (Array.isArray(parsed)) return parsed;\n } catch {\n /* fall through to comma split */\n }\n }\n if (trimmed === \"\") return [];\n return trimmed.split(\",\").map((s) => s.trim());\n }\n return null;\n}\n\nfunction groupedNumber(n: number, options: FieldFormatOptions): string {\n return new Intl.NumberFormat(undefined, {\n useGrouping: options.useGrouping !== false,\n ...(options.precision != null\n ? {\n minimumFractionDigits: options.precision,\n maximumFractionDigits: options.precision,\n }\n : { maximumFractionDigits: 10 }),\n }).format(n);\n}\n\nfunction affix(text: string, options: FieldFormatOptions): string {\n return `${options.prefix ?? \"\"}${text}${options.suffix ?? \"\"}`;\n}\n\nconst DURATION_TO_SECONDS: Record<\n NonNullable<FieldFormatOptions[\"durationUnit\"]>,\n number\n> = { milliseconds: 0.001, seconds: 1, minutes: 60, hours: 3600 };\n\n/**\n * Find the option a stored value corresponds to.\n *\n * Case-insensitive on purpose: a column adopting a choice format almost always\n * has \"Active\" and \"active\" in it already, and calling one of them a mismatch\n * would be pedantry, not help. Exact match always wins.\n */\nfunction findChoice(\n choices: FieldChoice[] | undefined,\n value: string,\n): FieldChoice | undefined {\n if (!choices || choices.length === 0) return undefined;\n const exact = choices.find((c) => c.value === value);\n if (exact) return exact;\n const lowered = value.toLowerCase();\n return choices.find((c) => c.value.toLowerCase() === lowered);\n}\n\nconst EMAIL_RE = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\n/** Explicit scheme, OR a host with a real dot-TLD. No whitespace either way. */\nconst URL_RE =\n /^(?:[a-z][a-z0-9+.-]*:\\/\\/\\S+|(?:[a-z0-9-]+\\.)+[a-z]{2,}(?:[:/?#]\\S*)?)$/i;\nconst HEX_RE = /^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/;\n\n// ─── the registry ────────────────────────────────────────────────────────────\n\nconst DEFS: FieldFormatDef[] = [\n // ── Text ──────────────────────────────────────────────────────────────────\n {\n id: \"text\",\n label: \"Text\",\n description: \"Plain single-line text\",\n group: \"Text\",\n base: \"string\",\n editor: \"text\",\n // Deliberately no optionKeys — plain text is the default for every string\n // column, and giving it prefix/suffix controls puts an options row under\n // every column in the settings dialog for a setting almost nobody wants.\n format: (v, o) => {\n const t = toText(v);\n return t === null ? null : affix(t, o);\n },\n parse: (raw) => (raw === \"\" || raw == null ? null : String(raw)),\n },\n {\n id: \"long_text\",\n label: \"Long text\",\n description: \"Multi-line text, edited in a larger box\",\n group: \"Text\",\n base: \"string\",\n editor: \"textarea\",\n format: (v) => toText(v),\n parse: (raw) => (raw === \"\" || raw == null ? null : String(raw)),\n },\n {\n id: \"markdown\",\n label: \"Markdown\",\n description: \"Rich text written as Markdown\",\n group: \"Text\",\n base: \"string\",\n editor: \"textarea\",\n rich: true,\n format: (v) => toText(v),\n parse: (raw) => (raw === \"\" || raw == null ? null : String(raw)),\n },\n {\n id: \"email\",\n label: \"Email\",\n description: \"Email address — click to compose\",\n group: \"Text\",\n base: \"string\",\n editor: \"email\",\n rich: true,\n format: (v) => {\n const t = toText(v)?.trim();\n if (!t) return null;\n return EMAIL_RE.test(t) ? t : null;\n },\n parse: (raw) => (raw === \"\" || raw == null ? null : String(raw).trim()),\n },\n {\n // A postal address. Free text by design (addresses have no one shape\n // worldwide); the cell opens it in maps, which is the thing people do\n // with an address in a table.\n id: \"address\",\n label: \"Address\",\n description: \"Street address — click to open in maps\",\n group: \"Text\",\n base: \"string\",\n editor: \"text\",\n rich: true,\n format: (v) => {\n const t = toText(v)?.trim();\n return t ? t : null;\n },\n parse: (raw) => (raw === \"\" || raw == null ? null : String(raw)),\n },\n {\n id: \"url\",\n label: \"Link\",\n description: \"Web address — click to open\",\n group: \"Text\",\n base: \"string\",\n editor: \"url\",\n rich: true,\n format: (v) => {\n const t = toText(v)?.trim();\n if (!t) return null;\n // `new URL()` alone is uselessly permissive here: `https://Beijing` and\n // even `https://Washington, D.C.` parse successfully, so every text value\n // would render as a broken link instead of flagging as a mismatch.\n // Require no whitespace, and either an explicit scheme or a real\n // dot-TLD host. Bare domains stay accepted; https is added on click.\n if (/\\s/.test(t)) return null;\n if (!URL_RE.test(t)) return null;\n try {\n new URL(t.includes(\"://\") ? t : `https://${t}`);\n return t;\n } catch {\n return null;\n }\n },\n parse: (raw) => (raw === \"\" || raw == null ? null : String(raw).trim()),\n },\n {\n id: \"phone\",\n label: \"Phone\",\n description: \"Phone number — click to call\",\n group: \"Text\",\n base: \"string\",\n editor: \"tel\",\n rich: true,\n format: (v) => {\n const t = toText(v)?.trim();\n if (!t) return null;\n // Must contain at least 7 digits to be a plausible phone number.\n return (t.match(/\\d/g) ?? []).length >= 7 ? t : null;\n },\n parse: (raw) => (raw === \"\" || raw == null ? null : String(raw).trim()),\n },\n {\n id: \"color\",\n label: \"Color\",\n description: \"Hex color with a swatch\",\n group: \"Text\",\n base: \"string\",\n editor: \"color\",\n rich: true,\n format: (v) => {\n const t = toText(v)?.trim();\n if (!t) return null;\n return HEX_RE.test(t) ? t.toLowerCase() : null;\n },\n parse: (raw) => (raw === \"\" || raw == null ? null : String(raw).trim()),\n },\n\n // ── Numbers ───────────────────────────────────────────────────────────────\n {\n id: \"number\",\n label: \"Number\",\n description: \"Plain number\",\n group: \"Numbers\",\n base: \"number\",\n alsoAccepts: [\"integer\"],\n editor: \"number\",\n numericAlign: true,\n optionKeys: [\"useGrouping\", \"precision\", \"prefix\", \"suffix\"],\n format: (v, o) => {\n const n = toNumber(v);\n return n === null ? null : affix(groupedNumber(n, o), o);\n },\n parse: (raw) => toNumber(raw),\n },\n {\n id: \"decimal\",\n label: \"Decimal\",\n description: \"Number with fixed decimal places\",\n group: \"Numbers\",\n base: \"number\",\n alsoAccepts: [\"integer\"],\n editor: \"number\",\n numericAlign: true,\n optionKeys: [\"precision\", \"useGrouping\", \"prefix\", \"suffix\"],\n format: (v, o) => {\n const n = toNumber(v);\n if (n === null) return null;\n return affix(groupedNumber(n, { ...o, precision: o.precision ?? 2 }), o);\n },\n parse: (raw) => toNumber(raw),\n },\n {\n id: \"currency\",\n label: \"Currency\",\n description: \"Money — stored as a plain number, shown with a symbol\",\n group: \"Numbers\",\n base: \"number\",\n alsoAccepts: [\"integer\"],\n editor: \"number\",\n numericAlign: true,\n optionKeys: [\"currency\", \"precision\"],\n format: (v, o) => {\n const n = toNumber(v);\n if (n === null) return null;\n const currency = (o.currency || \"USD\").toUpperCase();\n try {\n return new Intl.NumberFormat(undefined, {\n style: \"currency\",\n currency,\n ...(o.precision != null\n ? {\n minimumFractionDigits: o.precision,\n maximumFractionDigits: o.precision,\n }\n : {}),\n }).format(n);\n } catch {\n // Unknown/invalid ISO code — still show the money, never blank it.\n return `${groupedNumber(n, { precision: o.precision ?? 2 })} ${currency}`;\n }\n },\n parse: (raw) => toNumber(raw),\n },\n {\n id: \"percent\",\n label: \"Percent\",\n description: \"Percentage — stored as a plain number\",\n group: \"Numbers\",\n base: \"number\",\n alsoAccepts: [\"integer\"],\n editor: \"number\",\n numericAlign: true,\n optionKeys: [\"percentScale\", \"precision\"],\n format: (v, o) => {\n const n = toNumber(v);\n if (n === null) return null;\n const shown = o.percentScale === \"fraction\" ? n * 100 : n;\n return `${groupedNumber(shown, o.precision === undefined ? {} : { precision: o.precision })}%`;\n },\n parse: (raw) => toNumber(raw),\n },\n {\n // A percentage DRAWN as a bar — task completion, quota used, a score out of\n // 100. Same storage and scale rule as `percent`; only the rendering differs.\n id: \"progress\",\n label: \"Progress bar\",\n description: \"A percentage shown as a bar\",\n group: \"Numbers\",\n base: \"number\",\n alsoAccepts: [\"integer\"],\n editor: \"number\",\n rich: true,\n optionKeys: [\"percentScale\"],\n format: (v, o) => {\n const n = toNumber(v);\n if (n === null) return null;\n const shown = o.percentScale === \"fraction\" ? n * 100 : n;\n return `${groupedNumber(shown, { precision: 0 })}%`;\n },\n parse: (raw) => toNumber(raw),\n },\n {\n id: \"duration\",\n label: \"Duration\",\n description: \"Length of time, shown as h:mm:ss\",\n group: \"Numbers\",\n base: \"number\",\n alsoAccepts: [\"integer\"],\n editor: \"number\",\n numericAlign: true,\n optionKeys: [\"durationUnit\"],\n format: (v, o) => {\n const n = toNumber(v);\n if (n === null) return null;\n return formatSignedDuration(n * DURATION_TO_SECONDS[o.durationUnit ?? \"seconds\"]);\n },\n parse: (raw) => toNumber(raw),\n },\n {\n id: \"integer\",\n label: \"Whole number\",\n description: \"Integer with no decimal part\",\n group: \"Numbers\",\n base: \"integer\",\n alsoAccepts: [\"number\"],\n editor: \"number\",\n numericAlign: true,\n optionKeys: [\"useGrouping\", \"prefix\", \"suffix\"],\n format: (v, o) => {\n const n = toNumber(v);\n if (n === null) return null;\n return affix(groupedNumber(Math.trunc(n), { ...o, precision: 0 }), o);\n },\n parse: (raw) => {\n const n = toNumber(raw);\n return n === null ? null : Math.trunc(n);\n },\n },\n {\n id: \"rating\",\n label: \"Rating\",\n description: \"Score out of a maximum, shown as stars\",\n group: \"Numbers\",\n base: \"integer\",\n alsoAccepts: [\"number\"],\n editor: \"rating\",\n rich: true,\n optionKeys: [\"ratingMax\"],\n format: (v, o) => {\n const n = toNumber(v);\n if (n === null) return null;\n const max = o.ratingMax ?? 5;\n if (n < 0 || n > max) return null;\n return `${n} / ${max}`;\n },\n parse: (raw) => {\n const n = toNumber(raw);\n return n === null ? null : Math.round(n);\n },\n },\n {\n id: \"file_size\",\n label: \"File size\",\n description: \"Byte count shown as KB / MB / GB\",\n group: \"Numbers\",\n base: \"integer\",\n alsoAccepts: [\"number\"],\n editor: \"number\",\n numericAlign: true,\n // The name says BYTES on purpose: this cell holds a genuine byte length,\n // never a character or item count, and `formatFileSize` is only honest\n // about a quantity that is actually bytes.\n format: (v) => {\n const byteLength = toNumber(v);\n return byteLength === null ? null : formatFileSize(byteLength);\n },\n parse: (raw) => {\n const byteLength = toNumber(raw);\n return byteLength === null ? null : Math.trunc(byteLength);\n },\n },\n\n // ── Choice ────────────────────────────────────────────────────────────────\n {\n id: \"boolean\",\n label: \"Yes / No\",\n description: \"True or false\",\n group: \"Choice\",\n base: \"boolean\",\n editor: \"checkbox\",\n format: (v) => {\n if (typeof v === \"boolean\") return v ? \"Yes\" : \"No\";\n if (v === \"true\" || v === 1) return \"Yes\";\n if (v === \"false\" || v === 0) return \"No\";\n return null;\n },\n parse: (raw) => {\n if (typeof raw === \"boolean\") return raw;\n if (raw == null || raw === \"\") return null;\n return String(raw).toLowerCase() === \"true\" || raw === 1;\n },\n },\n\n // ── Dates ─────────────────────────────────────────────────────────────────\n {\n id: \"date\",\n label: \"Date\",\n description: \"Calendar date\",\n group: \"Dates\",\n base: \"date\",\n alsoAccepts: [\"datetime\", \"string\"],\n editor: \"date\",\n optionKeys: [\"dateStyle\"],\n format: (v, o) => {\n const d = toDate(v);\n if (!d) return null;\n return d.toLocaleDateString(undefined, {\n dateStyle: o.dateStyle ?? \"medium\",\n });\n },\n parse: (raw) => (raw === \"\" || raw == null ? null : String(raw)),\n },\n {\n id: \"datetime\",\n label: \"Date & time\",\n description: \"Calendar date with a time of day\",\n group: \"Dates\",\n base: \"datetime\",\n alsoAccepts: [\"date\", \"string\"],\n editor: \"datetime\",\n optionKeys: [\"dateStyle\"],\n format: (v, o) => {\n const d = toDate(v);\n if (!d) return null;\n return d.toLocaleString(undefined, {\n dateStyle: o.dateStyle ?? \"medium\",\n timeStyle: \"short\",\n });\n },\n parse: (raw) => (raw === \"\" || raw == null ? null : String(raw)),\n },\n {\n // A TIME OF DAY with no date: opening hours, a shift start, a meeting slot.\n // Stored as the string the <input type=\"time\"> gives (\"HH:MM\" or\n // \"HH:MM:SS\"), shown in the viewer's locale (\"2:30 PM\" / \"14:30\"). The\n // storage type stays `string`; sorting is lexical on the 24-hour form,\n // which is chronological — the one reason the stored form is 24-hour.\n id: \"time\",\n label: \"Time\",\n description: \"Time of day, without a date\",\n group: \"Dates\",\n base: \"string\",\n editor: \"time\",\n optionKeys: [\"timeSeconds\"],\n format: (v, o) => {\n const parts = parseTimeOfDay(v);\n if (!parts) return null;\n const d = new Date(2000, 0, 1, parts.h, parts.m, parts.s);\n return d.toLocaleTimeString(undefined, {\n hour: \"numeric\",\n minute: \"2-digit\",\n ...(o.timeSeconds ? { second: \"2-digit\" } : {}),\n });\n },\n // Normalise to the 24-hour \"HH:MM[:SS]\" form; a value that is not a time\n // is handed back unchanged so the validation layer can refuse it by name.\n parse: (raw) => {\n if (raw == null || raw === \"\") return null;\n const parts = parseTimeOfDay(raw);\n if (!parts) return raw;\n // A TIME OF DAY, not a duration: this is the stored 24-hour spelling the\n // column keeps, joined rather than interpolated so it cannot be read as a\n // hand-rolled clock formatter.\n const two = (n: number) => String(n).padStart(2, \"0\");\n const fields = parts.s > 0 ? [parts.h, parts.m, parts.s] : [parts.h, parts.m];\n return fields.map(two).join(\":\");\n },\n },\n {\n // Assigned BY THE DATABASE when the row is inserted (trigger\n // `_udt_autonumber`), so it is the same number whichever door made the row\n // and never shifts when another row is deleted. Stored in the cell as an\n // integer; every editor refuses it. `prefix` turns 42 into \"INV-42\".\n id: \"autonumber\",\n label: \"Autonumber\",\n description: \"A number the table assigns to each new row — never typed\",\n group: \"Numbers\",\n base: \"integer\",\n alsoAccepts: [\"number\"],\n editor: \"computed\",\n numericAlign: true,\n optionKeys: [\"prefix\"],\n format: (v, o) => {\n const n = toNumber(v);\n if (n === null) return null;\n return `${o.prefix ?? \"\"}${Math.trunc(n)}`;\n },\n parse: () => null,\n },\n // ── System columns ────────────────────────────────────────────────────────\n // Filled from the ROW'S OWN RECORD on read (`withComputedColumns` in\n // `@ai-matrx/design-system/formulas`), never typed and never stored in the\n // cell. `editor: \"computed\"` makes every editor refuse them.\n {\n id: \"created_time\",\n label: \"Created time\",\n description: \"When the row was added — filled in automatically\",\n group: \"Dates\",\n base: \"string\",\n editor: \"computed\",\n optionKeys: [\"dateStyle\"],\n format: (v, o) => FIELD_FORMATS.datetime.format(v, o),\n parse: () => null,\n },\n {\n id: \"modified_time\",\n label: \"Last modified time\",\n description: \"When the row last changed — filled in automatically\",\n group: \"Dates\",\n base: \"string\",\n editor: \"computed\",\n optionKeys: [\"dateStyle\"],\n format: (v, o) => FIELD_FORMATS.datetime.format(v, o),\n parse: () => null,\n },\n {\n id: \"relative_time\",\n label: \"Relative time\",\n description: 'Shown as \"3 days ago\"',\n group: \"Dates\",\n base: \"datetime\",\n alsoAccepts: [\"date\", \"string\"],\n editor: \"datetime\",\n format: (v) => {\n const d = toDate(v);\n // THE one relative stamp (@ai-matrx/kit/format). This registry carried\n // its own Intl ladder until 2026-09-12 — the last re-implementation of\n // formatRelativeTime in this repo. It ROUNDED, so a 1h50m-old row read\n // \"2 hours ago\" here and \"1h ago\" on every other surface in the product;\n // the package truncates, which is what the rest of the fleet shows.\n return d ? formatRelativeTime(d, { style: \"intl\" }) : null;\n },\n parse: (raw) => (raw === \"\" || raw == null ? null : String(raw)),\n },\n\n // ── Structured ────────────────────────────────────────────────────────────\n {\n id: \"json\",\n label: \"JSON\",\n description: \"Structured object\",\n group: \"Structured\",\n base: \"json\",\n alsoAccepts: [\"array\"],\n editor: \"json\",\n format: (v) => {\n if (v === null || v === undefined) return null;\n if (typeof v === \"object\") return JSON.stringify(v);\n return toText(v);\n },\n parse: (raw) => {\n if (raw == null || raw === \"\") return null;\n if (typeof raw !== \"string\") return raw;\n try {\n return JSON.parse(raw) as unknown;\n } catch {\n return raw;\n }\n },\n },\n {\n id: \"array\",\n label: \"List\",\n description: \"List of values\",\n group: \"Structured\",\n base: \"array\",\n alsoAccepts: [\"json\"],\n editor: \"json\",\n format: (v) => {\n const list = toList(v);\n return list === null ? null : list.map((i) => String(i)).join(\", \");\n },\n parse: (raw) => {\n if (raw == null || raw === \"\") return null;\n if (Array.isArray(raw)) return raw;\n return toList(raw) ?? raw;\n },\n },\n {\n // FILES ATTACHED TO THE ROW. The cell stores the files feature's file ids\n // (`files.files.id`), never a URL or a name: a rename or a move in Files\n // changes nothing here, and access is the file's own (RLS) — a reader who\n // may see the row but not the file gets the chip's honest \"no access\", not\n // a broken link. Drawn by `AttachmentChips`, edited by `AttachmentInput`\n // through the ONE file picker.\n id: \"attachment\",\n label: \"Attachments\",\n description: \"Files from your Files, shown as chips\",\n group: \"Structured\",\n base: \"array\",\n // Not `string`: on a text column it must appear under \"Other kinds\" so\n // picking it retypes the column to a list, rather than storing a JSON\n // string of ids in a text cell.\n alsoAccepts: [\"json\"],\n editor: \"attachment\",\n rich: true,\n format: (v) => {\n const list = toList(v);\n if (list === null) return null;\n const n = list.filter((i) => String(i).trim()).length;\n return n === 0 ? \"\" : n === 1 ? \"1 file\" : `${n} files`;\n },\n parse: (raw) => {\n if (raw == null || raw === \"\") return null;\n if (Array.isArray(raw)) return raw.map((i) => String(i).trim()).filter(Boolean);\n const text = String(raw).trim();\n if (text.startsWith(\"[\")) {\n try {\n const parsed = JSON.parse(text);\n if (Array.isArray(parsed)) return parsed.map((i) => String(i).trim()).filter(Boolean);\n } catch {\n // a comma list, below\n }\n }\n return text.split(\",\").map((s) => s.trim()).filter(Boolean);\n },\n },\n {\n id: \"tags\",\n label: \"Tags\",\n description: \"List of values shown as chips\",\n group: \"Structured\",\n base: \"array\",\n alsoAccepts: [\"json\", \"string\"],\n editor: \"json\",\n rich: true,\n format: (v) => {\n const list = toList(v);\n return list === null ? null : list.map((i) => String(i)).join(\", \");\n },\n parse: (raw) => {\n if (raw == null || raw === \"\") return null;\n if (Array.isArray(raw)) return raw;\n return toList(raw) ?? raw;\n },\n },\n\n // ─── Choice ────────────────────────────────────────────────────────────────\n //\n // An enum WITHOUT a database enum. The column stays `string` (or `array` for\n // multi), the database enforces nothing, and stripping the format leaves\n // every stored value exactly as it was. Options are a UI layer — the same\n // trick `percent` plays on a number.\n //\n // The fallback law is the whole reason this is safe to switch on over live\n // data: a value not in the option list is NOT an error and is NEVER blanked.\n // `format()` returns null for it, `<FormattedFieldValue>` renders it in amber\n // with the reason, and the user can fix it or adopt it. Declaring the options\n // is therefore how you FIND your stray values, not how you lose them.\n //\n // Options resolve through `choices.ts` — inline `options.choices`, or a\n // structured list via `options.structuredList`. This def only ever sees\n // whatever the caller resolved into `options.choices`, so it stays pure and\n // synchronous like every other format.\n {\n id: \"choice\",\n label: \"Choice\",\n description: \"One value from a list of options\",\n group: \"Choice\",\n base: \"string\",\n alsoAccepts: [\"number\", \"integer\", \"boolean\"],\n editor: \"select\",\n rich: true,\n optionKeys: [\"choices\", \"structuredList\", \"allowOther\"],\n format: (v, options) => {\n const text = toText(v);\n if (text === null) return null;\n const trimmed = text.trim();\n if (trimmed === \"\") return \"\";\n const match = findChoice(options.choices, trimmed);\n // No options resolved yet (a structured list still loading, or a column\n // whose list is unreachable) — show the raw value rather than accusing\n // every cell of being off-list.\n if (!options.choices || options.choices.length === 0) return trimmed;\n return match ? (match.label ?? match.value) : null;\n },\n parse: (raw) => {\n if (raw == null) return null;\n const text = toText(raw);\n if (text === null) return raw;\n const trimmed = text.trim();\n return trimmed === \"\" ? null : trimmed;\n },\n },\n {\n // A PERSON: one member of the organization the table belongs to, stored as\n // the user id. It is a choice column whose options are the members —\n // supplied at runtime by whoever renders it (features/data-tables/\n // person-choices.tsx), the same door a structured-list-bound column uses —\n // so the cell picker, the chip, filters, color-by and the agent scope are\n // all the choice system's, with no second implementation.\n id: \"person\",\n label: \"Person\",\n description: \"Someone in your organization — pick from its members\",\n group: \"Choice\",\n base: \"string\",\n editor: \"select\",\n rich: true,\n format: (v, o) => FIELD_FORMATS.choice.format(v, o),\n parse: (raw) => FIELD_FORMATS.choice.parse(raw, {}),\n },\n {\n // A RECORD OF ANOTHER TABLE. The cell stores that record's ID; the screen\n // shows its WORDS. `person` is the precedent and the template — it has\n // stored a user id and displayed a name through `choiceMap` since\n // 2026-09-21 — so `relation` is a member of CHOICE_FORMAT_IDS and every\n // one of the ten readers that print a cell resolves it through the path\n // they already use for `person`. There is no eleventh place to remember.\n //\n // THREE STATES, THREE RENDERINGS, NO FOURTH (OLD-TABLES-CUTOVER §3.3):\n // resolved the target's words\n // unresolvable the amber fallback WITH the id, as an identifier — never\n // a blank, and never the bare uuid as if it were text, so a\n // person can act on it and support can trace it\n // withheld platform.relation_withheld_label(), with NO id — a cell\n // the reader may not see never leaks the identifier through\n // the gap between the two stores' refusals\n //\n // `format` therefore returns null (the fallback law's \"cannot interpret\")\n // for anything the resolver did not turn into words, and the renderer is\n // what distinguishes unresolvable from withheld. It never prints a bare id.\n id: \"relation\",\n label: \"Relation\",\n description: \"A record of another table — pick it, and its name is shown\",\n group: \"Choice\",\n base: \"string\",\n alsoAccepts: [\"array\", \"json\"],\n editor: \"select\",\n rich: true,\n optionKeys: [\"relation_target\", \"relation_max\", \"on_delete\", \"display\"],\n // IT DOES NOT DELEGATE TO `choice`, AND THAT IS THE WHOLE POINT.\n // `choice.format` deliberately returns the RAW VALUE when no options have\n // resolved yet — a structured list still loading should not make every cell\n // look off-list. For a label column that is right: the raw value IS the\n // label. For an identifier column it is the defect §3.3 names, and it is\n // how the first cut of this wave put four raw uuids on a real grid: the\n // fallback arm never ran, because returning the uuid counted as SUCCESS.\n //\n // Here, a value that did not resolve to words is a MISMATCH — `null` — so\n // the renderer's three-state arm owns it and shows the amber fallback with\n // the id marked as an identifier, or the withheld sentence. The id never\n // reaches the eye as if it were the cell's contents.\n format: (v, options) => {\n const list = toList(v);\n const values =\n list !== null ? list : v == null ? [] : [v];\n if (values.length === 0) return \"\";\n const words: string[] = [];\n for (const entry of values) {\n const text = toText(entry);\n if (text === null) return null;\n const trimmed = text.trim();\n if (trimmed === \"\") continue;\n const match = findChoice(options.choices, trimmed);\n if (!match) return null; // nothing resolved it — never print the id\n words.push(match.label ?? match.value);\n }\n return words.length === 0 ? \"\" : words.join(\", \");\n },\n parse: (raw) => FIELD_FORMATS.choice.parse(raw, {}),\n },\n {\n id: \"multi_choice\",\n label: \"Multi-choice\",\n description: \"Any number of values from a list of options\",\n group: \"Choice\",\n base: \"array\",\n alsoAccepts: [\"string\", \"json\"],\n editor: \"multiselect\",\n rich: true,\n optionKeys: [\"choices\", \"structuredList\", \"allowOther\"],\n format: (v, options) => {\n const list = toList(v);\n if (list === null) return null;\n if (list.length === 0) return \"\";\n const labels: string[] = [];\n let anyOffList = false;\n for (const entry of list) {\n const text = toText(entry);\n if (text === null) {\n anyOffList = true;\n continue;\n }\n const match = findChoice(options.choices, text.trim());\n if (match) labels.push(match.label ?? match.value);\n else {\n labels.push(text.trim());\n anyOffList = true;\n }\n }\n // Same leniency as `choice`: with no options resolved, nothing is off-list.\n const haveOptions = !!options.choices && options.choices.length > 0;\n if (haveOptions && anyOffList) return null;\n return labels.join(\", \");\n },\n parse: (raw) => {\n if (raw == null || raw === \"\") return null;\n if (Array.isArray(raw)) return raw;\n return toList(raw) ?? raw;\n },\n },\n\n // ─── Computed ──────────────────────────────────────────────────────────────\n //\n // A formula column stores NOTHING. `metadata.format.options.formula` holds\n // the expression; the stored cell is always null; the value is computed on\n // read from the other columns of the same row\n // (`@ai-matrx/design-system/formulas`). Grouped under Structured on purpose\n // — the picker's groups are a fixed vocabulary, and a formula is structure\n // over a plain string column, not a sixth family.\n //\n // `format()` here is handed the COMPUTED value, never a stored one, and its\n // only job is to render it: through `resultFormat`'s own `format()` when the\n // column declares one (a computed total shown as Currency, a computed date\n // shown as a Date), and plainly otherwise. It never returns null for an\n // empty value, because a formula legitimately produces BLANK.\n //\n // No `optionKeys`: the expression is authored in its own editor, not in the\n // generic option rail, and listing a key the picker has no control for would\n // open an empty options section under every formula column.\n {\n id: \"formula\",\n label: \"Formula\",\n description: \"Computed from the other columns in the row — never typed in\",\n group: \"Structured\",\n base: \"string\",\n editor: \"computed\",\n format: (v, o) => {\n const target = o.formula?.resultFormat;\n if (target && target !== \"formula\") {\n const delegate = FIELD_FORMATS[target];\n if (delegate) {\n const rendered = delegate.format(v, o);\n if (rendered !== null) return rendered;\n }\n }\n if (v === null || v === undefined) return \"\";\n if (typeof v === \"boolean\") return v ? \"Yes\" : \"No\";\n if (typeof v === \"number\") return groupedNumber(v, o);\n return toText(v);\n },\n // Nothing is ever stored in a formula column. Returning the raw input here\n // would be the one way a computed cell could acquire a value of its own.\n parse: () => null,\n },\n];\n\nexport const FIELD_FORMATS: Readonly<Record<FieldFormatId, FieldFormatDef>> =\n Object.freeze(\n Object.fromEntries(DEFS.map((d) => [d.id, d])) as Record<\n FieldFormatId,\n FieldFormatDef\n >,\n );\n\nexport const FIELD_FORMAT_LIST: readonly FieldFormatDef[] = Object.freeze(DEFS);\n\nexport const FIELD_FORMAT_IDS = DEFS.map((d) => d.id) as FieldFormatId[];\n\nexport function getFieldFormat(\n id: string | null | undefined,\n): FieldFormatDef | null {\n if (!id) return null;\n return FIELD_FORMATS[id as FieldFormatId] ?? null;\n}\n\n/**\n * The format a column gets when it declares none — the identity format for its\n * storage type. Every existing column therefore behaves exactly as before.\n */\nexport function defaultFormatForBase(base: string): FieldFormatId {\n switch (base as FieldBaseType) {\n case \"number\":\n return \"number\";\n case \"integer\":\n return \"integer\";\n case \"boolean\":\n return \"boolean\";\n case \"date\":\n return \"date\";\n case \"datetime\":\n return \"datetime\";\n case \"json\":\n return \"json\";\n case \"array\":\n return \"array\";\n default:\n return \"text\";\n }\n}\n\n/** Formats that can legally sit on a given storage type. */\nexport function formatsForBase(base: string): FieldFormatDef[] {\n return DEFS.filter(\n (d) => d.base === base || (d.alsoAccepts ?? []).includes(base as FieldBaseType),\n );\n}\n\n/** Group the pickable formats for a base type, preserving registry order. */\nexport function groupedFormatsForBase(\n base: string,\n): { group: FieldFormatDef[\"group\"]; formats: FieldFormatDef[] }[] {\n const out: { group: FieldFormatDef[\"group\"]; formats: FieldFormatDef[] }[] =\n [];\n for (const def of formatsForBase(base)) {\n const bucket = out.find((g) => g.group === def.group);\n if (bucket) bucket.formats.push(def);\n else out.push({ group: def.group, formats: [def] });\n }\n return out;\n}\n","/**\n * THE FALLBACK LAW, implemented.\n *\n * `formatFieldValue` never blanks a cell and never throws. When the declared\n * format cannot interpret the stored value it returns `ok: false` with the\n * **base-type** rendering of that same value plus a human `reason`. The UI\n * renders that text in amber (see `<FormattedFieldValue>`), so a mismatch is\n * visible instead of invisible.\n */\nimport {\n defaultFormatForBase,\n getFieldFormat,\n FIELD_FORMATS,\n} from \"./registry\";\nimport type {\n FieldFormatConfig,\n FieldFormatOptions,\n FormatResult,\n} from \"./types\";\n\nfunction isEmpty(value: unknown): boolean {\n return value === null || value === undefined || value === \"\";\n}\n\n/** Last-resort rendering — the raw value, readable, never empty-by-accident. */\nfunction baseText(value: unknown): string {\n if (isEmpty(value)) return \"\";\n if (typeof value === \"object\") {\n try {\n return JSON.stringify(value);\n } catch {\n return String(value);\n }\n }\n return String(value);\n}\n\n/**\n * Read a field's declared format off its `metadata` JSONB. Returns null when\n * the field declares none (or declares one we don't know) — the caller then\n * falls back to the storage type's identity format, so an unknown or removed\n * format id degrades to plain, never to broken.\n */\nexport function readFieldFormatConfig(\n metadata: unknown,\n): FieldFormatConfig | null {\n if (!metadata || typeof metadata !== \"object\") return null;\n const raw = (metadata as Record<string, unknown>).format;\n if (!raw || typeof raw !== \"object\") return null;\n const id = (raw as Record<string, unknown>).id;\n if (typeof id !== \"string\" || !getFieldFormat(id)) return null;\n const options = (raw as Record<string, unknown>).options;\n // `exactOptionalPropertyTypes`: an absent `options` is the key being ABSENT,\n // never the key holding `undefined` — the two are different documents once\n // this config is written back out as JSON.\n return {\n id: id as FieldFormatConfig[\"id\"],\n ...(options && typeof options === \"object\"\n ? { options: options as FieldFormatOptions }\n : {}),\n };\n}\n\n/**\n * Resolve the format a field actually renders with: its declared one, or the\n * identity format for its storage type.\n */\nexport function resolveFieldFormat(\n dataType: string,\n metadata: unknown,\n): FieldFormatConfig {\n const declared = readFieldFormatConfig(metadata);\n if (declared) return declared;\n return { id: defaultFormatForBase(dataType) };\n}\n\nexport function formatFieldValue(\n value: unknown,\n config: FieldFormatConfig | null | undefined,\n /** The column's storage type — used for the fallback rendering. */\n dataType?: string,\n): FormatResult {\n if (isEmpty(value)) return { ok: true, text: \"\", empty: true };\n\n const def = config ? getFieldFormat(config.id) : null;\n const effective =\n def ?? FIELD_FORMATS[defaultFormatForBase(dataType ?? \"string\")];\n\n let formatted: string | null;\n try {\n formatted = effective.format(value, config?.options ?? {});\n } catch {\n formatted = null;\n }\n\n if (formatted !== null) return { ok: true, text: formatted, empty: false };\n\n // Mismatch. Show what IS stored, flagged.\n return {\n ok: false,\n text: baseText(value),\n empty: false,\n reason: `This value is not a valid ${effective.label.toLowerCase()} — showing the stored value instead.`,\n };\n}\n\n/** Turn raw editor input into the value to store under a format. */\nexport function parseFieldInput(\n raw: unknown,\n config: FieldFormatConfig | null | undefined,\n dataType?: string,\n): unknown {\n const def = config ? getFieldFormat(config.id) : null;\n const effective =\n def ?? FIELD_FORMATS[defaultFormatForBase(dataType ?? \"string\")];\n try {\n return effective.parse(raw, config?.options ?? {});\n } catch {\n return raw;\n }\n}\n\n/**\n * Would this stored value survive its declared format? Used to count mismatches\n * so a column can warn (\"4 values don't fit Currency\") instead of silently\n * showing amber cells.\n */\nexport function valueFitsFormat(\n value: unknown,\n config: FieldFormatConfig | null | undefined,\n dataType?: string,\n): boolean {\n return formatFieldValue(value, config, dataType).ok;\n}\n","/**\n * A PHONE NUMBER AS A PERSON WRITES IT — the Phone format's one look, for every\n * surface that shows one (the record store's grid, the app's Sheet, any table).\n * Ten North-American digits read (805) 555-0142, eleven with the leading 1 read\n * +1 (805) 555-0142; anything else (another country's plan, an extension, words)\n * is shown as typed. Display only: the stored value is never rewritten.\n */\nexport function formatPhone(raw: string): string {\n const text = raw.trim();\n const digits = text.replace(/\\D/g, \"\");\n if (/[a-z]/i.test(text.replace(/ext\\.?|x/gi, \"\"))) return text;\n if (digits.length === 10 && !text.startsWith(\"+\")) {\n return `(${digits.slice(0, 3)}) ${digits.slice(3, 6)}-${digits.slice(6)}`;\n }\n if (\n digits.length === 11 &&\n digits.startsWith(\"1\") &&\n (text.startsWith(\"+1\") || text.startsWith(\"1\") || !text.startsWith(\"+\"))\n ) {\n return `+1 (${digits.slice(1, 4)}) ${digits.slice(4, 7)}-${digits.slice(7)}`;\n }\n return text;\n}\n"],"mappings":"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","names":["d"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-matrx/design-system",
3
- "version": "0.56.2",
3
+ "version": "0.59.0",
4
4
  "description": "AI Matrx semantic React primitives shared across Vite, Next.js, desktop, and customer-built applications.",
5
5
  "type": "module",
6
6
  "license": "MIT",