@orkestrel/scaffold 0.0.103 → 0.0.105
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/agents/skills/orkestrel-dispatch/scripts/helpers.js +84 -1
- package/dist/agents/skills/orkestrel-dispatch/scripts/launch.js +21 -6
- package/dist/agents/skills/orkestrel-harden/scripts/titles.js +338 -0
- package/dist/agents/skills/orkestrel-publish/scripts/swap.js +3 -39
- package/dist/agents/skills/orkestrel-publish/scripts/wave.js +433 -47
- package/dist/agents/skills/orkestrel-publish/scripts/window.js +2 -1
- package/dist/bin/main.js +17 -10
- package/dist/bin/main.js.map +1 -1
- package/dist/host/agents/orchestration.md +6 -6
- package/dist/host/agents/skills/enterprise-bootstrap/references/components.md +2 -2
- package/dist/host/agents/skills/enterprise-bootstrap/references/inspection.md +1 -1
- package/dist/host/agents/skills/orkestrel-build/SKILL.md +2 -2
- package/dist/host/agents/skills/orkestrel-debrief/SKILL.md +2 -2
- package/dist/host/agents/skills/orkestrel-debrief/references/field-testing.md +1 -1
- package/dist/host/agents/skills/orkestrel-debrief/references/instruction-audit.md +3 -3
- package/dist/host/agents/skills/orkestrel-dispatch/SKILL.md +10 -10
- package/dist/host/agents/skills/orkestrel-dispatch/references/launch.md +1 -0
- package/dist/host/agents/skills/orkestrel-dispatch/scripts/helpers.ts +100 -1
- package/dist/host/agents/skills/orkestrel-dispatch/scripts/launch.ts +27 -6
- package/dist/host/agents/skills/orkestrel-falsify/SKILL.md +4 -4
- package/dist/host/agents/skills/orkestrel-falsify/agents/openai.yaml +1 -1
- package/dist/host/agents/skills/orkestrel-falsify/references/brief.md +8 -7
- package/dist/host/agents/skills/orkestrel-falsify/references/reconcile.md +6 -6
- package/dist/host/agents/skills/orkestrel-harden/SKILL.md +6 -1
- package/dist/host/agents/skills/orkestrel-harden/references/bindings.md +1700 -0
- package/dist/host/agents/skills/orkestrel-harden/references/contract.md +2 -2
- package/dist/host/agents/skills/orkestrel-harden/references/equivalence.md +48 -0
- package/dist/host/agents/skills/orkestrel-harden/references/hardening.md +4 -2
- package/dist/host/agents/skills/orkestrel-harden/scripts/titles.ts +425 -0
- package/dist/host/agents/skills/orkestrel-journey/SKILL.md +2 -2
- package/dist/host/agents/skills/orkestrel-journey/references/captures.md +9 -8
- package/dist/host/agents/skills/orkestrel-parallel/SKILL.md +72 -0
- package/dist/host/agents/skills/orkestrel-parallel/agents/openai.yaml +4 -0
- package/dist/host/agents/skills/orkestrel-parallel/references/harnesses.md +12 -0
- package/dist/host/agents/skills/orkestrel-polish/SKILL.md +3 -3
- package/dist/host/agents/skills/orkestrel-publish/SKILL.md +2 -2
- package/dist/host/agents/skills/orkestrel-publish/references/release.md +1 -0
- package/dist/host/agents/skills/orkestrel-publish/references/wave.md +56 -13
- package/dist/host/agents/skills/orkestrel-publish/references/window.md +6 -6
- package/dist/host/agents/skills/orkestrel-publish/scripts/swap.ts +5 -49
- package/dist/host/agents/skills/orkestrel-publish/scripts/wave.ts +646 -49
- package/dist/host/agents/skills/orkestrel-publish/scripts/window.ts +2 -1
- package/dist/host/agents/transports/codex.md +1 -1
- package/dist/host/claude/agents/orkestrel.md +54 -53
- package/dist/host/claude/agents/reviewer.md +4 -4
- package/dist/host/claude/agents/verifier.md +1 -1
- package/dist/host/claude/rules/documentation.md +2 -1
- package/dist/host/claude/rules/patterns.md +1 -1
- package/dist/host/claude/rules/quality.md +2 -2
- package/dist/host/claude/rules/styles.md +1 -1
- package/dist/host/claude/rules/tests.md +34 -8
- package/dist/host/claude/rules/typescript.md +29 -0
- package/dist/host/claude/rules/workspace.md +32 -22
- package/dist/host/claude/rules/writing.md +34 -26
- package/dist/host/claude/settings.json +2 -0
- package/dist/host/claude/skills/orkestrel-debrief/SKILL.md +1 -1
- package/dist/host/claude/skills/orkestrel-falsify/SKILL.md +1 -1
- package/dist/host/claude/skills/orkestrel-parallel/SKILL.md +11 -0
- package/dist/host/codex/agents/analyst.toml +3 -3
- package/dist/host/codex/agents/reviewer.toml +4 -4
- package/dist/host/codex/agents/verifier.toml +3 -1
- package/dist/host/configs/policy.ts +13 -2
- package/dist/host/dotfiles/editorconfig +0 -1
- package/dist/host/dotfiles/gitattributes +0 -1
- package/dist/host/dotfiles/gitignore +0 -1
- package/dist/host/dotfiles/oxlintrc.json +24 -0
- package/dist/host/guides/agent.md +1674 -598
- package/dist/host/guides/budget.md +89 -29
- package/dist/host/guides/form.md +485 -99
- package/dist/host/guides/mcp.md +14 -2
- package/dist/host/guides/ollama.md +367 -86
- package/dist/host/guides/probe.md +1 -1
- package/dist/host/guides/scaffold.md +158 -101
- package/dist/host/guides/study.md +3185 -0
- package/dist/host/guides/table.md +239 -29
- package/dist/host/guides/terminal.md +22 -20
- package/dist/host/guides/test.md +776 -315
- package/dist/host/manifest.json +117 -65
- package/dist/host/tests/config.test.ts +136 -82
- package/dist/host/tests/policy.test.ts +41 -3
- package/dist/host/tests/setupPolicy.ts +87 -29
- package/dist/src/core/index.cjs +1389 -612
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +35 -20
- package/dist/src/core/index.d.ts +35 -20
- package/dist/src/core/index.js +1387 -613
- package/dist/src/core/index.js.map +1 -1
- package/dist/src/server/index.cjs +12 -12
- package/dist/src/server/index.cjs.map +1 -1
- package/dist/src/server/index.d.cts +12 -12
- package/dist/src/server/index.d.ts +12 -12
- package/dist/src/server/index.js +12 -12
- package/dist/src/server/index.js.map +1 -1
- package/package.json +5 -5
|
@@ -4,16 +4,15 @@
|
|
|
4
4
|
> holding the rows given against it, and one lens of sort, filter, and page deciding which of them
|
|
5
5
|
> the view shows.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
The core renders nothing and names no host type. **A grid, a report,
|
|
8
8
|
a terminal listing, and a CSV export are the same abstraction.** They all hold a set of records,
|
|
9
9
|
order them, narrow them, and show a stretch of them. What differs is who draws the result, and
|
|
10
|
-
drawing
|
|
11
|
-
person looks at.
|
|
10
|
+
drawing belongs to each host. The browser face binds native table elements to that same document.
|
|
12
11
|
|
|
13
12
|
The row store is the source of truth, and it is the whole of it. Sort terms, filters, the picked
|
|
14
13
|
keys, the opened keys, and the page are held; `view` and every tally are worked out on read, so no
|
|
15
14
|
second copy of an answer can go stale. A write validates all of itself before any of it lands, and
|
|
16
|
-
announces itself
|
|
15
|
+
announces itself after it has.
|
|
17
16
|
|
|
18
17
|
The core refuses rather than throws. Every guard returns `false` off-shape rather than throwing,
|
|
19
18
|
every parser returns `undefined` on refusal, and every row the table hands back is a frozen owned
|
|
@@ -21,7 +20,8 @@ copy. Table-owned refusals raise `TableError`, and each one names a caller mista
|
|
|
21
20
|
|
|
22
21
|
## Surface
|
|
23
22
|
|
|
24
|
-
|
|
23
|
+
The core surface is exported from `@orkestrel/table` ([`src/core`](../src/core)); the native binding
|
|
24
|
+
surface comes from `@orkestrel/table/browser` ([`src/browser`](../src/browser)). The manager
|
|
25
25
|
classes and the key-set shell they compose are the module's internal declarations: they stay out
|
|
26
26
|
of the barrel, so no consumer can construct one, and each constructor takes the table's emitter
|
|
27
27
|
and closures over state `Table` keeps private, which is why they are internal rather than
|
|
@@ -63,6 +63,208 @@ table.pagination.count // 2 — two pages of two
|
|
|
63
63
|
table.view.map((row) => row.name) // ['Grace', 'Alan'] — page one, oldest first
|
|
64
64
|
```
|
|
65
65
|
|
|
66
|
+
### Native binding
|
|
67
|
+
|
|
68
|
+
These declarations belong to the browser entry.
|
|
69
|
+
|
|
70
|
+
| API | Kind | Shape | Summary |
|
|
71
|
+
| ----------------------- | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- |
|
|
72
|
+
| `TableBinding` | class | `TableBindingInterface` | Retains keyed native rows while projecting every core table manager. |
|
|
73
|
+
| `TableBindingInterface` | interface | `{ element, document, destroyed, emitter } plus refresh, destroy` | Binds native table elements to a borrowed core document. |
|
|
74
|
+
| `TableBindingOptions` | interface | `{ row?, detail?, scope?, describe?: { range?, sort? }, on?, error? }` | Configures row ownership, detail presentation, scoped controls, and event listeners. |
|
|
75
|
+
| `TableRange` | interface | `{ first, last, count, page, pages, limit }` | Describes the displayed page within the filtered rows. |
|
|
76
|
+
| `TableSortDescription` | interface | `{ column, direction, priority, count }` | Describes a column's direction and one-based priority among active sort orders. |
|
|
77
|
+
| `TableExpansion` | interface | `{ key, expanded }` | Proposes one row's next expansion state in a cancelable expand request. |
|
|
78
|
+
| `TableBindingEventMap` | type | `{ sort: readonly [event: CustomEvent<readonly TableOrder[]>]; expand: readonly [event: CustomEvent<readonly TableExpansion[]>]; render: readonly []; error: readonly [error: unknown] }` | Announces cancelable sorting and expanding, completed rendering, and failed DOM operations. |
|
|
79
|
+
| `TableRenderHandler` | type | `(element: HTMLTableRowElement, row: TableRow) => void` | Patches the contents of a retained row without moving or replacing that row. |
|
|
80
|
+
| `TableProjection` | interface | `{ schema, rows }` | Holds a validated native table projection for constructing a core document. |
|
|
81
|
+
| `TableBindingErrorCode` | type | `'STRUCTURE'` | Names a native binding failure at its structure, rendering, or lifecycle boundary. |
|
|
82
|
+
| `TableBindingError` | class | `new (code: TableBindingErrorCode, message: string) => TableBindingError` | Represents a native table binding failure with a machine-readable code. |
|
|
83
|
+
| `isTableBindingError` | function | `(value: unknown) => value is TableBindingError` | Checks whether a caught value is a native table binding error. |
|
|
84
|
+
| `createTableBinding` | function | `(element: HTMLTableElement, document: TableInterface, options?: TableBindingOptions) => TableBindingInterface` | Creates a native table binding with one retained row owner. |
|
|
85
|
+
| `readTable` | function | `(element: HTMLTableElement) => TableProjection` | Reads declared header metadata and explicit row keys into a validated core projection. |
|
|
86
|
+
| `readTableSchema` | function | `(element: HTMLTableElement) => TableSchema` | Reads and validates explicit native header metadata. |
|
|
87
|
+
| `readTableRows` | function | `(schema: TableSchema, elements: readonly HTMLTableRowElement[]) => readonly TableRow[]` | Reads explicit keys and declared direct cells against a core schema. |
|
|
88
|
+
|
|
89
|
+
### Enhance authored rows
|
|
90
|
+
|
|
91
|
+
Declare one body, a table identity-column name, explicit header column keys and cell kinds, and a
|
|
92
|
+
stable key on every data row. Direct cells declare their columns; omitted cells stay absent. Text
|
|
93
|
+
is preserved verbatim. Numbers and flags use the core parser. A native data element's value or a
|
|
94
|
+
time element's datetime takes precedence over cell text. Choice headers carry their explicit
|
|
95
|
+
choice array as JSON in data-table-choices. Nested tables supply no headers or controls to their parent.
|
|
96
|
+
|
|
97
|
+
This browser example constructs the document before binding it.
|
|
98
|
+
|
|
99
|
+
```ts
|
|
100
|
+
import { createTable } from '@orkestrel/table'
|
|
101
|
+
import { createTableBinding, readTable } from '@orkestrel/table/browser'
|
|
102
|
+
|
|
103
|
+
const element = document.createElement('table')
|
|
104
|
+
element.setAttribute('data-table-key', 'id')
|
|
105
|
+
element.innerHTML =
|
|
106
|
+
'<caption>People</caption><thead><tr><th data-table-column="id" data-table-cell="text">Reference</th><th data-table-column="name" data-table-cell="text"><button data-table-sort="name">Name</button></th></tr></thead><tbody><tr data-table-key="ash"><td data-table-column="name">Ash</td></tr><tr data-table-key="cedar"><td data-table-column="name">Cedar</td></tr></tbody>'
|
|
107
|
+
document.body.append(element)
|
|
108
|
+
const projection = readTable(element)
|
|
109
|
+
const table = createTable(projection.schema, { rows: projection.rows })
|
|
110
|
+
const binding = createTableBinding(element, table)
|
|
111
|
+
table.filter.set({ column: 'name', operator: 'contains', text: 'Ash' })
|
|
112
|
+
binding.refresh()
|
|
113
|
+
element.tBodies[0]?.rows.length // 1
|
|
114
|
+
binding.destroy()
|
|
115
|
+
element.tBodies[0]?.rows.length // 2: restored in core store order
|
|
116
|
+
table.destroyed // false
|
|
117
|
+
element.remove()
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
The binding validates the entire authored read before changing core rows. External inserts,
|
|
121
|
+
removals, and cell edits reach the document; an invalid read emits error and writes nothing.
|
|
122
|
+
Detached rows remain observed while their keys live. Authored mode owns cell values in the DOM;
|
|
123
|
+
use the core managers for the view, and edit authored cells to change values. Model mode owns
|
|
124
|
+
values in the document and requires an empty body and a row callback. That callback receives
|
|
125
|
+
the retained row first and its value second. It only patches contents, and runs on creation or
|
|
126
|
+
value change. Sort, selection, filtering, expansion, and pagination do not rerun it.
|
|
127
|
+
|
|
128
|
+
A detail callback receives a generated row containing one spanning cell. Patch that cell's
|
|
129
|
+
contents; the binding owns its placement and identity. Collapsed and off-view details are retained
|
|
130
|
+
until their data key leaves the store. Render callbacks must return synchronously. A throw emits
|
|
131
|
+
error without render success; refresh retries. Arbitrary callback effects are not rolled back.
|
|
132
|
+
|
|
133
|
+
Native hooks use real controls: a header button with data-table-sort naming the column, a row
|
|
134
|
+
checkbox with data-table-select, a row button with data-table-expand, and a scope checkbox with
|
|
135
|
+
data-table-page. Scope defaults to the table. Use a scope dedicated to that table for an external
|
|
136
|
+
controls. It selects or clears only current view keys; off-page selection stays selected.
|
|
137
|
+
Sort clicks cycle ascending, descending, and absent, replacing the primary order. Programmatic
|
|
138
|
+
multi-sort remains intact and is described on each sorted header button. The sort event carries
|
|
139
|
+
a cancelable CustomEvent whose detail is the proposed order array; preventDefault vetoes before
|
|
140
|
+
the document changes. A row expand button or the page expand button emits expand first, with a
|
|
141
|
+
cancelable CustomEvent whose detail lists each key and its proposed expanded state; preventDefault
|
|
142
|
+
leaves every key and row unchanged, so a host can run its own close transition and then clear the
|
|
143
|
+
key through the document's expansion. The core managers remain the only owners of filters and paging.
|
|
144
|
+
|
|
145
|
+
An external pager belongs inside the binding's dedicated scope. Use a select with
|
|
146
|
+
`data-table-limit`, buttons with `data-table-move="previous"` and `data-table-move="next"`, and an
|
|
147
|
+
authored `output` with `data-table-range`. A limit option's value is a positive integer; an empty
|
|
148
|
+
value removes paging. Invalid values emit a binding error without changing pagination. Programmatic
|
|
149
|
+
limit changes update the select; include an option for each limit your page uses. Native disabled
|
|
150
|
+
states follow the bounds. A focused button that becomes disabled hands focus to the enabled
|
|
151
|
+
opposite button, or to the named table when neither direction is available.
|
|
152
|
+
|
|
153
|
+
The range output is cleared before filling on a later task. It fills initially and when the
|
|
154
|
+
displayed range, filtered count, page, page count, or limit changes. Sorting, selection, expansion,
|
|
155
|
+
and refresh alone don't repeat the sentence. Its implicit status role needs no added live role.
|
|
156
|
+
The default is `1 to 2 of 3`, or `0 to 0 of 0` for an empty view. The `describe.range` callback
|
|
157
|
+
receives one-based bounds, filtered count, page and page count, and the optional page limit.
|
|
158
|
+
The `describe.sort` callback receives the column, direction, one-based priority, and active order
|
|
159
|
+
count. Its default is `Sort priority 2 of 2, descending`. Formatter throws emit error without
|
|
160
|
+
render success; refresh retries. Destroy cancels pending fills and restores control values still
|
|
161
|
+
carrying the binding's last write, preserving caller edits.
|
|
162
|
+
|
|
163
|
+
A button with `data-table-expand="page"` expands the visible keys, or collapses them when every
|
|
164
|
+
visible key is expanded. Off-page expansion stays intact. The cancelable `expand` request lists
|
|
165
|
+
only the keys whose state changes. The label stays authored;
|
|
166
|
+
`aria-expanded` carries the state, and an empty view disables the button. Row controls continue
|
|
167
|
+
to use bare `data-table-expand`. Detail rows and their identifiers survive collapse and paging.
|
|
168
|
+
Nested tables supply none of these controls to the containing binding. The binding renders no
|
|
169
|
+
numbered page window; presentation classes belong to the host.
|
|
170
|
+
|
|
171
|
+
This scoped binding example customizes wording while retaining native control labels.
|
|
172
|
+
|
|
173
|
+
```ts
|
|
174
|
+
import { createTable } from '@orkestrel/table'
|
|
175
|
+
import { createTableBinding, readTable } from '@orkestrel/table/browser'
|
|
176
|
+
|
|
177
|
+
const scope = document.createElement('section')
|
|
178
|
+
scope.innerHTML =
|
|
179
|
+
'<table data-table-key="id"><caption>Accounts</caption><thead><tr><th data-table-column="id" data-table-cell="text"><button data-table-sort="id">Account</button></th></tr></thead><tbody><tr data-table-key="ash"><td data-table-column="id">ash</td></tr><tr data-table-key="cedar"><td data-table-column="id">cedar</td></tr></tbody></table><nav aria-label="Account pages"><output data-table-range></output><select aria-label="Rows per page" data-table-limit><option value="1">1</option><option value="">All</option></select><button type="button" data-table-move="previous">Previous</button><button type="button" data-table-move="next">Next</button><button type="button" data-table-expand="page">Page details</button></nav>'
|
|
180
|
+
document.body.append(scope)
|
|
181
|
+
const element = scope.querySelector('table')
|
|
182
|
+
if (element === null) throw new Error('Missing account table')
|
|
183
|
+
const projection = readTable(element)
|
|
184
|
+
const table = createTable(projection.schema, { rows: projection.rows, limit: 1 })
|
|
185
|
+
const binding = createTableBinding(element, table, {
|
|
186
|
+
scope,
|
|
187
|
+
describe: {
|
|
188
|
+
range: ({ first, last, count }) => `${first}–${last} of ${count} accounts`,
|
|
189
|
+
sort: ({ priority, count, direction }) => `Order ${priority}/${count}: ${direction}`,
|
|
190
|
+
},
|
|
191
|
+
detail: (node, row) => {
|
|
192
|
+
const cell = node.cells[0]
|
|
193
|
+
if (cell !== undefined) cell.textContent = `Account ${row['id']}`
|
|
194
|
+
},
|
|
195
|
+
})
|
|
196
|
+
// Keep the binding while the page is mounted; release it before removing the scope.
|
|
197
|
+
binding.destroy()
|
|
198
|
+
table.destroy()
|
|
199
|
+
scope.remove()
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
Rendering coalesces on a later task, including writes during callbacks. Rows and child controls
|
|
203
|
+
retain identity across the view. Reorders use native state-preserving movement when available;
|
|
204
|
+
focus is restored on fallback moves. When a focused row leaves the view, focus moves to the named
|
|
205
|
+
table. An already focused external control keeps focus. Destroy disconnects scheduling, observers,
|
|
206
|
+
and listeners, restores attributes still carrying the binding's values, removes details, and
|
|
207
|
+
returns retained authored rows in core order while retaining a connected focused control. A caller's
|
|
208
|
+
deliberate focus choice outside the row remains untouched. Model rows remain an inert final snapshot. The
|
|
209
|
+
borrowed document stays live.
|
|
210
|
+
|
|
211
|
+
The lower-level readers share the same validation boundary, and the row comparison ignores
|
|
212
|
+
snapshot identity.
|
|
213
|
+
|
|
214
|
+
```ts
|
|
215
|
+
import {
|
|
216
|
+
TableBinding,
|
|
217
|
+
TableBindingError,
|
|
218
|
+
isTableBindingError,
|
|
219
|
+
readTableRows,
|
|
220
|
+
readTableSchema,
|
|
221
|
+
} from '@orkestrel/table/browser'
|
|
222
|
+
import { createTable, matchesTableRow } from '@orkestrel/table'
|
|
223
|
+
|
|
224
|
+
const element = document.createElement('table')
|
|
225
|
+
element.setAttribute('data-table-key', 'id')
|
|
226
|
+
element.innerHTML =
|
|
227
|
+
'<thead><tr><th data-table-column="id" data-table-cell="text">Reference</th></tr></thead><tbody></tbody>'
|
|
228
|
+
const schema = readTableSchema(element)
|
|
229
|
+
const rows = readTableRows(schema, []) // []
|
|
230
|
+
const table = createTable(schema, { rows })
|
|
231
|
+
const binding = new TableBinding(element, table)
|
|
232
|
+
binding.destroy()
|
|
233
|
+
matchesTableRow({ id: 'ash' }, { id: 'ash' }) // true
|
|
234
|
+
isTableBindingError(new TableBindingError('STRUCTURE', 'Missing identity')) // true
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### Replace complete rows
|
|
238
|
+
|
|
239
|
+
Replacement removes omitted optional cells while preserving the identity and its manager state.
|
|
240
|
+
|
|
241
|
+
```ts
|
|
242
|
+
import { createTable } from '@orkestrel/table'
|
|
243
|
+
|
|
244
|
+
const table = createTable(
|
|
245
|
+
{
|
|
246
|
+
key: 'id',
|
|
247
|
+
columns: [
|
|
248
|
+
{ key: 'id', cell: 'text' },
|
|
249
|
+
{ key: 'name', cell: 'text' },
|
|
250
|
+
],
|
|
251
|
+
},
|
|
252
|
+
{ rows: [{ id: 'ash', name: 'Ash' }] },
|
|
253
|
+
)
|
|
254
|
+
table.selection.select('ash')
|
|
255
|
+
table.rows.replace({ id: 'ash' }) // true
|
|
256
|
+
table.rows.row('ash') // { id: 'ash' }
|
|
257
|
+
table.selection.keys.has('ash') // true
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
### Row equality
|
|
261
|
+
|
|
262
|
+
This helper belongs to the core entry.
|
|
263
|
+
|
|
264
|
+
| API | Kind | Shape | Summary |
|
|
265
|
+
| ----------------- | -------- | ----------------------------------------------------------- | ------------------------------------------------------------------------ |
|
|
266
|
+
| `matchesTableRow` | function | `(left: TableRow \| undefined, right: TableRow) => boolean` | Compares complete row values independently of their snapshot identities. |
|
|
267
|
+
|
|
66
268
|
### Rows, cells, and columns
|
|
67
269
|
|
|
68
270
|
The document itself — what a table declares and what one row of it holds. All data, no behavior.
|
|
@@ -116,7 +318,7 @@ A `Shape` cell holds an interface's data members as bare names in braces, `?` ma
|
|
|
116
318
|
| `createTable` | function | `(schema: TableSchema, options?: TableOptions) => TableInterface` | Opens a table against a schema. The schema is copied, and the copy is what the table declares. |
|
|
117
319
|
| `TableOptions` | interface | `{ on?, error?, rows?, comparators?, matchers?, limit? }` | Describes how to open a table — the listeners wired at construction and where a throw from one goes, the rows seeded into it, the per-column comparison and test replacements, and the page size. |
|
|
118
320
|
| `TableEventMap` | type | `{ write, remove, sort, filter, select, expand, paginate, clear }` | Lists everything a table announces, mapping each event to the payload its listeners receive. |
|
|
119
|
-
| `RowManagerInterface` | interface | `{} plus row, rows, add, update, move, remove`
|
|
321
|
+
| `RowManagerInterface` | interface | `{} plus row, rows, add, update, replace, move, remove` | Manages the rows a table holds, in the order it holds them. |
|
|
120
322
|
| `SortManagerInterface` | interface | `{} plus order, orders, set, remove` | Manages the order a table reads its rows in. |
|
|
121
323
|
| `FilterManagerInterface` | interface | `{} plus filter, filters, set, remove` | Manages which rows a table keeps. |
|
|
122
324
|
| `SelectionManagerInterface` | interface | `{ keys } plus select, clear, toggle` | Manages the rows somebody has picked. |
|
|
@@ -160,7 +362,7 @@ A `Shape` cell holds the constant's declared type.
|
|
|
160
362
|
### Guards
|
|
161
363
|
|
|
162
364
|
Total `is*` guards over unknown input. None throws, none coerces, and each returns `false` for
|
|
163
|
-
anything off-shape — including
|
|
365
|
+
anything off-shape — including an unexpected prototype, a symbol key, or a cyclic value.
|
|
164
366
|
|
|
165
367
|
In a guard table a `Shape` cell holds the type the guard narrows to.
|
|
166
368
|
|
|
@@ -202,7 +404,7 @@ lens-list operations, the cell gate, the comparison, the filter tests, the row p
|
|
|
202
404
|
audit, and the wire projections. `computeKeys`, `matchesTerms`, `filterRows`, and `sortRows`
|
|
203
405
|
propagate exceptions from supplied callbacks; `serializeTable` raises `SCHEMA` for a `meta` no clone
|
|
204
406
|
can own. The other helpers are total over ordinary declared inputs, subject to the core's
|
|
205
|
-
|
|
407
|
+
reflection-failure boundary described later.
|
|
206
408
|
|
|
207
409
|
| API | Kind | Summary |
|
|
208
410
|
| ---------------- | -------- | --------------------------------------------------------------------------------------------------------------------------- |
|
|
@@ -233,7 +435,7 @@ row the table hands back is a live internal reference.
|
|
|
233
435
|
| `cloneRow` | function | Clones one row into an owned frozen snapshot. |
|
|
234
436
|
| `cloneSchema` | function | Clones a whole schema into an owned frozen snapshot, freezing every nested column, choice list, choice, and `meta`; raises `SCHEMA` for a `meta` it cannot own. |
|
|
235
437
|
|
|
236
|
-
`cloneRow` cannot fail for an ordinary row record, subject to the core's
|
|
438
|
+
`cloneRow` cannot fail for an ordinary row record, subject to the core's reflection-failure boundary
|
|
237
439
|
described later. `cloneSchema` can also fail when a column's `meta` holds something no clone can own, such as a
|
|
238
440
|
record that refers back to itself. `meta` is typed as JSON and a cycle satisfies that type, so the
|
|
239
441
|
refusal is a `TableError` coded `SCHEMA` rather than a silent partial copy. `createTable` never reaches
|
|
@@ -253,9 +455,9 @@ owned value rather than the caller's.
|
|
|
253
455
|
| `parseTable` | function | Parses unknown wire data into an owned, structurally valid, semantically sound table schema. |
|
|
254
456
|
| `parseRows` | function | Parses unknown wire data into owned rows against one table schema, coercing a numeric string and `'true'` / `'false'`. |
|
|
255
457
|
|
|
256
|
-
**
|
|
458
|
+
**Reflection failures.** A TypeScript shape does not guarantee that reflection succeeds. An ordinary
|
|
257
459
|
helper or cloner may propagate a throw from a proxy trap or accessor. Guards and parsers contain that
|
|
258
|
-
throw and refuse instead. This is the
|
|
460
|
+
throw and refuse instead. This is the reflection-failure boundary for the whole core.
|
|
259
461
|
|
|
260
462
|
## Cells
|
|
261
463
|
|
|
@@ -780,7 +982,7 @@ the filter admits, so nothing here can drift out of step with what the table hol
|
|
|
780
982
|
"page 1 of 1" over an empty list needs no separate case for it.
|
|
781
983
|
- A page beyond the last one clamps to the last one. Narrowing the filter therefore moves the page
|
|
782
984
|
on its own, and moving to page 9 of 3 shows page 3 rather than nothing.
|
|
783
|
-
- `resize` keeps the first row the view was showing, so the page moves to wherever that row
|
|
985
|
+
- `resize` keeps the first row the view was showing, so the page moves to wherever that row
|
|
784
986
|
falls. Resizing is a change of magnification, not a jump to the top.
|
|
785
987
|
|
|
786
988
|
```ts
|
|
@@ -942,11 +1144,11 @@ An event fires only when something actually moved: a write that changes nothing
|
|
|
942
1144
|
| ---------- | --------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
943
1145
|
| `write` | the row's `key` | Once per row admitted, written over, or moved to a different place, in the order the table wrote them. A listener that wants the row reads it back. |
|
|
944
1146
|
| `remove` | the row's `key` | Once per row taken out, in the order the table removed them. A `clear` is the exception; see its row. |
|
|
945
|
-
| `sort` | every current `TableOrder` | Whenever the term list's content changes, carrying the whole list as it
|
|
946
|
-
| `filter` | every current `TableFilter` | Whenever the filter list's content changes, carrying the whole list as it
|
|
1147
|
+
| `sort` | every current `TableOrder` | Whenever the term list's content changes, carrying the whole list as it stands. |
|
|
1148
|
+
| `filter` | every current `TableFilter` | Whenever the filter list's content changes, carrying the whole list as it stands. |
|
|
947
1149
|
| `select` | the picked `TableKey` set | Whenever the picked set changes, carrying the whole set — including when removing rows pruned it. |
|
|
948
1150
|
| `expand` | the opened `TableKey` set | Whenever the opened set changes, carrying the whole set — including when removing rows pruned it. |
|
|
949
|
-
| `paginate` | the `page`
|
|
1151
|
+
| `paginate` | the `page` shown | Whenever the paged window moves: a `move`, a `resize`, and a clamp caused by a narrower filter or by rows leaving. |
|
|
950
1152
|
| `clear` | nothing | On a completed `clear`. It is the whole announcement of that reset: no `remove`, no `sort`, no `filter`, no `select`, no `expand`, and no `paginate` is emitted beside it. |
|
|
951
1153
|
|
|
952
1154
|
A payload is owned by the listener. `sort` and `filter` carry the whole axis because a single term is
|
|
@@ -1163,7 +1365,7 @@ a host can check against the same number the package checks against.
|
|
|
1163
1365
|
They bind at the schema door and the value door, and which door a limit sits at is the whole story.
|
|
1164
1366
|
|
|
1165
1367
|
**The schema door reports.** `auditTable` counts columns, choices, names, strings, total text, and
|
|
1166
|
-
total nodes — `meta` included,
|
|
1368
|
+
total nodes — `meta` included, because it is retained like everything else — and returns one human
|
|
1167
1369
|
diagnostic per breach. `createTable` raises `SCHEMA` carrying them and `parseTable` refuses the
|
|
1168
1370
|
schema, so no over-budget schema is ever held.
|
|
1169
1371
|
|
|
@@ -1257,14 +1459,22 @@ it either way.
|
|
|
1257
1459
|
|
|
1258
1460
|
#### `RowManagerInterface`
|
|
1259
1461
|
|
|
1260
|
-
| Method
|
|
1261
|
-
|
|
|
1262
|
-
| `row`
|
|
1263
|
-
| `rows`
|
|
1264
|
-
| `add`
|
|
1265
|
-
| `update`
|
|
1266
|
-
| `
|
|
1267
|
-
| `
|
|
1462
|
+
| Method | Returns | Summary |
|
|
1463
|
+
| --------- | ------------------------- | ------------------------------------------------------------------------------------------------------------------- |
|
|
1464
|
+
| `row` | `TableRow` or `undefined` | Finds one row by key; `undefined` when the table holds no such key. |
|
|
1465
|
+
| `rows` | `readonly TableRow[]` | Reads every row the table holds, in its own order — unfiltered, unsorted, and unpaged. |
|
|
1466
|
+
| `add` | `void` | Takes in one row or several, appending them in the order given. Every row is checked before any is admitted. |
|
|
1467
|
+
| `update` | `boolean` | Writes over one row or several, each found by the key it carries. The cells given replace; the cells left out stay. |
|
|
1468
|
+
| `replace` | `boolean` | Replaces complete rows under existing keys, removing omitted cells without moving identities. |
|
|
1469
|
+
| `move` | `boolean` | Moves one row to another place in the table's own order, counted from zero and clamped to the rows that exist. |
|
|
1470
|
+
| `remove` | `void` or `boolean` | Takes out every row, one row, or several. Selection and expansion drop the keys of the rows that went. |
|
|
1471
|
+
|
|
1472
|
+
#### `TableBindingInterface`
|
|
1473
|
+
|
|
1474
|
+
| Method | Returns | Summary |
|
|
1475
|
+
| --------- | ------- | --------------------------------------------------------------------------------- |
|
|
1476
|
+
| `refresh` | `void` | Reconciles and renders the document, retrying a failed renderer. |
|
|
1477
|
+
| `destroy` | `void` | Releases listeners and restores owned attributes without destroying the document. |
|
|
1268
1478
|
|
|
1269
1479
|
#### `SortManagerInterface`
|
|
1270
1480
|
|
|
@@ -1372,7 +1582,7 @@ table.count // 1
|
|
|
1372
1582
|
These invariants hold across [`src/core`](../src/core) and this guide.
|
|
1373
1583
|
|
|
1374
1584
|
1. **Documented surface equals exported surface.** Every row in the `## Surface` tables is a real
|
|
1375
|
-
barrel export of `src/core`, and every barrel export is a row — both directions, exhaustively.
|
|
1585
|
+
barrel export of `src/core` or `src/browser`, and every barrel export is a row — both directions, exhaustively.
|
|
1376
1586
|
The manager classes and the key-set shell are internal, so the parity suite's internal list names
|
|
1377
1587
|
them and nothing else.
|
|
1378
1588
|
2. **Documented methods equal interface methods.** Each `## Methods` table lists exactly its
|
|
@@ -1432,7 +1642,7 @@ These invariants hold across [`src/core`](../src/core) and this guide.
|
|
|
1432
1642
|
whatever it reset, so a reset of ten thousand rows is one event. `destroy` called twice does what
|
|
1433
1643
|
calling it once did; afterwards every write raises `DESTROYED` while every getter still answers
|
|
1434
1644
|
what the table last held, and `destroyed` reports the fact so a host need not catch to learn it.
|
|
1435
|
-
16. **Guards are total and parsers refuse.** No `is*` throws for any input —
|
|
1645
|
+
16. **Guards are total and parsers refuse.** No `is*` throws for any input — unexpected prototype,
|
|
1436
1646
|
symbol key, cycle, or depth. No `parse*` throws; each returns `undefined` on refusal. A
|
|
1437
1647
|
guard-valid value is never refused by its parser, and every parsed result satisfies its guard.
|
|
1438
1648
|
`isTableSchema` is therefore semantic and not merely structural: it is the exact shape plus an
|
|
@@ -1491,7 +1701,7 @@ this package answers today through a mechanism it already exposes.
|
|
|
1491
1701
|
| Grouping and aggregation | out | Group headers, subtotals, and rollups add a second row kind that is not a row, and a fold that is not a filter. `view` stays a flat list of the rows the table holds. |
|
|
1492
1702
|
| Tree and hierarchical rows | out | A parent key would make `view` a traversal rather than a projection, and expansion would mean "show children" instead of "this row is open". Expansion here says only which rows are open. |
|
|
1493
1703
|
| Editing transactions | out | A staged edit set with commit and rollback is a second store beside the row store, and two writers that can disagree. A host that needs one holds the pending values and calls `update` once. |
|
|
1494
|
-
| Undo and history | out | The table holds the rows
|
|
1704
|
+
| Undo and history | out | The table holds the rows. A stack of everything before is a host concern with a host's retention policy, built over the same `update` and `remove` calls. |
|
|
1495
1705
|
| Async data sources | host | Every read here is synchronous, so `view` is right the instant anything moves. Fetching, paging a server, and reconciling a response are the host's; it reads the lens and calls `add` or `clear`. |
|
|
1496
1706
|
| Server-side paging | seam | `sort.orders()`, `filter.filters()`, `pagination.offset`, and `pagination.limit` are already the shape a query asks for, in the vocabulary `@orkestrel/database` uses. |
|
|
1497
1707
|
| Row count budget | out | A table legitimately holds a million rows. The budgets bound one schema's declarations, which arrive from a wire; how many rows a host holds is the host's memory to spend. |
|
|
@@ -1503,7 +1713,7 @@ this package answers today through a mechanism it already exposes.
|
|
|
1503
1713
|
| CSV and spreadsheet export | host | `view` or `rows()` plus `schema.columns` is the whole input an exporter needs, and the file format, encoding, and download belong to the host that has a filesystem or a browser. |
|
|
1504
1714
|
| Cell-level selection | out | Selection holds row keys. A rectangular cell range is a second selection model with its own vocabulary, and no consumer has asked for one. |
|
|
1505
1715
|
| Choice `meta` | out | `meta` is on `ColumnBase` alone, because a column carrier is what the first consumer asked for. The exact guard refuses it on `ColumnChoice` until one asks. |
|
|
1506
|
-
| Browser binding | `src/browser` |
|
|
1716
|
+
| Browser binding | `src/browser` | The browser entry binds native table rows and controls to a borrowed core document; the core remains host-independent. |
|
|
1507
1717
|
|
|
1508
1718
|
## Tests
|
|
1509
1719
|
|
|
@@ -1536,7 +1746,7 @@ this package answers today through a mechanism it already exposes.
|
|
|
1536
1746
|
`compareCells`, `admitsFilter`, `matchesFilter`, `filterRows`, `sortRows`, `auditTable`,
|
|
1537
1747
|
`serializeTable`, `serializeRows`, and the budgets.
|
|
1538
1748
|
- [`tests/src/core/validators.test.ts`](../tests/src/core/validators.test.ts) — every guard against
|
|
1539
|
-
valid, off-shape, and
|
|
1749
|
+
valid, off-shape, and untrusted input, plus guard/parser soundness in both directions.
|
|
1540
1750
|
- [`tests/src/core/parsers.test.ts`](../tests/src/core/parsers.test.ts) — `parseTable`, `parseRows`,
|
|
1541
1751
|
the coercions, identity at the parse door, and the canonical byte-stable round trip.
|
|
1542
1752
|
- [`tests/src/core/cloners.test.ts`](../tests/src/core/cloners.test.ts) — every clone is owned,
|
|
@@ -351,23 +351,23 @@ A `Shape` cell holds an interface's data members as bare names in braces, `?` ma
|
|
|
351
351
|
The stream guards, the cursor math behind the in-place re-render, and the per-field line projections
|
|
352
352
|
the walk renders with ([`src/server`](../src/server)). All pure, all exported, all unit-tested.
|
|
353
353
|
|
|
354
|
-
| API | Kind | Summary
|
|
355
|
-
| ----------------------- | -------- |
|
|
356
|
-
| `isInputStream` | function | Checks whether `value` is a usable `InputStreamInterface` — a record with callable `on` / `off` `'data'` subscription methods. A total type guard: it never throws and returns `false` for anything off-shape, so it narrows the one unavoidable input boundary (the real `process.stdin`, or a fake TTY a test injects) to the exact slice the driver reads, never an assertion.
|
|
357
|
-
| `isReadable` | function | Checks whether `value` is a Node `NodeJS.ReadableStream` — a total structural guard checking for the callable `read` / `pipe` / `on` that `node:readline`'s `createInterface` requires as its `input`. The non-TTY fallback narrows the resolved input stream through this before handing it to readline, never through an assertion, so a real piped `process.stdin` (or a `PassThrough` a test injects) crosses into the readline boundary honestly. Never throws; returns `false` for a minimal fake that isn't a full readable.
|
|
358
|
-
| `supportsRawMode` | function | Checks whether an input stream can be driven in raw mode — it reports `isTTY === true` and exposes a callable `setRawMode`. The `Terminal` probes this to choose its path: true selects the interactive raw-mode fields, with arrow-key navigation and a live re-render; false selects the `node:readline` line-input fallback, because a piped or non-terminal stream cannot enter raw mode. Total — never throws.
|
|
359
|
-
| `lineCount` | function | Counts the terminal lines a rendered prompt `view` occupies — one more than its newline count, so a view with no newline is a single line and a view with N newlines spans N+1 lines. The basis of the in-place re-render: the driver records the line count of the view it wrote so the next redraw knows how far up to move the cursor before overwriting. Total; an empty string is one empty line.
|
|
360
|
-
| `renderCursorUp` | function | Returns the cursor-up control sequence that moves the cursor up `count` lines (`ESC[{count}A`), or the empty string when `count` is zero or negative, because no movement is needed and `ESC[0A` is a wasted write. The pure step the in-place re-render uses to climb back over the previous view before clearing it. Total.
|
|
361
|
-
| `redrawPrefix` | function | Returns the full reposition-and-clear prefix to write before re-rendering a prompt view in place — given the line count of the previous view, it moves the cursor up over those lines, returns it to column 0, and erases everything from there to the end of the screen, so the next view is drawn on a clean region and a taller previous view leaves no orphaned rows. Pure; the driver writes this immediately followed by the new view.
|
|
362
|
-
| `fieldToText` | function | Projects any field the walk reads as a line of text into the `TextField` the text reducer takes — `text` itself, and the controls a terminal has no widget for: `number`, `date`, `time`, `datetime`, `color`, and one `file` entry. The label carries that control's format cue from `CONTROL_HINTS`, and a declared `default` becomes the line a bare return submits. The projection carries no rule, because the authoritative form still evaluates the answer this line binds; it exists only so one reducer covers every one of them. |
|
|
363
|
-
| `valueToText` | function | Projects one held answer into the text a read-only line shows — a scalar as itself, a boolean as `yes` / `no` (the word the confirm reducer commits), and a list joined by commas. Absence renders as nothing, because a locked field nobody has answered has nothing to show.
|
|
364
|
-
| `filterEnabled` | function | Returns the choices a `select` or `checkbox` field actually offers — the form refuses a disabled choice's value at every door, including a fill, so the walk never puts one in front of the cursor. Pair with `filterDisabled` to tell the reader what was withheld.
|
|
365
|
-
| `filterDisabled` | function | Returns the choices a `select` or `checkbox` field shows but refuses — the complement of `filterEnabled`, rendered by `renderUnavailableLine` above the list so a reader sees why a declared choice is missing from it.
|
|
366
|
-
| `renderGroupHeader` | function | Renders the section header the walk writes when it enters a new field group, painted by the `message` role.
|
|
367
|
-
| `renderLockedLine` | function | Renders the read-only line a locked field shows — its label, the `LOCKED_MARK`, and the answer the form already holds. The walk writes this instead of a prompt, because the field is still validated and still submitted but must not be edited here.
|
|
368
|
-
| `renderSuggestionLine` | function | Renders the line listing an open select's offered values above its text prompt — a suggestion list, because an open select admits an answer the list does not offer.
|
|
369
|
-
| `renderUnavailableLine` | function | Renders the line naming the choices a field shows but refuses, written above the list the walk drives.
|
|
370
|
-
| `renderNumberedList` | function | Renders the numbered choice list the non-TTY fallback prints — a piped stream cannot navigate with arrow keys, so each offered choice is printed with the number the reader types back. One line per choice, with no trailing newline.
|
|
354
|
+
| API | Kind | Summary |
|
|
355
|
+
| ----------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
356
|
+
| `isInputStream` | function | Checks whether `value` is a usable `InputStreamInterface` — a record with callable `on` / `off` `'data'` subscription methods. A total type guard: it never throws and returns `false` for anything off-shape, so it narrows the one unavoidable input boundary (the real `process.stdin`, or a fake TTY a test injects) to the exact slice the driver reads, never an assertion. |
|
|
357
|
+
| `isReadable` | function | Checks whether `value` is a Node `NodeJS.ReadableStream` — a total structural guard checking for the callable `read` / `pipe` / `on` that `node:readline`'s `createInterface` requires as its `input`. The non-TTY fallback narrows the resolved input stream through this before handing it to readline, never through an assertion, so a real piped `process.stdin` (or a `PassThrough` a test injects) crosses into the readline boundary honestly. Never throws; returns `false` for a minimal fake that isn't a full readable. |
|
|
358
|
+
| `supportsRawMode` | function | Checks whether an input stream can be driven in raw mode — it reports `isTTY === true` and exposes a callable `setRawMode`. The `Terminal` probes this to choose its path: true selects the interactive raw-mode fields, with arrow-key navigation and a live re-render; false selects the `node:readline` line-input fallback, because a piped or non-terminal stream cannot enter raw mode. Total — never throws. |
|
|
359
|
+
| `lineCount` | function | Counts the terminal lines a rendered prompt `view` occupies — one more than its newline count, so a view with no newline is a single line and a view with N newlines spans N+1 lines. The basis of the in-place re-render: the driver records the line count of the view it wrote so the next redraw knows how far up to move the cursor before overwriting. Total; an empty string is one empty line. |
|
|
360
|
+
| `renderCursorUp` | function | Returns the cursor-up control sequence that moves the cursor up `count` lines (`ESC[{count}A`), or the empty string when `count` is zero or negative, because no movement is needed and `ESC[0A` is a wasted write. The pure step the in-place re-render uses to climb back over the previous view before clearing it. Total. |
|
|
361
|
+
| `redrawPrefix` | function | Returns the full reposition-and-clear prefix to write before re-rendering a prompt view in place — given the line count of the previous view, it moves the cursor up over those lines, returns it to column 0, and erases everything from there to the end of the screen, so the next view is drawn on a clean region and a taller previous view leaves no orphaned rows. Pure; the driver writes this immediately followed by the new view. |
|
|
362
|
+
| `fieldToText` | function | Projects any field the walk reads as a line of text into the `TextField` the text reducer takes — `text` itself, and the controls a terminal has no widget for: `number`, `date`, `time`, `datetime`, `color`, one `list` entry, and one `file` entry. The label carries that control's format cue from `CONTROL_HINTS`, and a declared `default` becomes the line a bare return submits. The projection carries no rule, because the authoritative form still evaluates the answer this line binds; it exists only so one reducer covers every one of them. |
|
|
363
|
+
| `valueToText` | function | Projects one held answer into the text a read-only line shows — a scalar as itself, a boolean as `yes` / `no` (the word the confirm reducer commits), and a list joined by commas. Absence renders as nothing, because a locked field nobody has answered has nothing to show. |
|
|
364
|
+
| `filterEnabled` | function | Returns the choices a `select` or `checkbox` field actually offers — the form refuses a disabled choice's value at every door, including a fill, so the walk never puts one in front of the cursor. Pair with `filterDisabled` to tell the reader what was withheld. |
|
|
365
|
+
| `filterDisabled` | function | Returns the choices a `select` or `checkbox` field shows but refuses — the complement of `filterEnabled`, rendered by `renderUnavailableLine` above the list so a reader sees why a declared choice is missing from it. |
|
|
366
|
+
| `renderGroupHeader` | function | Renders the section header the walk writes when it enters a new field group, painted by the `message` role. |
|
|
367
|
+
| `renderLockedLine` | function | Renders the read-only line a locked field shows — its label, the `LOCKED_MARK`, and the answer the form already holds. The walk writes this instead of a prompt, because the field is still validated and still submitted but must not be edited here. |
|
|
368
|
+
| `renderSuggestionLine` | function | Renders the line listing an open select's offered values above its text prompt — a suggestion list, because an open select admits an answer the list does not offer. |
|
|
369
|
+
| `renderUnavailableLine` | function | Renders the line naming the choices a field shows but refuses, written above the list the walk drives. |
|
|
370
|
+
| `renderNumberedList` | function | Renders the numbered choice list the non-TTY fallback prints — a piped stream cannot navigate with arrow keys, so each offered choice is printed with the number the reader types back. One line per choice, with no trailing newline. |
|
|
371
371
|
|
|
372
372
|
### The server constants
|
|
373
373
|
|
|
@@ -385,6 +385,7 @@ A `Shape` cell holds the constant's declared type.
|
|
|
385
385
|
| `CLEAR_DOWN` | const | `string` | Erases from the cursor down to the end of the screen (`ESC[J`) — wipes the whole previous view, which a `select` or `checkbox` can spread over several lines, in one write before the new view is rendered, so a redraw never leaves orphaned rows behind. |
|
|
386
386
|
| `CONTROL_HINTS` | const | `Readonly<Partial<Record<FieldControl, string>>>` | Holds the format cue appended to a field's label for each control the walk reads as a line of text — the terminal has no date picker, no color well, and no file chooser, so the accepted shape is stated instead. A control with no entry needs none: `text` and `editor` accept any line, `password` masks one, and `confirm`, `select`, and `checkbox` are answered by key rather than by format. The form's own rules still decide whether the typed value is acceptable. |
|
|
387
387
|
| `FILE_HINT` | const | `string` | Holds the instruction a `file` field with `multiple` shows before its entries — one path per line, and a blank line ends the list. |
|
|
388
|
+
| `LIST_HINT` | const | `string` | Holds the instruction a `list` field shows before its entries — one entry per line, and a blank line ends the list. |
|
|
388
389
|
| `SUGGESTION_LEAD` | const | `string` | Holds the lead on the line listing an open `select`'s offered values, which a typed answer can ignore. |
|
|
389
390
|
| `UNAVAILABLE_LEAD` | const | `string` | Holds the lead on the line listing the choices a `select` or `checkbox` shows but refuses, so a reader sees why one is missing from the list it heads. |
|
|
390
391
|
| `LOCKED_MARK` | const | `string` | Holds the mark on a locked field's line — the walk renders its value and moves on, because the form refuses an edit there. |
|
|
@@ -561,9 +562,10 @@ These invariants hold across `src/core`, `src/server`, and this guide.
|
|
|
561
562
|
carries no `name` with the raw sequence preserved, so the driver cannot crash on a stray byte.
|
|
562
563
|
`KeyEvent.name` is therefore optional and absence is `undefined`: an undecoded key has no name
|
|
563
564
|
rather than an empty one, and every reducer reads it as a key it does not consume.
|
|
564
|
-
10. **Every control reaches a reducer.** `text`, `number`, `date`, `time`, `datetime`, `color`,
|
|
565
|
-
each `file` entry are read as one line of text through `fieldToText`, which
|
|
566
|
-
control's format cue to the label;
|
|
565
|
+
10. **Every control reaches a reducer.** `text`, `number`, `date`, `time`, `datetime`, `color`, each
|
|
566
|
+
`list` entry, and each `file` entry are read as one line of text through `fieldToText`, which
|
|
567
|
+
appends that control's format cue to the label; a `list` reads one entry per line until a blank
|
|
568
|
+
one and keeps its declared `default` when none is typed; `password`, `confirm`, `editor`, `select`, and `checkbox`
|
|
567
569
|
each drive their own reducer. An open select is a suggestion list plus a typed line, because
|
|
568
570
|
`open` means the answer need not come from the list. A disabled choice is named on an
|
|
569
571
|
unavailable line and never offered, since the form refuses its value at every door. A `hidden`
|