@ai-matrx/records-ui 0.5.4 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  import * as react from 'react';
2
2
  import { ReactNode } from 'react';
3
- import { Uuid as Uuid$1, Table, RecordsError, Field as Field$1, RecordDocument, ValueEnvelope, ParityFieldType, ReadRow as ReadRow$1, RuleExpression, AggregateBucket, AggregateMeasure, DocTemplateRow, DocRenderRow, AnonTokenBinding, RecordsConfig, RecordsActor, RecordsDataSource } from '@ai-matrx/records';
4
- import { Uuid, Field, ReadRow } from '@ai-matrx/records/react';
5
- import { MatrxColumnDef } from '@ai-matrx/design-system/data-table/types';
3
+ import { Uuid as Uuid$1, Table, Field, RecordDocument, ReadRow, RecordsError, WriteConflict, ValueEnvelope, ParityFieldType, RuleExpression, AggregateBucket, AggregateMeasure, DocTemplateRow, DocRenderRow, AnonTokenBinding, RecordsConfig, RecordsActor, RecordsDataSource } from '@ai-matrx/records';
4
+ import { Uuid, Field as Field$1, ReadRow as ReadRow$1 } from '@ai-matrx/records/react';
6
5
  import { RecordsClient } from '@ai-matrx/records/core';
6
+ import { MatrxColumnDef } from '@ai-matrx/design-system/data-table/types';
7
7
 
8
8
  /** One named piece of grounding, shaped the way the platform's context slice takes them. */
9
9
  interface RecordChatEntry {
@@ -136,6 +136,21 @@ interface RecordsUiHost {
136
136
  id: Uuid$1;
137
137
  name: string;
138
138
  }>>;
139
+ /**
140
+ * OPTIONAL. WHO IS IN THIS ORGANIZATION (FLD-11 `member`).
141
+ *
142
+ * A person field must offer PEOPLE. Who the people are is the platform's
143
+ * answer — organization membership lives in `iam`, not in the record store —
144
+ * and the store has no door onto it, so this is a port rather than a read
145
+ * this package invents. `PersonPicker` resolves whoever is picked to their
146
+ * record in the kernel Person Table, which is what the Field actually stores.
147
+ *
148
+ * Unbound, a person question is ABSENT with `NO_MEMBERS_REASON`. It never
149
+ * falls back to listing the kernel Person Table's contents: that Table also
150
+ * holds the Homes this package writes, and offering "Comments Home" as a
151
+ * person is exactly what this port exists to stop.
152
+ */
153
+ members?: () => Promise<OrganizationMember[]>;
139
154
  /**
140
155
  * OPTIONAL. THE HOST'S OWN CHAT SURFACE (SCR-26).
141
156
  *
@@ -174,6 +189,15 @@ interface RecordsUiHost {
174
189
  */
175
190
  captureQueue?: CaptureQueuePort;
176
191
  }
192
+ /** One member of the organization, in the words a person reads. */
193
+ interface OrganizationMember {
194
+ /** The platform's user id. This is what a Person record carries as `user_id`. */
195
+ userId: string;
196
+ /** Their display name, when the platform has one. */
197
+ name?: string | null;
198
+ email?: string | null;
199
+ avatarUrl?: string | null;
200
+ }
177
201
  /** The durable half of offline capture: read the queue, write it, that is all. */
