@ai-matrx/records-ui 0.85.9 → 0.85.10

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/dist/index.d.cts CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as react from 'react';
2
2
  import { ReactNode, ComponentType, RefObject } from 'react';
3
3
  import * as _ai_matrx_records from '@ai-matrx/records';
4
- import { PermissionLevel, Table, RecordScopeContext, EnrichCell, RecordsError, Uuid as Uuid$1, StoreFilter, AggregateFilter, Field, RecordDocument, ReadRow, RelationDisplay, WriteConflict, RecordsErrorCode, TableDecorations, DeclarableFieldKind, ParityFieldType, NewFieldDeclaration, RuleExpression, HiddenFieldNotice, ValueEnvelope, ChecklistRequirementKind, ContextPolicy, WorkDueState, FieldSensitivity, StageRuleOnFail, SubscriptionCadence, WorkApprovalState, WorkInboxKind, FieldKind, RecordFilter, RecordsResult, SavedViewRow, ArchiveLane, ArchivedRow, WorkInboxView, WorkInboxItem, FormSummary, PortalCard, PortalPrincipal, PortalPreviewRow, MySubscription, DashboardBlockKind, AggregateBucket, AggregateMeasure, AggregateCompare, DashboardTarget, DashboardBlock, DashboardSummary, DocTemplateRow, DocRenderRow, QuietHours, DashboardBlockResult, ChecklistRunStep, BookingSummary, CaptureSheetFace, CaptureField, AnonTokenBinding, RecordsConfig, RecordsActor, RecordsDataSource, HistoryActor, FieldHistoryEntry } from '@ai-matrx/records';
4
+ import { PermissionLevel, Table, RecordScopeContext, EnrichCell, RecordsError, Uuid as Uuid$1, StoreFilter, AggregateFilter, Field, RecordDocument, ReadRow, RelationDisplay, WriteConflict, RecordsErrorCode, TableDecorations, RecordFilter, RuleExpression, RecordsResult, SavedViewRow, DeclarableFieldKind, ParityFieldType, NewFieldDeclaration, HiddenFieldNotice, ValueEnvelope, ChecklistRequirementKind, ContextPolicy, WorkDueState, FieldSensitivity, StageRuleOnFail, SubscriptionCadence, WorkApprovalState, WorkInboxKind, FieldKind, ArchiveLane, ArchivedRow, WorkInboxView, WorkInboxItem, FormSummary, PortalCard, PortalPrincipal, PortalPreviewRow, MySubscription, DashboardBlockKind, AggregateBucket, AggregateMeasure, AggregateCompare, DashboardTarget, DashboardBlock, DashboardSummary, DocTemplateRow, DocRenderRow, QuietHours, DashboardBlockResult, ChecklistRunStep, BookingSummary, CaptureSheetFace, CaptureField, AnonTokenBinding, RecordsConfig, RecordsActor, RecordsDataSource, HistoryActor, FieldHistoryEntry } from '@ai-matrx/records';
5
5
  import { Uuid, StoreFilter as StoreFilter$1, Field as Field$1, ReadRow as ReadRow$1 } from '@ai-matrx/records/react';
6
6
  import { RecordsClient } from '@ai-matrx/records/core';
7
7
  export { DEFAULT_FIELDS, DeclareResult, NewFieldSpec, NewTableSpec, addFields, declareTable, fieldDeclarationFor, tokenFor } from '@ai-matrx/records/core';
