@ai-matrx/records-ui 0.3.1
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 +91 -0
- package/LICENSE +21 -0
- package/README.md +236 -0
- package/dist/index.cjs +5962 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1117 -0
- package/dist/index.d.ts +1117 -0
- package/dist/index.js +5986 -0
- package/dist/index.js.map +1 -0
- package/package.json +89 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# Changelog — @ai-matrx/records-ui
|
|
2
|
+
|
|
3
|
+
## 0.3.1
|
|
4
|
+
|
|
5
|
+
Ships the canonical `TablesHome`, `TablePage`, `RecordsMount`, and table-creation helpers
|
|
6
|
+
that were added after 0.3.0 was tagged. The real-store demo now shares the package-wide
|
|
7
|
+
verification lock, and the packed-artifact canary verifies `@ai-matrx/records-ui` itself
|
|
8
|
+
instead of accidentally re-verifying the headless `@ai-matrx/records` package.
|
|
9
|
+
|
|
10
|
+
**Consumer action:** replace temporary workspace or source references with
|
|
11
|
+
`@ai-matrx/records-ui@latest`; no API rename is required.
|
|
12
|
+
|
|
13
|
+
## 0.3.0
|
|
14
|
+
|
|
15
|
+
**The last eight, and the package is complete at thirty-one components.**
|
|
16
|
+
|
|
17
|
+
`RecordChat` (SCR-26) reuses the platform's ONE chat surface instead of shipping
|
|
18
|
+
a second: it builds the context — the record through the read door, each value
|
|
19
|
+
carrying its Field's unit and format, the masked fields named, the `secret` and
|
|
20
|
+
`on_demand` ones held back with the reason — and hands it to a new `chat` port
|
|
21
|
+
that matrx-frontend fills with `AgentConversationColumn`.
|
|
22
|
+
|
|
23
|
+
`EnrichPanel` (SCR-27) drives per-field AI fill off the Fields' own
|
|
24
|
+
`source = agent` and `review_interval_days`, dates each value from its own
|
|
25
|
+
envelope rather than the row, shows VAL-8 provenance on every value (including
|
|
26
|
+
which person an agent acted for), and sends the fill through the server's
|
|
27
|
+
`records` tool via a new `enrich` port — because a browser stamping itself
|
|
28
|
+
`agent` would be a browser claiming to be one.
|
|
29
|
+
|
|
30
|
+
`ChecklistRunner` (SCR-28) instantiates a declarative template as REAL RECORDS
|
|
31
|
+
in one act. `BookingSlots` (SCR-29) holds the slot before it takes the details
|
|
32
|
+
and lets the database decide a double-booking. `CaptureSheet` (SCR-30) is the
|
|
33
|
+
offline capture contract: the client mints the id before the first attempt and
|
|
34
|
+
`custom.anon_capture` deduplicates every replay onto the same record.
|
|
35
|
+
|
|
36
|
+
`PortalShell` (SCR-23), `PublicViewPage` (SCR-24) and `EmbedFrame` (SCR-25) are
|
|
37
|
+
the outsider's three surfaces, over `iam`'s external principal and publish
|
|
38
|
+
binding and over the embed token handshake.
|
|
39
|
+
|
|
40
|
+
New host ports: `chat`, `enrich`, `captureQueue`. Requires
|
|
41
|
+
`@ai-matrx/records` 0.3.0.
|
|
42
|
+
|
|
43
|
+
**A RACE EVERY SEED-TAKING COMPONENT HAD, closed as a class.** `DocTemplate`,
|
|
44
|
+
`ViewBar`, `FormBuilder`, `DashboardCanvas` and `ActionInbox` all read what
|
|
45
|
+
exists, worked out what was missing, and wrote it — from a loader whose identity
|
|
46
|
+
changes when its inputs settle. Against a slower store the second run read a
|
|
47
|
+
list the first was still writing into, and seeded the same thing twice: two
|
|
48
|
+
identical "Agreement" templates, two buttons with one name. A check against the
|
|
49
|
+
server's list is not a lock. `useSeedGuard` claims each seed name once per
|
|
50
|
+
mount, and all five now use it.
|
|
51
|
+
|
|
52
|
+
**Four more defects this wave found and closed.** `FormRunner` told every person that
|
|
53
|
+
a public form's link was "pending" for a door named `custom.anon_record_write`
|
|
54
|
+
that never existed, while the real anonymous lane had shipped — it now names
|
|
55
|
+
`custom.anon_write` and tells a person the true next step (issue an embed). And
|
|
56
|
+
the demo harness read a rehearsal copy that was missing a third of the doors
|
|
57
|
+
this package now calls; it reads the one live store, and stands its own table,
|
|
58
|
+
fields and records up rather than depending on rows somebody typed once. The
|
|
59
|
+
two vocabulary drifts behind `RecordChat`'s context rules — a Field's
|
|
60
|
+
sensitivity words and its context-policy words — were corrected in
|
|
61
|
+
`@ai-matrx/records` 0.3.0; `RecordChat` holds back `confidential` and
|
|
62
|
+
`restricted` values and FLD-12's `exclude` and `on_request` fields, and names
|
|
63
|
+
every one of them rather than letting an agent answer around a gap.
|
|
64
|
+
|
|
65
|
+
## 0.2.0
|
|
66
|
+
|
|
67
|
+
`DocTemplate`, `DocRender` and `SignBlock` (SCR-20/21/22): a template is saved
|
|
68
|
+
through `custom.doc_template_save`, a render is frozen once and printed through
|
|
69
|
+
`@ai-matrx/print`, and a signature is checked against the exact bytes that were
|
|
70
|
+
signed (`custom.doc_signature_intact`). A token the Table cannot answer is named
|
|
71
|
+
before anything is rendered.
|
|
72
|
+
|
|
73
|
+
Every component now reads through `@ai-matrx/records` 0.2.0's read door, so a
|
|
74
|
+
row is the store's masked document and a masked field is present-and-null with
|
|
75
|
+
its own reason. `useRecordVersion` reads the edited record's version from
|
|
76
|
+
`custom.io_revisions`, and `useSystemTable` adds Fields a package Table grew to
|
|
77
|
+
organizations that already hold a copy.
|
|
78
|
+
|
|
79
|
+
One store defect was found by wiring this and fixed on the main database:
|
|
80
|
+
`custom.read_record` refused records `custom.read_records` had just listed,
|
|
81
|
+
because the one-record door asked a narrower question than its own list sibling.
|
|
82
|
+
It now asks `iam.has_access_for`, the platform's one access question
|
|
83
|
+
(`matrx-frontend/migrations/campaign/w6_ui_the_read_door_asks_the_one_access_question.sql`).
|
|
84
|
+
|
|
85
|
+
## 0.1.0
|
|
86
|
+
|
|
87
|
+
First release: `Grid`, `RecordForm`, `FieldEditor`, `TableSettings`,
|
|
88
|
+
`RelationPicker`, `Peek`, `ImportWizard`, `ExportMenu` and
|
|
89
|
+
`CustomFieldsSection`, plus the `RecordsUiProvider` rights port and the shared
|
|
90
|
+
refusal surface. Every one of them is proven headlessly against the rehearsal
|
|
91
|
+
branch by `pnpm test`.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 AI Matrix Engine
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
# @ai-matrx/records-ui
|
|
2
|
+
|
|
3
|
+
THE canonical screens of the AI Matrx record store. Every component here reads
|
|
4
|
+
and writes ONLY through [`@ai-matrx/records`](../records/README.md), renders
|
|
5
|
+
with `@ai-matrx/design-system`'s own primitives and data table, and carries
|
|
6
|
+
`@ai-matrx/alchemy`'s menu where a person reaches for one.
|
|
7
|
+
|
|
8
|
+
There is no bespoke per-feature record UI in this platform. A feature that needs
|
|
9
|
+
a grid, a form, a picker or a peek mounts one of these — and any one usage is a
|
|
10
|
+
few lines.
|
|
11
|
+
|
|
12
|
+
## Bind once
|
|
13
|
+
|
|
14
|
+
```tsx
|
|
15
|
+
import { RecordsProvider } from "@ai-matrx/records/react";
|
|
16
|
+
import { RecordsUiProvider } from "@ai-matrx/records-ui";
|
|
17
|
+
import "@ai-matrx/design-system/styles.css";
|
|
18
|
+
|
|
19
|
+
<RecordsProvider config={{ dataSource: supabase, organizationId, actor: { actor: "user", user_id } }}>
|
|
20
|
+
<RecordsUiProvider value={{ rights: (table) => myRights(table), density: "condensed" }}>
|
|
21
|
+
{children}
|
|
22
|
+
</RecordsUiProvider>
|
|
23
|
+
</RecordsProvider>;
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
`rights` is the port that decides whether a person sees the administering
|
|
27
|
+
controls. Unbound, it answers "no" WITH A REASON, and every such control is
|
|
28
|
+
ABSENT — this package ships no disabled variant of any of them.
|
|
29
|
+
|
|
30
|
+
## Then, a few lines each
|
|
31
|
+
|
|
32
|
+
```tsx
|
|
33
|
+
import {
|
|
34
|
+
Grid, RecordForm, FieldEditor, TableSettings, RelationPicker,
|
|
35
|
+
Peek, ImportWizard, ExportMenu, CustomFieldsSection,
|
|
36
|
+
} from "@ai-matrx/records-ui";
|
|
37
|
+
|
|
38
|
+
<Grid tableId={id} onOpenRecord={setPeeking} onAddField={openSettings} /> // records as rows, fields as columns
|
|
39
|
+
<RecordForm tableId={id} /> // create, generated from the Field records
|
|
40
|
+
<RecordForm tableId={id} recordId={recordId} /> // edit, with the conflict shown when someone wins
|
|
41
|
+
<Peek tableId={id} recordId={recordId} onClose={close} /> // any custom table's record, no per-table code
|
|
42
|
+
<TableSettings tableId={id} /> // rename · reorder · retype · delete
|
|
43
|
+
<FieldEditor tableId={id} field={field} /> // one Field's type, rules and sensitivity
|
|
44
|
+
<ImportWizard tableId={id} onProposeField={propose} /> // map columns, preview rows, propose the rest
|
|
45
|
+
<ExportMenu tableId={id} label="Deals" /> // CSV · XLSX · Alchemy
|
|
46
|
+
<CustomFieldsSection tableId={entityTableId} recordId={record.id} /> // THE one line a standard entity page adds
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
`RelationPicker` is used by `RecordForm` automatically for any `relation` field;
|
|
50
|
+
mount it directly when a screen needs just the picker.
|
|
51
|
+
|
|
52
|
+
## Views, the queue, history and comments
|
|
53
|
+
|
|
54
|
+
```tsx
|
|
55
|
+
<ViewBar tableId={id} onActiveView={setView} seed={[{ name: "By stage", subject: id, layout: "kanban", groupField: "stage" }]} />
|
|
56
|
+
<ViewSwitcher view={view} /> // grid · kanban · calendar · gallery, one membership Rule
|
|
57
|
+
<ActionInbox tableId={id} seed={[...]} /> // approvals, assignments and agent proposals, one queue
|
|
58
|
+
<HistoryPanel tableId={id} recordId={recordId} /> // the timeline IS the record; undo is the store's soft delete
|
|
59
|
+
<CommentThread tableId={id} recordId={recordId} /> // a comment is a record
|
|
60
|
+
<FormBuilder tableId={id} seed={[form]} /> <FormRunner form={form} /> // a form is a view on a real Table
|
|
61
|
+
<DashboardCanvas tableId={id} seed={[dashboard]} /> <ChartBlock spec={chart} /> // every block is one `record_aggregate`
|
|
62
|
+
<NotifyRuleEditor tableId={id} /> // a subscription is a Rule record over a saved view
|
|
63
|
+
<DocTemplate tableId={id} seed={[tpl]} /> <DocRender templateId={t} recordId={r} /> // tokens resolved by the store
|
|
64
|
+
<SignBlock tableId={id} recordId={r} render={render} /> // the signature is checked against the exact bytes
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Talking to a record, and letting an agent keep it filled
|
|
68
|
+
|
|
69
|
+
```tsx
|
|
70
|
+
<RecordChat tableId={id} recordId={recordId} /> // the HOST's chat surface, this record as its context
|
|
71
|
+
<EnrichPanel tableId={id} recordId={recordId} /> // per-field AI fill, freshness and provenance
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
`RecordChat` ships **no chat UI**. AI Matrx has exactly one — matrx-frontend's
|
|
75
|
+
`AgentConversationColumn` — and it reads that app's Redux store, so a package
|
|
76
|
+
that imported it would stop being a package and a second one here would drift
|
|
77
|
+
within a month. `RecordChat` builds the part only the record store can build:
|
|
78
|
+
the record read through the read door, each value carrying its Field's unit and
|
|
79
|
+
format (the agent is given `$48,000`, never a bare `48000`), the fields the
|
|
80
|
+
store MASKED named out loud, and the `secret` and `on_demand` ones held back
|
|
81
|
+
with the reason. Then it hands that to the `chat` port:
|
|
82
|
+
|
|
83
|
+
```tsx
|
|
84
|
+
<RecordsUiProvider value={{
|
|
85
|
+
chat: (ctx) => {
|
|
86
|
+
const { conversationId } = useAgentLauncher(agentId, {
|
|
87
|
+
surfaceKey: ctx.surfaceKey,
|
|
88
|
+
runtime: { context: Object.fromEntries(ctx.entries.map((e) => [e.key, e.value])) },
|
|
89
|
+
});
|
|
90
|
+
return <AgentConversationColumn conversationId={conversationId} surfaceKey={ctx.surfaceKey} />;
|
|
91
|
+
},
|
|
92
|
+
}}>
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Resolve the agent through a **Mandate**, never a pinned UUID. Unbound, the panel
|
|
96
|
+
is absent with its reason and draws no composer.
|
|
97
|
+
|
|
98
|
+
`EnrichPanel` reads which Fields an agent keeps filled from the Fields
|
|
99
|
+
themselves (`source = agent`) and how often from `review_interval_days`, and
|
|
100
|
+
dates each value off its own envelope — never off the row's `updated_at`, which
|
|
101
|
+
would call every field stale because one of them changed. The fill itself goes
|
|
102
|
+
through the **server's `records` tool**, because a write stamped actor `agent`
|
|
103
|
+
on behalf of a person can only honestly be made by the server:
|
|
104
|
+
|
|
105
|
+
```tsx
|
|
106
|
+
<RecordsUiProvider value={{
|
|
107
|
+
enrich: (ask) => fetch("/api/ai/tools/execute", { method: "POST", body: JSON.stringify({
|
|
108
|
+
tool_name: "records", surface: "records-ui:enrich",
|
|
109
|
+
arguments: { action: "record_write", record_id: ask.recordId, values: {/* the agent's answers */} },
|
|
110
|
+
})}).then((r) => r.json()).then((r) => ({ ok: r.ok, message: r.output })),
|
|
111
|
+
}}>
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Two live switches sit in front of that call today and the panel names both
|
|
115
|
+
rather than spinning: the knob `custom/code_paths_enabled` resolves false, and
|
|
116
|
+
the `records` row in `tool.binding` is `is_active = false`.
|
|
117
|
+
|
|
118
|
+
## Work: checklists, bookings and a phone with no signal
|
|
119
|
+
|
|
120
|
+
```tsx
|
|
121
|
+
<ChecklistRunner tableId={id} seed={[{ name: "Onboarding", subject: id, steps: [{ title: "Confirm the address" }] }]} />
|
|
122
|
+
<BookingSlots tableId={id} form={bookingForm} availability={{ fromHour: 9, toHour: 17, everyMinutes: 60, days: 5 }} />
|
|
123
|
+
<CaptureSheet tableId={id} readingField="amount" noteField="title" />
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
`ChecklistRunner` makes every step a **real record** — assignable, commentable,
|
|
127
|
+
exportable, historied — and the whole graph is created in ONE act
|
|
128
|
+
(`custom.work_template_instantiate` judges it before writing any of it), so a
|
|
129
|
+
half-made checklist is not a state this store can be in. The states and the
|
|
130
|
+
moves offered come from `custom.work_states()`; a move the store does not list
|
|
131
|
+
as next is not shown greyed out, it is not there.
|
|
132
|
+
|
|
133
|
+
`BookingSlots` holds the slot **first** and takes the details second — taking
|
|
134
|
+
details first is how two people fill in a form and one of them finds out at the
|
|
135
|
+
end. One hold per slot is a UNIQUE INDEX in the database, not a check this
|
|
136
|
+
screen makes, so the loser of a race reads the database's own sentence naming
|
|
137
|
+
the index that refused it. Holds live in a slots Table the component declares
|
|
138
|
+
through `custom.work_slots_declare`; if the store cannot build that index it
|
|
139
|
+
refuses, and this screen shows the refusal rather than a picker that would sell
|
|
140
|
+
the same hour twice.
|
|
141
|
+
|
|
142
|
+
`CaptureSheet` is the offline capture contract, and both halves matter: the
|
|
143
|
+
**client mints the id** the moment a person hits Capture — offline, before the
|
|
144
|
+
first attempt, never re-minted on retry — and **the store dedupes on it**, so a
|
|
145
|
+
reconnecting phone that fires the same capture thirty times writes one record
|
|
146
|
+
and gets the same id back every time. Bind `captureQueue` to keep the queue
|
|
147
|
+
across a reload; unbound, it is in memory for the life of the page and says so.
|
|
148
|
+
|
|
149
|
+
## Outsiders: a portal, a public page, an embed
|
|
150
|
+
|
|
151
|
+
```tsx
|
|
152
|
+
<PortalShell tableId={id} form={intakeForm} /> // their list, their record, their form
|
|
153
|
+
<PublicViewPage slug={params.slug} /> // a signed-out visitor's page
|
|
154
|
+
<EmbedFrame tableId={id} formId={formId} embedUrl="https://app.example/embed" /> // issue the token
|
|
155
|
+
const { binding, error } = useEmbedHandshake({ secret, requiredMode: "read" }); // and verify it, inside the iframe
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
`PortalShell` asks `iam` who the person is rather than deciding for itself:
|
|
159
|
+
"external" means no membership of a **non-personal** organization, because
|
|
160
|
+
signup mints everyone a personal one and the other reading makes nobody
|
|
161
|
+
external. What they reach is `iam.external_principal_reach` — exactly the
|
|
162
|
+
resource ids Visibility handed them, one at a time, never "their
|
|
163
|
+
organization's records".
|
|
164
|
+
|
|
165
|
+
`PublicViewPage` renders a publish binding, and **a share is not a publish**: an
|
|
166
|
+
address nobody made, one that was revoked, and one whose lane closed all resolve
|
|
167
|
+
to the same nothing, because an address that answered differently for a revoked
|
|
168
|
+
binding would be an existence oracle. Every read carries the store's
|
|
169
|
+
"not scanned" note (D-15) — one function, so the page, the API and the admission
|
|
170
|
+
row cannot drift into three different admissions. Reaching the published happy
|
|
171
|
+
path needs a claimed namespace **and a verified publisher**, which is a person
|
|
172
|
+
standing behind what goes on the open internet, not something a screen may forge.
|
|
173
|
+
|
|
174
|
+
`EmbedFrame` mints a token through `custom.anon_token_issue`, shows the secret
|
|
175
|
+
**once** (only its digest is stored), and states the rules before the refusal: a
|
|
176
|
+
token carries one decision — read or write, never both — and its origin list is
|
|
177
|
+
required and matched exactly, so `example.com.evil.test` fails a check a suffix
|
|
178
|
+
match would pass.
|
|
179
|
+
|
|
180
|
+
## What every one of them does the same way
|
|
181
|
+
|
|
182
|
+
- **The store's own sentence.** A refusal is never re-worded. "Someone else
|
|
183
|
+
changed this record while you were working on it" is what the person reads,
|
|
184
|
+
because it is what the store said, and the conflict comes with it field by
|
|
185
|
+
field.
|
|
186
|
+
- **Absent, never dead.** A control a person may not use is not rendered. There
|
|
187
|
+
is no disabled "+" and no greyed settings panel anywhere in this package.
|
|
188
|
+
- **A Field decides its editor.** The thirteen parity types come from the store
|
|
189
|
+
(`custom.parity_field_types()`), generated into `@ai-matrx/records`. The unit
|
|
190
|
+
and the format are on the Field because they change what the value MEANS.
|
|
191
|
+
- **Nothing is dropped quietly.** An unmapped import column is shown and
|
|
192
|
+
offered as a field proposal; a list that is not live says so; a door that does
|
|
193
|
+
not exist yet is named, with the lane that owes it.
|
|
194
|
+
- **Dense, one-row headers, light and dark, iOS-safe**, through the design
|
|
195
|
+
system's own tokens — this package sets no colours of its own.
|
|
196
|
+
|
|
197
|
+
## The demo
|
|
198
|
+
|
|
199
|
+
`pnpm demo` runs every component against **the one live store**, headless, on
|
|
200
|
+
this lane's own port (127.0.0.1:3005), and stands its own table, fields and
|
|
201
|
+
records up the first time it runs. Its data seam is the server lane the platform
|
|
202
|
+
uses today, in one transaction per request that opens the product switch, acts
|
|
203
|
+
as the test identity (`test@test.com`) through the store's own doors, and sets
|
|
204
|
+
every switch back before committing — so no other session ever observes one
|
|
205
|
+
open. `pnpm test` renders those same pages in jsdom against that server and
|
|
206
|
+
asserts what a person would see.
|
|
207
|
+
|
|
208
|
+
It reads the live store deliberately: `custom.anon_*`, the embed token, the
|
|
209
|
+
external principal and the publish binding exist only there, and a demo pointed
|
|
210
|
+
at a rehearsal copy would answer `door_absent` for a third of this package and
|
|
211
|
+
call it a finding.
|
|
212
|
+
|
|
213
|
+
## The thirty-one components
|
|
214
|
+
|
|
215
|
+
| | | |
|
|
216
|
+
|---|---|---|
|
|
217
|
+
| `Grid` | `RecordForm` | `FieldEditor` |
|
|
218
|
+
| `TableSettings` | `RelationPicker` | `Peek` |
|
|
219
|
+
| `ViewSwitcher` | `ViewBar` | `ImportWizard` |
|
|
220
|
+
| `ExportMenu` | `CustomFieldsSection` | `ActionInbox` |
|
|
221
|
+
| `ProposalRow` | `HistoryPanel` | `CommentThread` |
|
|
222
|
+
| `FormBuilder` | `FormRunner` | `ChartBlock` |
|
|
223
|
+
| `DashboardCanvas` | `NotifyRuleEditor` | `DocTemplate` |
|
|
224
|
+
| `DocRender` | `SignBlock` | `RecordChat` |
|
|
225
|
+
| `EnrichPanel` | `ChecklistRunner` | `BookingSlots` |
|
|
226
|
+
| `CaptureSheet` | `PortalShell` | `PublicViewPage` |
|
|
227
|
+
| `EmbedFrame` | | |
|
|
228
|
+
|
|
229
|
+
## Commands
|
|
230
|
+
|
|
231
|
+
| Command | What it does |
|
|
232
|
+
|---|---|
|
|
233
|
+
| `pnpm demo` | Every component, live on the rehearsal branch, headless |
|
|
234
|
+
| `pnpm test` | The demo pages asserted headlessly |
|
|
235
|
+
| `pnpm build` | tsup → `dist` (esm + cjs + d.ts), then the client boundary stamp |
|
|
236
|
+
| `pnpm check:package` | build · exports agreement · publint · tarball canary |
|