178
202
  interface CaptureQueuePort {
179
203
  load: () => Promise<PendingCapture[]>;
@@ -197,6 +221,8 @@ interface PendingCapture {
197
221
  }
198
222
  /** The sentence the notify editor shows when no saved-view port is bound. */
199
223
  declare const NO_SAVED_VIEWS_REASON: string;
224
+ /** The sentence a person question shows when no membership port is bound. */
225
+ declare const NO_MEMBERS_REASON: string;
200
226
  /** The sentence an attachment question shows when no file store is bound. */
201
227
  declare const NO_UPLOAD_REASON: string;
202
228
  declare function RecordsUiProvider({ value, children }: {
@@ -207,6 +233,126 @@ declare function useRecordsUi(): RecordsUiHost;
207
233
  /** The rights for one table — `NO_RIGHTS`, with its reason, when nothing is bound. */
208
234
  declare function useTableRights(table: Table | null | undefined): TableRights;
209
235
 
236
+ /**
237
+ * A token (`records_ui_view`, `long_text`, `v78_widget2`) as a person would
238
+ * write it. Used ONLY as a last resort, when the thing carries no label at all
239
+ * — a raw token on a screen is the defect, and a humanised token is the least
240
+ * bad honest answer, because it still names the same thing.
241
+ */
242
+ declare function humanize(token: string): string;
243
+ /** The name of one Field, as a header, a form label or a peek's term. */
244
+ declare function fieldName(field: Field): string;
245
+ /** The name of one Table, as a card, a heading or a breadcrumb. */
246
+ declare function tableName(table: Table): string;
247
+ /**
248
+ * The name of one RECORD, for a chip, a card, a relation cell or a peek title.
249
+ *
250
+ * `titleKey` is the Table's own `title_field` when the caller knows the Table —
251
+ * that is the store's answer to "what makes this record a chip" (REC-2) — and
252
+ * the conventional keys are tried after it. A record with nothing readable is
253
+ * NOT shown as a truncated uuid: it says it has no name yet, because "8f2c1b0a"
254
+ * tells a person nothing and looks like a bug.
255
+ */
256
+ declare function recordName(document: RecordDocument | undefined, titleKey?: string | null, fallback?: string): string;
257
+ /** The same question for a row the read door returned. */
258
+ declare function rowName(row: ReadRow | undefined, titleKey?: string | null, fallback?: string): string;
259
+
260
+ interface PersonPickerProps {
261
+ field: Field;
262
+ value: unknown;
263
+ onChange: (value: unknown) => void;
264
+ disabled?: boolean | undefined;
265
+ id?: string | undefined;
266
+ className?: string | undefined;
267
+ }
268
+ /** A Person record in the kernel Table, as this picker needs to read it. */
269
+ interface PersonRecord {
270
+ id: Uuid$1;
271
+ userId: string | null;
272
+ name: string;
273
+ }
274
+ /** The member's own name, as a person reads it. Never a uuid. */
275
+ declare function memberName(member: OrganizationMember): string;
276
+ /**
277
+ * Find (or write) the kernel Person record for one member. Exported because
278
+ * an agent filling a person field server-side needs exactly the same answer,
279
+ * and two implementations of "which record is this person" is two answers.
280
+ */
281
+ declare function personRecordForMember(client: RecordsClient, personTableId: Uuid$1, people: PersonRecord[], member: OrganizationMember): Promise<{
282
+ ok: true;
283
+ data: Uuid$1;
284
+ } | {
285
+ ok: false;
286
+ error: RecordsError;
287
+ }>;
288
+ declare function PersonPicker({ field, value, onChange, disabled, id, className }: PersonPickerProps): react.JSX.Element;
289
+
290
+ /** Which cell is open. A grid opens exactly one at a time, like every other grid. */
291
+ interface CellAddress {
292
+ rowId: Uuid$1;
293
+ key: string;
294
+ }
295
+ /** What one cell is doing right now, so nothing about it is guessed by a caller. */
296
+ type CellState = "clean" | "saving" | "refused";
297
+ interface CellRefusal {
298
+ error: RecordsError;
299
+ /** Present when the store said `PT409`: the other person's value, from the store. */
300
+ conflict: WriteConflict | null;
301
+ /** The value that was being written, so Retry can write it again. */
302
+ attempted: unknown;
303
+ }
304
+ /**
305
+ * FLD-9. A computed Field is not editable and never will be — the store works
306
+ * it out. Saying so here, once, keeps every caller from re-deciding it.
307
+ */
308
+ declare function fieldIsEditable(field: Field): boolean;
309
+ interface GridEditing {
310
+ /** The value to DRAW for one cell — the optimistic one when there is one. */
311
+ valueFor: (row: ReadRow, field: Field) => unknown;
312
+ editing: CellAddress | null;
313
+ stateOf: (rowId: Uuid$1, key: string) => CellState;
314
+ refusalOf: (rowId: Uuid$1, key: string) => CellRefusal | null;
315
+ begin: (address: CellAddress) => void;
316
+ cancel: () => void;
317
+ /** Commit the open cell. `move` opens the next/previous editable cell after it. */
318
+ commit: (value: unknown, move?: "next" | "previous" | null) => void;
319
+ /** Write the attempted value again, after re-reading the version. */
320
+ retry: (rowId: Uuid$1, key: string) => void;
321
+ /** Drop the edit and show what the other person wrote. */
322
+ keepTheirs: (rowId: Uuid$1, key: string) => void;
323
+ /** Soft delete, through the store's own door. Answers the store's refusal, or null. */
324
+ remove: (rowId: Uuid$1) => Promise<RecordsError | null>;
325
+ /** Write an empty record and open its first editable cell. */
326
+ addRecord: () => Promise<RecordsError | null>;
327
+ /** The last refusal from a delete or an add, for the grid to print. */
328
+ rowError: RecordsError | null;
329
+ clearRowError: () => void;
330
+ busy: boolean;
331
+ }
332
+ interface Options {
333
+ tableId: Uuid$1;
334
+ fields: Field[];
335
+ rows: ReadRow[];
336
+ /** Re-read the page once the store has accepted something. */
337
+ reload: () => void;
338
+ /** May this person write records at all? A read-only grid opens no editor. */
339
+ canWrite: boolean;
340
+ }
341
+ declare function useGridEditing({ tableId, fields, rows, reload, canWrite }: Options): GridEditing;
342
+ /**
343
+ * ONE CELL. Read, until somebody asks to change it; then the Field's own
344
+ * editor, in place, with the keys a person expects from a grid.
345
+ *
346
+ * `data-matrx-cell-control` is the data table's own opt-out from whole-row
347
+ * click, so opening a cell never also opens the record panel behind it.
348
+ */
349
+ declare function GridCell({ field, row, editing, canWrite, }: {
350
+ field: Field;
351
+ row: ReadRow;
352
+ editing: GridEditing;
353
+ canWrite: boolean;
354
+ }): react.JSX.Element;
355
+
210
356
  declare function RefusalNotice({ error, className, actions, }: {
211
357
  error: RecordsError;
212
358
  className?: string | undefined;
@@ -226,18 +372,30 @@ interface GridProps {
226
372
  onOpenRecord?: ((recordId: Uuid) => void) | undefined;
227
373
  /** SCR-2. Called when an admin clicks the "+" at the end of the headers. */
228
374
  onAddField?: (() => void) | undefined;
375
+ /**
376
+ * The host's whole-record form. Offered BY NAME when the store refuses an
377
+ * empty record — a Table with a required Field cannot be added to one cell at
378
+ * a time, and saying so beats a button that fails.
379
+ */
380
+ onNewRecordForm?: (() => void) | undefined;
229
381
  /** Extra toolbar content — the view switcher, the export menu, the view bar. */
230
382
  toolbarActions?: ReactNode | undefined;
383
+ /** Editing in place is on by default; a read-only surface (a portal) turns it off. */
384
+ editable?: boolean | undefined;
231
385
  className?: string | undefined;
232
386
  }
233
- declare function Grid({ tableId, pageSize, onOpenRecord, onAddField, toolbarActions, className, }: GridProps): react.JSX.Element;
234
- /** One Field becomes one column. The header is the Field's label; the cell is the value, rendered by its parity type. */
235
- declare function columnForField(field: Field): MatrxColumnDef<ReadRow>;
387
+ declare function Grid({ tableId, pageSize, onOpenRecord, onAddField, onNewRecordForm, toolbarActions, editable, className, }: GridProps): react.JSX.Element;
388
+ /**
389
+ * One Field becomes one column. The header is the Field's own NAME — never its
390
+ * key (`names.ts`) — and the cell is the value, rendered by its parity type and
391
+ * editable in place when an editing session is passed.
392
+ */
393
+ declare function columnForField(field: Field$1, editing?: GridEditing | null): MatrxColumnDef<ReadRow$1>;
236
394
 
237
395
  interface ExportMenuProps {
238
396
  tableId: Uuid;
239
397
  /** The rows on screen. Absent means "this table's first page", read the same way. */
240
- rows?: ReadRow[] | undefined;
398
+ rows?: ReadRow$1[] | undefined;
241
399
  /** What the file is called. The table's name by default. */
242
400
  label?: string | undefined;
243
401
  className?: string | undefined;
@@ -276,7 +434,7 @@ declare function CustomFieldsSection({ tableId, entityToken, recordId, title, cl
276
434
  interface FieldEditorProps {
277
435
  tableId: Uuid$1;
278
436
  /** The Field being edited. Absent means a new one. */
279
- field?: Field$1 | undefined;
437
+ field?: Field | undefined;
280
438
  onSaved?: (() => void) | undefined;
281
439
  onCancel?: (() => void) | undefined;
282
440
  className?: string | undefined;
@@ -286,7 +444,7 @@ declare function FieldEditor({ tableId, field, onSaved, onCancel, className }: F
286
444
  interface FieldProposalRow {
287
445
  id: string;
288
446
  /** What the agent proposed, in the Field shape the store would store. */
289
- field: Partial<Field> & {
447
+ field: Partial<Field$1> & {
290
448
  key: string;
291
449
  label?: string;
292
450
  };
@@ -305,9 +463,14 @@ interface TableSettingsProps {
305
463
  proposals?: FieldProposalRow[] | undefined;
306
464
  onAcceptProposal?: ((proposal: FieldProposalRow) => void) | undefined;
307
465
  onRejectProposal?: ((proposal: FieldProposalRow) => void) | undefined;
466
+ /**
467
+ * SCR-3's fourth verb. Called after the store has accepted the delete, so the
468
+ * host can leave a page that is now about a table nobody can open.
469
+ */
470
+ onDeleted?: (() => void) | undefined;
308
471
  className?: string | undefined;
309
472
  }
310
- declare function TableSettings({ tableId, proposals, onAcceptProposal, onRejectProposal, className, }: TableSettingsProps): react.JSX.Element;
473
+ declare function TableSettings({ tableId, proposals, onAcceptProposal, onRejectProposal, onDeleted, className, }: TableSettingsProps): react.JSX.Element;
311
474
 
312
475
  interface PeekProps {
313
476
  tableId: Uuid;
@@ -330,7 +493,7 @@ interface RecordFormProps {
330
493
  declare function RecordForm({ tableId, recordId, recordType, onSaved, onCancel, className, }: RecordFormProps): react.JSX.Element;
331
494
 
332
495
  interface RelationPickerProps {
333
- field: Field$1;
496
+ field: Field;
334
497
  value: unknown;
335
498
  onChange: (value: unknown) => void;
336
499
  disabled?: boolean | undefined;
@@ -340,7 +503,7 @@ interface RelationPickerProps {
340
503
  declare function RelationPicker({ field, value, onChange, disabled, id, className }: RelationPickerProps): react.JSX.Element;
341
504
 
342
505
  interface FieldEditorControlProps {
343
- field: Field$1;
506
+ field: Field;
344
507
  value: unknown;
345
508
  onChange: (value: unknown) => void;
346
509
  disabled?: boolean | undefined;
@@ -348,7 +511,7 @@ interface FieldEditorControlProps {
348
511
  }
349
512
  /** The label row every editor shares: one line, the label, the unit, the required mark. */
350
513
  declare function FieldLabel({ field, htmlFor, children }: {
351
- field: Field$1;
514
+ field: Field;
352
515
  htmlFor: string;
353
516
  children?: ReactNode;
354
517
  }): react.JSX.Element;
@@ -362,9 +525,9 @@ declare function RecordChip({ title, onRemove, className, }: {
362
525
 
363
526
  /** The envelope for one key, when the document carries one. */
364
527
  declare function envelopeFor(document: RecordDocument | undefined, key: string): ValueEnvelope | undefined;
365
- declare function renderValue(field: Field$1, value: unknown, document?: RecordDocument): react.JSX.Element;
528
+ declare function renderValue(field: Field, value: unknown, document?: RecordDocument): react.JSX.Element;
366
529
  /** The text of one scalar, with the Field's UNIT and FORMAT applied — FLD-N-1 says both change the MEANING. */
367
- declare function scalarText(field: Field$1, value: unknown): string;
530
+ declare function scalarText(field: Field, value: unknown): string;
368
531
  /** The provenance badge a value carries when the store interned a source for it (SCR-27). */
369
532
  declare function ProvenanceBadge({ document, fieldKey }: {
370
533
  document: RecordDocument | undefined;
@@ -375,16 +538,16 @@ declare const PARITY_LABEL: Record<ParityFieldType, string>;
375
538
  /** The "made of" sentence the store itself publishes, for the field editor's help text. */
376
539
  declare const PARITY_MADE_OF: Record<string, string>;
377
540
  /** Plain text and plain number are behaviours with no parity type of their own. */
378
- type EditorKind = ParityFieldType | "relation" | "text" | "number" | "boolean" | "date" | "long_text";
541
+ type EditorKind = ParityFieldType | "relation" | "text" | "number" | "boolean" | "date" | "json" | "long_text";
379
542
  /**
380
543
  * Which editor one Field wants. Reads the Field's own declaration only — the
381
544
  * behaviour, the modifiers, the format, the relation target and the config —
382
545
  * because those are what the store stores, and a screen that guessed from a
383
546
  * value would draw a different editor for an empty record.
384
547
  */
385
- declare function editorKindFor(field: Field$1): EditorKind;
548
+ declare function editorKindFor(field: Field): EditorKind;
386
549
  /** The type word a person sees in a header or a settings row. */
387
- declare function fieldTypeLabel(field: Field$1): string;
550
+ declare function fieldTypeLabel(field: Field): string;
388
551
 
389
552
  /** One Field of a package-owned Table, in the store's own words. */
390
553
  interface SystemFieldSpec {
@@ -421,6 +584,31 @@ interface UseSystemTableState {
421
584
  }
422
585
  /** The hook every package-owned component opens with. */
423
586
  declare function useSystemTable(spec: SystemTableSpec): UseSystemTableState;
587
+ /**
588
+ * THE VERSION OF THE ONE RECORD BEING EDITED.
589
+ *
590
+ * The read door (`custom.read_records`) answers documents, never versions —
591
+ * deliberately, because a version is a fact about the row and not about what
592
+ * this reader may see. A screen that saves optimistically still needs the
593
+ * version it LOADED, so it reads it here, for the one record it is editing and
594
+ * never for a list, through `custom.io_revisions`.
595
+ *
596
+ * `null` is an honest answer: the save then carries no expected version and is
597
+ * the store's ordinary last-writer-wins write. It is never a guess.
598
+ */
599
+ /** What `useRecordVersion` hands a screen that writes the record more than once. */
600
+ interface TrackedVersion {
601
+ /** The version this screen is writing against, or `null` when none was read. */
602
+ version: number | null;
603
+ /**
604
+ * TELL IT THE STORE MOVED. Every successful write answers the record's NEW
605
+ * version; a screen that does not hand it back here will write against the
606
+ * old one next time and the store will — correctly — refuse it as a conflict
607
+ * with somebody who turns out to be the same person. Measured 2026-09-19:
608
+ * switching a saved view's layout twice in a row did exactly that.
609
+ */
610
+ note: (version: number) => void;
611
+ }
424
612
 
425
613
  /** The four ways one saved view can be looked at (SCR-6). */
426
614
  declare const VIEW_LAYOUTS: readonly ["grid", "kanban", "calendar", "gallery"];
@@ -473,10 +661,32 @@ interface SavedView extends SavedViewSpec {
473
661
  }
474
662
  /** The package-owned Table every saved view is a record of. */
475
663
  declare const VIEW_TABLE: SystemTableSpec;
664
+ /**
665
+ * WHICH FIELD EACH LAYOUT CAN BE DRAWN FROM, decided from the FIELD'S OWN
666
+ * DECLARATION and never from the values it happens to hold.
667
+ *
668
+ * A kanban makes columns from a choice: the Field whose value is one of a
669
+ * closed set (FLD-5 / FLD-6 — a pick-list is a Table, and a select stores the
670
+ * id of one of its records). Grouping by a free-text Field produces one column
671
+ * per record, which is not a board. A calendar places a record in time, so it
672
+ * needs a Field that holds a date.
673
+ *
674
+ * These two answers are here, beside the layouts, because the picker on the
675
+ * screen and any agent writing a view spec must agree about what is offerable.
676
+ */
677
+ declare const LAYOUT_FIELD_KIND: Record<ViewLayout, "choice" | "date" | null>;
678
+ /** The one sentence a layout says when the table holds no Field it could use. */
679
+ declare const LAYOUT_NO_FIELD: Record<ViewLayout, string>;
476
680
  /** The spec as the one document the store holds. */
477
681
  declare function viewDocument(spec: SavedViewSpec): Record<string, unknown>;
478
682
  /** One stored record back into the spec a screen and an agent both read. */
479
- declare function viewFromRecord(row: ReadRow$1): SavedView;
683
+ declare function viewFromRecord(row: ReadRow): SavedView;
684
+ /**
685
+ * A PARTIAL change to a saved view, as the patch the store takes. Only the keys
686
+ * the caller actually changed are written — a full document would re-write a
687
+ * name or a rule the person edited in another tab.
688
+ */
689
+ declare function viewPatchDocument(patch: Partial<SavedViewSpec>): Record<string, unknown>;
480
690
 
481
691
  interface ViewSwitcherProps {
482
692
  /**
@@ -487,19 +697,29 @@ interface ViewSwitcherProps {
487
697
  view: SavedViewSpec;
488
698
  /** Called when the person picks another layout, so the host can save it. */
489
699
  onLayoutChange?: ((layout: ViewLayout) => void) | undefined;
700
+ /**
701
+ * Called with whatever part of the view the person just changed — the layout,
702
+ * the Field the kanban groups by, the Field the calendar reads. The HOST
703
+ * saves it onto the view record, so the choice is remembered per saved view
704
+ * rather than per page load. Unbound, the pickers still work for this visit
705
+ * and the screen says the choice is not being kept.
706
+ */
707
+ onViewChange?: ((patch: Partial<SavedViewSpec>) => void) | undefined;
490
708
  onOpenRecord?: ((recordId: Uuid) => void) | undefined;
709
+ /** The host's whole-record form, offered by the grid when an empty record is refused. */
710
+ onNewRecordForm?: (() => void) | undefined;
491
711
  pageSize?: number | undefined;
492
712
  className?: string | undefined;
493
713
  }
494
714
  /** The records this view holds: its Rule's members when it names one, the table otherwise. */
495
715
  declare function useViewRecords(view: SavedViewSpec, pageSize?: number): {
496
- rows: ReadRow[];
716
+ rows: ReadRow$1[];
497
717
  loading: boolean;
498
718
  error: RecordsError | null;
499
719
  /** Where membership came from, in a sentence. A screen that hides this is hiding the view's meaning. */
500
720
  membership: string;
501
721
  };
502
- declare function ViewSwitcher({ view, onLayoutChange, onOpenRecord, pageSize, className }: ViewSwitcherProps): react.JSX.Element;
722
+ declare function ViewSwitcher({ view, onLayoutChange, onViewChange, onOpenRecord, onNewRecordForm, pageSize, className, }: ViewSwitcherProps): react.JSX.Element;
503
723
 
504
724
  interface ViewBarProps {
505
725
  /** The Table whose views these are. */
@@ -701,7 +921,7 @@ declare const FORM_TABLE: SystemTableSpec;
701
921
  /** The spec as the one document the store holds. */
702
922
  declare function formDocument(spec: FormSpec): Record<string, unknown>;
703
923
  /** One stored record back into the spec a screen and an agent both read. */
704
- declare function formFromRecord(row: ReadRow$1): SavedForm;
924
+ declare function formFromRecord(row: ReadRow): SavedForm;
705
925
  /**
706
926
  * THE SOURCE STAMP every submission carries. A record that arrived through a
707
927
  * form is not indistinguishable from one somebody typed into the grid: it says
@@ -779,7 +999,7 @@ interface SavedDashboard extends DashboardSpec {
779
999
  /** The package-owned Table every dashboard is a record of. */
780
1000
  declare const DASHBOARD_TABLE: SystemTableSpec;
781
1001
  declare function dashboardDocument(spec: DashboardSpec): Record<string, unknown>;
782
- declare function dashboardFromRecord(row: ReadRow$1): SavedDashboard;
1002
+ declare function dashboardFromRecord(row: ReadRow): SavedDashboard;
783
1003
  /**
784
1004
  * The series palette. Six SEMANTIC tokens the design system already flips for
785
1005
  * dark mode, so a chart is correct in both themes without a second palette and
@@ -807,11 +1027,11 @@ interface DocTemplateSpec {
807
1027
  * `custom.doc_unresolved_tokens` names it and the screen shows the store's own
808
1028
  * reason — it is never quietly deleted from the body.
809
1029
  */
810
- declare function bodyFromKeys(body: string, fields: Field$1[]): string;
1030
+ declare function bodyFromKeys(body: string, fields: Field[]): string;
811
1031
  /** The reverse, for a person reading a stored body: ids shown as the Field's label. */
812
- declare function bodyForReading(body: string, fields: Field$1[]): string;
1032
+ declare function bodyForReading(body: string, fields: Field[]): string;
813
1033
  /** VAL-10: a signature is a Value on a text Field whose format is `signature`. */
814
- declare function isSignatureField(field: Field$1): boolean;
1034
+ declare function isSignatureField(field: Field): boolean;
815
1035
 
816
1036
  interface DocTemplateProps {
817
1037
  /** The Table these templates render. */
@@ -1076,8 +1296,8 @@ declare function personActor(userId: Uuid$1 | null | undefined): RecordsActor;
1076
1296
  */
1077
1297
  declare function recordsDataSource(client: object, fallbackSchema?: string): RecordsDataSource;
1078
1298
 
1079
- type TableLane = "mine" | "organization" | "system" | "community";
1080
- /** The four lanes, in the order a person reads them. Never a flat list. */
1299
+ type TableLane = "mine" | "organization" | "system" | "community" | "app";
1300
+ /** The five lanes, in the order a person reads them. Never a flat list. */
1081
1301
  declare const TABLE_LANES: readonly TableLane[];
1082
1302
  declare const LANE_TITLE: Record<TableLane, string>;
1083
1303
  /** What fills an empty lane. A heading with nothing under it must still teach. */
@@ -1102,9 +1322,51 @@ interface TablePageProps {
1102
1322
  * used — see why that default is not optional.
1103
1323
  */
1104
1324
  seedViews?: SavedViewSpec[] | undefined;
1325
+ /**
1326
+ * What a person does when the table is not theirs to see — switch
1327
+ * organization, go back to the list. Absent, the screen still SAYS what
1328
+ * happened; it just has no button to offer, which is honest and is never a
1329
+ * blank frame.
1330
+ */
1331
+ onLeave?: (() => void) | undefined;
1332
+ /** The word on that button. Defaults to "Back to your tables". */
1333
+ leaveLabel?: string | undefined;
1105
1334
  className?: string | undefined;
1106
1335
  }
1107
- declare function TablePage({ tableId, pageSize, seedViews, className }: TablePageProps): react.JSX.Element;
1336
+ /**
1337
+ * THE SENTENCE A TABLE YOU CANNOT SEE SAYS.
1338
+ *
1339
+ * Measured on the real screen (independent verdict, 19 September): opening a
1340
+ * table's page while another organization was active left the main area's text
1341
+ * "literally the empty string" after eighteen seconds — no sentence, no
1342
+ * spinner, no console error. A made-up id did the same. The cause is exactly
1343
+ * one line of this file: `custom.table_list` correctly answers the tables this
1344
+ * person can see in THIS organization, the table is not among them, so
1345
+ * `useTable` answers `{ data: null, error: null }` — not a refusal, just
1346
+ * absence — and the page rendered a skeleton for ever.
1347
+ *
1348
+ * Absence is an ANSWER and it gets a sentence. It deliberately does not say
1349
+ * whether the table exists: this person cannot see it, and which of the two it
1350
+ * is is not theirs to learn.
1351
+ */
1352
+ declare const TABLE_NOT_REACHABLE: string;
1353
+ /**
1354
+ * THE VIEW EVERY TABLE HAS, AND WHY IT IS NOT OPTIONAL.
1355
+ *
1356
+ * A view is a record, so a table nobody has saved a view for has none — and
1357
+ * this page then renders its own honest sentence ("Waiting for this table's
1358
+ * views") and NO RECORDS AT ALL. Measured on 2026-09-19 by walking the real
1359
+ * product: declare a table, write a record, open the table — and the record is
1360
+ * not on the screen, with the only remedy being a control the person has no
1361
+ * reason to press. That is a dead end on the first table anybody makes.
1362
+ *
1363
+ * So the page seeds one: every record, newest work first, as a grid. It is an
1364
+ * ordinary saved view record like any other — renamable, re-sortable,
1365
+ * deletable — not a hidden built-in mode, because a "special" default view is a
1366
+ * second kind of view that every feature after it has to know about.
1367
+ */
1368
+ declare const DEFAULT_VIEW_NAME = "All records";
1369
+ declare function TablePage({ tableId, pageSize, seedViews, onLeave, leaveLabel, className, }: TablePageProps): react.JSX.Element;
1108
1370
 
1109
1371
  /** One Field a person asked for, in the store's own behaviour words (FLD-1). */
1110
1372
  interface NewFieldSpec {
@@ -1137,6 +1399,18 @@ type DeclareResult = {
1137
1399
  /** `My Sales Pipeline` → `my_sales_pipeline`. A token, never a sentence. */
1138
1400
  declare function tokenFor(name: string): string;
1139
1401
  /** The one Field a Table cannot be declared without: something to call a record. */
1402
+ /**
1403
+ * WHY THE FIRST FIELD IS NOT REQUIRED.
1404
+ *
1405
+ * It was, and the cost showed up the moment the grid could add a row: "New
1406
+ * record" writes an empty record and puts you in the first cell, and the store
1407
+ * correctly refused every one of them — `REC-51: Title is required` — so the
1408
+ * one gesture that makes a grid feel like a grid never worked on a table a
1409
+ * person had just made. A title is what makes a record a CHIP (REC-2), which is
1410
+ * a fact about how it is shown, not a promise it can never be written without;
1411
+ * an organization that wants it demanded says so in the field editor, and the
1412
+ * store then enforces it for everyone.
1413
+ */
1140
1414
  declare const DEFAULT_FIELDS: NewFieldSpec[];
1141
1415
  declare function declareTable(client: RecordsClient, spec: NewTableSpec): Promise<DeclareResult>;
1142
1416
  /** Write Field records for an existing Table. Used by the create path and by import. */
@@ -1148,4 +1422,4 @@ declare function addFields(client: RecordsClient, tableId: Uuid$1, fields: NewFi
1148
1422
  error: RecordsError;
1149
1423
  }>;
1150
1424
 
1151
- export { ACTION_KINDS, ACTION_TABLE, ActionInbox, type ActionInboxProps, type ActionKind, BookingSlots, type BookingSlotsProps, CAPTURE_MODES, CHART_KINDS, CHART_KIND_LABEL, CHART_NEEDS, CHECKLIST_TABLE, COMMENT_TABLE, type CaptureMode, type CaptureQueuePort, CaptureSheet, type CaptureSheetProps, ChartBlock, type ChartBlockProps, type ChartKind, type ChartSpec, ChecklistRunner, type ChecklistRunnerProps, type ChecklistSpec, type ChecklistStepSpec, CommentThread, type CommentThreadProps, CustomFieldsSection, type CustomFieldsSectionProps, DASHBOARD_TABLE, DEFAULT_FIELDS, DashboardCanvas, type DashboardCanvasProps, type DashboardSpec, type DeclareResult, DocRender, type DocRenderProps, DocTemplate, type DocTemplateProps, type DocTemplateSpec, type EditorKind, EmbedFrame, type EmbedFrameProps, type EnrichAsk, type EnrichOutcome, EnrichPanel, type EnrichPanelProps, ExportMenu, type ExportMenuProps, FORM_FLOWS, FORM_TABLE, FieldControl, FieldEditor, type FieldEditorControlProps, type FieldEditorProps, FieldLabel, type FieldProposalRow, FormBuilder, type FormBuilderProps, type FormFlow, type FormQuestionSpec, FormRunner, type FormRunnerProps, type FormSpec, type FormTheme, Grid, type GridProps, HistoryPanel, type HistoryPanelProps, IN_MEMORY_QUEUE_REASON, ImportWizard, type ImportWizardProps, KERNEL_REASON, LANE_EMPTY, LANE_TITLE, LAYOUT_LABEL, LAYOUT_NEEDS, NO_CHAT_REASON, NO_ENRICH_REASON, NO_RIGHTS, NO_SAVED_VIEWS_REASON, NO_UPLOAD_REASON, type NewFieldSpec, type NewTableSpec, NotifyRuleEditor, type NotifyRuleEditorProps, type NotifyRuleSpec, PARITY_LABEL, PARITY_MADE_OF, Peek, type PeekProps, type PendingCapture, PortalShell, type PortalShellProps, type ProposalOutcome, ProposalRow, type ProposalRowProps, type ProposedChange, ProvenanceBadge, PublicViewPage, type PublicViewPageProps, type QueuedAction, type QueuedActionSpec, RecordChat, type RecordChatContext, type RecordChatEntry, type RecordChatProps, type RecordChatWithheld, RecordChip, RecordForm, type RecordFormProps, RecordsMount, type RecordsMountProps, type RecordsUiHost, RecordsUiProvider, RefusalLine, RefusalNotice, RelationPicker, type RelationPickerProps, SERIES_COLORS, STORE_DECIDES_REASON, type SavedDashboard, type SavedForm, type SavedView, type SavedViewSpec, SignBlock, type SignBlockProps, type SystemFieldSpec, type SystemTableSpec, TABLE_LANES, type TableLane, TablePage, type TablePageProps, type TableRights, TableSettings, type TableSettingsProps, TablesHome, type TablesHomeProps, type UseSystemTableState, VIEW_LAYOUTS, VIEW_TABLE, ViewBar, type ViewBarProps, type ViewLayout, type ViewSort, ViewSwitcher, type ViewSwitcherProps, actionDocument, addFields, bodyForReading, bodyFromKeys, columnForField, dashboardDocument, dashboardFromRecord, declareTable, editorKindFor, ensureSystemTable, envelopeFor, fieldToken, fieldTypeLabel, formDocument, formFromRecord, groupLabel, isSignatureField, laneFor, personActor, recordsDataSource, renderValue, scalarText, storeDecidesRights, submissionStamp, tokenFor, useEmbedHandshake, useRecordsUi, useSystemTable, useTableRights, useViewRecords, viewDocument, viewFromRecord };
1425
+ export { ACTION_KINDS, ACTION_TABLE, ActionInbox, type ActionInboxProps, type ActionKind, BookingSlots, type BookingSlotsProps, CAPTURE_MODES, CHART_KINDS, CHART_KIND_LABEL, CHART_NEEDS, CHECKLIST_TABLE, COMMENT_TABLE, type CaptureMode, type CaptureQueuePort, CaptureSheet, type CaptureSheetProps, type CellAddress, type CellRefusal, type CellState, ChartBlock, type ChartBlockProps, type ChartKind, type ChartSpec, ChecklistRunner, type ChecklistRunnerProps, type ChecklistSpec, type ChecklistStepSpec, CommentThread, type CommentThreadProps, CustomFieldsSection, type CustomFieldsSectionProps, DASHBOARD_TABLE, DEFAULT_FIELDS, DEFAULT_VIEW_NAME, DashboardCanvas, type DashboardCanvasProps, type DashboardSpec, type DeclareResult, DocRender, type DocRenderProps, DocTemplate, type DocTemplateProps, type DocTemplateSpec, type EditorKind, EmbedFrame, type EmbedFrameProps, type EnrichAsk, type EnrichOutcome, EnrichPanel, type EnrichPanelProps, ExportMenu, type ExportMenuProps, FORM_FLOWS, FORM_TABLE, FieldControl, FieldEditor, type FieldEditorControlProps, type FieldEditorProps, FieldLabel, type FieldProposalRow, FormBuilder, type FormBuilderProps, type FormFlow, type FormQuestionSpec, FormRunner, type FormRunnerProps, type FormSpec, type FormTheme, Grid, GridCell, type GridEditing, type GridProps, HistoryPanel, type HistoryPanelProps, IN_MEMORY_QUEUE_REASON, ImportWizard, type ImportWizardProps, KERNEL_REASON, LANE_EMPTY, LANE_TITLE, LAYOUT_FIELD_KIND, LAYOUT_LABEL, LAYOUT_NEEDS, LAYOUT_NO_FIELD, NO_CHAT_REASON, NO_ENRICH_REASON, NO_MEMBERS_REASON, NO_RIGHTS, NO_SAVED_VIEWS_REASON, NO_UPLOAD_REASON, type NewFieldSpec, type NewTableSpec, NotifyRuleEditor, type NotifyRuleEditorProps, type NotifyRuleSpec, type OrganizationMember, PARITY_LABEL, PARITY_MADE_OF, Peek, type PeekProps, type PendingCapture, PersonPicker, type PersonPickerProps, PortalShell, type PortalShellProps, type ProposalOutcome, ProposalRow, type ProposalRowProps, type ProposedChange, ProvenanceBadge, PublicViewPage, type PublicViewPageProps, type QueuedAction, type QueuedActionSpec, RecordChat, type RecordChatContext, type RecordChatEntry, type RecordChatProps, type RecordChatWithheld, RecordChip, RecordForm, type RecordFormProps, RecordsMount, type RecordsMountProps, type RecordsUiHost, RecordsUiProvider, RefusalLine, RefusalNotice, RelationPicker, type RelationPickerProps, SERIES_COLORS, STORE_DECIDES_REASON, type SavedDashboard, type SavedForm, type SavedView, type SavedViewSpec, SignBlock, type SignBlockProps, type SystemFieldSpec, type SystemTableSpec, TABLE_LANES, TABLE_NOT_REACHABLE, type TableLane, TablePage, type TablePageProps, type TableRights, TableSettings, type TableSettingsProps, TablesHome, type TablesHomeProps, type TrackedVersion, type UseSystemTableState, VIEW_LAYOUTS, VIEW_TABLE, ViewBar, type ViewBarProps, type ViewLayout, type ViewSort, ViewSwitcher, type ViewSwitcherProps, actionDocument, addFields, bodyForReading, bodyFromKeys, columnForField, dashboardDocument, dashboardFromRecord, declareTable, editorKindFor, ensureSystemTable, envelopeFor, fieldIsEditable, fieldName, fieldToken, fieldTypeLabel, formDocument, formFromRecord, groupLabel, humanize, isSignatureField, laneFor, memberName, personActor, personRecordForMember, recordName, recordsDataSource, renderValue, rowName, scalarText, storeDecidesRights, submissionStamp, tableName, tokenFor, useEmbedHandshake, useGridEditing, useRecordsUi, useSystemTable, useTableRights, useViewRecords, viewDocument, viewFromRecord, viewPatchDocument };