@ai-matrx/records-ui 0.84.7 → 0.85.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,6 +1,363 @@
1
1
  # Changelog — @ai-matrx/records-ui
2
2
 
3
- ## 0.84.7 (unpublished — needs @ai-matrx/records 0.57.3)
3
+ ## Unreleased — lane DATA-V2-FACE (2026-09-24)
4
+
5
+ * **A table opens the way it was designated, and its face is one row.** `TablePage` draws, above the
6
+ records, only the saved-views bar (when the table has two or more views, or the person asked for it),
7
+ and on the right ONE cluster: the layout chooser (only when nothing is designated; in the menu on a
8
+ phone), the view's filter (absent over a host layout, which reads its own records), New record on a
9
+ board, whatever the menu opened (pressed; pressing it closes it), Share, and the table menu. The menu
10
+ holds everything that was fourteen buttons in a row: Add field, Dashboards, Archived, Forms, Bookings,
11
+ Checklists, Notifications, Portals, Inbox, Import, Export (now a rail), Settings, the other layouts
12
+ when one is designated, and the saved views. Owner, 2026-09-24: "not confusing with 1,000 options…
13
+ just shows them the way it should in the default view."
14
+ * **Designation** (`designatedLayoutOf`, `tableOpening`, `SavedView.designatedLayout`): the default view
15
+ names a layout the host draws (`layout: "sheet"`), or is the older table's own default view the mover
16
+ carried in (`moved_from.kind = "default_view"`), which reads as the Sheet and as the table's default
17
+ (no second "All records" is seeded beside it). A package layout (`grid`, `kanban`…) never designates,
18
+ because a click writes those; on a designated table a layout picked from the menu is for the visit and
19
+ is never written over the designation.
20
+ * `ViewSwitcher` takes `chooser={false}` (the page draws the choice) and `openExport`; a host layout's
21
+ `render` receives `openExport`, so the Sheet's "Export this table…" opens the page's own export.
22
+ `ViewBar` takes `quietWhenAlone` and `onViews`.
23
+ * Tests: `a-table-opens-the-way-it-was-designated.test.tsx` (RED 3/3 against the pre-lane page, GREEN
24
+ 8/8); `every-table-can-reach-its-archive.test.tsx` now reaches Archived through the menu.
25
+
26
+ ## Unreleased — lane SC-1-TAILS (2026-09-24)
27
+
28
+ * **`WhereItLives`** — the one chip for "Lives in <organization>" and moving a table, moved here from matrx-frontend
29
+ so every host (and `TablesHome`) mounts the same builder. The consequence is said in the panel before "Move it";
30
+ a refusal shows the store's sentence and says nothing moved. Needs the unreleased `@ai-matrx/records`
31
+ `tableHome` / `tableMove`.
32
+ * **`TablesHome`**: what the app keeps for itself sits behind one **Show everything** control that says how many
33
+ there are, each kept table carries the store's `keeper_says`, and every row carries a quiet organization chip
34
+ (new optional `organizationName` prop; absent, the list asks the store once).
35
+ * **Consumer action:** matrx-frontend passes `organizationName` to `TablesHome` in `OrganizationHub.tsx` and swaps
36
+ its three `WhereItLives` call sites to this export.
37
+
38
+ ## 0.85.4 (2026-09-24 — published by PUBLISH-SWEEP-5; folds every section below down to 0.84.8)
39
+
40
+ **0.85.3 never reached npm** (the release run's automatic tag, run 36064683447): every test passed
41
+ and vitest exited red on unhandled rejections again, this time from `TableSettings.hooks.test.tsx`,
42
+ whose double never answered `row_actions` (GRID-TAILS' section) or `table_decorations` (the one
43
+ colour resolver). It now answers both with what the live store says for a table without actions or
44
+ colours. The gate was proven with zero unhandled errors on an origin/main export before this tag.
45
+
46
+ **Needs `@ai-matrx/design-system` 0.39.0** (S4's `toggleSelection`). 0.85.2's tag (run 35969941639)
47
+ passed every test and failed the packed-consumer canary because the board imports
48
+ `toggleSelection`, which design-system 0.38.1 does not export; 0.39.0 was published first this time.
49
+
50
+ **0.85.1 was tagged and never reached npm either** (run 35967872646): every test passed, and vitest
51
+ still exited red on 15 unhandled rejections. The grid and the board began asking
52
+ `custom.table_decorations`, `custom.grid_layout` and `custom.row_actions`, which the deterministic
53
+ gate's recordings never held, and S4's `many-at-once` double never modelled `table_decorations`. The
54
+ recordings now carry the live store's answers for those three doors (read as test@test.com), and the
55
+ double answers an undecorated table (`7b4d49b3ad`). Nothing in the shipped code changed.
56
+
57
+ **0.85.0 was tagged and never reached npm** (run 35966710984): the publish gate lost a race in
58
+ `a-table-can-start-from-a-real-example.test.tsx`, which counted the example cards after 20
59
+ zero-delay ticks while they still loaded; it now waits for the cards (`5da1344ffc`). 0.85.1 also
60
+ carries DEFAULT-GRID-PARITY's export fix (`3443eb823e`, an export writes a relation's words).
61
+
62
+ **Needs `@ai-matrx/records` 0.58.0** (published first). One publish carries everything since 0.84.8:
63
+ source versions 0.84.9 and 0.84.10 were never published on their own and are folded below, and
64
+ "0.84.11" in UI-QUICK-1's correction line is this release. Also in it without a section of their
65
+ own: S0 ONE-SAVED-VIEW (the view bar reads and writes `platform.saved_view`, `5367edd12e`) and
66
+ S2-PRIME FILTER-GROUPS (nested conditions in the one condition builder and a view's own
67
+ `definition.where`, `0efe199849` / `1ef813282e` / `54a23e5608`; S1-PRIME's `c58088eecc` stops a
68
+ table's saved views being re-read many times a second). Some of these bytes reached main through the
69
+ integrator's sweeps (`79b505cfa5`, `9a6618f2d0`, `452e363c2e`) rather than their lanes' own commits.
70
+
71
+ ### Lane GRID-TAILS (2026-09-24)
72
+
73
+ * **An owner writes the table's own buttons (TABLE-PARITY M4).** Table settings gains a "Row actions"
74
+ section (`RowActionsEditor.tsx`, `RowActionsSection`): list the table's actions in its order with a
75
+ plain sentence each, add one ("Changes the record" with its column changes — set to, clear, work out
76
+ from a formula — or "Asks an agent" with the words to ask), edit, move up and down, and retire (the
77
+ consequence is named first; "Put it back" restores it at the same place with the same id). Start
78
+ from a record turns a real record's values into the changes; the before-and-after preview shows the
79
+ chosen record column by column, and a worked-out column says it is worked out when the action runs.
80
+ Every change is one `custom.action_declare` with the whole list; a store refusal is shown under the
81
+ form and what was typed stays. Same controls as the field panel. Suite
82
+ `an-owner-writes-the-tables-own-buttons.test.tsx`: RED 6 on the bytes without the section, GREEN 8.
83
+ * **A button declared in settings appears on the grid beside it without a reload.** `rowActionsChanged(tableId)`
84
+ (gridActions.tsx): every mounted `useTableActions` of that table reads `custom.row_actions` again after a
85
+ declare. Test RED without the listener, GREEN with it.
86
+
87
+ ### Lane S1-PRIME VIEW-KEYS (2026-09-24)
88
+
89
+ * **Every per-view setting is a declared key of the one saved view.** `viewKeys.ts`: `VIEW_KEYS` mirrors
90
+ the store's registry `custom.view_keys()` (path, shape, the layouts that read it, who writes it);
91
+ `viewSettings(definition)` is THE reader `viewFromRow` goes through; `viewSettingsFor(view, layout)`
92
+ hands a layout only the keys it reads; `viewSettingsPatch` is the writer's shape `saveViewPatch` sends.
93
+ * `SavedViewSpec` gains `swimlaneField` (board swimlanes) and `collapsedColumns` (board columns kept shut);
94
+ `GridGrouping` gains `then` (up to two sub-group levels) and keeps it when the look is written back whole
95
+ — a colour change no longer erases sub-groups.
96
+ * **A page open never writes.** `ViewBar`'s default seed is a read, then a declare only on absence: a table
97
+ with a default view of any name (a renamed "All records") is opened without a write, and the loader no
98
+ longer re-runs on every parent render (a host passing `seed={[…]}` inline re-read the views in a loop).
99
+ Exported `seedIsSatisfied`.
100
+
101
+ ### Lane S4 BATCH-DOORS (2026-09-24)
102
+
103
+ * **The board takes many cards at once.** `PipelineBoard`: cmd/ctrl-click takes one card, shift-click
104
+ takes the run (the data table's own `toggleSelection` rule, now in `@ai-matrx/design-system`), `x`
105
+ takes the focused card, shift+arrow extends, Esc clears. A bulk bar says "N cards selected" with
106
+ "Move N to…" and one menu item per stage ("Move 6 to Scheduled"); it sends each card's loaded version
107
+ in ONE call and paints each refused card with its own sentence (needs a column, changed since loaded,
108
+ no right). A Viewer can select nothing, so a Viewer never sees the bar.
109
+ * **The inbox decides many at once.** `ActionInbox`: a checkbox per actionable item, shift-click for a
110
+ run, "Approve N" / "Decline N" in the same bar; `x` selects, `a`/`d` act on the selection. One call,
111
+ one sentence back ("6 approved."), and each item that was not decided says why.
112
+ * Headless two-seat walk on the dev clone: `demo/batch-doors-walk.mjs` (serve with
113
+ `demo/vite.walk.config.ts` so a peer's save does not reload the page mid-walk).
114
+
115
+ ### Lane S6 PORTAL-BRANDING (2026-09-24)
116
+
117
+ **A portal carries the business's own look and its forms** (UI-CHAMPIONS rows 45, 46, 55; champions
118
+ Softr's branded portal and the Stripe customer portal).
119
+
120
+ * **`PortalLookEditor`** (new export), shown inside every portal card in `PortalsPanel`: the name
121
+ clients see (empty = the organization's), a welcome line, a logo chosen from the organization's
122
+ own PUBLIC pictures (`card.logo_candidates`), a colour from the design system's seven names (the
123
+ same swatches the table highlights use), footer links (`https://`, `mailto:`, `tel:`), and every
124
+ form on the tables the portal shows — tick to offer it, optional button words, Up/Down order. Saved
125
+ through `portalDeclare({ …, config })`, re-stating the portal exactly as the card read it; the
126
+ store's refusal is drawn by `RefusalNotice`.
127
+ * **`PortalCardViewProps.look`** — the slot the editor renders in (absent = none offered).
128
+
129
+ **Consumer action:** needs `@ai-matrx/records` with `PortalCard.config/style/forms/accents/logo_candidates`
130
+ and `portalDeclare({ config })` (aidream `4bf803c452`), and the S6 store doors on the database.
131
+
132
+ ### Lane SC-R (2026-09-24; landed by DEFAULT-GRID-PARITY)
133
+
134
+ * `EntityReferenceControl` (chips of what an entity-reference value names, withheld words said,
135
+ take-off, no add control until the store has a search door), `EntityReferenceKindPicker`,
136
+ `entityReferenceSpec`, `whatIsMissingForEntityReference`; an entity-reference cell reads as its
137
+ words instead of `[object Object]`.
138
+ * The field panel asks an entity-reference column which kinds it points at, sends
139
+ `allowed_types`, and shows an existing column's kinds locked. The menu entry "Points at
140
+ something else on the platform" is drawn only once `custom.field_kinds()` publishes
141
+ `entity_reference` (after the chair's apply and `pnpm records:generate`).
142
+
143
+ ### Lane DEFAULT-GRID-PARITY (2026-09-24)
144
+
145
+ **The default grid shows what the store holds** (TABLE-PARITY, "in the store but not shown by the
146
+ default grid"). Each item is read through the store's own door and drawn through the data table's
147
+ own seams (`rowActions`, `selection`, `defaultSort`, `footerRows`, `rowHeight`, `copy`), with no
148
+ second table. A control the store cannot serve for a table is absent, never dead.
149
+
150
+ * **Row actions (M1–M3).** A table's own actions (`custom.row_actions`) are one-click buttons on
151
+ every row, and on the selection bar for the ticked rows. One press is ONE `custom.action_run`,
152
+ and it asks first when the action says to. A refusal names the record and the column. Undo writes
153
+ back what each row held. An `agent` action hands the row and its prompt to the new optional host
154
+ port `runAgentAction`, and is absent when the host does not bind it. Readers get no buttons.
155
+ * **The table's default sort (S2).** `data.default_sort` (`[{field, direction}]`; older spellings
156
+ also read) is where the grid opens.
157
+ * **Summary bar (F1).** Under each column, a summary the view keeps (`presentation.summaries`),
158
+ worked out by `custom.record_aggregate` over the whole table. It offers only the measures
159
+ `custom.agg_operations` lists that mean something for that column.
160
+ * **Layout knob (D1 D5 D6).** Row height, freeze first column, wrap and fit/scroll are read from the
161
+ organization's `custom/grid_layout`, and the view's own choice wins (`presentation.wrap`, `fit`,
162
+ `freezeFirst`). A Layout menu says where each choice came from.
163
+ * **Formula columns (K1 K2).** A worked-out column's header carries an `fx` mark with what it is
164
+ worked out from. The field panel writes a formula in the language a person types
165
+ (`formula_text`, checked live by `custom.formula_parse`) and can re-aim an existing one.
166
+ * **System columns (H2 H3).** The field menu offers Record number, Created time and Last changed
167
+ time (the store's `autonumber` / `created_time` / `modified_time` words). A new record-number
168
+ column numbers the records already there through `custom.autonumber_backfill`, and says so if it
169
+ was refused.
170
+ * **Duplicate a row (A7)**: the typed values only, never the worked-out ones.
171
+ * **A reference for a prompt (R3)**, beside Copy: named by the row label, carrying the table and
172
+ row ids.
173
+ * **Webhooks in the Notifications rail (N3)**: list, delivery health, start one (the signing
174
+ secret is shown once), archive. A reader gets no controls.
175
+ * **Start from an example (P2)** on the tables home: builds a registered real use case through
176
+ `custom.table_from_example`.
177
+ * **A row opens its record (VERIFIER-17).** An editable grid's cells are each their own control, so
178
+ a row click never fired and nothing opened the record. The first cell of every row now carries an
179
+ Open control, named for the record and always reachable by keyboard and touch. Space on any
180
+ focused cell opens that row's record, and Enter still edits. Absent when the host cannot open a
181
+ record (`onOpenRecord`). Kanban, calendar and gallery cards already opened on click.
182
+ * **An export writes what the grid shows (GRID-PORT finding).** CSV and XLSX used to write a
183
+ relation's stored record id. They now write the words the grid draws for it: the same resolver
184
+ (`useRecordLabels`), and `relation_words_many` for any id no cell on screen has drawn yet. An id
185
+ nobody can resolve stays the id rather than a blank. Choice cells carry their labels, and a
186
+ worked-out column carries the store's value.
187
+ * **No machine "…ss" plurals (VERIFIER-16/17 M3).** `realPlural` and `pluralNoun` (`names.ts`): a
188
+ `label_plural` that is only the name with an "s" appended ("Customerss", "Status optionss") is
189
+ never shown, and a sentence's noun never doubles a final "s". The stored values come from the
190
+ store's `name || 's'` fallback. That root is the store's to fix.
191
+
192
+ ### Lane S3 (2026-09-24)
193
+
194
+ **Period comparison, date grain and targets on the dashboard canvas** (UI-CHAMPIONS-PLAN rows 17,
195
+ 18, 20). Needs `@ai-matrx/records` with `recordAggregate({compare})` / `dashboardRun({compare,
196
+ grain})` and the store's `uichamp_s3_*` files; on a store without them nothing new is drawn.
197
+
198
+ * **A number tile shows the trend and the target.** A block the store compared (`block.compare`)
199
+ shows its total with an arrow and signed percent against the prior window ("16,930 ▼ -7.9% —
200
+ September 2026 · 18,380 in August 2026"), and a block with a `target` draws a progress ring
201
+ ("Monthly target 25,000 · 68%"). The arrow is coloured only when the block declares a target (the
202
+ one declared fact that says up is good); otherwise it is the neutral tone.
203
+ * **A compared chart draws the prior period behind the current one** in the palette's new
204
+ comparison tone (`COMPARISON_COLOR`, `--muted-foreground`), with a legend naming both windows and a
205
+ dashed goal line (`GOAL_COLOR`) at the target's pace per bucket. A quiet week inside the window is
206
+ dated ("Week of Sep 27"); only a position past the window's last bucket is named by its place.
207
+ * **A date-grain picker on the canvas** ("By the saved grain" / day / week / month / quarter / year),
208
+ shown only when a block runs along a date; it re-cuts every bucketed block through
209
+ `dashboard_run(p_grain)`, in the organization's own time zone and week start.
210
+ * **Buckets are named in the organization's calendar** (`bucketName`, `windowName`,
211
+ `positionBucket`), read from the store's local ISO moment and never from the browser's clock.
212
+ * **Drill-through on a bucket opens that period of its date Field** (`bucketWindow`) — it used to ask
213
+ for a Field called `completed_on_week`, which matched nothing — and a compared block opens its
214
+ current window only.
215
+ * Harness: `RECORDS_UI_DEMO_TABLE` opens every page on an existing Table and writes nothing of its
216
+ own into that organization; `demo/walk-s3-periods.mjs` walks the canvas headless from two seats.
217
+
218
+ **Added at publish (S3's lines, owed because the file held another lane's uncommitted edits):**
219
+
220
+ * **The owner's canvas is the owner's from its first frame.** `DashboardCanvas` shows its skeleton
221
+ until the rights answer (`custom.my_levels`) is in, instead of drawing the blocks first with no
222
+ controls.
223
+ * **Per-block compare and target editor** in each block's settings row (the same `Select` /
224
+ `BasicInput` as the grouping picker): no comparison / the period before / the same period last
225
+ year, the period, and no target / a target number / a goal column (number, money and percent
226
+ columns only). Absent on a list that does not move and, for compare, on a table with no date column.
227
+ * **A target read from a goal column** (`target {field, op: sum|avg|min|max}`): a reader who may not
228
+ read that column sees the store's sentence in place of the target and the block still draws its number.
229
+
230
+ ### Also in this release
231
+
232
+ **Lane S7-PRIME — a stranger starts from a link that already knows something, stops, comes back
233
+ on another device, and leaves by the owner's door (UI-CHAMPIONS-PLAN rows 34, 35, 40).**
234
+
235
+ * **Prefill.** `FormRunner` takes `initialAnswers`: the form starts with those answers in their
236
+ questions, changeable, and hands them to `whichAsked` like typed answers, so a question that
237
+ depends on a prefilled one branches exactly as if the person had typed it.
238
+ * **Keep my place.** `FormRunner` takes `onAnswersChange` — every change to the answers after the
239
+ first paint, for the host to keep (the public page saves it through `custom.form_draft_save`).
240
+ A form resumed part-way opens on the first question still waiting for an answer.
241
+ * **The thank-you screen leaves by the owner's door.** `FormSpec.thankYou` is now `FormThankYou`
242
+ (`title`, `body`, `redirectUrl`, each optional); the runner shows the message, then goes on to
243
+ `redirectUrl` a moment later with a "Go now" link beside it (a preview never leaves and says
244
+ where the live form would go). The store judges the address — a secure page on the
245
+ organization's own sites — so the runner never does. `formPresentation` / `formFromSummary`
246
+ carry it as `presentation.thank_you.redirect_url` through `thankYouDocument` /
247
+ `thankYouFromDocument`; an empty thank-you is `null`.
248
+ * **`FormBuilder`** gains an "After somebody sends it" section beside the notify editor: title,
249
+ message and "Then go to (optional)". A foreign address is refused on Save in the store's words,
250
+ naming the sites it may use.
251
+
252
+ **Consumer action:** matrx-frontend's `PublicFormRunner` hands `whichAsked`, `initialAnswers` and
253
+ `onAnswersChange` through a spread and casts the spec so it compiles on 0.84.8; on adopting this
254
+ version, import `FormAsked`, pass the three ports by name and drop the cast.
255
+
256
+ **Added at publish (S7-PRIME's lines, owed for `ec860af7ac` and `cd33e86359`):**
257
+
258
+ * `resumed`: only a saved place being resumed opens on its first unanswered question; a link that
259
+ only prefills opens on question one with the prefilled answer in place.
260
+ * FormBuilder: a write the store refuses (Save, publish, notify choice, New form) shows the store's
261
+ words above the builder with Keep editing / Discard; only a failed load replaces the screen.
262
+
263
+ ### Lane UI-QUICK-1 (TABLE-PARITY review)
264
+
265
+ **The class 0.84.9 left open.**
266
+
267
+ * **A table colored at the TABLE level now paints everywhere, not just wherever a view redecorated
268
+ it.** 0.84.9 wired `Card` and `Grid` to a view's own `presentation.style`; it never read G1's
269
+ table-level decorations (`custom.table_decorations` / `client.tableDecorations`) at all, so a
270
+ table whose colors are a `color_by` and a set of rules stored on the TABLE — never on any one
271
+ view — drew nothing on the default grid, the kanban, the calendar or the gallery alike (Arman's
272
+ Coding Accounts table: colored by Account Type, with a MAXED/NEAR rule, no view has ever set its
273
+ own look). New `styleFromDecorations` (`presentation.ts`) translates G1's Field-id-addressed
274
+ decorations into the Field-key-addressed `TableStyle` every reader already understands; new
275
+ `resolveTableStyle` is the ONE presentation resolver — it layers the view's own style over the
276
+ table's decorations piece by piece (a view that set only a manual highlight still inherits the
277
+ table's `colorBy`/`rules`; a view that set its own `colorBy` AND `rules` is not haunted by the
278
+ table's). `Grid` and `ViewSwitcher`'s `Board` (feeding `Kanban`/`Calendar`/`Gallery`/`Card`) both
279
+ fetch the table's decorations once (new `useTableDecorations` hook,
280
+ `tableDecorations.ts`) and both call the same resolver — no second color logic anywhere in this
281
+ package. A table with no decorations and no view style resolves to nothing, exactly as before.
282
+ * Corrects the 0.84.9 changelog entry above, which overclaimed "the table's own colors" when it
283
+ had only wired the view's.
284
+
285
+ ### Folded from source version 0.84.10
286
+
287
+ **Lane FORMS-FIX-1 — two screens that lied about forms (UI-BENCH-FORMS gap 1 and its cheapest correctness win).**
288
+
289
+ * **A public form branches the way its owner set it.** `FormRunner`'s public arm had no evaluator,
290
+ so every "ask this only when …" question was shown to every stranger. It now takes a
291
+ `whichAsked` port — the host's server asks the STORE (`custom.form_public_asks`, the same Rule
292
+ through the same `custom.rule_eval`) with the answers so far — and hides exactly what the store
293
+ says to hide, a quarter-second after typing stops. Undecided shows; a condition the store could
294
+ not work out shows WITH the store's sentence. A public mount with no port still says out loud
295
+ that it cannot branch. New exported type `FormAsked`.
296
+ * **"Tell me when somebody answers" is on the form builder.** `notifyRuleId` round-tripped and
297
+ `custom.form_notify` delivered through it, but nothing on the builder could set it. `FormBuilder`
298
+ now mounts `NotifyRuleEditor` beside the link, in a new CHOOSE MODE (`chosenRuleId` /
299
+ `onChoose`): pick a subscription or make one and the form is re-declared with it at once — the
300
+ whole form, so its accept Rule rides back. A table with no saved view offers "Watch every
301
+ record" through the store's own `custom.view_declare`.
302
+ * `NotifyRuleEditor` reads saved views from the store's own `custom.views` door when the host binds
303
+ no `savedViews` port, instead of printing a dead end.
304
+ * **The owner's preview branches too.** The signed-in arm of `FormRunner` (the builder's live
305
+ preview) used to call `custom.rule_eval` from the browser — an internal evaluator no client may
306
+ call — so it printed "You do not have access to this" and never hid a question. It now builds its
307
+ `whichAsked` port from `client.formPreviewAsks` (`custom.form_preview_asks`: the same store loop
308
+ as the public door, behind the organization wall and the person's own rung on the Table), over
309
+ the DRAFT's own questions. The `evaluate` path is gone: one port, one store loop, both arms.
310
+ `FormAsked`'s refusal may carry the store's own `error`. **Needs `@ai-matrx/records` with
311
+ `formPreviewAsks`** (FORMS-FIX-1); until the door is regenerated into `STORE_DOORS` the preview
312
+ shows every question with the store's `door_absent` sentence.
313
+
314
+ **Consumer action:** matrx-frontend's `PublicFormRunner` already hands `whichAsked` through a spread
315
+ so it compiled on 0.84.8; on adopting this version, import `FormAsked` and pass `whichAsked` by
316
+ name.
317
+
318
+ ### Folded from source version 0.84.9
319
+
320
+ **Lane UI-QUICK-1 — two gaps from the CALENDAR-GALLERY bench, both cheap wins by design.**
321
+
322
+ * **Every kanban, calendar and gallery card now paints its VIEW's own colors.** `Grid` has
323
+ colored rows (`presentation.style`'s `color_by`, ordered rules, hand highlights) since this
324
+ package's own view-presentation work; the shared `Card` component that draws all three other
325
+ layouts never called the resolver. `Card` now takes an optional `style: TableStyle` and resolves
326
+ it through `resolveRowColor` + `ROW_TINT_CLASS` — the SAME primitive and the SAME class map the
327
+ grid uses, not a second color scheme. `ViewSwitcher` threads `view.presentation?.style` down to
328
+ `Kanban`, `Calendar` and `Gallery`. A table nobody has ever colored, or a `Card` given no `style`
329
+ at all, looks exactly as it did before this version. **Correction (see Unreleased, lane UI-QUICK-1):** this line
330
+ overclaimed — it wired the VIEW's own look only; a table colored at the TABLE level through G1's
331
+ `custom.table_decorations`, with no view ever setting its own `presentation.style` (Arman's
332
+ Coding Accounts table is exactly this shape), still drew nothing here. The Unreleased entry above closes that.
333
+ * **Gallery gets lazy images, a real placeholder, a card-size control and configurable fields.**
334
+ Cover images now carry `loading="lazy"` inside a fixed-height placeholder (`GALLERY_SIZE`), so a
335
+ gallery of hundreds only fetches what scrolls into view and mixed image/no-image records no
336
+ longer jump the grid. A small/medium/large size control is saved on the view as
337
+ `presentation.gallerySize`, the same pattern as the grid's own `rowHeight`. Fields shown are no
338
+ longer hardcoded to the first four — they read `presentation.hiddenFields`, the SAME hidden-
339
+ column set the grid's own column picker holds, so hiding a column anywhere hides it everywhere.
340
+ * `GridPresentation` gains `hiddenFields?: string[]` and `gallerySize?: "small" | "medium" |
341
+ "large"`, parsed and round-tripped the same tolerant way as every other field in
342
+ `parseGridPresentation`/`presentationDocument`.
343
+
344
+ ## 0.84.8
345
+
346
+ (lane GRID-PORT `5cb9bcbd99`; published by PUBLISH-SWEEP-4.)
347
+
348
+ **A host can draw a layout beside the package's own four, on the one table page.**
349
+ `RecordsUiHost.layouts?: HostLayout[]` — `{ id, label, render({ tableId, filter }) }`. Each
350
+ appears as a button in `ViewSwitcher`'s layout row after Grid / Kanban / Calendar / Gallery, and
351
+ `?view=<id>` opens it (`openingHostLayout`); `TablePage.onViewChanged` is called with the id.
352
+ Page-level, never written onto the view record (the store's layout words stay the four). A host
353
+ id equal to one of `PAGE_VIEWS` (or a spelling of one) is ignored, so `?view=grid` is always the
354
+ package's Grid. Why: the owner's ruling (2026-09-23) — AI Matrx's ported /data grid is the
355
+ "Sheet" layout of `/data-v2/<table>`, not a second page for the same table.
356
+
357
+ Consumer action: `onViewChanged` now receives `PageView | string`; widen a handler typed
358
+ `(view: PageView) => void`.
359
+
360
+ ## 0.84.7 (needs @ai-matrx/records 0.57.3)
4
361
 
5
362
  * **The suite is green again (386/386), and the class is gone rather than patched.** 0.84.5's
6
363
  publish gate went red in 15 files because records 0.57.2 made every table list ask
@@ -1638,9 +1995,6 @@ A table that is not a pipeline draws the plain kanban exactly as before; the
1638
1995
  decision is the Table's own `stage_field`, asked once, never guessed from the shape
1639
1996
  of a Choice column.
1640
1997
 
1641
-
1642
-
1643
-
1644
1998
  ## 0.38.0 — 2026-09-20
1645
1999
 
1646
2000
  **0.37.0's gate went red on the tarball, not on this package.** The packed
@@ -1650,7 +2004,6 @@ call. That is the tarball canary doing exactly its job: green in the monorepo,
1650
2004
  absent for an npm consumer. `@ai-matrx/records` 0.26.0 carries it, and this
1651
2005
  release is the first that can resolve against it.
1652
2006
 
1653
-
1654
2007
  ## 0.37.0 — 2026-09-20
1655
2008
 
1656
2009
  **Three unhandled rejections were failing the release gate while every test
@@ -1662,7 +2015,6 @@ rejection, so vitest reported `12 passed / 79 passed` and exited non-zero, and
1662
2015
  0.36.0 never reached npm. The fixtures are re-recorded against the live store;
1663
2016
  the suite is now 12 files / 79 tests with no errors.
1664
2017
 
1665
-
1666
2018
  ## 0.37.0 — 2026-09-20
1667
2019
 
1668
2020
  **The capture sheet, run on a phone, with or without a signal (PRODUCTS row 15).**
@@ -1712,7 +2064,6 @@ costs somebody a text and never robs the real Monday summary of a week of news.
1712
2064
 
1713
2065
  Needs `@ai-matrx/records` ≥ 0.26.0.
1714
2066
 
1715
-
1716
2067
  ## 0.36.0 — 2026-09-20
1717
2068
 
1718
2069
  **0.35.0 never reached npm: the demo harness asked the store for ten thousand
@@ -1742,7 +2093,6 @@ Also in this release, from 0.34.0/0.35.0 and this lane:
1742
2093
  `FieldHistoryPanel`, so a screen that is green in the monorepo and absent from
1743
2094
  an npm consumer's tarball is caught at the gate.
1744
2095
 
1745
-
1746
2096
  ## 0.35.0
1747
2097
 
1748
2098
  **Per-record AI enrichment: the badge on every agent-owned cell, and the panel behind it.**
@@ -1794,7 +2144,6 @@ question to ask, and `GridCell` takes `editing: GridEditing | null` — a null
1794
2144
  session means no editor, and nothing else. `src/who-changed.test.tsx` covers
1795
2145
  it; its load-bearing clause fails on the old gate.
1796
2146
 
1797
-
1798
2147
  ## 0.32.0
1799
2148
 
1800
2149
  **The import wizard, rebuilt on the store's own import doors (SCR-10 / SCR-N-7).**
@@ -2182,7 +2531,6 @@ through the field panel.
2182
2531
 
2183
2532
  None. `presentation` is optional and its absence is today's behavior.
2184
2533
 
2185
-
2186
2534
  ## 0.19.1
2187
2535
 
2188
2536
  **The progress bar and the count beside it said different things.** One question
@@ -2193,7 +2541,6 @@ form, not by reading it. Typeform's bar advances as you move through the form,
2193
2541
  and now so does this one. On a single page there is no position, so it keeps
2194
2542
  measuring what it can: how much is answered.
2195
2543
 
2196
-
2197
2544
  ## 0.19.0
2198
2545
 
2199
2546
  **The choice picker reads a document from any era, and writes the contract.**
@@ -2210,7 +2557,6 @@ falls through and renders as itself.
2210
2557
 
2211
2558
  **Consumer action:** none beyond taking 0.19.0 with `@ai-matrx/records` 0.13.0.
2212
2559
 
2213
-
2214
2560
  ## 0.18.0
2215
2561
 
2216
2562
  **A person can switch off a notification an agent switched on.** "Make me an
@@ -2231,7 +2577,6 @@ never say a subscription is off while it keeps firing.
2231
2577
  None — `TablePage` grew the rail. Needs `@ai-matrx/records` 0.12.0 or above for
2232
2578
  `client.subscriptions` and `client.subscriptionMute`.
2233
2579
 
2234
-
2235
2580
  ## 0.17.0
2236
2581
 
2237
2582
  **There is one Form object, and it is the one with the link.** `FormBuilder` kept
@@ -2582,7 +2927,6 @@ import { recordStoreShare } from "@/features/sharing/components/RecordStoreShare
2582
2927
  Leave it out and nothing changes — the screens render exactly as they did in
2583
2928
  0.8.0, with no Share button.
2584
2929
 
2585
-
2586
2930
  ## 0.8.0
2587
2931
 
2588
2932
  **The worst finding of the 19 September verdict: "Add field" opened a proper
@@ -2633,7 +2977,6 @@ A table made with the product's own button had exactly one column.
2633
2977
  drawing values outside `RecordsMount` should render `<RecordValue>` (or wrap in
2634
2978
  `RecordLabelProvider`) so ids resolve to names.
2635
2979
 
2636
-
2637
2980
  ## 0.7.0
2638
2981
 
2639
2982
  What the first headless walk of 0.6.0 through a real browser found, in the same