@ai-matrx/records-ui 0.111.0 โ†’ 0.113.2

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/names.ts","../src/parity.ts","../src/gridLook.ts","../src/gridPage.ts","../src/rowVersion.ts","../src/groupReads.ts","../src/presentation.ts","../src/viewKeys.ts","../src/views.ts","../src/viewLook.ts","../../entity-data/src/core/errors.ts","../../entity-data/src/core/client.ts","../../entity-data/src/core/realtime.ts","../../entity-data/src/core/wire.ts","../../entity-data/src/port/rows.ts","../../entity-data/src/port/port.ts","../src/entityQuery.ts","../src/firstPageEntity.ts","../src/firstPage.ts"],"names":["field","zone","op","raw","held","isUuidShape","asRecordsDataSource","recordingDataSource","createRecordsClient"],"mappings":"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","file":"first-page.js","sourcesContent":["// src/names.ts โ€” @ai-matrx/records-ui\n//\n// WHAT A PERSON READS, NEVER WHAT THE MACHINE STORES.\n//\n// Measured on the real screens (independent verdict, 19 September): a table row\n// read `Saved views ยท 9 fields ยท records_ui_view`, a relation cell read a bare\n// `8f2cโ€ฆ` identifier, a header read `stage` because the Field's label was\n// blank, and the type word on a column tooltip read `long_text`. Airtable,\n// Notion and Linear never show a person an internal name; the verdict called\n// this one of the three things a first-time user feels within a minute.\n//\n// So every place this package prints something a person reads goes through one\n// of these three functions. They are deliberately dumb: they choose the best\n// human string that IS AVAILABLE and, when none is, say what the thing is\n// rather than printing the token. No screen in this package formats a name of\n// its own any more.\n\nimport type { Field, ReadRow, RecordDocument, Table } from \"@ai-matrx/records\";\nimport { isUuidShape } from \"@ai-matrx/kit/uuid\";\nimport { humanizeIdentifier } from \"@ai-matrx/kit/text-case\";\n\n/** The name of one Field, as a header, a form label or a peek's term. */\nexport function fieldName(field: Field): string {\n const label = (field.label ?? \"\").trim();\n // A LABEL THAT IS ONLY A MACHINE KEY is not a name (merged-grid review 2026-09-26): a table made\n // by an import or an agent kept `label: \"job_no\"`, and the header read `job_no`. A key joined by\n // underscores is written the way a person writes it (\"Job no\"). A single lowercase word the\n // owner typed (\"mobile\", \"title\") is their own words and is shown as they wrote it.\n if (label !== \"\" && label === field.key && /^[a-z0-9]+(?:[_.-][a-z0-9]+)+$/.test(label)) return humanizeIdentifier(label);\n if (label !== \"\") return label;\n const name = (field.name ?? \"\").trim();\n if (name !== \"\") return name;\n return humanizeIdentifier(field.key);\n}\n\n/** The name of one Table, as a card, a heading or a breadcrumb. */\nexport function tableName(table: Table): string {\n const name = (table.name ?? \"\").trim();\n if (name !== \"\") return name;\n const singular = (table.label_singular ?? \"\").trim();\n if (singular !== \"\") return singular;\n return humanizeIdentifier(table.slug ?? \"\");\n}\n\n/**\n * The name of one RECORD, for a chip, a card, a relation cell or a peek title.\n *\n * `titleKey` is the Table's own `title_field` when the caller knows the Table โ€”\n * that is the store's answer to \"what makes this record a chip\" (REC-2) โ€” and\n * the conventional keys are tried after it. A record with nothing readable is\n * NOT shown as a truncated uuid: it says it has no name yet, because \"8f2c1b0a\"\n * tells a person nothing and looks like a bug.\n */\nexport function recordName(\n document: RecordDocument | undefined,\n titleKey?: string | null,\n fallback = \"Untitled\",\n): string {\n const data = (document ?? {}) as Record<string, unknown>;\n const keys = [titleKey, \"title\", \"name\", \"label\", \"full_name\", \"subject\", \"summary\"].filter(\n (key): key is string => typeof key === \"string\" && key !== \"\",\n );\n for (const key of keys) {\n const value = data[key];\n // AN ID IS NEVER A NAME, AND THIS FUNCTION USED TO RETURN ONE (TAILS-2, 2026-09-21).\n // A Table's title column may be a RELATION, and a relation's stored value IS the other\n // record's id (FLD-6 / REL-1) โ€” so `recordName` handed every caller a bare uuid for\n // every Table titled that way, which is how the board's unplaced list came to read\n // \"11 records are not in any of these columns โ€” ca5f1ac7-โ€ฆ\". A caller that can resolve\n // the id resolves it (`scalarText` with the label lookup); a caller that cannot is\n // better off reading the next column, and then the fallback, than a hex number.\n if (typeof value === \"string\" && value.trim() !== \"\" && !looksLikeId(value.trim())) {\n return value.trim();\n }\n if (typeof value === \"number\") return String(value);\n }\n return fallback;\n}\n\n\n/**\n * Is this string the store's id for something, rather than words a person wrote?\n *\n * The same question `custom._card_words` asks on the database, with the same regex, so the\n * screen and the store agree about what is a name (TAILS-2, 2026-09-21).\n */\nexport function looksLikeId(value: unknown): boolean {\n return typeof value === \"string\" && isUuidShape(value.trim());\n}\n\n/** The same question for a row the read door returned. */\nexport function rowName(row: ReadRow | undefined, titleKey?: string | null, fallback = \"Untitled\"): string {\n return recordName(row?.document, titleKey, fallback);\n}\n\n// โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•\n// NAMING A RECORD WHOSE TABLE YOU ALREADY HOLD โ€” ONE CALL, NOT A COPY PER SCREEN.\n// โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•\n//\n// ๐Ÿšจ WHY THESE TWO EXIST (lane RELATION-DISPLAY-2, 2026-09-21).\n//\n// `check:one-reference-builder` shipped with a baseline of SIX, and its own header said the\n// number was not zero because \"moving six surfaces onto the store door is real work\". Three\n// of the six were not asking the store's question at all. `Grid.tsx` names the row a person\n// is looking at for the Talk action; `Peek.tsx` names the record being peeked at;\n// `PortalsPanel.tsx` names the rows of the portal's own client Table; and this repo's\n// `TryEverythingScreen.tsx` does the same in two places. NONE of them is resolving a\n// RELATION โ€” they hold the Table and they hold the document, and there is nothing to ask a\n// door about. What they were each doing by hand was the two-line join between them:\n//\n// recordName(row.document, table.title_field)\n//\n// Written out five times in four files, which is how a Table whose title column is `cname`\n// came to read \"Untitled\" on one screen and correctly on the next. It is the same question\n// as the store's, one rung down the ladder, and the same one answer should serve it โ€” so it\n// lives HERE, in the file the guard exempts BY CONSTRUCTION as the primitive itself, and\n// every screen calls it instead of re-deriving it.\n//\n// WHAT THESE ARE NOT. They are not a second resolver and must never become one. They take a\n// Table a caller ALREADY HAS and a document it ALREADY HAS. The moment a caller is holding\n// an ID and wants words, that is the store's question and the answer is\n// `client.relationWords` / `client.relationWordsMany` โ€” which ask the read-access ladder\n// BEFORE they read a name, apply that column's own display spec, and return the withheld\n// sentence for a record the reader may not open. Nothing in here can do any of that, and\n// nothing in here should try.\n\n/** A Table as far as naming is concerned โ€” its own declared title column (REC-2). */\ntype TitledTable = Pick<Table, \"title_field\"> | null | undefined;\n\n/**\n * THE NAME OF A RECORD WHOSE TABLE YOU ALREADY HOLD.\n *\n * `table` is the Table the record belongs to; its `title_field` is the store's answer to\n * \"what makes this record a chip\" (REC-2) and is tried first, then the conventional keys,\n * then the fallback. A Table that has not loaded yet is `null` and the conventional keys\n * still answer โ€” a name arriving a tick late is better than a screen that shows nothing.\n */\nexport function recordNameIn(\n table: TitledTable,\n document: RecordDocument | undefined,\n fallback = \"Untitled\",\n): string {\n return recordName(document, table?.title_field ?? null, fallback);\n}\n\n/**\n * THE NAME A CHANGE NOTICE USES โ€” the record as it is AFTER the change (merged-grid tails,\n * 2026-09-26). Typing a first name into a row's label column read \"Changed Title on Untitled\":\n * the notice named the row from the document held before the write. The row is called what it is\n * called now; only when the change emptied its name does the notice use the name it had.\n */\nexport function changedRecordName(\n table: TitledTable,\n document: RecordDocument | undefined,\n key: string,\n after: unknown,\n): string {\n const now = recordName({ ...((document ?? {}) as Record<string, unknown>), [key]: after } as RecordDocument, table?.title_field ?? null, \"\");\n return now !== \"\" ? now : recordNameIn(table, document);\n}\n\n/** The same, for a row the read door returned. */\nexport function rowNameIn(table: TitledTable, row: ReadRow | undefined, fallback = \"Untitled\"): string {\n return recordName(row?.document, table?.title_field ?? null, fallback);\n}\n\n/**\n * WHAT A RELATION CHIP SAYS WHEN THE RECORD IT POINTS AT IS NOT YOURS.\n *\n * A relation joins two records in two tables, and holding one end says nothing\n * about the other: a Job shared with you may point at a Client nobody shared.\n * The chip then has three possible answers and only one of them is honest โ€”\n * the target's TITLE is a leak, a bare id is a bug with a hex number in it, and\n * a permanent \"Loadingโ€ฆ\" is a lie that never resolves.\n *\n * This is the SAME sentence `platform.relation_withheld_label()` answers on the\n * database, word for word, so a person reads one wording whether the screen or\n * the store worked it out. Changing it changes both (RELATION-DECLARE,\n * 2026-09-20).\n */\nexport const WITHHELD_RECORD_LABEL = \"A record you have not been given access to\";\n/**\n * WHEN THE STORE COULD NOT BE ASKED (a 503, a dropped connection) the name is UNREAD, not\n * withheld: saying \"not given access\" for a failed read lies about the data (W1.1 verifier,\n * 2026-10-02). This is what a reference reads until a later read answers.\n */\nexport const UNREAD_RECORD_LABEL = \"Couldn't load this name\";\n\n/**\n * THE PLURAL A PERSON READS, NEVER A MACHINE'S \"โ€ฆss\" (VERIFIER-16 M3 / VERIFIER-17 M3).\n *\n * A table the store or the mover never gave a real plural carries `name + \"s\"` โ€” so a table\n * already named in the plural reads \"Customerss\", \"Status optionss\". That word was made by a\n * machine appending one letter, and it says nothing the name does not; it is never shown.\n * Returns the table's own plural when a person (or the example library) wrote one, and `null`\n * when the only plural on record is that appended letter.\n */\nexport function realPlural(table: { name?: string | null; label_singular?: string | null; label_plural?: string | null }): string | null {\n const plural = (table.label_plural ?? \"\").trim();\n if (plural === \"\") return null;\n for (const base of [table.name, table.label_singular]) {\n const b = (base ?? \"\").trim();\n if (b !== \"\" && plural === `${b}s`) return null;\n }\n return plural;\n}\n\n/**\n * ONE RECORD'S NOUN, IN A SENTENCE (\"Book a 30-minute visit\") โ€” lane HANDOVER, 2026-09-28.\n *\n * Cedar Ridge Physical Therapy's booking page was titled \"Book a 30-minute Visits appointment\":\n * the table's NAME (a plural, a heading) was put where one record's noun goes. The noun is the\n * table's own singular (\"Visit\"), else its name, else the fallback; a Title-case word is written\n * the way a sentence writes it (\"visit\"), an acronym is kept (\"MRI scan\").\n */\nexport function singularNoun(\n table: { name?: string | null; label_singular?: string | null } | null | undefined,\n fallback = \"record\",\n): string {\n const word = (table?.label_singular ?? \"\").trim() || (table?.name ?? \"\").trim() || fallback;\n return word\n .split(/\\s+/)\n .map((w) => (/^[A-Z][a-z'โ€™-]*$/.test(w) ? w.toLowerCase() : w))\n .join(\" \");\n}\n\n/**\n * A plural for a sentence (\"Check in ran on 2 appointments\"): the table's real plural, else the\n * singular with an \"s\" only when that does not double a final \"s\".\n */\nexport function pluralNoun(table: { name?: string | null; label_singular?: string | null; label_plural?: string | null } | null | undefined, fallback = \"record\"): string {\n const real = table ? realPlural(table) : null;\n if (real) return real;\n const singular = (table?.label_singular ?? \"\").trim() || fallback;\n return /s$/i.test(singular) ? singular : `${singular}s`;\n}\n\n\n// โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•\n// WHAT A CARD IS TITLED BY (lane UI-FIX-18, VERIFIER-18 H5).\n// โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•\n//\n// MEASURED 2026-09-24 on production: *ZZZ UI-QUICK-1 Rincon-style Jobs* (title column `title`)\n// grouped by Stage titled every kanban card \"false\" or \"true\". The board took the FIRST Field\n// the read door answered that was not the group column โ€” here the *Urgent* checkbox โ€” as the\n// title. A card is named by what names its record: the Table's own `title_field` first (REC-2),\n// then the conventional name keys, then the first words column in declared order. A yes/no\n// column, the column the board is grouped by and the store's own stamps never title a card.\n\nconst CONVENTIONAL_TITLE_KEYS = [\"title\", \"name\", \"label\", \"full_name\", \"subject\", \"summary\"];\n\nfunction canTitle(field: Field, excluded: ReadonlySet<string>): boolean {\n if (excluded.has(field.key)) return false;\n if (field.type === \"boolean\") return false;\n if (field.type === \"formula\" && (field.config ?? {})[\"system\"]) return false;\n return true;\n}\n\n/**\n * THE FIELD A CARD IS TITLED BY โ€” kanban, calendar, gallery and pipeline alike.\n *\n * Order: the Table's `title_field` โ†’ a conventional name key (`title`, `name`, โ€ฆ) โ†’ the first\n * text column in declared `sort` order โ†’ the first other column that can title (never a\n * checkbox, never the group column, never a system stamp). `undefined` when nothing can, and\n * the card falls back to `rowName`, which says \"Untitled\" rather than \"false\".\n */\nexport function cardTitleField(\n fields: readonly Field[],\n titleKey: string | null | undefined,\n exclude: readonly (string | null | undefined)[] = [],\n): Field | undefined {\n const excluded = new Set(exclude.filter((k): k is string => typeof k === \"string\" && k !== \"\"));\n const ordered = [...fields].sort((a, b) => (a.sort ?? 100) - (b.sort ?? 100) || a.key.localeCompare(b.key));\n const byKey = (key: string) => ordered.find((f) => f.key === key && canTitle(f, excluded));\n if (titleKey) {\n const declared = byKey(titleKey);\n if (declared) return declared;\n }\n for (const key of CONVENTIONAL_TITLE_KEYS) {\n const found = byKey(key);\n if (found) return found;\n }\n return (\n ordered.find((f) => f.type === \"text\" && canTitle(f, excluded)) ?? ordered.find((f) => canTitle(f, excluded))\n );\n}\n","// src/parity.ts โ€” @ai-matrx/records-ui\n//\n// FLD-11, the parity floor. A Field stores a BEHAVIOUR (one of five) plus\n// modifiers, unit and format; a person picked one of THIRTEEN parity types in\n// the UI. This resolves the second from the first, using the same facts the\n// store's own `custom.parity_field_types()` describes โ€” which is generated into\n// `@ai-matrx/records` and imported here, never retyped.\n//\n// Nothing in this file decides policy. It decides which editor to draw.\n\nimport { displayFormatOf } from \"./choiceLooks\";\nimport {\n KERNEL_TABLES,\n PARITY_FIELD_TYPES,\n fieldKindFor,\n newerKindOf,\n type Field,\n type FieldKind,\n type KernelTargets,\n type ParityFieldType,\n} from \"@ai-matrx/records\";\nimport { humanizeIdentifier } from \"@ai-matrx/kit/text-case\";\nimport { isSignatureField } from \"./docs\";\nimport { fieldTypeChoice, isSystemFieldKind, type PickableFieldType } from \"./fieldTypes\";\n\n/** The kernel ids come from the store's own kernel rows, never from a literal here. */\nconst kernelId = (name: string): string =>\n KERNEL_TABLES.find((t) => t.name === name)?.id ?? \"\";\nconst KERNEL_FILE_TABLE = kernelId(\"File\");\nconst KERNEL_PERSON_TABLE = kernelId(\"Person\");\n\nexport const PARITY_LABEL: Record<ParityFieldType, string> = {\n attachment: \"Attachment\",\n checkbox: \"Yes / no\",\n currency: \"Currency\",\n datetime: \"Date\",\n email: \"Email\",\n formula: \"Formula\",\n lookup: \"Lookup\",\n member: \"Person\",\n multi_select: \"Multi-select\",\n percent: \"Percent\",\n phone: \"Phone\",\n rollup: \"Rollup\",\n select: \"Select\",\n url: \"Link\",\n};\n\n/** The \"made of\" sentence the store itself publishes, for the field editor's help text. */\nexport const PARITY_MADE_OF: Record<string, string> = Object.fromEntries(\n PARITY_FIELD_TYPES.map((p) => [p.parity_type, p.made_of]),\n);\n\n/**\n * Plain text and plain number are behaviours with no parity type of their own.\n *\n * ๐Ÿšจ THIS IS NOW AN ALIAS, AND THAT IS THE POINT (TAILS-3, 2026-09-21). The\n * classifier moved into `@ai-matrx/records` as `FieldKind` so that the editor a\n * person types into and the COERCION applied to what they typed are decided by\n * one body. They were decided by two, and the public booking page paid for it:\n * \"Vehicle Year takes a number, and it was given a string\" on every table with\n * a number field.\n */\nexport type EditorKind = FieldKind;\n\n/** The kernel ids come from the store's own kernel rows, never from a literal here. */\nconst KERNEL: KernelTargets = { file: KERNEL_FILE_TABLE, person: KERNEL_PERSON_TABLE };\n\n/**\n * Which editor one Field wants. Reads the Field's own declaration only โ€” the\n * behaviour, the modifiers, the format, the relation target and the config โ€”\n * because those are what the store stores, and a screen that guessed from a\n * value would draw a different editor for an empty record.\n *\n * It is `fieldKindFor` with this package's kernel ids bound. Nothing here\n * decides anything the core does not.\n */\nexport function editorKindFor(field: Field): EditorKind {\n return fieldKindFor(field, KERNEL);\n}\n\n/**\n * WHICH KIND A PERSON PICKED, read back off the Field the store keeps โ€” the ONE reader every\n * \"what does this column hold\" answer goes through (the Settings column list, the column panel).\n *\n * `editorKindFor` answers which EDITOR to draw, and for two families that is not the kind a\n * person picked: a Signature is a text Field whose format is `signature` (it draws a line of\n * text), and the store's stamps โ€” Record number, Created time, Last changed time โ€” are formula\n * Fields whose `config.system` names the stamp. Read through the editor classifier alone, the\n * Settings list called a Signature \"Text\" and a Record number \"Worked out from other columns\"\n * (lane 10 W1-A, guard `every-pickable-kind-reads-back-as-itself.test.tsx`).\n *\n * `undefined` when the Field is none of the kinds the menu offers.\n */\nexport function pickableTypeOf(field: Field): PickableFieldType | undefined {\n if (isSignatureField(field)) return \"signature\";\n // LANE 10 P4: the nine newer kinds, each a base type wearing a format (`newerKindOf`).\n const newer = newerKindOf(field);\n if (newer) return newer;\n // A RATING OR A TIME made as a plain column wearing its look (`LOOK_KINDS`): named by that kind.\n const look = displayFormatOf(field)?.id;\n if (look === \"rating\" && field.type === \"range\") return \"rating\";\n if (look === \"time\" && field.type === \"text\") return \"time\";\n const system = (field.config as Record<string, unknown> | undefined)?.[\"system\"];\n if (field.type === \"formula\" && typeof system === \"string\" && isSystemFieldKind(system)) return system;\n const kind = editorKindFor(field);\n return fieldTypeChoice(kind) ? (kind as PickableFieldType) : undefined;\n}\n\n/**\n * The type word a person sees in a header or a settings row โ€” and it is the\n * SAME word the field panel offers, read from `FIELD_TYPE_CHOICES`, so a column\n * added as \"Money\" is never described anywhere else as \"Currency\".\n */\nexport function fieldTypeLabel(field: Field): string {\n const kind = pickableTypeOf(field) ?? editorKindFor(field);\n const known = fieldTypeChoice(kind)?.label;\n if (known) return field.multi && kind !== \"multi_select\" ? `${known} (many)` : known;\n const plain: Record<string, string> = {\n // `boolean` is the BEHAVIOUR word; `checkbox` is the type a person picks. Both are\n // written the way a person writes them, never as a raw token.\n boolean: \"Yes / no\",\n date: \"Date\",\n json: \"Structured data\",\n relation: \"Link to a record\",\n };\n // Never a raw token on a screen: an editor kind this table has not named is\n // still written the way a person writes it.\n return plain[kind] ?? humanizeIdentifier(kind);\n}\n","// src/gridLook.ts โ€” @ai-matrx/records-ui ยท lane DEFAULT-GRID-PARITY\n//\n// WHAT THE STORE ALREADY SAYS ABOUT HOW A TABLE OPENS, READ IN ONE PLACE.\n//\n// The owner, on his real table's new page (2026-09-23): \"none of my advanced features are\n// copied over.\" Each of the things below was IN THE STORE and the default grid never read\n// it (`common-docs/systems/data/custom-data/HANDOFF.md`, rows D1 D3 D4 D5 D6\n// S2 F1 R3). They are pure functions on purpose โ€” the grid is a large component and every\n// one of these answers is a question a test must be able to ask without mounting it:\n//\n// ยท the table's DEFAULT SORT (`data.default_sort`, `[{field, direction}]`), which the store\n// keeps and nothing read;\n// ยท the LAYOUT the grid opens with: the platform, then the organization's knob\n// (`custom/grid_layout`, read through `custom.grid_layout`), then THIS VIEW's own choice โ€”\n// three layers, in that order, and the view always wins;\n// ยท the SUMMARY measures a column may offer, from the store's own list\n// (`custom.agg_operations`), filtered to what the column can mean;\n// ยท a REFERENCE a person pastes into a prompt โ€” the `table` / `table_row` reference nouns.\n\nimport type { Field, GridLayout, GridLayoutMode, GridRowHeight, ReadRow, Table } from \"@ai-matrx/records\";\nimport type { MatrxDataTableCopyConfig } from \"@ai-matrx/design-system/data-table/types\";\n\nimport { fieldName } from \"./names\";\nimport { shownValue, type LabelLookup } from \"./labels\";\nimport { editorKindFor } from \"./parity\";\n\n// โ”€โ”€ the default sort โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n\nexport interface DefaultSort {\n /** The Field KEY โ€” the column id the grid builds every column with. */\n id: string;\n direction: \"asc\" | \"desc\";\n}\n\n/**\n * THE TABLE'S OWN SORT, as the grid's primitive takes it โ€” or `null`.\n *\n * The store writes `default_sort` as `[{field, direction}]` (`custom.table_declare`, the\n * mover, and this package's own `declareTable` all do), with `field` a Field KEY. The type\n * the read carries says `string | null`, and older rows hold a bare key or `\"key desc\"`, so\n * all three shapes are read. A sort that names a column this table no longer has is\n * DROPPED, never guessed at: sorting by the nearest column would be the screen inventing an\n * order nobody chose. Only the first sort key is used โ€” the grid's primitive sorts by one.\n */\nexport function defaultSortOf(\n table: Pick<Table, \"default_sort\"> | null | undefined,\n fields: readonly Field[],\n): DefaultSort | null {\n const raw = (table?.default_sort ?? null) as unknown;\n const first = Array.isArray(raw) ? raw[0] : raw;\n let name: string | null = null;\n let direction: \"asc\" | \"desc\" = \"asc\";\n if (typeof first === \"string\") {\n const trimmed = first.trim();\n if (trimmed === \"\") return null;\n if (trimmed.startsWith(\"-\")) {\n name = trimmed.slice(1).trim();\n direction = \"desc\";\n } else {\n const [head, tail] = trimmed.split(/\\s+/);\n name = head ?? null;\n if (tail && tail.toLowerCase() === \"desc\") direction = \"desc\";\n }\n } else if (first && typeof first === \"object\") {\n const entry = first as Record<string, unknown>;\n const named = entry[\"field\"] ?? entry[\"key\"] ?? entry[\"id\"];\n name = typeof named === \"string\" ? named : null;\n if (String(entry[\"direction\"] ?? \"\").toLowerCase() === \"desc\") direction = \"desc\";\n }\n if (!name) return null;\n // A Field named by its KEY (what the store writes) or, in an older row, by its id.\n const field = fields.find((f) => f.key === name) ?? fields.find((f) => f.id === name);\n return field ? { id: field.key, direction } : null;\n}\n\n// โ”€โ”€ the layout โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n\n/** A view's own layout choices โ€” the per-view override of the organization's knob. */\nexport interface ViewLook {\n /** px, the grid's existing per-view row height. */\n rowHeight?: number | undefined;\n /** The view's own frozen columns, by key (the grid's existing per-view freeze). */\n frozen?: string[] | undefined;\n wrap?: boolean | undefined;\n fit?: GridLayoutMode | undefined;\n /**\n * \"Freeze the first column\", said by THIS VIEW โ€” `false` is an explicit NO over an\n * organization whose knob freezes it, which an empty `frozen` list cannot say (an empty\n * list is how a view that never chose reads).\n */\n freezeFirst?: boolean | undefined;\n}\n\n/**\n * The store's three row heights, in the pixels the grid's primitive takes. \"normal\" is the\n * primitive's own density default, which is why it maps to nothing rather than a number\n * that would override the density a host chose.\n */\nexport const ROW_HEIGHT_PX: Record<GridRowHeight, number | undefined> = {\n compact: 28,\n normal: undefined,\n tall: 64,\n};\n\n/** The px a view remembers, read back as the store's word โ€” for the menu's check mark. */\nexport function rowHeightWord(px: number | undefined): GridRowHeight {\n if (px === undefined) return \"normal\";\n if (px <= 32) return \"compact\";\n if (px >= 52) return \"tall\";\n return \"normal\";\n}\n\nexport interface ResolvedLook {\n rowHeight: number | undefined;\n rowHeightWord: GridRowHeight;\n wrap: boolean;\n /** What the table does with its width: fit it, or keep natural widths and scroll. */\n fit: \"fit\" | \"scroll\";\n /** The choice as stored โ€” `auto` resolves to fit or scroll by the column count. */\n fitChoice: GridLayoutMode;\n frozen: string[];\n /** Where each of the four came from, so the menu can say \"your organization's\". */\n from: { rowHeight: Source; wrap: Source; fit: Source; frozen: Source };\n}\n\nexport type Source = \"view\" | \"organization\" | \"platform\";\n\nfunction storeSource(layout: GridLayout | null, key: string): Source {\n const said = layout?.source?.[key];\n return said === \"organization\" ? \"organization\" : \"platform\";\n}\n\n/**\n * THE COLUMN ORDER THE LOOK READS \"FIRST\" FROM: the Fields' own order, with the column that names\n * each record (`title_field`) leading โ€” \"freeze the first column\" freezes the record's name, as\n * Airtable's primary field is always the one that stays (merged-grid review 2, D1: the menu said\n * \"Freeze Title\" and pinned whichever column sorted first). Pure.\n */\nexport function lookColumnKeys(orderedKeys: readonly string[], titleField: string | null | undefined): string[] {\n return titleField && orderedKeys.includes(titleField)\n ? [titleField, ...orderedKeys.filter((k) => k !== titleField)]\n : [...orderedKeys];\n}\n\n/**\n * THE LOOK THE GRID OPENS WITH: the view's own choice over the organization's knob over the\n * platform. `columnKeys` is the grid's column order, because \"freeze the first column\" is a\n * statement about whichever column is first, and \"automatic\" fit is a statement about how\n * many columns there are (`fit_max_columns`).\n *\n * `layout` null means the store's answer has not arrived or was refused; the grid then opens\n * exactly as it always did โ€” the knob is an override, never the only thing that makes a\n * table readable.\n */\nexport function resolveLook(\n layout: GridLayout | null,\n view: ViewLook,\n columnKeys: readonly string[],\n): ResolvedLook {\n const stored = layout?.layout;\n const rowHeight =\n view.rowHeight !== undefined ? view.rowHeight : stored ? ROW_HEIGHT_PX[stored.row_height] : undefined;\n const wrap = view.wrap ?? stored?.wrap ?? true;\n const fitChoice: GridLayoutMode = view.fit ?? stored?.mode ?? \"auto\";\n const max = stored?.fit_max_columns ?? 8;\n const fit: \"fit\" | \"scroll\" =\n fitChoice === \"fit\" ? \"fit\" : fitChoice === \"scroll\" ? \"scroll\" : columnKeys.length <= max ? \"fit\" : \"scroll\";\n const freezeFirst = view.freezeFirst ?? stored?.freeze_first_column ?? false;\n const frozen =\n view.frozen && view.frozen.length > 0\n ? view.frozen\n : freezeFirst && columnKeys[0]\n ? [columnKeys[0]]\n : [];\n return {\n rowHeight,\n rowHeightWord: view.rowHeight !== undefined ? rowHeightWord(view.rowHeight) : (stored?.row_height ?? \"normal\"),\n wrap,\n fit,\n fitChoice,\n frozen,\n from: {\n rowHeight: view.rowHeight !== undefined ? \"view\" : storeSource(layout, \"row_height\"),\n wrap: view.wrap !== undefined ? \"view\" : storeSource(layout, \"wrap\"),\n fit: view.fit !== undefined ? \"view\" : storeSource(layout, \"mode\"),\n frozen:\n (view.frozen && view.frozen.length > 0) || view.freezeFirst !== undefined\n ? \"view\"\n : storeSource(layout, \"freeze_first_column\"),\n },\n };\n}\n\n// โ”€โ”€ the summary bar โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n\n/** What each of the store's measures is called on screen. */\nexport const MEASURE_LABEL: Record<string, string> = {\n count: \"Count\",\n sum: \"Sum\",\n avg: \"Average\",\n min: \"Smallest\",\n max: \"Largest\",\n median: \"Median\",\n filled: \"Filled\",\n empty: \"Empty\",\n unique: \"Unique\",\n percent_empty: \"Percent empty\",\n percent_filled: \"Percent filled\",\n range: \"Range\",\n};\n\n/**\n * NOTION'S CALCULATE MENU (Spaces N6) โ€” the embedded grid's footer offers Notion's list, in Notion's\n * order and words, each answered by one of the store's measures. `footer` is what the footer cell\n * says before the value (\"Sum 1,240\"). Count all counts records, so every column offers it.\n */\nexport const NOTION_CALCULATIONS: ReadonlyArray<{ op: string; menu: string; footer: string }> = [\n { op: \"count\", menu: \"Count all\", footer: \"Count\" },\n { op: \"unique\", menu: \"Count unique values\", footer: \"Unique\" },\n { op: \"empty\", menu: \"Count empty\", footer: \"Empty\" },\n { op: \"filled\", menu: \"Count not empty\", footer: \"Not empty\" },\n { op: \"percent_empty\", menu: \"Percent empty\", footer: \"Empty\" },\n { op: \"percent_filled\", menu: \"Percent not empty\", footer: \"Not empty\" },\n { op: \"sum\", menu: \"Sum\", footer: \"Sum\" },\n { op: \"avg\", menu: \"Average\", footer: \"Average\" },\n { op: \"median\", menu: \"Median\", footer: \"Median\" },\n { op: \"min\", menu: \"Min\", footer: \"Min\" },\n { op: \"max\", menu: \"Max\", footer: \"Max\" },\n { op: \"range\", menu: \"Range\", footer: \"Range\" },\n];\n\n/** Notion's calculations this column can mean, among those the store offers. */\nexport function calculationsFor(field: Field, storeOps: readonly string[]): Array<{ op: string; menu: string; footer: string }> {\n const offered = new Set(measuresFor(field, storeOps, true));\n return NOTION_CALCULATIONS.filter((c) => offered.has(c.op));\n}\n\nconst NUMERIC_ONLY = new Set([\"sum\", \"avg\", \"median\"]);\nconst ARITHMETIC = new Set([\n \"add\", \"sub\", \"mul\", \"div\", \"mod\", \"power\", \"sum\", \"average\", \"round\", \"abs\", \"min\", \"max\", \"ceiling\", \"floor\",\n]);\nconst ORDERED = new Set([\"min\", \"max\", \"range\"]);\n\n/** Is this a column whose values are numbers the store can add up? */\nexport function isNumericField(field: Field): boolean {\n const kind = editorKindFor(field);\n if (kind === \"number\" || kind === \"currency\" || kind === \"percent\" || kind === \"rollup\") return true;\n // A worked-out column is a number when its expression is arithmetic โ€” `{Visit fee} -\n // {Deposit taken}` is stored as `{\"op\":\"fx.sub\",โ€ฆ}`. A joined label is not.\n if (kind !== \"formula\") return false;\n const expr = ((field.config ?? {}) as Record<string, unknown>)[\"expr\"] as { op?: unknown } | undefined;\n const op = typeof expr?.op === \"string\" ? expr.op.replace(/^fx\\./, \"\") : \"\";\n return ARITHMETIC.has(op);\n}\n\nfunction isOrderedField(field: Field): boolean {\n return isNumericField(field) || editorKindFor(field) === \"datetime\";\n}\n\n/**\n * THE MEASURES THIS COLUMN MAY OFFER โ€” the store's own list (`custom.agg_operations`),\n * narrowed to what the column can mean. A sum of a text column is not a number the store\n * would refuse; it is a number that means nothing, which is worse, so it is never offered.\n * `count` counts records and belongs to no column, so it is offered once, on the first.\n */\nexport function measuresFor(field: Field, storeOps: readonly string[], firstColumn: boolean): string[] {\n return storeOps.filter((op) => {\n if (op === \"count\") return firstColumn;\n if (NUMERIC_ONLY.has(op)) return isNumericField(field);\n if (ORDERED.has(op)) return isOrderedField(field);\n return true;\n });\n}\n\n// โ”€โ”€ a reference for a prompt โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n\n/**\n * THE GRID'S COPY, WITH ITS REFERENCES (TABLE-PARITY R3).\n *\n * The data table's Copy already offers the rows as text and as agent JSON; what it could not\n * offer is a REFERENCE โ€” the pointer an agent expands itself (`table` and `table_row`, the\n * reference nouns the server resolves, and which follow a moved table into the record store).\n * The primitive takes references only on an explicit copy config, so this is that config: the\n * same text and JSON the primitive's own default writes (every column that reads a value, in\n * the view's order, by its own name), plus the two references. A row reference is NAMED by\n * the table's row label so it reads as a name. The host's content-transfer port decides\n * whether it can build them; where none is installed, the reference choice is simply absent.\n */\nexport function gridCopyConfig(args: {\n tableId: string;\n tableName: string;\n fields: readonly Field[];\n rowLabel: (row: ReadRow) => string;\n /**\n * WHAT A REFERENCE IS CALLED โ€” the grid's own resolver (`useRecordLabels`). Copy and Copy for\n * AI carry the words the cell shows (\"Ojeda residence\"), never the id the store keeps.\n */\n labels?: LabelLookup;\n}): MatrxDataTableCopyConfig<ReadRow> {\n const { tableId, tableName, fields, rowLabel, labels } = args;\n const shown = (row: ReadRow) => {\n const document = (row.document ?? {}) as Record<string, unknown>;\n const out: Record<string, unknown> = {};\n for (const field of fields) {\n if (document[field.key] !== undefined) out[fieldName(field)] = shownValue(field, document[field.key], labels);\n }\n return out;\n };\n const text = (value: unknown): string =>\n value === null || value === undefined ? \"\" : typeof value === \"object\" ? JSON.stringify(value) : String(value);\n return {\n label: tableName,\n listLabel: tableName,\n location: tableName,\n rowKind: \"table-row\",\n listKind: \"table-view\",\n listDescription: `${tableName} โ€” the records on this page, filtered and sorted as shown.`,\n humanRow: (row) =>\n Object.entries(shown(row))\n .map(([name, value]) => `${name}: ${text(value)}`)\n .join(\"\\n\"),\n agentRow: (row) => shown(row),\n rowReferences: (row) => [\n {\n id: `table_row:${tableId}:${row.id}`,\n label: rowLabel(row),\n noun: \"table_row\",\n items: [{ table_id: tableId, row_id: row.id, table_name: tableName, row_label: rowLabel(row) }],\n },\n ],\n references: () => [\n {\n id: `table:${tableId}`,\n label: tableName,\n noun: \"table\",\n items: [{ table_id: tableId, table_name: tableName }],\n },\n ],\n };\n}\n","// src/gridPage.ts โ€” @ai-matrx/records-ui ยท merge tranche 6k (server search, sort and paging)\n//\n// THE MERGED GRID ASKS THE STORE FOR ITS PAGE (`custom.read_records_page` through `useRecordPage`).\n//\n// The classic grid read one page and then searched, filtered and sorted THAT page in the browser โ€”\n// the readiness audit searched a 1,000-row table for a record on page six and found nothing, and a\n// sort ordered fifty rows while claiming to order the table. Here the grid's own query state (the\n// data table's search box, its column filters, its header sort, its page) is translated into the\n// store's one question and the store answers it: sorted over the whole table, searched over every\n// column this reader may see, filtered in its own WHERE clause, counted.\n//\n// These are the pure halves: the sort in the store's shape, and the column filters in the store's\n// one filter shape. A filter the store cannot ask is NEVER applied in the browser to the page it\n// happens to hold (that is the lie this tranche removes) โ€” it is refused by name, and the grid\n// says so.\n\nimport type { Field, RecordFilter, RecordPageSort, RecordsError, RuleExpression, StoreFilter } from \"@ai-matrx/records\";\nimport { isRuleFilter } from \"@ai-matrx/records\";\nimport { dateFilterBounds, dateFilterDayBounds, dayInstantWindow } from \"@ai-matrx/design-system/data-table\";\nimport { getPersonTimeZone } from \"./personTime\";\nimport type { ColumnFilterValue, ColumnFiltersState, SortState } from \"@ai-matrx/design-system/data-table/types\";\nimport { editorKindFor } from \"./parity\";\nimport { fieldName } from \"./names\";\n\n/**\n * ๐Ÿšจ WHY THE STORE CANNOT SORT BY THIS COLUMN, or `null` when it can (merged-grid review\n * 2026-09-26). A column worked out on every read (`type: \"formula\"`, `compute_on: \"read\"` โ€” the\n * default, and what a lookup or a rollup is) keeps no value in the record, so\n * `custom.read_records_page` refuses to order the whole table by it. The header offered \"Sort\n * ascending\" anyway; the refused page then emptied the grid. The screen asks this ONE question\n * before offering a sort, and says the store's own reason instead.\n */\nexport function sortRefusal(f: Field): string | null {\n if (!computedOnRead(f)) return null;\n return `${fieldName(f)} is worked out each time it is read, so the store keeps no value to sort the whole table by. Sort by a column it is worked out from.`;\n}\n\n/** The same fact for a filter: the store's WHERE clause reads kept values, and this column keeps none. */\nexport function filterRefusal(f: Field): string | null {\n if (!computedOnRead(f)) return null;\n return `${fieldName(f)} is worked out each time it is read, so the store keeps no value to filter the whole table by. Filter by a column it is worked out from.`;\n}\n\nfunction computedOnRead(f: Field): boolean {\n const kind = String(editorKindFor(f));\n return (\n (f.type === \"formula\" || kind === \"formula\" || kind === \"lookup\" || kind === \"rollup\") &&\n String((f as { compute_on?: unknown }).compute_on ?? \"read\") === \"read\"\n );\n}\n\n/**\n * Does this column hold NUMBERS the store keeps โ€” a number, money or percent column, or the record\n * number (`config.system: autonumber`, worked out once on save and kept)?\n */\nexport function holdsNumbers(field: Field): boolean {\n const kind = String(editorKindFor(field));\n if (kind === \"number\" || kind === \"currency\" || kind === \"percent\") return true;\n return String(((field.config ?? {}) as Record<string, unknown>)[\"system\"] ?? \"\") === \"autonumber\";\n}\n\n/** How the store should compare a column's values when it sorts by it. */\nexport function sortAsFor(field: Field): NonNullable<RecordPageSort[\"as\"]> {\n const kind = String(editorKindFor(field));\n // The record number sorts 1, 2 โ€ฆ 10, never \"10\" before \"9\".\n if (holdsNumbers(field)) return \"number\";\n if (kind === \"datetime\") return \"date\";\n return \"text\";\n}\n\n/** The grid's header sort, in the store's page-order shape. A sort on a column that is gone is no sort. */\nexport function pageSortFor(fields: readonly Field[], sort: SortState | null | undefined): RecordPageSort[] {\n if (!sort) return [];\n const field = fields.find((f) => f.key === sort.id);\n if (!field) return [];\n return [{ field: field.key, direction: sort.direction === \"desc\" ? \"desc\" : \"asc\", as: sortAsFor(field) }];\n}\n\n/** A text a person typed, as a case-insensitive \"contains\" pattern (the store's `matches` is a regex). */\nexport function containsPattern(text: string): string {\n return `(?i)${text.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\")}`;\n}\n\nexport interface StoreQuestion {\n /** The whole question: the view's own filter AND every column filter the store can ask. */\n filter: StoreFilter | undefined;\n /** Column filters the store cannot ask, each as a sentence naming the column. Never applied locally. */\n refused: string[];\n}\n\ntype Clause = { flat: RecordFilter } | { rule: RuleExpression };\n\n/**\n * THE DATA TABLE'S \"(empty)\" TICK, as it writes it into a select filter's `values`\n * (`SELECT_EMPTY_VALUE` in the design system's filter engine). Asked of the store as \"has not been\n * answered\". `a-text-column-filters-by-its-values-counted-by-the-store.test.tsx` ticks the real\n * checklist, so this word drifting from the table's is a red test, not a silent no-op.\n */\nexport const BLANK_VALUE = \"__empty__\";\n\nconst field = (f: Field): RuleExpression => ({ field: f.id });\nconst constant = (value: unknown): RuleExpression => ({ const: value });\n/** A column's moment is in the half-open window `{from, to}` โ€” the store's `within` node. */\nconst withinWindow = (f: Field, window: { from?: string; to?: string }): RuleExpression =>\n ({ op: \"within\", args: [field(f), constant(window)] }) as unknown as RuleExpression;\nconst op = (name: string, ...args: RuleExpression[]): RuleExpression =>\n ({ op: name, args }) as unknown as RuleExpression;\n\n/** One column filter, as a store clause โ€” or the sentence saying why the store cannot ask it. */\nexport function clauseFor(f: Field, value: ColumnFilterValue, now: Date = new Date()): Clause | string | null {\n const name = fieldName(f);\n const refused = filterRefusal(f);\n if (refused) return refused;\n const not = (rule: RuleExpression): RuleExpression => op(\"not\", rule);\n switch (value.kind) {\n case \"text\": {\n const mode = value.mode ?? \"contains\";\n if (mode === \"empty\" || mode === \"null\") return value.negated ? { rule: op(\"present\", field(f)) } : { rule: not(op(\"present\", field(f))) };\n if (mode === \"not_empty\" || mode === \"not_null\") return value.negated ? { rule: not(op(\"present\", field(f))) } : { rule: op(\"present\", field(f)) };\n const text = value.value.trim();\n if (text === \"\") return null;\n const kind = String(editorKindFor(f));\n if (kind === \"number\" || kind === \"currency\" || kind === \"percent\" || kind === \"checkbox\") {\n return `${name} holds numbers, so it is filtered by a range, not by words.`;\n }\n const matches = op(\"matches\", field(f), constant(containsPattern(text)));\n return { rule: value.negated ? not(matches) : matches };\n }\n case \"select\": {\n // \"Is any of\" the ticked values; \"(empty)\" ticked (`BLANK_VALUE`) is \"has not been answered\";\n // Exclude is everything else โ€” a blank row is not one of the ticked values, so Exclude keeps it\n // unless \"(empty)\" is ticked too (the Sheet's \"Exclude these instead\", slice C).\n const ticked = value.values ?? (value.value === \"\" ? [] : [value.value]);\n const blank = ticked.includes(BLANK_VALUE);\n const words = ticked.filter((w) => w !== BLANK_VALUE);\n if (!blank && words.length === 0) return value.negated ? null : { rule: constant(false) };\n const present = op(\"present\", field(f));\n const kind = String(editorKindFor(f));\n if (kind === \"datetime\" || kind === \"date\") {\n // TICKED DAYS: each is a half-open window, asked as the store's one `within` leaf. One day\n // alone stays a flat window (what a dashboard number asks); several days are an ANY of\n // them, a day beside \"(empty)\" adds the unanswered, and Exclude is NOT โ€” the same shapes\n // every other value list makes, kept by a saved view in the one filter grammar.\n if (words.length === 0) return { rule: value.negated ? present : not(present) };\n const holdsDays = String(((f.config ?? {}) as Record<string, unknown>)[\"kind\"] ?? \"\") === \"date\";\n if (words.length === 1 && !blank && !value.negated) return { flat: { [f.key]: dayWindow(words[0]!, holdsDays) } };\n const leaves = words.map((day) => withinWindow(f, dayWindow(day, holdsDays)));\n const anyDay = leaves.length === 1 ? leaves[0]! : op(\"or\", ...leaves);\n if (!value.negated) return { rule: blank ? op(\"or\", not(present), anyDay) : anyDay };\n return { rule: blank ? op(\"and\", present, not(anyDay)) : not(anyDay) };\n }\n // The list's words are text; a number or yes/no column is asked in its own kind.\n const asStored = (w: string): unknown =>\n holdsNumbers(f) || kind === \"rating\"\n ? Number.isFinite(Number(w)) ? Number(w) : w\n : kind === \"checkbox\" && (w === \"true\" || w === \"false\") ? w === \"true\" : w;\n const stored = words.map(asStored);\n const anyWord =\n stored.length === 0 ? null : stored.length === 1 ? op(\"eq\", field(f), constant(stored[0])) : op(\"or\", ...stored.map((w) => op(\"eq\", field(f), constant(w))));\n if (!value.negated) {\n if (!blank && stored.length === 1) return { flat: { [f.key]: stored[0] as string | number | boolean } };\n if (!blank) return { rule: anyWord! };\n return { rule: anyWord ? op(\"or\", not(present), anyWord) : not(present) };\n }\n if (blank) return { rule: anyWord ? op(\"and\", present, not(anyWord)) : present };\n return { rule: op(\"or\", not(present), not(anyWord!)) };\n }\n case \"boolean\":\n return value.negated ? { rule: op(\"ne\", field(f), constant(value.value)) } : { flat: { [f.key]: value.value } };\n case \"number\": {\n // The number filter's operators (lane C): =, โ‰ , <, >, empty, not empty; \"between\" below.\n // The header's Exclude (slice C) turns every one of them round, never only \"between\".\n const turned = (rule: RuleExpression): Clause => ({ rule: value.negated ? not(rule) : rule });\n if (value.op === \"empty\") return turned(not(op(\"present\", field(f))));\n if (value.op === \"not_empty\") return turned(op(\"present\", field(f)));\n if (value.op === \"eq\" || value.op === \"ne\") {\n if (typeof value.min !== \"number\") return null;\n return turned(op(value.op, field(f), constant(value.min)));\n }\n if (value.op === \"lt\") return typeof value.max === \"number\" ? turned(op(\"lt\", field(f), constant(value.max))) : null;\n if (value.op === \"gt\") return typeof value.min === \"number\" ? turned(op(\"gt\", field(f), constant(value.min))) : null;\n const parts: RuleExpression[] = [];\n if (typeof value.min === \"number\") parts.push(op(\"gte\", field(f), constant(value.min)));\n if (typeof value.max === \"number\") parts.push(op(\"lte\", field(f), constant(value.max)));\n if (parts.length === 0) return null;\n const range = parts.length === 1 ? parts[0]! : op(\"and\", ...parts);\n return { rule: value.negated ? not(range) : range };\n }\n case \"date\": {\n // A relative preset (\"Today\", \"Last hour\") is worked out at the moment the question is asked,\n // in the viewer's own clock, and sent as exact bounds (grids review 3, lane H item 3). A saved\n // view's \"Today\" is today when it is opened โ€” never the day somebody first picked it. A\n // column that holds a DAY is asked in days, never compared with an instant.\n // The window is read in the PERSON'S zone (setPersonTimeZone), so \"Today\" here is what the calendar calls today.\n const zone = getPersonTimeZone() ?? undefined;\n const moments = dateFilterBounds(value.preset ? { ...value, anchor: now.toISOString() } : value, now, zone);\n const holdsDays = String(((f.config ?? {}) as Record<string, unknown>)[\"kind\"] ?? \"\") === \"date\";\n const bounds = holdsDays ? dateFilterDayBounds(moments, zone) : moments;\n if (bounds.since === undefined && bounds.until === undefined) return null;\n return {\n flat: {\n [f.key]: {\n ...(bounds.since === undefined ? {} : { from: bounds.since }),\n ...(bounds.until === undefined ? {} : { to: bounds.until }),\n },\n },\n };\n }\n default:\n return `${name} cannot be filtered that way by the store.`;\n }\n}\n\n/**\n * ONE DAY AS A HALF-OPEN WINDOW. A column that holds DAYS is asked in days (\"2026-09-01\" up to\n * \"2026-09-02\"); a column of moments is asked from the viewer's own midnight to the next.\n */\nexport function dayWindow(day: string, holdsDays: boolean): { from: string; to: string } {\n if (!holdsDays) return dayInstantWindow(day, getPersonTimeZone() ?? undefined);\n const [y, m, d] = day.split(\"-\").map(Number);\n const ymd = (t: Date) => `${t.getUTCFullYear()}-${String(t.getUTCMonth() + 1).padStart(2, \"0\")}-${String(t.getUTCDate()).padStart(2, \"0\")}`;\n return { from: ymd(new Date(Date.UTC(y!, (m ?? 1) - 1, d ?? 1))), to: ymd(new Date(Date.UTC(y!, (m ?? 1) - 1, (d ?? 1) + 1))) };\n}\n\n/**\n * THE VIEW'S QUESTION AND THE COLUMN FILTERS, AS ONE STORE QUESTION.\n *\n * Flat clauses (an equality, `null`, a date window) stay flat when nothing else needs a Rule, so a\n * plain \"Status is Scheduled\" is the same question a dashboard number asks. When any clause needs a\n * Rule (words that contain, a range, several choices, NOT), the flat equalities become `eq` nodes of\n * one ALL group. A date window becomes a `within` leaf of that group.\n */\nexport function storeQuestion(\n fields: readonly Field[],\n base: StoreFilter | undefined,\n columnFilters: ColumnFiltersState,\n now: Date = new Date(),\n): StoreQuestion {\n const refused: string[] = [];\n const flat: RecordFilter = {};\n const rules: RuleExpression[] = [];\n if (base && isRuleFilter(base)) rules.push(base);\n else if (base) Object.assign(flat, base);\n for (const [key, value] of Object.entries(columnFilters)) {\n if (!value) continue;\n const f = fields.find((candidate) => candidate.key === key);\n if (!f) continue;\n const clause = clauseFor(f, value, now);\n if (clause === null) continue;\n if (typeof clause === \"string\") {\n refused.push(clause);\n continue;\n }\n if (\"flat\" in clause) Object.assign(flat, clause.flat);\n else rules.push(clause.rule);\n }\n const flatKeys = Object.keys(flat);\n if (rules.length === 0) return { filter: flatKeys.length > 0 ? flat : undefined, refused };\n for (const key of flatKeys) {\n const f = fields.find((candidate) => candidate.key === key);\n const value = flat[key];\n if (!f) continue;\n if (value !== null && typeof value === \"object\") {\n rules.push(withinWindow(f, value as { from?: string; to?: string }));\n continue;\n }\n rules.push(value === null ? op(\"not\", op(\"present\", field(f))) : op(\"eq\", field(f), constant(value)));\n }\n return { filter: rules.length === 1 ? rules[0]! : op(\"and\", ...rules), refused };\n}\n\n/**\n * ๐Ÿšจ THE STORE'S SENTENCE, WITH ITS COLUMNS CALLED BY THEIR NAMES (merged-grid review 2026-09-26).\n * A refused page read names the column by its KEY (\"The column \\\"visit_total\\\" is worked out each\n * time it is readโ€ฆ\"), and the refusal formatter โ€” rightly โ€” drops a sentence carrying a machine\n * key, so the grid printed only \"That was not accepted\" and \"Nothing was read.\": a red fragment.\n * The key is the grid's to translate: every Field key the sentence names becomes that column's\n * name, and the store's sentence is then said whole.\n */\nexport function inColumnWords(error: RecordsError, fields: readonly Field[]): RecordsError {\n const say = (text: string | null | undefined): string | null | undefined => {\n if (!text) return text;\n let out = text;\n for (const f of [...fields].sort((a, b) => b.key.length - a.key.length)) {\n const key = f.key.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n out = out.replace(new RegExp(`([\"โ€œ'])${key}([\"โ€'])`, \"g\"), `$1${fieldName(f)}$2`);\n }\n return out;\n };\n return { ...error, message: say(error.message) ?? error.message, ...(error.hint ? { hint: say(error.hint) ?? error.hint } : {}) };\n}\n","// src/rowVersion.ts โ€” @ai-matrx/records-ui ยท pure (server-safe)\nimport type { ReadRow } from \"@ai-matrx/records\";\n\n/** The version a row carries itself, when the host's read gave it one. */\nexport function ownVersion(row: ReadRow): number | null {\n const v = (row as ReadRow & { version?: unknown }).version;\n return typeof v === \"number\" ? v : null;\n}\n","// src/groupReads.ts โ€” @ai-matrx/records-ui ยท lane SEED-ENTITY\n//\n// WHAT A GROUPED GRID ASKS, AS PURE FUNCTIONS โ€” shared by the grid (which asks them) and the server's\n// first-page seed (which asks them first, with the same arguments, so the browser's call finds its\n// answer). Server-safe: no React.\n\nimport type { AggregateMeasure, AggregateOperation, Field, RecordPageSort } from \"@ai-matrx/records\";\n\nimport type { GridGrouping } from \"./presentation\";\nimport { MEASURE_LABEL } from \"./gridLook\";\nimport { sortAsFor } from \"./gridPage\";\nimport { editorKindFor } from \"./parity\";\n\n/**\n * A grouping the merged grid keeps as ROWS under group headers: one level, no pivot, no drill trail.\n * Anything else opens the drill (`useGridDrillQuestion`), which reads other doors.\n */\nexport function isPlainGrouping(grouping: GridGrouping | undefined): grouping is GridGrouping {\n return Boolean(grouping) && !(grouping!.then && grouping!.then.length > 0) && !grouping!.across && !(grouping!.trail && grouping!.trail.length > 0);\n}\n\n/** The page is asked in group order first, then in the sort the person chose. */\nexport function groupedPageSort(fields: readonly Field[], groupKey: string | null, sort: RecordPageSort[]): RecordPageSort[] {\n if (!groupKey) return sort;\n const field = fields.find((f) => f.key === groupKey);\n if (!field) return sort;\n return [{ field: groupKey, direction: \"asc\", as: sortAsFor(field) }, ...sort.filter((s) => s.field !== groupKey)];\n}\n\n/** The chosen summaries as the store's measures, in the order the grid sends them (sorted, unknown ops left out). */\nfunction chosenEntries(chosen: Record<string, string>): Array<[string, string]> {\n return (JSON.parse(JSON.stringify(Object.entries(chosen).sort())) as Array<[string, string]>).filter(([, op]) => op in MEASURE_LABEL);\n}\n\n/** Every group's count and each chosen summary: `record_aggregate`'s measures when grouped by the grouping column. */\nexport function groupFactMeasures(chosen: Record<string, string>): AggregateMeasure[] {\n return [{ op: \"count\" }, ...chosenEntries(chosen).filter(([, op]) => op !== \"count\").map(([key, op]) => ({ op: op as AggregateOperation, key }))];\n}\n\n/** The summary bar's measures over the whole table: none when nothing is chosen. */\nexport function summaryBarMeasures(chosen: Record<string, string>): AggregateMeasure[] {\n return chosenEntries(chosen).map(([key, op]) => (op === \"count\" ? { op: \"count\" as const } : { op: op as AggregateOperation, key }));\n}\n\n/** The keys of the choice columns, in the table's field order: each has a header filter list with counts. */\nexport function choiceFieldKeys(fields: readonly Field[]): string[] {\n return fields\n .filter((f) => {\n const kind = String(editorKindFor(f));\n return kind === \"select\" || kind === \"multi_select\";\n })\n .map((f) => f.key);\n}\n\n/** How many links a grid cell draws of a reverse column (the chips; the rest is \"+N\"). */\nexport const REVERSE_CELL_CHIPS = 3;\n","/**\n * HOW A TABLE LOOKS โ€” colors, formats and the frozen column.\n *\n * THE SPLIT, AND WHY IT IS THIS ONE. A Field declares WHAT it holds: a\n * currency, a percent, a date, a person. A VIEW declares HOW that reads and\n * what it is worth looking at: two decimal places or none, tint the row when\n * Status is Blocked, keep Name in place while the rest scrolls. Airtable draws\n * the line in exactly this place, and it has to be here: two people read one\n * table two different ways, and neither reading is a fact about the records.\n *\n * So nothing in this module is a new column on a Field and nothing here needed\n * a migration. A presentation lives on the saved-view record (`views.ts`), which\n * is an ordinary record of an ordinary Table โ€” which means an agent sets it\n * through the same `records` tool it uses for everything else, and it travels\n * with the table the way every other record does.\n *\n * A FIELD STILL GETS ITS MONEY RIGHT WITH NO CONFIGURATION. `formatForField`\n * reads the Field's OWN declaration โ€” a `currency` parity type with unit `$`, a\n * `percent`, a `datetime` with `config.kind` โ€” and returns the format that\n * follows from it. The view's `formats` map only OVERRIDES that. A table nobody\n * has ever styled still shows `$1,250.50` and `45%`.\n */\n\n// The style parser from its own client-free subpath (lane SSR-ROWS): `@ai-matrx/records-ui/first-page`\n// runs in a host's SERVER component, and every export of the \"use client\" `data-table` entry is a\n// client reference there โ€” calling `parseTableStyle` through it threw on the server.\nimport { parseTableStyle, tableStyleIsEmpty } from \"@ai-matrx/design-system/data-table/table-style\";\nimport {\n COLOR_ROTATION,\n type ChoiceColorLookup,\n type GroupAggregateKind,\n type GroupAggregateSpec,\n type GroupOrder,\n type StyleColor,\n type TableStyle,\n} from \"@ai-matrx/design-system/data-table\";\nimport { getFieldFormat, type FieldFormatConfig } from \"@ai-matrx/design-system/field-formats\";\nimport type { ColorRule } from \"@ai-matrx/design-system/data-table\";\nimport { newerKindOf, ratingMaxOf, type Field, type TableDecorations } from \"@ai-matrx/records\";\n\nimport { editorKindFor } from \"./parity\";\nimport { dateFormatFor } from \"./dates\";\n\n/**\n * SECTIONS BY A FIELD โ€” the view's own grouping.\n *\n * `field` is a Field KEY, exactly like `frozen` and the keys of `formats`,\n * because a view is written against the Table's declaration and never against\n * whatever id a screen happened to build its columns with.\n *\n * `aggregates` is keyed by Field key too, and `collapsed` holds the data\n * table's own group keys (`s:Blocked`, `n:3`, `empty`), which is what the\n * primitive emits and the only thing that survives a value rendering the same\n * text as another.\n *\n * NOTHING HERE IS EVER CALLED `valueOf`, `toString` or `hasOwnProperty`: every\n * object in JavaScript already has all three, so an optional field by one of\n * those names is NEVER absent and a `config.valueOf ? โ€ฆ : โ€ฆ` check silently\n * reads `Object.prototype`. It cost the primitive a whole grouping once.\n */\nexport type GridGrouping = {\n /** The Field key whose value makes the sections. */\n field: string;\n /** Per-Field-key subtotal on every section header. */\n aggregates?: Record<string, GroupAggregateSpec>;\n /** Section order. The primitive's default is `value-asc`; blanks sort last. */\n order?: GroupOrder;\n /** The sections this view keeps shut, by the primitive's own group key. */\n collapsed?: string[];\n /** The grouped column drawn as a column too. Absent = it steps aside (the headers name its value). */\n showColumn?: boolean;\n /**\n * SUB-GROUPS (lane S1-PRIME VIEW-KEYS; ui-bench LISTS \"sub-grouping\"): up to two more levels\n * inside each section, by Field key. At most three levels in all โ€” the store refuses a fourth\n * (`custom.view_declare`). A layout that draws one level ignores the rest; it never drops them,\n * because this list is parsed and written back whole with the rest of the look.\n */\n then?: GridSubGroup[];\n /**\n * DRILL (lane DRILL-TABLE-PRIMITIVE): the Dimension pivoted across the top โ€” a Field key, or\n * `<key>:<grain>` for a date cut into periods.\n */\n across?: string;\n /** DRILL: the period `field` is cut into when it is a date (`month`); the store keeps `field` a Field key. */\n grain?: string;\n /** The Measures shown (\"what to display\"), by the store's own keys (`count`, `sum_quantity`). */\n show?: string[];\n /** The drill trail, in order: each crumb a Dimension reference and the value it narrowed to. */\n trail?: GridDrillStep[];\n};\n\n/** One crumb of a saved drill trail. `value: null` is \"no value\". */\nexport type GridDrillStep = { dim: string; value: string | null };\n\n/** One sub-group level: a Field key and its section order. */\nexport type GridSubGroup = { field: string; order?: GroupOrder; grain?: string };\n\n/** Everything a view says about how its table LOOKS. */\nexport type GridPresentation = {\n /** Color-by-a-column, rules and manual highlights. */\n style?: TableStyle;\n /** Per-column format overrides, by the Field's key. */\n formats?: Record<string, FieldFormatConfig>;\n /**\n * The columns that stay in place while the table scrolls sideways, by Field\n * key. Only the LEADING run of the view's column order can actually freeze โ€”\n * the data table decides that, and declines rather than pinning a column at a\n * guessed offset.\n */\n frozen?: string[];\n /** Collapsible sections by one Field, with subtotals on each header. */\n grouping?: GridGrouping;\n /**\n * The width a person dragged a column to, by Field key. SPARSE on purpose โ€”\n * a view saved before widths existed carries none and still parses, and a\n * column nobody resized is left to the table's own measurement.\n */\n widths?: Record<string, number>;\n /** The body row height this view remembers, in px. */\n rowHeight?: number;\n /**\n * Field keys hidden from this view โ€” the SAME hidden-column list the grid's\n * own column picker holds, now given a place on the view so it survives a\n * reload and a layout switch reads the identical set gallery and grid agree\n * on, rather than a second \"fields shown\" list nobody kept in sync.\n */\n hiddenFields?: string[];\n /**\n * THE FIELDS A CARD LEAVES OFF (board, list, gallery, calendar, timeline), by Field key โ€” the\n * card's own choice, kept apart from `hiddenFields` so a board's \"Card fields\" never hides the\n * grid's columns when the view switches layout (v7 TABLE-EXPERIENCE, walk 2026-10-05). An empty\n * list is a choice (every field shown); absent means the card never chose.\n */\n cardHidden?: string[];\n /**\n * THE ORDER A PERSON DRAGGED THE COLUMNS INTO, by Field key (merge tranche 6a). Sparse like\n * `widths`: a key it does not name keeps its Field's own place after the named ones, and a key\n * whose Field is gone is skipped โ€” a column order can never hide or invent a column.\n */\n columnOrder?: string[];\n /**\n * The gallery's card size, remembered the same way the grid remembers its\n * row height โ€” a per-view look, not a global default.\n */\n gallerySize?: \"small\" | \"medium\" | \"large\";\n /**\n * THIS VIEW'S OWN LAYOUT CHOICES, over the organization's `custom/grid_layout` knob (lane\n * DEFAULT-GRID-PARITY, TABLE-PARITY D1 D5 D6). Absent means \"whatever the organization\n * says\"; present is the view's word. `freezeFirst: false` is an explicit NO over an\n * organization that freezes the first column โ€” an empty `frozen` cannot say that.\n */\n wrap?: boolean;\n /** NOTION'S \"WRAP COLUMN\" (N5): the columns that wrap their text when the view as a whole does not. */\n wrapColumns?: string[];\n fit?: \"auto\" | \"fit\" | \"scroll\";\n freezeFirst?: boolean;\n /**\n * THE SUMMARY BAR (TABLE-PARITY F1): Field key โ†’ the store measure shown under that column\n * (`count`, `sum`, `avg`, `min`, `max`, `median`, `filled`, `empty`, `unique`), worked out\n * by `custom.record_aggregate` over the whole table.\n */\n summaries?: Record<string, string>;\n /**\n * WHERE THE FOOTER SITS (lane TABLE-PAGE-CHROME, owner 2026-09-25) โ€” the row count and the\n * pages: `sticky`, pinned to the bottom of the screen while the rows scroll (the older grid's\n * way, and the default when a view says nothing), or `inline`, right after the last row with\n * the page scrolling. Read by the Grid and a host's Sheet.\n */\n footer?: FooterPlacement;\n};\n\n/** Where a grid's footer sits. */\nexport type FooterPlacement = \"sticky\" | \"inline\";\n\n/** What a view that says nothing gets โ€” the older grid's pinned footer. */\nexport const DEFAULT_FOOTER: FooterPlacement = \"sticky\";\n\n/** Where THIS view's footer sits, said or defaulted. */\nexport function footerOf(p: GridPresentation | null | undefined): FooterPlacement {\n return p?.footer ?? DEFAULT_FOOTER;\n}\n\nexport const EMPTY_PRESENTATION: GridPresentation = {};\n\n/** A frozen column needs a known width, or the table declines to freeze at all. */\nexport const FROZEN_COLUMN_WIDTH = 200;\n\n/**\n * WHAT A ROW HEIGHT IS ALLOWED TO BE. Below the floor the text is clipped and\n * no editor fits; above the ceiling one record fills the screen. A view asking\n * for anything outside it is DROPPED rather than obeyed, because a saved look\n * must not be able to make a table unreadable โ€” and the Fields' own heights\n * still draw, which is the tolerant half.\n */\nexport const MIN_ROW_HEIGHT = 24;\nexport const MAX_ROW_HEIGHT = 96;\n\n/**\n * The aggregate kinds and orders the primitive actually implements. Listed here\n * because they are the tolerant parser's allow-list and a view is hostile input:\n * `satisfies` ties them to the primitive's own union, so a kind it renames stops\n * this file compiling instead of quietly parsing into a subtotal nobody draws.\n */\nconst AGGREGATE_KINDS = [\n \"count\",\n \"sum\",\n \"average\",\n \"min\",\n \"max\",\n \"empty\",\n \"filled\",\n \"unique\",\n \"checked\",\n \"unchecked\",\n \"percentFilled\",\n] as const satisfies readonly GroupAggregateKind[];\n\nconst GROUP_ORDERS = [\n \"value-asc\",\n \"value-desc\",\n \"count-desc\",\n \"first-seen\",\n] as const satisfies readonly GroupOrder[];\n\nfunction isRecord(v: unknown): v is Record<string, unknown> {\n return typeof v === \"object\" && v !== null && !Array.isArray(v);\n}\n\n/**\n * Read a presentation out of whatever the view record holds โ€” an object, or the\n * JSON string a text field stores.\n *\n * Tolerant in exactly the way `parseTableStyle` is: an unreadable half is\n * dropped and the rest still applies, because a table must keep drawing when\n * one piece of its styling goes out of date.\n */\nexport function parseGridPresentation(raw: unknown): GridPresentation {\n let value = raw;\n if (typeof value === \"string\") {\n const trimmed = value.trim();\n if (trimmed === \"\") return {};\n try {\n value = JSON.parse(trimmed);\n } catch {\n return {};\n }\n }\n if (!isRecord(value)) return {};\n\n const out: GridPresentation = {};\n\n const style = parseTableStyle(value[\"style\"]);\n if (!tableStyleIsEmpty(style)) out.style = style;\n\n if (isRecord(value[\"formats\"])) {\n const formats: Record<string, FieldFormatConfig> = {};\n for (const [key, cfg] of Object.entries(value[\"formats\"])) {\n if (!isRecord(cfg)) continue;\n const id = cfg[\"id\"];\n if (typeof id !== \"string\") continue;\n const options = cfg[\"options\"];\n formats[key] = isRecord(options)\n ? {\n id: id as FieldFormatConfig[\"id\"],\n options: options as NonNullable<FieldFormatConfig[\"options\"]>,\n }\n : { id: id as FieldFormatConfig[\"id\"] };\n }\n if (Object.keys(formats).length > 0) out.formats = formats;\n }\n\n if (Array.isArray(value[\"frozen\"])) {\n const frozen = value[\"frozen\"].filter((k): k is string => typeof k === \"string\");\n if (frozen.length > 0) out.frozen = frozen;\n }\n\n const grouping = parseGrouping(value[\"grouping\"]);\n if (grouping) out.grouping = grouping;\n\n if (isRecord(value[\"widths\"])) {\n const widths: Record<string, number> = {};\n for (const [key, raw] of Object.entries(value[\"widths\"])) {\n // A width that is not a usable number is one column's loss, never the\n // view's: the rest of the widths still apply and that column is measured.\n if (typeof raw !== \"number\" || !Number.isFinite(raw) || raw <= 0) continue;\n widths[key] = Math.round(raw);\n }\n if (Object.keys(widths).length > 0) out.widths = widths;\n }\n\n const rowHeight = value[\"rowHeight\"];\n if (\n typeof rowHeight === \"number\" &&\n Number.isFinite(rowHeight) &&\n rowHeight >= MIN_ROW_HEIGHT &&\n rowHeight <= MAX_ROW_HEIGHT\n ) {\n out.rowHeight = Math.round(rowHeight);\n }\n\n if (Array.isArray(value[\"hiddenFields\"])) {\n const hiddenFields = value[\"hiddenFields\"].filter((k): k is string => typeof k === \"string\");\n if (hiddenFields.length > 0) out.hiddenFields = hiddenFields;\n }\n if (Array.isArray(value[\"cardHidden\"])) {\n out.cardHidden = value[\"cardHidden\"].filter((k): k is string => typeof k === \"string\");\n }\n\n if (Array.isArray(value[\"columnOrder\"])) {\n const columnOrder = value[\"columnOrder\"].filter((k): k is string => typeof k === \"string\");\n if (columnOrder.length > 0) out.columnOrder = columnOrder;\n }\n\n const gallerySize = value[\"gallerySize\"];\n if (gallerySize === \"small\" || gallerySize === \"medium\" || gallerySize === \"large\") {\n out.gallerySize = gallerySize;\n }\n\n if (typeof value[\"wrap\"] === \"boolean\") out.wrap = value[\"wrap\"];\n if (Array.isArray(value[\"wrapColumns\"])) {\n const wrapColumns = value[\"wrapColumns\"].filter((k): k is string => typeof k === \"string\");\n if (wrapColumns.length > 0) out.wrapColumns = wrapColumns;\n }\n const fit = value[\"fit\"];\n if (fit === \"auto\" || fit === \"fit\" || fit === \"scroll\") out.fit = fit;\n if (typeof value[\"freezeFirst\"] === \"boolean\") out.freezeFirst = value[\"freezeFirst\"];\n const footer = value[\"footer\"];\n if (footer === \"sticky\" || footer === \"inline\") out.footer = footer;\n if (isRecord(value[\"summaries\"])) {\n const summaries: Record<string, string> = {};\n for (const [key, op] of Object.entries(value[\"summaries\"])) {\n // A measure name is judged by the store when it is asked; a non-string is nobody's.\n if (typeof op === \"string\" && op.trim() !== \"\") summaries[key] = op;\n }\n if (Object.keys(summaries).length > 0) out.summaries = summaries;\n }\n\n return out;\n}\n\n/**\n * A grouping, or nothing. A grouping with no Field to group BY is not a partial\n * grouping โ€” it is no grouping at all, so it is dropped whole; an unreadable\n * subtotal or a section order this primitive does not have costs only itself.\n */\nfunction parseGrouping(raw: unknown): GridGrouping | undefined {\n if (!isRecord(raw)) return undefined;\n const field = raw[\"field\"];\n if (typeof field !== \"string\" || field.trim() === \"\") return undefined;\n\n const out: GridGrouping = { field };\n\n if (isRecord(raw[\"aggregates\"])) {\n const aggregates: Record<string, GroupAggregateSpec> = {};\n for (const [key, spec] of Object.entries(raw[\"aggregates\"])) {\n if (!isRecord(spec)) continue;\n const kind = spec[\"kind\"];\n if (typeof kind !== \"string\") continue;\n if (!(AGGREGATE_KINDS as readonly string[]).includes(kind)) continue;\n const label = spec[\"label\"];\n aggregates[key] =\n typeof label === \"string\"\n ? { kind: kind as GroupAggregateKind, label }\n : { kind: kind as GroupAggregateKind };\n }\n if (Object.keys(aggregates).length > 0) out.aggregates = aggregates;\n }\n\n const order = raw[\"order\"];\n if (typeof order === \"string\" && (GROUP_ORDERS as readonly string[]).includes(order)) {\n out.order = order as GroupOrder;\n }\n\n if (Array.isArray(raw[\"collapsed\"])) {\n const collapsed = raw[\"collapsed\"].filter((k): k is string => typeof k === \"string\");\n if (collapsed.length > 0) out.collapsed = collapsed;\n }\n\n if (raw[\"showColumn\"] === true) out.showColumn = true;\n\n if (Array.isArray(raw[\"then\"])) {\n const then: GridSubGroup[] = [];\n for (const level of raw[\"then\"]) {\n if (!isRecord(level)) continue;\n const sub = level[\"field\"];\n if (typeof sub !== \"string\" || sub.trim() === \"\") continue;\n const subOrder = level[\"order\"];\n const subGrain = level[\"grain\"];\n then.push({\n field: sub,\n ...(typeof subOrder === \"string\" && (GROUP_ORDERS as readonly string[]).includes(subOrder)\n ? { order: subOrder as GroupOrder }\n : {}),\n ...(typeof subGrain === \"string\" && subGrain !== \"\" ? { grain: subGrain } : {}),\n });\n }\n if (then.length > 0) out.then = then.slice(0, 2);\n }\n\n const grain = raw[\"grain\"];\n if (typeof grain === \"string\" && grain !== \"\") out.grain = grain;\n const across = raw[\"across\"];\n if (typeof across === \"string\" && across.trim() !== \"\") out.across = across;\n if (Array.isArray(raw[\"show\"])) {\n const show = raw[\"show\"].filter((k): k is string => typeof k === \"string\" && k !== \"\");\n if (show.length > 0) out.show = show;\n }\n if (Array.isArray(raw[\"trail\"])) {\n const trail: GridDrillStep[] = [];\n for (const step of raw[\"trail\"]) {\n if (!isRecord(step) || typeof step[\"dim\"] !== \"string\" || step[\"dim\"] === \"\") continue;\n const value = step[\"value\"];\n if (value !== null && typeof value !== \"string\") continue;\n trail.push({ dim: step[\"dim\"], value });\n }\n if (trail.length > 0) out.trail = trail;\n }\n\n return out;\n}\n\n/** True when nothing here would change how anything looks. */\nexport function presentationIsEmpty(p: GridPresentation): boolean {\n return (\n (p.style === undefined || tableStyleIsEmpty(p.style)) &&\n Object.keys(p.formats ?? {}).length === 0 &&\n (p.frozen?.length ?? 0) === 0 &&\n p.grouping === undefined &&\n Object.keys(p.widths ?? {}).length === 0 &&\n p.rowHeight === undefined &&\n (p.hiddenFields?.length ?? 0) === 0 &&\n p.cardHidden === undefined &&\n (p.columnOrder?.length ?? 0) === 0 &&\n p.gallerySize === undefined &&\n p.wrap === undefined &&\n p.fit === undefined &&\n p.freezeFirst === undefined &&\n p.footer === undefined &&\n Object.keys(p.summaries ?? {}).length === 0\n );\n}\n\n/** The string a view record stores. Empty string for \"this view styles nothing\". */\nexport function presentationDocument(p: GridPresentation): string {\n return presentationIsEmpty(p) ? \"\" : JSON.stringify(p);\n}\n\n/** Kinds whose value is a control the record renderer draws, never a formatter's string. */\nconst DRAWN_KINDS = new Set([\"select\", \"multi_select\", \"relation\", \"member\", \"person\", \"attachment\", \"file\", \"entity_reference\", \"checkbox\", \"boolean\", \"signature\", \"json\"]);\n\n/**\n * The display format a column's settings chose for it, when it is one the platform registry\n * knows and the column is not one the record renderer draws itself. `undefined` otherwise.\n */\nexport function shownAsOf(field: Field): FieldFormatConfig | undefined {\n if (DRAWN_KINDS.has(String(editorKindFor(field)))) return undefined;\n const raw = (field as unknown as { display_format?: unknown }).display_format;\n if (!raw || typeof raw !== \"object\") return undefined;\n const id = (raw as { id?: unknown }).id;\n // `formula` is the column's KIND, never a look: a worked-out column shows as its result format.\n if (typeof id !== \"string\" || id === \"formula\" || !getFieldFormat(id)) return undefined;\n const options = (raw as { options?: unknown }).options;\n return options && typeof options === \"object\" && Object.keys(options).length > 0\n ? ({ id, options } as FieldFormatConfig)\n : ({ id } as FieldFormatConfig);\n}\n\n/**\n * The format a Field's OWN declaration implies, before any view says anything.\n *\n * Returns `undefined` for a Field whose value the record renderer already draws\n * better than a formatter could โ€” a relation chip, a person, an attachment, a\n * choice โ€” because handing those to a text formatter would REPLACE a working\n * control with a string.\n */\nexport function formatForField(field: Field): FieldFormatConfig | undefined {\n // \"SHOWS AS\" โ€” THE FORMAT THE COLUMN'S OWN SETTINGS CHOSE (merge tranche 6a/6e). The column\n // keeps it on the Field (`display_format`, written by `custom.field_update`), so the grid, the\n // record panel and the export read one answer. A column the record renderer draws better than\n // a formatter (a choice, a link, a person, a file) keeps its own drawing.\n const shownAs = shownAsOf(field);\n // A RATING'S TOP STAR IS ITS MAX RULE (lane 10 P4), so changing the rule changes the stars.\n if (newerKindOf(field) === \"rating\" && (!shownAs || shownAs.id === \"rating\")) {\n return { id: \"rating\", options: { ...(shownAs?.options ?? {}), ratingMax: ratingMaxOf(field) } };\n }\n if (shownAs) return shownAs;\n // THE STORE'S OWN STAMPS (Created time, Last changed time) are instants and print like\n // every other date-and-time cell โ€” `dates.ts` answers, the same helper every card reads.\n const stamp = dateFormatFor(field);\n if (stamp && editorKindFor(field) !== \"datetime\") return stamp;\n switch (editorKindFor(field)) {\n case \"currency\":\n return {\n id: \"currency\",\n options: { currency: currencyCodeFor(field.unit) },\n };\n case \"percent\":\n return { id: \"percent\" };\n case \"datetime\":\n // The COLUMN's declaration: a `kind: \"date\"` column is a date column, anything else can\n // hold a moment. Each VALUE is still printed in its own shape (`RecordValue` โ†’\n // `dates.dateFormatFor(field, value)`), so a bare date in a datetime column reads as a date.\n return String(field.config?.[\"kind\"] ?? \"\") === \"date\" ? { id: \"date\" } : { id: \"datetime\" };\n case \"email\":\n return { id: \"email\" };\n case \"url\":\n return { id: \"url\" };\n case \"phone\":\n return { id: \"phone\" };\n case \"number\":\n return { id: \"number\" };\n default:\n return undefined;\n }\n}\n\n/**\n * THE FORMAT ONE CELL OF A COLUMN IS DRAWN IN โ€” the grid's cell and the gallery's card alike\n * (guard G4 \"one format\", lane VIEWS-AND-FIELDS W1-B): the view's own format for the column when\n * it set one, else the Field's own (`formatForField`). One function, so no layout formats money\n * (or a date, or a percent) its own way.\n */\nexport function cellFormatFor(field: Field, viewFormat?: FieldFormatConfig | undefined): FieldFormatConfig | undefined {\n return viewFormat ?? formatForField(field);\n}\n\n/**\n * The store lets a person type the SYMBOL they think in (`$`, `โ‚ฌ`), and\n * `Intl.NumberFormat` wants the ISO code. The symbols people actually enter are\n * mapped; anything else is passed straight through, because a real ISO code is\n * the other thing that lands in this field, and an unknown code still renders\n * (`1,234.00 XYZ`) rather than blanking โ€” THE FALLBACK LAW.\n */\nconst SYMBOL_TO_CODE: Record<string, string> = {\n $: \"USD\",\n \"ยฃ\": \"GBP\",\n \"โ‚ฌ\": \"EUR\",\n \"ยฅ\": \"JPY\",\n \"โ‚น\": \"INR\",\n \"โ‚ฝ\": \"RUB\",\n \"โ‚ฉ\": \"KRW\",\n \"R$\": \"BRL\",\n \"C$\": \"CAD\",\n \"A$\": \"AUD\",\n};\n\nexport function currencyCodeFor(unit: string | null | undefined): string {\n const raw = (unit ?? \"\").trim();\n if (raw === \"\") return \"USD\";\n return SYMBOL_TO_CODE[raw] ?? raw.toUpperCase();\n}\n\n/**\n * The color a value paints with when a view colors BY a column.\n *\n * NOTHING FAILS SILENTLY, so this never returns \"no color\" for a value that\n * exists. The store keeps a list column's choices as records of another Table,\n * and this grid has not read that Table's option colors, so the color is\n * derived from the value's own text: the same word always gets the same color,\n * different words get different ones, and `true` is green the way every\n * checklist reads. When those option colors do land, this is the ONE function\n * that changes and every screen follows.\n */\nexport const colorFromTheValue: ChoiceColorLookup = (_fieldKey, value) => {\n if (value === true) return \"green\";\n if (value === false || value === null || value === undefined) return undefined;\n const text = String(value).trim().toLowerCase();\n if (text === \"\") return undefined;\n let hash = 0;\n for (let i = 0; i < text.length; i += 1) {\n hash = (hash * 31 + text.charCodeAt(i)) >>> 0;\n }\n return COLOR_ROTATION[hash % COLOR_ROTATION.length] as StyleColor;\n};\n\n/**\n * G1's TABLE-LEVEL COLORS (`custom.table_decorations` / `client.tableDecorations`),\n * TRANSLATED INTO A `TableStyle` โ€” the same document a saved view's own `presentation.style`\n * is, so one resolver (`resolveTableStyle`, below) can treat \"the table's colors\" and \"this\n * view's colors\" as two layers of the SAME thing instead of two unrelated systems.\n *\n * THE TRANSLATION IS NECESSARY AND IT IS THE ONLY ONE. G1 addresses a Field by its ID\n * (REC-17 โ€” a column renamed keeps its color), because it is the STORE's own door; a\n * `TableStyle` addresses a Field by its KEY, because it reads straight off `row.document`,\n * which is keyed by Field key. A rule or a color-by whose Field has been deleted (its id no\n * longer resolves to a key) is dropped rather than crashing the whole style โ€” a defect in\n * one rule costs that rule, never the read.\n */\nexport function styleFromDecorations(\n decorations: TableDecorations | null | undefined,\n fields: readonly Field[],\n): TableStyle {\n if (!decorations) return { version: 1 };\n const keyOf = (id: string) => fields.find((f) => f.id === id)?.key;\n const style: TableStyle = { version: 1 };\n\n if (decorations.color_by?.field) {\n const field = keyOf(decorations.color_by.field);\n if (field) style.colorBy = { field, target: decorations.color_by.target };\n }\n\n if (Array.isArray(decorations.rules) && decorations.rules.length > 0) {\n const rules: ColorRule[] = [];\n decorations.rules.forEach((rule, index) => {\n const field = keyOf(rule.field);\n if (!field) return;\n rules.push({\n id: rule.id ?? `decoration:${index}`,\n field,\n op: rule.op,\n color: rule.color,\n target: rule.target,\n ...(rule.value === undefined || rule.value === null ? {} : { value: String(rule.value) }),\n });\n });\n if (rules.length > 0) style.rules = rules;\n }\n\n if (decorations.rows && Object.keys(decorations.rows).length > 0) {\n style.rows = { ...decorations.rows };\n }\n\n const columns: Record<string, StyleColor> = {};\n for (const [id, color] of Object.entries(decorations.columns ?? {})) {\n const key = keyOf(id);\n if (key) columns[key] = color;\n }\n if (Object.keys(columns).length > 0) style.columns = columns;\n\n const cells: Record<string, Record<string, StyleColor>> = {};\n for (const [rowId, byField] of Object.entries(decorations.cells ?? {})) {\n for (const [id, color] of Object.entries(byField ?? {})) {\n const key = keyOf(id);\n if (!key) continue;\n (cells[rowId] ??= {})[key] = color;\n }\n }\n if (Object.keys(cells).length > 0) style.cells = cells;\n\n return style;\n}\n\n/**\n * THE ONE PRESENTATION RESOLVER every colored surface in this package reads โ€”\n * `Grid`, and (via `Card`) the kanban board, the calendar and the gallery.\n *\n * THE CLASS THIS CLOSES (lane UI-QUICK-1, TABLE-PARITY review, 2026-09-24). Threading\n * `view.presentation.style` into `Card` closed the instance the CALENDAR-GALLERY bench\n * found โ€” a VIEW that had its own color-by looked right everywhere โ€” but it left the\n * class open: a table whose colors are the ORGANIZATION's decorations (G1's\n * `custom.table_decorations` โ€” \"color rows by Account Type\", \"MAXED and NEAR rules\" on\n * Arman's own Coding Accounts table) and whose views never set a `presentation.style` of\n * their own drew NOTHING, on the grid exactly as much as on every other layout, because\n * nothing in this package had ever read G1's door at all.\n *\n * TWO LAYERS, ONE DOCUMENT. The table's decorations are the base coat โ€” what every view\n * of this table shows until a view says otherwise. A view's OWN `presentation.style` is\n * an override, piece by piece: a view that only ever set manual row highlights still\n * inherits the table's color-by and rules for everything it never touched, and a view\n * that fully redecorated (its own `colorBy` AND its own `rules`) is not haunted by a\n * table rule it meant to replace. Per-piece, never all-or-nothing, because a view is a\n * LOOK on top of the table, not a wholesale substitute for it.\n */\nexport function resolveTableStyle(\n decorations: TableDecorations | null | undefined,\n fields: readonly Field[],\n viewStyle: TableStyle | undefined,\n): TableStyle {\n const base = styleFromDecorations(decorations, fields);\n if (!viewStyle) return base;\n const colorBy = viewStyle.colorBy ?? base.colorBy;\n const rules = viewStyle.rules ?? base.rules;\n const rows = viewStyle.rows ?? base.rows;\n const columns = viewStyle.columns ?? base.columns;\n const cells = viewStyle.cells ?? base.cells;\n return {\n version: 1,\n ...(colorBy !== undefined ? { colorBy } : {}),\n ...(rules !== undefined ? { rules } : {}),\n ...(rows !== undefined ? { rows } : {}),\n ...(columns !== undefined ? { columns } : {}),\n ...(cells !== undefined ? { cells } : {}),\n };\n}\n","\"use client\";\n\n// src/viewKeys.ts โ€” @ai-matrx/records-ui ยท lane S1-PRIME VIEW-KEYS (UI-CHAMPIONS-PLAN rev 2)\n//\n// EVERY PER-VIEW SETTING IS A DECLARED KEY OF THE ONE SAVED VIEW.\n//\n// A saved view is one row of `platform.saved_view` (lane S0). What it may hold is decided in ONE\n// place: `custom.view_keys()`, the registry `custom.view_declare` judges every save against. This\n// file is that registry's client half and the ONE accessor every layout reads a view through:\n//\n// - `VIEW_KEYS` mirrors `custom.view_keys()` row for row (path, shape, the layouts that read it,\n// who may write it). The live walk compares the two through REST, as test@test.com, so a key\n// added on one side and not the other fails a walk, never a person.\n// - `viewSettings(definition)` is THE reader: a stored definition in, every declared key out,\n// typed. `viewFromRow` (views.ts) goes through it, so the Grid, the board, the calendar,\n// and the gallery all see a view the same way.\n// - `viewSettingsFor(view, layout)` hands a layout only the keys it reads; a layout that does\n// not use a key never sees it (the calendar is never told about swimlanes).\n// - `viewSettingsPatch(patch)` is THE writer's shape: only the keys a press changed, a cleared\n// key as null (the door removes it). `saveViewPatch` sends it through `custom.view_declare`.\n//\n// Fields are named by KEY (a Field's key is immutable; every reader draws rows by key). The door\n// accepts an id and stores the key, so an agent may send either.\n\nimport type { RecordFilter, RuleExpression } from \"@ai-matrx/records\";\nimport { parseGridPresentation, presentationDocument, presentationIsEmpty, type GridPresentation } from \"./presentation\";\n\n/** Every surface a saved view is drawn by. */\nexport type ViewKeyLayout = \"grid\" | \"kanban\" | \"calendar\" | \"gallery\" | \"timeline\" | \"list\";\n\n/**\n * ๐Ÿšฉ THE ONE SWITCH FOR THE TIMELINE'S STORE KEYS (lane VIEWS-AND-FIELDS, sublane TL).\n *\n * `vk_a_calendar_keeps_where_a_record_ends_and_a_timeline_is_a_view.sql` (sublane VK) teaches\n * `custom.view_keys()` the layout `timeline`, the keys `start_field` and `end_field` (calendar +\n * timeline), and `group_field` for the timeline. Until that migration is on production this stays\n * `false`, and:\n * ยท `timeline` is not a layout the store keeps (`STORE_KEPT_LAYOUTS`), so it is drawn for the visit;\n * ยท `start_field` / `end_field` are held for the visit, never sent (`viewSettingsPatch`), and the\n * Start also rides `date_field`, which the store already keeps;\n * ยท the timeline's toolbar says \"Kept for this visit only\".\n * Flipped to `true`, VIEW_KEYS below mirrors the migrated registry row for row (the live walk\n * compares them) and every one of those is kept on the view.\n */\nexport const STORE_KEEPS_TIMELINE = true;\n\n/** Who may write a key: anyone who may save the view, an input the door rewrites, or the server only. */\nexport type ViewKeyWriter = \"caller\" | \"input\" | \"server\";\n\nexport interface ViewKeyDeclaration {\n /** Where it lives in `platform.saved_view.definition` (dotted under `presentation`). */\n path: string;\n /** What the store's guard checks (`custom._view_key_value`). */\n shape: string;\n /** The layouts that read it. */\n layouts: readonly ViewKeyLayout[];\n writer: ViewKeyWriter;\n}\n\nconst ALL: readonly ViewKeyLayout[] = [\"grid\", \"kanban\", \"calendar\", \"gallery\", ...(STORE_KEEPS_TIMELINE ? ([\"timeline\"] as const) : []), \"list\"];\n/** The layouts whose cards draw the fields a view leaves shown (`presentation.hiddenFields`). */\nconst HIDES: readonly ViewKeyLayout[] = [\"grid\", \"gallery\", \"kanban\", \"calendar\", ...(STORE_KEEPS_TIMELINE ? ([\"timeline\"] as const) : []), \"list\"];\nconst GRIDS: readonly ViewKeyLayout[] = [\"grid\"];\n\n/**\n * THE REGISTRY, as `custom.view_keys()` declares it (uichamp_s1_a_view_keeps_every_setting_it_is_given.sql).\n * Order and content are compared with the store by the live walk.\n */\nexport const VIEW_KEYS: readonly ViewKeyDeclaration[] = [\n { path: \"layout\", shape: \"kind\", layouts: ALL, writer: \"caller\" },\n { path: \"filters\", shape: \"filter\", layouts: ALL, writer: \"caller\" },\n { path: \"where\", shape: \"rule\", layouts: ALL, writer: \"caller\" },\n { path: \"group_field\", shape: \"field\", layouts: [\"kanban\", ...(STORE_KEEPS_TIMELINE ? ([\"timeline\"] as const) : [])], writer: \"caller\" },\n { path: \"swimlane_field\", shape: \"field:lane\", layouts: [\"kanban\"], writer: \"caller\" },\n { path: \"collapsed_columns\", shape: \"values\", layouts: [\"kanban\"], writer: \"caller\" },\n { path: \"measure\", shape: \"field:number\", layouts: [\"kanban\"], writer: \"caller\" },\n { path: \"date_field\", shape: \"field:date\", layouts: [\"calendar\"], writer: \"caller\" },\n ...(STORE_KEEPS_TIMELINE\n ? ([\n { path: \"start_field\", shape: \"field:date\", layouts: [\"timeline\"], writer: \"caller\" },\n { path: \"end_field\", shape: \"field:date\", layouts: [\"calendar\", \"timeline\"], writer: \"caller\" },\n ] as const)\n : []),\n { path: \"image_field\", shape: \"field\", layouts: [\"gallery\"], writer: \"caller\" },\n { path: \"sorts\", shape: \"sorts\", layouts: ALL, writer: \"caller\" },\n { path: \"rule_id\", shape: \"uuid\", layouts: ALL, writer: \"caller\" },\n { path: \"is_default\", shape: \"boolean\", layouts: ALL, writer: \"caller\" },\n { path: \"presentation\", shape: \"presentation\", layouts: ALL, writer: \"caller\" },\n { path: \"presentation.style\", shape: \"style\", layouts: ALL, writer: \"caller\" },\n { path: \"presentation.formats\", shape: \"object\", layouts: GRIDS, writer: \"caller\" },\n { path: \"presentation.frozen\", shape: \"fields\", layouts: GRIDS, writer: \"caller\" },\n { path: \"presentation.grouping\", shape: \"grouping\", layouts: [\"grid\", \"gallery\", \"list\"], writer: \"caller\" },\n { path: \"presentation.widths\", shape: \"widths\", layouts: GRIDS, writer: \"caller\" },\n { path: \"presentation.rowHeight\", shape: \"row_height\", layouts: GRIDS, writer: \"caller\" },\n { path: \"presentation.hiddenFields\", shape: \"fields\", layouts: HIDES, writer: \"caller\" },\n { path: \"presentation.cardHidden\", shape: \"fields\", layouts: [\"kanban\", \"calendar\", ...(STORE_KEEPS_TIMELINE ? ([\"timeline\"] as const) : []), \"list\", \"gallery\"], writer: \"caller\" },\n { path: \"presentation.columnOrder\", shape: \"fields\", layouts: GRIDS, writer: \"caller\" },\n { path: \"presentation.gallerySize\", shape: \"gallery_size\", layouts: [\"gallery\"], writer: \"caller\" },\n { path: \"presentation.wrap\", shape: \"boolean\", layouts: GRIDS, writer: \"caller\" },\n { path: \"presentation.wrapColumns\", shape: \"fields\", layouts: GRIDS, writer: \"caller\" },\n { path: \"presentation.fit\", shape: \"fit\", layouts: GRIDS, writer: \"caller\" },\n { path: \"presentation.freezeFirst\", shape: \"boolean\", layouts: GRIDS, writer: \"caller\" },\n { path: \"presentation.footer\", shape: \"footer\", layouts: GRIDS, writer: \"caller\" },\n { path: \"presentation.summaries\", shape: \"summaries\", layouts: GRIDS, writer: \"caller\" },\n { path: \"grid\", shape: \"grid\", layouts: GRIDS, writer: \"caller\" },\n { path: \"hidden_fields\", shape: \"input\", layouts: HIDES, writer: \"input\" },\n { path: \"table_id\", shape: \"server\", layouts: ALL, writer: \"server\" },\n { path: \"order\", shape: \"server\", layouts: GRIDS, writer: \"server\" },\n { path: \"moved_from\", shape: \"server\", layouts: ALL, writer: \"server\" },\n];\n\n/**\n * THE LAYOUTS THE STORE KEEPS ON A VIEW. `custom.view_keys()` judges a view's `layout` against its\n * own list (grid, kanban, calendar, gallery) and refuses any other word by name. A layout this\n * package draws before the store keeps it โ€” the timeline, 2026-10-02 โ€” is never sent as a view's or a\n * look's layout (`viewSettingsPatch`, `viewDocument`): it is drawn for this visit and says so. When the\n * store's registry admits it, add it here and nothing else changes.\n */\nexport const STORE_KEPT_LAYOUTS: readonly string[] = [\"grid\", \"kanban\", \"calendar\", \"gallery\", ...(STORE_KEEPS_TIMELINE ? [\"timeline\"] : []), \"list\"];\n\n/** Whether the store keeps this layout on a saved view. */\nexport function storeKeepsLayout(layout: string): boolean {\n return STORE_KEPT_LAYOUTS.includes(layout);\n}\n\n/** The keys one layout reads, by path. */\nexport function viewKeysFor(layout: ViewKeyLayout): string[] {\n return VIEW_KEYS.filter((k) => k.layouts.includes(layout)).map((k) => k.path);\n}\n\n/** One sort clause: a Field key and a direction. */\nexport interface ViewSortKey {\n field: string;\n direction: \"asc\" | \"desc\";\n}\n\n/**\n * EVERY DECLARED KEY OF A VIEW, TYPED. Absent keys are `null` / empty, never guessed.\n * `presentation` is the parsed look (colour field, pinned fields, row height, grouping with its\n * sub-groups and shut sections, hidden fields, gallery size, summaries).\n */\nexport interface ViewSettings {\n layout: \"grid\" | \"kanban\" | \"calendar\" | \"gallery\" | \"timeline\" | \"list\";\n filters: RecordFilter | null;\n where: RuleExpression | null;\n groupField: string | null;\n swimlaneField: string | null;\n collapsedColumns: string[];\n measure: string | null;\n dateField: string | null;\n /** Timeline: where a record's bar starts (`start_field`). */\n startField: string | null;\n /** Calendar and timeline: where a record ends (`end_field`). */\n endField: string | null;\n imageField: string | null;\n sorts: ViewSortKey[];\n ruleId: string | null;\n isDefault: boolean;\n presentation: GridPresentation;\n movedFrom: Record<string, unknown> | null;\n}\n\n// The layouts a stored view reads back as; one the store does not keep yet reads as a grid. A\n// saved timeline read back as \"grid\" was the reload that lost it (FDT, 2026-10-03).\nconst KINDS: readonly string[] = STORE_KEPT_LAYOUTS;\n\n/** THE READER. A stored `definition` (and the row's flat `filters`) in, every declared key out. */\nexport function viewSettings(definition: Record<string, unknown> | null | undefined, rowFilters?: RecordFilter | null): ViewSettings {\n const data = definition ?? {};\n const text = (key: string): string | null => {\n const v = data[key];\n return typeof v === \"string\" && v !== \"\" ? v : null;\n };\n const layout = String(data[\"layout\"] ?? \"grid\");\n const flat = rowFilters ?? (isObject(data[\"filters\"]) ? (data[\"filters\"] as RecordFilter) : null);\n const moved = data[\"moved_from\"];\n return {\n layout: (KINDS as readonly string[]).includes(layout) ? (layout as ViewSettings[\"layout\"]) : \"grid\",\n filters: flat && Object.keys(flat).length > 0 ? flat : null,\n where: isRule(data[\"where\"]) ? (data[\"where\"] as RuleExpression) : null,\n groupField: text(\"group_field\"),\n swimlaneField: text(\"swimlane_field\"),\n collapsedColumns: Array.isArray(data[\"collapsed_columns\"])\n ? (data[\"collapsed_columns\"] as unknown[]).filter((v) => [\"string\", \"number\", \"boolean\"].includes(typeof v)).map(String)\n : [],\n measure: text(\"measure\"),\n dateField: text(\"date_field\"),\n startField: text(\"start_field\"),\n endField: text(\"end_field\"),\n imageField: text(\"image_field\"),\n sorts: parseSorts(data[\"sorts\"]),\n ruleId: text(\"rule_id\"),\n isDefault: data[\"is_default\"] === true || data[\"is_default\"] === \"true\",\n presentation: parseGridPresentation(data[\"presentation\"]),\n movedFrom: isObject(moved) ? (moved as Record<string, unknown>) : null,\n };\n}\n\n/** The settings as the spec names them โ€” what `viewSettingsPatch` takes. */\nexport type ViewSettingsPatch = Partial<Omit<ViewSettings, \"movedFrom\" | \"layout\">> & {\n /** Any layout the package draws; one the store does not keep is not sent. */\n layout?: string;\n};\n\n/**\n * THE KEYS ONE LAYOUT READS, and nothing else. A layout that does not use a key never sees it:\n * a calendar is not told the board's swimlanes, a board is not told the grid's pinned fields.\n */\nexport function viewSettingsFor(settings: ViewSettings, layout: ViewKeyLayout): Partial<ViewSettings> {\n const reads = new Set(viewKeysFor(layout));\n const out: Partial<ViewSettings> = { layout: settings.layout };\n const take = <K extends keyof ViewSettings>(key: K, path: string) => {\n if (reads.has(path)) out[key] = settings[key];\n };\n take(\"filters\", \"filters\");\n take(\"where\", \"where\");\n take(\"groupField\", \"group_field\");\n take(\"swimlaneField\", \"swimlane_field\");\n take(\"collapsedColumns\", \"collapsed_columns\");\n take(\"measure\", \"measure\");\n take(\"dateField\", \"date_field\");\n take(\"startField\", \"start_field\");\n take(\"endField\", \"end_field\");\n take(\"imageField\", \"image_field\");\n take(\"sorts\", \"sorts\");\n take(\"ruleId\", \"rule_id\");\n take(\"isDefault\", \"is_default\");\n const p = settings.presentation;\n const look: GridPresentation = {};\n for (const [key, value] of Object.entries(p)) {\n if (reads.has(`presentation.${key}`)) (look as Record<string, unknown>)[key] = value;\n }\n out.presentation = look;\n return out;\n}\n\n/**\n * THE WRITER'S SHAPE. Only the keys a press changed; a cleared Field is `null` (the door removes\n * it); lists are sent whole. `presentation` is sent whole because the door judges it as one key.\n */\nexport function viewSettingsPatch(patch: ViewSettingsPatch): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n const field = (key: keyof ViewSettingsPatch, path: string) => {\n if (patch[key] !== undefined) out[path] = (patch[key] as string | null) || null;\n };\n // A layout the store does not keep yet is held for this visit, never sent (`STORE_KEPT_LAYOUTS`).\n if (patch.layout !== undefined && storeKeepsLayout(patch.layout)) out[\"layout\"] = patch.layout;\n field(\"groupField\", \"group_field\");\n field(\"swimlaneField\", \"swimlane_field\");\n field(\"measure\", \"measure\");\n field(\"dateField\", \"date_field\");\n // Held for the visit until the store keeps them (`STORE_KEEPS_TIMELINE`).\n if (STORE_KEEPS_TIMELINE) {\n field(\"startField\", \"start_field\");\n field(\"endField\", \"end_field\");\n }\n field(\"imageField\", \"image_field\");\n field(\"ruleId\", \"rule_id\");\n if (patch.collapsedColumns !== undefined) out[\"collapsed_columns\"] = patch.collapsedColumns ?? [];\n if (patch.sorts !== undefined) out[\"sorts\"] = patch.sorts ?? [];\n if (patch.where !== undefined) out[\"where\"] = patch.where ?? null;\n if (patch.isDefault !== undefined) out[\"is_default\"] = patch.isDefault === true;\n if (patch.presentation !== undefined) {\n out[\"presentation\"] =\n !patch.presentation || presentationIsEmpty(patch.presentation)\n ? null\n : (JSON.parse(presentationDocument(patch.presentation)) as Record<string, unknown>);\n }\n return out;\n}\n\nfunction isObject(raw: unknown): boolean {\n return Boolean(raw) && typeof raw === \"object\" && !Array.isArray(raw);\n}\n\nfunction isRule(raw: unknown): boolean {\n if (!isObject(raw)) return false;\n const node = raw as { op?: unknown; args?: unknown };\n return typeof node.op === \"string\" && Array.isArray(node.args);\n}\n\nfunction parseSorts(raw: unknown): ViewSortKey[] {\n let list: unknown = raw;\n if (typeof raw === \"string\") {\n try {\n list = raw.trim() === \"\" ? [] : JSON.parse(raw);\n } catch {\n return [];\n }\n }\n if (!Array.isArray(list)) return [];\n return list\n .filter((s): s is { field: string; direction?: string } => isObject(s) && typeof (s as { field?: unknown }).field === \"string\")\n .map((s) => ({ field: s.field, direction: s.direction === \"desc\" ? \"desc\" : \"asc\" }));\n}\n","\"use client\";\n\n// src/views.ts โ€” @ai-matrx/records-ui ยท SCR-6, SCR-7, SCR-N-6\n//\n// A SAVED VIEW IS ONE ROW OF THE ONE SAVED-VIEW STORE, `platform.saved_view`\n// (chair ruling 2026-09-23, lane S0 ONE-SAVED-VIEW), read through `custom.views`\n// and written through `custom.view_declare` โ€” the same door the digests, the\n// notify rules, the agent's boards, the grid's choices (G1/G7), the hand-set\n// order (G13) and the mover use. It owns its layout and REFERENCES its\n// membership rule.\n//\n// ๐Ÿšจ THERE WERE TWO STORES (UI-CHAMPIONS-PLAN-ATTACK hole 1). Until S0 this bar\n// kept each view as a record of a package-owned `records_ui_view` Table in every\n// organization, beside `platform.saved_view`: a view the mover carried in never\n// reached the bar, a view saved in the bar was invisible to every subscription,\n// and a grid choice G1 kept had no screen that read it. The repair migration\n// `oneview_every_view_bar_view_moves_into_the_one_store.sql` moved every one of\n// those records into `platform.saved_view` UNDER THE SAME ID and archived the\n// Tables; nothing in this package reads or writes them any more.\n//\n// The split matters. A view does not carry a filter predicate of its own that a\n// screen re-implements in JavaScript: it names a Rule, and the STORE answers\n// who is in it (`custom.rule_members`). So the same view means the same thing\n// to a grid, to an export, to an agent and to the server. What the view owns\n// itself is presentation โ€” which of the four layouts, which Field the kanban\n// columns come from, which Field the calendar reads, which Field the gallery\n// shows โ€” because that is not data, it is how this person wants to look at it.\n//\n// The whole view is also a plain declarative object (`SavedViewSpec`), which is\n// what makes \"an agent writes the view in 45 seconds\" true: one `view_declare`\n// of one definition, and the person only tweaks.\n\nimport { presentationDocument, presentationIsEmpty, type GridPresentation } from \"./presentation\";\nimport { STORE_KEEPS_TIMELINE, storeKeepsLayout, viewSettings, viewSettingsPatch } from \"./viewKeys\";\nexport { STORE_KEEPS_TIMELINE, STORE_KEPT_LAYOUTS, storeKeepsLayout } from \"./viewKeys\";\nimport type { RecordFilter, RecordsResult, RuleExpression, SavedViewRow, Uuid } from \"@ai-matrx/records\";\nimport type { RecordsClient } from \"@ai-matrx/records/core\";\n\n/** The ways one saved view can be looked at (SCR-6; the timeline, lane VIEWS-AND-FIELDS TL). */\nexport const VIEW_LAYOUTS = [\"grid\", \"kanban\", \"calendar\", \"gallery\", \"timeline\", \"list\"] as const;\nexport type ViewLayout = (typeof VIEW_LAYOUTS)[number];\n\nexport const LAYOUT_LABEL: Record<ViewLayout, string> = {\n grid: \"Grid\",\n kanban: \"Kanban\",\n calendar: \"Calendar\",\n gallery: \"Gallery\",\n timeline: \"Timeline\",\n list: \"List\",\n};\n\n\n/**\n * โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n * WHAT EACH LAYOUT NEEDS FROM THE TABLE, AND HOW THE SCREEN SUPPLIES IT\n * (lane DATA-V2-VIEWS-1, Arman 2026-09-28).\n *\n * THE DEFECT. A plain table opened as a Kanban said \"A kanban board needs a Field to make its\n * columns from โ€ฆ Add column, then Select โ€” and it appears here.\" It told the person what to do\n * instead of doing it, and on an AI platform it offered no AI. The ruling: ONE primitive, used by\n * every view (`ViewNeeds`) โ€” the empty state says in one plain sentence what is missing and what\n * pressing will do, offers \"Make it work\" (the column is declared through the store's\n * `custom.field_declare`, its key minted by the store, its name and choices editable before the\n * press) and \"Ask AI\" (the agent does it through the same door, held for the person's approval by\n * the organization's \"Agent changes to this organization's data\" setting).\n *\n * THE CENSUS โ€” every layout this package draws, and what it reads:\n * ยท grid nothing.\n * ยท kanban a CHOICE column (its options become the board's columns). Blocking.\n * ยท calendar a DATE column (it places each record on a day). Blocking.\n * ยท gallery a FILE column for the card's picture. NOT blocking: the cards draw without one,\n * so the offer sits above them instead of in place of them.\n * ยท timeline a DATE column each bar starts on (the calendar's `dateField`). Blocking.\n * `PAGE_VIEWS`' two extra values (dashboards, archived) read\n * nothing a table can lack. This package ships no map layout; the day it does,\n * `Record<ViewLayout, โ€ฆ>` below refuses to compile until the new layout names what it needs.\n */\nexport type ViewNeedKind = \"choice\" | \"date\" | \"file\";\n\nexport interface ViewNeed {\n /** What the layout reads, as a kind a column declares (`offerableFields`). */\n kind: ViewNeedKind;\n /** The view key the made column is written onto, so the layout opens drawn by it. */\n viewKey: \"groupField\" | \"dateField\" | \"imageField\";\n /** True when the layout cannot draw at all without it (kanban, calendar). */\n blocking: boolean;\n /** The column the press makes, before the person edits it: its name, the store's word, its choices. */\n starter: { label: string; type: \"select\" | \"datetime\" | \"attachment\"; options?: readonly string[] };\n /** The table HAS columns this layout can use and none is chosen: the sentence over the picker. */\n pick: string;\n /** One plain sentence: what the table is missing (all a person who may not change it is told). */\n missing: string;\n /** One plain sentence: what pressing Make it work will do, with the column's name. */\n does: (label: string) => string;\n /** The sentence an agent is asked with โ€” the same act, said in words. */\n ask: (label: string, options: readonly string[]) => string;\n}\n\n/** A list of words as a person says it: \"To do, In progress and Done\". */\nexport function wordsList(words: readonly string[]): string {\n const clean = words.map((w) => w.trim()).filter((w) => w !== \"\");\n if (clean.length <= 1) return clean.join(\"\");\n return `${clean.slice(0, -1).join(\", \")} and ${clean[clean.length - 1]}`;\n}\n\nexport const VIEW_NEEDS: Record<ViewLayout, ViewNeed | null> = {\n grid: null,\n // A list reads nothing a table can lack: rows, titled, grouped only when the person chooses.\n list: null,\n kanban: {\n kind: \"choice\",\n viewKey: \"groupField\",\n blocking: true,\n starter: { label: \"Status\", type: \"select\", options: [\"To do\", \"In progress\", \"Done\"] },\n pick: \"Pick the choice column the board's columns come from.\",\n missing: \"This table has no choice column to make the board's columns from.\",\n does: (label) => `Make it work adds a ${label} column with the choices below and groups the board by it.`,\n ask: (label, options) =>\n `Add a choice column called ${label} to this table${options.length > 0 ? ` with the choices ${wordsList(options)}` : \"\"}, then group the Kanban board by it.`,\n },\n calendar: {\n kind: \"date\",\n viewKey: \"dateField\",\n blocking: true,\n starter: { label: \"Date\", type: \"datetime\" },\n pick: \"Pick the date column the calendar places records by.\",\n missing: \"This table has no date column to place records on.\",\n does: (label) => `Make it work adds a ${label} column and lays the calendar out by it.`,\n ask: (label) => `Add a date column called ${label} to this table, then lay the Calendar out by it.`,\n },\n gallery: {\n kind: \"file\",\n viewKey: \"imageField\",\n blocking: false,\n starter: { label: \"Photo\", type: \"attachment\" },\n pick: \"Pick the file column each card shows as its picture.\",\n missing: \"The cards have no picture because this table has no file column.\",\n does: (label) => `Make it work adds a ${label} column and shows it on each card.`,\n ask: (label) => `Add a file column called ${label} to this table, then show it as the picture on each Gallery card.`,\n },\n timeline: {\n kind: \"date\",\n viewKey: \"dateField\",\n blocking: true,\n starter: { label: \"Start\", type: \"datetime\" },\n pick: \"Pick the date column each bar starts on.\",\n missing: \"This table has no date column to place records on.\",\n does: (label) => `Make it work adds a ${label} column and lays the timeline out by it.`,\n ask: (label) => `Add a date column called ${label} to this table, then lay the Timeline out by it.`,\n },\n};\n\n/**\n * A NAME NO COLUMN OF THIS TABLE ALREADY HAS: the starter's own, else \"Status 2\", \"Status 3\"โ€ฆ\n * A table that already holds a text column called Status must not be refused the board.\n */\nexport function freeColumnName(wanted: string, taken: readonly string[]): string {\n const held = new Set(taken.map((t) => t.trim().toLowerCase()));\n const base = wanted.trim() === \"\" ? \"Column\" : wanted.trim();\n if (!held.has(base.toLowerCase())) return base;\n for (let n = 2; n < 100; n += 1) {\n const next = `${base} ${n}`;\n if (!held.has(next.toLowerCase())) return next;\n }\n return base;\n}\n\n/** One sort clause. The Field's key and a direction; nothing else is a sort. */\nexport interface ViewSort {\n field: string;\n direction: \"asc\" | \"desc\";\n}\n\n/** How a saved view is ordered: by hand, by its sort, or it never said (the same as sorted). */\nexport type ViewOrder = \"manual\" | \"sorted\" | null;\n\n/** The order word a stored definition carries. Only the two words the store writes are read. */\nexport function viewOrderOf(definition: Record<string, unknown> | null | undefined): ViewOrder {\n const raw = definition?.[\"order\"];\n return raw === \"manual\" ? \"manual\" : raw === \"sorted\" ? \"sorted\" : null;\n}\n\n/**\n * THE DECLARATIVE SPEC. An agent writes this whole object; a person tweaks it.\n * Every builder component in this package takes one of these as a prop.\n */\nexport interface SavedViewSpec {\n name: string;\n /** The Table this view looks at. */\n subject: Uuid;\n layout: ViewLayout;\n /** Kanban: the Field whose value becomes a column. */\n groupField?: string | null;\n /**\n * Kanban: a number Field to TOTAL under each column heading. It lives on the VIEW for\n * the same reason the colours do โ€” two people read one board two ways and neither\n * reading is a fact about the records.\n */\n measure?: string | null;\n /**\n * Kanban: the Field whose values cut the board into SWIMLANES across the columns โ€” a\n * dispatcher's board by Status, one lane per Technician (S1-PRIME VIEW-KEYS). Never the\n * column Field itself; the store refuses that.\n */\n swimlaneField?: string | null;\n /** Kanban: the columns this view keeps shut to a strip, by the column's stored value. */\n collapsedColumns?: string[];\n /** Calendar: the Field that places a record in the month. */\n dateField?: string | null;\n /** Timeline: the Field a record's bar starts on (`start_field`). */\n startField?: string | null;\n /** Calendar and timeline: the Field a record ends on (`end_field`). */\n endField?: string | null;\n /** Gallery: the Field shown large on the card. */\n imageField?: string | null;\n sorts?: ViewSort[];\n /**\n * HOW THE VIEW IS ORDERED (ORDER-FIX): `\"manual\"` โ€” the positions a person set by hand ARE the\n * view's sort, and it carries no `sorts`; `\"sorted\"` or null โ€” by `sorts`. READ ONLY: the store\n * writes \"manual\" only when the rows are placed (`custom.view_record_order_set`) and turns it\n * off itself when a sort is saved, so no patch ever sends it (`viewSettingsPatch` does not).\n */\n order?: ViewOrder;\n /**\n * The membership Rule. The STORE decides who is in this view; this is a\n * reference to its Rule record, never a predicate this package evaluates.\n */\n ruleId?: Uuid | null;\n /**\n * THE FLAT FILTER a digest or a notify rule wrote the view with โ€” the one shape\n * `custom.record_filter_sql` compiles, by Field KEY. A view saved in the bar has\n * none; a view saved by `DigestScheduler` (\"where Status is Open\") does, and the\n * grid and the board show exactly the records it names.\n */\n filters?: RecordFilter;\n /**\n * S2-PRIME โ€” THE VIEW'S OWN QUESTION, as the condition builder writes it: the store's Rule\n * shape `{op, args}`, ALL / ANY / NOT groups nested to any depth, Fields by id. Kept in the\n * definition as `where` and asked of the store unchanged โ€” the grid's list door, the board's\n * headings and the dashboard's numbers compile it into the one WHERE clause\n * (`custom.record_filter_sql`). Never stored under `filters`: that key is the flat map the\n * digests and the notifier read, and they would refuse it by name.\n */\n where?: RuleExpression | null;\n isDefault?: boolean;\n /**\n * HOW THIS VIEW LOOKS โ€” its colors, its per-column formats and its frozen\n * column (`presentation.ts`).\n *\n * It lives on the VIEW and not on the Fields because two people read one\n * table two ways and neither reading is a fact about the records; it lives on\n * a RECORD, not a new column, because that is what makes it something an\n * agent writes with the same `records` tool as everything else.\n */\n presentation?: GridPresentation;\n}\n\n/**\n * A saved view as it comes back out of the store, with its row's own id.\n *\n * It carries no version: `custom.view_declare` MERGES one key at a time, so two\n * tabs pressing two different controls on one view both stand, and one control\n * pressed in both keeps the latest โ€” how Airtable and Notion keep a view change.\n */\nexport interface SavedView extends SavedViewSpec {\n id: Uuid;\n /** Where the view came from when it was moved in (the mover, the S0 repair), else null. */\n movedFrom: Record<string, unknown> | null;\n}\n\n/** THE TABLE'S DEFAULT VIEW: the first view that says it is. */\nexport function defaultViewOf<V extends Pick<SavedView, \"isDefault\">>(views: readonly V[]): V | undefined {\n return views.find((v) => v.isDefault);\n}\n\n/**\n * WHICH FIELD EACH LAYOUT CAN BE DRAWN FROM, decided from the FIELD'S OWN\n * DECLARATION and never from the values it happens to hold.\n *\n * A kanban makes columns from a choice: the Field whose value is one of a\n * closed set (FLD-5 / FLD-6 โ€” a pick-list is a Table, and a select stores the\n * id of one of its records). Grouping by a free-text Field produces one column\n * per record, which is not a board. A calendar places a record in time, so it\n * needs a Field that holds a date.\n *\n * These two answers are here, beside the layouts, because the picker on the\n * screen and any agent writing a view spec must agree about what is offerable.\n */\nexport const LAYOUT_FIELD_KIND: Record<ViewLayout, ViewNeedKind | null> = {\n grid: null,\n kanban: \"choice\",\n calendar: \"date\",\n gallery: \"file\",\n timeline: \"date\",\n list: null,\n};\n\n/**\n * โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n * WHICH VIEW A LINK NAMES โ€” the address bar as a first-class control.\n *\n * ๐Ÿšจ THE DEFECT THIS CLOSES (lane FRONT-DOOR, 2026-09-21; VERIFIER-8 HIGH-2).\n * `/data/<table>?view=kanban` returned 200 and rendered THE GRID. The\n * switcher was there, the four buttons worked, and the parameter was read by\n * nobody โ€” so a person who bookmarked a board, or sent one to a colleague, got\n * a grid, and nothing on the screen said the view had been dropped. A link that\n * does not land is the inbox-link defect (`activeRecordId`) and the dashboard\n * defect (`activeDashboardId`) for the third time, so this is the same shape of\n * fix: the URL names it, the page opens on it, and the URL FOLLOWS the person\n * when they pick another.\n *\n * The dashboards are a fifth value and not a special case: to the person, \"the\n * dashboard\" is one of the ways to look at this table, and a link to it has to\n * work like a link to the board.\n */\n/**\n * ๐Ÿšจ `archived` IS THE SIXTH VALUE, AND IT IS NOT A SPECIAL CASE EITHER (lane\n * FIX-10A-ARCHIVE, 2026-09-22; VERIFIER-10 F4).\n *\n * Archiving a record used to be a ONE-WAY DOOR: the grid's row action soft-\n * deletes correctly โ€” `deleted_at` set, version bumped, nothing removed โ€” and\n * then no screen anywhere in the platform could bring it back. The store's\n * `custom.record_restore` was there the whole time and did exactly the right\n * thing; nothing reached it, because every read door answers live rows only.\n *\n * So the archive is a WAY OF LOOKING AT THIS TABLE, like the board and the\n * dashboards, reached by the same row of buttons and named by the same\n * `?view=` parameter โ€” never a Trash area of the app somebody has to know\n * exists, and never a per-table screen. Every Table inherits it the moment it\n * is in this list.\n */\nexport const PAGE_VIEWS = [...VIEW_LAYOUTS, \"dashboards\", \"archived\"] as const;\nexport type PageView = (typeof PAGE_VIEWS)[number];\n\nexport const PAGE_VIEW_LABEL: Record<PageView, string> = {\n ...LAYOUT_LABEL,\n dashboards: \"Dashboards\",\n archived: \"Archived\",\n};\n\n/**\n * THE WORDS A LINK MAY USE. The canonical token first; the rest are what a\n * person or an agent actually types, because a link that dies on \"board\" when\n * the button says \"Kanban\" is the same dead link with better spelling.\n */\nconst PAGE_VIEW_SPELLINGS: Record<string, PageView> = {\n grid: \"grid\",\n table: \"grid\",\n rows: \"grid\",\n list: \"list\",\n kanban: \"kanban\",\n board: \"kanban\",\n pipeline: \"kanban\",\n calendar: \"calendar\",\n month: \"calendar\",\n schedule: \"calendar\",\n gallery: \"gallery\",\n cards: \"gallery\",\n timeline: \"timeline\",\n gantt: \"timeline\",\n dashboards: \"dashboards\",\n dashboard: \"dashboards\",\n charts: \"dashboards\",\n archived: \"archived\",\n archive: \"archived\",\n // The grid's row action says \"Delete\" and the store soft-deletes, so the word\n // a person types is as likely to be the one the button used as the one this\n // view is called. Both land in the same place; neither dies.\n deleted: \"archived\",\n trash: \"archived\",\n removed: \"archived\",\n};\n\n/**\n * What a `?view=` value means, or `null` when it means nothing here.\n *\n * `null` is NOT \"show the grid\": the caller is expected to say that the link\n * named something this table has no view for, because silently substituting a\n * different view is exactly the defect above wearing a parser.\n */\nexport function pageViewFromParam(raw: string | null | undefined): PageView | null {\n if (typeof raw !== \"string\") return null;\n const token = raw.trim().toLowerCase();\n if (token === \"\") return null;\n return PAGE_VIEW_SPELLINGS[token] ?? null;\n}\n\n/** The sentence a link that named a view nobody has says. Never silent. */\nexport function unknownViewLine(raw: string): string {\n return (\n `The link asked for a view called โ€œ${raw}โ€, which this table has no view for, so you are looking at ` +\n `${PAGE_VIEW_LABEL.grid}. The views this table has are ` +\n `${PAGE_VIEWS.map((v) => PAGE_VIEW_LABEL[v]).join(\", \")} โ€” pick one above and the address follows it.`\n );\n}\n\n/**\n * THE SPEC AS THE ONE DEFINITION `platform.saved_view` HOLDS โ€” the keys\n * `custom.view_declare` documents, by Field KEY. `subject` is not in it: the door\n * takes the Table as its own argument and writes `table_id` itself.\n */\nexport function viewDocument(spec: SavedViewSpec): Record<string, unknown> {\n const out: Record<string, unknown> = {\n // A layout the store does not keep yet is not sent: the view is saved as a grid (`STORE_KEPT_LAYOUTS`).\n layout: storeKeepsLayout(spec.layout) ? spec.layout : \"grid\",\n sorts: spec.sorts ?? [],\n is_default: spec.isDefault === true,\n };\n if (spec.groupField) out[\"group_field\"] = spec.groupField;\n if (spec.swimlaneField) out[\"swimlane_field\"] = spec.swimlaneField;\n if (spec.collapsedColumns && spec.collapsedColumns.length > 0) out[\"collapsed_columns\"] = spec.collapsedColumns;\n if (spec.measure) out[\"measure\"] = spec.measure;\n if (spec.dateField) out[\"date_field\"] = spec.dateField;\n if (STORE_KEEPS_TIMELINE && spec.startField) out[\"start_field\"] = spec.startField;\n if (STORE_KEEPS_TIMELINE && spec.endField) out[\"end_field\"] = spec.endField;\n if (spec.imageField) out[\"image_field\"] = spec.imageField;\n if (spec.ruleId) out[\"rule_id\"] = spec.ruleId;\n if (spec.where) out[\"where\"] = spec.where;\n if (spec.presentation && !presentationIsEmpty(spec.presentation)) {\n out[\"presentation\"] = JSON.parse(presentationDocument(spec.presentation)) as Record<string, unknown>;\n }\n return out;\n}\n\n/**\n * One row of `custom.views` back into the spec a screen and an agent both read โ€” THROUGH THE ONE\n * ACCESSOR (`viewSettings`, viewKeys.ts), so every layout sees every declared key the same way.\n */\nexport function viewFromRow(row: SavedViewRow): SavedView {\n const data = (row.definition ?? {}) as Record<string, unknown>;\n const k = viewSettings(data, row.filters && Object.keys(row.filters).length > 0 ? row.filters : null);\n const tableFromDefinition = typeof data[\"table_id\"] === \"string\" && data[\"table_id\"] !== \"\" ? (data[\"table_id\"] as string) : null;\n return {\n id: row.view_id,\n name: row.name || \"Untitled view\",\n subject: (row.table_id ?? tableFromDefinition ?? \"\") as Uuid,\n layout: k.layout,\n groupField: k.groupField,\n swimlaneField: k.swimlaneField,\n collapsedColumns: k.collapsedColumns,\n measure: k.measure,\n dateField: k.dateField,\n startField: k.startField,\n endField: k.endField,\n imageField: k.imageField,\n sorts: k.sorts,\n order: viewOrderOf(data),\n ruleId: k.ruleId,\n ...(k.filters ? { filters: k.filters } : {}),\n where: k.where,\n // The older table's own default view, carried in by the mover, IS this table's default โ€”\n // the mover wrote the row's column, which `custom.views` does not return. Read as anything\n // else, the page would seed a second \"All records\" beside it and open on that.\n isDefault: k.isDefault || k.movedFrom?.[\"kind\"] === \"default_view\",\n presentation: k.presentation,\n movedFrom: k.movedFrom,\n };\n}\n\n/**\n * SAVE ONE NEW VIEW of a Table โ€” the bar's \"New view\" and an agent's seed spec.\n * One `view_declare` with no `view_id`; the answer is the new view's id.\n */\nexport async function declareView(\n client: Pick<RecordsClient, \"viewDeclare\">,\n spec: SavedViewSpec,\n): Promise<RecordsResult<Uuid>> {\n return client.viewDeclare({\n table_id: spec.subject,\n spec: {\n name: spec.name,\n ...(spec.filters ? { filters: spec.filters } : {}),\n definition: viewDocument(spec),\n },\n });\n}\n\n/**\n * WRITE A CHANGE ONTO A SAVED VIEW, AND KEEP IT.\n *\n * `custom.view_declare` with the view's id MERGES: only the keys in `patch` are\n * written, a key cleared to null is removed, and everything the patch did not\n * name โ€” the grid's own choices, a hand-set order, where the view was moved from,\n * a colour set in another tab โ€” stays exactly as it was (S0, 2026-09-23: the door\n * used to rebuild the whole definition from what one press sent).\n *\n * There is no version to be stale against (VERIFIER-15 M4 is closed by the shape,\n * not by a retry): a saved view is a person's own arrangement of a table, and\n * their latest press on a control is the one that stands. A refusal โ€” the view\n * was removed, or belongs to a table this person cannot open โ€” is handed back for\n * the page to say through the one notice.\n */\nexport async function saveViewPatch(\n client: Pick<RecordsClient, \"viewDeclare\">,\n tableId: Uuid,\n viewId: Uuid,\n patch: Partial<SavedViewSpec>,\n): Promise<RecordsResult<Uuid>> {\n const definition = viewPatchDocument(patch);\n const renamed = patch.name !== undefined && patch.name.trim() !== \"\";\n const refiltered = patch.filters !== undefined;\n return client.viewDeclare({\n table_id: tableId,\n spec: {\n view_id: viewId,\n ...(renamed ? { name: patch.name!.trim() } : {}),\n ...(refiltered ? { filters: patch.filters ?? null } : {}),\n definition,\n },\n });\n}\n\n/**\n * A PARTIAL change to a saved view, as the definition keys the door merges. Only\n * the keys the caller actually changed are sent; a Field cleared (a board with no\n * \"Group by\") is sent as `null`, which the door removes.\n */\nexport function viewPatchDocument(patch: Partial<SavedViewSpec>): Record<string, unknown> {\n const { name: _name, subject: _subject, filters: _filters, ...keys } = patch;\n return viewSettingsPatch(keys);\n}\n\n/**\n * A LOOK AT A TABLE NEVER CHANGES HOW IT OPENS (lane VIEW-SWITCH-NOT-DESIGNATION, VERIFIER-21).\n *\n * On production the owner pressed Kanban, then Calendar, and the office member's table opened in\n * the Calendar: every layout press was saved onto the DEFAULT view, which is how the table opens\n * for everyone. The table's default view's `layout` and `isDefault` are its DESIGNATION, changed\n * only by an editor's deliberate \"Make this the default\" (`designateView`); the store refuses them\n * through `view_declare` by name. So a press on the default view carries every other key it\n * changed โ€” a board's grouping, a calendar's date, a colour โ€” and never those two.\n */\nexport function viewPatchFor(\n view: Pick<SavedView, \"isDefault\">,\n patch: Partial<SavedViewSpec>,\n): Partial<SavedViewSpec> {\n if (!view.isDefault) return patch;\n const { layout: _layout, isDefault: _isDefault, ...kept } = patch;\n return kept;\n}\n\n/** The one words for the explicit designation, with the layout it would make the default. */\nexport function makeDefaultLabel(layoutLabel: string): string {\n return `Make ${layoutLabel} the default`;\n}\n\n/**\n * MAKE THIS THE DEFAULT โ€” the ONE designation door (`custom.view_designate`, editor on the table).\n * `layout` is the layout this table should open in for everyone: one of the layouts the store keeps. The view\n * becomes the table's only default.\n */\nexport async function designateView(\n client: Pick<RecordsClient, \"viewDesignate\">,\n tableId: Uuid,\n viewId: Uuid,\n layout: string | null,\n): Promise<RecordsResult<Uuid>> {\n return client.viewDesignate({ table_id: tableId, view_id: viewId, layout });\n}\n","\"use client\";\n\n// src/viewLook.ts โ€” @ai-matrx/records-ui ยท lane VIEW-STATE-PERSONAL (VERIFIER-23 item 1, 2026-09-25)\n//\n// A LOOK AT A VIEW IS YOURS UNTIL YOU SAVE IT.\n//\n// On production the owner of a maintenance-requests table grouped its board by Status, and the\n// tenant liaison's board came up grouped by Status the next time she opened it: every pick was\n// written onto the shared view. Airtable and Notion keep the two apart โ€” how you are looking at a\n// shared view (its grouping, its date field, its sort, its filter, hidden columns, widths, the\n// layout) is YOUR look, it persists for you, and the view changes only when you press \"Save to\n// view\" (Notion's \"Save for everyone\"), save it as a new view, or \"Reset to view\".\n//\n// The store keeps one look per (person, view) through `custom.view_look_set` / `view_look_read`\n// (viewer on the table, never written onto the view row). This file is the look's shape on this\n// side: the keys a look may hold, how it reads back, how it lays over the view, and what \"Save to\n// view\" sends.\n\nimport type { RecordsResult, Uuid } from \"@ai-matrx/records\";\nimport type { RecordsClient } from \"@ai-matrx/records/core\";\n\nimport { parseGridPresentation } from \"./presentation\";\nimport { viewSettings, viewSettingsPatch } from \"./viewKeys\";\nimport { VIEW_LAYOUTS, viewPatchFor, type SavedView, type SavedViewSpec, type ViewLayout } from \"./views\";\n\n/**\n * WHAT A LOOK MAY HOLD โ€” how a person looks, never what the view is: not its name, not whether it\n * is the default (`view_designate`), not its Rule, not the flat filters the digests read, not the\n * order the store writes. The store refuses anything else by name.\n */\nexport type ViewLook = Partial<\n Pick<\n SavedViewSpec,\n | \"layout\"\n | \"groupField\"\n | \"swimlaneField\"\n | \"collapsedColumns\"\n | \"measure\"\n | \"dateField\"\n | \"startField\"\n | \"endField\"\n | \"imageField\"\n | \"sorts\"\n | \"where\"\n | \"presentation\"\n >\n>;\n\nconst LOOK_KEYS: ReadonlyArray<keyof ViewLook> = [\n \"layout\",\n \"groupField\",\n \"swimlaneField\",\n \"collapsedColumns\",\n \"measure\",\n \"dateField\",\n \"startField\",\n \"endField\",\n \"imageField\",\n \"sorts\",\n \"where\",\n \"presentation\",\n];\n\n/** Only the look's own keys of a press โ€” a name, a default, a Rule or an order is never a look. */\nexport function lookPart(patch: Partial<SavedViewSpec>): ViewLook {\n const out: ViewLook = {};\n for (const key of LOOK_KEYS) {\n if (patch[key] !== undefined) (out as Record<string, unknown>)[key] = patch[key];\n }\n return out;\n}\n\n/** The look as the definition keys the store keeps (`view_look_set`); a cleared key is null. */\nexport function lookDocument(look: ViewLook): Record<string, unknown> {\n return viewSettingsPatch(look);\n}\n\n/** A kept look back into the spec's words. Only the keys it holds; a null key is \"none, for me\". */\nexport function lookFromDocument(doc: Record<string, unknown> | null | undefined): ViewLook {\n const data = doc ?? {};\n const read = viewSettings(data);\n const out: ViewLook = {};\n const has = (key: string) => Object.prototype.hasOwnProperty.call(data, key);\n if (has(\"layout\") && (VIEW_LAYOUTS as readonly string[]).includes(String(data[\"layout\"]))) {\n out.layout = data[\"layout\"] as ViewLayout;\n }\n if (has(\"group_field\")) out.groupField = read.groupField;\n if (has(\"swimlane_field\")) out.swimlaneField = read.swimlaneField;\n if (has(\"collapsed_columns\")) out.collapsedColumns = read.collapsedColumns;\n if (has(\"measure\")) out.measure = read.measure;\n if (has(\"date_field\")) out.dateField = read.dateField;\n if (has(\"start_field\")) out.startField = read.startField;\n if (has(\"end_field\")) out.endField = read.endField;\n if (has(\"image_field\")) out.imageField = read.imageField;\n if (has(\"sorts\")) out.sorts = read.sorts;\n if (has(\"where\")) out.where = read.where;\n if (has(\"presentation\")) out.presentation = parseGridPresentation(data[\"presentation\"]);\n return out;\n}\n\n/** THE VIEW AS THIS PERSON SEES IT: the saved view, with her look laid over it. */\nexport function applyLook(view: SavedView, look: ViewLook | null | undefined): SavedView {\n if (!look) return view;\n return { ...view, ...look } as SavedView;\n}\n\n/** Whether the look shows anything the view does not โ€” which is when \"Save to view\" means something. */\nexport function lookDiffers(view: SavedView, look: ViewLook | null | undefined): boolean {\n if (!look) return false;\n const shown = applyLook(view, look);\n return LOOK_KEYS.some(\n (key) => look[key] !== undefined && JSON.stringify(shown[key] ?? null) !== JSON.stringify(view[key] ?? null),\n );\n}\n\n/**\n * WHAT \"SAVE TO VIEW\" SENDS, and what stays the person's own afterwards. The default view's layout\n * is the table's designation (`viewPatchFor`): it is never saved here โ€” \"Make โ€ฆ the default\" is โ€”\n * so a look's layout on the default view stays her look.\n */\nexport function saveToViewPlan(view: SavedView, look: ViewLook): { patch: Partial<SavedViewSpec>; left: ViewLook | null } {\n const patch = viewPatchFor(view, look);\n const left: ViewLook = {};\n if (look.layout !== undefined && patch.layout === undefined && look.layout !== view.layout) left.layout = look.layout;\n return { patch, left: Object.keys(left).length > 0 ? left : null };\n}\n\n/** The caller's own looks at every view of a table, by view id. */\nexport async function readLooks(\n client: Pick<RecordsClient, \"viewLookRead\">,\n tableId: Uuid,\n): Promise<RecordsResult<Record<string, ViewLook>>> {\n const answered = await client.viewLookRead({ table_id: tableId });\n if (!answered.ok) return answered;\n const out: Record<string, ViewLook> = {};\n for (const [viewId, row] of Object.entries(answered.data ?? {})) {\n out[viewId] = lookFromDocument(row?.look ?? null);\n }\n return { ok: true, data: out };\n}\n\n/** Keep the whole look (null: \"Reset to view\"). The shared view is never written. */\nexport async function keepLook(\n client: Pick<RecordsClient, \"viewLookSet\">,\n tableId: Uuid,\n viewId: Uuid,\n look: ViewLook | null,\n): Promise<RecordsResult<Record<string, unknown> | null>> {\n const doc = look ? lookDocument(look) : null;\n return client.viewLookSet({ table_id: tableId, view_id: viewId, look: doc && Object.keys(doc).length > 0 ? doc : null });\n}\n\n/** The words of the look bar. One sentence, then the doors. */\nexport const LOOK_WORDS = {\n /** The reason, for a tooltip or a screen reader โ€” never a line on the page (interface-text). */\n yours: \"You changed how this view looks. Only you see it.\",\n /** The visible label for the same fact. */\n yoursLabel: \"Only you see this\",\n saveToView: \"Save to view\",\n saveAsNew: \"Save as new view\",\n reset: \"Reset to view\",\n /** The one button your look's doors fold into when the toolbar row has no room for three. */\n folded: \"Your look\",\n newName: \"Name for the new view\",\n} as const;\n","// src/core/errors.ts โ€” @ai-matrx/entity-data/core\n//\n// THE ERROR MAP (PHASE-B-APPLETS ยง4). The entity doors raise their own `MX0xx` class; this turns it\n// into the `RecordsError` every other door of the records package answers, carrying THE STORE'S\n// SENTENCE VERBATIM (message and hint are never rewritten here). Anything that is not an `MX` code\n// (a timeout, a dropped connection, a revoked schema) goes through records' own `mapPgError`.\n\nimport { mapPgError } from \"@ai-matrx/records/core\";\nimport type { PgError, RecordsError, RecordsErrorCode } from \"@ai-matrx/records\";\n\n/**\n * Every MX code a read door raises, and the engine code a screen branches on. A code the doors learn\n * later and this map lacks comes back `internal` with `unmapped: true` (never silently \"handled\").\n */\nexport const ENTITY_REFUSALS: Readonly<Record<string, RecordsErrorCode>> = {\n /** conflict_version: somebody else's write won. */\n MX001: \"stale_write\",\n /** organization_required: the call needs an organization and carried none. */\n MX002: \"organization_required\",\n /** forbidden. */\n MX003: \"door\",\n /** not_found (also: not visible to this person). */\n MX004: \"not_found\",\n /** validation_refused (detail.reason required / bad_value / ambiguous_word / constraint; `not_supported` is read apart below). */\n MX005: \"invalid_argument\",\n /** system_column: a write named a column the store owns. */\n MX008: \"invalid_argument\",\n /** unknown_identifier: a column that is not there or not usable here. */\n MX012: \"invalid_argument\",\n /** cursor_invalid: the page cursor belongs to another list; the engine restarts the page. */\n MX013: \"invalid_argument\",\n /** view_not_offered. */\n MX015: \"invalid_argument\",\n /** invalid_argument: a parameter the door refuses by name. */\n MX016: \"invalid_argument\",\n};\n\nexport function mapEntityError(error: PgError, where: string): RecordsError {\n const mapped = mapPgError(error, where);\n const state = mapped.sqlstate;\n if (state && /^MX\\d{3}$/.test(state)) {\n const code = ENTITY_REFUSALS[state];\n if (code) {\n // MX005 that says the store does not offer this yet is a different question for a screen than a bad value.\n const reason = (mapped.detail as { reason?: unknown } | undefined)?.reason;\n const known: RecordsError = { ...mapped, code: state === \"MX005\" && reason === \"not_supported\" ? \"not_supported\" : code };\n delete known.unmapped;\n delete known.diagnostic;\n return known;\n }\n }\n return mapped;\n}\n\n/** A refusal this package itself raises (an alias nobody declared, a write not offered yet). */\nexport function refusal(code: RecordsErrorCode, message: string, hint?: string): { ok: false; error: RecordsError } {\n return { ok: false, error: { code, message, ...(hint ? { hint } : {}) } };\n}\n","// src/core/client.ts โ€” @ai-matrx/entity-data/core\n//\n// THE THIN TYPED CLIENT over the live INVOKER doors in `platform`: reads (`entity_list_scoped`,\n// `entity_get`, `entity_columns`) and, since 0.3, writes (`entity_insert`, `entity_update`,\n// `entity_archive`, `entity_unarchive`). Every call answers a `RecordsResult` and\n// never throws; every refusal is the store's own sentence (see `errors.ts`). The caller's seat is the\n// one on the supabase client handed in โ€” nothing here widens or narrows what the store lets her see.\n\nimport { asRecordsDataSource } from \"@ai-matrx/records/core\";\nimport type { RecordsDataSource, RecordsResult, Uuid } from \"@ai-matrx/records\";\nimport { mapEntityError } from \"./errors\";\nimport type { EntityColumnsAnswer, EntityGetAnswer, EntityListAnswer, EntityWireQuery, EntityWriteAnswer } from \"./types\";\n\n/**\n * THE ECHO HEADER (U6). The entity doors take no `_op_id` argument: the writer's op id rides the request\n * as this header, PostgREST hands it to `request.headers`, and `platform._entity_org_notice` puts it on\n * the notice as `op_id`, so the browser that wrote drops its own echo (`@ai-matrx/records` `isOwnOp`).\n */\nexport const OP_ID_HEADER = \"x-matrx-op-id\";\n\n/** What a write takes beyond its arguments: the op id minted for it (`mintOpId`). */\nexport interface EntityWriteOptions {\n opId?: string;\n}\n\n/** The schema every entity door lives in. */\nexport const ENTITY_DOOR_SCHEMA = \"platform\";\n\nexport interface CreateEntityClientOptions {\n /** A supabase-js v2 client for the signed-in person, or anything with `rpc(fn, args, {schema})`. */\n dataSource?: unknown;\n /** Same thing, spelled the way the plan names it. Give one of the two. */\n supabase?: unknown;\n}\n\nexport interface EntityListRequest extends EntityWireQuery {\n token: string;\n /** Keyset cursor from the previous answer's `next_after`. */\n after?: string | null;\n /** Ask the door to skip the count. Default: counted. */\n withTotal?: boolean;\n archived?: \"active\" | \"archived\" | \"all\";\n}\n\nexport interface EntityClient {\n list(request: EntityListRequest): Promise<RecordsResult<EntityListAnswer>>;\n get(request: { token: string; ids: readonly Uuid[] }): Promise<RecordsResult<EntityGetAnswer>>;\n /** `organizationId` null reads the custom fields of every organization the person belongs to. */\n columns(request: { token: string; organizationId?: Uuid | null }): Promise<RecordsResult<EntityColumnsAnswer>>;\n /** One `entity_data` knob as `platform.knob_resolve` answers it for an organization (live updates read three). */\n knob(request: { key: string; organizationId?: Uuid | null }): Promise<RecordsResult<unknown>>;\n /** `platform.entity_insert`: rows into the organization the host names (never guessed). */\n insert(request: { token: string; organizationId: Uuid; rows: readonly Record<string, unknown>[] } & EntityWriteOptions): Promise<RecordsResult<EntityWriteAnswer>>;\n /** `platform.entity_update`: one row, refused as stale (`MX001`) unless `baseVersion` is still current. */\n update(request: { token: string; id: Uuid; baseVersion: number; patch: Record<string, unknown> } & EntityWriteOptions): Promise<RecordsResult<EntityWriteAnswer>>;\n /** `platform.entity_archive` (on these types, the trash). */\n archive(request: { token: string; ids: readonly Uuid[] } & EntityWriteOptions): Promise<RecordsResult<EntityWriteAnswer>>;\n /** `platform.entity_unarchive`. */\n unarchive(request: { token: string; ids: readonly Uuid[] } & EntityWriteOptions): Promise<RecordsResult<EntityWriteAnswer>>;\n /** The organizations the person belongs to, as row security counts them (`iam.my_orgs()`: archived ones closed). */\n memberOrganizations(): Promise<RecordsResult<Uuid[]>>;\n}\n\nexport function createEntityClient(options: CreateEntityClientOptions): EntityClient {\n const source: RecordsDataSource = asRecordsDataSource(options.dataSource ?? options.supabase);\n\n let warnedHeaderless = false;\n\n /**\n * A per-call header rides the request builder supabase-js hands back (`PostgrestBuilder.setHeader`, set\n * before the builder is awaited). `@ai-matrx/records`' adapter returns that builder unwrapped, so the\n * header reaches PostgREST on this one call only โ€” never on the client every other caller shares.\n * A data seam whose answer has no `setHeader` (a host's own adapter, a test double) cannot carry it:\n * the write still lands, the writer re-reads its own echo once, and the console says why.\n */\n function withHeaders(pending: PromiseLike<unknown>, headers: Record<string, string> | undefined): void {\n if (!headers) return;\n const builder = pending as { setHeader?: (name: string, value: string) => unknown };\n if (typeof builder.setHeader === \"function\") {\n for (const [name, value] of Object.entries(headers)) builder.setHeader(name, value);\n return;\n }\n if (!warnedHeaderless) {\n warnedHeaderless = true;\n console.warn(\n `[@ai-matrx/entity-data] This data source cannot carry a per-call header (${Object.keys(headers).join(\", \")}), so this browser re-reads the notice of its own writes once.`,\n );\n }\n }\n\n async function call<T>(\n fn: string,\n args: Record<string, unknown>,\n schema: string = ENTITY_DOOR_SCHEMA,\n headers?: Record<string, string>,\n ): Promise<RecordsResult<T>> {\n try {\n const pending = source.rpc(fn, args, { schema });\n withHeaders(pending, headers);\n const answer = await pending;\n if (answer.error) return { ok: false, error: mapEntityError(answer.error, `${schema}.${fn}`) };\n return { ok: true, data: answer.data as T };\n } catch (thrown) {\n // supabase-js answers failures; a throw here is a transport that never reached the store.\n return {\n ok: false,\n error: mapEntityError({ code: \"\", message: thrown instanceof Error ? thrown.message : String(thrown) }, `${schema}.${fn}`),\n };\n }\n }\n\n return {\n list(request) {\n const args: Record<string, unknown> = {\n p_entity_type: request.token,\n p_scope: request.scope ?? null,\n p_filter: request.filter ?? {},\n p_search: request.search ?? null,\n p_sort: request.sort ?? null,\n p_archived: request.archived ?? \"active\",\n p_page_size: request.pageSize ?? null,\n p_after: request.after ?? null,\n p_with_total: request.withTotal ?? true,\n };\n return call<EntityListAnswer>(\"entity_list_scoped\", args);\n },\n get(request) {\n return call<EntityGetAnswer>(\"entity_get\", { p_entity_type: request.token, p_ids: [...request.ids] });\n },\n columns(request) {\n return call<EntityColumnsAnswer>(\"entity_columns\", { p_organization_id: request.organizationId ?? null, p_token: request.token });\n },\n insert(request) {\n return call<EntityWriteAnswer>(\n \"entity_insert\",\n { p_entity_type: request.token, p_organization_id: request.organizationId, p_rows: request.rows.map((r) => ({ ...r })) },\n ENTITY_DOOR_SCHEMA,\n opHeader(request.opId),\n );\n },\n update(request) {\n return call<EntityWriteAnswer>(\n \"entity_update\",\n { p_entity_type: request.token, p_id: request.id, p_base_version: request.baseVersion, p_patch: request.patch },\n ENTITY_DOOR_SCHEMA,\n opHeader(request.opId),\n );\n },\n archive(request) {\n return call<EntityWriteAnswer>(\"entity_archive\", { p_entity_type: request.token, p_ids: [...request.ids] }, ENTITY_DOOR_SCHEMA, opHeader(request.opId));\n },\n unarchive(request) {\n return call<EntityWriteAnswer>(\"entity_unarchive\", { p_entity_type: request.token, p_ids: [...request.ids] }, ENTITY_DOOR_SCHEMA, opHeader(request.opId));\n },\n knob(request) {\n return call<unknown>(\"knob_resolve\", { p_feature: \"entity_data\", p_key: request.key, p_organization_id: request.organizationId ?? null });\n },\n async memberOrganizations() {\n const answer = await call<unknown>(\"my_orgs\", {}, \"iam\");\n if (!answer.ok) return answer;\n const rows = Array.isArray(answer.data) ? answer.data : [];\n // A SETOF uuid comes back as bare strings; a row shape (`{my_orgs}`) is read the same way.\n const ids = rows\n .map((row) => (typeof row === \"string\" ? row : row && typeof row === \"object\" ? Object.values(row as Record<string, unknown>)[0] : null))\n .filter((id): id is string => typeof id === \"string\");\n return { ok: true, data: ids };\n },\n };\n}\n\nfunction opHeader(opId: string | undefined): Record<string, string> | undefined {\n return opId ? { [OP_ID_HEADER]: opId } : undefined;\n}\n","// src/core/realtime.ts โ€” @ai-matrx/entity-data/core\n//\n// LIVE NOTICES FOR PLATFORM LISTS (PHASE-B-APPLETS R4 / U6).\n//\n// The database announces every statement on the five Table-API tables (party, crm_deal, project, task,\n// hr_employee) with ONE broadcast per organization it touched, on the PRIVATE topic\n// `mx:entity-org:<organization>:<token>`, event `entity_bump`, payload\n// `{id, entity_type, organization_id, op, op_id, ids | null, at}` โ€” ids, never values. It is written by\n// `platform.entity_org_notice_stmt` and admitted by `platform.entity_org_topic_admits` through the one RLS\n// policy on `realtime.messages`: a signed-in member of that organization (her own memberships, never the\n// admin route) whose role may read the table. The subscriber re-reads the ids through the read door, so\n// row security decides what she sees at read time.\n//\n// This file joins those topics through `@ai-matrx/realtime` โ€” the ONE realtime layer, never a hand-written\n// `.channel(` โ€” so the socket, `setAuth` before a private join, backoff, dedupe and wake backfill are the\n// manager's.\n//\n// ยท ONE CHANNEL PER (organization, token), HOWEVER MANY READERS โ€” opened by the first, closed by the last.\n// ยท BURSTS COALESCED per reader: the first notice starts a `batchMs` timer, later ones join it, and\n// `maxWaitMs` caps how long a steady stream can hold a re-read back (knobs entity_data.realtime_batch_ms\n// / realtime_max_wait_ms).\n// ยท `ids: null` PASSES THROUGH โ€” above knob entity_data.realtime_max_ids the database stops naming ids.\n// ยท ECHO DROPPED โ€” a write that carried header `x-matrx-op-id` comes back with that `op_id`; a notice whose\n// op id this client minted (`@ai-matrx/records` `isOwnOp`) is our own write and is dropped.\n// ยท NO REPLAY ON A WAKE OR RECONNECT โ€” the answer is `ids: null` (read again).\n\nimport {\n defineChannelNamespace,\n subscribeToRealtimeManager,\n type ChannelSpec,\n type RealtimeManager,\n} from \"@ai-matrx/realtime\";\nimport { isOwnOp } from \"@ai-matrx/records\";\n\nimport type { EntityClient } from \"./client\";\n\n/** The topic is a CONTRACT WITH THE DATABASE (a trigger writes it, an RLS policy parses it): built once, here. */\nexport const entityOrgChannel = defineChannelNamespace({\n namespace: \"entity-org\",\n foreignTopic: \"mx:entity-org\",\n parts: [\"organizationId\", \"token\"],\n description:\n \"One organization's changes to one platform type (contacts, deals, projects, tasks, employees). Carries NOTICES written by platform.entity_org_notice_stmt โ€” which ids moved, never a value. Admitted by the one RLS policy on realtime.messages to members of that organization.\",\n});\n\n/** The broadcast event the database sends on an organization topic. */\nexport const ENTITY_NOTICE_EVENT = \"entity_bump\";\n\n/** Starting values of the knobs the runtime coalesces with (entity_data.realtime_batch_ms / _max_wait_ms). */\nexport const DEFAULT_BATCH_MS = 150;\nexport const DEFAULT_MAX_WAIT_MS = 1000;\n/** Starting value of knob entity_data.realtime_max_org_topics. */\nexport const DEFAULT_MAX_ORG_TOPICS = 20;\n\nexport interface EntityNotice {\n id?: string;\n entity_type?: string;\n organization_id?: string;\n op?: \"created\" | \"updated\" | \"deleted\";\n /** The client operation that caused it (header `x-matrx-op-id`), or null for any other writer. */\n op_id?: string | null;\n /** `null` MEANS \"more changed than the database lists: re-read\". */\n ids?: string[] | null;\n at?: string;\n}\n\n/** A redelivered notice is the same notice: the database mints one id per send. */\nexport function entityNoticeKey(notice: EntityNotice): string | undefined {\n return notice.id ?? (notice.at ? `${notice.organization_id ?? \"\"}:${notice.entity_type ?? \"\"}:${notice.at}` : undefined);\n}\n\nexport function entityOrgTopic(organizationId: string, token: string): string {\n return entityOrgChannel.topic({ organizationId, token });\n}\n\n/** The channel spec for one (organization, token): private, echo-guarded, deduped, backfilled. */\nexport function entityOrgChannelSpec(\n organizationId: string,\n token: string,\n onNotice: (notice: EntityNotice) => void,\n onBackfill: () => void,\n): ChannelSpec {\n return {\n topic: entityOrgTopic(organizationId, token),\n // Database Broadcast is authorized by RLS on realtime.messages; the manager awaits setAuth() first.\n private: true,\n // The sender is Postgres: no Matrx envelope. Our own write is known by the op id it carried.\n wire: { mode: \"raw\", isOwnMessage: (payload) => isOwnOp((payload as EntityNotice | null)?.op_id) },\n eventKey: (_source, payload) => entityNoticeKey((payload ?? {}) as EntityNotice),\n broadcast: [\n {\n event: ENTITY_NOTICE_EVENT,\n onMessage: (message) => {\n const notice = (message.data ?? {}) as EntityNotice;\n if (isOwnOp(notice.op_id)) return; // belt and braces: the manager's test ran first\n onNotice(notice);\n },\n },\n ],\n // Realtime has no replay: a channel back from a gap re-reads.\n onBackfill: () => onBackfill(),\n };\n}\n\n/**\n * Which organization topics to join, in order, until `max`: the active organization, the alias's `org`\n * filter, then the organizations of held rows by number of held rows (descending; organization id\n * ascending on ties), then the person's remaining memberships by id. Only memberships are kept when they\n * are known โ€” any other topic is refused by the database anyway. Deterministic for the same input.\n */\nexport function orderOrganizationTopics(input: {\n active?: string | null;\n filter?: string | null;\n held?: ReadonlyMap<string, number>;\n members?: readonly string[] | null;\n max: number;\n}): string[] {\n const members = input.members ? new Set(input.members) : null;\n const out: string[] = [];\n const push = (org: string | null | undefined) => {\n if (!org || out.includes(org)) return;\n if (members && !members.has(org)) return;\n out.push(org);\n };\n push(input.active);\n push(input.filter);\n const held = [...(input.held ?? new Map<string, number>()).entries()].sort((a, b) => b[1] - a[1] || (a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0));\n for (const [org] of held) push(org);\n for (const org of [...(input.members ?? [])].sort()) push(org);\n return out.slice(0, Math.max(0, input.max));\n}\n\nexport interface EntityNoticeSink {\n /** `ids` to re-read, or `null` to re-read everything this reader shows. */\n rows(ids: string[] | null): void;\n}\n\nexport interface EntityRealtime {\n /** Hear `token` across `organizationIds`; the sink gets one coalesced call per burst. Returns the stop. */\n subscribe(target: { token: string; organizationIds: readonly string[] }, sink: EntityNoticeSink): () => void;\n}\n\nexport interface CreateEntityRealtimeOptions {\n /** The host's realtime manager. Left out, the AMBIENT manager the app mounted โ€” never a socket of our own. */\n manager?: RealtimeManager;\n /** Knob entity_data.realtime_batch_ms (starting value 150). */\n batchMs?: number;\n /** Knob entity_data.realtime_max_wait_ms (starting value 1000). */\n maxWaitMs?: number;\n}\n\ninterface OrgListener {\n notice(notice: EntityNotice): void;\n backfill(): void;\n}\n\ninterface OrgChannel {\n listeners: Set<OrgListener>;\n stop: (() => void) | null;\n}\n\n/** The live-notice layer for platform lists, bound to a realtime manager. One per host is enough. */\nexport function createEntityRealtime(options: CreateEntityRealtimeOptions = {}): EntityRealtime {\n const batchMs = options.batchMs ?? DEFAULT_BATCH_MS;\n const maxWaitMs = options.maxWaitMs ?? DEFAULT_MAX_WAIT_MS;\n const channels = new Map<string, OrgChannel>();\n\n const open = (organizationId: string, token: string): OrgChannel => {\n const channel: OrgChannel = { listeners: new Set(), stop: null };\n const spec = entityOrgChannelSpec(\n organizationId,\n token,\n (notice) => {\n for (const listener of channel.listeners) listener.notice(notice);\n },\n () => {\n for (const listener of channel.listeners) listener.backfill();\n },\n );\n if (options.manager) {\n const handle = options.manager.open(spec);\n channel.stop = () => handle.close();\n } else {\n channel.stop = subscribeToRealtimeManager(() => spec);\n }\n return channel;\n };\n\n return {\n subscribe({ token, organizationIds }, sink) {\n let timer: ReturnType<typeof setTimeout> | null = null;\n let firstAt = 0;\n let pending = new Set<string>();\n let wholeList = false;\n\n const flush = () => {\n if (timer !== null) clearTimeout(timer);\n timer = null;\n const ids = [...pending];\n const all = wholeList;\n pending = new Set();\n wholeList = false;\n // THE IDS ARE USED AS IDS. `null` costs a whole re-read; three ids re-read three rows.\n if (all) sink.rows(null);\n else if (ids.length > 0) sink.rows(ids);\n };\n const nudge = () => {\n const now = Date.now();\n if (timer === null) firstAt = now;\n else clearTimeout(timer);\n // Wait `batchMs` after the latest notice, never past `maxWaitMs` after the first one.\n const wait = Math.max(0, Math.min(batchMs, firstAt + maxWaitMs - now));\n timer = setTimeout(flush, wait);\n };\n\n const listener: OrgListener = {\n notice(notice) {\n if (notice.entity_type && notice.entity_type !== token) return;\n if (notice.ids == null) wholeList = true;\n else for (const id of notice.ids) pending.add(id);\n nudge();\n },\n backfill() {\n wholeList = true;\n flush();\n },\n };\n\n const keys = [...new Set(organizationIds)].map((org) => {\n const key = entityOrgTopic(org, token);\n const channel = channels.get(key) ?? open(org, token);\n channels.set(key, channel);\n channel.listeners.add(listener);\n return key;\n });\n\n return () => {\n if (timer !== null) clearTimeout(timer);\n for (const key of keys) {\n const channel = channels.get(key);\n if (!channel) continue;\n channel.listeners.delete(listener);\n if (channel.listeners.size > 0) continue;\n // The last reader of this (organization, token) left: its one subscription goes with it.\n channel.stop?.();\n channels.delete(key);\n }\n };\n },\n };\n}\n\n/** The three live-update knobs as the store answers them for an organization (starting values when unset). */\nexport async function readEntityRealtimeKnobs(\n client: Pick<EntityClient, \"knob\">,\n organizationId?: string | null,\n): Promise<{ batchMs: number; maxWaitMs: number; maxOrgTopics: number }> {\n const read = async (key: string, fallback: number) => {\n const answer = await client.knob({ key, organizationId: organizationId ?? null });\n const value = answer.ok ? Number(answer.data) : Number.NaN;\n if (!Number.isFinite(value)) {\n // SAID OUT LOUD: the starting value stands in for a knob the store did not answer.\n console.warn(`[@ai-matrx/entity-data] knob entity_data.${key} did not resolve; using its starting value ${fallback}.`);\n return fallback;\n }\n return value;\n };\n const [batchMs, maxWaitMs, maxOrgTopics] = await Promise.all([\n read(\"realtime_batch_ms\", DEFAULT_BATCH_MS),\n read(\"realtime_max_wait_ms\", DEFAULT_MAX_WAIT_MS),\n read(\"realtime_max_org_topics\", DEFAULT_MAX_ORG_TOPICS),\n ]);\n return { batchMs, maxWaitMs, maxOrgTopics };\n}\n","// src/core/wire.ts โ€” @ai-matrx/entity-data/core\n//\n// `toWire`: the Applets `Query` (ยง4) โ†’ the door's arguments (scope, filter, sort). Pure.\n//\n// What the doors do not offer yet is NOT filtered, reordered or emulated here: the clause is sent as\n// asked and the door refuses it BY NAME (`\"contains\" filters are not offered yet.`), so the day the\n// door learns it the port already speaks it, and until then the person reads the store's sentence.\n// A browser-side filter would silently drop everything the browser was not shown.\n\nimport type { Query, WhereClause } from \"@ai-matrx/records/data\";\nimport type { EntityWirePredicate, EntityWireQuery, EntityWireSort } from \"./types\";\n\n/** The runtime's underscore names for the three identity columns the doors call `id`, `version`, `organization_id`. */\nconst IDENTITY: Readonly<Record<string, string>> = {\n _id: \"id\",\n _version: \"version\",\n _organization_id: \"organization_id\",\n};\n\nexport const CUSTOM_PREFIX = \"_custom.\";\n\n/**\n * Applets column key โ†’ the door's identifier. A custom field is `_custom.<key>` (the key as\n * `columns()` answered it โ€” the same plain key for every viewer; `<key>@<organization_id>` is accepted, never produced).\n */\nexport function wireColumn(column: string, customKeys: ReadonlySet<string>): string {\n const identity = IDENTITY[column];\n if (identity) return identity;\n if (column.startsWith(CUSTOM_PREFIX)) return column;\n return customKeys.has(column) ? `${CUSTOM_PREFIX}${column}` : column;\n}\n\nfunction predicate(clause: WhereClause, customKeys: ReadonlySet<string>): EntityWirePredicate {\n const column = wireColumn(clause.column, customKeys);\n const v = clause.value;\n switch (clause.op) {\n case \"eq\":\n // The store reads \"equals nothing\" as empty (the table port's rule, so both kinds of source agree).\n return v === null || v === undefined ? { column, op: \"empty\" } : { column, op: \"eq\", value: v };\n case \"neq\":\n return v === null || v === undefined ? { column, op: \"empty\", negated: true } : { column, op: \"neq\", value: v };\n case \"lt\":\n case \"lte\":\n case \"gt\":\n case \"gte\":\n return { column, op: clause.op, value: v };\n case \"in\":\n return { column, op: \"in\", values: Array.isArray(v) ? v : [] };\n case \"empty\":\n return v === false ? { column, op: \"empty\", negated: true } : { column, op: \"empty\" };\n case \"contains\":\n return { column, op: \"contains\", value: v };\n default:\n // An op this version does not know is still SENT, so the door names it, instead of vanishing here.\n return { column, op: String((clause as WhereClause).op), value: v };\n }\n}\n\nexport function toWire(query: Query | undefined, customKeys: ReadonlySet<string> = new Set()): EntityWireQuery {\n const q = query ?? {};\n const out: EntityWireQuery = {};\n const scope: NonNullable<EntityWireQuery[\"scope\"]> = {};\n if (q.lane) scope.kind = q.lane; // absent = the type's own landing lane, decided by the door\n if (q.org && q.org !== \"all\") scope.organization_id = q.org; // a filter, never a lock\n if (scope.kind || scope.organization_id) out.scope = scope;\n if (q.where && q.where.length > 0) out.filter = { all: q.where.map((c) => predicate(c, customKeys)) };\n const search = q.search?.trim();\n if (search) out.search = search;\n if (q.sort && q.sort.length > 0) {\n out.sort = q.sort.map<EntityWireSort>((s) => ({\n column: wireColumn(s.column, customKeys),\n dir: s.direction === \"desc\" ? \"desc\" : \"asc\",\n nulls: \"last\",\n }));\n }\n if (typeof q.pageSize === \"number\" && Number.isFinite(q.pageSize)) out.pageSize = Math.max(1, Math.floor(q.pageSize));\n return out;\n}\n","// src/port/rows.ts โ€” @ai-matrx/entity-data/port\n//\n// ROW AND COLUMN SHAPING (Applets CONTRACTS ยง0): the door's wire row โ†’ `Row`, its columns โ†’ `Column`.\n// Pure; every case is pinned against recorded door answers (`__fixtures__`).\n\nimport type { Column, EntityRef, Row } from \"@ai-matrx/records/data\";\nimport type { EntityColumnWire, EntityWireRow } from \"../core/types\";\n\n/** The door's identity columns become the runtime's own underscore keys on a Row; they are never plain columns. */\nconst IDENTITY_COLUMNS = new Set([\"id\", \"version\", \"organization_id\"]);\n/** The compiler's own presentation markers; not data. */\nconst PRESENTATION_KEYS = new Set([\"_state\", \"_k\"]);\n\nfunction isRefList(value: unknown): value is { token: string; id: string; label?: unknown }[] {\n return (\n Array.isArray(value) &&\n value.length > 0 &&\n value.every((item) => item !== null && typeof item === \"object\" && typeof (item as { token?: unknown }).token === \"string\" && typeof (item as { id?: unknown }).id === \"string\")\n );\n}\n\n/** Every link reads as `EntityRef[]` โ€” `{token, id, label}`, the label null when the store had no words for it. */\nexport function toRefs(value: unknown): EntityRef[] {\n if (!isRefList(value)) return [];\n return value.map<EntityRef>((item) => ({ token: item.token, id: item.id, label: typeof item.label === \"string\" ? item.label : null }));\n}\n\nexport function toRow(wire: EntityWireRow): Row {\n const row: Row = {\n _id: wire.id,\n _version: wire.version,\n _organization_id: wire.organization_id,\n _custom: wire._custom && typeof wire._custom === \"object\" ? { ...wire._custom } : {},\n };\n for (const [key, value] of Object.entries(wire)) {\n if (IDENTITY_COLUMNS.has(key) || PRESENTATION_KEYS.has(key) || key === \"_custom\") continue;\n row[key] = isRefList(value) ? toRefs(value) : value === undefined ? null : value;\n }\n return row;\n}\n\nexport function toColumn(wire: EntityColumnWire): Column {\n const column: Column = {\n key: wire.key,\n label: wire.label || wire.key,\n type: wire.type,\n origin: wire.origin === \"custom\" ? \"custom\" : \"platform\",\n required: Boolean(wire.required),\n readOnly: Boolean(wire.readOnly),\n };\n if (wire.choices && wire.choices.length > 0) column.choices = [...wire.choices];\n if (wire.linksTo && wire.linksTo.length > 0) column.linksTo = [...wire.linksTo];\n return column;\n}\n\n/** The columns a person sees: the door's own list minus the identity columns a Row carries as `_id` / `_version` / `_organization_id`. */\nexport function toColumns(wire: readonly EntityColumnWire[]): Column[] {\n return wire.filter((c) => !IDENTITY_COLUMNS.has(c.key)).map(toColumn);\n}\n","// src/port/port.ts โ€” @ai-matrx/entity-data/port\n//\n// THE HOST'S DATAPORT FOR ENTITY ALIASES (Applets CONTRACTS ยง4; PHASE-B-APPLETS U5, read-only).\n//\n// One port per mounted Applet, scoped to the aliases its record declares: `{kind:\"entity\", token}`.\n// Every call goes through the store's entity doors under the signed-in person's seat โ€” the store\n// decides what she may see; nothing here widens or narrows it.\n//\n// ยท sources โ€” canCreate / canUpdate are `entity_columns`' own answer for the person.\n// ยท columns โ€” live: platform columns plus the organizations' custom fields (`origin`), the identity\n// columns left to `_id` / `_version` / `_organization_id`.\n// ยท list โ€” KEYSET: the cursor is the door's `next_after`; the lane defaults to the type's own\n// landing lane (decided by the door, never here); `org` is a filter, never a lock.\n// ยท get โ€” by id, through the same door every list row opens through. Every id asked in the same\n// tick (the engine re-reading the rows a notice named) is ONE `entity_get(ids)`.\n// ยท create โ€” `entity_insert` into the organization the HOST names (`activeOrganizationId()`), never\n// guessed; with none, refused `organization_required` before anything is sent.\n// ยท update โ€” `entity_update` with the `baseVersion` the person saw; a link (`EntityRef[]`) goes back\n// as its id, `_custom` as `{key: value}`. Stale โ†’ `stale_write` with the store's sentence.\n// ยท archive / restore โ€” `entity_archive` / `entity_unarchive` (on these types, the trash).\n// Every write mints an op id (`mintOpId`) and sends it as header `x-matrx-op-id`, so the\n// notice of the writer's own change comes back marked and is dropped.\n// ยท subscribe โ€” LIVE (U6): `rows:<alias>` hears the organization topics `mx:entity-org:<org>:<token>`\n// through the host's `realtime` (core/realtime.ts). An alias with an organization hears\n// that one; otherwise the active organization, then the organizations of the rows this\n// port has handed out (most rows first), then the person's other memberships โ€” capped by\n// knob entity_data.realtime_max_org_topics. `{ids}` names rows to re-read; `{ids: null}`\n// re-reads all. With no `realtime` bound it says so out loud and stays a no-op.\n\nimport { isUuidShape } from \"@ai-matrx/kit/uuid\";\nimport type {\n Column,\n DataPortEventName,\n DataSource,\n HostDataPort,\n Page,\n Query,\n Row,\n RowsEvent,\n Uuid,\n Values,\n} from \"@ai-matrx/records/data\";\nimport { mintOpId, type RecordsResult } from \"@ai-matrx/records\";\nimport { createEntityClient, type EntityClient } from \"../core/client\";\nimport { refusal } from \"../core/errors\";\nimport { DEFAULT_MAX_ORG_TOPICS, orderOrganizationTopics, type EntityRealtime } from \"../core/realtime\";\nimport type { EntityColumnsAnswer, EntityGetAnswer, EntityWriteAnswer, EntityWireRow } from \"../core/types\";\nimport { toWire } from \"../core/wire\";\nimport { toColumns, toRow } from \"./rows\";\n\n/** An alias as the Applet record declares it for an entity: one token (a `platform.entity_types` word). */\nexport interface EntityAlias {\n kind: \"entity\";\n token: string;\n /** Whose custom fields `columns()` reads. Left out: every organization the person belongs to. */\n organizationId?: Uuid;\n /** Shown in `sources()`; the type's own name when left out. */\n label?: string;\n}\n\nexport interface CreateEntityDataPortOptions {\n /** A client from `createEntityClient` โ€ฆ */\n client?: EntityClient;\n /** โ€ฆ or the supabase-js client (or `rpc` source) to build one from. Give one of the two. */\n dataSource?: unknown;\n aliases: Readonly<Record<string, EntityAlias>>;\n /**\n * TOKEN-FIRST (the `/react` layer): an alias nobody declared is read as a type token โ€”\n * `list(\"party\")` is `{kind:\"entity\", token:\"party\"}`. `sources()` still lists only the declared\n * aliases. Off by default: an Applet's port answers only what its record declares.\n */\n tokensAreAliases?: boolean;\n /**\n * The active organization (CONTRACTS ยง4: `viewer().activeOrganizationId`), read at the moment it is\n * needed: where `create` saves, and the first live topic joined. The host's own answer, never guessed;\n * null/undefined means none is chosen and `create` is refused `organization_required`.\n */\n activeOrganizationId?: () => Uuid | null | undefined;\n /** A fixed active organization, for a host whose port lives no longer than its choice. `activeOrganizationId` wins. */\n organization?: Uuid | null;\n /** The host's live-notice layer (`createEntityRealtime`). Left out, lists are as fresh as their last read. */\n realtime?: EntityRealtime;\n}\n\n/** A value as rows carry it โ†’ as the write doors take it: a link (`EntityRef[]` or one ref) becomes its id. */\nexport function toWireValue(value: unknown): unknown {\n if (isRef(value)) return value.id;\n if (Array.isArray(value) && value.length > 0 && value.every(isRef)) {\n // One link is one id; none is null; several are sent as ids and the door refuses a one-record column by name.\n return value.length === 1 ? (value[0] as { id: string }).id : value.map((ref) => (ref as { id: string }).id);\n }\n return value;\n}\n\nfunction isRef(value: unknown): value is { token: string; id: string } {\n return value !== null && typeof value === \"object\" && !Array.isArray(value)\n && typeof (value as { token?: unknown }).token === \"string\" && typeof (value as { id?: unknown }).id === \"string\";\n}\n\n/** `Values` โ†’ the doors' `{column: value, _custom: {key: value}}`: links back to ids at both levels. */\nexport function toWireValues(values: Values): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(values)) {\n if (value === undefined) continue;\n if (key === \"_custom\" && value && typeof value === \"object\" && !Array.isArray(value)) {\n out._custom = Object.fromEntries(Object.entries(value as Record<string, unknown>).filter(([, v]) => v !== undefined).map(([k, v]) => [k, toWireValue(v)]));\n continue;\n }\n // A link column emptied in a grid arrives as [] and means \"no link\".\n out[key] = Array.isArray(value) && value.length === 0 && key !== \"_custom\" ? null : toWireValue(value);\n }\n return out;\n}\n\n/** A type as the person may use it: its name and whether she may add or change rows (`entity_columns`' own answer). */\nexport interface EntityTypeFacts {\n token: string;\n label: string;\n canCreate: boolean;\n canUpdate: boolean;\n}\n\n/** The host's entity port: the DataPort, plus `describe` for a token-first screen that has no `sources()` entry. */\nexport interface EntityDataPort extends HostDataPort {\n /** The type behind an alias, from the same one `entity_columns` read `columns()` uses. */\n describe(alias: string): Promise<RecordsResult<EntityTypeFacts>>;\n}\n\nexport function createEntityDataPort(options: CreateEntityDataPortOptions): EntityDataPort {\n const client: EntityClient = options.client ?? createEntityClient({ dataSource: options.dataSource });\n const activeOrganization = (): Uuid | null => (options.activeOrganizationId ? options.activeOrganizationId() : options.organization) ?? null;\n /** Ids asked by `get` in this tick, per alias: answered by one `entity_get`. */\n const gets = new Map<string, Map<Uuid, ((answer: RecordsResult<Row | null>) => void)[]>>();\n const aliases = options.aliases;\n const shapes = new Map<string, Promise<RecordsResult<EntityColumnsAnswer>>>();\n let warnedNotLive = false;\n /** Which organization each row this port handed out belongs to, per alias: the \"held rows\" topic order. */\n const held = new Map<string, Map<Uuid, Uuid>>();\n const replans = new Map<string, Set<() => void>>();\n let memberships: Promise<Uuid[] | null> | null = null;\n let maxOrgTopics: Promise<number> | null = null;\n\n function hold(alias: string, rows: readonly { id?: unknown; organization_id?: unknown }[]) {\n const byId = held.get(alias) ?? new Map<Uuid, Uuid>();\n held.set(alias, byId);\n const before = new Set(byId.values());\n for (const r of rows) {\n if (typeof r.id === \"string\" && typeof r.organization_id === \"string\") byId.set(r.id, r.organization_id);\n }\n // A row from an organization this alias did not hold yet may move it into the joined topics.\n if ([...byId.values()].some((org) => !before.has(org))) for (const replan of replans.get(alias) ?? []) replan();\n }\n\n function heldCounts(alias: string): Map<Uuid, number> {\n const counts = new Map<Uuid, number>();\n for (const org of held.get(alias)?.values() ?? []) counts.set(org, (counts.get(org) ?? 0) + 1);\n return counts;\n }\n\n function membershipsOnce(): Promise<Uuid[] | null> {\n memberships ??= client.memberOrganizations().then((answer) => {\n if (answer.ok) return answer.data;\n memberships = null; // a refusal is never kept for the next asker\n console.warn(`[@ai-matrx/entity-data] Could not read your organizations for live updates: ${answer.error.message}`);\n return null;\n });\n return memberships;\n }\n\n function maxOrgTopicsOnce(): Promise<number> {\n maxOrgTopics ??= client.knob({ key: \"realtime_max_org_topics\", organizationId: activeOrganization() }).then((answer) => {\n const value = answer.ok ? Number(answer.data) : Number.NaN;\n if (Number.isFinite(value) && value >= 0) return value;\n console.warn(`[@ai-matrx/entity-data] knob entity_data.realtime_max_org_topics did not resolve; joining at most ${DEFAULT_MAX_ORG_TOPICS} organizations.`);\n return DEFAULT_MAX_ORG_TOPICS;\n });\n return maxOrgTopics;\n }\n\n const tokenBindings = new Map<string, EntityAlias>();\n function bindingOf(alias: string): EntityAlias | null {\n if (Object.prototype.hasOwnProperty.call(aliases, alias)) return aliases[alias]!;\n if (!options.tokensAreAliases || !/^[a-z][a-z0-9_]*$/.test(alias)) return null;\n let bound = tokenBindings.get(alias);\n if (!bound) {\n bound = { kind: \"entity\", token: alias };\n tokenBindings.set(alias, bound);\n }\n return bound;\n }\n\n function noSuchAlias(alias: string) {\n return refusal(\"not_found\", `This Applet has no data source called \"${alias}\".`, \"Use one of the aliases sources() lists.\");\n }\n\n /** One read of a type's columns per port; a refusal is never kept for the next asker. */\n function shapeOf(alias: string, binding: EntityAlias): Promise<RecordsResult<EntityColumnsAnswer>> {\n let held = shapes.get(alias);\n if (!held) {\n held = client.columns({ token: binding.token, organizationId: binding.organizationId ?? null }).then((answer) => {\n if (!answer.ok) shapes.delete(alias);\n return answer;\n });\n shapes.set(alias, held);\n }\n return held;\n }\n\n /** Ask for one row; every id asked for the alias before the tick ends goes in the same `entity_get`. */\n function getCoalesced(alias: string, token: string, id: Uuid): Promise<RecordsResult<Row | null>> {\n return new Promise((resolve) => {\n let batch = gets.get(alias);\n if (!batch) {\n batch = new Map();\n gets.set(alias, batch);\n const asked = batch;\n queueMicrotask(() => {\n gets.delete(alias);\n void flushGets(alias, token, asked);\n });\n }\n const waiting = batch.get(id) ?? [];\n waiting.push(resolve);\n batch.set(id, waiting);\n });\n }\n\n async function flushGets(alias: string, token: string, asked: Map<Uuid, ((answer: RecordsResult<Row | null>) => void)[]>) {\n const answer: RecordsResult<EntityGetAnswer> = await client.get({ token, ids: [...asked.keys()] });\n if (answer.ok) hold(alias, answer.data.rows);\n const byId = new Map<string, EntityWireRow>(answer.ok ? answer.data.rows.map((r) => [r.id, r]) : []);\n for (const [id, waiting] of asked) {\n // A row in the trash is not a row of this port: the DataPort's lists are active rows only (a Query has no\n // archived option), so a re-read that finds one trashed answers null and the engine takes it off the list.\n // `entity_get` itself opens trashed rows by id (`_state.archived`) for screens that read the doors directly.\n const found = byId.get(id);\n const wire = found && (found[\"_state\"] as { archived?: unknown } | undefined)?.archived === true ? undefined : found;\n const result: RecordsResult<Row | null> = answer.ok ? { ok: true, data: wire ? toRow(wire) : null } : answer;\n for (const resolve of waiting) resolve(result);\n }\n }\n\n /** The one row a write door read back (with its new `version`). */\n function readBack(alias: string, answer: RecordsResult<EntityWriteAnswer>, what: string): RecordsResult<Row> {\n if (!answer.ok) return answer;\n hold(alias, answer.data.rows);\n const wire = answer.data.rows[0];\n if (!wire) {\n return refusal(\n \"not_found\",\n `${what} was saved, but you cannot open it now, so it is not shown here.`,\n \"The row is in the store; who may open it decides whether it appears in this list.\",\n );\n }\n return { ok: true, data: toRow(wire) };\n }\n\n function done(answer: RecordsResult<EntityWriteAnswer>): RecordsResult<void> {\n return answer.ok ? { ok: true, data: undefined } : answer;\n }\n\n return {\n async sources() {\n const out: DataSource[] = [];\n for (const [alias, binding] of Object.entries(aliases)) {\n const shape = await shapeOf(alias, binding);\n if (!shape.ok) return shape;\n out.push({\n alias,\n label: binding.label ?? shape.data.label ?? alias,\n kind: \"entity\",\n canCreate: Boolean(shape.data.can_create),\n canUpdate: Boolean(shape.data.can_update),\n });\n }\n return { ok: true, data: out };\n },\n\n async describe(alias) {\n const binding = bindingOf(alias);\n if (!binding) return noSuchAlias(alias);\n const shape = await shapeOf(alias, binding);\n if (!shape.ok) return shape;\n return {\n ok: true,\n data: {\n token: binding.token,\n label: binding.label ?? shape.data.label ?? binding.token,\n canCreate: Boolean(shape.data.can_create),\n canUpdate: Boolean(shape.data.can_update),\n },\n };\n },\n\n async columns(alias) {\n const binding = bindingOf(alias);\n if (!binding) return noSuchAlias(alias);\n const shape = await shapeOf(alias, binding);\n return shape.ok ? { ok: true, data: toColumns(shape.data.columns) satisfies Column[] } : shape;\n },\n\n async list(alias, q: Query = {}, cursor) {\n const binding = bindingOf(alias);\n if (!binding) return noSuchAlias(alias);\n // A custom field's key is only known from the columns; ask for them only when the query names a column.\n let customKeys: ReadonlySet<string> = new Set();\n if ((q.where && q.where.length > 0) || (q.sort && q.sort.length > 0)) {\n const shape = await shapeOf(alias, binding);\n if (!shape.ok) return shape;\n customKeys = new Set(shape.data.columns.filter((c) => c.origin === \"custom\").map((c) => c.key));\n }\n const wire = toWire(q, customKeys);\n // A SOURCE DECLARED IN ONE ORGANIZATION reads that organization only (an Applet's own source scope,\n // CONTRACTS v2.11): the alias's organization replaces any per-query org filter โ€” it is the source's\n // definition, not the person's list filter. Without one, the query's filter (or none) stands.\n if (binding.organizationId) wire.scope = { ...wire.scope, organization_id: binding.organizationId };\n const answer = await client.list({ token: binding.token, ...wire, after: cursor ?? null });\n if (!answer.ok) return answer;\n const { data } = answer;\n if (data.sort_ignored.length > 0) {\n // SAID OUT LOUD: the door keeps one sort key today; the rest were dropped, not silently reordered.\n console.warn(`[@ai-matrx/entity-data] \"${alias}\" sorts by one column for now; ignored: ${data.sort_ignored.join(\", \")}.`);\n }\n hold(alias, data.rows);\n const page: Page = {\n rows: data.rows.map(toRow),\n // A count that stopped at the store's cap is a floor, not a count: unknown beats a wrong number.\n total: data.total === null || data.total === undefined || data.total_is_capped ? null : data.total,\n cursor: data.has_more ? data.next_after : null,\n };\n return { ok: true, data: page };\n },\n\n async get(alias, id: Uuid) {\n const binding = bindingOf(alias);\n if (!binding) return noSuchAlias(alias);\n if (!isUuidShape(id)) return refusal(\"invalid_argument\", `\"${String(id)}\" is not a row id.`);\n return getCoalesced(alias, binding.token, id);\n },\n\n async create(alias, values: Values) {\n const binding = bindingOf(alias);\n if (!binding) return noSuchAlias(alias);\n // A source declared in one organization saves there; otherwise the host's active organization.\n const organizationId = binding.organizationId ?? activeOrganization();\n if (!organizationId) {\n // MX002, said before anything is sent: a new row's organization is the person's choice, never ours.\n return refusal(\n \"organization_required\",\n \"Choose the organization this new record belongs to.\",\n \"Pick an organization, then add the row again. Nothing was saved.\",\n );\n }\n const answer = await client.insert({ token: binding.token, organizationId, rows: [toWireValues(values)], opId: mintOpId() });\n return readBack(alias, answer, \"The new row\");\n },\n\n async update(alias, id: Uuid, patch: Values, baseVersion: number) {\n const binding = bindingOf(alias);\n if (!binding) return noSuchAlias(alias);\n if (!isUuidShape(id)) return refusal(\"invalid_argument\", `\"${String(id)}\" is not a row id.`);\n const answer = await client.update({ token: binding.token, id, baseVersion, patch: toWireValues(patch), opId: mintOpId() });\n return readBack(alias, answer, \"The change\");\n },\n\n async archive(alias, id: Uuid) {\n const binding = bindingOf(alias);\n if (!binding) return noSuchAlias(alias);\n if (!isUuidShape(id)) return refusal(\"invalid_argument\", `\"${String(id)}\" is not a row id.`);\n return done(await client.archive({ token: binding.token, ids: [id], opId: mintOpId() }));\n },\n\n async restore(alias, id: Uuid) {\n const binding = bindingOf(alias);\n if (!binding) return noSuchAlias(alias);\n if (!isUuidShape(id)) return refusal(\"invalid_argument\", `\"${String(id)}\" is not a row id.`);\n return done(await client.unarchive({ token: binding.token, ids: [id], opId: mintOpId() }));\n },\n\n subscribe(event: DataPortEventName, listener: (event: RowsEvent) => void) {\n const alias = event.startsWith(\"rows:\") ? event.slice(5) : null;\n const binding = alias ? bindingOf(alias) : null;\n if (!alias || !binding) {\n throw new Error(`DataPort.subscribe: \"${event}\" is not an event this Applet's port carries. Use rows:<alias>.`);\n }\n const realtime = options.realtime;\n if (!realtime) {\n // NOT LIVE, SAID OUT LOUD: a host that bound no realtime layer goes stale, and the console says why.\n if (!warnedNotLive) {\n warnedNotLive = true;\n console.warn(\"[@ai-matrx/entity-data] This host bound no realtime layer, so other people's changes appear on the next read.\");\n }\n return () => {};\n }\n\n let stopped = false;\n let joined: string[] = [];\n let stopLive: (() => void) | null = null;\n const sink = { rows: (ids: string[] | null) => listener({ ids: ids === null ? null : [...ids] }) };\n\n const plan = async () => {\n const organizationIds = binding.organizationId\n ? [binding.organizationId]\n : orderOrganizationTopics({\n active: activeOrganization(),\n held: heldCounts(alias),\n members: await membershipsOnce(),\n max: await maxOrgTopicsOnce(),\n });\n if (stopped) return;\n if (organizationIds.length === joined.length && organizationIds.every((org, i) => org === joined[i])) return;\n // Join the new set before leaving the old, so a topic in both never closes and reopens.\n const previous = stopLive;\n joined = organizationIds;\n stopLive = realtime.subscribe({ token: binding.token, organizationIds }, sink);\n previous?.();\n };\n const replan = () => void plan();\n const set = replans.get(alias) ?? new Set<() => void>();\n replans.set(alias, set);\n set.add(replan);\n replan();\n\n return () => {\n stopped = true;\n set.delete(replan);\n stopLive?.();\n stopLive = null;\n };\n },\n };\n}\n\n/**\n * THE PORT A SERVER ASKS THROUGH, AND THE PAGE READS THROUGH WHILE IT HYDRATES (lane SEED-ENTITY).\n * One construction for both, so the doors' arguments are the same on either side: a token is its own\n * alias, nothing is bound to an organization, and nothing is live. `dataSource` is the person's own\n * (server), a recording one, or one that answers from a page seed.\n */\nexport function createSeedPort(dataSource: unknown): EntityDataPort {\n return createEntityDataPort({ client: createEntityClient({ dataSource }), aliases: {}, tokensAreAliases: true, activeOrganizationId: () => null });\n}\n","// src/entityQuery.ts โ€” @ai-matrx/records-ui ยท lane SEED-ENTITY\n//\n// THE QUESTIONS A STANDARD SOURCE'S BLOCKS ASK, in one React-free place: the screens (`useEntityTable`,\n// `EntityChartBlock`) and the server's seed (`askEntityBlockSeed`, `askChartSeed`) build them here, so a\n// seeded answer is found by the very arguments the block sends.\n\nimport type { DrillQuestion } from \"@ai-matrx/records\";\nimport type { Query } from \"@ai-matrx/records/data\";\n\n/** A standard source's first read: its page size and the viewer's search (an empty search is none). */\nexport function entityTableQuery(options: { pageSize?: number | undefined; search?: string | null | undefined } = {}): Query {\n const search = (options.search ?? \"\").trim();\n return { ...(options.pageSize ? { pageSize: options.pageSize } : {}), ...(search ? { search } : {}) };\n}\n\n/** The block's own props that decide a chart's question. */\nexport interface EntityChartQuestionInput {\n /** The Dimension to group by. Absent: one total. */\n by?: string | undefined;\n /** What to count: the rows (default) or a number column summed. */\n measure?: { op: \"count\" } | { op: \"sum\" | \"avg\" | \"min\" | \"max\"; of: string } | undefined;\n where?: Record<string, unknown> | undefined;\n organization?: string | undefined;\n}\n\n/** The chart's `platform.drill_ask` question. */\nexport function entityChartQuestion(input: EntityChartQuestionInput): DrillQuestion & { organization?: string } {\n const m = input.measure ?? { op: \"count\" as const };\n return {\n ...(input.by ? { by: [input.by] } : {}),\n show: [m.op === \"count\" ? \"count\" : { op: m.op, of: m.of }],\n ...(input.where ? { where: input.where } : {}),\n ...(input.organization ? { organization: input.organization } : {}),\n };\n}\n","// src/firstPageEntity.ts โ€” @ai-matrx/records-ui/first-page ยท lane SEED-ENTITY\n//\n// THE FIRST ANSWERS OF A STANDARD SOURCE'S BLOCKS AND OF A CHART TILE, ASKED ON THE SERVER AS THE PERSON.\n//\n// Dr. Patel's Space holds a deals table (\"crm_deal\"), a \"deals by stage\" donut and a \"visits this month\"\n// tile. The server that renders the page already holds her session, so it asks each block's first read\n// with EXACTLY the arguments the block will send and hands the answers to `<RecordsSeedProvider>`\n// (`@ai-matrx/records/react`): the HTML carries the rows and the numbers, and hydrating asks nothing.\n//\n// askEntityBlockSeed an entity source's block: `platform.entity_columns` + `platform.entity_list_scoped`\n// askChartSeed an entity source's chart: `platform.drill_ask`\n// askTileSeed a table's tile: `custom.record_aggregate` (one spec, or several)\n//\n// Each returns the `{ at, answers }` of `askTablePageSeed`; merge several with `mergeSeeds`. A door that\n// refuses is simply not in the seed (the block asks for itself). A wrong guess costs one wasted read and\n// never a wrong value: a seeded answer is used only on an exact door + arguments match. Server-safe.\n\nimport type { RecordsConfig, Uuid } from \"@ai-matrx/records\";\nimport type { RecordsClient, RecordsSeed } from \"@ai-matrx/records/core\";\nimport { asRecordsDataSource, createRecordsClient, recordingDataSource } from \"@ai-matrx/records/core\";\nimport { createSeedPort } from \"@ai-matrx/entity-data/port\";\nimport type { DrillSource } from \"@ai-matrx/records\";\n\nimport { entityChartQuestion, entityTableQuery, type EntityChartQuestionInput } from \"./entityQuery\";\n\ntype Base = { dataSource: unknown; organizationId: Uuid; actor: RecordsConfig[\"actor\"] };\n\nfunction recordedClient(input: Base) {\n const recording = recordingDataSource(asRecordsDataSource(input.dataSource));\n const client = createRecordsClient({ dataSource: recording.source, organizationId: input.organizationId, actor: input.actor, onError: () => {} });\n return { recording, client };\n}\n\n/** Several seeds as one: the later answer wins on the same door and arguments; `at` is the oldest. */\nexport function mergeSeeds(...seeds: Array<RecordsSeed | null | undefined>): RecordsSeed {\n const held = seeds.filter((s): s is RecordsSeed => Boolean(s));\n const answers = new Map<string, RecordsSeed[\"answers\"][number]>();\n for (const seed of held) for (const a of seed.answers) answers.set(`${a.door}|${JSON.stringify(a.args)}`, a);\n return { at: held.length ? Math.min(...held.map((s) => s.at)) : Date.now(), answers: [...answers.values()] };\n}\n\n/**\n * AN ENTITY SOURCE'S BLOCK (a table, board or card of \"task\", \"project\", \"crm_deal\", \"hr_employee\"):\n * its columns and first page, asked through the entity engine's own port with the question the block's\n * `useEntityTable` sends โ€” `question.pageSize` (the block's page size: 100, or 50 embedded) and the\n * viewer's `question.search`, if any. Never throws.\n */\nexport async function askEntityBlockSeed(\n input: Base & { token: string; question?: { pageSize?: number | undefined; search?: string | null | undefined } },\n): Promise<RecordsSeed> {\n const recording = recordingDataSource(asRecordsDataSource(input.dataSource));\n const port = createSeedPort(recording.source);\n try {\n await Promise.all([port.describe(input.token), port.list(input.token, entityTableQuery(input.question ?? {}))]);\n } catch {\n // a seed is a head start, never a requirement\n }\n return recording.seed();\n}\n\n/**\n * AN ENTITY SOURCE'S CHART (`EntityChartBlock`): `platform.drill_ask` with the question the block sends.\n * `question` is the block's own props: `by`, `measure`, `where`, `organization`.\n */\nexport async function askChartSeed(\n input: Base & {\n source: { kind: \"entity\"; token: string };\n question?: EntityChartQuestionInput;\n },\n): Promise<RecordsSeed> {\n const { recording, client } = recordedClient(input);\n try {\n await client.drillAsk({ source: input.source as DrillSource, question: entityChartQuestion(input.question ?? {}) });\n } catch {\n // a seed is a head start, never a requirement\n }\n return recording.seed();\n}\n\n/** One aggregate question of a tile: `client.recordAggregate`'s arguments but the table. */\nexport type TileSpec = Omit<Parameters<RecordsClient[\"recordAggregate\"]>[0], \"table_id\">;\n\n/**\n * A TABLE'S TILE: `custom.record_aggregate` over `tableId` for each `spec` (a tile that also shows a\n * whole-table total gives both). The tile reads the number back with `useSeededAggregate`\n * (`@ai-matrx/records/react`) under the very same spec.\n */\nexport async function askTileSeed(input: Base & { tableId: Uuid; spec: TileSpec | TileSpec[] }): Promise<RecordsSeed> {\n const { recording, client } = recordedClient(input);\n const specs = Array.isArray(input.spec) ? input.spec : [input.spec];\n await Promise.all(specs.map((spec) => client.recordAggregate({ table_id: input.tableId, ...spec }).catch(() => null)));\n return recording.seed();\n}\n","// src/firstPage.ts โ€” @ai-matrx/records-ui/first-page ยท lane PAGE-BUNDLE-2\n//\n// THE GRID'S FIRST PAGE, ASKED BEFORE THE GRID EXISTS.\n//\n// A host that renders on a server (Next.js) can ask the table page's first rows while the browser\n// is still loading the app โ€” but only if it asks with EXACTLY the arguments the grid will send, or\n// the records client cannot serve the grid from it (a part is used only on an exact door + args\n// match). This builds that request from the same pieces the screen uses: the view the bar opens\n// (`ViewBar`: the default view, else the first), the person's own look laid over it (`applyLook`),\n// the opening sort (`Grid`: the view's sort, else the table's `default_sort`, through\n// `pageSortFor`) and the table page's page size (100).\n//\n// IT ONLY PREDICTS THE PLAIN OPENING. A view grouped into sections, drilled, filtered, hand-ordered,\n// built on a Rule, or not a grid answers null โ€” the grid asks for itself, exactly as before. A wrong\n// guess costs one wasted read and never a wrong row: the grid uses a seeded page only on an exact\n// match. No React here (server-safe entry).\n\nimport type { Field, RecordPageSort, StoreFilter, Table, Uuid } from \"@ai-matrx/records\";\nimport type { RecordsClient, RecordsSeed } from \"@ai-matrx/records/core\";\nimport { asRecordsDataSource, createRecordsClient, recordingDataSource } from \"@ai-matrx/records/core\";\nimport type { RecordsConfig } from \"@ai-matrx/records\";\nimport { isUuidShape } from \"@ai-matrx/kit/uuid\";\nimport { defaultSortOf } from \"./gridLook\";\nimport { pageSortFor, storeQuestion } from \"./gridPage\";\nimport { ownVersion } from \"./rowVersion\";\nimport { REVERSE_CELL_CHIPS, choiceFieldKeys, groupFactMeasures, groupedPageSort, isPlainGrouping, summaryBarMeasures } from \"./groupReads\";\nimport { applyLook, readLooks } from \"./viewLook\";\nimport { defaultViewOf, viewFromRow, type SavedView, type SavedViewSpec } from \"./views\";\n\n/** The table page's own page size (`TablePage`: 100 unless embedded). */\nexport const TABLE_PAGE_SIZE = 100;\n\n/**\n * HOW MANY ROWS AN EMBEDDED TABLE READS AT A TIME (CHAIR-EMBEDDED-FIDELITY 16): its first page, and\n * each \"Show more\" press brings about this many more. `embeddedLook.tsx` re-exports it.\n */\nexport const EMBEDDED_PAGE_SIZE = 50;\n\nexport interface FirstGridPageArgs {\n p_table_id: Uuid;\n p_filter: StoreFilter | Record<string, never>;\n p_search: null;\n p_sort: RecordPageSort[];\n p_view_id: null;\n p_limit: number;\n p_offset: 0;\n}\n\n/**\n * THE VIEW'S OWN QUESTION, as the grid asks it (`TablePage` `shownFilter`): its Rule, else its flat filter,\n * through the one question builder. Undefined when it asks none.\n */\nexport function viewFilterOf(shown: Pick<SavedViewSpec, \"where\" | \"filters\"> | null, fields: readonly Field[]): StoreFilter | undefined {\n if (!shown) return undefined;\n const base: StoreFilter | undefined = shown.where ?? (shown.filters && Object.keys(shown.filters).length > 0 ? shown.filters : undefined);\n return storeQuestion(fields, base, {}).filter;\n}\n\n/** The request for the plain opening, from what the screen itself reads โ€” or null. Pure. */\nexport function firstGridPageArgs(input: {\n tableId: Uuid;\n table: Pick<Table, \"default_sort\"> | null;\n fields: readonly Field[];\n /** The view as THIS person sees it (the saved view with her look over it), or null for none. */\n shown: SavedView | null;\n pageSize?: number;\n /**\n * The host draws the MERGED grid (`RecordsUiProvider grid=\"merged\"`), which keeps a plain grouping as rows\n * under group headers: the page is then read in group order. The classic grid opens the drill for a\n * grouped view, so a grouped view stays unseeded there.\n */\n merged?: boolean;\n}): FirstGridPageArgs | null {\n const { shown, fields } = input;\n if (!shown) return null;\n if ((shown.layout ?? \"grid\") !== \"grid\") return null;\n if (shown.ruleId) return null;\n if (shown.order === \"manual\") return null;\n const grouping = shown.presentation?.grouping;\n if (grouping && !(input.merged && isPlainGrouping(grouping))) return null;\n const first = (shown.sorts ?? [])[0];\n const viewSort = first && fields.some((f) => f.key === first.field) ? { id: first.field, direction: first.direction } : null;\n const opening = viewSort ?? defaultSortOf(input.table, fields);\n return {\n p_table_id: input.tableId,\n p_filter: viewFilterOf(shown, fields) ?? {},\n p_search: null,\n p_sort: groupedPageSort(fields, grouping?.field ?? null, pageSortFor(fields, opening)),\n p_view_id: null,\n p_limit: input.pageSize ?? TABLE_PAGE_SIZE,\n p_offset: 0,\n };\n}\n\n/**\n * Read what the screen reads (through `client`, e.g. one served from `custom.table_page_bundle`)\n * and build the first page's request. Null when anything it needs is not answered.\n */\nexport async function firstGridPageRequest(\n client: Pick<RecordsClient, \"tableRead\" | \"fields\" | \"views\" | \"viewLookRead\">,\n tableId: Uuid,\n options: { pageSize?: number; merged?: boolean } = {},\n): Promise<FirstGridPageArgs | null> {\n return (await firstGridOpening(client, tableId, options))?.args ?? null;\n}\n\n/** The unsaved default the grid runs on until a view is saved (`TablePage`'s `defaultView`). */\nfunction defaultSpec(tableId: Uuid): SavedViewSpec {\n return { name: \"All\", subject: tableId, layout: \"grid\", isDefault: true };\n}\n\n/** A spec as the grid shows it before any look is laid over it. */\nfunction shownFromSpec(spec: SavedViewSpec, tableId: Uuid): SavedView {\n return { ...spec, subject: spec.subject ?? tableId, id: \"\" as Uuid, movedFrom: null };\n}\n\n/** The plain opening's request with what it was built from (the view as shown, the table's fields). */\nasync function firstGridOpening(\n client: Pick<RecordsClient, \"tableRead\" | \"fields\" | \"views\" | \"viewLookRead\">,\n tableId: Uuid,\n options: { pageSize?: number; merged?: boolean; view?: SavedViewSpec | undefined },\n): Promise<{ args: FirstGridPageArgs; shown: SavedView | null; fields: readonly Field[] } | null> {\n const explicit = options.view;\n const [table, fields, views, looks] = await Promise.all([\n client.tableRead({ table_id: tableId }),\n client.fields({ table_id: tableId }),\n explicit ? Promise.resolve(null) : client.views({ table_id: tableId }),\n explicit ? Promise.resolve(null) : readLooks(client, tableId),\n ]);\n if (!table.ok || !fields.ok) return null;\n let shown: SavedView;\n if (explicit) {\n // THE HOST'S OWN SPEC (a Space's grid): the view the grid is handed, with no saved view behind it.\n shown = shownFromSpec(explicit, tableId);\n } else {\n if (!views?.ok || !looks?.ok) return null;\n const mine = views.data.map(viewFromRow).filter((v) => v.subject === tableId);\n const view = defaultViewOf(mine) ?? mine[0] ?? null;\n // A table with no saved view opens on the unsaved default (the bar saves it a moment later).\n shown = view ? applyLook(view, looks.data[view.id]) : shownFromSpec(defaultSpec(tableId), tableId);\n }\n const args = firstGridPageArgs({\n tableId,\n table: table.data,\n fields: fields.data,\n shown,\n ...(options.pageSize ? { pageSize: options.pageSize } : {}),\n ...(options.merged ? { merged: true } : {}),\n });\n return args ? { args, shown, fields: fields.data } : null;\n}\n\n/**\n * A TABLE PAGE'S SEED, ASKED BY THE SERVER AS THE PERSON (lane SSR-ROWS).\n *\n * Asks what the table page opens on โ€” the table's bundle (`custom.table_page_bundle`, through the\n * records client exactly as the browser would), the grid's first page when `firstGridPageRequest`\n * can predict it, and on a record page the record's bundle โ€” through a recording data source, and\n * answers every door answer it got as a `RecordsSeed`. The host hands that to\n * `<RecordsSeedProvider>` (`@ai-matrx/records/react`) above the table page: the page is drawn with\n * its rows in the server's HTML and hydrates onto them without asking again.\n *\n * `dataSource` is the person's own (a server supabase client carrying her session): every door\n * decides exactly as it does from the browser. Never throws; a door that refuses is simply not in\n * the seed, and the page asks it for itself.\n */\n/**\n * THE PERSON'S SERVER-ROWS KNOBS, FROM THE TABLE'S BUNDLE (lanes SSR-ROWS-2, SSR-ROWS-3):\n * `custom.table_page_bundle` carries a `server_rows` part โ€” `data.server_rows` and\n * `data.server_rows_cap_ms` (how long the page may wait for the server's rows before the browser asks\n * for itself) resolved for the person asking in that organization. Null when the seed holds no such\n * answer (an older store, a refused part): the host treats that as off.\n */\nexport function serverRowsOf(seed: RecordsSeed | null | undefined): { on: boolean; capMs: number | null } | null {\n const found = seed?.answers.find((a) => a.door === \"server_rows\");\n const data = found?.data as { on?: unknown; cap_ms?: unknown } | undefined;\n if (!data || typeof data !== \"object\") return null;\n return { on: data.on === true, capMs: typeof data.cap_ms === \"number\" && data.cap_ms > 0 ? data.cap_ms : null };\n}\n\nexport async function askTablePageSeed(input: {\n dataSource: unknown;\n organizationId: Uuid;\n tableId: Uuid;\n actor: RecordsConfig[\"actor\"];\n /** A record page: its record's bundle (`custom.record_page_bundle`) is asked beside the table's. */\n recordId?: Uuid | null;\n /**\n * Ask the grid's first page (default true). Off when the address opens something else. A function\n * decides once the table's shape (its bundle) has been answered โ€” it is handed everything asked so\n * far, so a host can read the person's `server_rows` knob from the bundle (`serverRowsOf`) and ask\n * the rows only when it is on, at no extra read.\n */\n rows?: boolean | ((asked: RecordsSeed) => boolean);\n /** The table is drawn EMBEDDED (`TablePage presentation=\"embedded\"`, a Space's inline table): its page size. */\n embedded?: boolean;\n pageSize?: number;\n /**\n * The host draws the merged grid. A view grouped by one field (no sub-levels, pivot or drill trail) is then\n * seeded too: the page in group order, the group counts, the summary bar, and the words of the links on the\n * page โ€” each asked with the arguments the grid sends, so the browser's own call finds its answer.\n */\n merged?: boolean;\n /**\n * THE VIEW THE HOST HANDS THE GRID (a Space's own spec: sorts, filters, `where`, grouping), for a table with\n * no saved view behind it. The first page is built from it exactly as the grid reads it; the table's saved\n * views and looks are not asked.\n */\n view?: SavedViewSpec | undefined;\n}): Promise<RecordsSeed> {\n const recording = recordingDataSource(asRecordsDataSource(input.dataSource));\n const client = createRecordsClient({\n dataSource: recording.source,\n organizationId: input.organizationId,\n actor: input.actor,\n onError: () => {},\n });\n const record = input.recordId\n ? Promise.resolve(\n recording.source.rpc(\"record_page_bundle\", { p_organization_id: input.organizationId, p_record_id: input.recordId }, { schema: \"custom\" }),\n ).catch(() => null)\n : null;\n try {\n const pageSize = input.pageSize ?? (input.embedded ? EMBEDDED_PAGE_SIZE : TABLE_PAGE_SIZE);\n const opening = await firstGridOpening(client, input.tableId, { pageSize, ...(input.merged ? { merged: true } : {}), ...(input.view ? { view: input.view } : {}) });\n const wantRows = typeof input.rows === \"function\" ? input.rows(recording.seed()) : input.rows !== false;\n if (opening && wantRows) {\n const { args, shown, fields } = opening;\n const page = await client.listPage({\n table_id: args.p_table_id,\n filter: args.p_filter,\n search: args.p_search,\n sort: args.p_sort,\n view_id: args.p_view_id,\n limit: args.p_limit,\n offset: args.p_offset,\n });\n if (page.ok) {\n const chosen = shown?.presentation?.summaries ?? {};\n const groupKey = shown?.presentation?.grouping?.field ?? null;\n const reads: Array<Promise<unknown>> = [];\n // The question every one of the grid's table reads carries (`readFilter`): the view's own.\n const filter = args.p_filter && Object.keys(args.p_filter).length > 0 ? (args.p_filter as StoreFilter) : undefined;\n // The grouped grid's header counts (`useGroupFacts`) and the summary bar (`useColumnSummaries`).\n if (groupKey) {\n reads.push(client.recordAggregate({ table_id: input.tableId, groupBy: [groupKey], measures: groupFactMeasures(chosen), limit: 1000, ...(filter ? { filter } : {}) }));\n }\n const bar = summaryBarMeasures(chosen);\n if (bar.length > 0) reads.push(client.recordAggregate({ table_id: input.tableId, measures: bar, ...(filter ? { filter } : {}) }));\n // The words of every link on the page: one call per field, its ids in row order (`useRecordLabels`).\n for (const field of fields) {\n if (field.type !== \"list\" && field.type !== \"relation\") continue;\n const ids = new Set<string>();\n for (const row of page.data.rows) {\n const value = row.document?.[field.key];\n for (const id of Array.isArray(value) ? value.map(String) : typeof value === \"string\" ? [value] : []) if (isUuidShape(id)) ids.add(id);\n }\n if (ids.size > 0) reads.push(client.relationWordsMany({ field_id: field.id as Uuid, record_ids: [...ids] as Uuid[] }));\n }\n if (input.merged) {\n // The header filters' counts per choice column (`gridControls`, one `record_aggregate` each, limit 500).\n for (const key of choiceFieldKeys(fields)) {\n reads.push(client.recordAggregate({ table_id: input.tableId, groupBy: [key], measures: [{ op: \"count\" }], limit: 500, ...(filter ? { filter } : {}) }));\n }\n }\n // The versions of the rows the page drew, for the rows that carry none themselves (`useRecordVersions`).\n const unversioned = page.data.rows.filter((row) => ownVersion(row) === null).map((row) => row.id);\n if (unversioned.length > 0) reads.push(client.recordHeaders({ ids: unversioned }));\n // The table's reverse columns (links made from other tables) and, for each, the page's links\n // (`useReverseGridColumns`: the rows' ids in order, `REVERSE_CELL_CHIPS` a record). Merged or not.\n reads.push(\n client.reverseColumns({ table_id: input.tableId }).then(async (columns) => {\n if (!columns.ok || columns.data.length === 0) return null;\n const record_ids = page.data.rows.map((row) => row.id);\n // ONE call for every reverse column (`useReverseLinks`); a store without that door is asked per column.\n const all = await client.reverseLinksFields({ table_id: input.tableId, source_field_ids: columns.data.map((c) => c.source_field_id), record_ids, limit: REVERSE_CELL_CHIPS });\n if (all.ok || all.error.code !== \"door_absent\") return null;\n return Promise.all(columns.data.map((c) => client.reverseLinksMany({ table_id: input.tableId, source_field_id: c.source_field_id, record_ids, limit: REVERSE_CELL_CHIPS })));\n }),\n );\n await Promise.all(reads.map((read) => read.catch(() => null)));\n }\n }\n } catch {\n // a seed is a head start, never a requirement: whatever was answered is kept\n }\n if (record) await record;\n return recording.seed();\n}\n\n// Entity blocks and chart tiles (lane SEED-ENTITY).\nexport * from \"./firstPageEntity\";\n"]}
1
+ {"version":3,"sources":["../src/names.ts","../src/parity.ts","../src/gridLook.ts","../src/gridPage.ts","../src/rowVersion.ts","../src/groupReads.ts","../src/presentation.ts","../src/viewKeys.ts","../src/views.ts","../src/viewLook.ts","../src/entityQuery.ts","../src/firstPageEntity.ts","../src/firstPage.ts"],"names":["field","zone","op","raw","recordingDataSource","asRecordsDataSource","createRecordsClient","isUuidShape"],"mappings":";;;;;;;;;;;AAsBO,SAAS,UAAUA,MAAAA,EAAsB;AAC9C,EAAA,MAAM,KAAA,GAAA,CAASA,MAAAA,CAAM,KAAA,IAAS,EAAA,EAAI,IAAA,EAAK;AAKvC,EAAA,IAAI,KAAA,KAAU,EAAA,IAAM,KAAA,KAAUA,MAAAA,CAAM,GAAA,IAAO,gCAAA,CAAiC,IAAA,CAAK,KAAK,CAAA,EAAG,OAAO,kBAAA,CAAmB,KAAK,CAAA;AACxH,EAAA,IAAI,KAAA,KAAU,IAAI,OAAO,KAAA;AACzB,EAAA,MAAM,IAAA,GAAA,CAAQA,MAAAA,CAAM,IAAA,IAAQ,EAAA,EAAI,IAAA,EAAK;AACrC,EAAA,IAAI,IAAA,KAAS,IAAI,OAAO,IAAA;AACxB,EAAA,OAAO,kBAAA,CAAmBA,OAAM,GAAG,CAAA;AACrC;ACPA,IAAM,QAAA,GAAW,CAAC,IAAA,KAChB,aAAA,CAAc,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,IAAA,KAAS,IAAI,CAAA,EAAG,EAAA,IAAM,EAAA;AACpD,IAAM,iBAAA,GAAoB,SAAS,MAAM,CAAA;AACzC,IAAM,mBAAA,GAAsB,SAAS,QAAQ,CAAA;AAoBS,MAAA,CAAO,WAAA;AAAA,EAC3D,kBAAA,CAAmB,IAAI,CAAC,CAAA,KAAM,CAAC,CAAA,CAAE,WAAA,EAAa,CAAA,CAAE,OAAO,CAAC;AAC1D;AAeA,IAAM,MAAA,GAAwB,EAAE,IAAA,EAAM,iBAAA,EAAmB,QAAQ,mBAAA,EAAoB;AAW9E,SAAS,cAAcA,MAAAA,EAA0B;AACtD,EAAA,OAAO,YAAA,CAAaA,QAAO,MAAM,CAAA;AACnC;;;ACnCO,SAAS,aAAA,CACd,OACA,MAAA,EACoB;AACpB,EAAA,MAAM,GAAA,GAAO,OAAO,YAAA,IAAgB,IAAA;AACpC,EAAA,MAAM,QAAQ,KAAA,CAAM,OAAA,CAAQ,GAAG,CAAA,GAAI,GAAA,CAAI,CAAC,CAAA,GAAI,GAAA;AAC5C,EAAA,IAAI,IAAA,GAAsB,IAAA;AAC1B,EAAA,IAAI,SAAA,GAA4B,KAAA;AAChC,EAAA,IAAI,OAAO,UAAU,QAAA,EAAU;AAC7B,IAAA,MAAM,OAAA,GAAU,MAAM,IAAA,EAAK;AAC3B,IAAA,IAAI,OAAA,KAAY,IAAI,OAAO,IAAA;AAC3B,IAAA,IAAI,OAAA,CAAQ,UAAA,CAAW,GAAG,CAAA,EAAG;AAC3B,MAAA,IAAA,GAAO,OAAA,CAAQ,KAAA,CAAM,CAAC,CAAA,CAAE,IAAA,EAAK;AAC7B,MAAA,SAAA,GAAY,MAAA;AAAA,IACd,CAAA,MAAO;AACL,MAAA,MAAM,CAAC,IAAA,EAAM,IAAI,CAAA,GAAI,OAAA,CAAQ,MAAM,KAAK,CAAA;AACxC,MAAA,IAAA,GAAO,IAAA,IAAQ,IAAA;AACf,MAAA,IAAI,IAAA,IAAQ,IAAA,CAAK,WAAA,EAAY,KAAM,QAAQ,SAAA,GAAY,MAAA;AAAA,IACzD;AAAA,EACF,CAAA,MAAA,IAAW,KAAA,IAAS,OAAO,KAAA,KAAU,QAAA,EAAU;AAC7C,IAAA,MAAM,KAAA,GAAQ,KAAA;AACd,IAAA,MAAM,KAAA,GAAQ,MAAM,OAAO,CAAA,IAAK,MAAM,KAAK,CAAA,IAAK,MAAM,IAAI,CAAA;AAC1D,IAAA,IAAA,GAAO,OAAO,KAAA,KAAU,QAAA,GAAW,KAAA,GAAQ,IAAA;AAC3C,IAAA,IAAI,MAAA,CAAO,MAAM,WAAW,CAAA,IAAK,EAAE,CAAA,CAAE,WAAA,EAAY,KAAM,MAAA,EAAQ,SAAA,GAAY,MAAA;AAAA,EAC7E;AACA,EAAA,IAAI,CAAC,MAAM,OAAO,IAAA;AAElB,EAAA,MAAMA,MAAAA,GAAQ,MAAA,CAAO,IAAA,CAAK,CAAC,MAAM,CAAA,CAAE,GAAA,KAAQ,IAAI,CAAA,IAAK,OAAO,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,OAAO,IAAI,CAAA;AACpF,EAAA,OAAOA,SAAQ,EAAE,EAAA,EAAIA,MAAAA,CAAM,GAAA,EAAK,WAAU,GAAI,IAAA;AAChD;AA2HO,IAAM,aAAA,GAAwC;AAAA,EACnD,KAAA,EAAO,OAAA;AAAA,EACP,GAAA,EAAK,KAAA;AAAA,EACL,GAAA,EAAK,SAAA;AAAA,EACL,GAAA,EAAK,UAAA;AAAA,EACL,GAAA,EAAK,SAAA;AAAA,EACL,MAAA,EAAQ,QAAA;AAAA,EACR,MAAA,EAAQ,QAAA;AAAA,EACR,KAAA,EAAO,OAAA;AAAA,EACP,MAAA,EAAQ,QAAA;AAAA,EACR,aAAA,EAAe,eAAA;AAAA,EACf,cAAA,EAAgB,gBAAA;AAAA,EAChB,KAAA,EAAO;AACT,CAAA;;;AC3KO,SAAS,cAAc,CAAA,EAAyB;AACrD,EAAA,IAAI,CAAC,cAAA,CAAe,CAAC,CAAA,EAAG,OAAO,IAAA;AAC/B,EAAA,OAAO,CAAA,EAAG,SAAA,CAAU,CAAC,CAAC,CAAA,wIAAA,CAAA;AACxB;AAEA,SAAS,eAAe,CAAA,EAAmB;AACzC,EAAA,MAAM,IAAA,GAAO,MAAA,CAAO,aAAA,CAAc,CAAC,CAAC,CAAA;AACpC,EAAA,OAAA,CACG,CAAA,CAAE,IAAA,KAAS,SAAA,IAAa,IAAA,KAAS,SAAA,IAAa,IAAA,KAAS,QAAA,IAAY,IAAA,KAAS,QAAA,KAC7E,MAAA,CAAQ,CAAA,CAA+B,UAAA,IAAc,MAAM,CAAA,KAAM,MAAA;AAErE;AAMO,SAAS,aAAaA,MAAAA,EAAuB;AAClD,EAAA,MAAM,IAAA,GAAO,MAAA,CAAO,aAAA,CAAcA,MAAK,CAAC,CAAA;AACxC,EAAA,IAAI,SAAS,QAAA,IAAY,IAAA,KAAS,UAAA,IAAc,IAAA,KAAS,WAAW,OAAO,IAAA;AAC3E,EAAA,OAAO,MAAA,CAAA,CAASA,OAAM,MAAA,IAAU,IAAgC,QAAQ,CAAA,IAAK,EAAE,CAAA,KAAM,YAAA;AACvF;AAGO,SAAS,UAAUA,MAAAA,EAAiD;AACzE,EAAA,MAAM,IAAA,GAAO,MAAA,CAAO,aAAA,CAAcA,MAAK,CAAC,CAAA;AAExC,EAAA,IAAI,YAAA,CAAaA,MAAK,CAAA,EAAG,OAAO,QAAA;AAChC,EAAA,IAAI,IAAA,KAAS,YAAY,OAAO,MAAA;AAChC,EAAA,OAAO,MAAA;AACT;AAGO,SAAS,WAAA,CAAY,QAA0B,IAAA,EAAsD;AAC1G,EAAA,IAAI,CAAC,IAAA,EAAM,OAAO,EAAC;AACnB,EAAA,MAAMA,MAAAA,GAAQ,OAAO,IAAA,CAAK,CAAC,MAAM,CAAA,CAAE,GAAA,KAAQ,KAAK,EAAE,CAAA;AAClD,EAAA,IAAI,CAACA,MAAAA,EAAO,OAAO,EAAC;AACpB,EAAA,OAAO,CAAC,EAAE,KAAA,EAAOA,MAAAA,CAAM,KAAK,SAAA,EAAW,IAAA,CAAK,SAAA,KAAc,MAAA,GAAS,SAAS,KAAA,EAAO,EAAA,EAAI,SAAA,CAAUA,MAAK,GAAG,CAAA;AAC3G;AAGO,SAAS,gBAAgB,IAAA,EAAsB;AACpD,EAAA,OAAO,CAAA,IAAA,EAAO,IAAA,CAAK,OAAA,CAAQ,qBAAA,EAAuB,MAAM,CAAC,CAAA,CAAA;AAC3D;AAiBO,IAAM,WAAA,GAAc,WAAA;AAE3B,IAAM,QAAQ,CAAC,CAAA,MAA8B,EAAE,KAAA,EAAO,EAAE,EAAA,EAAG,CAAA;AAC3D,IAAM,QAAA,GAAW,CAAC,KAAA,MAAoC,EAAE,OAAO,KAAA,EAAM,CAAA;AAErE,IAAM,YAAA,GAAe,CAAC,CAAA,EAAU,MAAA,MAC7B,EAAE,EAAA,EAAI,QAAA,EAAU,IAAA,EAAM,CAAC,MAAM,CAAC,CAAA,EAAG,QAAA,CAAS,MAAM,CAAC,CAAA,EAAE,CAAA;AACtD,IAAM,KAAK,CAAC,IAAA,EAAA,GAAiB,UAC1B,EAAE,EAAA,EAAI,MAAM,IAAA,EAAK,CAAA;AAGb,SAAS,UAAU,CAAA,EAAU,KAAA,EAA0B,GAAA,mBAAY,IAAI,MAAK,EAA2B;AAC5G,EAAA,MAAM,IAAA,GAAO,UAAU,CAAC,CAAA;AACxB,EAAA,MAAM,OAAA,GAAU,cAAc,CAAC,CAAA;AAC/B,EAAA,IAAI,SAAS,OAAO,OAAA;AACpB,EAAA,MAAM,GAAA,GAAM,CAAC,IAAA,KAAyC,EAAA,CAAG,OAAO,IAAI,CAAA;AACpE,EAAA,QAAQ,MAAM,IAAA;AAAM,IAClB,KAAK,MAAA,EAAQ;AACX,MAAA,MAAM,IAAA,GAAO,MAAM,IAAA,IAAQ,UAAA;AAC3B,MAAA,IAAI,IAAA,KAAS,OAAA,IAAW,IAAA,KAAS,MAAA,EAAQ,OAAO,MAAM,OAAA,GAAU,EAAE,IAAA,EAAM,EAAA,CAAG,SAAA,EAAW,KAAA,CAAM,CAAC,CAAC,CAAA,EAAE,GAAI,EAAE,IAAA,EAAM,GAAA,CAAI,EAAA,CAAG,SAAA,EAAW,KAAA,CAAM,CAAC,CAAC,CAAC,CAAA,EAAE;AACzI,MAAA,IAAI,IAAA,KAAS,WAAA,IAAe,IAAA,KAAS,UAAA,EAAY,OAAO,MAAM,OAAA,GAAU,EAAE,IAAA,EAAM,GAAA,CAAI,EAAA,CAAG,SAAA,EAAW,MAAM,CAAC,CAAC,CAAC,CAAA,EAAE,GAAI,EAAE,IAAA,EAAM,EAAA,CAAG,SAAA,EAAW,KAAA,CAAM,CAAC,CAAC,CAAA,EAAE;AACjJ,MAAA,MAAM,IAAA,GAAO,KAAA,CAAM,KAAA,CAAM,IAAA,EAAK;AAC9B,MAAA,IAAI,IAAA,KAAS,IAAI,OAAO,IAAA;AACxB,MAAA,MAAM,IAAA,GAAO,MAAA,CAAO,aAAA,CAAc,CAAC,CAAC,CAAA;AACpC,MAAA,IAAI,SAAS,QAAA,IAAY,IAAA,KAAS,cAAc,IAAA,KAAS,SAAA,IAAa,SAAS,UAAA,EAAY;AACzF,QAAA,OAAO,GAAG,IAAI,CAAA,2DAAA,CAAA;AAAA,MAChB;AACA,MAAA,MAAM,OAAA,GAAU,EAAA,CAAG,SAAA,EAAW,KAAA,CAAM,CAAC,GAAG,QAAA,CAAS,eAAA,CAAgB,IAAI,CAAC,CAAC,CAAA;AACvE,MAAA,OAAO,EAAE,IAAA,EAAM,KAAA,CAAM,UAAU,GAAA,CAAI,OAAO,IAAI,OAAA,EAAQ;AAAA,IACxD;AAAA,IACA,KAAK,QAAA,EAAU;AAIb,MAAA,MAAM,MAAA,GAAS,KAAA,CAAM,MAAA,KAAW,KAAA,CAAM,KAAA,KAAU,KAAK,EAAC,GAAI,CAAC,KAAA,CAAM,KAAK,CAAA,CAAA;AACtE,MAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,QAAA,CAAS,WAAW,CAAA;AACzC,MAAA,MAAM,QAAQ,MAAA,CAAO,MAAA,CAAO,CAAC,CAAA,KAAM,MAAM,WAAW,CAAA;AACpD,MAAA,IAAI,CAAC,KAAA,IAAS,KAAA,CAAM,MAAA,KAAW,CAAA,EAAG,OAAO,KAAA,CAAM,OAAA,GAAU,IAAA,GAAO,EAAE,IAAA,EAAM,QAAA,CAAS,KAAK,CAAA,EAAE;AACxF,MAAA,MAAM,OAAA,GAAU,EAAA,CAAG,SAAA,EAAW,KAAA,CAAM,CAAC,CAAC,CAAA;AACtC,MAAA,MAAM,IAAA,GAAO,MAAA,CAAO,aAAA,CAAc,CAAC,CAAC,CAAA;AACpC,MAAA,IAAI,IAAA,KAAS,UAAA,IAAc,IAAA,KAAS,MAAA,EAAQ;AAK1C,QAAA,IAAI,KAAA,CAAM,MAAA,KAAW,CAAA,EAAG,OAAO,EAAE,IAAA,EAAM,KAAA,CAAM,OAAA,GAAU,OAAA,GAAU,GAAA,CAAI,OAAO,CAAA,EAAE;AAC9E,QAAA,MAAM,SAAA,GAAY,QAAS,CAAA,CAAE,MAAA,IAAU,EAAC,EAA+B,MAAM,CAAA,IAAK,EAAE,CAAA,KAAM,MAAA;AAC1F,QAAA,IAAI,KAAA,CAAM,WAAW,CAAA,IAAK,CAAC,SAAS,CAAC,KAAA,CAAM,OAAA,EAAS,OAAO,EAAE,IAAA,EAAM,EAAE,CAAC,CAAA,CAAE,GAAG,GAAG,SAAA,CAAU,MAAM,CAAC,CAAA,EAAI,SAAS,CAAA,EAAE,EAAE;AAChH,QAAA,MAAM,MAAA,GAAS,KAAA,CAAM,GAAA,CAAI,CAAC,GAAA,KAAQ,YAAA,CAAa,CAAA,EAAG,SAAA,CAAU,GAAA,EAAK,SAAS,CAAC,CAAC,CAAA;AAC5E,QAAA,MAAM,MAAA,GAAS,MAAA,CAAO,MAAA,KAAW,CAAA,GAAI,MAAA,CAAO,CAAC,CAAA,GAAK,EAAA,CAAG,IAAA,EAAM,GAAG,MAAM,CAAA;AACpE,QAAA,IAAI,CAAC,KAAA,CAAM,OAAA,EAAS,OAAO,EAAE,IAAA,EAAM,KAAA,GAAQ,EAAA,CAAG,IAAA,EAAM,GAAA,CAAI,OAAO,CAAA,EAAG,MAAM,IAAI,MAAA,EAAO;AACnF,QAAA,OAAO,EAAE,IAAA,EAAM,KAAA,GAAQ,EAAA,CAAG,KAAA,EAAO,OAAA,EAAS,GAAA,CAAI,MAAM,CAAC,CAAA,GAAI,GAAA,CAAI,MAAM,CAAA,EAAE;AAAA,MACvE;AAEA,MAAA,MAAM,QAAA,GAAW,CAAC,CAAA,KAChB,YAAA,CAAa,CAAC,KAAK,IAAA,KAAS,QAAA,GACxB,MAAA,CAAO,QAAA,CAAS,MAAA,CAAO,CAAC,CAAC,CAAA,GAAI,MAAA,CAAO,CAAC,CAAA,GAAI,CAAA,GACzC,IAAA,KAAS,UAAA,KAAe,CAAA,KAAM,MAAA,IAAU,CAAA,KAAM,OAAA,CAAA,GAAW,CAAA,KAAM,MAAA,GAAS,CAAA;AAC9E,MAAA,MAAM,MAAA,GAAS,KAAA,CAAM,GAAA,CAAI,QAAQ,CAAA;AACjC,MAAA,MAAM,OAAA,GACJ,MAAA,CAAO,MAAA,KAAW,CAAA,GAAI,OAAO,MAAA,CAAO,MAAA,KAAW,CAAA,GAAI,EAAA,CAAG,MAAM,KAAA,CAAM,CAAC,CAAA,EAAG,QAAA,CAAS,OAAO,CAAC,CAAC,CAAC,CAAA,GAAI,GAAG,IAAA,EAAM,GAAG,MAAA,CAAO,GAAA,CAAI,CAAC,CAAA,KAAM,EAAA,CAAG,IAAA,EAAM,KAAA,CAAM,CAAC,CAAA,EAAG,QAAA,CAAS,CAAC,CAAC,CAAC,CAAC,CAAA;AAC7J,MAAA,IAAI,CAAC,MAAM,OAAA,EAAS;AAClB,QAAA,IAAI,CAAC,KAAA,IAAS,MAAA,CAAO,MAAA,KAAW,CAAA,SAAU,EAAE,IAAA,EAAM,EAAE,CAAC,EAAE,GAAG,GAAG,MAAA,CAAO,CAAC,GAA+B,EAAE;AACtG,QAAA,IAAI,CAAC,KAAA,EAAO,OAAO,EAAE,MAAM,OAAA,EAAS;AACpC,QAAA,OAAO,EAAE,IAAA,EAAM,OAAA,GAAU,EAAA,CAAG,IAAA,EAAM,GAAA,CAAI,OAAO,CAAA,EAAG,OAAO,CAAA,GAAI,GAAA,CAAI,OAAO,CAAA,EAAE;AAAA,MAC1E;AACA,MAAA,IAAI,KAAA,EAAO,OAAO,EAAE,IAAA,EAAM,OAAA,GAAU,EAAA,CAAG,KAAA,EAAO,OAAA,EAAS,GAAA,CAAI,OAAO,CAAC,CAAA,GAAI,OAAA,EAAQ;AAC/E,MAAA,OAAO,EAAE,IAAA,EAAM,EAAA,CAAG,IAAA,EAAM,GAAA,CAAI,OAAO,CAAA,EAAG,GAAA,CAAI,OAAQ,CAAC,CAAA,EAAE;AAAA,IACvD;AAAA,IACA,KAAK,SAAA;AACH,MAAA,OAAO,KAAA,CAAM,OAAA,GAAU,EAAE,IAAA,EAAM,EAAA,CAAG,MAAM,KAAA,CAAM,CAAC,CAAA,EAAG,QAAA,CAAS,KAAA,CAAM,KAAK,CAAC,CAAA,EAAE,GAAI,EAAE,IAAA,EAAM,EAAE,CAAC,EAAE,GAAG,GAAG,KAAA,CAAM,KAAA,EAAM,EAAE;AAAA,IAChH,KAAK,QAAA,EAAU;AAGb,MAAA,MAAM,MAAA,GAAS,CAAC,IAAA,MAAkC,EAAE,IAAA,EAAM,MAAM,OAAA,GAAU,GAAA,CAAI,IAAI,CAAA,GAAI,IAAA,EAAK,CAAA;AAC3F,MAAA,IAAI,KAAA,CAAM,EAAA,KAAO,OAAA,EAAS,OAAO,MAAA,CAAO,GAAA,CAAI,EAAA,CAAG,SAAA,EAAW,KAAA,CAAM,CAAC,CAAC,CAAC,CAAC,CAAA;AACpE,MAAA,IAAI,KAAA,CAAM,EAAA,KAAO,WAAA,EAAa,OAAO,MAAA,CAAO,GAAG,SAAA,EAAW,KAAA,CAAM,CAAC,CAAC,CAAC,CAAA;AACnE,MAAA,IAAI,KAAA,CAAM,EAAA,KAAO,IAAA,IAAQ,KAAA,CAAM,OAAO,IAAA,EAAM;AAC1C,QAAA,IAAI,OAAO,KAAA,CAAM,GAAA,KAAQ,QAAA,EAAU,OAAO,IAAA;AAC1C,QAAA,OAAO,MAAA,CAAO,EAAA,CAAG,KAAA,CAAM,EAAA,EAAI,KAAA,CAAM,CAAC,CAAA,EAAG,QAAA,CAAS,KAAA,CAAM,GAAG,CAAC,CAAC,CAAA;AAAA,MAC3D;AACA,MAAA,IAAI,MAAM,EAAA,KAAO,IAAA,SAAa,OAAO,KAAA,CAAM,QAAQ,QAAA,GAAW,MAAA,CAAO,GAAG,IAAA,EAAM,KAAA,CAAM,CAAC,CAAA,EAAG,QAAA,CAAS,MAAM,GAAG,CAAC,CAAC,CAAA,GAAI,IAAA;AAChH,MAAA,IAAI,MAAM,EAAA,KAAO,IAAA,SAAa,OAAO,KAAA,CAAM,QAAQ,QAAA,GAAW,MAAA,CAAO,GAAG,IAAA,EAAM,KAAA,CAAM,CAAC,CAAA,EAAG,QAAA,CAAS,MAAM,GAAG,CAAC,CAAC,CAAA,GAAI,IAAA;AAChH,MAAA,MAAM,QAA0B,EAAC;AACjC,MAAA,IAAI,OAAO,KAAA,CAAM,GAAA,KAAQ,QAAA,QAAgB,IAAA,CAAK,EAAA,CAAG,KAAA,EAAO,KAAA,CAAM,CAAC,CAAA,EAAG,QAAA,CAAS,KAAA,CAAM,GAAG,CAAC,CAAC,CAAA;AACtF,MAAA,IAAI,OAAO,KAAA,CAAM,GAAA,KAAQ,QAAA,QAAgB,IAAA,CAAK,EAAA,CAAG,KAAA,EAAO,KAAA,CAAM,CAAC,CAAA,EAAG,QAAA,CAAS,KAAA,CAAM,GAAG,CAAC,CAAC,CAAA;AACtF,MAAA,IAAI,KAAA,CAAM,MAAA,KAAW,CAAA,EAAG,OAAO,IAAA;AAC/B,MAAA,MAAM,KAAA,GAAQ,KAAA,CAAM,MAAA,KAAW,CAAA,GAAI,KAAA,CAAM,CAAC,CAAA,GAAK,EAAA,CAAG,KAAA,EAAO,GAAG,KAAK,CAAA;AACjE,MAAA,OAAO,EAAE,IAAA,EAAM,KAAA,CAAM,UAAU,GAAA,CAAI,KAAK,IAAI,KAAA,EAAM;AAAA,IACpD;AAAA,IACA,KAAK,MAAA,EAAQ;AAMX,MAAA,MAAMC,KAAAA,GAA8B,MAAA;AACpC,MAAA,MAAM,OAAA,GAAU,gBAAA,CAAiB,KAAA,CAAM,MAAA,GAAS,EAAE,GAAG,KAAA,EAAO,MAAA,EAAQ,GAAA,CAAI,WAAA,EAAY,EAAE,GAAI,KAAA,EAAO,KAAKA,KAAI,CAAA;AAC1G,MAAA,MAAM,SAAA,GAAY,QAAS,CAAA,CAAE,MAAA,IAAU,EAAC,EAA+B,MAAM,CAAA,IAAK,EAAE,CAAA,KAAM,MAAA;AAC1F,MAAA,MAAM,MAAA,GAAS,SAAA,GAAY,mBAAA,CAAoB,OAAA,EAASA,KAAI,CAAA,GAAI,OAAA;AAChE,MAAA,IAAI,OAAO,KAAA,KAAU,MAAA,IAAa,MAAA,CAAO,KAAA,KAAU,QAAW,OAAO,IAAA;AACrE,MAAA,OAAO;AAAA,QACL,IAAA,EAAM;AAAA,UACJ,CAAC,CAAA,CAAE,GAAG,GAAG;AAAA,YACP,GAAI,OAAO,KAAA,KAAU,MAAA,GAAY,EAAC,GAAI,EAAE,IAAA,EAAM,MAAA,CAAO,KAAA,EAAM;AAAA,YAC3D,GAAI,OAAO,KAAA,KAAU,MAAA,GAAY,EAAC,GAAI,EAAE,EAAA,EAAI,MAAA,CAAO,KAAA;AAAM;AAC3D;AACF,OACF;AAAA,IACF;AAAA,IACA;AACE,MAAA,OAAO,GAAG,IAAI,CAAA,0CAAA,CAAA;AAAA;AAEpB;AAMO,SAAS,SAAA,CAAU,KAAa,SAAA,EAAkD;AACvF,EAAA,IAAI,CAAC,SAAA,EAAW,OAAO,iBAAiB,GAAA,EAA4B,MAAS,CAAA;AAC7E,EAAA,MAAM,CAAC,CAAA,EAAG,CAAA,EAAG,CAAC,CAAA,GAAI,IAAI,KAAA,CAAM,GAAG,CAAA,CAAE,GAAA,CAAI,MAAM,CAAA;AAC3C,EAAA,MAAM,GAAA,GAAM,CAAC,CAAA,KAAY,CAAA,EAAG,CAAA,CAAE,cAAA,EAAgB,CAAA,CAAA,EAAI,MAAA,CAAO,CAAA,CAAE,WAAA,EAAY,GAAI,CAAC,CAAA,CAAE,QAAA,CAAS,CAAA,EAAG,GAAG,CAAC,CAAA,CAAA,EAAI,MAAA,CAAO,CAAA,CAAE,UAAA,EAAY,CAAA,CAAE,QAAA,CAAS,CAAA,EAAG,GAAG,CAAC,CAAA,CAAA;AACzI,EAAA,OAAO,EAAE,IAAA,EAAM,GAAA,CAAI,IAAI,IAAA,CAAK,IAAA,CAAK,GAAA,CAAI,CAAA,EAAA,CAAK,CAAA,IAAK,CAAA,IAAK,CAAA,EAAG,CAAA,IAAK,CAAC,CAAC,CAAC,CAAA,EAAG,EAAA,EAAI,GAAA,CAAI,IAAI,IAAA,CAAK,IAAA,CAAK,IAAI,CAAA,EAAA,CAAK,CAAA,IAAK,CAAA,IAAK,CAAA,EAAA,CAAI,CAAA,IAAK,CAAA,IAAK,CAAC,CAAC,CAAC,CAAA,EAAE;AAChI;AAUO,SAAS,cACd,MAAA,EACA,IAAA,EACA,eACA,GAAA,mBAAY,IAAI,MAAK,EACN;AACf,EAAA,MAAM,UAAoB,EAAC;AAC3B,EAAA,MAAM,OAAqB,EAAC;AAC5B,EAAA,MAAM,QAA0B,EAAC;AACjC,EAAA,IAAI,QAAQ,YAAA,CAAa,IAAI,CAAA,EAAG,KAAA,CAAM,KAAK,IAAI,CAAA;AAAA,OAAA,IACtC,IAAA,EAAM,MAAA,CAAO,MAAA,CAAO,IAAA,EAAM,IAAI,CAAA;AACvC,EAAA,KAAA,MAAW,CAAC,GAAA,EAAK,KAAK,KAAK,MAAA,CAAO,OAAA,CAAQ,aAAa,CAAA,EAAG;AACxD,IAAA,IAAI,CAAC,KAAA,EAAO;AACZ,IAAA,MAAM,IAAI,MAAA,CAAO,IAAA,CAAK,CAAC,SAAA,KAAc,SAAA,CAAU,QAAQ,GAAG,CAAA;AAC1D,IAAA,IAAI,CAAC,CAAA,EAAG;AACR,IAAA,MAAM,MAAA,GAAS,SAAA,CAAU,CAAA,EAAG,KAAA,EAAO,GAAG,CAAA;AACtC,IAAA,IAAI,WAAW,IAAA,EAAM;AACrB,IAAA,IAAI,OAAO,WAAW,QAAA,EAAU;AAC9B,MAAA,OAAA,CAAQ,KAAK,MAAM,CAAA;AACnB,MAAA;AAAA,IACF;AACA,IAAA,IAAI,UAAU,MAAA,EAAQ,MAAA,CAAO,MAAA,CAAO,IAAA,EAAM,OAAO,IAAI,CAAA;AAAA,SAChD,KAAA,CAAM,IAAA,CAAK,MAAA,CAAO,IAAI,CAAA;AAAA,EAC7B;AACA,EAAA,MAAM,QAAA,GAAW,MAAA,CAAO,IAAA,CAAK,IAAI,CAAA;AACjC,EAAA,IAAI,KAAA,CAAM,MAAA,KAAW,CAAA,EAAG,OAAO,EAAE,MAAA,EAAQ,QAAA,CAAS,MAAA,GAAS,CAAA,GAAI,IAAA,GAAO,MAAA,EAAW,OAAA,EAAQ;AACzF,EAAA,KAAA,MAAW,OAAO,QAAA,EAAU;AAC1B,IAAA,MAAM,IAAI,MAAA,CAAO,IAAA,CAAK,CAAC,SAAA,KAAc,SAAA,CAAU,QAAQ,GAAG,CAAA;AAC1D,IAAA,MAAM,KAAA,GAAQ,KAAK,GAAG,CAAA;AACtB,IAAA,IAAI,CAAC,CAAA,EAAG;AACR,IAAA,IAAI,KAAA,KAAU,IAAA,IAAQ,OAAO,KAAA,KAAU,QAAA,EAAU;AAC/C,MAAA,KAAA,CAAM,IAAA,CAAK,YAAA,CAAa,CAAA,EAAG,KAAuC,CAAC,CAAA;AACnE,MAAA;AAAA,IACF;AACA,IAAA,KAAA,CAAM,IAAA,CAAK,UAAU,IAAA,GAAO,EAAA,CAAG,OAAO,EAAA,CAAG,SAAA,EAAW,MAAM,CAAC,CAAC,CAAC,CAAA,GAAI,EAAA,CAAG,MAAM,KAAA,CAAM,CAAC,GAAG,QAAA,CAAS,KAAK,CAAC,CAAC,CAAA;AAAA,EACtG;AACA,EAAA,OAAO,EAAE,MAAA,EAAQ,KAAA,CAAM,MAAA,KAAW,CAAA,GAAI,KAAA,CAAM,CAAC,CAAA,GAAK,EAAA,CAAG,KAAA,EAAO,GAAG,KAAK,GAAG,OAAA,EAAQ;AACjF;;;ACzQO,SAAS,WAAW,GAAA,EAA6B;AACtD,EAAA,MAAM,IAAK,GAAA,CAAwC,OAAA;AACnD,EAAA,OAAO,OAAO,CAAA,KAAM,QAAA,GAAW,CAAA,GAAI,IAAA;AACrC;;;ACUO,SAAS,gBAAgB,QAAA,EAA8D;AAC5F,EAAA,OAAO,QAAQ,QAAQ,CAAA,IAAK,EAAE,QAAA,CAAU,IAAA,IAAQ,SAAU,IAAA,CAAK,MAAA,GAAS,CAAA,CAAA,IAAM,CAAC,SAAU,MAAA,IAAU,EAAE,SAAU,KAAA,IAAS,QAAA,CAAU,MAAM,MAAA,GAAS,CAAA,CAAA;AACnJ;AAGO,SAAS,eAAA,CAAgB,MAAA,EAA0B,QAAA,EAAyB,IAAA,EAA0C;AAC3H,EAAA,IAAI,CAAC,UAAU,OAAO,IAAA;AACtB,EAAA,MAAMD,SAAQ,MAAA,CAAO,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,QAAQ,QAAQ,CAAA;AACnD,EAAA,IAAI,CAACA,QAAO,OAAO,IAAA;AACnB,EAAA,OAAO,CAAC,EAAE,KAAA,EAAO,UAAU,SAAA,EAAW,KAAA,EAAO,IAAI,SAAA,CAAUA,MAAK,GAAE,EAAG,GAAG,KAAK,MAAA,CAAO,CAAC,MAAM,CAAA,CAAE,KAAA,KAAU,QAAQ,CAAC,CAAA;AAClH;AAGA,SAAS,cAAc,MAAA,EAAyD;AAC9E,EAAA,OAAQ,KAAK,KAAA,CAAM,IAAA,CAAK,UAAU,MAAA,CAAO,OAAA,CAAQ,MAAM,CAAA,CAAE,IAAA,EAAM,CAAC,CAAA,CAA8B,OAAO,CAAC,GAAGE,GAAE,CAAA,KAAMA,OAAM,aAAa,CAAA;AACtI;AAGO,SAAS,kBAAkB,MAAA,EAAoD;AACpF,EAAA,OAAO,CAAC,EAAE,EAAA,EAAI,OAAA,EAAQ,EAAG,GAAG,aAAA,CAAc,MAAM,CAAA,CAAE,MAAA,CAAO,CAAC,GAAGA,GAAE,CAAA,KAAMA,GAAAA,KAAO,OAAO,CAAA,CAAE,GAAA,CAAI,CAAC,CAAC,GAAA,EAAKA,GAAE,CAAA,MAAO,EAAE,EAAA,EAAIA,GAAAA,EAA0B,GAAA,GAAM,CAAC,CAAA;AAClJ;AAGO,SAAS,mBAAmB,MAAA,EAAoD;AACrF,EAAA,OAAO,cAAc,MAAM,CAAA,CAAE,IAAI,CAAC,CAAC,KAAKA,GAAE,CAAA,KAAOA,QAAO,OAAA,GAAU,EAAE,IAAI,OAAA,EAAiB,GAAI,EAAE,EAAA,EAAIA,GAAAA,EAA0B,KAAM,CAAA;AACrI;AAGO,SAAS,gBAAgB,MAAA,EAAoC;AAClE,EAAA,OAAO,MAAA,CACJ,MAAA,CAAO,CAAC,CAAA,KAAM;AACb,IAAA,MAAM,IAAA,GAAO,MAAA,CAAO,aAAA,CAAc,CAAC,CAAC,CAAA;AACpC,IAAA,OAAO,IAAA,KAAS,YAAY,IAAA,KAAS,cAAA;AAAA,EACvC,CAAC,CAAA,CACA,GAAA,CAAI,CAAC,CAAA,KAAM,EAAE,GAAG,CAAA;AACrB;AAGO,IAAM,kBAAA,GAAqB,CAAA;AC2I3B,IAAM,cAAA,GAAiB,EAAA;AACvB,IAAM,cAAA,GAAiB,EAAA;AAQ9B,IAAM,eAAA,GAAkB;AAAA,EACtB,OAAA;AAAA,EACA,KAAA;AAAA,EACA,SAAA;AAAA,EACA,KAAA;AAAA,EACA,KAAA;AAAA,EACA,OAAA;AAAA,EACA,QAAA;AAAA,EACA,QAAA;AAAA,EACA,SAAA;AAAA,EACA,WAAA;AAAA,EACA;AACF,CAAA;AAEA,IAAM,YAAA,GAAe;AAAA,EACnB,WAAA;AAAA,EACA,YAAA;AAAA,EACA,YAAA;AAAA,EACA;AACF,CAAA;AAEA,SAAS,SAAS,CAAA,EAA0C;AAC1D,EAAA,OAAO,OAAO,MAAM,QAAA,IAAY,CAAA,KAAM,QAAQ,CAAC,KAAA,CAAM,QAAQ,CAAC,CAAA;AAChE;AAUO,SAAS,sBAAsB,GAAA,EAAgC;AACpE,EAAA,IAAI,KAAA,GAAQ,GAAA;AACZ,EAAA,IAAI,OAAO,UAAU,QAAA,EAAU;AAC7B,IAAA,MAAM,OAAA,GAAU,MAAM,IAAA,EAAK;AAC3B,IAAA,IAAI,OAAA,KAAY,EAAA,EAAI,OAAO,EAAC;AAC5B,IAAA,IAAI;AACF,MAAA,KAAA,GAAQ,IAAA,CAAK,MAAM,OAAO,CAAA;AAAA,IAC5B,CAAA,CAAA,MAAQ;AACN,MAAA,OAAO,EAAC;AAAA,IACV;AAAA,EACF;AACA,EAAA,IAAI,CAAC,QAAA,CAAS,KAAK,CAAA,SAAU,EAAC;AAE9B,EAAA,MAAM,MAAwB,EAAC;AAE/B,EAAA,MAAM,KAAA,GAAQ,eAAA,CAAgB,KAAA,CAAM,OAAO,CAAC,CAAA;AAC5C,EAAA,IAAI,CAAC,iBAAA,CAAkB,KAAK,CAAA,MAAO,KAAA,GAAQ,KAAA;AAE3C,EAAA,IAAI,QAAA,CAAS,KAAA,CAAM,SAAS,CAAC,CAAA,EAAG;AAC9B,IAAA,MAAM,UAA6C,EAAC;AACpD,IAAA,KAAA,MAAW,CAAC,KAAK,GAAG,CAAA,IAAK,OAAO,OAAA,CAAQ,KAAA,CAAM,SAAS,CAAC,CAAA,EAAG;AACzD,MAAA,IAAI,CAAC,QAAA,CAAS,GAAG,CAAA,EAAG;AACpB,MAAA,MAAM,EAAA,GAAK,IAAI,IAAI,CAAA;AACnB,MAAA,IAAI,OAAO,OAAO,QAAA,EAAU;AAC5B,MAAA,MAAM,OAAA,GAAU,IAAI,SAAS,CAAA;AAC7B,MAAA,OAAA,CAAQ,GAAG,CAAA,GAAI,QAAA,CAAS,OAAO,CAAA,GAC3B;AAAA,QACE,EAAA;AAAA,QACA;AAAA,OACF,GACA,EAAE,EAAA,EAAkC;AAAA,IAC1C;AACA,IAAA,IAAI,OAAO,IAAA,CAAK,OAAO,EAAE,MAAA,GAAS,CAAA,MAAO,OAAA,GAAU,OAAA;AAAA,EACrD;AAEA,EAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,KAAA,CAAM,QAAQ,CAAC,CAAA,EAAG;AAClC,IAAA,MAAM,MAAA,GAAS,MAAM,QAAQ,CAAA,CAAE,OAAO,CAAC,CAAA,KAAmB,OAAO,CAAA,KAAM,QAAQ,CAAA;AAC/E,IAAA,IAAI,MAAA,CAAO,MAAA,GAAS,CAAA,EAAG,GAAA,CAAI,MAAA,GAAS,MAAA;AAAA,EACtC;AAEA,EAAA,MAAM,QAAA,GAAW,aAAA,CAAc,KAAA,CAAM,UAAU,CAAC,CAAA;AAChD,EAAA,IAAI,QAAA,MAAc,QAAA,GAAW,QAAA;AAE7B,EAAA,IAAI,QAAA,CAAS,KAAA,CAAM,QAAQ,CAAC,CAAA,EAAG;AAC7B,IAAA,MAAM,SAAiC,EAAC;AACxC,IAAA,KAAA,MAAW,CAAC,KAAKC,IAAG,CAAA,IAAK,OAAO,OAAA,CAAQ,KAAA,CAAM,QAAQ,CAAC,CAAA,EAAG;AAGxD,MAAA,IAAI,OAAOA,SAAQ,QAAA,IAAY,CAAC,OAAO,QAAA,CAASA,IAAG,CAAA,IAAKA,IAAAA,IAAO,CAAA,EAAG;AAClE,MAAA,MAAA,CAAO,GAAG,CAAA,GAAI,IAAA,CAAK,KAAA,CAAMA,IAAG,CAAA;AAAA,IAC9B;AACA,IAAA,IAAI,OAAO,IAAA,CAAK,MAAM,EAAE,MAAA,GAAS,CAAA,MAAO,MAAA,GAAS,MAAA;AAAA,EACnD;AAEA,EAAA,MAAM,SAAA,GAAY,MAAM,WAAW,CAAA;AACnC,EAAA,IACE,OAAO,SAAA,KAAc,QAAA,IACrB,MAAA,CAAO,QAAA,CAAS,SAAS,CAAA,IACzB,SAAA,IAAa,cAAA,IACb,SAAA,IAAa,cAAA,EACb;AACA,IAAA,GAAA,CAAI,SAAA,GAAY,IAAA,CAAK,KAAA,CAAM,SAAS,CAAA;AAAA,EACtC;AAEA,EAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,KAAA,CAAM,cAAc,CAAC,CAAA,EAAG;AACxC,IAAA,MAAM,YAAA,GAAe,MAAM,cAAc,CAAA,CAAE,OAAO,CAAC,CAAA,KAAmB,OAAO,CAAA,KAAM,QAAQ,CAAA;AAC3F,IAAA,IAAI,YAAA,CAAa,MAAA,GAAS,CAAA,EAAG,GAAA,CAAI,YAAA,GAAe,YAAA;AAAA,EAClD;AACA,EAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,KAAA,CAAM,YAAY,CAAC,CAAA,EAAG;AACtC,IAAA,GAAA,CAAI,UAAA,GAAa,MAAM,YAAY,CAAA,CAAE,OAAO,CAAC,CAAA,KAAmB,OAAO,CAAA,KAAM,QAAQ,CAAA;AAAA,EACvF;AAEA,EAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,KAAA,CAAM,aAAa,CAAC,CAAA,EAAG;AACvC,IAAA,MAAM,WAAA,GAAc,MAAM,aAAa,CAAA,CAAE,OAAO,CAAC,CAAA,KAAmB,OAAO,CAAA,KAAM,QAAQ,CAAA;AACzF,IAAA,IAAI,WAAA,CAAY,MAAA,GAAS,CAAA,EAAG,GAAA,CAAI,WAAA,GAAc,WAAA;AAAA,EAChD;AAEA,EAAA,MAAM,WAAA,GAAc,MAAM,aAAa,CAAA;AACvC,EAAA,IAAI,WAAA,KAAgB,OAAA,IAAW,WAAA,KAAgB,QAAA,IAAY,gBAAgB,OAAA,EAAS;AAClF,IAAA,GAAA,CAAI,WAAA,GAAc,WAAA;AAAA,EACpB;AAEA,EAAA,IAAI,OAAO,MAAM,MAAM,CAAA,KAAM,WAAW,GAAA,CAAI,IAAA,GAAO,MAAM,MAAM,CAAA;AAC/D,EAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,KAAA,CAAM,aAAa,CAAC,CAAA,EAAG;AACvC,IAAA,MAAM,WAAA,GAAc,MAAM,aAAa,CAAA,CAAE,OAAO,CAAC,CAAA,KAAmB,OAAO,CAAA,KAAM,QAAQ,CAAA;AACzF,IAAA,IAAI,WAAA,CAAY,MAAA,GAAS,CAAA,EAAG,GAAA,CAAI,WAAA,GAAc,WAAA;AAAA,EAChD;AACA,EAAA,MAAM,GAAA,GAAM,MAAM,KAAK,CAAA;AACvB,EAAA,IAAI,QAAQ,MAAA,IAAU,GAAA,KAAQ,SAAS,GAAA,KAAQ,QAAA,MAAc,GAAA,GAAM,GAAA;AACnE,EAAA,IAAI,OAAO,MAAM,aAAa,CAAA,KAAM,WAAW,GAAA,CAAI,WAAA,GAAc,MAAM,aAAa,CAAA;AACpF,EAAA,MAAM,MAAA,GAAS,MAAM,QAAQ,CAAA;AAC7B,EAAA,IAAI,MAAA,KAAW,QAAA,IAAY,MAAA,KAAW,QAAA,MAAc,MAAA,GAAS,MAAA;AAC7D,EAAA,IAAI,QAAA,CAAS,KAAA,CAAM,WAAW,CAAC,CAAA,EAAG;AAChC,IAAA,MAAM,YAAoC,EAAC;AAC3C,IAAA,KAAA,MAAW,CAAC,KAAKD,GAAE,CAAA,IAAK,OAAO,OAAA,CAAQ,KAAA,CAAM,WAAW,CAAC,CAAA,EAAG;AAE1D,MAAA,IAAI,OAAOA,QAAO,QAAA,IAAYA,GAAAA,CAAG,MAAK,KAAM,EAAA,EAAI,SAAA,CAAU,GAAG,CAAA,GAAIA,GAAAA;AAAA,IACnE;AACA,IAAA,IAAI,OAAO,IAAA,CAAK,SAAS,EAAE,MAAA,GAAS,CAAA,MAAO,SAAA,GAAY,SAAA;AAAA,EACzD;AAEA,EAAA,OAAO,GAAA;AACT;AAOA,SAAS,cAAc,GAAA,EAAwC;AAC7D,EAAA,IAAI,CAAC,QAAA,CAAS,GAAG,CAAA,EAAG,OAAO,MAAA;AAC3B,EAAA,MAAMF,MAAAA,GAAQ,IAAI,OAAO,CAAA;AACzB,EAAA,IAAI,OAAOA,MAAAA,KAAU,QAAA,IAAYA,OAAM,IAAA,EAAK,KAAM,IAAI,OAAO,MAAA;AAE7D,EAAA,MAAM,GAAA,GAAoB,EAAE,KAAA,EAAAA,MAAAA,EAAM;AAElC,EAAA,IAAI,QAAA,CAAS,GAAA,CAAI,YAAY,CAAC,CAAA,EAAG;AAC/B,IAAA,MAAM,aAAiD,EAAC;AACxD,IAAA,KAAA,MAAW,CAAC,KAAK,IAAI,CAAA,IAAK,OAAO,OAAA,CAAQ,GAAA,CAAI,YAAY,CAAC,CAAA,EAAG;AAC3D,MAAA,IAAI,CAAC,QAAA,CAAS,IAAI,CAAA,EAAG;AACrB,MAAA,MAAM,IAAA,GAAO,KAAK,MAAM,CAAA;AACxB,MAAA,IAAI,OAAO,SAAS,QAAA,EAAU;AAC9B,MAAA,IAAI,CAAE,eAAA,CAAsC,QAAA,CAAS,IAAI,CAAA,EAAG;AAC5D,MAAA,MAAM,KAAA,GAAQ,KAAK,OAAO,CAAA;AAC1B,MAAA,UAAA,CAAW,GAAG,CAAA,GACZ,OAAO,KAAA,KAAU,QAAA,GACb,EAAE,IAAA,EAAkC,KAAA,EAAM,GAC1C,EAAE,IAAA,EAAiC;AAAA,IAC3C;AACA,IAAA,IAAI,OAAO,IAAA,CAAK,UAAU,EAAE,MAAA,GAAS,CAAA,MAAO,UAAA,GAAa,UAAA;AAAA,EAC3D;AAEA,EAAA,MAAM,KAAA,GAAQ,IAAI,OAAO,CAAA;AACzB,EAAA,IAAI,OAAO,KAAA,KAAU,QAAA,IAAa,YAAA,CAAmC,QAAA,CAAS,KAAK,CAAA,EAAG;AACpF,IAAA,GAAA,CAAI,KAAA,GAAQ,KAAA;AAAA,EACd;AAEA,EAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,GAAA,CAAI,WAAW,CAAC,CAAA,EAAG;AACnC,IAAA,MAAM,SAAA,GAAY,IAAI,WAAW,CAAA,CAAE,OAAO,CAAC,CAAA,KAAmB,OAAO,CAAA,KAAM,QAAQ,CAAA;AACnF,IAAA,IAAI,SAAA,CAAU,MAAA,GAAS,CAAA,EAAG,GAAA,CAAI,SAAA,GAAY,SAAA;AAAA,EAC5C;AAEA,EAAA,IAAI,GAAA,CAAI,YAAY,CAAA,KAAM,IAAA,MAAU,UAAA,GAAa,IAAA;AAEjD,EAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,GAAA,CAAI,MAAM,CAAC,CAAA,EAAG;AAC9B,IAAA,MAAM,OAAuB,EAAC;AAC9B,IAAA,KAAA,MAAW,KAAA,IAAS,GAAA,CAAI,MAAM,CAAA,EAAG;AAC/B,MAAA,IAAI,CAAC,QAAA,CAAS,KAAK,CAAA,EAAG;AACtB,MAAA,MAAM,GAAA,GAAM,MAAM,OAAO,CAAA;AACzB,MAAA,IAAI,OAAO,GAAA,KAAQ,QAAA,IAAY,GAAA,CAAI,IAAA,OAAW,EAAA,EAAI;AAClD,MAAA,MAAM,QAAA,GAAW,MAAM,OAAO,CAAA;AAC9B,MAAA,MAAM,QAAA,GAAW,MAAM,OAAO,CAAA;AAC9B,MAAA,IAAA,CAAK,IAAA,CAAK;AAAA,QACR,KAAA,EAAO,GAAA;AAAA,QACP,GAAI,OAAO,QAAA,KAAa,QAAA,IAAa,YAAA,CAAmC,QAAA,CAAS,QAAQ,CAAA,GACrF,EAAE,KAAA,EAAO,QAAA,EAAuB,GAChC,EAAC;AAAA,QACL,GAAI,OAAO,QAAA,KAAa,QAAA,IAAY,QAAA,KAAa,KAAK,EAAE,KAAA,EAAO,QAAA,EAAS,GAAI;AAAC,OAC9E,CAAA;AAAA,IACH;AACA,IAAA,IAAI,IAAA,CAAK,SAAS,CAAA,EAAG,GAAA,CAAI,OAAO,IAAA,CAAK,KAAA,CAAM,GAAG,CAAC,CAAA;AAAA,EACjD;AAEA,EAAA,MAAM,KAAA,GAAQ,IAAI,OAAO,CAAA;AACzB,EAAA,IAAI,OAAO,KAAA,KAAU,QAAA,IAAY,KAAA,KAAU,EAAA,MAAQ,KAAA,GAAQ,KAAA;AAC3D,EAAA,MAAM,MAAA,GAAS,IAAI,QAAQ,CAAA;AAC3B,EAAA,IAAI,OAAO,WAAW,QAAA,IAAY,MAAA,CAAO,MAAK,KAAM,EAAA,MAAQ,MAAA,GAAS,MAAA;AACrE,EAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,GAAA,CAAI,MAAM,CAAC,CAAA,EAAG;AAC9B,IAAA,MAAM,IAAA,GAAO,GAAA,CAAI,MAAM,CAAA,CAAE,MAAA,CAAO,CAAC,CAAA,KAAmB,OAAO,CAAA,KAAM,QAAA,IAAY,CAAA,KAAM,EAAE,CAAA;AACrF,IAAA,IAAI,IAAA,CAAK,MAAA,GAAS,CAAA,EAAG,GAAA,CAAI,IAAA,GAAO,IAAA;AAAA,EAClC;AACA,EAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,GAAA,CAAI,OAAO,CAAC,CAAA,EAAG;AAC/B,IAAA,MAAM,QAAyB,EAAC;AAChC,IAAA,KAAA,MAAW,IAAA,IAAQ,GAAA,CAAI,OAAO,CAAA,EAAG;AAC/B,MAAA,IAAI,CAAC,QAAA,CAAS,IAAI,CAAA,IAAK,OAAO,IAAA,CAAK,KAAK,CAAA,KAAM,QAAA,IAAY,IAAA,CAAK,KAAK,CAAA,KAAM,EAAA,EAAI;AAC9E,MAAA,MAAM,KAAA,GAAQ,KAAK,OAAO,CAAA;AAC1B,MAAA,IAAI,KAAA,KAAU,IAAA,IAAQ,OAAO,KAAA,KAAU,QAAA,EAAU;AACjD,MAAA,KAAA,CAAM,KAAK,EAAE,GAAA,EAAK,KAAK,KAAK,CAAA,EAAG,OAAO,CAAA;AAAA,IACxC;AACA,IAAA,IAAI,KAAA,CAAM,MAAA,GAAS,CAAA,EAAG,GAAA,CAAI,KAAA,GAAQ,KAAA;AAAA,EACpC;AAEA,EAAA,OAAO,GAAA;AACT;AC7SO,IAAM,kBAAA,GAAwC,CAAC,MAAA,EAAQ,QAAA,EAAU,UAAA,EAAY,SAAA,EAAW,GAA2B,CAAC,UAAU,CAAA,GAAS,MAAM,CAAA;AA8CpJ,IAAM,KAAA,GAA2B,kBAAA;AAG1B,SAAS,YAAA,CAAa,YAAwD,UAAA,EAAgD;AACnI,EAAA,MAAM,IAAA,GAAO,cAAc,EAAC;AAC5B,EAAA,MAAM,IAAA,GAAO,CAAC,GAAA,KAA+B;AAC3C,IAAA,MAAM,CAAA,GAAI,KAAK,GAAG,CAAA;AAClB,IAAA,OAAO,OAAO,CAAA,KAAM,QAAA,IAAY,CAAA,KAAM,KAAK,CAAA,GAAI,IAAA;AAAA,EACjD,CAAA;AACA,EAAA,MAAM,MAAA,GAAS,MAAA,CAAO,IAAA,CAAK,QAAQ,KAAK,MAAM,CAAA;AAC9C,EAAA,MAAM,IAAA,GAAO,eAAe,QAAA,CAAS,IAAA,CAAK,SAAS,CAAC,CAAA,GAAK,IAAA,CAAK,SAAS,CAAA,GAAqB,IAAA,CAAA;AAC5F,EAAA,MAAM,KAAA,GAAQ,KAAK,YAAY,CAAA;AAC/B,EAAA,OAAO;AAAA,IACL,MAAA,EAAS,KAAA,CAA4B,QAAA,CAAS,MAAM,IAAK,MAAA,GAAoC,MAAA;AAAA,IAC7F,OAAA,EAAS,QAAQ,MAAA,CAAO,IAAA,CAAK,IAAI,CAAA,CAAE,MAAA,GAAS,IAAI,IAAA,GAAO,IAAA;AAAA,IACvD,KAAA,EAAO,OAAO,IAAA,CAAK,OAAO,CAAC,CAAA,GAAK,IAAA,CAAK,OAAO,CAAA,GAAuB,IAAA;AAAA,IACnE,UAAA,EAAY,KAAK,aAAa,CAAA;AAAA,IAC9B,aAAA,EAAe,KAAK,gBAAgB,CAAA;AAAA,IACpC,gBAAA,EAAkB,KAAA,CAAM,OAAA,CAAQ,IAAA,CAAK,mBAAmB,CAAC,CAAA,GACpD,IAAA,CAAK,mBAAmB,CAAA,CAAgB,MAAA,CAAO,CAAC,CAAA,KAAM,CAAC,QAAA,EAAU,QAAA,EAAU,SAAS,CAAA,CAAE,QAAA,CAAS,OAAO,CAAC,CAAC,CAAA,CAAE,GAAA,CAAI,MAAM,CAAA,GACrH,EAAC;AAAA,IACL,OAAA,EAAS,KAAK,SAAS,CAAA;AAAA,IACvB,SAAA,EAAW,KAAK,YAAY,CAAA;AAAA,IAC5B,UAAA,EAAY,KAAK,aAAa,CAAA;AAAA,IAC9B,QAAA,EAAU,KAAK,WAAW,CAAA;AAAA,IAC1B,UAAA,EAAY,KAAK,aAAa,CAAA;AAAA,IAC9B,KAAA,EAAO,UAAA,CAAW,IAAA,CAAK,OAAO,CAAC,CAAA;AAAA,IAC/B,MAAA,EAAQ,KAAK,SAAS,CAAA;AAAA,IACtB,WAAW,IAAA,CAAK,YAAY,MAAM,IAAA,IAAQ,IAAA,CAAK,YAAY,CAAA,KAAM,MAAA;AAAA,IACjE,YAAA,EAAc,qBAAA,CAAsB,IAAA,CAAK,cAAc,CAAC,CAAA;AAAA,IACxD,SAAA,EAAW,QAAA,CAAS,KAAK,CAAA,GAAK,KAAA,GAAoC;AAAA,GACpE;AACF;AA2EA,SAAS,SAAS,GAAA,EAAuB;AACvC,EAAA,OAAO,OAAA,CAAQ,GAAG,CAAA,IAAK,OAAO,QAAQ,QAAA,IAAY,CAAC,KAAA,CAAM,OAAA,CAAQ,GAAG,CAAA;AACtE;AAEA,SAAS,OAAO,GAAA,EAAuB;AACrC,EAAA,IAAI,CAAC,QAAA,CAAS,GAAG,CAAA,EAAG,OAAO,KAAA;AAC3B,EAAA,MAAM,IAAA,GAAO,GAAA;AACb,EAAA,OAAO,OAAO,IAAA,CAAK,EAAA,KAAO,YAAY,KAAA,CAAM,OAAA,CAAQ,KAAK,IAAI,CAAA;AAC/D;AAEA,SAAS,WAAW,GAAA,EAA6B;AAC/C,EAAA,IAAI,IAAA,GAAgB,GAAA;AACpB,EAAA,IAAI,OAAO,QAAQ,QAAA,EAAU;AAC3B,IAAA,IAAI;AACF,MAAA,IAAA,GAAO,GAAA,CAAI,MAAK,KAAM,EAAA,GAAK,EAAC,GAAI,IAAA,CAAK,MAAM,GAAG,CAAA;AAAA,IAChD,CAAA,CAAA,MAAQ;AACN,MAAA,OAAO,EAAC;AAAA,IACV;AAAA,EACF;AACA,EAAA,IAAI,CAAC,KAAA,CAAM,OAAA,CAAQ,IAAI,CAAA,SAAU,EAAC;AAClC,EAAA,OAAO,IAAA,CACJ,MAAA,CAAO,CAAC,CAAA,KAAkD,QAAA,CAAS,CAAC,CAAA,IAAK,OAAQ,CAAA,CAA0B,KAAA,KAAU,QAAQ,CAAA,CAC7H,IAAI,CAAC,CAAA,MAAO,EAAE,KAAA,EAAO,CAAA,CAAE,KAAA,EAAO,SAAA,EAAW,CAAA,CAAE,SAAA,KAAc,MAAA,GAAS,MAAA,GAAS,KAAA,EAAM,CAAE,CAAA;AACxF;;;AC/PO,IAAM,eAAe,CAAC,MAAA,EAAQ,UAAU,UAAA,EAAY,SAAA,EAAW,YAAY,MAAM,CAAA;AA0IjF,SAAS,YAAY,UAAA,EAAmE;AAC7F,EAAA,MAAM,GAAA,GAAM,aAAa,OAAO,CAAA;AAChC,EAAA,OAAO,GAAA,KAAQ,QAAA,GAAW,QAAA,GAAW,GAAA,KAAQ,WAAW,QAAA,GAAW,IAAA;AACrE;AA2FO,SAAS,cAAsD,KAAA,EAAoC;AACxG,EAAA,OAAO,KAAA,CAAM,IAAA,CAAK,CAAC,CAAA,KAAM,EAAE,SAAS,CAAA;AACtC;AA2JO,SAAS,YAAY,GAAA,EAA8B;AACxD,EAAA,MAAM,IAAA,GAAQ,GAAA,CAAI,UAAA,IAAc,EAAC;AACjC,EAAA,MAAM,CAAA,GAAI,YAAA,CAAa,IAAA,EAAM,GAAA,CAAI,WAAW,MAAA,CAAO,IAAA,CAAK,GAAA,CAAI,OAAO,CAAA,CAAE,MAAA,GAAS,CAAA,GAAI,GAAA,CAAI,UAAU,IAAI,CAAA;AACpG,EAAA,MAAM,mBAAA,GAAsB,OAAO,IAAA,CAAK,UAAU,CAAA,KAAM,QAAA,IAAY,IAAA,CAAK,UAAU,CAAA,KAAM,EAAA,GAAM,IAAA,CAAK,UAAU,CAAA,GAAe,IAAA;AAC7H,EAAA,OAAO;AAAA,IACL,IAAI,GAAA,CAAI,OAAA;AAAA,IACR,IAAA,EAAM,IAAI,IAAA,IAAQ,eAAA;AAAA,IAClB,OAAA,EAAU,GAAA,CAAI,QAAA,IAAY,mBAAA,IAAuB,EAAA;AAAA,IACjD,QAAQ,CAAA,CAAE,MAAA;AAAA,IACV,YAAY,CAAA,CAAE,UAAA;AAAA,IACd,eAAe,CAAA,CAAE,aAAA;AAAA,IACjB,kBAAkB,CAAA,CAAE,gBAAA;AAAA,IACpB,SAAS,CAAA,CAAE,OAAA;AAAA,IACX,WAAW,CAAA,CAAE,SAAA;AAAA,IACb,YAAY,CAAA,CAAE,UAAA;AAAA,IACd,UAAU,CAAA,CAAE,QAAA;AAAA,IACZ,YAAY,CAAA,CAAE,UAAA;AAAA,IACd,OAAO,CAAA,CAAE,KAAA;AAAA,IACT,KAAA,EAAO,YAAY,IAAI,CAAA;AAAA,IACvB,QAAQ,CAAA,CAAE,MAAA;AAAA,IACV,GAAI,EAAE,OAAA,GAAU,EAAE,SAAS,CAAA,CAAE,OAAA,KAAY,EAAC;AAAA,IAC1C,OAAO,CAAA,CAAE,KAAA;AAAA;AAAA;AAAA;AAAA,IAIT,WAAW,CAAA,CAAE,SAAA,IAAa,CAAA,CAAE,SAAA,GAAY,MAAM,CAAA,KAAM,cAAA;AAAA,IACpD,cAAc,CAAA,CAAE,YAAA;AAAA,IAChB,WAAW,CAAA,CAAE;AAAA,GACf;AACF;;;AC3XO,SAAS,iBAAiB,GAAA,EAA2D;AAC1F,EAAA,MAAM,IAAA,GAAO,OAAO,EAAC;AACrB,EAAA,MAAM,IAAA,GAAO,aAAa,IAAI,CAAA;AAC9B,EAAA,MAAM,MAAgB,EAAC;AACvB,EAAA,MAAM,GAAA,GAAM,CAAC,GAAA,KAAgB,MAAA,CAAO,UAAU,cAAA,CAAe,IAAA,CAAK,MAAM,GAAG,CAAA;AAC3E,EAAA,IAAI,GAAA,CAAI,QAAQ,CAAA,IAAM,YAAA,CAAmC,QAAA,CAAS,OAAO,IAAA,CAAK,QAAQ,CAAC,CAAC,CAAA,EAAG;AACzF,IAAA,GAAA,CAAI,MAAA,GAAS,KAAK,QAAQ,CAAA;AAAA,EAC5B;AACA,EAAA,IAAI,GAAA,CAAI,aAAa,CAAA,EAAG,GAAA,CAAI,aAAa,IAAA,CAAK,UAAA;AAC9C,EAAA,IAAI,GAAA,CAAI,gBAAgB,CAAA,EAAG,GAAA,CAAI,gBAAgB,IAAA,CAAK,aAAA;AACpD,EAAA,IAAI,GAAA,CAAI,mBAAmB,CAAA,EAAG,GAAA,CAAI,mBAAmB,IAAA,CAAK,gBAAA;AAC1D,EAAA,IAAI,GAAA,CAAI,SAAS,CAAA,EAAG,GAAA,CAAI,UAAU,IAAA,CAAK,OAAA;AACvC,EAAA,IAAI,GAAA,CAAI,YAAY,CAAA,EAAG,GAAA,CAAI,YAAY,IAAA,CAAK,SAAA;AAC5C,EAAA,IAAI,GAAA,CAAI,aAAa,CAAA,EAAG,GAAA,CAAI,aAAa,IAAA,CAAK,UAAA;AAC9C,EAAA,IAAI,GAAA,CAAI,WAAW,CAAA,EAAG,GAAA,CAAI,WAAW,IAAA,CAAK,QAAA;AAC1C,EAAA,IAAI,GAAA,CAAI,aAAa,CAAA,EAAG,GAAA,CAAI,aAAa,IAAA,CAAK,UAAA;AAC9C,EAAA,IAAI,GAAA,CAAI,OAAO,CAAA,EAAG,GAAA,CAAI,QAAQ,IAAA,CAAK,KAAA;AACnC,EAAA,IAAI,GAAA,CAAI,OAAO,CAAA,EAAG,GAAA,CAAI,QAAQ,IAAA,CAAK,KAAA;AACnC,EAAA,IAAI,GAAA,CAAI,cAAc,CAAA,EAAG,GAAA,CAAI,eAAe,qBAAA,CAAsB,IAAA,CAAK,cAAc,CAAC,CAAA;AACtF,EAAA,OAAO,GAAA;AACT;AAGO,SAAS,SAAA,CAAU,MAAiB,IAAA,EAA8C;AACvF,EAAA,IAAI,CAAC,MAAM,OAAO,IAAA;AAClB,EAAA,OAAO,EAAE,GAAG,IAAA,EAAM,GAAG,IAAA,EAAK;AAC5B;AAwBA,eAAsB,SAAA,CACpB,QACA,OAAA,EACkD;AAClD,EAAA,MAAM,WAAW,MAAM,MAAA,CAAO,aAAa,EAAE,QAAA,EAAU,SAAS,CAAA;AAChE,EAAA,IAAI,CAAC,QAAA,CAAS,EAAA,EAAI,OAAO,QAAA;AACzB,EAAA,MAAM,MAAgC,EAAC;AACvC,EAAA,KAAA,MAAW,CAAC,MAAA,EAAQ,GAAG,CAAA,IAAK,MAAA,CAAO,QAAQ,QAAA,CAAS,IAAA,IAAQ,EAAE,CAAA,EAAG;AAC/D,IAAA,GAAA,CAAI,MAAM,CAAA,GAAI,gBAAA,CAAiB,GAAA,EAAK,QAAQ,IAAI,CAAA;AAAA,EAClD;AACA,EAAA,OAAO,EAAE,EAAA,EAAI,IAAA,EAAM,IAAA,EAAM,GAAA,EAAI;AAC/B;;;ACjIO,SAAS,gBAAA,CAAiB,OAAA,GAAiF,EAAC,EAAU;AAC3H,EAAA,MAAM,MAAA,GAAA,CAAU,OAAA,CAAQ,MAAA,IAAU,EAAA,EAAI,IAAA,EAAK;AAC3C,EAAA,OAAO,EAAE,GAAI,OAAA,CAAQ,QAAA,GAAW,EAAE,UAAU,OAAA,CAAQ,QAAA,EAAS,GAAI,IAAK,GAAI,MAAA,GAAS,EAAE,MAAA,EAAO,GAAI,EAAC,EAAG;AACtG;AAaO,SAAS,oBAAoB,KAAA,EAA4E;AAC9G,EAAA,MAAM,CAAA,GAAI,KAAA,CAAM,OAAA,IAAW,EAAE,IAAI,OAAA,EAAiB;AAClD,EAAA,OAAO;AAAA,IACL,GAAI,KAAA,CAAM,EAAA,GAAK,EAAE,EAAA,EAAI,CAAC,KAAA,CAAM,EAAE,CAAA,EAAE,GAAI,EAAC;AAAA,IACrC,IAAA,EAAM,CAAC,CAAA,CAAE,EAAA,KAAO,OAAA,GAAU,OAAA,GAAU,EAAE,EAAA,EAAI,CAAA,CAAE,EAAA,EAAI,EAAA,EAAI,CAAA,CAAE,IAAI,CAAA;AAAA,IAC1D,GAAI,MAAM,KAAA,GAAQ,EAAE,OAAO,KAAA,CAAM,KAAA,KAAU,EAAC;AAAA,IAC5C,GAAI,MAAM,YAAA,GAAe,EAAE,cAAc,KAAA,CAAM,YAAA,KAAiB;AAAC,GACnE;AACF;;;ACPA,SAAS,eAAe,KAAA,EAAa;AACnC,EAAA,MAAM,SAAA,GAAY,mBAAA,CAAoB,mBAAA,CAAoB,KAAA,CAAM,UAAU,CAAC,CAAA;AAC3E,EAAA,MAAM,MAAA,GAAS,mBAAA,CAAoB,EAAE,UAAA,EAAY,SAAA,CAAU,MAAA,EAAQ,cAAA,EAAgB,KAAA,CAAM,cAAA,EAAgB,KAAA,EAAO,KAAA,CAAM,KAAA,EAAO,SAAS,MAAM;AAAA,EAAC,GAAG,CAAA;AAChJ,EAAA,OAAO,EAAE,WAAW,MAAA,EAAO;AAC7B;AAGO,SAAS,cAAc,KAAA,EAA2D;AACvF,EAAA,MAAM,OAAO,KAAA,CAAM,MAAA,CAAO,CAAC,CAAA,KAAwB,OAAA,CAAQ,CAAC,CAAC,CAAA;AAC7D,EAAA,MAAM,OAAA,uBAAc,GAAA,EAA4C;AAChE,EAAA,KAAA,MAAW,QAAQ,IAAA,EAAM,KAAA,MAAW,KAAK,IAAA,CAAK,OAAA,UAAiB,GAAA,CAAI,CAAA,EAAG,CAAA,CAAE,IAAI,IAAI,IAAA,CAAK,SAAA,CAAU,EAAE,IAAI,CAAC,IAAI,CAAC,CAAA;AAC3G,EAAA,OAAO,EAAE,EAAA,EAAI,IAAA,CAAK,MAAA,GAAS,IAAA,CAAK,IAAI,GAAG,IAAA,CAAK,GAAA,CAAI,CAAC,CAAA,KAAM,CAAA,CAAE,EAAE,CAAC,CAAA,GAAI,IAAA,CAAK,GAAA,EAAI,EAAG,OAAA,EAAS,CAAC,GAAG,OAAA,CAAQ,MAAA,EAAQ,CAAA,EAAE;AAC7G;AAQA,eAAsB,mBACpB,KAAA,EACsB;AACtB,EAAA,MAAM,SAAA,GAAY,mBAAA,CAAoB,mBAAA,CAAoB,KAAA,CAAM,UAAU,CAAC,CAAA;AAC3E,EAAA,MAAM,IAAA,GAAO,cAAA,CAAe,SAAA,CAAU,MAAM,CAAA;AAC5C,EAAA,IAAI;AACF,IAAA,MAAM,QAAQ,GAAA,CAAI,CAAC,KAAK,QAAA,CAAS,KAAA,CAAM,KAAK,CAAA,EAAG,IAAA,CAAK,KAAK,KAAA,CAAM,KAAA,EAAO,iBAAiB,KAAA,CAAM,QAAA,IAAY,EAAE,CAAC,CAAC,CAAC,CAAA;AAAA,EAChH,CAAA,CAAA,MAAQ;AAAA,EAER;AACA,EAAA,OAAO,UAAU,IAAA,EAAK;AACxB;AAMA,eAAsB,aACpB,KAAA,EAIsB;AACtB,EAAA,MAAM,EAAE,SAAA,EAAW,MAAA,EAAO,GAAI,eAAe,KAAK,CAAA;AAClD,EAAA,IAAI;AACF,IAAA,MAAM,MAAA,CAAO,QAAA,CAAS,EAAE,MAAA,EAAQ,KAAA,CAAM,MAAA,EAAuB,QAAA,EAAU,mBAAA,CAAoB,KAAA,CAAM,QAAA,IAAY,EAAE,GAAG,CAAA;AAAA,EACpH,CAAA,CAAA,MAAQ;AAAA,EAER;AACA,EAAA,OAAO,UAAU,IAAA,EAAK;AACxB;AAUA,eAAsB,YAAY,KAAA,EAAoF;AACpH,EAAA,MAAM,EAAE,SAAA,EAAW,MAAA,EAAO,GAAI,eAAe,KAAK,CAAA;AAClD,EAAA,MAAM,KAAA,GAAQ,KAAA,CAAM,OAAA,CAAQ,KAAA,CAAM,IAAI,IAAI,KAAA,CAAM,IAAA,GAAO,CAAC,KAAA,CAAM,IAAI,CAAA;AAClE,EAAA,MAAM,OAAA,CAAQ,IAAI,KAAA,CAAM,GAAA,CAAI,CAAC,IAAA,KAAS,MAAA,CAAO,gBAAgB,EAAE,QAAA,EAAU,MAAM,OAAA,EAAS,GAAG,MAAM,CAAA,CAAE,MAAM,MAAM,IAAI,CAAC,CAAC,CAAA;AACrH,EAAA,OAAO,UAAU,IAAA,EAAK;AACxB;;;AC9DO,IAAM,eAAA,GAAkB;AAMxB,IAAM,kBAAA,GAAqB;AAgB3B,SAAS,YAAA,CAAa,OAAwD,MAAA,EAAmD;AACtI,EAAA,IAAI,CAAC,OAAO,OAAO,MAAA;AACnB,EAAA,MAAM,IAAA,GAAgC,KAAA,CAAM,KAAA,KAAU,KAAA,CAAM,OAAA,IAAW,MAAA,CAAO,IAAA,CAAK,KAAA,CAAM,OAAO,CAAA,CAAE,MAAA,GAAS,CAAA,GAAI,MAAM,OAAA,GAAU,MAAA,CAAA;AAC/H,EAAA,OAAO,aAAA,CAAc,MAAA,EAAQ,IAAA,EAAM,EAAE,CAAA,CAAE,MAAA;AACzC;AAGO,SAAS,kBAAkB,KAAA,EAaL;AAC3B,EAAA,MAAM,EAAE,KAAA,EAAO,MAAA,EAAO,GAAI,KAAA;AAC1B,EAAA,IAAI,CAAC,OAAO,OAAO,IAAA;AACnB,EAAA,IAAA,CAAK,KAAA,CAAM,MAAA,IAAU,MAAA,MAAY,MAAA,EAAQ,OAAO,IAAA;AAChD,EAAA,IAAI,KAAA,CAAM,QAAQ,OAAO,IAAA;AACzB,EAAA,IAAI,KAAA,CAAM,KAAA,KAAU,QAAA,EAAU,OAAO,IAAA;AACrC,EAAA,MAAM,QAAA,GAAW,MAAM,YAAA,EAAc,QAAA;AACrC,EAAA,IAAI,YAAY,EAAE,KAAA,CAAM,UAAU,eAAA,CAAgB,QAAQ,IAAI,OAAO,IAAA;AACrE,EAAA,MAAM,KAAA,GAAA,CAAS,KAAA,CAAM,KAAA,IAAS,IAAI,CAAC,CAAA;AACnC,EAAA,MAAM,WAAW,KAAA,IAAS,MAAA,CAAO,KAAK,CAAC,CAAA,KAAM,EAAE,GAAA,KAAQ,KAAA,CAAM,KAAK,CAAA,GAAI,EAAE,EAAA,EAAI,KAAA,CAAM,OAAO,SAAA,EAAW,KAAA,CAAM,WAAU,GAAI,IAAA;AACxH,EAAA,MAAM,OAAA,GAAU,QAAA,IAAY,aAAA,CAAc,KAAA,CAAM,OAAO,MAAM,CAAA;AAC7D,EAAA,OAAO;AAAA,IACL,YAAY,KAAA,CAAM,OAAA;AAAA,IAClB,QAAA,EAAU,YAAA,CAAa,KAAA,EAAO,MAAM,KAAK,EAAC;AAAA,IAC1C,QAAA,EAAU,IAAA;AAAA,IACV,MAAA,EAAQ,gBAAgB,MAAA,EAAQ,QAAA,EAAU,SAAS,IAAA,EAAM,WAAA,CAAY,MAAA,EAAQ,OAAO,CAAC,CAAA;AAAA,IACrF,SAAA,EAAW,IAAA;AAAA,IACX,OAAA,EAAS,MAAM,QAAA,IAAY,eAAA;AAAA,IAC3B,QAAA,EAAU;AAAA,GACZ;AACF;AAMA,eAAsB,oBAAA,CACpB,MAAA,EACA,OAAA,EACA,OAAA,GAAmD,EAAC,EACjB;AACnC,EAAA,OAAA,CAAQ,MAAM,gBAAA,CAAiB,MAAA,EAAQ,OAAA,EAAS,OAAO,IAAI,IAAA,IAAQ,IAAA;AACrE;AAGA,SAAS,YAAY,OAAA,EAA8B;AACjD,EAAA,OAAO,EAAE,MAAM,KAAA,EAAO,OAAA,EAAS,SAAS,MAAA,EAAQ,MAAA,EAAQ,WAAW,IAAA,EAAK;AAC1E;AAGA,SAAS,aAAA,CAAc,MAAqB,OAAA,EAA0B;AACpE,EAAA,OAAO,EAAE,GAAG,IAAA,EAAM,OAAA,EAAS,IAAA,CAAK,WAAW,OAAA,EAAS,EAAA,EAAI,EAAA,EAAY,SAAA,EAAW,IAAA,EAAK;AACtF;AAGA,eAAe,gBAAA,CACb,MAAA,EACA,OAAA,EACA,OAAA,EACgG;AAChG,EAAA,MAAM,WAAW,OAAA,CAAQ,IAAA;AACzB,EAAA,MAAM,CAAC,OAAO,MAAA,EAAQ,KAAA,EAAO,KAAK,CAAA,GAAI,MAAM,QAAQ,GAAA,CAAI;AAAA,IACtD,MAAA,CAAO,SAAA,CAAU,EAAE,QAAA,EAAU,SAAS,CAAA;AAAA,IACtC,MAAA,CAAO,MAAA,CAAO,EAAE,QAAA,EAAU,SAAS,CAAA;AAAA,IACnC,QAAA,GAAW,OAAA,CAAQ,OAAA,CAAQ,IAAI,CAAA,GAAI,OAAO,KAAA,CAAM,EAAE,QAAA,EAAU,OAAA,EAAS,CAAA;AAAA,IACrE,WAAW,OAAA,CAAQ,OAAA,CAAQ,IAAI,CAAA,GAAI,SAAA,CAAU,QAAQ,OAAO;AAAA,GAC7D,CAAA;AACD,EAAA,IAAI,CAAC,KAAA,CAAM,EAAA,IAAM,CAAC,MAAA,CAAO,IAAI,OAAO,IAAA;AACpC,EAAA,IAAI,KAAA;AACJ,EAAA,IAAI,QAAA,EAAU;AAEZ,IAAA,KAAA,GAAQ,aAAA,CAAc,UAAU,OAAO,CAAA;AAAA,EACzC,CAAA,MAAO;AACL,IAAA,IAAI,CAAC,KAAA,EAAO,EAAA,IAAM,CAAC,KAAA,EAAO,IAAI,OAAO,IAAA;AACrC,IAAA,MAAM,IAAA,GAAO,KAAA,CAAM,IAAA,CAAK,GAAA,CAAI,WAAW,CAAA,CAAE,MAAA,CAAO,CAAC,CAAA,KAAM,CAAA,CAAE,OAAA,KAAY,OAAO,CAAA;AAC5E,IAAA,MAAM,OAAO,aAAA,CAAc,IAAI,CAAA,IAAK,IAAA,CAAK,CAAC,CAAA,IAAK,IAAA;AAE/C,IAAA,KAAA,GAAQ,IAAA,GAAO,SAAA,CAAU,IAAA,EAAM,KAAA,CAAM,IAAA,CAAK,IAAA,CAAK,EAAE,CAAC,CAAA,GAAI,aAAA,CAAc,WAAA,CAAY,OAAO,GAAG,OAAO,CAAA;AAAA,EACnG;AACA,EAAA,MAAM,OAAO,iBAAA,CAAkB;AAAA,IAC7B,OAAA;AAAA,IACA,OAAO,KAAA,CAAM,IAAA;AAAA,IACb,QAAQ,MAAA,CAAO,IAAA;AAAA,IACf,KAAA;AAAA,IACA,GAAI,QAAQ,QAAA,GAAW,EAAE,UAAU,OAAA,CAAQ,QAAA,KAAa,EAAC;AAAA,IACzD,GAAI,OAAA,CAAQ,MAAA,GAAS,EAAE,MAAA,EAAQ,IAAA,KAAS;AAAC,GAC1C,CAAA;AACD,EAAA,OAAO,OAAO,EAAE,IAAA,EAAM,OAAO,MAAA,EAAQ,MAAA,CAAO,MAAK,GAAI,IAAA;AACvD;AAuBO,SAAS,aAAa,IAAA,EAAoF;AAC/G,EAAA,MAAM,KAAA,GAAQ,MAAM,OAAA,CAAQ,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,SAAS,aAAa,CAAA;AAChE,EAAA,MAAM,OAAO,KAAA,EAAO,IAAA;AACpB,EAAA,IAAI,CAAC,IAAA,IAAQ,OAAO,IAAA,KAAS,UAAU,OAAO,IAAA;AAC9C,EAAA,OAAO,EAAE,EAAA,EAAI,IAAA,CAAK,EAAA,KAAO,MAAM,KAAA,EAAO,OAAO,IAAA,CAAK,MAAA,KAAW,YAAY,IAAA,CAAK,MAAA,GAAS,CAAA,GAAI,IAAA,CAAK,SAAS,IAAA,EAAK;AAChH;AAEA,eAAsB,iBAAiB,KAAA,EA6Bd;AACvB,EAAA,MAAM,SAAA,GAAYI,mBAAAA,CAAoBC,mBAAAA,CAAoB,KAAA,CAAM,UAAU,CAAC,CAAA;AAC3E,EAAA,MAAM,SAASC,mBAAAA,CAAoB;AAAA,IACjC,YAAY,SAAA,CAAU,MAAA;AAAA,IACtB,gBAAgB,KAAA,CAAM,cAAA;AAAA,IACtB,OAAO,KAAA,CAAM,KAAA;AAAA,IACb,SAAS,MAAM;AAAA,IAAC;AAAA,GACjB,CAAA;AACD,EAAA,MAAM,MAAA,GAAS,KAAA,CAAM,QAAA,GACjB,OAAA,CAAQ,OAAA;AAAA,IACN,SAAA,CAAU,MAAA,CAAO,GAAA,CAAI,oBAAA,EAAsB,EAAE,iBAAA,EAAmB,KAAA,CAAM,cAAA,EAAgB,WAAA,EAAa,MAAM,QAAA,EAAS,EAAG,EAAE,MAAA,EAAQ,UAAU;AAAA,GAC3I,CAAE,KAAA,CAAM,MAAM,IAAI,CAAA,GAClB,IAAA;AACJ,EAAA,IAAI;AACF,IAAA,MAAM,QAAA,GAAW,KAAA,CAAM,QAAA,KAAa,KAAA,CAAM,WAAW,kBAAA,GAAqB,eAAA,CAAA;AAC1E,IAAA,MAAM,OAAA,GAAU,MAAM,gBAAA,CAAiB,MAAA,EAAQ,KAAA,CAAM,OAAA,EAAS,EAAE,QAAA,EAAU,GAAI,KAAA,CAAM,MAAA,GAAS,EAAE,MAAA,EAAQ,IAAA,EAAK,GAAI,EAAC,EAAI,GAAI,KAAA,CAAM,IAAA,GAAO,EAAE,IAAA,EAAM,KAAA,CAAM,IAAA,EAAK,GAAI,EAAC,EAAI,CAAA;AAClK,IAAA,MAAM,QAAA,GAAW,OAAO,KAAA,CAAM,IAAA,KAAS,UAAA,GAAa,KAAA,CAAM,IAAA,CAAK,SAAA,CAAU,IAAA,EAAM,CAAA,GAAI,KAAA,CAAM,IAAA,KAAS,KAAA;AAClG,IAAA,IAAI,WAAW,QAAA,EAAU;AACvB,MAAA,MAAM,EAAE,IAAA,EAAM,KAAA,EAAO,MAAA,EAAO,GAAI,OAAA;AAChC,MAAA,MAAM,IAAA,GAAO,MAAM,MAAA,CAAO,QAAA,CAAS;AAAA,QACjC,UAAU,IAAA,CAAK,UAAA;AAAA,QACf,QAAQ,IAAA,CAAK,QAAA;AAAA,QACb,QAAQ,IAAA,CAAK,QAAA;AAAA,QACb,MAAM,IAAA,CAAK,MAAA;AAAA,QACX,SAAS,IAAA,CAAK,SAAA;AAAA,QACd,OAAO,IAAA,CAAK,OAAA;AAAA,QACZ,QAAQ,IAAA,CAAK;AAAA,OACd,CAAA;AACD,MAAA,IAAI,KAAK,EAAA,EAAI;AACX,QAAA,MAAM,MAAA,GAAS,KAAA,EAAO,YAAA,EAAc,SAAA,IAAa,EAAC;AAClD,QAAA,MAAM,QAAA,GAAW,KAAA,EAAO,YAAA,EAAc,QAAA,EAAU,KAAA,IAAS,IAAA;AACzD,QAAA,MAAM,QAAiC,EAAC;AAExC,QAAA,MAAM,MAAA,GAAS,IAAA,CAAK,QAAA,IAAY,MAAA,CAAO,IAAA,CAAK,IAAA,CAAK,QAAQ,CAAA,CAAE,MAAA,GAAS,CAAA,GAAK,IAAA,CAAK,QAAA,GAA2B,KAAA,CAAA;AAEzG,QAAA,IAAI,QAAA,EAAU;AACZ,UAAA,KAAA,CAAM,IAAA,CAAK,MAAA,CAAO,eAAA,CAAgB,EAAE,QAAA,EAAU,MAAM,OAAA,EAAS,OAAA,EAAS,CAAC,QAAQ,CAAA,EAAG,QAAA,EAAU,kBAAkB,MAAM,CAAA,EAAG,KAAA,EAAO,GAAA,EAAM,GAAI,MAAA,GAAS,EAAE,MAAA,EAAO,GAAI,EAAC,EAAI,CAAC,CAAA;AAAA,QACtK;AACA,QAAA,MAAM,GAAA,GAAM,mBAAmB,MAAM,CAAA;AACrC,QAAA,IAAI,GAAA,CAAI,SAAS,CAAA,EAAG,KAAA,CAAM,KAAK,MAAA,CAAO,eAAA,CAAgB,EAAE,QAAA,EAAU,KAAA,CAAM,SAAS,QAAA,EAAU,GAAA,EAAK,GAAI,MAAA,GAAS,EAAE,QAAO,GAAI,EAAC,EAAI,CAAC,CAAA;AAEhI,QAAA,KAAA,MAAWN,UAAS,MAAA,EAAQ;AAC1B,UAAA,IAAIA,MAAAA,CAAM,IAAA,KAAS,MAAA,IAAUA,MAAAA,CAAM,SAAS,UAAA,EAAY;AACxD,UAAA,MAAM,GAAA,uBAAU,GAAA,EAAY;AAC5B,UAAA,KAAA,MAAW,GAAA,IAAO,IAAA,CAAK,IAAA,CAAK,IAAA,EAAM;AAChC,YAAA,MAAM,KAAA,GAAQ,GAAA,CAAI,QAAA,GAAWA,MAAAA,CAAM,GAAG,CAAA;AACtC,YAAA,KAAA,MAAW,EAAA,IAAM,MAAM,OAAA,CAAQ,KAAK,IAAI,KAAA,CAAM,GAAA,CAAI,MAAM,CAAA,GAAI,OAAO,KAAA,KAAU,WAAW,CAAC,KAAK,CAAA,GAAI,EAAC,EAAG,IAAIO,YAAY,EAAE,CAAA,EAAG,GAAA,CAAI,GAAA,CAAI,EAAE,CAAA;AAAA,UACvI;AACA,UAAA,IAAI,IAAI,IAAA,GAAO,CAAA,EAAG,KAAA,CAAM,IAAA,CAAK,OAAO,iBAAA,CAAkB,EAAE,QAAA,EAAUP,MAAAA,CAAM,IAAY,UAAA,EAAY,CAAC,GAAG,GAAG,CAAA,EAAa,CAAC,CAAA;AAAA,QACvH;AACA,QAAA,IAAI,MAAM,MAAA,EAAQ;AAEhB,UAAA,KAAA,MAAW,GAAA,IAAO,eAAA,CAAgB,MAAM,CAAA,EAAG;AACzC,YAAA,KAAA,CAAM,IAAA,CAAK,MAAA,CAAO,eAAA,CAAgB,EAAE,QAAA,EAAU,KAAA,CAAM,OAAA,EAAS,OAAA,EAAS,CAAC,GAAG,CAAA,EAAG,QAAA,EAAU,CAAC,EAAE,EAAA,EAAI,OAAA,EAAS,CAAA,EAAG,KAAA,EAAO,GAAA,EAAK,GAAI,MAAA,GAAS,EAAE,MAAA,EAAO,GAAI,EAAC,EAAI,CAAC,CAAA;AAAA,UACxJ;AAAA,QACF;AAEA,QAAA,MAAM,cAAc,IAAA,CAAK,IAAA,CAAK,IAAA,CAAK,MAAA,CAAO,CAAC,GAAA,KAAQ,UAAA,CAAW,GAAG,CAAA,KAAM,IAAI,CAAA,CAAE,GAAA,CAAI,CAAC,GAAA,KAAQ,IAAI,EAAE,CAAA;AAChG,QAAA,IAAI,WAAA,CAAY,MAAA,GAAS,CAAA,EAAG,KAAA,CAAM,IAAA,CAAK,MAAA,CAAO,aAAA,CAAc,EAAE,GAAA,EAAK,WAAA,EAAa,CAAC,CAAA;AAGjF,QAAA,KAAA,CAAM,IAAA;AAAA,UACJ,MAAA,CAAO,cAAA,CAAe,EAAE,QAAA,EAAU,KAAA,CAAM,SAAS,CAAA,CAAE,IAAA,CAAK,OAAO,OAAA,KAAY;AACzE,YAAA,IAAI,CAAC,OAAA,CAAQ,EAAA,IAAM,QAAQ,IAAA,CAAK,MAAA,KAAW,GAAG,OAAO,IAAA;AACrD,YAAA,MAAM,UAAA,GAAa,KAAK,IAAA,CAAK,IAAA,CAAK,IAAI,CAAC,GAAA,KAAQ,IAAI,EAAE,CAAA;AAErD,YAAA,MAAM,GAAA,GAAM,MAAM,MAAA,CAAO,kBAAA,CAAmB,EAAE,QAAA,EAAU,KAAA,CAAM,SAAS,gBAAA,EAAkB,OAAA,CAAQ,KAAK,GAAA,CAAI,CAAC,MAAM,CAAA,CAAE,eAAe,GAAG,UAAA,EAAY,KAAA,EAAO,oBAAoB,CAAA;AAC5K,YAAA,IAAI,IAAI,EAAA,IAAM,GAAA,CAAI,KAAA,CAAM,IAAA,KAAS,eAAe,OAAO,IAAA;AACvD,YAAA,OAAO,OAAA,CAAQ,IAAI,OAAA,CAAQ,IAAA,CAAK,IAAI,CAAC,CAAA,KAAM,OAAO,gBAAA,CAAiB,EAAE,UAAU,KAAA,CAAM,OAAA,EAAS,iBAAiB,CAAA,CAAE,eAAA,EAAiB,YAAY,KAAA,EAAO,kBAAA,EAAoB,CAAC,CAAC,CAAA;AAAA,UAC7K,CAAC;AAAA,SACH;AACA,QAAA,MAAM,OAAA,CAAQ,GAAA,CAAI,KAAA,CAAM,GAAA,CAAI,CAAC,IAAA,KAAS,IAAA,CAAK,KAAA,CAAM,MAAM,IAAI,CAAC,CAAC,CAAA;AAAA,MAC/D;AAAA,IACF;AAAA,EACF,CAAA,CAAA,MAAQ;AAAA,EAER;AACA,EAAA,IAAI,QAAQ,MAAM,MAAA;AAClB,EAAA,OAAO,UAAU,IAAA,EAAK;AACxB","file":"first-page.js","sourcesContent":["// src/names.ts โ€” @ai-matrx/records-ui\n//\n// WHAT A PERSON READS, NEVER WHAT THE MACHINE STORES.\n//\n// Measured on the real screens (independent verdict, 19 September): a table row\n// read `Saved views ยท 9 fields ยท records_ui_view`, a relation cell read a bare\n// `8f2cโ€ฆ` identifier, a header read `stage` because the Field's label was\n// blank, and the type word on a column tooltip read `long_text`. Airtable,\n// Notion and Linear never show a person an internal name; the verdict called\n// this one of the three things a first-time user feels within a minute.\n//\n// So every place this package prints something a person reads goes through one\n// of these three functions. They are deliberately dumb: they choose the best\n// human string that IS AVAILABLE and, when none is, say what the thing is\n// rather than printing the token. No screen in this package formats a name of\n// its own any more.\n\nimport type { Field, ReadRow, RecordDocument, Table } from \"@ai-matrx/records\";\nimport { isUuidShape } from \"@ai-matrx/kit/uuid\";\nimport { humanizeIdentifier } from \"@ai-matrx/kit/text-case\";\n\n/** The name of one Field, as a header, a form label or a peek's term. */\nexport function fieldName(field: Field): string {\n const label = (field.label ?? \"\").trim();\n // A LABEL THAT IS ONLY A MACHINE KEY is not a name (merged-grid review 2026-09-26): a table made\n // by an import or an agent kept `label: \"job_no\"`, and the header read `job_no`. A key joined by\n // underscores is written the way a person writes it (\"Job no\"). A single lowercase word the\n // owner typed (\"mobile\", \"title\") is their own words and is shown as they wrote it.\n if (label !== \"\" && label === field.key && /^[a-z0-9]+(?:[_.-][a-z0-9]+)+$/.test(label)) return humanizeIdentifier(label);\n if (label !== \"\") return label;\n const name = (field.name ?? \"\").trim();\n if (name !== \"\") return name;\n return humanizeIdentifier(field.key);\n}\n\n/** The name of one Table, as a card, a heading or a breadcrumb. */\nexport function tableName(table: Table): string {\n const name = (table.name ?? \"\").trim();\n if (name !== \"\") return name;\n const singular = (table.label_singular ?? \"\").trim();\n if (singular !== \"\") return singular;\n return humanizeIdentifier(table.slug ?? \"\");\n}\n\n/**\n * The name of one RECORD, for a chip, a card, a relation cell or a peek title.\n *\n * `titleKey` is the Table's own `title_field` when the caller knows the Table โ€”\n * that is the store's answer to \"what makes this record a chip\" (REC-2) โ€” and\n * the conventional keys are tried after it. A record with nothing readable is\n * NOT shown as a truncated uuid: it says it has no name yet, because \"8f2c1b0a\"\n * tells a person nothing and looks like a bug.\n */\nexport function recordName(\n document: RecordDocument | undefined,\n titleKey?: string | null,\n fallback = \"Untitled\",\n): string {\n const data = (document ?? {}) as Record<string, unknown>;\n const keys = [titleKey, \"title\", \"name\", \"label\", \"full_name\", \"subject\", \"summary\"].filter(\n (key): key is string => typeof key === \"string\" && key !== \"\",\n );\n for (const key of keys) {\n const value = data[key];\n // AN ID IS NEVER A NAME, AND THIS FUNCTION USED TO RETURN ONE (TAILS-2, 2026-09-21).\n // A Table's title column may be a RELATION, and a relation's stored value IS the other\n // record's id (FLD-6 / REL-1) โ€” so `recordName` handed every caller a bare uuid for\n // every Table titled that way, which is how the board's unplaced list came to read\n // \"11 records are not in any of these columns โ€” ca5f1ac7-โ€ฆ\". A caller that can resolve\n // the id resolves it (`scalarText` with the label lookup); a caller that cannot is\n // better off reading the next column, and then the fallback, than a hex number.\n if (typeof value === \"string\" && value.trim() !== \"\" && !looksLikeId(value.trim())) {\n return value.trim();\n }\n if (typeof value === \"number\") return String(value);\n }\n return fallback;\n}\n\n\n/**\n * Is this string the store's id for something, rather than words a person wrote?\n *\n * The same question `custom._card_words` asks on the database, with the same regex, so the\n * screen and the store agree about what is a name (TAILS-2, 2026-09-21).\n */\nexport function looksLikeId(value: unknown): boolean {\n return typeof value === \"string\" && isUuidShape(value.trim());\n}\n\n/** The same question for a row the read door returned. */\nexport function rowName(row: ReadRow | undefined, titleKey?: string | null, fallback = \"Untitled\"): string {\n return recordName(row?.document, titleKey, fallback);\n}\n\n// โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•\n// NAMING A RECORD WHOSE TABLE YOU ALREADY HOLD โ€” ONE CALL, NOT A COPY PER SCREEN.\n// โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•\n//\n// ๐Ÿšจ WHY THESE TWO EXIST (lane RELATION-DISPLAY-2, 2026-09-21).\n//\n// `check:one-reference-builder` shipped with a baseline of SIX, and its own header said the\n// number was not zero because \"moving six surfaces onto the store door is real work\". Three\n// of the six were not asking the store's question at all. `Grid.tsx` names the row a person\n// is looking at for the Talk action; `Peek.tsx` names the record being peeked at;\n// `PortalsPanel.tsx` names the rows of the portal's own client Table; and this repo's\n// `TryEverythingScreen.tsx` does the same in two places. NONE of them is resolving a\n// RELATION โ€” they hold the Table and they hold the document, and there is nothing to ask a\n// door about. What they were each doing by hand was the two-line join between them:\n//\n// recordName(row.document, table.title_field)\n//\n// Written out five times in four files, which is how a Table whose title column is `cname`\n// came to read \"Untitled\" on one screen and correctly on the next. It is the same question\n// as the store's, one rung down the ladder, and the same one answer should serve it โ€” so it\n// lives HERE, in the file the guard exempts BY CONSTRUCTION as the primitive itself, and\n// every screen calls it instead of re-deriving it.\n//\n// WHAT THESE ARE NOT. They are not a second resolver and must never become one. They take a\n// Table a caller ALREADY HAS and a document it ALREADY HAS. The moment a caller is holding\n// an ID and wants words, that is the store's question and the answer is\n// `client.relationWords` / `client.relationWordsMany` โ€” which ask the read-access ladder\n// BEFORE they read a name, apply that column's own display spec, and return the withheld\n// sentence for a record the reader may not open. Nothing in here can do any of that, and\n// nothing in here should try.\n\n/** A Table as far as naming is concerned โ€” its own declared title column (REC-2). */\ntype TitledTable = Pick<Table, \"title_field\"> | null | undefined;\n\n/**\n * THE NAME OF A RECORD WHOSE TABLE YOU ALREADY HOLD.\n *\n * `table` is the Table the record belongs to; its `title_field` is the store's answer to\n * \"what makes this record a chip\" (REC-2) and is tried first, then the conventional keys,\n * then the fallback. A Table that has not loaded yet is `null` and the conventional keys\n * still answer โ€” a name arriving a tick late is better than a screen that shows nothing.\n */\nexport function recordNameIn(\n table: TitledTable,\n document: RecordDocument | undefined,\n fallback = \"Untitled\",\n): string {\n return recordName(document, table?.title_field ?? null, fallback);\n}\n\n/**\n * THE NAME A CHANGE NOTICE USES โ€” the record as it is AFTER the change (merged-grid tails,\n * 2026-09-26). Typing a first name into a row's label column read \"Changed Title on Untitled\":\n * the notice named the row from the document held before the write. The row is called what it is\n * called now; only when the change emptied its name does the notice use the name it had.\n */\nexport function changedRecordName(\n table: TitledTable,\n document: RecordDocument | undefined,\n key: string,\n after: unknown,\n): string {\n const now = recordName({ ...((document ?? {}) as Record<string, unknown>), [key]: after } as RecordDocument, table?.title_field ?? null, \"\");\n return now !== \"\" ? now : recordNameIn(table, document);\n}\n\n/** The same, for a row the read door returned. */\nexport function rowNameIn(table: TitledTable, row: ReadRow | undefined, fallback = \"Untitled\"): string {\n return recordName(row?.document, table?.title_field ?? null, fallback);\n}\n\n/**\n * WHAT A RELATION CHIP SAYS WHEN THE RECORD IT POINTS AT IS NOT YOURS.\n *\n * A relation joins two records in two tables, and holding one end says nothing\n * about the other: a Job shared with you may point at a Client nobody shared.\n * The chip then has three possible answers and only one of them is honest โ€”\n * the target's TITLE is a leak, a bare id is a bug with a hex number in it, and\n * a permanent \"Loadingโ€ฆ\" is a lie that never resolves.\n *\n * This is the SAME sentence `platform.relation_withheld_label()` answers on the\n * database, word for word, so a person reads one wording whether the screen or\n * the store worked it out. Changing it changes both (RELATION-DECLARE,\n * 2026-09-20).\n */\nexport const WITHHELD_RECORD_LABEL = \"A record you have not been given access to\";\n/**\n * WHEN THE STORE COULD NOT BE ASKED (a 503, a dropped connection) the name is UNREAD, not\n * withheld: saying \"not given access\" for a failed read lies about the data (W1.1 verifier,\n * 2026-10-02). This is what a reference reads until a later read answers.\n */\nexport const UNREAD_RECORD_LABEL = \"Couldn't load this name\";\n\n/**\n * THE PLURAL A PERSON READS, NEVER A MACHINE'S \"โ€ฆss\" (VERIFIER-16 M3 / VERIFIER-17 M3).\n *\n * A table the store or the mover never gave a real plural carries `name + \"s\"` โ€” so a table\n * already named in the plural reads \"Customerss\", \"Status optionss\". That word was made by a\n * machine appending one letter, and it says nothing the name does not; it is never shown.\n * Returns the table's own plural when a person (or the example library) wrote one, and `null`\n * when the only plural on record is that appended letter.\n */\nexport function realPlural(table: { name?: string | null; label_singular?: string | null; label_plural?: string | null }): string | null {\n const plural = (table.label_plural ?? \"\").trim();\n if (plural === \"\") return null;\n for (const base of [table.name, table.label_singular]) {\n const b = (base ?? \"\").trim();\n if (b !== \"\" && plural === `${b}s`) return null;\n }\n return plural;\n}\n\n/**\n * ONE RECORD'S NOUN, IN A SENTENCE (\"Book a 30-minute visit\") โ€” lane HANDOVER, 2026-09-28.\n *\n * Cedar Ridge Physical Therapy's booking page was titled \"Book a 30-minute Visits appointment\":\n * the table's NAME (a plural, a heading) was put where one record's noun goes. The noun is the\n * table's own singular (\"Visit\"), else its name, else the fallback; a Title-case word is written\n * the way a sentence writes it (\"visit\"), an acronym is kept (\"MRI scan\").\n */\nexport function singularNoun(\n table: { name?: string | null; label_singular?: string | null } | null | undefined,\n fallback = \"record\",\n): string {\n const word = (table?.label_singular ?? \"\").trim() || (table?.name ?? \"\").trim() || fallback;\n return word\n .split(/\\s+/)\n .map((w) => (/^[A-Z][a-z'โ€™-]*$/.test(w) ? w.toLowerCase() : w))\n .join(\" \");\n}\n\n/**\n * A plural for a sentence (\"Check in ran on 2 appointments\"): the table's real plural, else the\n * singular with an \"s\" only when that does not double a final \"s\".\n */\nexport function pluralNoun(table: { name?: string | null; label_singular?: string | null; label_plural?: string | null } | null | undefined, fallback = \"record\"): string {\n const real = table ? realPlural(table) : null;\n if (real) return real;\n const singular = (table?.label_singular ?? \"\").trim() || fallback;\n return /s$/i.test(singular) ? singular : `${singular}s`;\n}\n\n\n// โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•\n// WHAT A CARD IS TITLED BY (lane UI-FIX-18, VERIFIER-18 H5).\n// โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•\n//\n// MEASURED 2026-09-24 on production: *ZZZ UI-QUICK-1 Rincon-style Jobs* (title column `title`)\n// grouped by Stage titled every kanban card \"false\" or \"true\". The board took the FIRST Field\n// the read door answered that was not the group column โ€” here the *Urgent* checkbox โ€” as the\n// title. A card is named by what names its record: the Table's own `title_field` first (REC-2),\n// then the conventional name keys, then the first words column in declared order. A yes/no\n// column, the column the board is grouped by and the store's own stamps never title a card.\n\nconst CONVENTIONAL_TITLE_KEYS = [\"title\", \"name\", \"label\", \"full_name\", \"subject\", \"summary\"];\n\nfunction canTitle(field: Field, excluded: ReadonlySet<string>): boolean {\n if (excluded.has(field.key)) return false;\n if (field.type === \"boolean\") return false;\n if (field.type === \"formula\" && (field.config ?? {})[\"system\"]) return false;\n return true;\n}\n\n/**\n * THE FIELD A CARD IS TITLED BY โ€” kanban, calendar, gallery and pipeline alike.\n *\n * Order: the Table's `title_field` โ†’ a conventional name key (`title`, `name`, โ€ฆ) โ†’ the first\n * text column in declared `sort` order โ†’ the first other column that can title (never a\n * checkbox, never the group column, never a system stamp). `undefined` when nothing can, and\n * the card falls back to `rowName`, which says \"Untitled\" rather than \"false\".\n */\nexport function cardTitleField(\n fields: readonly Field[],\n titleKey: string | null | undefined,\n exclude: readonly (string | null | undefined)[] = [],\n): Field | undefined {\n const excluded = new Set(exclude.filter((k): k is string => typeof k === \"string\" && k !== \"\"));\n const ordered = [...fields].sort((a, b) => (a.sort ?? 100) - (b.sort ?? 100) || a.key.localeCompare(b.key));\n const byKey = (key: string) => ordered.find((f) => f.key === key && canTitle(f, excluded));\n if (titleKey) {\n const declared = byKey(titleKey);\n if (declared) return declared;\n }\n for (const key of CONVENTIONAL_TITLE_KEYS) {\n const found = byKey(key);\n if (found) return found;\n }\n return (\n ordered.find((f) => f.type === \"text\" && canTitle(f, excluded)) ?? ordered.find((f) => canTitle(f, excluded))\n );\n}\n","// src/parity.ts โ€” @ai-matrx/records-ui\n//\n// FLD-11, the parity floor. A Field stores a BEHAVIOUR (one of five) plus\n// modifiers, unit and format; a person picked one of THIRTEEN parity types in\n// the UI. This resolves the second from the first, using the same facts the\n// store's own `custom.parity_field_types()` describes โ€” which is generated into\n// `@ai-matrx/records` and imported here, never retyped.\n//\n// Nothing in this file decides policy. It decides which editor to draw.\n\nimport { displayFormatOf } from \"./choiceLooks\";\nimport {\n KERNEL_TABLES,\n PARITY_FIELD_TYPES,\n fieldKindFor,\n newerKindOf,\n type Field,\n type FieldKind,\n type KernelTargets,\n type ParityFieldType,\n} from \"@ai-matrx/records\";\nimport { humanizeIdentifier } from \"@ai-matrx/kit/text-case\";\nimport { isSignatureField } from \"./docs\";\nimport { fieldTypeChoice, isSystemFieldKind, type PickableFieldType } from \"./fieldTypes\";\n\n/** The kernel ids come from the store's own kernel rows, never from a literal here. */\nconst kernelId = (name: string): string =>\n KERNEL_TABLES.find((t) => t.name === name)?.id ?? \"\";\nconst KERNEL_FILE_TABLE = kernelId(\"File\");\nconst KERNEL_PERSON_TABLE = kernelId(\"Person\");\n\nexport const PARITY_LABEL: Record<ParityFieldType, string> = {\n attachment: \"Attachment\",\n checkbox: \"Yes / no\",\n currency: \"Currency\",\n datetime: \"Date\",\n email: \"Email\",\n formula: \"Formula\",\n lookup: \"Lookup\",\n member: \"Person\",\n multi_select: \"Multi-select\",\n percent: \"Percent\",\n phone: \"Phone\",\n rollup: \"Rollup\",\n select: \"Select\",\n url: \"Link\",\n};\n\n/** The \"made of\" sentence the store itself publishes, for the field editor's help text. */\nexport const PARITY_MADE_OF: Record<string, string> = Object.fromEntries(\n PARITY_FIELD_TYPES.map((p) => [p.parity_type, p.made_of]),\n);\n\n/**\n * Plain text and plain number are behaviours with no parity type of their own.\n *\n * ๐Ÿšจ THIS IS NOW AN ALIAS, AND THAT IS THE POINT (TAILS-3, 2026-09-21). The\n * classifier moved into `@ai-matrx/records` as `FieldKind` so that the editor a\n * person types into and the COERCION applied to what they typed are decided by\n * one body. They were decided by two, and the public booking page paid for it:\n * \"Vehicle Year takes a number, and it was given a string\" on every table with\n * a number field.\n */\nexport type EditorKind = FieldKind;\n\n/** The kernel ids come from the store's own kernel rows, never from a literal here. */\nconst KERNEL: KernelTargets = { file: KERNEL_FILE_TABLE, person: KERNEL_PERSON_TABLE };\n\n/**\n * Which editor one Field wants. Reads the Field's own declaration only โ€” the\n * behaviour, the modifiers, the format, the relation target and the config โ€”\n * because those are what the store stores, and a screen that guessed from a\n * value would draw a different editor for an empty record.\n *\n * It is `fieldKindFor` with this package's kernel ids bound. Nothing here\n * decides anything the core does not.\n */\nexport function editorKindFor(field: Field): EditorKind {\n return fieldKindFor(field, KERNEL);\n}\n\n/**\n * WHICH KIND A PERSON PICKED, read back off the Field the store keeps โ€” the ONE reader every\n * \"what does this column hold\" answer goes through (the Settings column list, the column panel).\n *\n * `editorKindFor` answers which EDITOR to draw, and for two families that is not the kind a\n * person picked: a Signature is a text Field whose format is `signature` (it draws a line of\n * text), and the store's stamps โ€” Record number, Created time, Last changed time โ€” are formula\n * Fields whose `config.system` names the stamp. Read through the editor classifier alone, the\n * Settings list called a Signature \"Text\" and a Record number \"Worked out from other columns\"\n * (lane 10 W1-A, guard `every-pickable-kind-reads-back-as-itself.test.tsx`).\n *\n * `undefined` when the Field is none of the kinds the menu offers.\n */\nexport function pickableTypeOf(field: Field): PickableFieldType | undefined {\n if (isSignatureField(field)) return \"signature\";\n // LANE 10 P4: the nine newer kinds, each a base type wearing a format (`newerKindOf`).\n const newer = newerKindOf(field);\n if (newer) return newer;\n // A RATING OR A TIME made as a plain column wearing its look (`LOOK_KINDS`): named by that kind.\n const look = displayFormatOf(field)?.id;\n if (look === \"rating\" && field.type === \"range\") return \"rating\";\n if (look === \"time\" && field.type === \"text\") return \"time\";\n const system = (field.config as Record<string, unknown> | undefined)?.[\"system\"];\n if (field.type === \"formula\" && typeof system === \"string\" && isSystemFieldKind(system)) return system;\n const kind = editorKindFor(field);\n return fieldTypeChoice(kind) ? (kind as PickableFieldType) : undefined;\n}\n\n/**\n * The type word a person sees in a header or a settings row โ€” and it is the\n * SAME word the field panel offers, read from `FIELD_TYPE_CHOICES`, so a column\n * added as \"Money\" is never described anywhere else as \"Currency\".\n */\nexport function fieldTypeLabel(field: Field): string {\n const kind = pickableTypeOf(field) ?? editorKindFor(field);\n const known = fieldTypeChoice(kind)?.label;\n if (known) return field.multi && kind !== \"multi_select\" ? `${known} (many)` : known;\n const plain: Record<string, string> = {\n // `boolean` is the BEHAVIOUR word; `checkbox` is the type a person picks. Both are\n // written the way a person writes them, never as a raw token.\n boolean: \"Yes / no\",\n date: \"Date\",\n json: \"Structured data\",\n relation: \"Link to a record\",\n };\n // Never a raw token on a screen: an editor kind this table has not named is\n // still written the way a person writes it.\n return plain[kind] ?? humanizeIdentifier(kind);\n}\n","// src/gridLook.ts โ€” @ai-matrx/records-ui ยท lane DEFAULT-GRID-PARITY\n//\n// WHAT THE STORE ALREADY SAYS ABOUT HOW A TABLE OPENS, READ IN ONE PLACE.\n//\n// The owner, on his real table's new page (2026-09-23): \"none of my advanced features are\n// copied over.\" Each of the things below was IN THE STORE and the default grid never read\n// it (`common-docs/systems/data/custom-data/HANDOFF.md`, rows D1 D3 D4 D5 D6\n// S2 F1 R3). They are pure functions on purpose โ€” the grid is a large component and every\n// one of these answers is a question a test must be able to ask without mounting it:\n//\n// ยท the table's DEFAULT SORT (`data.default_sort`, `[{field, direction}]`), which the store\n// keeps and nothing read;\n// ยท the LAYOUT the grid opens with: the platform, then the organization's knob\n// (`custom/grid_layout`, read through `custom.grid_layout`), then THIS VIEW's own choice โ€”\n// three layers, in that order, and the view always wins;\n// ยท the SUMMARY measures a column may offer, from the store's own list\n// (`custom.agg_operations`), filtered to what the column can mean;\n// ยท a REFERENCE a person pastes into a prompt โ€” the `table` / `table_row` reference nouns.\n\nimport type { Field, GridLayout, GridLayoutMode, GridRowHeight, ReadRow, Table } from \"@ai-matrx/records\";\nimport type { MatrxDataTableCopyConfig } from \"@ai-matrx/design-system/data-table/types\";\n\nimport { fieldName } from \"./names\";\nimport { shownValue, type LabelLookup } from \"./labels\";\nimport { editorKindFor } from \"./parity\";\n\n// โ”€โ”€ the default sort โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n\nexport interface DefaultSort {\n /** The Field KEY โ€” the column id the grid builds every column with. */\n id: string;\n direction: \"asc\" | \"desc\";\n}\n\n/**\n * THE TABLE'S OWN SORT, as the grid's primitive takes it โ€” or `null`.\n *\n * The store writes `default_sort` as `[{field, direction}]` (`custom.table_declare`, the\n * mover, and this package's own `declareTable` all do), with `field` a Field KEY. The type\n * the read carries says `string | null`, and older rows hold a bare key or `\"key desc\"`, so\n * all three shapes are read. A sort that names a column this table no longer has is\n * DROPPED, never guessed at: sorting by the nearest column would be the screen inventing an\n * order nobody chose. Only the first sort key is used โ€” the grid's primitive sorts by one.\n */\nexport function defaultSortOf(\n table: Pick<Table, \"default_sort\"> | null | undefined,\n fields: readonly Field[],\n): DefaultSort | null {\n const raw = (table?.default_sort ?? null) as unknown;\n const first = Array.isArray(raw) ? raw[0] : raw;\n let name: string | null = null;\n let direction: \"asc\" | \"desc\" = \"asc\";\n if (typeof first === \"string\") {\n const trimmed = first.trim();\n if (trimmed === \"\") return null;\n if (trimmed.startsWith(\"-\")) {\n name = trimmed.slice(1).trim();\n direction = \"desc\";\n } else {\n const [head, tail] = trimmed.split(/\\s+/);\n name = head ?? null;\n if (tail && tail.toLowerCase() === \"desc\") direction = \"desc\";\n }\n } else if (first && typeof first === \"object\") {\n const entry = first as Record<string, unknown>;\n const named = entry[\"field\"] ?? entry[\"key\"] ?? entry[\"id\"];\n name = typeof named === \"string\" ? named : null;\n if (String(entry[\"direction\"] ?? \"\").toLowerCase() === \"desc\") direction = \"desc\";\n }\n if (!name) return null;\n // A Field named by its KEY (what the store writes) or, in an older row, by its id.\n const field = fields.find((f) => f.key === name) ?? fields.find((f) => f.id === name);\n return field ? { id: field.key, direction } : null;\n}\n\n// โ”€โ”€ the layout โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n\n/** A view's own layout choices โ€” the per-view override of the organization's knob. */\nexport interface ViewLook {\n /** px, the grid's existing per-view row height. */\n rowHeight?: number | undefined;\n /** The view's own frozen columns, by key (the grid's existing per-view freeze). */\n frozen?: string[] | undefined;\n wrap?: boolean | undefined;\n fit?: GridLayoutMode | undefined;\n /**\n * \"Freeze the first column\", said by THIS VIEW โ€” `false` is an explicit NO over an\n * organization whose knob freezes it, which an empty `frozen` list cannot say (an empty\n * list is how a view that never chose reads).\n */\n freezeFirst?: boolean | undefined;\n}\n\n/**\n * The store's three row heights, in the pixels the grid's primitive takes. \"normal\" is the\n * primitive's own density default, which is why it maps to nothing rather than a number\n * that would override the density a host chose.\n */\nexport const ROW_HEIGHT_PX: Record<GridRowHeight, number | undefined> = {\n compact: 28,\n normal: undefined,\n tall: 64,\n};\n\n/** The px a view remembers, read back as the store's word โ€” for the menu's check mark. */\nexport function rowHeightWord(px: number | undefined): GridRowHeight {\n if (px === undefined) return \"normal\";\n if (px <= 32) return \"compact\";\n if (px >= 52) return \"tall\";\n return \"normal\";\n}\n\nexport interface ResolvedLook {\n rowHeight: number | undefined;\n rowHeightWord: GridRowHeight;\n wrap: boolean;\n /** What the table does with its width: fit it, or keep natural widths and scroll. */\n fit: \"fit\" | \"scroll\";\n /** The choice as stored โ€” `auto` resolves to fit or scroll by the column count. */\n fitChoice: GridLayoutMode;\n frozen: string[];\n /** Where each of the four came from, so the menu can say \"your organization's\". */\n from: { rowHeight: Source; wrap: Source; fit: Source; frozen: Source };\n}\n\nexport type Source = \"view\" | \"organization\" | \"platform\";\n\nfunction storeSource(layout: GridLayout | null, key: string): Source {\n const said = layout?.source?.[key];\n return said === \"organization\" ? \"organization\" : \"platform\";\n}\n\n/**\n * THE COLUMN ORDER THE LOOK READS \"FIRST\" FROM: the Fields' own order, with the column that names\n * each record (`title_field`) leading โ€” \"freeze the first column\" freezes the record's name, as\n * Airtable's primary field is always the one that stays (merged-grid review 2, D1: the menu said\n * \"Freeze Title\" and pinned whichever column sorted first). Pure.\n */\nexport function lookColumnKeys(orderedKeys: readonly string[], titleField: string | null | undefined): string[] {\n return titleField && orderedKeys.includes(titleField)\n ? [titleField, ...orderedKeys.filter((k) => k !== titleField)]\n : [...orderedKeys];\n}\n\n/**\n * THE LOOK THE GRID OPENS WITH: the view's own choice over the organization's knob over the\n * platform. `columnKeys` is the grid's column order, because \"freeze the first column\" is a\n * statement about whichever column is first, and \"automatic\" fit is a statement about how\n * many columns there are (`fit_max_columns`).\n *\n * `layout` null means the store's answer has not arrived or was refused; the grid then opens\n * exactly as it always did โ€” the knob is an override, never the only thing that makes a\n * table readable.\n */\nexport function resolveLook(\n layout: GridLayout | null,\n view: ViewLook,\n columnKeys: readonly string[],\n): ResolvedLook {\n const stored = layout?.layout;\n const rowHeight =\n view.rowHeight !== undefined ? view.rowHeight : stored ? ROW_HEIGHT_PX[stored.row_height] : undefined;\n const wrap = view.wrap ?? stored?.wrap ?? true;\n const fitChoice: GridLayoutMode = view.fit ?? stored?.mode ?? \"auto\";\n const max = stored?.fit_max_columns ?? 8;\n const fit: \"fit\" | \"scroll\" =\n fitChoice === \"fit\" ? \"fit\" : fitChoice === \"scroll\" ? \"scroll\" : columnKeys.length <= max ? \"fit\" : \"scroll\";\n const freezeFirst = view.freezeFirst ?? stored?.freeze_first_column ?? false;\n const frozen =\n view.frozen && view.frozen.length > 0\n ? view.frozen\n : freezeFirst && columnKeys[0]\n ? [columnKeys[0]]\n : [];\n return {\n rowHeight,\n rowHeightWord: view.rowHeight !== undefined ? rowHeightWord(view.rowHeight) : (stored?.row_height ?? \"normal\"),\n wrap,\n fit,\n fitChoice,\n frozen,\n from: {\n rowHeight: view.rowHeight !== undefined ? \"view\" : storeSource(layout, \"row_height\"),\n wrap: view.wrap !== undefined ? \"view\" : storeSource(layout, \"wrap\"),\n fit: view.fit !== undefined ? \"view\" : storeSource(layout, \"mode\"),\n frozen:\n (view.frozen && view.frozen.length > 0) || view.freezeFirst !== undefined\n ? \"view\"\n : storeSource(layout, \"freeze_first_column\"),\n },\n };\n}\n\n// โ”€โ”€ the summary bar โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n\n/** What each of the store's measures is called on screen. */\nexport const MEASURE_LABEL: Record<string, string> = {\n count: \"Count\",\n sum: \"Sum\",\n avg: \"Average\",\n min: \"Smallest\",\n max: \"Largest\",\n median: \"Median\",\n filled: \"Filled\",\n empty: \"Empty\",\n unique: \"Unique\",\n percent_empty: \"Percent empty\",\n percent_filled: \"Percent filled\",\n range: \"Range\",\n};\n\n/**\n * NOTION'S CALCULATE MENU (Spaces N6) โ€” the embedded grid's footer offers Notion's list, in Notion's\n * order and words, each answered by one of the store's measures. `footer` is what the footer cell\n * says before the value (\"Sum 1,240\"). Count all counts records, so every column offers it.\n */\nexport const NOTION_CALCULATIONS: ReadonlyArray<{ op: string; menu: string; footer: string }> = [\n { op: \"count\", menu: \"Count all\", footer: \"Count\" },\n { op: \"unique\", menu: \"Count unique values\", footer: \"Unique\" },\n { op: \"empty\", menu: \"Count empty\", footer: \"Empty\" },\n { op: \"filled\", menu: \"Count not empty\", footer: \"Not empty\" },\n { op: \"percent_empty\", menu: \"Percent empty\", footer: \"Empty\" },\n { op: \"percent_filled\", menu: \"Percent not empty\", footer: \"Not empty\" },\n { op: \"sum\", menu: \"Sum\", footer: \"Sum\" },\n { op: \"avg\", menu: \"Average\", footer: \"Average\" },\n { op: \"median\", menu: \"Median\", footer: \"Median\" },\n { op: \"min\", menu: \"Min\", footer: \"Min\" },\n { op: \"max\", menu: \"Max\", footer: \"Max\" },\n { op: \"range\", menu: \"Range\", footer: \"Range\" },\n];\n\n/** Notion's calculations this column can mean, among those the store offers. */\nexport function calculationsFor(field: Field, storeOps: readonly string[]): Array<{ op: string; menu: string; footer: string }> {\n const offered = new Set(measuresFor(field, storeOps, true));\n return NOTION_CALCULATIONS.filter((c) => offered.has(c.op));\n}\n\nconst NUMERIC_ONLY = new Set([\"sum\", \"avg\", \"median\"]);\nconst ARITHMETIC = new Set([\n \"add\", \"sub\", \"mul\", \"div\", \"mod\", \"power\", \"sum\", \"average\", \"round\", \"abs\", \"min\", \"max\", \"ceiling\", \"floor\",\n]);\nconst ORDERED = new Set([\"min\", \"max\", \"range\"]);\n\n/** Is this a column whose values are numbers the store can add up? */\nexport function isNumericField(field: Field): boolean {\n const kind = editorKindFor(field);\n if (kind === \"number\" || kind === \"currency\" || kind === \"percent\" || kind === \"rollup\") return true;\n // A worked-out column is a number when its expression is arithmetic โ€” `{Visit fee} -\n // {Deposit taken}` is stored as `{\"op\":\"fx.sub\",โ€ฆ}`. A joined label is not.\n if (kind !== \"formula\") return false;\n const expr = ((field.config ?? {}) as Record<string, unknown>)[\"expr\"] as { op?: unknown } | undefined;\n const op = typeof expr?.op === \"string\" ? expr.op.replace(/^fx\\./, \"\") : \"\";\n return ARITHMETIC.has(op);\n}\n\nfunction isOrderedField(field: Field): boolean {\n return isNumericField(field) || editorKindFor(field) === \"datetime\";\n}\n\n/**\n * THE MEASURES THIS COLUMN MAY OFFER โ€” the store's own list (`custom.agg_operations`),\n * narrowed to what the column can mean. A sum of a text column is not a number the store\n * would refuse; it is a number that means nothing, which is worse, so it is never offered.\n * `count` counts records and belongs to no column, so it is offered once, on the first.\n */\nexport function measuresFor(field: Field, storeOps: readonly string[], firstColumn: boolean): string[] {\n return storeOps.filter((op) => {\n if (op === \"count\") return firstColumn;\n if (NUMERIC_ONLY.has(op)) return isNumericField(field);\n if (ORDERED.has(op)) return isOrderedField(field);\n return true;\n });\n}\n\n// โ”€โ”€ a reference for a prompt โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n\n/**\n * THE GRID'S COPY, WITH ITS REFERENCES (TABLE-PARITY R3).\n *\n * The data table's Copy already offers the rows as text and as agent JSON; what it could not\n * offer is a REFERENCE โ€” the pointer an agent expands itself (`table` and `table_row`, the\n * reference nouns the server resolves, and which follow a moved table into the record store).\n * The primitive takes references only on an explicit copy config, so this is that config: the\n * same text and JSON the primitive's own default writes (every column that reads a value, in\n * the view's order, by its own name), plus the two references. A row reference is NAMED by\n * the table's row label so it reads as a name. The host's content-transfer port decides\n * whether it can build them; where none is installed, the reference choice is simply absent.\n */\nexport function gridCopyConfig(args: {\n tableId: string;\n tableName: string;\n fields: readonly Field[];\n rowLabel: (row: ReadRow) => string;\n /**\n * WHAT A REFERENCE IS CALLED โ€” the grid's own resolver (`useRecordLabels`). Copy and Copy for\n * AI carry the words the cell shows (\"Ojeda residence\"), never the id the store keeps.\n */\n labels?: LabelLookup;\n}): MatrxDataTableCopyConfig<ReadRow> {\n const { tableId, tableName, fields, rowLabel, labels } = args;\n const shown = (row: ReadRow) => {\n const document = (row.document ?? {}) as Record<string, unknown>;\n const out: Record<string, unknown> = {};\n for (const field of fields) {\n if (document[field.key] !== undefined) out[fieldName(field)] = shownValue(field, document[field.key], labels);\n }\n return out;\n };\n const text = (value: unknown): string =>\n value === null || value === undefined ? \"\" : typeof value === \"object\" ? JSON.stringify(value) : String(value);\n return {\n label: tableName,\n listLabel: tableName,\n location: tableName,\n rowKind: \"table-row\",\n listKind: \"table-view\",\n listDescription: `${tableName} โ€” the records on this page, filtered and sorted as shown.`,\n humanRow: (row) =>\n Object.entries(shown(row))\n .map(([name, value]) => `${name}: ${text(value)}`)\n .join(\"\\n\"),\n agentRow: (row) => shown(row),\n rowReferences: (row) => [\n {\n id: `table_row:${tableId}:${row.id}`,\n label: rowLabel(row),\n noun: \"table_row\",\n items: [{ table_id: tableId, row_id: row.id, table_name: tableName, row_label: rowLabel(row) }],\n },\n ],\n references: () => [\n {\n id: `table:${tableId}`,\n label: tableName,\n noun: \"table\",\n items: [{ table_id: tableId, table_name: tableName }],\n },\n ],\n };\n}\n","// src/gridPage.ts โ€” @ai-matrx/records-ui ยท merge tranche 6k (server search, sort and paging)\n//\n// THE MERGED GRID ASKS THE STORE FOR ITS PAGE (`custom.read_records_page` through `useRecordPage`).\n//\n// The classic grid read one page and then searched, filtered and sorted THAT page in the browser โ€”\n// the readiness audit searched a 1,000-row table for a record on page six and found nothing, and a\n// sort ordered fifty rows while claiming to order the table. Here the grid's own query state (the\n// data table's search box, its column filters, its header sort, its page) is translated into the\n// store's one question and the store answers it: sorted over the whole table, searched over every\n// column this reader may see, filtered in its own WHERE clause, counted.\n//\n// These are the pure halves: the sort in the store's shape, and the column filters in the store's\n// one filter shape. A filter the store cannot ask is NEVER applied in the browser to the page it\n// happens to hold (that is the lie this tranche removes) โ€” it is refused by name, and the grid\n// says so.\n\nimport type { Field, RecordFilter, RecordPageSort, RecordsError, RuleExpression, StoreFilter } from \"@ai-matrx/records\";\nimport { isRuleFilter } from \"@ai-matrx/records\";\nimport { dateFilterBounds, dateFilterDayBounds, dayInstantWindow } from \"@ai-matrx/design-system/data-table\";\nimport { getPersonTimeZone } from \"./personTime\";\nimport type { ColumnFilterValue, ColumnFiltersState, SortState } from \"@ai-matrx/design-system/data-table/types\";\nimport { editorKindFor } from \"./parity\";\nimport { fieldName } from \"./names\";\n\n/**\n * ๐Ÿšจ WHY THE STORE CANNOT SORT BY THIS COLUMN, or `null` when it can (merged-grid review\n * 2026-09-26). A column worked out on every read (`type: \"formula\"`, `compute_on: \"read\"` โ€” the\n * default, and what a lookup or a rollup is) keeps no value in the record, so\n * `custom.read_records_page` refuses to order the whole table by it. The header offered \"Sort\n * ascending\" anyway; the refused page then emptied the grid. The screen asks this ONE question\n * before offering a sort, and says the store's own reason instead.\n */\nexport function sortRefusal(f: Field): string | null {\n if (!computedOnRead(f)) return null;\n return `${fieldName(f)} is worked out each time it is read, so the store keeps no value to sort the whole table by. Sort by a column it is worked out from.`;\n}\n\n/** The same fact for a filter: the store's WHERE clause reads kept values, and this column keeps none. */\nexport function filterRefusal(f: Field): string | null {\n if (!computedOnRead(f)) return null;\n return `${fieldName(f)} is worked out each time it is read, so the store keeps no value to filter the whole table by. Filter by a column it is worked out from.`;\n}\n\nfunction computedOnRead(f: Field): boolean {\n const kind = String(editorKindFor(f));\n return (\n (f.type === \"formula\" || kind === \"formula\" || kind === \"lookup\" || kind === \"rollup\") &&\n String((f as { compute_on?: unknown }).compute_on ?? \"read\") === \"read\"\n );\n}\n\n/**\n * Does this column hold NUMBERS the store keeps โ€” a number, money or percent column, or the record\n * number (`config.system: autonumber`, worked out once on save and kept)?\n */\nexport function holdsNumbers(field: Field): boolean {\n const kind = String(editorKindFor(field));\n if (kind === \"number\" || kind === \"currency\" || kind === \"percent\") return true;\n return String(((field.config ?? {}) as Record<string, unknown>)[\"system\"] ?? \"\") === \"autonumber\";\n}\n\n/** How the store should compare a column's values when it sorts by it. */\nexport function sortAsFor(field: Field): NonNullable<RecordPageSort[\"as\"]> {\n const kind = String(editorKindFor(field));\n // The record number sorts 1, 2 โ€ฆ 10, never \"10\" before \"9\".\n if (holdsNumbers(field)) return \"number\";\n if (kind === \"datetime\") return \"date\";\n return \"text\";\n}\n\n/** The grid's header sort, in the store's page-order shape. A sort on a column that is gone is no sort. */\nexport function pageSortFor(fields: readonly Field[], sort: SortState | null | undefined): RecordPageSort[] {\n if (!sort) return [];\n const field = fields.find((f) => f.key === sort.id);\n if (!field) return [];\n return [{ field: field.key, direction: sort.direction === \"desc\" ? \"desc\" : \"asc\", as: sortAsFor(field) }];\n}\n\n/** A text a person typed, as a case-insensitive \"contains\" pattern (the store's `matches` is a regex). */\nexport function containsPattern(text: string): string {\n return `(?i)${text.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\")}`;\n}\n\nexport interface StoreQuestion {\n /** The whole question: the view's own filter AND every column filter the store can ask. */\n filter: StoreFilter | undefined;\n /** Column filters the store cannot ask, each as a sentence naming the column. Never applied locally. */\n refused: string[];\n}\n\ntype Clause = { flat: RecordFilter } | { rule: RuleExpression };\n\n/**\n * THE DATA TABLE'S \"(empty)\" TICK, as it writes it into a select filter's `values`\n * (`SELECT_EMPTY_VALUE` in the design system's filter engine). Asked of the store as \"has not been\n * answered\". `a-text-column-filters-by-its-values-counted-by-the-store.test.tsx` ticks the real\n * checklist, so this word drifting from the table's is a red test, not a silent no-op.\n */\nexport const BLANK_VALUE = \"__empty__\";\n\nconst field = (f: Field): RuleExpression => ({ field: f.id });\nconst constant = (value: unknown): RuleExpression => ({ const: value });\n/** A column's moment is in the half-open window `{from, to}` โ€” the store's `within` node. */\nconst withinWindow = (f: Field, window: { from?: string; to?: string }): RuleExpression =>\n ({ op: \"within\", args: [field(f), constant(window)] }) as unknown as RuleExpression;\nconst op = (name: string, ...args: RuleExpression[]): RuleExpression =>\n ({ op: name, args }) as unknown as RuleExpression;\n\n/** One column filter, as a store clause โ€” or the sentence saying why the store cannot ask it. */\nexport function clauseFor(f: Field, value: ColumnFilterValue, now: Date = new Date()): Clause | string | null {\n const name = fieldName(f);\n const refused = filterRefusal(f);\n if (refused) return refused;\n const not = (rule: RuleExpression): RuleExpression => op(\"not\", rule);\n switch (value.kind) {\n case \"text\": {\n const mode = value.mode ?? \"contains\";\n if (mode === \"empty\" || mode === \"null\") return value.negated ? { rule: op(\"present\", field(f)) } : { rule: not(op(\"present\", field(f))) };\n if (mode === \"not_empty\" || mode === \"not_null\") return value.negated ? { rule: not(op(\"present\", field(f))) } : { rule: op(\"present\", field(f)) };\n const text = value.value.trim();\n if (text === \"\") return null;\n const kind = String(editorKindFor(f));\n if (kind === \"number\" || kind === \"currency\" || kind === \"percent\" || kind === \"checkbox\") {\n return `${name} holds numbers, so it is filtered by a range, not by words.`;\n }\n const matches = op(\"matches\", field(f), constant(containsPattern(text)));\n return { rule: value.negated ? not(matches) : matches };\n }\n case \"select\": {\n // \"Is any of\" the ticked values; \"(empty)\" ticked (`BLANK_VALUE`) is \"has not been answered\";\n // Exclude is everything else โ€” a blank row is not one of the ticked values, so Exclude keeps it\n // unless \"(empty)\" is ticked too (the Sheet's \"Exclude these instead\", slice C).\n const ticked = value.values ?? (value.value === \"\" ? [] : [value.value]);\n const blank = ticked.includes(BLANK_VALUE);\n const words = ticked.filter((w) => w !== BLANK_VALUE);\n if (!blank && words.length === 0) return value.negated ? null : { rule: constant(false) };\n const present = op(\"present\", field(f));\n const kind = String(editorKindFor(f));\n if (kind === \"datetime\" || kind === \"date\") {\n // TICKED DAYS: each is a half-open window, asked as the store's one `within` leaf. One day\n // alone stays a flat window (what a dashboard number asks); several days are an ANY of\n // them, a day beside \"(empty)\" adds the unanswered, and Exclude is NOT โ€” the same shapes\n // every other value list makes, kept by a saved view in the one filter grammar.\n if (words.length === 0) return { rule: value.negated ? present : not(present) };\n const holdsDays = String(((f.config ?? {}) as Record<string, unknown>)[\"kind\"] ?? \"\") === \"date\";\n if (words.length === 1 && !blank && !value.negated) return { flat: { [f.key]: dayWindow(words[0]!, holdsDays) } };\n const leaves = words.map((day) => withinWindow(f, dayWindow(day, holdsDays)));\n const anyDay = leaves.length === 1 ? leaves[0]! : op(\"or\", ...leaves);\n if (!value.negated) return { rule: blank ? op(\"or\", not(present), anyDay) : anyDay };\n return { rule: blank ? op(\"and\", present, not(anyDay)) : not(anyDay) };\n }\n // The list's words are text; a number or yes/no column is asked in its own kind.\n const asStored = (w: string): unknown =>\n holdsNumbers(f) || kind === \"rating\"\n ? Number.isFinite(Number(w)) ? Number(w) : w\n : kind === \"checkbox\" && (w === \"true\" || w === \"false\") ? w === \"true\" : w;\n const stored = words.map(asStored);\n const anyWord =\n stored.length === 0 ? null : stored.length === 1 ? op(\"eq\", field(f), constant(stored[0])) : op(\"or\", ...stored.map((w) => op(\"eq\", field(f), constant(w))));\n if (!value.negated) {\n if (!blank && stored.length === 1) return { flat: { [f.key]: stored[0] as string | number | boolean } };\n if (!blank) return { rule: anyWord! };\n return { rule: anyWord ? op(\"or\", not(present), anyWord) : not(present) };\n }\n if (blank) return { rule: anyWord ? op(\"and\", present, not(anyWord)) : present };\n return { rule: op(\"or\", not(present), not(anyWord!)) };\n }\n case \"boolean\":\n return value.negated ? { rule: op(\"ne\", field(f), constant(value.value)) } : { flat: { [f.key]: value.value } };\n case \"number\": {\n // The number filter's operators (lane C): =, โ‰ , <, >, empty, not empty; \"between\" below.\n // The header's Exclude (slice C) turns every one of them round, never only \"between\".\n const turned = (rule: RuleExpression): Clause => ({ rule: value.negated ? not(rule) : rule });\n if (value.op === \"empty\") return turned(not(op(\"present\", field(f))));\n if (value.op === \"not_empty\") return turned(op(\"present\", field(f)));\n if (value.op === \"eq\" || value.op === \"ne\") {\n if (typeof value.min !== \"number\") return null;\n return turned(op(value.op, field(f), constant(value.min)));\n }\n if (value.op === \"lt\") return typeof value.max === \"number\" ? turned(op(\"lt\", field(f), constant(value.max))) : null;\n if (value.op === \"gt\") return typeof value.min === \"number\" ? turned(op(\"gt\", field(f), constant(value.min))) : null;\n const parts: RuleExpression[] = [];\n if (typeof value.min === \"number\") parts.push(op(\"gte\", field(f), constant(value.min)));\n if (typeof value.max === \"number\") parts.push(op(\"lte\", field(f), constant(value.max)));\n if (parts.length === 0) return null;\n const range = parts.length === 1 ? parts[0]! : op(\"and\", ...parts);\n return { rule: value.negated ? not(range) : range };\n }\n case \"date\": {\n // A relative preset (\"Today\", \"Last hour\") is worked out at the moment the question is asked,\n // in the viewer's own clock, and sent as exact bounds (grids review 3, lane H item 3). A saved\n // view's \"Today\" is today when it is opened โ€” never the day somebody first picked it. A\n // column that holds a DAY is asked in days, never compared with an instant.\n // The window is read in the PERSON'S zone (setPersonTimeZone), so \"Today\" here is what the calendar calls today.\n const zone = getPersonTimeZone() ?? undefined;\n const moments = dateFilterBounds(value.preset ? { ...value, anchor: now.toISOString() } : value, now, zone);\n const holdsDays = String(((f.config ?? {}) as Record<string, unknown>)[\"kind\"] ?? \"\") === \"date\";\n const bounds = holdsDays ? dateFilterDayBounds(moments, zone) : moments;\n if (bounds.since === undefined && bounds.until === undefined) return null;\n return {\n flat: {\n [f.key]: {\n ...(bounds.since === undefined ? {} : { from: bounds.since }),\n ...(bounds.until === undefined ? {} : { to: bounds.until }),\n },\n },\n };\n }\n default:\n return `${name} cannot be filtered that way by the store.`;\n }\n}\n\n/**\n * ONE DAY AS A HALF-OPEN WINDOW. A column that holds DAYS is asked in days (\"2026-09-01\" up to\n * \"2026-09-02\"); a column of moments is asked from the viewer's own midnight to the next.\n */\nexport function dayWindow(day: string, holdsDays: boolean): { from: string; to: string } {\n if (!holdsDays) return dayInstantWindow(day, getPersonTimeZone() ?? undefined);\n const [y, m, d] = day.split(\"-\").map(Number);\n const ymd = (t: Date) => `${t.getUTCFullYear()}-${String(t.getUTCMonth() + 1).padStart(2, \"0\")}-${String(t.getUTCDate()).padStart(2, \"0\")}`;\n return { from: ymd(new Date(Date.UTC(y!, (m ?? 1) - 1, d ?? 1))), to: ymd(new Date(Date.UTC(y!, (m ?? 1) - 1, (d ?? 1) + 1))) };\n}\n\n/**\n * THE VIEW'S QUESTION AND THE COLUMN FILTERS, AS ONE STORE QUESTION.\n *\n * Flat clauses (an equality, `null`, a date window) stay flat when nothing else needs a Rule, so a\n * plain \"Status is Scheduled\" is the same question a dashboard number asks. When any clause needs a\n * Rule (words that contain, a range, several choices, NOT), the flat equalities become `eq` nodes of\n * one ALL group. A date window becomes a `within` leaf of that group.\n */\nexport function storeQuestion(\n fields: readonly Field[],\n base: StoreFilter | undefined,\n columnFilters: ColumnFiltersState,\n now: Date = new Date(),\n): StoreQuestion {\n const refused: string[] = [];\n const flat: RecordFilter = {};\n const rules: RuleExpression[] = [];\n if (base && isRuleFilter(base)) rules.push(base);\n else if (base) Object.assign(flat, base);\n for (const [key, value] of Object.entries(columnFilters)) {\n if (!value) continue;\n const f = fields.find((candidate) => candidate.key === key);\n if (!f) continue;\n const clause = clauseFor(f, value, now);\n if (clause === null) continue;\n if (typeof clause === \"string\") {\n refused.push(clause);\n continue;\n }\n if (\"flat\" in clause) Object.assign(flat, clause.flat);\n else rules.push(clause.rule);\n }\n const flatKeys = Object.keys(flat);\n if (rules.length === 0) return { filter: flatKeys.length > 0 ? flat : undefined, refused };\n for (const key of flatKeys) {\n const f = fields.find((candidate) => candidate.key === key);\n const value = flat[key];\n if (!f) continue;\n if (value !== null && typeof value === \"object\") {\n rules.push(withinWindow(f, value as { from?: string; to?: string }));\n continue;\n }\n rules.push(value === null ? op(\"not\", op(\"present\", field(f))) : op(\"eq\", field(f), constant(value)));\n }\n return { filter: rules.length === 1 ? rules[0]! : op(\"and\", ...rules), refused };\n}\n\n/**\n * ๐Ÿšจ THE STORE'S SENTENCE, WITH ITS COLUMNS CALLED BY THEIR NAMES (merged-grid review 2026-09-26).\n * A refused page read names the column by its KEY (\"The column \\\"visit_total\\\" is worked out each\n * time it is readโ€ฆ\"), and the refusal formatter โ€” rightly โ€” drops a sentence carrying a machine\n * key, so the grid printed only \"That was not accepted\" and \"Nothing was read.\": a red fragment.\n * The key is the grid's to translate: every Field key the sentence names becomes that column's\n * name, and the store's sentence is then said whole.\n */\nexport function inColumnWords(error: RecordsError, fields: readonly Field[]): RecordsError {\n const say = (text: string | null | undefined): string | null | undefined => {\n if (!text) return text;\n let out = text;\n for (const f of [...fields].sort((a, b) => b.key.length - a.key.length)) {\n const key = f.key.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n out = out.replace(new RegExp(`([\"โ€œ'])${key}([\"โ€'])`, \"g\"), `$1${fieldName(f)}$2`);\n }\n return out;\n };\n return { ...error, message: say(error.message) ?? error.message, ...(error.hint ? { hint: say(error.hint) ?? error.hint } : {}) };\n}\n","// src/rowVersion.ts โ€” @ai-matrx/records-ui ยท pure (server-safe)\nimport type { ReadRow } from \"@ai-matrx/records\";\n\n/** The version a row carries itself, when the host's read gave it one. */\nexport function ownVersion(row: ReadRow): number | null {\n const v = (row as ReadRow & { version?: unknown }).version;\n return typeof v === \"number\" ? v : null;\n}\n","// src/groupReads.ts โ€” @ai-matrx/records-ui ยท lane SEED-ENTITY\n//\n// WHAT A GROUPED GRID ASKS, AS PURE FUNCTIONS โ€” shared by the grid (which asks them) and the server's\n// first-page seed (which asks them first, with the same arguments, so the browser's call finds its\n// answer). Server-safe: no React.\n\nimport type { AggregateMeasure, AggregateOperation, Field, RecordPageSort } from \"@ai-matrx/records\";\n\nimport type { GridGrouping } from \"./presentation\";\nimport { MEASURE_LABEL } from \"./gridLook\";\nimport { sortAsFor } from \"./gridPage\";\nimport { editorKindFor } from \"./parity\";\n\n/**\n * A grouping the merged grid keeps as ROWS under group headers: one level, no pivot, no drill trail.\n * Anything else opens the drill (`useGridDrillQuestion`), which reads other doors.\n */\nexport function isPlainGrouping(grouping: GridGrouping | undefined): grouping is GridGrouping {\n return Boolean(grouping) && !(grouping!.then && grouping!.then.length > 0) && !grouping!.across && !(grouping!.trail && grouping!.trail.length > 0);\n}\n\n/** The page is asked in group order first, then in the sort the person chose. */\nexport function groupedPageSort(fields: readonly Field[], groupKey: string | null, sort: RecordPageSort[]): RecordPageSort[] {\n if (!groupKey) return sort;\n const field = fields.find((f) => f.key === groupKey);\n if (!field) return sort;\n return [{ field: groupKey, direction: \"asc\", as: sortAsFor(field) }, ...sort.filter((s) => s.field !== groupKey)];\n}\n\n/** The chosen summaries as the store's measures, in the order the grid sends them (sorted, unknown ops left out). */\nfunction chosenEntries(chosen: Record<string, string>): Array<[string, string]> {\n return (JSON.parse(JSON.stringify(Object.entries(chosen).sort())) as Array<[string, string]>).filter(([, op]) => op in MEASURE_LABEL);\n}\n\n/** Every group's count and each chosen summary: `record_aggregate`'s measures when grouped by the grouping column. */\nexport function groupFactMeasures(chosen: Record<string, string>): AggregateMeasure[] {\n return [{ op: \"count\" }, ...chosenEntries(chosen).filter(([, op]) => op !== \"count\").map(([key, op]) => ({ op: op as AggregateOperation, key }))];\n}\n\n/** The summary bar's measures over the whole table: none when nothing is chosen. */\nexport function summaryBarMeasures(chosen: Record<string, string>): AggregateMeasure[] {\n return chosenEntries(chosen).map(([key, op]) => (op === \"count\" ? { op: \"count\" as const } : { op: op as AggregateOperation, key }));\n}\n\n/** The keys of the choice columns, in the table's field order: each has a header filter list with counts. */\nexport function choiceFieldKeys(fields: readonly Field[]): string[] {\n return fields\n .filter((f) => {\n const kind = String(editorKindFor(f));\n return kind === \"select\" || kind === \"multi_select\";\n })\n .map((f) => f.key);\n}\n\n/** How many links a grid cell draws of a reverse column (the chips; the rest is \"+N\"). */\nexport const REVERSE_CELL_CHIPS = 3;\n","/**\n * HOW A TABLE LOOKS โ€” colors, formats and the frozen column.\n *\n * THE SPLIT, AND WHY IT IS THIS ONE. A Field declares WHAT it holds: a\n * currency, a percent, a date, a person. A VIEW declares HOW that reads and\n * what it is worth looking at: two decimal places or none, tint the row when\n * Status is Blocked, keep Name in place while the rest scrolls. Airtable draws\n * the line in exactly this place, and it has to be here: two people read one\n * table two different ways, and neither reading is a fact about the records.\n *\n * So nothing in this module is a new column on a Field and nothing here needed\n * a migration. A presentation lives on the saved-view record (`views.ts`), which\n * is an ordinary record of an ordinary Table โ€” which means an agent sets it\n * through the same `records` tool it uses for everything else, and it travels\n * with the table the way every other record does.\n *\n * A FIELD STILL GETS ITS MONEY RIGHT WITH NO CONFIGURATION. `formatForField`\n * reads the Field's OWN declaration โ€” a `currency` parity type with unit `$`, a\n * `percent`, a `datetime` with `config.kind` โ€” and returns the format that\n * follows from it. The view's `formats` map only OVERRIDES that. A table nobody\n * has ever styled still shows `$1,250.50` and `45%`.\n */\n\n// The style parser from its own client-free subpath (lane SSR-ROWS): `@ai-matrx/records-ui/first-page`\n// runs in a host's SERVER component, and every export of the \"use client\" `data-table` entry is a\n// client reference there โ€” calling `parseTableStyle` through it threw on the server.\nimport { parseTableStyle, tableStyleIsEmpty } from \"@ai-matrx/design-system/data-table/table-style\";\nimport {\n COLOR_ROTATION,\n type ChoiceColorLookup,\n type GroupAggregateKind,\n type GroupAggregateSpec,\n type GroupOrder,\n type StyleColor,\n type TableStyle,\n} from \"@ai-matrx/design-system/data-table\";\nimport { getFieldFormat, type FieldFormatConfig } from \"@ai-matrx/design-system/field-formats\";\nimport type { ColorRule } from \"@ai-matrx/design-system/data-table\";\nimport { newerKindOf, ratingMaxOf, type Field, type TableDecorations } from \"@ai-matrx/records\";\n\nimport { editorKindFor } from \"./parity\";\nimport { dateFormatFor } from \"./dates\";\n\n/**\n * SECTIONS BY A FIELD โ€” the view's own grouping.\n *\n * `field` is a Field KEY, exactly like `frozen` and the keys of `formats`,\n * because a view is written against the Table's declaration and never against\n * whatever id a screen happened to build its columns with.\n *\n * `aggregates` is keyed by Field key too, and `collapsed` holds the data\n * table's own group keys (`s:Blocked`, `n:3`, `empty`), which is what the\n * primitive emits and the only thing that survives a value rendering the same\n * text as another.\n *\n * NOTHING HERE IS EVER CALLED `valueOf`, `toString` or `hasOwnProperty`: every\n * object in JavaScript already has all three, so an optional field by one of\n * those names is NEVER absent and a `config.valueOf ? โ€ฆ : โ€ฆ` check silently\n * reads `Object.prototype`. It cost the primitive a whole grouping once.\n */\nexport type GridGrouping = {\n /** The Field key whose value makes the sections. */\n field: string;\n /** Per-Field-key subtotal on every section header. */\n aggregates?: Record<string, GroupAggregateSpec>;\n /** Section order. The primitive's default is `value-asc`; blanks sort last. */\n order?: GroupOrder;\n /** The sections this view keeps shut, by the primitive's own group key. */\n collapsed?: string[];\n /** The grouped column drawn as a column too. Absent = it steps aside (the headers name its value). */\n showColumn?: boolean;\n /**\n * SUB-GROUPS (lane S1-PRIME VIEW-KEYS; ui-bench LISTS \"sub-grouping\"): up to two more levels\n * inside each section, by Field key. At most three levels in all โ€” the store refuses a fourth\n * (`custom.view_declare`). A layout that draws one level ignores the rest; it never drops them,\n * because this list is parsed and written back whole with the rest of the look.\n */\n then?: GridSubGroup[];\n /**\n * DRILL (lane DRILL-TABLE-PRIMITIVE): the Dimension pivoted across the top โ€” a Field key, or\n * `<key>:<grain>` for a date cut into periods.\n */\n across?: string;\n /** DRILL: the period `field` is cut into when it is a date (`month`); the store keeps `field` a Field key. */\n grain?: string;\n /** The Measures shown (\"what to display\"), by the store's own keys (`count`, `sum_quantity`). */\n show?: string[];\n /** The drill trail, in order: each crumb a Dimension reference and the value it narrowed to. */\n trail?: GridDrillStep[];\n};\n\n/** One crumb of a saved drill trail. `value: null` is \"no value\". */\nexport type GridDrillStep = { dim: string; value: string | null };\n\n/** One sub-group level: a Field key and its section order. */\nexport type GridSubGroup = { field: string; order?: GroupOrder; grain?: string };\n\n/** Everything a view says about how its table LOOKS. */\nexport type GridPresentation = {\n /** Color-by-a-column, rules and manual highlights. */\n style?: TableStyle;\n /** Per-column format overrides, by the Field's key. */\n formats?: Record<string, FieldFormatConfig>;\n /**\n * The columns that stay in place while the table scrolls sideways, by Field\n * key. Only the LEADING run of the view's column order can actually freeze โ€”\n * the data table decides that, and declines rather than pinning a column at a\n * guessed offset.\n */\n frozen?: string[];\n /** Collapsible sections by one Field, with subtotals on each header. */\n grouping?: GridGrouping;\n /**\n * The width a person dragged a column to, by Field key. SPARSE on purpose โ€”\n * a view saved before widths existed carries none and still parses, and a\n * column nobody resized is left to the table's own measurement.\n */\n widths?: Record<string, number>;\n /** The body row height this view remembers, in px. */\n rowHeight?: number;\n /**\n * Field keys hidden from this view โ€” the SAME hidden-column list the grid's\n * own column picker holds, now given a place on the view so it survives a\n * reload and a layout switch reads the identical set gallery and grid agree\n * on, rather than a second \"fields shown\" list nobody kept in sync.\n */\n hiddenFields?: string[];\n /**\n * THE FIELDS A CARD LEAVES OFF (board, list, gallery, calendar, timeline), by Field key โ€” the\n * card's own choice, kept apart from `hiddenFields` so a board's \"Card fields\" never hides the\n * grid's columns when the view switches layout (v7 TABLE-EXPERIENCE, walk 2026-10-05). An empty\n * list is a choice (every field shown); absent means the card never chose.\n */\n cardHidden?: string[];\n /**\n * THE ORDER A PERSON DRAGGED THE COLUMNS INTO, by Field key (merge tranche 6a). Sparse like\n * `widths`: a key it does not name keeps its Field's own place after the named ones, and a key\n * whose Field is gone is skipped โ€” a column order can never hide or invent a column.\n */\n columnOrder?: string[];\n /**\n * The gallery's card size, remembered the same way the grid remembers its\n * row height โ€” a per-view look, not a global default.\n */\n gallerySize?: \"small\" | \"medium\" | \"large\";\n /**\n * THIS VIEW'S OWN LAYOUT CHOICES, over the organization's `custom/grid_layout` knob (lane\n * DEFAULT-GRID-PARITY, TABLE-PARITY D1 D5 D6). Absent means \"whatever the organization\n * says\"; present is the view's word. `freezeFirst: false` is an explicit NO over an\n * organization that freezes the first column โ€” an empty `frozen` cannot say that.\n */\n wrap?: boolean;\n /** NOTION'S \"WRAP COLUMN\" (N5): the columns that wrap their text when the view as a whole does not. */\n wrapColumns?: string[];\n fit?: \"auto\" | \"fit\" | \"scroll\";\n freezeFirst?: boolean;\n /**\n * THE SUMMARY BAR (TABLE-PARITY F1): Field key โ†’ the store measure shown under that column\n * (`count`, `sum`, `avg`, `min`, `max`, `median`, `filled`, `empty`, `unique`), worked out\n * by `custom.record_aggregate` over the whole table.\n */\n summaries?: Record<string, string>;\n /**\n * WHERE THE FOOTER SITS (lane TABLE-PAGE-CHROME, owner 2026-09-25) โ€” the row count and the\n * pages: `sticky`, pinned to the bottom of the screen while the rows scroll (the older grid's\n * way, and the default when a view says nothing), or `inline`, right after the last row with\n * the page scrolling. Read by the Grid and a host's Sheet.\n */\n footer?: FooterPlacement;\n};\n\n/** Where a grid's footer sits. */\nexport type FooterPlacement = \"sticky\" | \"inline\";\n\n/** What a view that says nothing gets โ€” the older grid's pinned footer. */\nexport const DEFAULT_FOOTER: FooterPlacement = \"sticky\";\n\n/** Where THIS view's footer sits, said or defaulted. */\nexport function footerOf(p: GridPresentation | null | undefined): FooterPlacement {\n return p?.footer ?? DEFAULT_FOOTER;\n}\n\nexport const EMPTY_PRESENTATION: GridPresentation = {};\n\n/** A frozen column needs a known width, or the table declines to freeze at all. */\nexport const FROZEN_COLUMN_WIDTH = 200;\n\n/**\n * WHAT A ROW HEIGHT IS ALLOWED TO BE. Below the floor the text is clipped and\n * no editor fits; above the ceiling one record fills the screen. A view asking\n * for anything outside it is DROPPED rather than obeyed, because a saved look\n * must not be able to make a table unreadable โ€” and the Fields' own heights\n * still draw, which is the tolerant half.\n */\nexport const MIN_ROW_HEIGHT = 24;\nexport const MAX_ROW_HEIGHT = 96;\n\n/**\n * The aggregate kinds and orders the primitive actually implements. Listed here\n * because they are the tolerant parser's allow-list and a view is hostile input:\n * `satisfies` ties them to the primitive's own union, so a kind it renames stops\n * this file compiling instead of quietly parsing into a subtotal nobody draws.\n */\nconst AGGREGATE_KINDS = [\n \"count\",\n \"sum\",\n \"average\",\n \"min\",\n \"max\",\n \"empty\",\n \"filled\",\n \"unique\",\n \"checked\",\n \"unchecked\",\n \"percentFilled\",\n] as const satisfies readonly GroupAggregateKind[];\n\nconst GROUP_ORDERS = [\n \"value-asc\",\n \"value-desc\",\n \"count-desc\",\n \"first-seen\",\n] as const satisfies readonly GroupOrder[];\n\nfunction isRecord(v: unknown): v is Record<string, unknown> {\n return typeof v === \"object\" && v !== null && !Array.isArray(v);\n}\n\n/**\n * Read a presentation out of whatever the view record holds โ€” an object, or the\n * JSON string a text field stores.\n *\n * Tolerant in exactly the way `parseTableStyle` is: an unreadable half is\n * dropped and the rest still applies, because a table must keep drawing when\n * one piece of its styling goes out of date.\n */\nexport function parseGridPresentation(raw: unknown): GridPresentation {\n let value = raw;\n if (typeof value === \"string\") {\n const trimmed = value.trim();\n if (trimmed === \"\") return {};\n try {\n value = JSON.parse(trimmed);\n } catch {\n return {};\n }\n }\n if (!isRecord(value)) return {};\n\n const out: GridPresentation = {};\n\n const style = parseTableStyle(value[\"style\"]);\n if (!tableStyleIsEmpty(style)) out.style = style;\n\n if (isRecord(value[\"formats\"])) {\n const formats: Record<string, FieldFormatConfig> = {};\n for (const [key, cfg] of Object.entries(value[\"formats\"])) {\n if (!isRecord(cfg)) continue;\n const id = cfg[\"id\"];\n if (typeof id !== \"string\") continue;\n const options = cfg[\"options\"];\n formats[key] = isRecord(options)\n ? {\n id: id as FieldFormatConfig[\"id\"],\n options: options as NonNullable<FieldFormatConfig[\"options\"]>,\n }\n : { id: id as FieldFormatConfig[\"id\"] };\n }\n if (Object.keys(formats).length > 0) out.formats = formats;\n }\n\n if (Array.isArray(value[\"frozen\"])) {\n const frozen = value[\"frozen\"].filter((k): k is string => typeof k === \"string\");\n if (frozen.length > 0) out.frozen = frozen;\n }\n\n const grouping = parseGrouping(value[\"grouping\"]);\n if (grouping) out.grouping = grouping;\n\n if (isRecord(value[\"widths\"])) {\n const widths: Record<string, number> = {};\n for (const [key, raw] of Object.entries(value[\"widths\"])) {\n // A width that is not a usable number is one column's loss, never the\n // view's: the rest of the widths still apply and that column is measured.\n if (typeof raw !== \"number\" || !Number.isFinite(raw) || raw <= 0) continue;\n widths[key] = Math.round(raw);\n }\n if (Object.keys(widths).length > 0) out.widths = widths;\n }\n\n const rowHeight = value[\"rowHeight\"];\n if (\n typeof rowHeight === \"number\" &&\n Number.isFinite(rowHeight) &&\n rowHeight >= MIN_ROW_HEIGHT &&\n rowHeight <= MAX_ROW_HEIGHT\n ) {\n out.rowHeight = Math.round(rowHeight);\n }\n\n if (Array.isArray(value[\"hiddenFields\"])) {\n const hiddenFields = value[\"hiddenFields\"].filter((k): k is string => typeof k === \"string\");\n if (hiddenFields.length > 0) out.hiddenFields = hiddenFields;\n }\n if (Array.isArray(value[\"cardHidden\"])) {\n out.cardHidden = value[\"cardHidden\"].filter((k): k is string => typeof k === \"string\");\n }\n\n if (Array.isArray(value[\"columnOrder\"])) {\n const columnOrder = value[\"columnOrder\"].filter((k): k is string => typeof k === \"string\");\n if (columnOrder.length > 0) out.columnOrder = columnOrder;\n }\n\n const gallerySize = value[\"gallerySize\"];\n if (gallerySize === \"small\" || gallerySize === \"medium\" || gallerySize === \"large\") {\n out.gallerySize = gallerySize;\n }\n\n if (typeof value[\"wrap\"] === \"boolean\") out.wrap = value[\"wrap\"];\n if (Array.isArray(value[\"wrapColumns\"])) {\n const wrapColumns = value[\"wrapColumns\"].filter((k): k is string => typeof k === \"string\");\n if (wrapColumns.length > 0) out.wrapColumns = wrapColumns;\n }\n const fit = value[\"fit\"];\n if (fit === \"auto\" || fit === \"fit\" || fit === \"scroll\") out.fit = fit;\n if (typeof value[\"freezeFirst\"] === \"boolean\") out.freezeFirst = value[\"freezeFirst\"];\n const footer = value[\"footer\"];\n if (footer === \"sticky\" || footer === \"inline\") out.footer = footer;\n if (isRecord(value[\"summaries\"])) {\n const summaries: Record<string, string> = {};\n for (const [key, op] of Object.entries(value[\"summaries\"])) {\n // A measure name is judged by the store when it is asked; a non-string is nobody's.\n if (typeof op === \"string\" && op.trim() !== \"\") summaries[key] = op;\n }\n if (Object.keys(summaries).length > 0) out.summaries = summaries;\n }\n\n return out;\n}\n\n/**\n * A grouping, or nothing. A grouping with no Field to group BY is not a partial\n * grouping โ€” it is no grouping at all, so it is dropped whole; an unreadable\n * subtotal or a section order this primitive does not have costs only itself.\n */\nfunction parseGrouping(raw: unknown): GridGrouping | undefined {\n if (!isRecord(raw)) return undefined;\n const field = raw[\"field\"];\n if (typeof field !== \"string\" || field.trim() === \"\") return undefined;\n\n const out: GridGrouping = { field };\n\n if (isRecord(raw[\"aggregates\"])) {\n const aggregates: Record<string, GroupAggregateSpec> = {};\n for (const [key, spec] of Object.entries(raw[\"aggregates\"])) {\n if (!isRecord(spec)) continue;\n const kind = spec[\"kind\"];\n if (typeof kind !== \"string\") continue;\n if (!(AGGREGATE_KINDS as readonly string[]).includes(kind)) continue;\n const label = spec[\"label\"];\n aggregates[key] =\n typeof label === \"string\"\n ? { kind: kind as GroupAggregateKind, label }\n : { kind: kind as GroupAggregateKind };\n }\n if (Object.keys(aggregates).length > 0) out.aggregates = aggregates;\n }\n\n const order = raw[\"order\"];\n if (typeof order === \"string\" && (GROUP_ORDERS as readonly string[]).includes(order)) {\n out.order = order as GroupOrder;\n }\n\n if (Array.isArray(raw[\"collapsed\"])) {\n const collapsed = raw[\"collapsed\"].filter((k): k is string => typeof k === \"string\");\n if (collapsed.length > 0) out.collapsed = collapsed;\n }\n\n if (raw[\"showColumn\"] === true) out.showColumn = true;\n\n if (Array.isArray(raw[\"then\"])) {\n const then: GridSubGroup[] = [];\n for (const level of raw[\"then\"]) {\n if (!isRecord(level)) continue;\n const sub = level[\"field\"];\n if (typeof sub !== \"string\" || sub.trim() === \"\") continue;\n const subOrder = level[\"order\"];\n const subGrain = level[\"grain\"];\n then.push({\n field: sub,\n ...(typeof subOrder === \"string\" && (GROUP_ORDERS as readonly string[]).includes(subOrder)\n ? { order: subOrder as GroupOrder }\n : {}),\n ...(typeof subGrain === \"string\" && subGrain !== \"\" ? { grain: subGrain } : {}),\n });\n }\n if (then.length > 0) out.then = then.slice(0, 2);\n }\n\n const grain = raw[\"grain\"];\n if (typeof grain === \"string\" && grain !== \"\") out.grain = grain;\n const across = raw[\"across\"];\n if (typeof across === \"string\" && across.trim() !== \"\") out.across = across;\n if (Array.isArray(raw[\"show\"])) {\n const show = raw[\"show\"].filter((k): k is string => typeof k === \"string\" && k !== \"\");\n if (show.length > 0) out.show = show;\n }\n if (Array.isArray(raw[\"trail\"])) {\n const trail: GridDrillStep[] = [];\n for (const step of raw[\"trail\"]) {\n if (!isRecord(step) || typeof step[\"dim\"] !== \"string\" || step[\"dim\"] === \"\") continue;\n const value = step[\"value\"];\n if (value !== null && typeof value !== \"string\") continue;\n trail.push({ dim: step[\"dim\"], value });\n }\n if (trail.length > 0) out.trail = trail;\n }\n\n return out;\n}\n\n/** True when nothing here would change how anything looks. */\nexport function presentationIsEmpty(p: GridPresentation): boolean {\n return (\n (p.style === undefined || tableStyleIsEmpty(p.style)) &&\n Object.keys(p.formats ?? {}).length === 0 &&\n (p.frozen?.length ?? 0) === 0 &&\n p.grouping === undefined &&\n Object.keys(p.widths ?? {}).length === 0 &&\n p.rowHeight === undefined &&\n (p.hiddenFields?.length ?? 0) === 0 &&\n p.cardHidden === undefined &&\n (p.columnOrder?.length ?? 0) === 0 &&\n p.gallerySize === undefined &&\n p.wrap === undefined &&\n p.fit === undefined &&\n p.freezeFirst === undefined &&\n p.footer === undefined &&\n Object.keys(p.summaries ?? {}).length === 0\n );\n}\n\n/** The string a view record stores. Empty string for \"this view styles nothing\". */\nexport function presentationDocument(p: GridPresentation): string {\n return presentationIsEmpty(p) ? \"\" : JSON.stringify(p);\n}\n\n/** Kinds whose value is a control the record renderer draws, never a formatter's string. */\nconst DRAWN_KINDS = new Set([\"select\", \"multi_select\", \"relation\", \"member\", \"person\", \"attachment\", \"file\", \"entity_reference\", \"checkbox\", \"boolean\", \"signature\", \"json\"]);\n\n/**\n * The display format a column's settings chose for it, when it is one the platform registry\n * knows and the column is not one the record renderer draws itself. `undefined` otherwise.\n */\nexport function shownAsOf(field: Field): FieldFormatConfig | undefined {\n if (DRAWN_KINDS.has(String(editorKindFor(field)))) return undefined;\n const raw = (field as unknown as { display_format?: unknown }).display_format;\n if (!raw || typeof raw !== \"object\") return undefined;\n const id = (raw as { id?: unknown }).id;\n // `formula` is the column's KIND, never a look: a worked-out column shows as its result format.\n if (typeof id !== \"string\" || id === \"formula\" || !getFieldFormat(id)) return undefined;\n const options = (raw as { options?: unknown }).options;\n return options && typeof options === \"object\" && Object.keys(options).length > 0\n ? ({ id, options } as FieldFormatConfig)\n : ({ id } as FieldFormatConfig);\n}\n\n/**\n * The format a Field's OWN declaration implies, before any view says anything.\n *\n * Returns `undefined` for a Field whose value the record renderer already draws\n * better than a formatter could โ€” a relation chip, a person, an attachment, a\n * choice โ€” because handing those to a text formatter would REPLACE a working\n * control with a string.\n */\nexport function formatForField(field: Field): FieldFormatConfig | undefined {\n // \"SHOWS AS\" โ€” THE FORMAT THE COLUMN'S OWN SETTINGS CHOSE (merge tranche 6a/6e). The column\n // keeps it on the Field (`display_format`, written by `custom.field_update`), so the grid, the\n // record panel and the export read one answer. A column the record renderer draws better than\n // a formatter (a choice, a link, a person, a file) keeps its own drawing.\n const shownAs = shownAsOf(field);\n // A RATING'S TOP STAR IS ITS MAX RULE (lane 10 P4), so changing the rule changes the stars.\n if (newerKindOf(field) === \"rating\" && (!shownAs || shownAs.id === \"rating\")) {\n return { id: \"rating\", options: { ...(shownAs?.options ?? {}), ratingMax: ratingMaxOf(field) } };\n }\n if (shownAs) return shownAs;\n // THE STORE'S OWN STAMPS (Created time, Last changed time) are instants and print like\n // every other date-and-time cell โ€” `dates.ts` answers, the same helper every card reads.\n const stamp = dateFormatFor(field);\n if (stamp && editorKindFor(field) !== \"datetime\") return stamp;\n switch (editorKindFor(field)) {\n case \"currency\":\n return {\n id: \"currency\",\n options: { currency: currencyCodeFor(field.unit) },\n };\n case \"percent\":\n return { id: \"percent\" };\n case \"datetime\":\n // The COLUMN's declaration: a `kind: \"date\"` column is a date column, anything else can\n // hold a moment. Each VALUE is still printed in its own shape (`RecordValue` โ†’\n // `dates.dateFormatFor(field, value)`), so a bare date in a datetime column reads as a date.\n return String(field.config?.[\"kind\"] ?? \"\") === \"date\" ? { id: \"date\" } : { id: \"datetime\" };\n case \"email\":\n return { id: \"email\" };\n case \"url\":\n return { id: \"url\" };\n case \"phone\":\n return { id: \"phone\" };\n case \"number\":\n return { id: \"number\" };\n default:\n return undefined;\n }\n}\n\n/**\n * THE FORMAT ONE CELL OF A COLUMN IS DRAWN IN โ€” the grid's cell and the gallery's card alike\n * (guard G4 \"one format\", lane VIEWS-AND-FIELDS W1-B): the view's own format for the column when\n * it set one, else the Field's own (`formatForField`). One function, so no layout formats money\n * (or a date, or a percent) its own way.\n */\nexport function cellFormatFor(field: Field, viewFormat?: FieldFormatConfig | undefined): FieldFormatConfig | undefined {\n return viewFormat ?? formatForField(field);\n}\n\n/**\n * The store lets a person type the SYMBOL they think in (`$`, `โ‚ฌ`), and\n * `Intl.NumberFormat` wants the ISO code. The symbols people actually enter are\n * mapped; anything else is passed straight through, because a real ISO code is\n * the other thing that lands in this field, and an unknown code still renders\n * (`1,234.00 XYZ`) rather than blanking โ€” THE FALLBACK LAW.\n */\nconst SYMBOL_TO_CODE: Record<string, string> = {\n $: \"USD\",\n \"ยฃ\": \"GBP\",\n \"โ‚ฌ\": \"EUR\",\n \"ยฅ\": \"JPY\",\n \"โ‚น\": \"INR\",\n \"โ‚ฝ\": \"RUB\",\n \"โ‚ฉ\": \"KRW\",\n \"R$\": \"BRL\",\n \"C$\": \"CAD\",\n \"A$\": \"AUD\",\n};\n\nexport function currencyCodeFor(unit: string | null | undefined): string {\n const raw = (unit ?? \"\").trim();\n if (raw === \"\") return \"USD\";\n return SYMBOL_TO_CODE[raw] ?? raw.toUpperCase();\n}\n\n/**\n * The color a value paints with when a view colors BY a column.\n *\n * NOTHING FAILS SILENTLY, so this never returns \"no color\" for a value that\n * exists. The store keeps a list column's choices as records of another Table,\n * and this grid has not read that Table's option colors, so the color is\n * derived from the value's own text: the same word always gets the same color,\n * different words get different ones, and `true` is green the way every\n * checklist reads. When those option colors do land, this is the ONE function\n * that changes and every screen follows.\n */\nexport const colorFromTheValue: ChoiceColorLookup = (_fieldKey, value) => {\n if (value === true) return \"green\";\n if (value === false || value === null || value === undefined) return undefined;\n const text = String(value).trim().toLowerCase();\n if (text === \"\") return undefined;\n let hash = 0;\n for (let i = 0; i < text.length; i += 1) {\n hash = (hash * 31 + text.charCodeAt(i)) >>> 0;\n }\n return COLOR_ROTATION[hash % COLOR_ROTATION.length] as StyleColor;\n};\n\n/**\n * G1's TABLE-LEVEL COLORS (`custom.table_decorations` / `client.tableDecorations`),\n * TRANSLATED INTO A `TableStyle` โ€” the same document a saved view's own `presentation.style`\n * is, so one resolver (`resolveTableStyle`, below) can treat \"the table's colors\" and \"this\n * view's colors\" as two layers of the SAME thing instead of two unrelated systems.\n *\n * THE TRANSLATION IS NECESSARY AND IT IS THE ONLY ONE. G1 addresses a Field by its ID\n * (REC-17 โ€” a column renamed keeps its color), because it is the STORE's own door; a\n * `TableStyle` addresses a Field by its KEY, because it reads straight off `row.document`,\n * which is keyed by Field key. A rule or a color-by whose Field has been deleted (its id no\n * longer resolves to a key) is dropped rather than crashing the whole style โ€” a defect in\n * one rule costs that rule, never the read.\n */\nexport function styleFromDecorations(\n decorations: TableDecorations | null | undefined,\n fields: readonly Field[],\n): TableStyle {\n if (!decorations) return { version: 1 };\n const keyOf = (id: string) => fields.find((f) => f.id === id)?.key;\n const style: TableStyle = { version: 1 };\n\n if (decorations.color_by?.field) {\n const field = keyOf(decorations.color_by.field);\n if (field) style.colorBy = { field, target: decorations.color_by.target };\n }\n\n if (Array.isArray(decorations.rules) && decorations.rules.length > 0) {\n const rules: ColorRule[] = [];\n decorations.rules.forEach((rule, index) => {\n const field = keyOf(rule.field);\n if (!field) return;\n rules.push({\n id: rule.id ?? `decoration:${index}`,\n field,\n op: rule.op,\n color: rule.color,\n target: rule.target,\n ...(rule.value === undefined || rule.value === null ? {} : { value: String(rule.value) }),\n });\n });\n if (rules.length > 0) style.rules = rules;\n }\n\n if (decorations.rows && Object.keys(decorations.rows).length > 0) {\n style.rows = { ...decorations.rows };\n }\n\n const columns: Record<string, StyleColor> = {};\n for (const [id, color] of Object.entries(decorations.columns ?? {})) {\n const key = keyOf(id);\n if (key) columns[key] = color;\n }\n if (Object.keys(columns).length > 0) style.columns = columns;\n\n const cells: Record<string, Record<string, StyleColor>> = {};\n for (const [rowId, byField] of Object.entries(decorations.cells ?? {})) {\n for (const [id, color] of Object.entries(byField ?? {})) {\n const key = keyOf(id);\n if (!key) continue;\n (cells[rowId] ??= {})[key] = color;\n }\n }\n if (Object.keys(cells).length > 0) style.cells = cells;\n\n return style;\n}\n\n/**\n * THE ONE PRESENTATION RESOLVER every colored surface in this package reads โ€”\n * `Grid`, and (via `Card`) the kanban board, the calendar and the gallery.\n *\n * THE CLASS THIS CLOSES (lane UI-QUICK-1, TABLE-PARITY review, 2026-09-24). Threading\n * `view.presentation.style` into `Card` closed the instance the CALENDAR-GALLERY bench\n * found โ€” a VIEW that had its own color-by looked right everywhere โ€” but it left the\n * class open: a table whose colors are the ORGANIZATION's decorations (G1's\n * `custom.table_decorations` โ€” \"color rows by Account Type\", \"MAXED and NEAR rules\" on\n * Arman's own Coding Accounts table) and whose views never set a `presentation.style` of\n * their own drew NOTHING, on the grid exactly as much as on every other layout, because\n * nothing in this package had ever read G1's door at all.\n *\n * TWO LAYERS, ONE DOCUMENT. The table's decorations are the base coat โ€” what every view\n * of this table shows until a view says otherwise. A view's OWN `presentation.style` is\n * an override, piece by piece: a view that only ever set manual row highlights still\n * inherits the table's color-by and rules for everything it never touched, and a view\n * that fully redecorated (its own `colorBy` AND its own `rules`) is not haunted by a\n * table rule it meant to replace. Per-piece, never all-or-nothing, because a view is a\n * LOOK on top of the table, not a wholesale substitute for it.\n */\nexport function resolveTableStyle(\n decorations: TableDecorations | null | undefined,\n fields: readonly Field[],\n viewStyle: TableStyle | undefined,\n): TableStyle {\n const base = styleFromDecorations(decorations, fields);\n if (!viewStyle) return base;\n const colorBy = viewStyle.colorBy ?? base.colorBy;\n const rules = viewStyle.rules ?? base.rules;\n const rows = viewStyle.rows ?? base.rows;\n const columns = viewStyle.columns ?? base.columns;\n const cells = viewStyle.cells ?? base.cells;\n return {\n version: 1,\n ...(colorBy !== undefined ? { colorBy } : {}),\n ...(rules !== undefined ? { rules } : {}),\n ...(rows !== undefined ? { rows } : {}),\n ...(columns !== undefined ? { columns } : {}),\n ...(cells !== undefined ? { cells } : {}),\n };\n}\n","\"use client\";\n\n// src/viewKeys.ts โ€” @ai-matrx/records-ui ยท lane S1-PRIME VIEW-KEYS (UI-CHAMPIONS-PLAN rev 2)\n//\n// EVERY PER-VIEW SETTING IS A DECLARED KEY OF THE ONE SAVED VIEW.\n//\n// A saved view is one row of `platform.saved_view` (lane S0). What it may hold is decided in ONE\n// place: `custom.view_keys()`, the registry `custom.view_declare` judges every save against. This\n// file is that registry's client half and the ONE accessor every layout reads a view through:\n//\n// - `VIEW_KEYS` mirrors `custom.view_keys()` row for row (path, shape, the layouts that read it,\n// who may write it). The live walk compares the two through REST, as test@test.com, so a key\n// added on one side and not the other fails a walk, never a person.\n// - `viewSettings(definition)` is THE reader: a stored definition in, every declared key out,\n// typed. `viewFromRow` (views.ts) goes through it, so the Grid, the board, the calendar,\n// and the gallery all see a view the same way.\n// - `viewSettingsFor(view, layout)` hands a layout only the keys it reads; a layout that does\n// not use a key never sees it (the calendar is never told about swimlanes).\n// - `viewSettingsPatch(patch)` is THE writer's shape: only the keys a press changed, a cleared\n// key as null (the door removes it). `saveViewPatch` sends it through `custom.view_declare`.\n//\n// Fields are named by KEY (a Field's key is immutable; every reader draws rows by key). The door\n// accepts an id and stores the key, so an agent may send either.\n\nimport type { RecordFilter, RuleExpression } from \"@ai-matrx/records\";\nimport { parseGridPresentation, presentationDocument, presentationIsEmpty, type GridPresentation } from \"./presentation\";\n\n/** Every surface a saved view is drawn by. */\nexport type ViewKeyLayout = \"grid\" | \"kanban\" | \"calendar\" | \"gallery\" | \"timeline\" | \"list\";\n\n/**\n * ๐Ÿšฉ THE ONE SWITCH FOR THE TIMELINE'S STORE KEYS (lane VIEWS-AND-FIELDS, sublane TL).\n *\n * `vk_a_calendar_keeps_where_a_record_ends_and_a_timeline_is_a_view.sql` (sublane VK) teaches\n * `custom.view_keys()` the layout `timeline`, the keys `start_field` and `end_field` (calendar +\n * timeline), and `group_field` for the timeline. Until that migration is on production this stays\n * `false`, and:\n * ยท `timeline` is not a layout the store keeps (`STORE_KEPT_LAYOUTS`), so it is drawn for the visit;\n * ยท `start_field` / `end_field` are held for the visit, never sent (`viewSettingsPatch`), and the\n * Start also rides `date_field`, which the store already keeps;\n * ยท the timeline's toolbar says \"Kept for this visit only\".\n * Flipped to `true`, VIEW_KEYS below mirrors the migrated registry row for row (the live walk\n * compares them) and every one of those is kept on the view.\n */\nexport const STORE_KEEPS_TIMELINE = true;\n\n/** Who may write a key: anyone who may save the view, an input the door rewrites, or the server only. */\nexport type ViewKeyWriter = \"caller\" | \"input\" | \"server\";\n\nexport interface ViewKeyDeclaration {\n /** Where it lives in `platform.saved_view.definition` (dotted under `presentation`). */\n path: string;\n /** What the store's guard checks (`custom._view_key_value`). */\n shape: string;\n /** The layouts that read it. */\n layouts: readonly ViewKeyLayout[];\n writer: ViewKeyWriter;\n}\n\nconst ALL: readonly ViewKeyLayout[] = [\"grid\", \"kanban\", \"calendar\", \"gallery\", ...(STORE_KEEPS_TIMELINE ? ([\"timeline\"] as const) : []), \"list\"];\n/** The layouts whose cards draw the fields a view leaves shown (`presentation.hiddenFields`). */\nconst HIDES: readonly ViewKeyLayout[] = [\"grid\", \"gallery\", \"kanban\", \"calendar\", ...(STORE_KEEPS_TIMELINE ? ([\"timeline\"] as const) : []), \"list\"];\nconst GRIDS: readonly ViewKeyLayout[] = [\"grid\"];\n\n/**\n * THE REGISTRY, as `custom.view_keys()` declares it (uichamp_s1_a_view_keeps_every_setting_it_is_given.sql).\n * Order and content are compared with the store by the live walk.\n */\nexport const VIEW_KEYS: readonly ViewKeyDeclaration[] = [\n { path: \"layout\", shape: \"kind\", layouts: ALL, writer: \"caller\" },\n { path: \"filters\", shape: \"filter\", layouts: ALL, writer: \"caller\" },\n { path: \"where\", shape: \"rule\", layouts: ALL, writer: \"caller\" },\n { path: \"group_field\", shape: \"field\", layouts: [\"kanban\", ...(STORE_KEEPS_TIMELINE ? ([\"timeline\"] as const) : [])], writer: \"caller\" },\n { path: \"swimlane_field\", shape: \"field:lane\", layouts: [\"kanban\"], writer: \"caller\" },\n { path: \"collapsed_columns\", shape: \"values\", layouts: [\"kanban\"], writer: \"caller\" },\n { path: \"measure\", shape: \"field:number\", layouts: [\"kanban\"], writer: \"caller\" },\n { path: \"date_field\", shape: \"field:date\", layouts: [\"calendar\"], writer: \"caller\" },\n ...(STORE_KEEPS_TIMELINE\n ? ([\n { path: \"start_field\", shape: \"field:date\", layouts: [\"timeline\"], writer: \"caller\" },\n { path: \"end_field\", shape: \"field:date\", layouts: [\"calendar\", \"timeline\"], writer: \"caller\" },\n ] as const)\n : []),\n { path: \"image_field\", shape: \"field\", layouts: [\"gallery\"], writer: \"caller\" },\n { path: \"sorts\", shape: \"sorts\", layouts: ALL, writer: \"caller\" },\n { path: \"rule_id\", shape: \"uuid\", layouts: ALL, writer: \"caller\" },\n { path: \"is_default\", shape: \"boolean\", layouts: ALL, writer: \"caller\" },\n { path: \"presentation\", shape: \"presentation\", layouts: ALL, writer: \"caller\" },\n { path: \"presentation.style\", shape: \"style\", layouts: ALL, writer: \"caller\" },\n { path: \"presentation.formats\", shape: \"object\", layouts: GRIDS, writer: \"caller\" },\n { path: \"presentation.frozen\", shape: \"fields\", layouts: GRIDS, writer: \"caller\" },\n { path: \"presentation.grouping\", shape: \"grouping\", layouts: [\"grid\", \"gallery\", \"list\"], writer: \"caller\" },\n { path: \"presentation.widths\", shape: \"widths\", layouts: GRIDS, writer: \"caller\" },\n { path: \"presentation.rowHeight\", shape: \"row_height\", layouts: GRIDS, writer: \"caller\" },\n { path: \"presentation.hiddenFields\", shape: \"fields\", layouts: HIDES, writer: \"caller\" },\n { path: \"presentation.cardHidden\", shape: \"fields\", layouts: [\"kanban\", \"calendar\", ...(STORE_KEEPS_TIMELINE ? ([\"timeline\"] as const) : []), \"list\", \"gallery\"], writer: \"caller\" },\n { path: \"presentation.columnOrder\", shape: \"fields\", layouts: GRIDS, writer: \"caller\" },\n { path: \"presentation.gallerySize\", shape: \"gallery_size\", layouts: [\"gallery\"], writer: \"caller\" },\n { path: \"presentation.wrap\", shape: \"boolean\", layouts: GRIDS, writer: \"caller\" },\n { path: \"presentation.wrapColumns\", shape: \"fields\", layouts: GRIDS, writer: \"caller\" },\n { path: \"presentation.fit\", shape: \"fit\", layouts: GRIDS, writer: \"caller\" },\n { path: \"presentation.freezeFirst\", shape: \"boolean\", layouts: GRIDS, writer: \"caller\" },\n { path: \"presentation.footer\", shape: \"footer\", layouts: GRIDS, writer: \"caller\" },\n { path: \"presentation.summaries\", shape: \"summaries\", layouts: GRIDS, writer: \"caller\" },\n { path: \"grid\", shape: \"grid\", layouts: GRIDS, writer: \"caller\" },\n { path: \"hidden_fields\", shape: \"input\", layouts: HIDES, writer: \"input\" },\n { path: \"table_id\", shape: \"server\", layouts: ALL, writer: \"server\" },\n { path: \"order\", shape: \"server\", layouts: GRIDS, writer: \"server\" },\n { path: \"moved_from\", shape: \"server\", layouts: ALL, writer: \"server\" },\n];\n\n/**\n * THE LAYOUTS THE STORE KEEPS ON A VIEW. `custom.view_keys()` judges a view's `layout` against its\n * own list (grid, kanban, calendar, gallery) and refuses any other word by name. A layout this\n * package draws before the store keeps it โ€” the timeline, 2026-10-02 โ€” is never sent as a view's or a\n * look's layout (`viewSettingsPatch`, `viewDocument`): it is drawn for this visit and says so. When the\n * store's registry admits it, add it here and nothing else changes.\n */\nexport const STORE_KEPT_LAYOUTS: readonly string[] = [\"grid\", \"kanban\", \"calendar\", \"gallery\", ...(STORE_KEEPS_TIMELINE ? [\"timeline\"] : []), \"list\"];\n\n/** Whether the store keeps this layout on a saved view. */\nexport function storeKeepsLayout(layout: string): boolean {\n return STORE_KEPT_LAYOUTS.includes(layout);\n}\n\n/** The keys one layout reads, by path. */\nexport function viewKeysFor(layout: ViewKeyLayout): string[] {\n return VIEW_KEYS.filter((k) => k.layouts.includes(layout)).map((k) => k.path);\n}\n\n/** One sort clause: a Field key and a direction. */\nexport interface ViewSortKey {\n field: string;\n direction: \"asc\" | \"desc\";\n}\n\n/**\n * EVERY DECLARED KEY OF A VIEW, TYPED. Absent keys are `null` / empty, never guessed.\n * `presentation` is the parsed look (colour field, pinned fields, row height, grouping with its\n * sub-groups and shut sections, hidden fields, gallery size, summaries).\n */\nexport interface ViewSettings {\n layout: \"grid\" | \"kanban\" | \"calendar\" | \"gallery\" | \"timeline\" | \"list\";\n filters: RecordFilter | null;\n where: RuleExpression | null;\n groupField: string | null;\n swimlaneField: string | null;\n collapsedColumns: string[];\n measure: string | null;\n dateField: string | null;\n /** Timeline: where a record's bar starts (`start_field`). */\n startField: string | null;\n /** Calendar and timeline: where a record ends (`end_field`). */\n endField: string | null;\n imageField: string | null;\n sorts: ViewSortKey[];\n ruleId: string | null;\n isDefault: boolean;\n presentation: GridPresentation;\n movedFrom: Record<string, unknown> | null;\n}\n\n// The layouts a stored view reads back as; one the store does not keep yet reads as a grid. A\n// saved timeline read back as \"grid\" was the reload that lost it (FDT, 2026-10-03).\nconst KINDS: readonly string[] = STORE_KEPT_LAYOUTS;\n\n/** THE READER. A stored `definition` (and the row's flat `filters`) in, every declared key out. */\nexport function viewSettings(definition: Record<string, unknown> | null | undefined, rowFilters?: RecordFilter | null): ViewSettings {\n const data = definition ?? {};\n const text = (key: string): string | null => {\n const v = data[key];\n return typeof v === \"string\" && v !== \"\" ? v : null;\n };\n const layout = String(data[\"layout\"] ?? \"grid\");\n const flat = rowFilters ?? (isObject(data[\"filters\"]) ? (data[\"filters\"] as RecordFilter) : null);\n const moved = data[\"moved_from\"];\n return {\n layout: (KINDS as readonly string[]).includes(layout) ? (layout as ViewSettings[\"layout\"]) : \"grid\",\n filters: flat && Object.keys(flat).length > 0 ? flat : null,\n where: isRule(data[\"where\"]) ? (data[\"where\"] as RuleExpression) : null,\n groupField: text(\"group_field\"),\n swimlaneField: text(\"swimlane_field\"),\n collapsedColumns: Array.isArray(data[\"collapsed_columns\"])\n ? (data[\"collapsed_columns\"] as unknown[]).filter((v) => [\"string\", \"number\", \"boolean\"].includes(typeof v)).map(String)\n : [],\n measure: text(\"measure\"),\n dateField: text(\"date_field\"),\n startField: text(\"start_field\"),\n endField: text(\"end_field\"),\n imageField: text(\"image_field\"),\n sorts: parseSorts(data[\"sorts\"]),\n ruleId: text(\"rule_id\"),\n isDefault: data[\"is_default\"] === true || data[\"is_default\"] === \"true\",\n presentation: parseGridPresentation(data[\"presentation\"]),\n movedFrom: isObject(moved) ? (moved as Record<string, unknown>) : null,\n };\n}\n\n/** The settings as the spec names them โ€” what `viewSettingsPatch` takes. */\nexport type ViewSettingsPatch = Partial<Omit<ViewSettings, \"movedFrom\" | \"layout\">> & {\n /** Any layout the package draws; one the store does not keep is not sent. */\n layout?: string;\n};\n\n/**\n * THE KEYS ONE LAYOUT READS, and nothing else. A layout that does not use a key never sees it:\n * a calendar is not told the board's swimlanes, a board is not told the grid's pinned fields.\n */\nexport function viewSettingsFor(settings: ViewSettings, layout: ViewKeyLayout): Partial<ViewSettings> {\n const reads = new Set(viewKeysFor(layout));\n const out: Partial<ViewSettings> = { layout: settings.layout };\n const take = <K extends keyof ViewSettings>(key: K, path: string) => {\n if (reads.has(path)) out[key] = settings[key];\n };\n take(\"filters\", \"filters\");\n take(\"where\", \"where\");\n take(\"groupField\", \"group_field\");\n take(\"swimlaneField\", \"swimlane_field\");\n take(\"collapsedColumns\", \"collapsed_columns\");\n take(\"measure\", \"measure\");\n take(\"dateField\", \"date_field\");\n take(\"startField\", \"start_field\");\n take(\"endField\", \"end_field\");\n take(\"imageField\", \"image_field\");\n take(\"sorts\", \"sorts\");\n take(\"ruleId\", \"rule_id\");\n take(\"isDefault\", \"is_default\");\n const p = settings.presentation;\n const look: GridPresentation = {};\n for (const [key, value] of Object.entries(p)) {\n if (reads.has(`presentation.${key}`)) (look as Record<string, unknown>)[key] = value;\n }\n out.presentation = look;\n return out;\n}\n\n/**\n * THE WRITER'S SHAPE. Only the keys a press changed; a cleared Field is `null` (the door removes\n * it); lists are sent whole. `presentation` is sent whole because the door judges it as one key.\n */\nexport function viewSettingsPatch(patch: ViewSettingsPatch): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n const field = (key: keyof ViewSettingsPatch, path: string) => {\n if (patch[key] !== undefined) out[path] = (patch[key] as string | null) || null;\n };\n // A layout the store does not keep yet is held for this visit, never sent (`STORE_KEPT_LAYOUTS`).\n if (patch.layout !== undefined && storeKeepsLayout(patch.layout)) out[\"layout\"] = patch.layout;\n field(\"groupField\", \"group_field\");\n field(\"swimlaneField\", \"swimlane_field\");\n field(\"measure\", \"measure\");\n field(\"dateField\", \"date_field\");\n // Held for the visit until the store keeps them (`STORE_KEEPS_TIMELINE`).\n if (STORE_KEEPS_TIMELINE) {\n field(\"startField\", \"start_field\");\n field(\"endField\", \"end_field\");\n }\n field(\"imageField\", \"image_field\");\n field(\"ruleId\", \"rule_id\");\n if (patch.collapsedColumns !== undefined) out[\"collapsed_columns\"] = patch.collapsedColumns ?? [];\n if (patch.sorts !== undefined) out[\"sorts\"] = patch.sorts ?? [];\n if (patch.where !== undefined) out[\"where\"] = patch.where ?? null;\n if (patch.isDefault !== undefined) out[\"is_default\"] = patch.isDefault === true;\n if (patch.presentation !== undefined) {\n out[\"presentation\"] =\n !patch.presentation || presentationIsEmpty(patch.presentation)\n ? null\n : (JSON.parse(presentationDocument(patch.presentation)) as Record<string, unknown>);\n }\n return out;\n}\n\nfunction isObject(raw: unknown): boolean {\n return Boolean(raw) && typeof raw === \"object\" && !Array.isArray(raw);\n}\n\nfunction isRule(raw: unknown): boolean {\n if (!isObject(raw)) return false;\n const node = raw as { op?: unknown; args?: unknown };\n return typeof node.op === \"string\" && Array.isArray(node.args);\n}\n\nfunction parseSorts(raw: unknown): ViewSortKey[] {\n let list: unknown = raw;\n if (typeof raw === \"string\") {\n try {\n list = raw.trim() === \"\" ? [] : JSON.parse(raw);\n } catch {\n return [];\n }\n }\n if (!Array.isArray(list)) return [];\n return list\n .filter((s): s is { field: string; direction?: string } => isObject(s) && typeof (s as { field?: unknown }).field === \"string\")\n .map((s) => ({ field: s.field, direction: s.direction === \"desc\" ? \"desc\" : \"asc\" }));\n}\n","\"use client\";\n\n// src/views.ts โ€” @ai-matrx/records-ui ยท SCR-6, SCR-7, SCR-N-6\n//\n// A SAVED VIEW IS ONE ROW OF THE ONE SAVED-VIEW STORE, `platform.saved_view`\n// (chair ruling 2026-09-23, lane S0 ONE-SAVED-VIEW), read through `custom.views`\n// and written through `custom.view_declare` โ€” the same door the digests, the\n// notify rules, the agent's boards, the grid's choices (G1/G7), the hand-set\n// order (G13) and the mover use. It owns its layout and REFERENCES its\n// membership rule.\n//\n// ๐Ÿšจ THERE WERE TWO STORES (UI-CHAMPIONS-PLAN-ATTACK hole 1). Until S0 this bar\n// kept each view as a record of a package-owned `records_ui_view` Table in every\n// organization, beside `platform.saved_view`: a view the mover carried in never\n// reached the bar, a view saved in the bar was invisible to every subscription,\n// and a grid choice G1 kept had no screen that read it. The repair migration\n// `oneview_every_view_bar_view_moves_into_the_one_store.sql` moved every one of\n// those records into `platform.saved_view` UNDER THE SAME ID and archived the\n// Tables; nothing in this package reads or writes them any more.\n//\n// The split matters. A view does not carry a filter predicate of its own that a\n// screen re-implements in JavaScript: it names a Rule, and the STORE answers\n// who is in it (`custom.rule_members`). So the same view means the same thing\n// to a grid, to an export, to an agent and to the server. What the view owns\n// itself is presentation โ€” which of the four layouts, which Field the kanban\n// columns come from, which Field the calendar reads, which Field the gallery\n// shows โ€” because that is not data, it is how this person wants to look at it.\n//\n// The whole view is also a plain declarative object (`SavedViewSpec`), which is\n// what makes \"an agent writes the view in 45 seconds\" true: one `view_declare`\n// of one definition, and the person only tweaks.\n\nimport { presentationDocument, presentationIsEmpty, type GridPresentation } from \"./presentation\";\nimport { STORE_KEEPS_TIMELINE, storeKeepsLayout, viewSettings, viewSettingsPatch } from \"./viewKeys\";\nexport { STORE_KEEPS_TIMELINE, STORE_KEPT_LAYOUTS, storeKeepsLayout } from \"./viewKeys\";\nimport type { RecordFilter, RecordsResult, RuleExpression, SavedViewRow, Uuid } from \"@ai-matrx/records\";\nimport type { RecordsClient } from \"@ai-matrx/records/core\";\n\n/** The ways one saved view can be looked at (SCR-6; the timeline, lane VIEWS-AND-FIELDS TL). */\nexport const VIEW_LAYOUTS = [\"grid\", \"kanban\", \"calendar\", \"gallery\", \"timeline\", \"list\"] as const;\nexport type ViewLayout = (typeof VIEW_LAYOUTS)[number];\n\nexport const LAYOUT_LABEL: Record<ViewLayout, string> = {\n grid: \"Grid\",\n kanban: \"Kanban\",\n calendar: \"Calendar\",\n gallery: \"Gallery\",\n timeline: \"Timeline\",\n list: \"List\",\n};\n\n\n/**\n * โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n * WHAT EACH LAYOUT NEEDS FROM THE TABLE, AND HOW THE SCREEN SUPPLIES IT\n * (lane DATA-V2-VIEWS-1, Arman 2026-09-28).\n *\n * THE DEFECT. A plain table opened as a Kanban said \"A kanban board needs a Field to make its\n * columns from โ€ฆ Add column, then Select โ€” and it appears here.\" It told the person what to do\n * instead of doing it, and on an AI platform it offered no AI. The ruling: ONE primitive, used by\n * every view (`ViewNeeds`) โ€” the empty state says in one plain sentence what is missing and what\n * pressing will do, offers \"Make it work\" (the column is declared through the store's\n * `custom.field_declare`, its key minted by the store, its name and choices editable before the\n * press) and \"Ask AI\" (the agent does it through the same door, held for the person's approval by\n * the organization's \"Agent changes to this organization's data\" setting).\n *\n * THE CENSUS โ€” every layout this package draws, and what it reads:\n * ยท grid nothing.\n * ยท kanban a CHOICE column (its options become the board's columns). Blocking.\n * ยท calendar a DATE column (it places each record on a day). Blocking.\n * ยท gallery a FILE column for the card's picture. NOT blocking: the cards draw without one,\n * so the offer sits above them instead of in place of them.\n * ยท timeline a DATE column each bar starts on (the calendar's `dateField`). Blocking.\n * `PAGE_VIEWS`' two extra values (dashboards, archived) read\n * nothing a table can lack. This package ships no map layout; the day it does,\n * `Record<ViewLayout, โ€ฆ>` below refuses to compile until the new layout names what it needs.\n */\nexport type ViewNeedKind = \"choice\" | \"date\" | \"file\";\n\nexport interface ViewNeed {\n /** What the layout reads, as a kind a column declares (`offerableFields`). */\n kind: ViewNeedKind;\n /** The view key the made column is written onto, so the layout opens drawn by it. */\n viewKey: \"groupField\" | \"dateField\" | \"imageField\";\n /** True when the layout cannot draw at all without it (kanban, calendar). */\n blocking: boolean;\n /** The column the press makes, before the person edits it: its name, the store's word, its choices. */\n starter: { label: string; type: \"select\" | \"datetime\" | \"attachment\"; options?: readonly string[] };\n /** The table HAS columns this layout can use and none is chosen: the sentence over the picker. */\n pick: string;\n /** One plain sentence: what the table is missing (all a person who may not change it is told). */\n missing: string;\n /** One plain sentence: what pressing Make it work will do, with the column's name. */\n does: (label: string) => string;\n /** The sentence an agent is asked with โ€” the same act, said in words. */\n ask: (label: string, options: readonly string[]) => string;\n}\n\n/** A list of words as a person says it: \"To do, In progress and Done\". */\nexport function wordsList(words: readonly string[]): string {\n const clean = words.map((w) => w.trim()).filter((w) => w !== \"\");\n if (clean.length <= 1) return clean.join(\"\");\n return `${clean.slice(0, -1).join(\", \")} and ${clean[clean.length - 1]}`;\n}\n\nexport const VIEW_NEEDS: Record<ViewLayout, ViewNeed | null> = {\n grid: null,\n // A list reads nothing a table can lack: rows, titled, grouped only when the person chooses.\n list: null,\n kanban: {\n kind: \"choice\",\n viewKey: \"groupField\",\n blocking: true,\n starter: { label: \"Status\", type: \"select\", options: [\"To do\", \"In progress\", \"Done\"] },\n pick: \"Pick the choice column the board's columns come from.\",\n missing: \"This table has no choice column to make the board's columns from.\",\n does: (label) => `Make it work adds a ${label} column with the choices below and groups the board by it.`,\n ask: (label, options) =>\n `Add a choice column called ${label} to this table${options.length > 0 ? ` with the choices ${wordsList(options)}` : \"\"}, then group the Kanban board by it.`,\n },\n calendar: {\n kind: \"date\",\n viewKey: \"dateField\",\n blocking: true,\n starter: { label: \"Date\", type: \"datetime\" },\n pick: \"Pick the date column the calendar places records by.\",\n missing: \"This table has no date column to place records on.\",\n does: (label) => `Make it work adds a ${label} column and lays the calendar out by it.`,\n ask: (label) => `Add a date column called ${label} to this table, then lay the Calendar out by it.`,\n },\n gallery: {\n kind: \"file\",\n viewKey: \"imageField\",\n blocking: false,\n starter: { label: \"Photo\", type: \"attachment\" },\n pick: \"Pick the file column each card shows as its picture.\",\n missing: \"The cards have no picture because this table has no file column.\",\n does: (label) => `Make it work adds a ${label} column and shows it on each card.`,\n ask: (label) => `Add a file column called ${label} to this table, then show it as the picture on each Gallery card.`,\n },\n timeline: {\n kind: \"date\",\n viewKey: \"dateField\",\n blocking: true,\n starter: { label: \"Start\", type: \"datetime\" },\n pick: \"Pick the date column each bar starts on.\",\n missing: \"This table has no date column to place records on.\",\n does: (label) => `Make it work adds a ${label} column and lays the timeline out by it.`,\n ask: (label) => `Add a date column called ${label} to this table, then lay the Timeline out by it.`,\n },\n};\n\n/**\n * A NAME NO COLUMN OF THIS TABLE ALREADY HAS: the starter's own, else \"Status 2\", \"Status 3\"โ€ฆ\n * A table that already holds a text column called Status must not be refused the board.\n */\nexport function freeColumnName(wanted: string, taken: readonly string[]): string {\n const held = new Set(taken.map((t) => t.trim().toLowerCase()));\n const base = wanted.trim() === \"\" ? \"Column\" : wanted.trim();\n if (!held.has(base.toLowerCase())) return base;\n for (let n = 2; n < 100; n += 1) {\n const next = `${base} ${n}`;\n if (!held.has(next.toLowerCase())) return next;\n }\n return base;\n}\n\n/** One sort clause. The Field's key and a direction; nothing else is a sort. */\nexport interface ViewSort {\n field: string;\n direction: \"asc\" | \"desc\";\n}\n\n/** How a saved view is ordered: by hand, by its sort, or it never said (the same as sorted). */\nexport type ViewOrder = \"manual\" | \"sorted\" | null;\n\n/** The order word a stored definition carries. Only the two words the store writes are read. */\nexport function viewOrderOf(definition: Record<string, unknown> | null | undefined): ViewOrder {\n const raw = definition?.[\"order\"];\n return raw === \"manual\" ? \"manual\" : raw === \"sorted\" ? \"sorted\" : null;\n}\n\n/**\n * THE DECLARATIVE SPEC. An agent writes this whole object; a person tweaks it.\n * Every builder component in this package takes one of these as a prop.\n */\nexport interface SavedViewSpec {\n name: string;\n /** The Table this view looks at. */\n subject: Uuid;\n layout: ViewLayout;\n /** Kanban: the Field whose value becomes a column. */\n groupField?: string | null;\n /**\n * Kanban: a number Field to TOTAL under each column heading. It lives on the VIEW for\n * the same reason the colours do โ€” two people read one board two ways and neither\n * reading is a fact about the records.\n */\n measure?: string | null;\n /**\n * Kanban: the Field whose values cut the board into SWIMLANES across the columns โ€” a\n * dispatcher's board by Status, one lane per Technician (S1-PRIME VIEW-KEYS). Never the\n * column Field itself; the store refuses that.\n */\n swimlaneField?: string | null;\n /** Kanban: the columns this view keeps shut to a strip, by the column's stored value. */\n collapsedColumns?: string[];\n /** Calendar: the Field that places a record in the month. */\n dateField?: string | null;\n /** Timeline: the Field a record's bar starts on (`start_field`). */\n startField?: string | null;\n /** Calendar and timeline: the Field a record ends on (`end_field`). */\n endField?: string | null;\n /** Gallery: the Field shown large on the card. */\n imageField?: string | null;\n sorts?: ViewSort[];\n /**\n * HOW THE VIEW IS ORDERED (ORDER-FIX): `\"manual\"` โ€” the positions a person set by hand ARE the\n * view's sort, and it carries no `sorts`; `\"sorted\"` or null โ€” by `sorts`. READ ONLY: the store\n * writes \"manual\" only when the rows are placed (`custom.view_record_order_set`) and turns it\n * off itself when a sort is saved, so no patch ever sends it (`viewSettingsPatch` does not).\n */\n order?: ViewOrder;\n /**\n * The membership Rule. The STORE decides who is in this view; this is a\n * reference to its Rule record, never a predicate this package evaluates.\n */\n ruleId?: Uuid | null;\n /**\n * THE FLAT FILTER a digest or a notify rule wrote the view with โ€” the one shape\n * `custom.record_filter_sql` compiles, by Field KEY. A view saved in the bar has\n * none; a view saved by `DigestScheduler` (\"where Status is Open\") does, and the\n * grid and the board show exactly the records it names.\n */\n filters?: RecordFilter;\n /**\n * S2-PRIME โ€” THE VIEW'S OWN QUESTION, as the condition builder writes it: the store's Rule\n * shape `{op, args}`, ALL / ANY / NOT groups nested to any depth, Fields by id. Kept in the\n * definition as `where` and asked of the store unchanged โ€” the grid's list door, the board's\n * headings and the dashboard's numbers compile it into the one WHERE clause\n * (`custom.record_filter_sql`). Never stored under `filters`: that key is the flat map the\n * digests and the notifier read, and they would refuse it by name.\n */\n where?: RuleExpression | null;\n isDefault?: boolean;\n /**\n * HOW THIS VIEW LOOKS โ€” its colors, its per-column formats and its frozen\n * column (`presentation.ts`).\n *\n * It lives on the VIEW and not on the Fields because two people read one\n * table two ways and neither reading is a fact about the records; it lives on\n * a RECORD, not a new column, because that is what makes it something an\n * agent writes with the same `records` tool as everything else.\n */\n presentation?: GridPresentation;\n}\n\n/**\n * A saved view as it comes back out of the store, with its row's own id.\n *\n * It carries no version: `custom.view_declare` MERGES one key at a time, so two\n * tabs pressing two different controls on one view both stand, and one control\n * pressed in both keeps the latest โ€” how Airtable and Notion keep a view change.\n */\nexport interface SavedView extends SavedViewSpec {\n id: Uuid;\n /** Where the view came from when it was moved in (the mover, the S0 repair), else null. */\n movedFrom: Record<string, unknown> | null;\n}\n\n/** THE TABLE'S DEFAULT VIEW: the first view that says it is. */\nexport function defaultViewOf<V extends Pick<SavedView, \"isDefault\">>(views: readonly V[]): V | undefined {\n return views.find((v) => v.isDefault);\n}\n\n/**\n * WHICH FIELD EACH LAYOUT CAN BE DRAWN FROM, decided from the FIELD'S OWN\n * DECLARATION and never from the values it happens to hold.\n *\n * A kanban makes columns from a choice: the Field whose value is one of a\n * closed set (FLD-5 / FLD-6 โ€” a pick-list is a Table, and a select stores the\n * id of one of its records). Grouping by a free-text Field produces one column\n * per record, which is not a board. A calendar places a record in time, so it\n * needs a Field that holds a date.\n *\n * These two answers are here, beside the layouts, because the picker on the\n * screen and any agent writing a view spec must agree about what is offerable.\n */\nexport const LAYOUT_FIELD_KIND: Record<ViewLayout, ViewNeedKind | null> = {\n grid: null,\n kanban: \"choice\",\n calendar: \"date\",\n gallery: \"file\",\n timeline: \"date\",\n list: null,\n};\n\n/**\n * โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n * WHICH VIEW A LINK NAMES โ€” the address bar as a first-class control.\n *\n * ๐Ÿšจ THE DEFECT THIS CLOSES (lane FRONT-DOOR, 2026-09-21; VERIFIER-8 HIGH-2).\n * `/data/<table>?view=kanban` returned 200 and rendered THE GRID. The\n * switcher was there, the four buttons worked, and the parameter was read by\n * nobody โ€” so a person who bookmarked a board, or sent one to a colleague, got\n * a grid, and nothing on the screen said the view had been dropped. A link that\n * does not land is the inbox-link defect (`activeRecordId`) and the dashboard\n * defect (`activeDashboardId`) for the third time, so this is the same shape of\n * fix: the URL names it, the page opens on it, and the URL FOLLOWS the person\n * when they pick another.\n *\n * The dashboards are a fifth value and not a special case: to the person, \"the\n * dashboard\" is one of the ways to look at this table, and a link to it has to\n * work like a link to the board.\n */\n/**\n * ๐Ÿšจ `archived` IS THE SIXTH VALUE, AND IT IS NOT A SPECIAL CASE EITHER (lane\n * FIX-10A-ARCHIVE, 2026-09-22; VERIFIER-10 F4).\n *\n * Archiving a record used to be a ONE-WAY DOOR: the grid's row action soft-\n * deletes correctly โ€” `deleted_at` set, version bumped, nothing removed โ€” and\n * then no screen anywhere in the platform could bring it back. The store's\n * `custom.record_restore` was there the whole time and did exactly the right\n * thing; nothing reached it, because every read door answers live rows only.\n *\n * So the archive is a WAY OF LOOKING AT THIS TABLE, like the board and the\n * dashboards, reached by the same row of buttons and named by the same\n * `?view=` parameter โ€” never a Trash area of the app somebody has to know\n * exists, and never a per-table screen. Every Table inherits it the moment it\n * is in this list.\n */\nexport const PAGE_VIEWS = [...VIEW_LAYOUTS, \"dashboards\", \"archived\"] as const;\nexport type PageView = (typeof PAGE_VIEWS)[number];\n\nexport const PAGE_VIEW_LABEL: Record<PageView, string> = {\n ...LAYOUT_LABEL,\n dashboards: \"Dashboards\",\n archived: \"Archived\",\n};\n\n/**\n * THE WORDS A LINK MAY USE. The canonical token first; the rest are what a\n * person or an agent actually types, because a link that dies on \"board\" when\n * the button says \"Kanban\" is the same dead link with better spelling.\n */\nconst PAGE_VIEW_SPELLINGS: Record<string, PageView> = {\n grid: \"grid\",\n table: \"grid\",\n rows: \"grid\",\n list: \"list\",\n kanban: \"kanban\",\n board: \"kanban\",\n pipeline: \"kanban\",\n calendar: \"calendar\",\n month: \"calendar\",\n schedule: \"calendar\",\n gallery: \"gallery\",\n cards: \"gallery\",\n timeline: \"timeline\",\n gantt: \"timeline\",\n dashboards: \"dashboards\",\n dashboard: \"dashboards\",\n charts: \"dashboards\",\n archived: \"archived\",\n archive: \"archived\",\n // The grid's row action says \"Delete\" and the store soft-deletes, so the word\n // a person types is as likely to be the one the button used as the one this\n // view is called. Both land in the same place; neither dies.\n deleted: \"archived\",\n trash: \"archived\",\n removed: \"archived\",\n};\n\n/**\n * What a `?view=` value means, or `null` when it means nothing here.\n *\n * `null` is NOT \"show the grid\": the caller is expected to say that the link\n * named something this table has no view for, because silently substituting a\n * different view is exactly the defect above wearing a parser.\n */\nexport function pageViewFromParam(raw: string | null | undefined): PageView | null {\n if (typeof raw !== \"string\") return null;\n const token = raw.trim().toLowerCase();\n if (token === \"\") return null;\n return PAGE_VIEW_SPELLINGS[token] ?? null;\n}\n\n/** The sentence a link that named a view nobody has says. Never silent. */\nexport function unknownViewLine(raw: string): string {\n return (\n `The link asked for a view called โ€œ${raw}โ€, which this table has no view for, so you are looking at ` +\n `${PAGE_VIEW_LABEL.grid}. The views this table has are ` +\n `${PAGE_VIEWS.map((v) => PAGE_VIEW_LABEL[v]).join(\", \")} โ€” pick one above and the address follows it.`\n );\n}\n\n/**\n * THE SPEC AS THE ONE DEFINITION `platform.saved_view` HOLDS โ€” the keys\n * `custom.view_declare` documents, by Field KEY. `subject` is not in it: the door\n * takes the Table as its own argument and writes `table_id` itself.\n */\nexport function viewDocument(spec: SavedViewSpec): Record<string, unknown> {\n const out: Record<string, unknown> = {\n // A layout the store does not keep yet is not sent: the view is saved as a grid (`STORE_KEPT_LAYOUTS`).\n layout: storeKeepsLayout(spec.layout) ? spec.layout : \"grid\",\n sorts: spec.sorts ?? [],\n is_default: spec.isDefault === true,\n };\n if (spec.groupField) out[\"group_field\"] = spec.groupField;\n if (spec.swimlaneField) out[\"swimlane_field\"] = spec.swimlaneField;\n if (spec.collapsedColumns && spec.collapsedColumns.length > 0) out[\"collapsed_columns\"] = spec.collapsedColumns;\n if (spec.measure) out[\"measure\"] = spec.measure;\n if (spec.dateField) out[\"date_field\"] = spec.dateField;\n if (STORE_KEEPS_TIMELINE && spec.startField) out[\"start_field\"] = spec.startField;\n if (STORE_KEEPS_TIMELINE && spec.endField) out[\"end_field\"] = spec.endField;\n if (spec.imageField) out[\"image_field\"] = spec.imageField;\n if (spec.ruleId) out[\"rule_id\"] = spec.ruleId;\n if (spec.where) out[\"where\"] = spec.where;\n if (spec.presentation && !presentationIsEmpty(spec.presentation)) {\n out[\"presentation\"] = JSON.parse(presentationDocument(spec.presentation)) as Record<string, unknown>;\n }\n return out;\n}\n\n/**\n * One row of `custom.views` back into the spec a screen and an agent both read โ€” THROUGH THE ONE\n * ACCESSOR (`viewSettings`, viewKeys.ts), so every layout sees every declared key the same way.\n */\nexport function viewFromRow(row: SavedViewRow): SavedView {\n const data = (row.definition ?? {}) as Record<string, unknown>;\n const k = viewSettings(data, row.filters && Object.keys(row.filters).length > 0 ? row.filters : null);\n const tableFromDefinition = typeof data[\"table_id\"] === \"string\" && data[\"table_id\"] !== \"\" ? (data[\"table_id\"] as string) : null;\n return {\n id: row.view_id,\n name: row.name || \"Untitled view\",\n subject: (row.table_id ?? tableFromDefinition ?? \"\") as Uuid,\n layout: k.layout,\n groupField: k.groupField,\n swimlaneField: k.swimlaneField,\n collapsedColumns: k.collapsedColumns,\n measure: k.measure,\n dateField: k.dateField,\n startField: k.startField,\n endField: k.endField,\n imageField: k.imageField,\n sorts: k.sorts,\n order: viewOrderOf(data),\n ruleId: k.ruleId,\n ...(k.filters ? { filters: k.filters } : {}),\n where: k.where,\n // The older table's own default view, carried in by the mover, IS this table's default โ€”\n // the mover wrote the row's column, which `custom.views` does not return. Read as anything\n // else, the page would seed a second \"All records\" beside it and open on that.\n isDefault: k.isDefault || k.movedFrom?.[\"kind\"] === \"default_view\",\n presentation: k.presentation,\n movedFrom: k.movedFrom,\n };\n}\n\n/**\n * SAVE ONE NEW VIEW of a Table โ€” the bar's \"New view\" and an agent's seed spec.\n * One `view_declare` with no `view_id`; the answer is the new view's id.\n */\nexport async function declareView(\n client: Pick<RecordsClient, \"viewDeclare\">,\n spec: SavedViewSpec,\n): Promise<RecordsResult<Uuid>> {\n return client.viewDeclare({\n table_id: spec.subject,\n spec: {\n name: spec.name,\n ...(spec.filters ? { filters: spec.filters } : {}),\n definition: viewDocument(spec),\n },\n });\n}\n\n/**\n * WRITE A CHANGE ONTO A SAVED VIEW, AND KEEP IT.\n *\n * `custom.view_declare` with the view's id MERGES: only the keys in `patch` are\n * written, a key cleared to null is removed, and everything the patch did not\n * name โ€” the grid's own choices, a hand-set order, where the view was moved from,\n * a colour set in another tab โ€” stays exactly as it was (S0, 2026-09-23: the door\n * used to rebuild the whole definition from what one press sent).\n *\n * There is no version to be stale against (VERIFIER-15 M4 is closed by the shape,\n * not by a retry): a saved view is a person's own arrangement of a table, and\n * their latest press on a control is the one that stands. A refusal โ€” the view\n * was removed, or belongs to a table this person cannot open โ€” is handed back for\n * the page to say through the one notice.\n */\nexport async function saveViewPatch(\n client: Pick<RecordsClient, \"viewDeclare\">,\n tableId: Uuid,\n viewId: Uuid,\n patch: Partial<SavedViewSpec>,\n): Promise<RecordsResult<Uuid>> {\n const definition = viewPatchDocument(patch);\n const renamed = patch.name !== undefined && patch.name.trim() !== \"\";\n const refiltered = patch.filters !== undefined;\n return client.viewDeclare({\n table_id: tableId,\n spec: {\n view_id: viewId,\n ...(renamed ? { name: patch.name!.trim() } : {}),\n ...(refiltered ? { filters: patch.filters ?? null } : {}),\n definition,\n },\n });\n}\n\n/**\n * A PARTIAL change to a saved view, as the definition keys the door merges. Only\n * the keys the caller actually changed are sent; a Field cleared (a board with no\n * \"Group by\") is sent as `null`, which the door removes.\n */\nexport function viewPatchDocument(patch: Partial<SavedViewSpec>): Record<string, unknown> {\n const { name: _name, subject: _subject, filters: _filters, ...keys } = patch;\n return viewSettingsPatch(keys);\n}\n\n/**\n * A LOOK AT A TABLE NEVER CHANGES HOW IT OPENS (lane VIEW-SWITCH-NOT-DESIGNATION, VERIFIER-21).\n *\n * On production the owner pressed Kanban, then Calendar, and the office member's table opened in\n * the Calendar: every layout press was saved onto the DEFAULT view, which is how the table opens\n * for everyone. The table's default view's `layout` and `isDefault` are its DESIGNATION, changed\n * only by an editor's deliberate \"Make this the default\" (`designateView`); the store refuses them\n * through `view_declare` by name. So a press on the default view carries every other key it\n * changed โ€” a board's grouping, a calendar's date, a colour โ€” and never those two.\n */\nexport function viewPatchFor(\n view: Pick<SavedView, \"isDefault\">,\n patch: Partial<SavedViewSpec>,\n): Partial<SavedViewSpec> {\n if (!view.isDefault) return patch;\n const { layout: _layout, isDefault: _isDefault, ...kept } = patch;\n return kept;\n}\n\n/** The one words for the explicit designation, with the layout it would make the default. */\nexport function makeDefaultLabel(layoutLabel: string): string {\n return `Make ${layoutLabel} the default`;\n}\n\n/**\n * MAKE THIS THE DEFAULT โ€” the ONE designation door (`custom.view_designate`, editor on the table).\n * `layout` is the layout this table should open in for everyone: one of the layouts the store keeps. The view\n * becomes the table's only default.\n */\nexport async function designateView(\n client: Pick<RecordsClient, \"viewDesignate\">,\n tableId: Uuid,\n viewId: Uuid,\n layout: string | null,\n): Promise<RecordsResult<Uuid>> {\n return client.viewDesignate({ table_id: tableId, view_id: viewId, layout });\n}\n","\"use client\";\n\n// src/viewLook.ts โ€” @ai-matrx/records-ui ยท lane VIEW-STATE-PERSONAL (VERIFIER-23 item 1, 2026-09-25)\n//\n// A LOOK AT A VIEW IS YOURS UNTIL YOU SAVE IT.\n//\n// On production the owner of a maintenance-requests table grouped its board by Status, and the\n// tenant liaison's board came up grouped by Status the next time she opened it: every pick was\n// written onto the shared view. Airtable and Notion keep the two apart โ€” how you are looking at a\n// shared view (its grouping, its date field, its sort, its filter, hidden columns, widths, the\n// layout) is YOUR look, it persists for you, and the view changes only when you press \"Save to\n// view\" (Notion's \"Save for everyone\"), save it as a new view, or \"Reset to view\".\n//\n// The store keeps one look per (person, view) through `custom.view_look_set` / `view_look_read`\n// (viewer on the table, never written onto the view row). This file is the look's shape on this\n// side: the keys a look may hold, how it reads back, how it lays over the view, and what \"Save to\n// view\" sends.\n\nimport type { RecordsResult, Uuid } from \"@ai-matrx/records\";\nimport type { RecordsClient } from \"@ai-matrx/records/core\";\n\nimport { parseGridPresentation } from \"./presentation\";\nimport { viewSettings, viewSettingsPatch } from \"./viewKeys\";\nimport { VIEW_LAYOUTS, viewPatchFor, type SavedView, type SavedViewSpec, type ViewLayout } from \"./views\";\n\n/**\n * WHAT A LOOK MAY HOLD โ€” how a person looks, never what the view is: not its name, not whether it\n * is the default (`view_designate`), not its Rule, not the flat filters the digests read, not the\n * order the store writes. The store refuses anything else by name.\n */\nexport type ViewLook = Partial<\n Pick<\n SavedViewSpec,\n | \"layout\"\n | \"groupField\"\n | \"swimlaneField\"\n | \"collapsedColumns\"\n | \"measure\"\n | \"dateField\"\n | \"startField\"\n | \"endField\"\n | \"imageField\"\n | \"sorts\"\n | \"where\"\n | \"presentation\"\n >\n>;\n\nconst LOOK_KEYS: ReadonlyArray<keyof ViewLook> = [\n \"layout\",\n \"groupField\",\n \"swimlaneField\",\n \"collapsedColumns\",\n \"measure\",\n \"dateField\",\n \"startField\",\n \"endField\",\n \"imageField\",\n \"sorts\",\n \"where\",\n \"presentation\",\n];\n\n/** Only the look's own keys of a press โ€” a name, a default, a Rule or an order is never a look. */\nexport function lookPart(patch: Partial<SavedViewSpec>): ViewLook {\n const out: ViewLook = {};\n for (const key of LOOK_KEYS) {\n if (patch[key] !== undefined) (out as Record<string, unknown>)[key] = patch[key];\n }\n return out;\n}\n\n/** The look as the definition keys the store keeps (`view_look_set`); a cleared key is null. */\nexport function lookDocument(look: ViewLook): Record<string, unknown> {\n return viewSettingsPatch(look);\n}\n\n/** A kept look back into the spec's words. Only the keys it holds; a null key is \"none, for me\". */\nexport function lookFromDocument(doc: Record<string, unknown> | null | undefined): ViewLook {\n const data = doc ?? {};\n const read = viewSettings(data);\n const out: ViewLook = {};\n const has = (key: string) => Object.prototype.hasOwnProperty.call(data, key);\n if (has(\"layout\") && (VIEW_LAYOUTS as readonly string[]).includes(String(data[\"layout\"]))) {\n out.layout = data[\"layout\"] as ViewLayout;\n }\n if (has(\"group_field\")) out.groupField = read.groupField;\n if (has(\"swimlane_field\")) out.swimlaneField = read.swimlaneField;\n if (has(\"collapsed_columns\")) out.collapsedColumns = read.collapsedColumns;\n if (has(\"measure\")) out.measure = read.measure;\n if (has(\"date_field\")) out.dateField = read.dateField;\n if (has(\"start_field\")) out.startField = read.startField;\n if (has(\"end_field\")) out.endField = read.endField;\n if (has(\"image_field\")) out.imageField = read.imageField;\n if (has(\"sorts\")) out.sorts = read.sorts;\n if (has(\"where\")) out.where = read.where;\n if (has(\"presentation\")) out.presentation = parseGridPresentation(data[\"presentation\"]);\n return out;\n}\n\n/** THE VIEW AS THIS PERSON SEES IT: the saved view, with her look laid over it. */\nexport function applyLook(view: SavedView, look: ViewLook | null | undefined): SavedView {\n if (!look) return view;\n return { ...view, ...look } as SavedView;\n}\n\n/** Whether the look shows anything the view does not โ€” which is when \"Save to view\" means something. */\nexport function lookDiffers(view: SavedView, look: ViewLook | null | undefined): boolean {\n if (!look) return false;\n const shown = applyLook(view, look);\n return LOOK_KEYS.some(\n (key) => look[key] !== undefined && JSON.stringify(shown[key] ?? null) !== JSON.stringify(view[key] ?? null),\n );\n}\n\n/**\n * WHAT \"SAVE TO VIEW\" SENDS, and what stays the person's own afterwards. The default view's layout\n * is the table's designation (`viewPatchFor`): it is never saved here โ€” \"Make โ€ฆ the default\" is โ€”\n * so a look's layout on the default view stays her look.\n */\nexport function saveToViewPlan(view: SavedView, look: ViewLook): { patch: Partial<SavedViewSpec>; left: ViewLook | null } {\n const patch = viewPatchFor(view, look);\n const left: ViewLook = {};\n if (look.layout !== undefined && patch.layout === undefined && look.layout !== view.layout) left.layout = look.layout;\n return { patch, left: Object.keys(left).length > 0 ? left : null };\n}\n\n/** The caller's own looks at every view of a table, by view id. */\nexport async function readLooks(\n client: Pick<RecordsClient, \"viewLookRead\">,\n tableId: Uuid,\n): Promise<RecordsResult<Record<string, ViewLook>>> {\n const answered = await client.viewLookRead({ table_id: tableId });\n if (!answered.ok) return answered;\n const out: Record<string, ViewLook> = {};\n for (const [viewId, row] of Object.entries(answered.data ?? {})) {\n out[viewId] = lookFromDocument(row?.look ?? null);\n }\n return { ok: true, data: out };\n}\n\n/** Keep the whole look (null: \"Reset to view\"). The shared view is never written. */\nexport async function keepLook(\n client: Pick<RecordsClient, \"viewLookSet\">,\n tableId: Uuid,\n viewId: Uuid,\n look: ViewLook | null,\n): Promise<RecordsResult<Record<string, unknown> | null>> {\n const doc = look ? lookDocument(look) : null;\n return client.viewLookSet({ table_id: tableId, view_id: viewId, look: doc && Object.keys(doc).length > 0 ? doc : null });\n}\n\n/** The words of the look bar. One sentence, then the doors. */\nexport const LOOK_WORDS = {\n /** The reason, for a tooltip or a screen reader โ€” never a line on the page (interface-text). */\n yours: \"You changed how this view looks. Only you see it.\",\n /** The visible label for the same fact. */\n yoursLabel: \"Only you see this\",\n saveToView: \"Save to view\",\n saveAsNew: \"Save as new view\",\n reset: \"Reset to view\",\n /** The one button your look's doors fold into when the toolbar row has no room for three. */\n folded: \"Your look\",\n newName: \"Name for the new view\",\n} as const;\n","// src/entityQuery.ts โ€” @ai-matrx/records-ui ยท lane SEED-ENTITY\n//\n// THE QUESTIONS A STANDARD SOURCE'S BLOCKS ASK, in one React-free place: the screens (`useEntityTable`,\n// `EntityChartBlock`) and the server's seed (`askEntityBlockSeed`, `askChartSeed`) build them here, so a\n// seeded answer is found by the very arguments the block sends.\n\nimport type { DrillQuestion } from \"@ai-matrx/records\";\nimport type { Query } from \"@ai-matrx/records/data\";\n\n/** A standard source's first read: its page size and the viewer's search (an empty search is none). */\nexport function entityTableQuery(options: { pageSize?: number | undefined; search?: string | null | undefined } = {}): Query {\n const search = (options.search ?? \"\").trim();\n return { ...(options.pageSize ? { pageSize: options.pageSize } : {}), ...(search ? { search } : {}) };\n}\n\n/** The block's own props that decide a chart's question. */\nexport interface EntityChartQuestionInput {\n /** The Dimension to group by. Absent: one total. */\n by?: string | undefined;\n /** What to count: the rows (default) or a number column summed. */\n measure?: { op: \"count\" } | { op: \"sum\" | \"avg\" | \"min\" | \"max\"; of: string } | undefined;\n where?: Record<string, unknown> | undefined;\n organization?: string | undefined;\n}\n\n/** The chart's `platform.drill_ask` question. */\nexport function entityChartQuestion(input: EntityChartQuestionInput): DrillQuestion & { organization?: string } {\n const m = input.measure ?? { op: \"count\" as const };\n return {\n ...(input.by ? { by: [input.by] } : {}),\n show: [m.op === \"count\" ? \"count\" : { op: m.op, of: m.of }],\n ...(input.where ? { where: input.where } : {}),\n ...(input.organization ? { organization: input.organization } : {}),\n };\n}\n","// src/firstPageEntity.ts โ€” @ai-matrx/records-ui/first-page ยท lane SEED-ENTITY\n//\n// THE FIRST ANSWERS OF A STANDARD SOURCE'S BLOCKS AND OF A CHART TILE, ASKED ON THE SERVER AS THE PERSON.\n//\n// Dr. Patel's Space holds a deals table (\"crm_deal\"), a \"deals by stage\" donut and a \"visits this month\"\n// tile. The server that renders the page already holds her session, so it asks each block's first read\n// with EXACTLY the arguments the block will send and hands the answers to `<RecordsSeedProvider>`\n// (`@ai-matrx/records/react`): the HTML carries the rows and the numbers, and hydrating asks nothing.\n//\n// askEntityBlockSeed an entity source's block: `platform.entity_columns` + `platform.entity_list_scoped`\n// askChartSeed an entity source's chart: `platform.drill_ask`\n// askTileSeed a table's tile: `custom.record_aggregate` (one spec, or several)\n//\n// Each returns the `{ at, answers }` of `askTablePageSeed`; merge several with `mergeSeeds`. A door that\n// refuses is simply not in the seed (the block asks for itself). A wrong guess costs one wasted read and\n// never a wrong value: a seeded answer is used only on an exact door + arguments match. Server-safe.\n\nimport type { RecordsConfig, Uuid } from \"@ai-matrx/records\";\nimport type { RecordsClient, RecordsSeed } from \"@ai-matrx/records/core\";\nimport { asRecordsDataSource, createRecordsClient, recordingDataSource } from \"@ai-matrx/records/core\";\nimport { createSeedPort } from \"@ai-matrx/entity-data/port\";\nimport type { DrillSource } from \"@ai-matrx/records\";\n\nimport { entityChartQuestion, entityTableQuery, type EntityChartQuestionInput } from \"./entityQuery\";\n\ntype Base = { dataSource: unknown; organizationId: Uuid; actor: RecordsConfig[\"actor\"] };\n\nfunction recordedClient(input: Base) {\n const recording = recordingDataSource(asRecordsDataSource(input.dataSource));\n const client = createRecordsClient({ dataSource: recording.source, organizationId: input.organizationId, actor: input.actor, onError: () => {} });\n return { recording, client };\n}\n\n/** Several seeds as one: the later answer wins on the same door and arguments; `at` is the oldest. */\nexport function mergeSeeds(...seeds: Array<RecordsSeed | null | undefined>): RecordsSeed {\n const held = seeds.filter((s): s is RecordsSeed => Boolean(s));\n const answers = new Map<string, RecordsSeed[\"answers\"][number]>();\n for (const seed of held) for (const a of seed.answers) answers.set(`${a.door}|${JSON.stringify(a.args)}`, a);\n return { at: held.length ? Math.min(...held.map((s) => s.at)) : Date.now(), answers: [...answers.values()] };\n}\n\n/**\n * AN ENTITY SOURCE'S BLOCK (a table, board or card of \"task\", \"project\", \"crm_deal\", \"hr_employee\"):\n * its columns and first page, asked through the entity engine's own port with the question the block's\n * `useEntityTable` sends โ€” `question.pageSize` (the block's page size: 100, or 50 embedded) and the\n * viewer's `question.search`, if any. Never throws.\n */\nexport async function askEntityBlockSeed(\n input: Base & { token: string; question?: { pageSize?: number | undefined; search?: string | null | undefined } },\n): Promise<RecordsSeed> {\n const recording = recordingDataSource(asRecordsDataSource(input.dataSource));\n const port = createSeedPort(recording.source);\n try {\n await Promise.all([port.describe(input.token), port.list(input.token, entityTableQuery(input.question ?? {}))]);\n } catch {\n // a seed is a head start, never a requirement\n }\n return recording.seed();\n}\n\n/**\n * AN ENTITY SOURCE'S CHART (`EntityChartBlock`): `platform.drill_ask` with the question the block sends.\n * `question` is the block's own props: `by`, `measure`, `where`, `organization`.\n */\nexport async function askChartSeed(\n input: Base & {\n source: { kind: \"entity\"; token: string };\n question?: EntityChartQuestionInput;\n },\n): Promise<RecordsSeed> {\n const { recording, client } = recordedClient(input);\n try {\n await client.drillAsk({ source: input.source as DrillSource, question: entityChartQuestion(input.question ?? {}) });\n } catch {\n // a seed is a head start, never a requirement\n }\n return recording.seed();\n}\n\n/** One aggregate question of a tile: `client.recordAggregate`'s arguments but the table. */\nexport type TileSpec = Omit<Parameters<RecordsClient[\"recordAggregate\"]>[0], \"table_id\">;\n\n/**\n * A TABLE'S TILE: `custom.record_aggregate` over `tableId` for each `spec` (a tile that also shows a\n * whole-table total gives both). The tile reads the number back with `useSeededAggregate`\n * (`@ai-matrx/records/react`) under the very same spec.\n */\nexport async function askTileSeed(input: Base & { tableId: Uuid; spec: TileSpec | TileSpec[] }): Promise<RecordsSeed> {\n const { recording, client } = recordedClient(input);\n const specs = Array.isArray(input.spec) ? input.spec : [input.spec];\n await Promise.all(specs.map((spec) => client.recordAggregate({ table_id: input.tableId, ...spec }).catch(() => null)));\n return recording.seed();\n}\n","// src/firstPage.ts โ€” @ai-matrx/records-ui/first-page ยท lane PAGE-BUNDLE-2\n//\n// THE GRID'S FIRST PAGE, ASKED BEFORE THE GRID EXISTS.\n//\n// A host that renders on a server (Next.js) can ask the table page's first rows while the browser\n// is still loading the app โ€” but only if it asks with EXACTLY the arguments the grid will send, or\n// the records client cannot serve the grid from it (a part is used only on an exact door + args\n// match). This builds that request from the same pieces the screen uses: the view the bar opens\n// (`ViewBar`: the default view, else the first), the person's own look laid over it (`applyLook`),\n// the opening sort (`Grid`: the view's sort, else the table's `default_sort`, through\n// `pageSortFor`) and the table page's page size (100).\n//\n// IT ONLY PREDICTS THE PLAIN OPENING. A view grouped into sections, drilled, filtered, hand-ordered,\n// built on a Rule, or not a grid answers null โ€” the grid asks for itself, exactly as before. A wrong\n// guess costs one wasted read and never a wrong row: the grid uses a seeded page only on an exact\n// match. No React here (server-safe entry).\n\nimport type { Field, RecordPageSort, StoreFilter, Table, Uuid } from \"@ai-matrx/records\";\nimport type { RecordsClient, RecordsSeed } from \"@ai-matrx/records/core\";\nimport { asRecordsDataSource, createRecordsClient, recordingDataSource } from \"@ai-matrx/records/core\";\nimport type { RecordsConfig } from \"@ai-matrx/records\";\nimport { isUuidShape } from \"@ai-matrx/kit/uuid\";\nimport { defaultSortOf } from \"./gridLook\";\nimport { pageSortFor, storeQuestion } from \"./gridPage\";\nimport { ownVersion } from \"./rowVersion\";\nimport { REVERSE_CELL_CHIPS, choiceFieldKeys, groupFactMeasures, groupedPageSort, isPlainGrouping, summaryBarMeasures } from \"./groupReads\";\nimport { applyLook, readLooks } from \"./viewLook\";\nimport { defaultViewOf, viewFromRow, type SavedView, type SavedViewSpec } from \"./views\";\n\n/** The table page's own page size (`TablePage`: 100 unless embedded). */\nexport const TABLE_PAGE_SIZE = 100;\n\n/**\n * HOW MANY ROWS AN EMBEDDED TABLE READS AT A TIME (CHAIR-EMBEDDED-FIDELITY 16): its first page, and\n * each \"Show more\" press brings about this many more. `embeddedLook.tsx` re-exports it.\n */\nexport const EMBEDDED_PAGE_SIZE = 50;\n\nexport interface FirstGridPageArgs {\n p_table_id: Uuid;\n p_filter: StoreFilter | Record<string, never>;\n p_search: null;\n p_sort: RecordPageSort[];\n p_view_id: null;\n p_limit: number;\n p_offset: 0;\n}\n\n/**\n * THE VIEW'S OWN QUESTION, as the grid asks it (`TablePage` `shownFilter`): its Rule, else its flat filter,\n * through the one question builder. Undefined when it asks none.\n */\nexport function viewFilterOf(shown: Pick<SavedViewSpec, \"where\" | \"filters\"> | null, fields: readonly Field[]): StoreFilter | undefined {\n if (!shown) return undefined;\n const base: StoreFilter | undefined = shown.where ?? (shown.filters && Object.keys(shown.filters).length > 0 ? shown.filters : undefined);\n return storeQuestion(fields, base, {}).filter;\n}\n\n/** The request for the plain opening, from what the screen itself reads โ€” or null. Pure. */\nexport function firstGridPageArgs(input: {\n tableId: Uuid;\n table: Pick<Table, \"default_sort\"> | null;\n fields: readonly Field[];\n /** The view as THIS person sees it (the saved view with her look over it), or null for none. */\n shown: SavedView | null;\n pageSize?: number;\n /**\n * The host draws the MERGED grid (`RecordsUiProvider grid=\"merged\"`), which keeps a plain grouping as rows\n * under group headers: the page is then read in group order. The classic grid opens the drill for a\n * grouped view, so a grouped view stays unseeded there.\n */\n merged?: boolean;\n}): FirstGridPageArgs | null {\n const { shown, fields } = input;\n if (!shown) return null;\n if ((shown.layout ?? \"grid\") !== \"grid\") return null;\n if (shown.ruleId) return null;\n if (shown.order === \"manual\") return null;\n const grouping = shown.presentation?.grouping;\n if (grouping && !(input.merged && isPlainGrouping(grouping))) return null;\n const first = (shown.sorts ?? [])[0];\n const viewSort = first && fields.some((f) => f.key === first.field) ? { id: first.field, direction: first.direction } : null;\n const opening = viewSort ?? defaultSortOf(input.table, fields);\n return {\n p_table_id: input.tableId,\n p_filter: viewFilterOf(shown, fields) ?? {},\n p_search: null,\n p_sort: groupedPageSort(fields, grouping?.field ?? null, pageSortFor(fields, opening)),\n p_view_id: null,\n p_limit: input.pageSize ?? TABLE_PAGE_SIZE,\n p_offset: 0,\n };\n}\n\n/**\n * Read what the screen reads (through `client`, e.g. one served from `custom.table_page_bundle`)\n * and build the first page's request. Null when anything it needs is not answered.\n */\nexport async function firstGridPageRequest(\n client: Pick<RecordsClient, \"tableRead\" | \"fields\" | \"views\" | \"viewLookRead\">,\n tableId: Uuid,\n options: { pageSize?: number; merged?: boolean } = {},\n): Promise<FirstGridPageArgs | null> {\n return (await firstGridOpening(client, tableId, options))?.args ?? null;\n}\n\n/** The unsaved default the grid runs on until a view is saved (`TablePage`'s `defaultView`). */\nfunction defaultSpec(tableId: Uuid): SavedViewSpec {\n return { name: \"All\", subject: tableId, layout: \"grid\", isDefault: true };\n}\n\n/** A spec as the grid shows it before any look is laid over it. */\nfunction shownFromSpec(spec: SavedViewSpec, tableId: Uuid): SavedView {\n return { ...spec, subject: spec.subject ?? tableId, id: \"\" as Uuid, movedFrom: null };\n}\n\n/** The plain opening's request with what it was built from (the view as shown, the table's fields). */\nasync function firstGridOpening(\n client: Pick<RecordsClient, \"tableRead\" | \"fields\" | \"views\" | \"viewLookRead\">,\n tableId: Uuid,\n options: { pageSize?: number; merged?: boolean; view?: SavedViewSpec | undefined },\n): Promise<{ args: FirstGridPageArgs; shown: SavedView | null; fields: readonly Field[] } | null> {\n const explicit = options.view;\n const [table, fields, views, looks] = await Promise.all([\n client.tableRead({ table_id: tableId }),\n client.fields({ table_id: tableId }),\n explicit ? Promise.resolve(null) : client.views({ table_id: tableId }),\n explicit ? Promise.resolve(null) : readLooks(client, tableId),\n ]);\n if (!table.ok || !fields.ok) return null;\n let shown: SavedView;\n if (explicit) {\n // THE HOST'S OWN SPEC (a Space's grid): the view the grid is handed, with no saved view behind it.\n shown = shownFromSpec(explicit, tableId);\n } else {\n if (!views?.ok || !looks?.ok) return null;\n const mine = views.data.map(viewFromRow).filter((v) => v.subject === tableId);\n const view = defaultViewOf(mine) ?? mine[0] ?? null;\n // A table with no saved view opens on the unsaved default (the bar saves it a moment later).\n shown = view ? applyLook(view, looks.data[view.id]) : shownFromSpec(defaultSpec(tableId), tableId);\n }\n const args = firstGridPageArgs({\n tableId,\n table: table.data,\n fields: fields.data,\n shown,\n ...(options.pageSize ? { pageSize: options.pageSize } : {}),\n ...(options.merged ? { merged: true } : {}),\n });\n return args ? { args, shown, fields: fields.data } : null;\n}\n\n/**\n * A TABLE PAGE'S SEED, ASKED BY THE SERVER AS THE PERSON (lane SSR-ROWS).\n *\n * Asks what the table page opens on โ€” the table's bundle (`custom.table_page_bundle`, through the\n * records client exactly as the browser would), the grid's first page when `firstGridPageRequest`\n * can predict it, and on a record page the record's bundle โ€” through a recording data source, and\n * answers every door answer it got as a `RecordsSeed`. The host hands that to\n * `<RecordsSeedProvider>` (`@ai-matrx/records/react`) above the table page: the page is drawn with\n * its rows in the server's HTML and hydrates onto them without asking again.\n *\n * `dataSource` is the person's own (a server supabase client carrying her session): every door\n * decides exactly as it does from the browser. Never throws; a door that refuses is simply not in\n * the seed, and the page asks it for itself.\n */\n/**\n * THE PERSON'S SERVER-ROWS KNOBS, FROM THE TABLE'S BUNDLE (lanes SSR-ROWS-2, SSR-ROWS-3):\n * `custom.table_page_bundle` carries a `server_rows` part โ€” `data.server_rows` and\n * `data.server_rows_cap_ms` (how long the page may wait for the server's rows before the browser asks\n * for itself) resolved for the person asking in that organization. Null when the seed holds no such\n * answer (an older store, a refused part): the host treats that as off.\n */\nexport function serverRowsOf(seed: RecordsSeed | null | undefined): { on: boolean; capMs: number | null } | null {\n const found = seed?.answers.find((a) => a.door === \"server_rows\");\n const data = found?.data as { on?: unknown; cap_ms?: unknown } | undefined;\n if (!data || typeof data !== \"object\") return null;\n return { on: data.on === true, capMs: typeof data.cap_ms === \"number\" && data.cap_ms > 0 ? data.cap_ms : null };\n}\n\nexport async function askTablePageSeed(input: {\n dataSource: unknown;\n organizationId: Uuid;\n tableId: Uuid;\n actor: RecordsConfig[\"actor\"];\n /** A record page: its record's bundle (`custom.record_page_bundle`) is asked beside the table's. */\n recordId?: Uuid | null;\n /**\n * Ask the grid's first page (default true). Off when the address opens something else. A function\n * decides once the table's shape (its bundle) has been answered โ€” it is handed everything asked so\n * far, so a host can read the person's `server_rows` knob from the bundle (`serverRowsOf`) and ask\n * the rows only when it is on, at no extra read.\n */\n rows?: boolean | ((asked: RecordsSeed) => boolean);\n /** The table is drawn EMBEDDED (`TablePage presentation=\"embedded\"`, a Space's inline table): its page size. */\n embedded?: boolean;\n pageSize?: number;\n /**\n * The host draws the merged grid. A view grouped by one field (no sub-levels, pivot or drill trail) is then\n * seeded too: the page in group order, the group counts, the summary bar, and the words of the links on the\n * page โ€” each asked with the arguments the grid sends, so the browser's own call finds its answer.\n */\n merged?: boolean;\n /**\n * THE VIEW THE HOST HANDS THE GRID (a Space's own spec: sorts, filters, `where`, grouping), for a table with\n * no saved view behind it. The first page is built from it exactly as the grid reads it; the table's saved\n * views and looks are not asked.\n */\n view?: SavedViewSpec | undefined;\n}): Promise<RecordsSeed> {\n const recording = recordingDataSource(asRecordsDataSource(input.dataSource));\n const client = createRecordsClient({\n dataSource: recording.source,\n organizationId: input.organizationId,\n actor: input.actor,\n onError: () => {},\n });\n const record = input.recordId\n ? Promise.resolve(\n recording.source.rpc(\"record_page_bundle\", { p_organization_id: input.organizationId, p_record_id: input.recordId }, { schema: \"custom\" }),\n ).catch(() => null)\n : null;\n try {\n const pageSize = input.pageSize ?? (input.embedded ? EMBEDDED_PAGE_SIZE : TABLE_PAGE_SIZE);\n const opening = await firstGridOpening(client, input.tableId, { pageSize, ...(input.merged ? { merged: true } : {}), ...(input.view ? { view: input.view } : {}) });\n const wantRows = typeof input.rows === \"function\" ? input.rows(recording.seed()) : input.rows !== false;\n if (opening && wantRows) {\n const { args, shown, fields } = opening;\n const page = await client.listPage({\n table_id: args.p_table_id,\n filter: args.p_filter,\n search: args.p_search,\n sort: args.p_sort,\n view_id: args.p_view_id,\n limit: args.p_limit,\n offset: args.p_offset,\n });\n if (page.ok) {\n const chosen = shown?.presentation?.summaries ?? {};\n const groupKey = shown?.presentation?.grouping?.field ?? null;\n const reads: Array<Promise<unknown>> = [];\n // The question every one of the grid's table reads carries (`readFilter`): the view's own.\n const filter = args.p_filter && Object.keys(args.p_filter).length > 0 ? (args.p_filter as StoreFilter) : undefined;\n // The grouped grid's header counts (`useGroupFacts`) and the summary bar (`useColumnSummaries`).\n if (groupKey) {\n reads.push(client.recordAggregate({ table_id: input.tableId, groupBy: [groupKey], measures: groupFactMeasures(chosen), limit: 1000, ...(filter ? { filter } : {}) }));\n }\n const bar = summaryBarMeasures(chosen);\n if (bar.length > 0) reads.push(client.recordAggregate({ table_id: input.tableId, measures: bar, ...(filter ? { filter } : {}) }));\n // The words of every link on the page: one call per field, its ids in row order (`useRecordLabels`).\n for (const field of fields) {\n if (field.type !== \"list\" && field.type !== \"relation\") continue;\n const ids = new Set<string>();\n for (const row of page.data.rows) {\n const value = row.document?.[field.key];\n for (const id of Array.isArray(value) ? value.map(String) : typeof value === \"string\" ? [value] : []) if (isUuidShape(id)) ids.add(id);\n }\n if (ids.size > 0) reads.push(client.relationWordsMany({ field_id: field.id as Uuid, record_ids: [...ids] as Uuid[] }));\n }\n if (input.merged) {\n // The header filters' counts per choice column (`gridControls`, one `record_aggregate` each, limit 500).\n for (const key of choiceFieldKeys(fields)) {\n reads.push(client.recordAggregate({ table_id: input.tableId, groupBy: [key], measures: [{ op: \"count\" }], limit: 500, ...(filter ? { filter } : {}) }));\n }\n }\n // The versions of the rows the page drew, for the rows that carry none themselves (`useRecordVersions`).\n const unversioned = page.data.rows.filter((row) => ownVersion(row) === null).map((row) => row.id);\n if (unversioned.length > 0) reads.push(client.recordHeaders({ ids: unversioned }));\n // The table's reverse columns (links made from other tables) and, for each, the page's links\n // (`useReverseGridColumns`: the rows' ids in order, `REVERSE_CELL_CHIPS` a record). Merged or not.\n reads.push(\n client.reverseColumns({ table_id: input.tableId }).then(async (columns) => {\n if (!columns.ok || columns.data.length === 0) return null;\n const record_ids = page.data.rows.map((row) => row.id);\n // ONE call for every reverse column (`useReverseLinks`); a store without that door is asked per column.\n const all = await client.reverseLinksFields({ table_id: input.tableId, source_field_ids: columns.data.map((c) => c.source_field_id), record_ids, limit: REVERSE_CELL_CHIPS });\n if (all.ok || all.error.code !== \"door_absent\") return null;\n return Promise.all(columns.data.map((c) => client.reverseLinksMany({ table_id: input.tableId, source_field_id: c.source_field_id, record_ids, limit: REVERSE_CELL_CHIPS })));\n }),\n );\n await Promise.all(reads.map((read) => read.catch(() => null)));\n }\n }\n } catch {\n // a seed is a head start, never a requirement: whatever was answered is kept\n }\n if (record) await record;\n return recording.seed();\n}\n\n// Entity blocks and chart tiles (lane SEED-ENTITY).\nexport * from \"./firstPageEntity\";\n"]}