@@ -650,6 +650,7 @@ declare function useRowRights(rowIds: Uuid$1[], enabled: boolean): (rowId: Uuid$
650
650
  * `retention_days` a document key, snake_case
651
651
  * `8f2c1b0a-…` a uuid
652
652
  * `23514` / `PT409` a SQLSTATE
653
+ * `STORE-TAILS-3:` / `UI-FIX-19:` a build lane's tag (UI-FIX-19)
653
654
  */
654
655
  declare const MACHINE_IDENTITY: RegExp[];
655
656
  /** Whether any shape above appears in this text. */
@@ -1227,12 +1228,292 @@ declare function styleFromDecorations(decorations: TableDecorations | null | und
1227
1228
  */
1228
1229
  declare function resolveTableStyle(decorations: TableDecorations | null | undefined, fields: readonly Field[], viewStyle: TableStyle | undefined): TableStyle;
1229
1230
 
1231
+ /** The four ways one saved view can be looked at (SCR-6). */
1232
+ declare const VIEW_LAYOUTS: readonly ["grid", "kanban", "calendar", "gallery"];
1233
+ type ViewLayout = (typeof VIEW_LAYOUTS)[number];
1234
+ declare const LAYOUT_LABEL: Record<ViewLayout, string>;
1235
+ /**
1236
+ * What each layout needs before it can draw anything, in one sentence a screen
1237
+ * prints. A layout that is missing its Field says so instead of rendering an
1238
+ * empty board that looks like "no records".
1239
+ */
1240
+ declare const LAYOUT_NEEDS: Record<ViewLayout, string | null>;
1241
+ /** One sort clause. The Field's key and a direction; nothing else is a sort. */
1242
+ interface ViewSort {
1243
+ field: string;
1244
+ direction: "asc" | "desc";
1245
+ }
1246
+ /** How a saved view is ordered: by hand, by its sort, or it never said (the same as sorted). */
1247
+ type ViewOrder = "manual" | "sorted" | null;
1248
+ /**
1249
+ * THE DECLARATIVE SPEC. An agent writes this whole object; a person tweaks it.
1250
+ * Every builder component in this package takes one of these as a prop.
1251
+ */
1252
+ interface SavedViewSpec {
1253
+ name: string;
1254
+ /** The Table this view looks at. */
1255
+ subject: Uuid$1;
1256
+ layout: ViewLayout;
1257
+ /** Kanban: the Field whose value becomes a column. */
1258
+ groupField?: string | null;
1259
+ /**
1260
+ * Kanban: a number Field to TOTAL under each column heading. It lives on the VIEW for
1261
+ * the same reason the colours do — two people read one board two ways and neither
1262
+ * reading is a fact about the records.
1263
+ */
1264
+ measure?: string | null;
1265
+ /**
1266
+ * Kanban: the Field whose values cut the board into SWIMLANES across the columns — a
1267
+ * dispatcher's board by Status, one lane per Technician (S1-PRIME VIEW-KEYS). Never the
1268
+ * column Field itself; the store refuses that.
1269
+ */
1270
+ swimlaneField?: string | null;
1271
+ /** Kanban: the columns this view keeps shut to a strip, by the column's stored value. */
1272
+ collapsedColumns?: string[];
1273
+ /** Calendar: the Field that places a record in the month. */
1274
+ dateField?: string | null;
1275
+ /** Gallery: the Field shown large on the card. */
1276
+ imageField?: string | null;
1277
+ sorts?: ViewSort[];
1278
+ /**
1279
+ * HOW THE VIEW IS ORDERED (ORDER-FIX): `"manual"` — the positions a person set by hand ARE the
1280
+ * view's sort, and it carries no `sorts`; `"sorted"` or null — by `sorts`. READ ONLY: the store
1281
+ * writes "manual" only when the rows are placed (`custom.view_record_order_set`) and turns it
1282
+ * off itself when a sort is saved, so no patch ever sends it (`viewSettingsPatch` does not).
1283
+ */
1284
+ order?: ViewOrder;
1285
+ /**
1286
+ * The membership Rule. The STORE decides who is in this view; this is a
1287
+ * reference to its Rule record, never a predicate this package evaluates.
1288
+ */
1289
+ ruleId?: Uuid$1 | null;
1290
+ /**
1291
+ * THE FLAT FILTER a digest or a notify rule wrote the view with — the one shape
1292
+ * `custom.record_filter_sql` compiles, by Field KEY. A view saved in the bar has
1293
+ * none; a view saved by `DigestScheduler` ("where Status is Open") does, and the
1294
+ * grid and the board show exactly the records it names.
1295
+ */
1296
+ filters?: RecordFilter;
1297
+ /**
1298
+ * S2-PRIME — THE VIEW'S OWN QUESTION, as the condition builder writes it: the store's Rule
1299
+ * shape `{op, args}`, ALL / ANY / NOT groups nested to any depth, Fields by id. Kept in the
1300
+ * definition as `where` and asked of the store unchanged — the grid's list door, the board's
1301
+ * headings and the dashboard's numbers compile it into the one WHERE clause
1302
+ * (`custom.record_filter_sql`). Never stored under `filters`: that key is the flat map the
1303
+ * digests and the notifier read, and they would refuse it by name.
1304
+ */
1305
+ where?: RuleExpression | null;
1306
+ isDefault?: boolean;
1307
+ /**
1308
+ * HOW THIS VIEW LOOKS — its colors, its per-column formats and its frozen
1309
+ * column (`presentation.ts`).
1310
+ *
1311
+ * It lives on the VIEW and not on the Fields because two people read one
1312
+ * table two ways and neither reading is a fact about the records; it lives on
1313
+ * a RECORD, not a new column, because that is what makes it something an
1314
+ * agent writes with the same `records` tool as everything else.
1315
+ */
1316
+ presentation?: GridPresentation;
1317
+ }
1318
+ /**
1319
+ * A saved view as it comes back out of the store, with its row's own id.
1320
+ *
1321
+ * It carries no version: `custom.view_declare` MERGES one key at a time, so two
1322
+ * tabs pressing two different controls on one view both stand, and one control
1323
+ * pressed in both keeps the latest — how Airtable and Notion keep a view change.
1324
+ */
1325
+ interface SavedView extends SavedViewSpec {
1326
+ id: Uuid$1;
1327
+ /** Where the view came from when it was moved in (the mover, the S0 repair), else null. */
1328
+ movedFrom: Record<string, unknown> | null;
1329
+ /**
1330
+ * THE WAY THIS TABLE IS MEANT TO BE LOOKED AT, when somebody decided one — a layout id the
1331
+ * host draws (`sheet`), read off the stored kind before `layout` folds it to the four the
1332
+ * package draws. Null when nobody designated one. See `designatedLayoutOf`.
1333
+ */
1334
+ designatedLayout?: string | null;
1335
+ }
1336
+ /**
1337
+ * WHICH LAYOUT A VIEW WAS DESIGNATED, OR NULL (lane DATA-V2-FACE, owner 2026-09-24).
1338
+ *
1339
+ * "For something like my data tables, which are predetermined with a default view of 'Sheet'
1340
+ * I want to see … it just shows them the way it should in the default view. That's how you take
1341
+ * custom systems and fake like they're hard-coded builtin systems."
1342
+ *
1343
+ * A designation is a DELIBERATE write, never a click: a view whose stored kind is a layout the
1344
+ * HOST draws (`sheet` — a layout the store's view-keys registry declares, and one the layout
1345
+ * buttons never write onto a view). That is the ONE way a table is designated (lane
1346
+ * VIEW-KEYS-SHEET): the mover and the repair write `layout: "sheet"` on a copied older table's
1347
+ * default view; `moved_from` says only where a view came from and designates nothing. A package
1348
+ * kind (`grid`, `kanban`, …) is not a designation: picking Kanban writes `layout: "kanban"` onto
1349
+ * the view, and a click must never take the layout chooser away.
1350
+ */
1351
+ declare function designatedLayoutOf(definition: Record<string, unknown> | null | undefined): string | null;
1352
+ /**
1353
+ * HOW THE TABLE OPENS, decided once from its default view and the layouts the host draws.
1354
+ *
1355
+ * · `designated` — the default view names a host layout this host registered: the page opens
1356
+ * IN it and offers no other layout by default (the others sit in the page's one menu);
1357
+ * · `chooser` — nothing designated (or the host does not draw what was designated): the
1358
+ * page opens on the view's own layout and shows the layout chooser.
1359
+ *
1360
+ * Only the DEFAULT view designates: a person who opens another saved view is choosing, and the
1361
+ * chooser is theirs.
1362
+ */
1363
+ type TableOpening = {
1364
+ kind: "designated";
1365
+ hostLayout: string;
1366
+ } | {
1367
+ kind: "chooser";
1368
+ };
1369
+ declare function tableOpening(view: Pick<SavedView, "isDefault" | "designatedLayout"> | null | undefined, hostLayoutIds: readonly string[]): TableOpening;
1370
+ /**
1371
+ * THE TABLE'S DEFAULT VIEW WHEN MORE THAN ONE SAYS IT IS (lane DATA-V2-FACE-2).
1372
+ *
1373
+ * Two views can both claim the default: the mover's designated one (the older table's own, the
1374
+ * Sheet) and a page seed that a later move of the view bar's older store brings in beside it —
1375
+ * both named "All records", so the store's name order cannot tell them apart. A designation is a
1376
+ * deliberate write and a seed is not, so the designated default wins; then the first default.
1377
+ */
1378
+ declare function defaultViewOf<V extends Pick<SavedView, "isDefault" | "designatedLayout">>(views: readonly V[]): V | undefined;
1379
+ /**
1380
+ * WHICH FIELD EACH LAYOUT CAN BE DRAWN FROM, decided from the FIELD'S OWN
1381
+ * DECLARATION and never from the values it happens to hold.
1382
+ *
1383
+ * A kanban makes columns from a choice: the Field whose value is one of a
1384
+ * closed set (FLD-5 / FLD-6 — a pick-list is a Table, and a select stores the
1385
+ * id of one of its records). Grouping by a free-text Field produces one column
1386
+ * per record, which is not a board. A calendar places a record in time, so it
1387
+ * needs a Field that holds a date.
1388
+ *
1389
+ * These two answers are here, beside the layouts, because the picker on the
1390
+ * screen and any agent writing a view spec must agree about what is offerable.
1391
+ */
1392
+ declare const LAYOUT_FIELD_KIND: Record<ViewLayout, "choice" | "date" | null>;
1393
+ /**
1394
+ * ─────────────────────────────────────────────────────────────────────────────
1395
+ * WHICH VIEW A LINK NAMES — the address bar as a first-class control.
1396
+ *
1397
+ * 🚨 THE DEFECT THIS CLOSES (lane FRONT-DOOR, 2026-09-21; VERIFIER-8 HIGH-2).
1398
+ * `/data-v2/<table>?view=kanban` returned 200 and rendered THE GRID. The
1399
+ * switcher was there, the four buttons worked, and the parameter was read by
1400
+ * nobody — so a person who bookmarked a board, or sent one to a colleague, got
1401
+ * a grid, and nothing on the screen said the view had been dropped. A link that
1402
+ * does not land is the inbox-link defect (`activeRecordId`) and the dashboard
1403
+ * defect (`activeDashboardId`) for the third time, so this is the same shape of
1404
+ * fix: the URL names it, the page opens on it, and the URL FOLLOWS the person
1405
+ * when they pick another.
1406
+ *
1407
+ * The dashboards are a fifth value and not a special case: to the person, "the
1408
+ * dashboard" is one of the ways to look at this table, and a link to it has to
1409
+ * work like a link to the board.
1410
+ */
1411
+ /**
1412
+ * 🚨 `archived` IS THE SIXTH VALUE, AND IT IS NOT A SPECIAL CASE EITHER (lane
1413
+ * FIX-10A-ARCHIVE, 2026-09-22; VERIFIER-10 F4).
1414
+ *
1415
+ * Archiving a record used to be a ONE-WAY DOOR: the grid's row action soft-
1416
+ * deletes correctly — `deleted_at` set, version bumped, nothing removed — and
1417
+ * then no screen anywhere in the platform could bring it back. The store's
1418
+ * `custom.record_restore` was there the whole time and did exactly the right
1419
+ * thing; nothing reached it, because every read door answers live rows only.
1420
+ *
1421
+ * So the archive is a WAY OF LOOKING AT THIS TABLE, like the board and the
1422
+ * dashboards, reached by the same row of buttons and named by the same
1423
+ * `?view=` parameter — never a Trash area of the app somebody has to know
1424
+ * exists, and never a per-table screen. Every Table inherits it the moment it
1425
+ * is in this list.
1426
+ */
1427
+ declare const PAGE_VIEWS: readonly ["grid", "kanban", "calendar", "gallery", "dashboards", "archived"];
1428
+ type PageView = (typeof PAGE_VIEWS)[number];
1429
+ declare const PAGE_VIEW_LABEL: Record<PageView, string>;
1430
+ /**
1431
+ * What a `?view=` value means, or `null` when it means nothing here.
1432
+ *
1433
+ * `null` is NOT "show the grid": the caller is expected to say that the link
1434
+ * named something this table has no view for, because silently substituting a
1435
+ * different view is exactly the defect above wearing a parser.
1436
+ */
1437
+ declare function pageViewFromParam(raw: string | null | undefined): PageView | null;
1438
+ /** The sentence a link that named a view nobody has says. Never silent. */
1439
+ declare function unknownViewLine(raw: string): string;
1440
+ /** The one sentence a layout says when the table holds no Field it could use. */
1441
+ declare const LAYOUT_NO_FIELD: Record<ViewLayout, string>;
1442
+ /**
1443
+ * THE SPEC AS THE ONE DEFINITION `platform.saved_view` HOLDS — the keys
1444
+ * `custom.view_declare` documents, by Field KEY. `subject` is not in it: the door
1445
+ * takes the Table as its own argument and writes `table_id` itself.
1446
+ */
1447
+ declare function viewDocument(spec: SavedViewSpec): Record<string, unknown>;
1448
+ /**
1449
+ * One row of `custom.views` back into the spec a screen and an agent both read — THROUGH THE ONE
1450
+ * ACCESSOR (`viewSettings`, viewKeys.ts), so every layout sees every declared key the same way.
1451
+ */
1452
+ declare function viewFromRow(row: SavedViewRow): SavedView;
1453
+ /**
1454
+ * SAVE ONE NEW VIEW of a Table — the bar's "New view" and an agent's seed spec.
1455
+ * One `view_declare` with no `view_id`; the answer is the new view's id.
1456
+ */
1457
+ declare function declareView(client: Pick<RecordsClient, "viewDeclare">, spec: SavedViewSpec): Promise<RecordsResult<Uuid$1>>;
1458
+ /**
1459
+ * WRITE A CHANGE ONTO A SAVED VIEW, AND KEEP IT.
1460
+ *
1461
+ * `custom.view_declare` with the view's id MERGES: only the keys in `patch` are
1462
+ * written, a key cleared to null is removed, and everything the patch did not
1463
+ * name — the grid's own choices, a hand-set order, where the view was moved from,
1464
+ * a colour set in another tab — stays exactly as it was (S0, 2026-09-23: the door
1465
+ * used to rebuild the whole definition from what one press sent).
1466
+ *
1467
+ * There is no version to be stale against (VERIFIER-15 M4 is closed by the shape,
1468
+ * not by a retry): a saved view is a person's own arrangement of a table, and
1469
+ * their latest press on a control is the one that stands. A refusal — the view
1470
+ * was removed, or belongs to a table this person cannot open — is handed back for
1471
+ * the page to say through the one notice.
1472
+ */
1473
+ declare function saveViewPatch(client: Pick<RecordsClient, "viewDeclare">, tableId: Uuid$1, viewId: Uuid$1, patch: Partial<SavedViewSpec>): Promise<RecordsResult<Uuid$1>>;
1474
+ /**
1475
+ * A PARTIAL change to a saved view, as the definition keys the door merges. Only
1476
+ * the keys the caller actually changed are sent; a Field cleared (a board with no
1477
+ * "Group by") is sent as `null`, which the door removes.
1478
+ */
1479
+ declare function viewPatchDocument(patch: Partial<SavedViewSpec>): Record<string, unknown>;
1480
+
1481
+ /** A column sort as the grid holds it: the Field key and a direction. */
1482
+ interface ColumnSort {
1483
+ field: string;
1484
+ direction: "asc" | "desc";
1485
+ }
1486
+
1230
1487
  /**
1231
1488
  * WHAT A TABLE WITH NO COLUMNS SAYS. Named here because the gate asserts on it:
1232
1489
  * the state it describes is what a first-time person met for two days.
1233
1490
  */
1234
1491
  declare const NO_COLUMNS_YET = "This table has no columns yet";
1235
1492
  declare const NO_COLUMNS_WHY: string;
1493
+ /**
1494
+ * THE OPEN VIEW'S ORDER (lane GRID-MANUAL). A saved view is ordered by its sort OR by hand, never
1495
+ * both (the store's rule, ORDER-FIX). The grid is told which view it is drawing only for this: the
1496
+ * hand order lives on the view's row and is read and written through the view's own doors
1497
+ * (`custom.read_records_in_view_order`, `custom.view_record_order_set`). Left out, the grid is
1498
+ * ordered by the table's own sort exactly as before, and offers no Reorder.
1499
+ */
1500
+ interface GridViewOrder {
1501
+ viewId: Uuid;
1502
+ /** The view's own order word (`definition.order`). */
1503
+ order: ViewOrder;
1504
+ /** The view's own sort (`definition.sorts`) — where a sorted view OPENS, over the table's. */
1505
+ sorts?: readonly ViewSort[] | undefined;
1506
+ /**
1507
+ * The rows were placed by hand (Reorder → Save): the store made the view manual and removed its
1508
+ * sort. The host updates the view it holds. Unbound, Reorder is ABSENT — never a dead button.
1509
+ */
1510
+ onPlaced?: (() => void) | undefined;
1511
+ /**
1512
+ * "Use this sort instead": the host writes the sort onto the view (`custom.view_declare` with
1513
+ * `sorts`), which turns the hand order off in the store. Unbound, the control is ABSENT.
1514
+ */
1515
+ onUseSort?: ((sort: ColumnSort) => void) | undefined;
1516
+ }
1236
1517
  interface GridProps {
1237
1518
  tableId: Uuid;
1238
1519
  /** Rows per page. The table's own pagination handles the rest. */
@@ -1301,9 +1582,11 @@ interface GridProps {
1301
1582
  * state — they are simply not remembered.
1302
1583
  */
1303
1584
  onPresentationChange?: ((next: GridPresentation) => void) | undefined;
1585
+ /** The open view's order — its hand-set order or its sort (`GridViewOrder`). */
1586
+ viewOrder?: GridViewOrder | undefined;
1304
1587
  className?: string | undefined;
1305
1588
  }
1306
- declare function Grid({ tableId, pageSize, filter, onOpenRecord, onAddField, onNewRecordForm, toolbarActions, editable, presentation, onPresentationChange, className, onAskWhoChanged, }: GridProps): react.JSX.Element;
1589
+ declare function Grid({ tableId, pageSize, filter, onOpenRecord, onAddField, onNewRecordForm, toolbarActions, editable, presentation, onPresentationChange, viewOrder, className, onAskWhoChanged, }: GridProps): react.JSX.Element;
1307
1590
  /**
1308
1591
  * One Field becomes one column. The header is the Field's own NAME — never its
1309
1592
  * key (`names.ts`) — and the cell is the value, rendered by its parity type and
@@ -2042,247 +2325,6 @@ interface TrackedVersion {
2042
2325
  note: (version: number) => void;
2043
2326
  }
2044
2327
 
2045
- /** The four ways one saved view can be looked at (SCR-6). */
2046
- declare const VIEW_LAYOUTS: readonly ["grid", "kanban", "calendar", "gallery"];
2047
- type ViewLayout = (typeof VIEW_LAYOUTS)[number];
2048
- declare const LAYOUT_LABEL: Record<ViewLayout, string>;
2049
- /**
2050
- * What each layout needs before it can draw anything, in one sentence a screen
2051
- * prints. A layout that is missing its Field says so instead of rendering an
2052
- * empty board that looks like "no records".
2053
- */
2054
- declare const LAYOUT_NEEDS: Record<ViewLayout, string | null>;
2055
- /** One sort clause. The Field's key and a direction; nothing else is a sort. */
2056
- interface ViewSort {
2057
- field: string;
2058
- direction: "asc" | "desc";
2059
- }
2060
- /**
2061
- * THE DECLARATIVE SPEC. An agent writes this whole object; a person tweaks it.
2062
- * Every builder component in this package takes one of these as a prop.
2063
- */
2064
- interface SavedViewSpec {
2065
- name: string;
2066
- /** The Table this view looks at. */
2067
- subject: Uuid$1;
2068
- layout: ViewLayout;
2069
- /** Kanban: the Field whose value becomes a column. */
2070
- groupField?: string | null;
2071
- /**
2072
- * Kanban: a number Field to TOTAL under each column heading. It lives on the VIEW for
2073
- * the same reason the colours do — two people read one board two ways and neither
2074
- * reading is a fact about the records.
2075
- */
2076
- measure?: string | null;
2077
- /**
2078
- * Kanban: the Field whose values cut the board into SWIMLANES across the columns — a
2079
- * dispatcher's board by Status, one lane per Technician (S1-PRIME VIEW-KEYS). Never the
2080
- * column Field itself; the store refuses that.
2081
- */
2082
- swimlaneField?: string | null;
2083
- /** Kanban: the columns this view keeps shut to a strip, by the column's stored value. */
2084
- collapsedColumns?: string[];
2085
- /** Calendar: the Field that places a record in the month. */
2086
- dateField?: string | null;
2087
- /** Gallery: the Field shown large on the card. */
2088
- imageField?: string | null;
2089
- sorts?: ViewSort[];
2090
- /**
2091
- * The membership Rule. The STORE decides who is in this view; this is a
2092
- * reference to its Rule record, never a predicate this package evaluates.
2093
- */
2094
- ruleId?: Uuid$1 | null;
2095
- /**
2096
- * THE FLAT FILTER a digest or a notify rule wrote the view with — the one shape
2097
- * `custom.record_filter_sql` compiles, by Field KEY. A view saved in the bar has
2098
- * none; a view saved by `DigestScheduler` ("where Status is Open") does, and the
2099
- * grid and the board show exactly the records it names.
2100
- */
2101
- filters?: RecordFilter;
2102
- /**
2103
- * S2-PRIME — THE VIEW'S OWN QUESTION, as the condition builder writes it: the store's Rule
2104
- * shape `{op, args}`, ALL / ANY / NOT groups nested to any depth, Fields by id. Kept in the
2105
- * definition as `where` and asked of the store unchanged — the grid's list door, the board's
2106
- * headings and the dashboard's numbers compile it into the one WHERE clause
2107
- * (`custom.record_filter_sql`). Never stored under `filters`: that key is the flat map the
2108
- * digests and the notifier read, and they would refuse it by name.
2109
- */
2110
- where?: RuleExpression | null;
2111
- isDefault?: boolean;
2112
- /**
2113
- * HOW THIS VIEW LOOKS — its colors, its per-column formats and its frozen
2114
- * column (`presentation.ts`).
2115
- *
2116
- * It lives on the VIEW and not on the Fields because two people read one
2117
- * table two ways and neither reading is a fact about the records; it lives on
2118
- * a RECORD, not a new column, because that is what makes it something an
2119
- * agent writes with the same `records` tool as everything else.
2120
- */
2121
- presentation?: GridPresentation;
2122
- }
2123
- /**
2124
- * A saved view as it comes back out of the store, with its row's own id.
2125
- *
2126
- * It carries no version: `custom.view_declare` MERGES one key at a time, so two
2127
- * tabs pressing two different controls on one view both stand, and one control
2128
- * pressed in both keeps the latest — how Airtable and Notion keep a view change.
2129
- */
2130
- interface SavedView extends SavedViewSpec {
2131
- id: Uuid$1;
2132
- /** Where the view came from when it was moved in (the mover, the S0 repair), else null. */
2133
- movedFrom: Record<string, unknown> | null;
2134
- /**
2135
- * THE WAY THIS TABLE IS MEANT TO BE LOOKED AT, when somebody decided one — a layout id the
2136
- * host draws (`sheet`), read off the stored kind before `layout` folds it to the four the
2137
- * package draws. Null when nobody designated one. See `designatedLayoutOf`.
2138
- */
2139
- designatedLayout?: string | null;
2140
- }
2141
- /**
2142
- * WHICH LAYOUT A VIEW WAS DESIGNATED, OR NULL (lane DATA-V2-FACE, owner 2026-09-24).
2143
- *
2144
- * "For something like my data tables, which are predetermined with a default view of 'Sheet'
2145
- * I want to see … it just shows them the way it should in the default view. That's how you take
2146
- * custom systems and fake like they're hard-coded builtin systems."
2147
- *
2148
- * A designation is a DELIBERATE write, never a click: a view whose stored kind is a layout the
2149
- * HOST draws (`sheet` — a layout the store's view-keys registry declares, and one the layout
2150
- * buttons never write onto a view). That is the ONE way a table is designated (lane
2151
- * VIEW-KEYS-SHEET): the mover and the repair write `layout: "sheet"` on a copied older table's
2152
- * default view; `moved_from` says only where a view came from and designates nothing. A package
2153
- * kind (`grid`, `kanban`, …) is not a designation: picking Kanban writes `layout: "kanban"` onto
2154
- * the view, and a click must never take the layout chooser away.
2155
- */
2156
- declare function designatedLayoutOf(definition: Record<string, unknown> | null | undefined): string | null;
2157
- /**
2158
- * HOW THE TABLE OPENS, decided once from its default view and the layouts the host draws.
2159
- *
2160
- * · `designated` — the default view names a host layout this host registered: the page opens
2161
- * IN it and offers no other layout by default (the others sit in the page's one menu);
2162
- * · `chooser` — nothing designated (or the host does not draw what was designated): the
2163
- * page opens on the view's own layout and shows the layout chooser.
2164
- *
2165
- * Only the DEFAULT view designates: a person who opens another saved view is choosing, and the
2166
- * chooser is theirs.
2167
- */
2168
- type TableOpening = {
2169
- kind: "designated";
2170
- hostLayout: string;
2171
- } | {
2172
- kind: "chooser";
2173
- };
2174
- declare function tableOpening(view: Pick<SavedView, "isDefault" | "designatedLayout"> | null | undefined, hostLayoutIds: readonly string[]): TableOpening;
2175
- /**
2176
- * THE TABLE'S DEFAULT VIEW WHEN MORE THAN ONE SAYS IT IS (lane DATA-V2-FACE-2).
2177
- *
2178
- * Two views can both claim the default: the mover's designated one (the older table's own, the
2179
- * Sheet) and a page seed that a later move of the view bar's older store brings in beside it —
2180
- * both named "All records", so the store's name order cannot tell them apart. A designation is a
2181
- * deliberate write and a seed is not, so the designated default wins; then the first default.
2182
- */
2183
- declare function defaultViewOf<V extends Pick<SavedView, "isDefault" | "designatedLayout">>(views: readonly V[]): V | undefined;
2184
- /**
2185
- * WHICH FIELD EACH LAYOUT CAN BE DRAWN FROM, decided from the FIELD'S OWN
2186
- * DECLARATION and never from the values it happens to hold.
2187
- *
2188
- * A kanban makes columns from a choice: the Field whose value is one of a
2189
- * closed set (FLD-5 / FLD-6 — a pick-list is a Table, and a select stores the
2190
- * id of one of its records). Grouping by a free-text Field produces one column
2191
- * per record, which is not a board. A calendar places a record in time, so it
2192
- * needs a Field that holds a date.
2193
- *
2194
- * These two answers are here, beside the layouts, because the picker on the
2195
- * screen and any agent writing a view spec must agree about what is offerable.
2196
- */
2197
- declare const LAYOUT_FIELD_KIND: Record<ViewLayout, "choice" | "date" | null>;
2198
- /**
2199
- * ─────────────────────────────────────────────────────────────────────────────
2200
- * WHICH VIEW A LINK NAMES — the address bar as a first-class control.
2201
- *
2202
- * 🚨 THE DEFECT THIS CLOSES (lane FRONT-DOOR, 2026-09-21; VERIFIER-8 HIGH-2).
2203
- * `/data-v2/<table>?view=kanban` returned 200 and rendered THE GRID. The
2204
- * switcher was there, the four buttons worked, and the parameter was read by
2205
- * nobody — so a person who bookmarked a board, or sent one to a colleague, got
2206
- * a grid, and nothing on the screen said the view had been dropped. A link that
2207
- * does not land is the inbox-link defect (`activeRecordId`) and the dashboard
2208
- * defect (`activeDashboardId`) for the third time, so this is the same shape of
2209
- * fix: the URL names it, the page opens on it, and the URL FOLLOWS the person
2210
- * when they pick another.
2211
- *
2212
- * The dashboards are a fifth value and not a special case: to the person, "the
2213
- * dashboard" is one of the ways to look at this table, and a link to it has to
2214
- * work like a link to the board.
2215
- */
2216
- /**
2217
- * 🚨 `archived` IS THE SIXTH VALUE, AND IT IS NOT A SPECIAL CASE EITHER (lane
2218
- * FIX-10A-ARCHIVE, 2026-09-22; VERIFIER-10 F4).
2219
- *
2220
- * Archiving a record used to be a ONE-WAY DOOR: the grid's row action soft-
2221
- * deletes correctly — `deleted_at` set, version bumped, nothing removed — and
2222
- * then no screen anywhere in the platform could bring it back. The store's
2223
- * `custom.record_restore` was there the whole time and did exactly the right
2224
- * thing; nothing reached it, because every read door answers live rows only.
2225
- *
2226
- * So the archive is a WAY OF LOOKING AT THIS TABLE, like the board and the
2227
- * dashboards, reached by the same row of buttons and named by the same
2228
- * `?view=` parameter — never a Trash area of the app somebody has to know
2229
- * exists, and never a per-table screen. Every Table inherits it the moment it
2230
- * is in this list.
2231
- */
2232
- declare const PAGE_VIEWS: readonly ["grid", "kanban", "calendar", "gallery", "dashboards", "archived"];
2233
- type PageView = (typeof PAGE_VIEWS)[number];
2234
- declare const PAGE_VIEW_LABEL: Record<PageView, string>;
2235
- /**
2236
- * What a `?view=` value means, or `null` when it means nothing here.
2237
- *
2238
- * `null` is NOT "show the grid": the caller is expected to say that the link
2239
- * named something this table has no view for, because silently substituting a
2240
- * different view is exactly the defect above wearing a parser.
2241
- */
2242
- declare function pageViewFromParam(raw: string | null | undefined): PageView | null;
2243
- /** The sentence a link that named a view nobody has says. Never silent. */
2244
- declare function unknownViewLine(raw: string): string;
2245
- /** The one sentence a layout says when the table holds no Field it could use. */
2246
- declare const LAYOUT_NO_FIELD: Record<ViewLayout, string>;
2247
- /**
2248
- * THE SPEC AS THE ONE DEFINITION `platform.saved_view` HOLDS — the keys
2249
- * `custom.view_declare` documents, by Field KEY. `subject` is not in it: the door
2250
- * takes the Table as its own argument and writes `table_id` itself.
2251
- */
2252
- declare function viewDocument(spec: SavedViewSpec): Record<string, unknown>;
2253
- /**
2254
- * One row of `custom.views` back into the spec a screen and an agent both read — THROUGH THE ONE
2255
- * ACCESSOR (`viewSettings`, viewKeys.ts), so every layout sees every declared key the same way.
2256
- */
2257
- declare function viewFromRow(row: SavedViewRow): SavedView;
2258
- /**
2259
- * SAVE ONE NEW VIEW of a Table — the bar's "New view" and an agent's seed spec.
2260
- * One `view_declare` with no `view_id`; the answer is the new view's id.
2261
- */
2262
- declare function declareView(client: Pick<RecordsClient, "viewDeclare">, spec: SavedViewSpec): Promise<RecordsResult<Uuid$1>>;
2263
- /**
2264
- * WRITE A CHANGE ONTO A SAVED VIEW, AND KEEP IT.
2265
- *
2266
- * `custom.view_declare` with the view's id MERGES: only the keys in `patch` are
2267
- * written, a key cleared to null is removed, and everything the patch did not
2268
- * name — the grid's own choices, a hand-set order, where the view was moved from,
2269
- * a colour set in another tab — stays exactly as it was (S0, 2026-09-23: the door
2270
- * used to rebuild the whole definition from what one press sent).
2271
- *
2272
- * There is no version to be stale against (VERIFIER-15 M4 is closed by the shape,
2273
- * not by a retry): a saved view is a person's own arrangement of a table, and
2274
- * their latest press on a control is the one that stands. A refusal — the view
2275
- * was removed, or belongs to a table this person cannot open — is handed back for
2276
- * the page to say through the one notice.
2277
- */
2278
- declare function saveViewPatch(client: Pick<RecordsClient, "viewDeclare">, tableId: Uuid$1, viewId: Uuid$1, patch: Partial<SavedViewSpec>): Promise<RecordsResult<Uuid$1>>;
2279
- /**
2280
- * A PARTIAL change to a saved view, as the definition keys the door merges. Only
2281
- * the keys the caller actually changed are sent; a Field cleared (a board with no
2282
- * "Group by") is sent as `null`, which the door removes.
2283
- */
2284
- declare function viewPatchDocument(patch: Partial<SavedViewSpec>): Record<string, unknown>;
2285
-
2286
2328
  /** Every surface a saved view is drawn by. `sheet` is the spreadsheet surface (G-lanes). */
2287
2329
  type ViewKeyLayout = "grid" | "kanban" | "calendar" | "gallery" | "sheet";
2288
2330
  /** Who may write a key: anyone who may save the view, an input the door rewrites, or the server only. */
@@ -2405,11 +2447,24 @@ interface ViewSwitcherProps {
2405
2447
  chooser?: boolean | undefined;
2406
2448
  /** Handed to a host layout's `render` — the page's own export. */
2407
2449
  openExport?: (() => void) | undefined;
2450
+ /**
2451
+ * THE SAVED VIEW'S OWN ID, when the view is a stored one (lane GRID-MANUAL). A hand-set order
2452
+ * lives on the view's row, so the grid reads it (`custom.read_records_in_view_order`) and writes
2453
+ * it (`custom.view_record_order_set`, Reorder → Save) through it. Left out — a literal spec —
2454
+ * the view has no hand order and the grid offers no Reorder.
2455
+ */
2456
+ viewId?: Uuid | null | undefined;
2457
+ /**
2458
+ * The rows were placed by hand: the store made this view manual and removed its sort. The host
2459
+ * updates the view it holds (nothing is written — the store already did).
2460
+ */
2461
+ onOrderPlaced?: (() => void) | undefined;
2408
2462
  className?: string | undefined;
2409
2463
  }
2410
2464
  /** The records this view holds: its Rule's members when it names one, the table otherwise. */
2411
- declare function useViewRecords(view: SavedViewSpec, pageSize?: number, filter?: StoreFilter): {
2465
+ declare function useViewRecords(view: SavedViewSpec, pageSize?: number, filter?: StoreFilter, viewId?: Uuid | null): {
2412
2466
  rows: ReadRow$1[];
2467
+ manual: boolean;
2413
2468
  loading: boolean;
2414
2469
  error: RecordsError | null;
2415
2470
  /**
@@ -2426,7 +2481,7 @@ declare function useViewRecords(view: SavedViewSpec, pageSize?: number, filter?:
2426
2481
  */
2427
2482
  membership: string;
2428
2483
  };
2429
- declare function ViewSwitcher({ view, onLayoutChange, onViewChange, onOpenRecord, onMoved, onNewRecordForm, onAskWhoChanged, pageSize, titleField, filter, hostLayout, onHostLayoutChange, chooser, openExport, className, }: ViewSwitcherProps): react.JSX.Element;
2484
+ declare function ViewSwitcher({ view, onLayoutChange, onViewChange, onOpenRecord, onMoved, onNewRecordForm, onAskWhoChanged, pageSize, titleField, filter, hostLayout, onHostLayoutChange, chooser, openExport, viewId, onOrderPlaced, className, }: ViewSwitcherProps): react.JSX.Element;
2430
2485
 
2431
2486
  /** What the screen says above the list, so nobody has to be told twice that nothing was destroyed. */
2432
2487
  declare const ARCHIVE_MEANS = "Archiving never destroys anything. A record you remove from the grid waits here until somebody brings it back.";