@ai-matrx/records 0.99.27 → 0.99.28

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/views/keys.ts","../../src/views/views.ts","../../src/views/look.ts","../../src/views/bind.ts"],"sourcesContent":["// src/views/keys.ts — @ai-matrx/records/views (headless; moved from records-ui viewKeys.ts, step 10 2026-10-08)\n// · 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\";\n\n/** A view's look as the store keeps it: one JSON object under `definition.presentation`. */\nexport type ViewPresentationDocument = Record<string, unknown>;\n\n/**\n * HOW A VIEW'S LOOK IS READ AND WRITTEN — a PORT. The store keeps the look as one JSON object; what it\n * means (colors, formats, frozen columns) belongs to the screens that draw it, so the screens hand their\n * codec in (`@ai-matrx/records-ui` binds its grid presentation). Left out, the look stays the plain\n * document the store keeps — a headless host reads and writes it untouched.\n */\nexport interface ViewPresentationCodec<P> {\n parse(raw: unknown): P;\n isEmpty(presentation: P): boolean;\n /** The look as the JSON object the store keeps. */\n toDocument(presentation: P): Record<string, unknown>;\n}\n\n/** The look as the plain JSON document the store keeps (no screen's meaning applied). */\nexport const plainPresentation: ViewPresentationCodec<ViewPresentationDocument> = {\n parse: (raw) => (raw && typeof raw === \"object\" && !Array.isArray(raw) ? { ...(raw as Record<string, unknown>) } : {}),\n isEmpty: (presentation) => Object.keys(presentation).length === 0,\n toDocument: (presentation) => ({ ...presentation }),\n};\n\nfunction plainCodec<P>(): ViewPresentationCodec<P> {\n return plainPresentation as unknown as ViewPresentationCodec<P>;\n}\n\n/** Every surface a saved view is drawn by. */\nexport type ViewKeyLayout = \"grid\" | \"kanban\" | \"calendar\" | \"gallery\" | \"timeline\" | \"list\" | \"chart\";\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\", \"chart\"];\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\"];\nconst BOARD: readonly ViewKeyLayout[] = [\"kanban\"];\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) : []), \"chart\"], 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\", \"chart\"], writer: \"caller\" },\n { path: \"chart_kind\", shape: \"chart_kind\", layouts: [\"chart\"], writer: \"caller\" },\n { path: \"chart_measure\", shape: \"chart_measure\", layouts: [\"chart\"], 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.boardSize\", shape: \"gallery_size\", layouts: BOARD, writer: \"caller\" },\n { path: \"presentation.boardPreview\", shape: \"field\", layouts: BOARD, writer: \"caller\" },\n { path: \"presentation.boardHideEmpty\", shape: \"boolean\", layouts: BOARD, writer: \"caller\" },\n { path: \"presentation.boardColorColumns\", shape: \"boolean\", layouts: BOARD, writer: \"caller\" },\n { path: \"presentation.boardCounts\", shape: \"boolean\", layouts: BOARD, writer: \"caller\" },\n { path: \"presentation.boardLanesShut\", shape: \"values\", layouts: BOARD, writer: \"caller\" },\n ...(STORE_KEEPS_TIMELINE ? ([{ path: \"presentation.gantt\", shape: \"object\", layouts: [\"timeline\"], writer: \"caller\" }] as const) : []),\n { path: \"presentation.locked\", shape: \"boolean\", layouts: ALL, 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: \"personal\", shape: \"boolean\", layouts: ALL, writer: \"caller\" },\n { path: \"section\", shape: \"section\", layouts: ALL, writer: \"caller\" },\n { path: \"position\", shape: \"position\", layouts: ALL, 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\", \"chart\"];\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<P = ViewPresentationDocument> {\n layout: \"grid\" | \"kanban\" | \"calendar\" | \"gallery\" | \"timeline\" | \"list\" | \"chart\";\n filters: RecordFilter | null;\n where: RuleExpression | null;\n groupField: string | null;\n swimlaneField: string | null;\n collapsedColumns: string[];\n measure: string | null;\n /** Chart: how it draws (`chart_kind`): bar, line or donut. */\n chartKind: string | null;\n /** Chart: what it measures per group (`chart_measure`): count, sum or avg. */\n chartMeasure: 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 /** Personal: only the person who made the view lists it. False = collaborative (everyone with the table). */\n personal: boolean;\n /** The named section of the view list this view sits under, or null. */\n section: string | null;\n /** Where the view sits in the view list among its section's views (lowest first), or null = unplaced. */\n position: number | null;\n presentation: P;\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<P = ViewPresentationDocument>(\n definition: Record<string, unknown> | null | undefined,\n rowFilters?: RecordFilter | null,\n codec: ViewPresentationCodec<P> = plainCodec<P>(),\n): ViewSettings<P> {\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<P>[\"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 chartKind: text(\"chart_kind\"),\n chartMeasure: text(\"chart_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 personal: data[\"personal\"] === true || data[\"personal\"] === \"true\",\n section: text(\"section\"),\n position: typeof data[\"position\"] === \"number\" && Number.isFinite(data[\"position\"]) ? (data[\"position\"] as number) : null,\n presentation: codec.parse(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<P = ViewPresentationDocument> = Partial<Omit<ViewSettings<P>, \"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<P extends object = ViewPresentationDocument>(settings: ViewSettings<P>, layout: ViewKeyLayout): Partial<ViewSettings<P>> {\n const reads = new Set(viewKeysFor(layout));\n const out: Partial<ViewSettings<P>> = { layout: settings.layout };\n const take = <K extends keyof ViewSettings<P>>(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(\"chartKind\", \"chart_kind\");\n take(\"chartMeasure\", \"chart_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 = {} as P;\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<P = ViewPresentationDocument>(\n patch: ViewSettingsPatch<P>,\n codec: ViewPresentationCodec<P> = plainCodec<P>(),\n): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n const field = (key: keyof ViewSettingsPatch<P>, 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(\"chartKind\", \"chart_kind\");\n field(\"chartMeasure\", \"chart_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 // Collaborative is the absence of the key: a view turned back to collaborative clears it.\n if (patch.personal !== undefined) out[\"personal\"] = patch.personal === true ? true : null;\n if (patch.position !== undefined) out[\"position\"] = typeof patch.position === \"number\" ? patch.position : null;\n if (patch.section !== undefined) out[\"section\"] = patch.section && patch.section.trim() !== \"\" ? patch.section.trim() : null;\n if (patch.presentation !== undefined) {\n out[\"presentation\"] =\n !patch.presentation || codec.isEmpty(patch.presentation) ? null : codec.toDocument(patch.presentation);\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","// src/views/views.ts — @ai-matrx/records/views (headless; moved from records-ui views.ts, step 10 2026-10-08)\n// · SCR-6, SCR-7, SCR-N-6. The look (`presentation`) is a codec port (keys.ts); records-ui binds its grid look.\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 {\n STORE_KEEPS_TIMELINE,\n plainPresentation,\n storeKeepsLayout,\n viewSettings,\n viewSettingsPatch,\n type ViewPresentationCodec,\n type ViewPresentationDocument,\n} from \"./keys\";\nimport type { RecordsClient } from \"../core/client\";\nimport type { RecordFilter, RuleExpression, SavedViewRow, Uuid } from \"../core/index\";\nimport type { RecordsResult } from \"../results\";\n\nfunction plainCodec<P>(): ViewPresentationCodec<P> {\n return plainPresentation as unknown as ViewPresentationCodec<P>;\n}\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\", \"chart\"] 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 chart: \"Chart\",\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 // A chart groups by any column or counts the whole table: it can always draw, and says how to group.\n chart: 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<P = ViewPresentationDocument> {\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 /** Chart: how it draws, `bar`, `line` or `donut` (`chart_kind`). */\n chartKind?: string | null;\n /** Chart: what it measures per group, `count`, `sum` or `avg` (`chart_measure`). */\n chartMeasure?: 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 /** PERSONAL (Airtable): only the person who made the view lists it. Absent/false = collaborative. */\n personal?: boolean;\n /** The named section of the view list this view sits under (VIEWS-AIRTABLE). Null/absent = unsectioned. */\n section?: string | null;\n /** Where the view sits in the view list among its section's views, lowest first (VIEWS-AIRTABLE). */\n position?: number | null;\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?: P;\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<P = ViewPresentationDocument> extends SavedViewSpec<P> {\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 chart: 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 chart: \"chart\",\n graph: \"chart\",\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<P = ViewPresentationDocument>(spec: SavedViewSpec<P>, codec: ViewPresentationCodec<P> = plainCodec<P>()): 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.chartKind) out[\"chart_kind\"] = spec.chartKind;\n if (spec.chartMeasure) out[\"chart_measure\"] = spec.chartMeasure;\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.personal === true) out[\"personal\"] = true;\n if (spec.section && spec.section.trim() !== \"\") out[\"section\"] = spec.section.trim();\n if (typeof spec.position === \"number\") out[\"position\"] = spec.position;\n if (spec.presentation && !codec.isEmpty(spec.presentation)) {\n out[\"presentation\"] = codec.toDocument(spec.presentation);\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<P = ViewPresentationDocument>(row: SavedViewRow, codec: ViewPresentationCodec<P> = plainCodec<P>()): SavedView<P> {\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, codec);\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 chartKind: k.chartKind,\n chartMeasure: k.chartMeasure,\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 personal: k.personal,\n section: k.section,\n position: k.position,\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<P = ViewPresentationDocument>(\n client: Pick<RecordsClient, \"viewDeclare\">,\n spec: SavedViewSpec<P>,\n codec: ViewPresentationCodec<P> = plainCodec<P>(),\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, codec),\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<P = ViewPresentationDocument>(\n client: Pick<RecordsClient, \"viewDeclare\">,\n tableId: Uuid,\n viewId: Uuid,\n patch: Partial<SavedViewSpec<P>>,\n codec: ViewPresentationCodec<P> = plainCodec<P>(),\n): Promise<RecordsResult<Uuid>> {\n const definition = viewPatchDocument(patch, codec);\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<P = ViewPresentationDocument>(\n patch: Partial<SavedViewSpec<P>>,\n codec: ViewPresentationCodec<P> = plainCodec<P>(),\n): Record<string, unknown> {\n const { name: _name, subject: _subject, filters: _filters, ...keys } = patch;\n return viewSettingsPatch(keys, codec);\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<S extends { layout?: unknown; isDefault?: boolean }>(\n view: { isDefault?: boolean },\n patch: Partial<S>,\n): Partial<S> {\n if (!view.isDefault) return patch;\n const { layout: _layout, isDefault: _isDefault, ...kept } = patch;\n return kept as Partial<S>;\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\n// ── VIEWS-AIRTABLE (2026-10-10): sections of the view list ──────────────────────────────────────────────\n\n/** A section of the view list with the views under it, in the list's order. `name` null = unsectioned. */\nexport interface ViewSection<V extends Pick<SavedView, \"section\"> = SavedView> {\n name: string | null;\n views: V[];\n}\n\n/**\n * THE VIEW LIST, GROUPED. Unsectioned views come first (the table's default among them), then each\n * section in the order its first view appears. A section exists while a view is in it.\n */\nexport function groupViewsBySection<V extends Pick<SavedView, \"section\"> & { position?: number | null }>(views: readonly V[]): ViewSection<V>[] {\n const loose: V[] = [];\n const named = new Map<string, V[]>();\n for (const v of views) {\n const name = v.section?.trim();\n if (!name) loose.push(v);\n else named.set(name, [...(named.get(name) ?? []), v]);\n }\n // Placed views first, lowest position first; the rest keep the order the store gave (a stable sort).\n const placed = (list: V[]) => [...list].sort((a, b) => (a.position ?? Number.MAX_SAFE_INTEGER) - (b.position ?? Number.MAX_SAFE_INTEGER));\n return [...(loose.length > 0 ? [{ name: null, views: placed(loose) }] : []), ...[...named].map(([name, list]) => ({ name, views: placed(list) }))];\n}\n\n/** The names of the sections the list has, in order. */\nexport function sectionNames(views: readonly Pick<SavedView, \"section\">[]): string[] {\n return groupViewsBySection(views).flatMap((s) => (s.name === null ? [] : [s.name]));\n}\n\n/** Whether a section name is one the store keeps: 1 to 60 characters after trimming. */\nexport function sectionNameOk(raw: string): boolean {\n const n = raw.trim();\n return n.length > 0 && n.length <= 60;\n}\n\n/**\n * RENAME OR DELETE A SECTION: one `view_declare` per view under it. `to` null deletes the section (its\n * views go back to the unsectioned list; no view is archived). Stops at the first refusal and hands it back.\n */\nexport async function reassignSection(\n client: Pick<RecordsClient, \"viewDeclare\">,\n tableId: Uuid,\n views: readonly Pick<SavedView, \"id\" | \"section\">[],\n from: string,\n to: string | null,\n): Promise<RecordsResult<number>> {\n let moved = 0;\n for (const v of views.filter((x) => x.section?.trim() === from)) {\n const written = await saveViewPatch(client, tableId, v.id, { section: to });\n if (!written.ok) return written;\n moved += 1;\n }\n return { ok: true, data: moved };\n}\n\n/** One write of a drag: the view, and the keys that change (`section` null = out of every section). */\nexport interface ViewDropWrite {\n id: Uuid;\n section?: string | null;\n position: number;\n}\n\n/** The droppable id of a section's empty space (the end of its list); \"\" is the unsectioned list. */\nexport const sectionDropId = (name: string | null): string => `section:${name ?? \"\"}`;\n\n/**\n * WHAT DROPPING A VIEW WRITES. `overId` is a view (it takes that view's place) or a section's space\n * (`sectionDropId`; it goes to the end of that section). Every view in the target section is given its\n * place 0..n-1 so the list stays what the person saw; only views whose place or section changed are written.\n * Pure: the bar sends each write through `saveViewPatch`.\n */\nexport function planViewDrop<V extends Pick<SavedView, \"id\" | \"section\"> & { position?: number | null }>(\n views: readonly V[],\n activeId: string,\n overId: string,\n): ViewDropWrite[] {\n const groups = groupViewsBySection(views);\n const from = groups.find((g) => g.views.some((v) => v.id === activeId));\n const moving = from?.views.find((v) => v.id === activeId);\n if (!from || !moving || activeId === overId) return [];\n let target: ViewSection<V> | undefined;\n let index: number;\n if (overId.startsWith(\"section:\")) {\n const raw = overId.slice(\"section:\".length).trim();\n const name = raw === \"\" ? null : raw;\n target = groups.find((g) => g.name === name) ?? { name, views: [] };\n index = target.views.filter((v) => v.id !== activeId).length;\n } else {\n target = groups.find((g) => g.views.some((v) => v.id === overId));\n if (!target) return [];\n const without = target.views.filter((v) => v.id !== activeId);\n const overAt = without.findIndex((v) => v.id === overId);\n const wasAt = target.views.findIndex((v) => v.id === activeId);\n const overWas = target.views.findIndex((v) => v.id === overId);\n // Dragging down inside a list lands after the one it was dropped on; up (or from another list), before it.\n index = wasAt !== -1 && wasAt < overWas ? overAt + 1 : overAt;\n }\n const list = target.views.filter((v) => v.id !== activeId);\n list.splice(index, 0, moving);\n const sectionChanged = (target.name ?? null) !== (moving.section?.trim() || null);\n const writes: ViewDropWrite[] = [];\n list.forEach((v, i) => {\n if (v.id === activeId) writes.push({ id: v.id as Uuid, ...(sectionChanged ? { section: target!.name } : {}), position: i });\n else if (v.position !== i) writes.push({ id: v.id as Uuid, position: i });\n });\n // The dragged view changes only when it moved: same section and same place writes nothing.\n return writes.filter((w) => w.id !== activeId || sectionChanged || moving.position !== w.position);\n}\n","// src/views/look.ts — @ai-matrx/records/views · A PERSON'S OWN LOOK AT A SHARED VIEW, HEADLESS\n//\n// (Moved from records-ui `viewLook.ts`, step 10, 2026-10-08.) How a person looks at a shared view (its grouping,\n// date field, sort, filter, presentation, layout) is HER look: the store keeps one per (person, view) through\n// `custom.view_look_set` / `view_look_read`, never written onto the view row. The view changes only when she\n// saves to it. This file is the look's shape: the keys a look may hold, how it reads back, how it lays over the\n// view, and what \"Save to view\" sends. The presentation half reads through the same codec port as the view.\n\nimport type { RecordsClient } from \"../core/client\";\nimport type { Uuid } from \"../core/index\";\nimport type { RecordsResult } from \"../results\";\nimport { plainPresentation, viewSettings, viewSettingsPatch, type ViewPresentationCodec, type ViewPresentationDocument } from \"./keys\";\nimport { VIEW_LAYOUTS, viewPatchFor, type SavedView, type SavedViewSpec, type ViewLayout } from \"./views\";\n\nconst plain = <P>() => plainPresentation as unknown as ViewPresentationCodec<P>;\n\n/**\n * WHAT A LOOK MAY HOLD — how a person looks, never what the view is: not its name, not whether it is the default,\n * not its Rule, not the flat filters the digests read, not the order the store writes.\n */\nexport type ViewLook<P = ViewPresentationDocument> = Partial<\n Pick<\n SavedViewSpec<P>,\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 = [\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] as const satisfies ReadonlyArray<keyof ViewLook>;\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<P = ViewPresentationDocument>(patch: Partial<SavedViewSpec<P>>): ViewLook<P> {\n const out: ViewLook<P> = {};\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<P = ViewPresentationDocument>(look: ViewLook<P>, codec: ViewPresentationCodec<P> = plain<P>()): Record<string, unknown> {\n return viewSettingsPatch<P>(look, codec);\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<P = ViewPresentationDocument>(\n doc: Record<string, unknown> | null | undefined,\n codec: ViewPresentationCodec<P> = plain<P>(),\n): ViewLook<P> {\n const data = doc ?? {};\n const read = viewSettings<P>(data, undefined, codec);\n const out: ViewLook<P> = {};\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 = codec.parse(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<P = ViewPresentationDocument>(view: SavedView<P>, look: ViewLook<P> | null | undefined): SavedView<P> {\n if (!look) return view;\n return { ...view, ...look } as SavedView<P>;\n}\n\n/** Whether the look shows anything the view does not — which is when \"Save to view\" means something. */\nexport function lookDiffers<P = ViewPresentationDocument>(view: SavedView<P>, look: ViewLook<P> | null | undefined): boolean {\n if (!look) return false;\n const shown = applyLook(view, look);\n return LOOK_KEYS.some((key) => look[key] !== undefined && JSON.stringify(shown[key] ?? null) !== JSON.stringify(view[key] ?? null));\n}\n\n/**\n * WHAT \"SAVE TO VIEW\" SENDS, and what stays the person's own afterwards. The default view's layout is the table's\n * designation (`viewPatchFor`): it is never saved here, so a look's layout on the default view stays her look.\n */\nexport function saveToViewPlan<P = ViewPresentationDocument>(\n view: SavedView<P>,\n look: ViewLook<P>,\n): { patch: Partial<SavedViewSpec<P>>; left: ViewLook<P> | null } {\n const patch = viewPatchFor(view, look) as Partial<SavedViewSpec<P>>;\n const left: ViewLook<P> = {};\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<P = ViewPresentationDocument>(\n client: Pick<RecordsClient, \"viewLookRead\">,\n tableId: Uuid,\n codec: ViewPresentationCodec<P> = plain<P>(),\n): Promise<RecordsResult<Record<string, ViewLook<P>>>> {\n const answered = await client.viewLookRead({ table_id: tableId });\n if (!answered.ok) return answered;\n const out: Record<string, ViewLook<P>> = {};\n for (const [viewId, row] of Object.entries(answered.data ?? {})) {\n out[viewId] = lookFromDocument(row?.look ?? null, codec);\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<P = ViewPresentationDocument>(\n client: Pick<RecordsClient, \"viewLookSet\">,\n tableId: Uuid,\n viewId: Uuid,\n look: ViewLook<P> | null,\n codec: ViewPresentationCodec<P> = plain<P>(),\n): Promise<RecordsResult<Record<string, unknown> | null>> {\n const doc = look ? lookDocument(look, codec) : null;\n return client.viewLookSet({ table_id: tableId, view_id: viewId, look: doc && Object.keys(doc).length > 0 ? doc : null });\n}\n","// src/views/bind.ts — @ai-matrx/records/views · ONE CODEC, BOUND ONCE\n//\n// A host that draws its own look (records-ui's grid presentation) binds its codec here once and re-exports the\n// bound doors under the same names. There is ONE definition of each door — this package's — and the host's names\n// are bindings of it, never a second body (architecture guard: no same-name twin across records and records-ui).\n\nimport type { RecordFilter, SavedViewRow, Uuid } from \"../core/index\";\nimport type { RecordsClient } from \"../core/client\";\nimport { viewSettings, viewSettingsPatch, type ViewPresentationCodec, type ViewSettingsPatch } from \"./keys\";\nimport { declareView, saveViewPatch, viewDocument, viewFromRow, viewPatchDocument, type SavedViewSpec } from \"./views\";\nimport { keepLook, lookDocument, lookFromDocument, readLooks, type ViewLook } from \"./look\";\n\n/** Every door of a saved view and a person's look, with the host's presentation codec bound. */\nexport function bindViewCodec<P>(codec: ViewPresentationCodec<P>) {\n return {\n viewSettings: (definition: Record<string, unknown> | null | undefined, rowFilters?: RecordFilter | null) =>\n viewSettings<P>(definition, rowFilters, codec),\n viewSettingsPatch: (patch: ViewSettingsPatch<P>) => viewSettingsPatch<P>(patch, codec),\n viewDocument: (spec: SavedViewSpec<P>) => viewDocument<P>(spec, codec),\n viewFromRow: (row: SavedViewRow) => viewFromRow<P>(row, codec),\n declareView: (client: Pick<RecordsClient, \"viewDeclare\">, spec: SavedViewSpec<P>) => declareView<P>(client, spec, codec),\n saveViewPatch: (client: Pick<RecordsClient, \"viewDeclare\">, tableId: Uuid, viewId: Uuid, patch: Partial<SavedViewSpec<P>>) =>\n saveViewPatch<P>(client, tableId, viewId, patch, codec),\n viewPatchDocument: (patch: Partial<SavedViewSpec<P>>) => viewPatchDocument<P>(patch, codec),\n lookDocument: (look: ViewLook<P>) => lookDocument<P>(look, codec),\n lookFromDocument: (doc: Record<string, unknown> | null | undefined) => lookFromDocument<P>(doc, codec),\n readLooks: (client: Pick<RecordsClient, \"viewLookRead\">, tableId: Uuid) => readLooks<P>(client, tableId, codec),\n keepLook: (client: Pick<RecordsClient, \"viewLookSet\">, tableId: Uuid, viewId: Uuid, look: ViewLook<P> | null) =>\n keepLook<P>(client, tableId, viewId, look, codec),\n };\n}\n"],"mappings":"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","names":["plainCodec"]}
1
+ {"version":3,"sources":["../../src/views/keys.ts","../../src/views/views.ts","../../src/views/look.ts","../../src/views/bind.ts"],"sourcesContent":["// src/views/keys.ts — @ai-matrx/records/views (headless; moved from records-ui viewKeys.ts, step 10 2026-10-08)\n// · 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\";\n\n/** A view's look as the store keeps it: one JSON object under `definition.presentation`. */\nexport type ViewPresentationDocument = Record<string, unknown>;\n\n/**\n * HOW A VIEW'S LOOK IS READ AND WRITTEN — a PORT. The store keeps the look as one JSON object; what it\n * means (colors, formats, frozen columns) belongs to the screens that draw it, so the screens hand their\n * codec in (`@ai-matrx/records-ui` binds its grid presentation). Left out, the look stays the plain\n * document the store keeps — a headless host reads and writes it untouched.\n */\nexport interface ViewPresentationCodec<P> {\n parse(raw: unknown): P;\n isEmpty(presentation: P): boolean;\n /** The look as the JSON object the store keeps. */\n toDocument(presentation: P): Record<string, unknown>;\n}\n\n/** The look as the plain JSON document the store keeps (no screen's meaning applied). */\nexport const plainPresentation: ViewPresentationCodec<ViewPresentationDocument> = {\n parse: (raw) => (raw && typeof raw === \"object\" && !Array.isArray(raw) ? { ...(raw as Record<string, unknown>) } : {}),\n isEmpty: (presentation) => Object.keys(presentation).length === 0,\n toDocument: (presentation) => ({ ...presentation }),\n};\n\nfunction plainCodec<P>(): ViewPresentationCodec<P> {\n return plainPresentation as unknown as ViewPresentationCodec<P>;\n}\n\n/** Every surface a saved view is drawn by. */\nexport type ViewKeyLayout = \"grid\" | \"kanban\" | \"calendar\" | \"gallery\" | \"timeline\" | \"list\" | \"chart\";\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\", \"chart\"];\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\"];\nconst BOARD: readonly ViewKeyLayout[] = [\"kanban\"];\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) : []), \"chart\"], 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\", \"chart\"], writer: \"caller\" },\n { path: \"chart_kind\", shape: \"chart_kind\", layouts: [\"chart\"], writer: \"caller\" },\n { path: \"chart_measure\", shape: \"chart_measure\", layouts: [\"chart\"], 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.boardSize\", shape: \"gallery_size\", layouts: BOARD, writer: \"caller\" },\n { path: \"presentation.boardPreview\", shape: \"field\", layouts: BOARD, writer: \"caller\" },\n { path: \"presentation.boardHideEmpty\", shape: \"boolean\", layouts: BOARD, writer: \"caller\" },\n { path: \"presentation.boardColorColumns\", shape: \"boolean\", layouts: BOARD, writer: \"caller\" },\n { path: \"presentation.boardCounts\", shape: \"boolean\", layouts: BOARD, writer: \"caller\" },\n { path: \"presentation.boardLanesShut\", shape: \"values\", layouts: BOARD, writer: \"caller\" },\n ...(STORE_KEEPS_TIMELINE ? ([{ path: \"presentation.gantt\", shape: \"object\", layouts: [\"timeline\"], writer: \"caller\" }] as const) : []),\n { path: \"presentation.locked\", shape: \"boolean\", layouts: ALL, writer: \"caller\" },\n { path: \"presentation.wrap\", shape: \"boolean\", layouts: GRIDS, writer: \"caller\" },\n { path: \"presentation.headerWrap\", 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: \"personal\", shape: \"boolean\", layouts: ALL, writer: \"caller\" },\n { path: \"section\", shape: \"section\", layouts: ALL, writer: \"caller\" },\n { path: \"position\", shape: \"position\", layouts: ALL, 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\", \"chart\"];\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<P = ViewPresentationDocument> {\n layout: \"grid\" | \"kanban\" | \"calendar\" | \"gallery\" | \"timeline\" | \"list\" | \"chart\";\n filters: RecordFilter | null;\n where: RuleExpression | null;\n groupField: string | null;\n swimlaneField: string | null;\n collapsedColumns: string[];\n measure: string | null;\n /** Chart: how it draws (`chart_kind`): bar, line or donut. */\n chartKind: string | null;\n /** Chart: what it measures per group (`chart_measure`): count, sum or avg. */\n chartMeasure: 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 /** Personal: only the person who made the view lists it. False = collaborative (everyone with the table). */\n personal: boolean;\n /** The named section of the view list this view sits under, or null. */\n section: string | null;\n /** Where the view sits in the view list among its section's views (lowest first), or null = unplaced. */\n position: number | null;\n presentation: P;\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<P = ViewPresentationDocument>(\n definition: Record<string, unknown> | null | undefined,\n rowFilters?: RecordFilter | null,\n codec: ViewPresentationCodec<P> = plainCodec<P>(),\n): ViewSettings<P> {\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<P>[\"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 chartKind: text(\"chart_kind\"),\n chartMeasure: text(\"chart_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 personal: data[\"personal\"] === true || data[\"personal\"] === \"true\",\n section: text(\"section\"),\n position: typeof data[\"position\"] === \"number\" && Number.isFinite(data[\"position\"]) ? (data[\"position\"] as number) : null,\n presentation: codec.parse(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<P = ViewPresentationDocument> = Partial<Omit<ViewSettings<P>, \"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<P extends object = ViewPresentationDocument>(settings: ViewSettings<P>, layout: ViewKeyLayout): Partial<ViewSettings<P>> {\n const reads = new Set(viewKeysFor(layout));\n const out: Partial<ViewSettings<P>> = { layout: settings.layout };\n const take = <K extends keyof ViewSettings<P>>(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(\"chartKind\", \"chart_kind\");\n take(\"chartMeasure\", \"chart_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 = {} as P;\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<P = ViewPresentationDocument>(\n patch: ViewSettingsPatch<P>,\n codec: ViewPresentationCodec<P> = plainCodec<P>(),\n): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n const field = (key: keyof ViewSettingsPatch<P>, 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(\"chartKind\", \"chart_kind\");\n field(\"chartMeasure\", \"chart_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 // Collaborative is the absence of the key: a view turned back to collaborative clears it.\n if (patch.personal !== undefined) out[\"personal\"] = patch.personal === true ? true : null;\n if (patch.position !== undefined) out[\"position\"] = typeof patch.position === \"number\" ? patch.position : null;\n if (patch.section !== undefined) out[\"section\"] = patch.section && patch.section.trim() !== \"\" ? patch.section.trim() : null;\n if (patch.presentation !== undefined) {\n out[\"presentation\"] =\n !patch.presentation || codec.isEmpty(patch.presentation) ? null : codec.toDocument(patch.presentation);\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","// src/views/views.ts — @ai-matrx/records/views (headless; moved from records-ui views.ts, step 10 2026-10-08)\n// · SCR-6, SCR-7, SCR-N-6. The look (`presentation`) is a codec port (keys.ts); records-ui binds its grid look.\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 {\n STORE_KEEPS_TIMELINE,\n plainPresentation,\n storeKeepsLayout,\n viewSettings,\n viewSettingsPatch,\n type ViewPresentationCodec,\n type ViewPresentationDocument,\n} from \"./keys\";\nimport type { RecordsClient } from \"../core/client\";\nimport type { RecordFilter, RuleExpression, SavedViewRow, Uuid } from \"../core/index\";\nimport type { RecordsResult } from \"../results\";\n\nfunction plainCodec<P>(): ViewPresentationCodec<P> {\n return plainPresentation as unknown as ViewPresentationCodec<P>;\n}\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\", \"chart\"] 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 chart: \"Chart\",\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 // A chart groups by any column or counts the whole table: it can always draw, and says how to group.\n chart: 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<P = ViewPresentationDocument> {\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 /** Chart: how it draws, `bar`, `line` or `donut` (`chart_kind`). */\n chartKind?: string | null;\n /** Chart: what it measures per group, `count`, `sum` or `avg` (`chart_measure`). */\n chartMeasure?: 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 /** PERSONAL (Airtable): only the person who made the view lists it. Absent/false = collaborative. */\n personal?: boolean;\n /** The named section of the view list this view sits under (VIEWS-AIRTABLE). Null/absent = unsectioned. */\n section?: string | null;\n /** Where the view sits in the view list among its section's views, lowest first (VIEWS-AIRTABLE). */\n position?: number | null;\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?: P;\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<P = ViewPresentationDocument> extends SavedViewSpec<P> {\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 chart: 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 chart: \"chart\",\n graph: \"chart\",\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<P = ViewPresentationDocument>(spec: SavedViewSpec<P>, codec: ViewPresentationCodec<P> = plainCodec<P>()): 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.chartKind) out[\"chart_kind\"] = spec.chartKind;\n if (spec.chartMeasure) out[\"chart_measure\"] = spec.chartMeasure;\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.personal === true) out[\"personal\"] = true;\n if (spec.section && spec.section.trim() !== \"\") out[\"section\"] = spec.section.trim();\n if (typeof spec.position === \"number\") out[\"position\"] = spec.position;\n if (spec.presentation && !codec.isEmpty(spec.presentation)) {\n out[\"presentation\"] = codec.toDocument(spec.presentation);\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<P = ViewPresentationDocument>(row: SavedViewRow, codec: ViewPresentationCodec<P> = plainCodec<P>()): SavedView<P> {\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, codec);\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 chartKind: k.chartKind,\n chartMeasure: k.chartMeasure,\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 personal: k.personal,\n section: k.section,\n position: k.position,\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<P = ViewPresentationDocument>(\n client: Pick<RecordsClient, \"viewDeclare\">,\n spec: SavedViewSpec<P>,\n codec: ViewPresentationCodec<P> = plainCodec<P>(),\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, codec),\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<P = ViewPresentationDocument>(\n client: Pick<RecordsClient, \"viewDeclare\">,\n tableId: Uuid,\n viewId: Uuid,\n patch: Partial<SavedViewSpec<P>>,\n codec: ViewPresentationCodec<P> = plainCodec<P>(),\n): Promise<RecordsResult<Uuid>> {\n const definition = viewPatchDocument(patch, codec);\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<P = ViewPresentationDocument>(\n patch: Partial<SavedViewSpec<P>>,\n codec: ViewPresentationCodec<P> = plainCodec<P>(),\n): Record<string, unknown> {\n const { name: _name, subject: _subject, filters: _filters, ...keys } = patch;\n return viewSettingsPatch(keys, codec);\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<S extends { layout?: unknown; isDefault?: boolean }>(\n view: { isDefault?: boolean },\n patch: Partial<S>,\n): Partial<S> {\n if (!view.isDefault) return patch;\n const { layout: _layout, isDefault: _isDefault, ...kept } = patch;\n return kept as Partial<S>;\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\n// ── VIEWS-AIRTABLE (2026-10-10): sections of the view list ──────────────────────────────────────────────\n\n/** A section of the view list with the views under it, in the list's order. `name` null = unsectioned. */\nexport interface ViewSection<V extends Pick<SavedView, \"section\"> = SavedView> {\n name: string | null;\n views: V[];\n}\n\n/**\n * THE VIEW LIST, GROUPED. Unsectioned views come first (the table's default among them), then each\n * section in the order its first view appears. A section exists while a view is in it.\n */\nexport function groupViewsBySection<V extends Pick<SavedView, \"section\"> & { position?: number | null }>(views: readonly V[]): ViewSection<V>[] {\n const loose: V[] = [];\n const named = new Map<string, V[]>();\n for (const v of views) {\n const name = v.section?.trim();\n if (!name) loose.push(v);\n else named.set(name, [...(named.get(name) ?? []), v]);\n }\n // Placed views first, lowest position first; the rest keep the order the store gave (a stable sort).\n const placed = (list: V[]) => [...list].sort((a, b) => (a.position ?? Number.MAX_SAFE_INTEGER) - (b.position ?? Number.MAX_SAFE_INTEGER));\n return [...(loose.length > 0 ? [{ name: null, views: placed(loose) }] : []), ...[...named].map(([name, list]) => ({ name, views: placed(list) }))];\n}\n\n/** The names of the sections the list has, in order. */\nexport function sectionNames(views: readonly Pick<SavedView, \"section\">[]): string[] {\n return groupViewsBySection(views).flatMap((s) => (s.name === null ? [] : [s.name]));\n}\n\n/** Whether a section name is one the store keeps: 1 to 60 characters after trimming. */\nexport function sectionNameOk(raw: string): boolean {\n const n = raw.trim();\n return n.length > 0 && n.length <= 60;\n}\n\n/**\n * RENAME OR DELETE A SECTION: one `view_declare` per view under it. `to` null deletes the section (its\n * views go back to the unsectioned list; no view is archived). Stops at the first refusal and hands it back.\n */\nexport async function reassignSection(\n client: Pick<RecordsClient, \"viewDeclare\">,\n tableId: Uuid,\n views: readonly Pick<SavedView, \"id\" | \"section\">[],\n from: string,\n to: string | null,\n): Promise<RecordsResult<number>> {\n let moved = 0;\n for (const v of views.filter((x) => x.section?.trim() === from)) {\n const written = await saveViewPatch(client, tableId, v.id, { section: to });\n if (!written.ok) return written;\n moved += 1;\n }\n return { ok: true, data: moved };\n}\n\n/** One write of a drag: the view, and the keys that change (`section` null = out of every section). */\nexport interface ViewDropWrite {\n id: Uuid;\n section?: string | null;\n position: number;\n}\n\n/** The droppable id of a section's empty space (the end of its list); \"\" is the unsectioned list. */\nexport const sectionDropId = (name: string | null): string => `section:${name ?? \"\"}`;\n\n/**\n * WHAT DROPPING A VIEW WRITES. `overId` is a view (it takes that view's place) or a section's space\n * (`sectionDropId`; it goes to the end of that section). Every view in the target section is given its\n * place 0..n-1 so the list stays what the person saw; only views whose place or section changed are written.\n * Pure: the bar sends each write through `saveViewPatch`.\n */\nexport function planViewDrop<V extends Pick<SavedView, \"id\" | \"section\"> & { position?: number | null }>(\n views: readonly V[],\n activeId: string,\n overId: string,\n): ViewDropWrite[] {\n const groups = groupViewsBySection(views);\n const from = groups.find((g) => g.views.some((v) => v.id === activeId));\n const moving = from?.views.find((v) => v.id === activeId);\n if (!from || !moving || activeId === overId) return [];\n let target: ViewSection<V> | undefined;\n let index: number;\n if (overId.startsWith(\"section:\")) {\n const raw = overId.slice(\"section:\".length).trim();\n const name = raw === \"\" ? null : raw;\n target = groups.find((g) => g.name === name) ?? { name, views: [] };\n index = target.views.filter((v) => v.id !== activeId).length;\n } else {\n target = groups.find((g) => g.views.some((v) => v.id === overId));\n if (!target) return [];\n const without = target.views.filter((v) => v.id !== activeId);\n const overAt = without.findIndex((v) => v.id === overId);\n const wasAt = target.views.findIndex((v) => v.id === activeId);\n const overWas = target.views.findIndex((v) => v.id === overId);\n // Dragging down inside a list lands after the one it was dropped on; up (or from another list), before it.\n index = wasAt !== -1 && wasAt < overWas ? overAt + 1 : overAt;\n }\n const list = target.views.filter((v) => v.id !== activeId);\n list.splice(index, 0, moving);\n const sectionChanged = (target.name ?? null) !== (moving.section?.trim() || null);\n const writes: ViewDropWrite[] = [];\n list.forEach((v, i) => {\n if (v.id === activeId) writes.push({ id: v.id as Uuid, ...(sectionChanged ? { section: target!.name } : {}), position: i });\n else if (v.position !== i) writes.push({ id: v.id as Uuid, position: i });\n });\n // The dragged view changes only when it moved: same section and same place writes nothing.\n return writes.filter((w) => w.id !== activeId || sectionChanged || moving.position !== w.position);\n}\n","// src/views/look.ts — @ai-matrx/records/views · A PERSON'S OWN LOOK AT A SHARED VIEW, HEADLESS\n//\n// (Moved from records-ui `viewLook.ts`, step 10, 2026-10-08.) How a person looks at a shared view (its grouping,\n// date field, sort, filter, presentation, layout) is HER look: the store keeps one per (person, view) through\n// `custom.view_look_set` / `view_look_read`, never written onto the view row. The view changes only when she\n// saves to it. This file is the look's shape: the keys a look may hold, how it reads back, how it lays over the\n// view, and what \"Save to view\" sends. The presentation half reads through the same codec port as the view.\n\nimport type { RecordsClient } from \"../core/client\";\nimport type { Uuid } from \"../core/index\";\nimport type { RecordsResult } from \"../results\";\nimport { plainPresentation, viewSettings, viewSettingsPatch, type ViewPresentationCodec, type ViewPresentationDocument } from \"./keys\";\nimport { VIEW_LAYOUTS, viewPatchFor, type SavedView, type SavedViewSpec, type ViewLayout } from \"./views\";\n\nconst plain = <P>() => plainPresentation as unknown as ViewPresentationCodec<P>;\n\n/**\n * WHAT A LOOK MAY HOLD — how a person looks, never what the view is: not its name, not whether it is the default,\n * not its Rule, not the flat filters the digests read, not the order the store writes.\n */\nexport type ViewLook<P = ViewPresentationDocument> = Partial<\n Pick<\n SavedViewSpec<P>,\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 = [\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] as const satisfies ReadonlyArray<keyof ViewLook>;\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<P = ViewPresentationDocument>(patch: Partial<SavedViewSpec<P>>): ViewLook<P> {\n const out: ViewLook<P> = {};\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<P = ViewPresentationDocument>(look: ViewLook<P>, codec: ViewPresentationCodec<P> = plain<P>()): Record<string, unknown> {\n return viewSettingsPatch<P>(look, codec);\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<P = ViewPresentationDocument>(\n doc: Record<string, unknown> | null | undefined,\n codec: ViewPresentationCodec<P> = plain<P>(),\n): ViewLook<P> {\n const data = doc ?? {};\n const read = viewSettings<P>(data, undefined, codec);\n const out: ViewLook<P> = {};\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 = codec.parse(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<P = ViewPresentationDocument>(view: SavedView<P>, look: ViewLook<P> | null | undefined): SavedView<P> {\n if (!look) return view;\n return { ...view, ...look } as SavedView<P>;\n}\n\n/** Whether the look shows anything the view does not — which is when \"Save to view\" means something. */\nexport function lookDiffers<P = ViewPresentationDocument>(view: SavedView<P>, look: ViewLook<P> | null | undefined): boolean {\n if (!look) return false;\n const shown = applyLook(view, look);\n return LOOK_KEYS.some((key) => look[key] !== undefined && JSON.stringify(shown[key] ?? null) !== JSON.stringify(view[key] ?? null));\n}\n\n/**\n * WHAT \"SAVE TO VIEW\" SENDS, and what stays the person's own afterwards. The default view's layout is the table's\n * designation (`viewPatchFor`): it is never saved here, so a look's layout on the default view stays her look.\n */\nexport function saveToViewPlan<P = ViewPresentationDocument>(\n view: SavedView<P>,\n look: ViewLook<P>,\n): { patch: Partial<SavedViewSpec<P>>; left: ViewLook<P> | null } {\n const patch = viewPatchFor(view, look) as Partial<SavedViewSpec<P>>;\n const left: ViewLook<P> = {};\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<P = ViewPresentationDocument>(\n client: Pick<RecordsClient, \"viewLookRead\">,\n tableId: Uuid,\n codec: ViewPresentationCodec<P> = plain<P>(),\n): Promise<RecordsResult<Record<string, ViewLook<P>>>> {\n const answered = await client.viewLookRead({ table_id: tableId });\n if (!answered.ok) return answered;\n const out: Record<string, ViewLook<P>> = {};\n for (const [viewId, row] of Object.entries(answered.data ?? {})) {\n out[viewId] = lookFromDocument(row?.look ?? null, codec);\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<P = ViewPresentationDocument>(\n client: Pick<RecordsClient, \"viewLookSet\">,\n tableId: Uuid,\n viewId: Uuid,\n look: ViewLook<P> | null,\n codec: ViewPresentationCodec<P> = plain<P>(),\n): Promise<RecordsResult<Record<string, unknown> | null>> {\n const doc = look ? lookDocument(look, codec) : null;\n return client.viewLookSet({ table_id: tableId, view_id: viewId, look: doc && Object.keys(doc).length > 0 ? doc : null });\n}\n","// src/views/bind.ts — @ai-matrx/records/views · ONE CODEC, BOUND ONCE\n//\n// A host that draws its own look (records-ui's grid presentation) binds its codec here once and re-exports the\n// bound doors under the same names. There is ONE definition of each door — this package's — and the host's names\n// are bindings of it, never a second body (architecture guard: no same-name twin across records and records-ui).\n\nimport type { RecordFilter, SavedViewRow, Uuid } from \"../core/index\";\nimport type { RecordsClient } from \"../core/client\";\nimport { viewSettings, viewSettingsPatch, type ViewPresentationCodec, type ViewSettingsPatch } from \"./keys\";\nimport { declareView, saveViewPatch, viewDocument, viewFromRow, viewPatchDocument, type SavedViewSpec } from \"./views\";\nimport { keepLook, lookDocument, lookFromDocument, readLooks, type ViewLook } from \"./look\";\n\n/** Every door of a saved view and a person's look, with the host's presentation codec bound. */\nexport function bindViewCodec<P>(codec: ViewPresentationCodec<P>) {\n return {\n viewSettings: (definition: Record<string, unknown> | null | undefined, rowFilters?: RecordFilter | null) =>\n viewSettings<P>(definition, rowFilters, codec),\n viewSettingsPatch: (patch: ViewSettingsPatch<P>) => viewSettingsPatch<P>(patch, codec),\n viewDocument: (spec: SavedViewSpec<P>) => viewDocument<P>(spec, codec),\n viewFromRow: (row: SavedViewRow) => viewFromRow<P>(row, codec),\n declareView: (client: Pick<RecordsClient, \"viewDeclare\">, spec: SavedViewSpec<P>) => declareView<P>(client, spec, codec),\n saveViewPatch: (client: Pick<RecordsClient, \"viewDeclare\">, tableId: Uuid, viewId: Uuid, patch: Partial<SavedViewSpec<P>>) =>\n saveViewPatch<P>(client, tableId, viewId, patch, codec),\n viewPatchDocument: (patch: Partial<SavedViewSpec<P>>) => viewPatchDocument<P>(patch, codec),\n lookDocument: (look: ViewLook<P>) => lookDocument<P>(look, codec),\n lookFromDocument: (doc: Record<string, unknown> | null | undefined) => lookFromDocument<P>(doc, codec),\n readLooks: (client: Pick<RecordsClient, \"viewLookRead\">, tableId: Uuid) => readLooks<P>(client, tableId, codec),\n keepLook: (client: Pick<RecordsClient, \"viewLookSet\">, tableId: Uuid, viewId: Uuid, look: ViewLook<P> | null) =>\n keepLook<P>(client, tableId, viewId, look, codec),\n };\n}\n"],"mappings":"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","names":["plainCodec"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-matrx/records",
3
- "version": "0.99.27",
3
+ "version": "0.99.28",
4
4
  "description": "The headless client of the AI Matrx record store: the Table / Field / Rule / Record / Relation / Home types, the closed vocabularies and refusal codes read from the store itself, the typed calls over the store's doors and nothing else, the merge-field declaration, a validation mirror that predicts only what the store's own triggers refuse, and the never-throw result envelope. Zero React, zero DOM in the root and /core.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -340,5 +340,5 @@
340
340
  "describe-bench:run": "../../../.venv/bin/python scripts/describe-bench/score-env.py run",
341
341
  "describe-bench:arms": "../../../.venv/bin/python scripts/describe-bench/score-env.py arms"
342
342
  },
343
- "gitHead": "5b24b338c9c9b2d61c672a6c1442cfd0d0c2d47d"
343
+ "gitHead": "409b5bc198f56715a28f016391c58f0827daf36e"
344
344
  }