@texuli/spec 0.4.0 → 0.6.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/README.md CHANGED
@@ -14,9 +14,14 @@ by Texuli's Kotlin Multiplatform twin. The package is framework-free: no
14
14
  DOM, no Svelte, no Node APIs and no runtime dependencies.
15
15
 
16
16
  > **Status: 0.x release.** Texuli is in development. The packages are
17
- > published to npm as `0.4.0`, a pre-1.0 release (1.0.0 will be the first
18
- > stable release). Under 0.x, a new minor (`0.5.0`) may contain breaking
19
- > changes, so pin the exact version or use `^0.4.0`, which accepts only 0.4.x.
17
+ > published to npm as `0.6.0`, a pre-1.0 release (1.0.0 will be the first
18
+ > stable release). Under 0.x, a new minor (`0.7.0`) may contain breaking
19
+ > changes, so pin the exact version or use `^0.6.0`, which accepts only 0.6.x.
20
+
21
+ > **New in `0.6.0`.** `@texuli/spec@0.6.0` carries **spec 0.6.0**: the
22
+ > closed motion vocabulary (`@texuli/spec/motion`, below), and the new
23
+ > `@texuli/spec/stream` subpath reads streamed blueprints. Every `0.5.0`
24
+ > envelope migrates by stamping `spec`.
20
25
 
21
26
  > **New in `0.4.0`.** `@texuli/spec@0.4.0` carries **spec 0.4.0**: spec
22
27
  > 0.3.0 (state, bindings, closed actions, `reorder`, workflow graphs), spec
@@ -56,7 +61,7 @@ import {
56
61
  validateInstance,
57
62
  } from '@texuli/spec';
58
63
 
59
- SPEC_VERSION; // the spec version: '0.4.0'
64
+ SPEC_VERSION; // the spec version: '0.6.0' in @texuli/spec@0.6.0
60
65
  SPEC_COMPONENT_IDS.includes('alert'); // true: the closed component vocabulary
61
66
 
62
67
  // The dialect validator: a closed subset of JSON Schema 2020-12.
@@ -88,7 +93,7 @@ import { type Schema, type SpecCatalog, validateBlueprint } from '@texuli/spec';
88
93
  import { migrateBlueprint } from '@texuli/spec/migrate';
89
94
  import { validateWorkflowGraph } from '@texuli/spec/workflow';
90
95
 
91
- const migrated = migrateBlueprint(blueprint); // 0.2.0 → 0.3.0 → 0.4.0; never throws
96
+ const migrated = migrateBlueprint(blueprint); // 0.2.0 → … → 0.5.0 → 0.6.0; never throws
92
97
  const result = validateBlueprint(migrated.blueprint, {
93
98
  blueprintSchema: blueprintSchemaJson as Schema,
94
99
  catalog: componentsJson as SpecCatalog,
@@ -120,6 +125,8 @@ warning without failing.
120
125
  | `@texuli/spec/workflow` | Runtime, opt-in: workflow graph validation | `validateWorkflowGraph` (pass it in `SpecDocuments.validateWorkflowGraph`) |
121
126
  | `@texuli/spec/migrate` | Runtime, opt-in: envelope migrations | `migrateBlueprint`, `SPEC_MIGRATIONS`, `UNDECLARED_SPEC_VERSION` |
122
127
  | `@texuli/spec/surface` | Runtime: the state surface rules every validator shares (no imports) | `checkStateSurface`, `surfaceInputs`, `SPEC_CONTROL_STATE_TYPES`, `SURFACE_PLAYER_CODES`, `LIST_TEXT_PATTERN` |
128
+ | `@texuli/spec/motion` | Runtime (new in `0.6.0`, spec `0.6.0`): the closed motion vocabulary and its check, shared by the catalog and the player ([motion.md](https://github.com/DailybotHQ/texuli/blob/main/packages/spec/docs/motion.md)) | `SPEC_MOTION_PRESETS`, `SPEC_MOTION_DURATIONS`, `SPEC_MOTION_STAGGERS`, `SPEC_MOTION_LEVELS`, `checkNodeMotion`, `checkEnvelopeMotion` |
129
+ | `@texuli/spec/stream` | Runtime, opt-in (new in `0.6.0`): streamed blueprints ([stream.md](https://github.com/DailybotHQ/texuli/blob/main/packages/spec/docs/stream.md)) | `createJsonTokenizer`, `createBlueprintAssembler`, `createBlueprintStream`, `STREAM_NODE_ORDER` |
123
130
  | `@texuli/spec/codegen` | Build time: generation and conformance | `buildSpec`, `projectCatalog`, `checkConformance`, `checkCatalogContract`, `formatJson`, `nextSpecVersion`, `checkVersionHistory` |
124
131
 
125
132
  ## Key guarantees
@@ -148,6 +155,19 @@ warning without failing.
148
155
  component slides. The change is additive: every 0.3.0 envelope is a valid
149
156
  0.4.0 envelope, and the `0.3.0 → 0.4.0` migration only stamps `spec`. The
150
157
  deck model itself is validated by `@texuli/deck`, not by this package.
158
+ - **Motion as data (spec 0.6.0, new in `0.6.0`).** A node may declare `motion`
159
+ (`enter` and `exit` presets `fade`, `rise`, `scale`, `slide-start` and
160
+ `slide-end`; `layout`; `stagger` and `duration` in token steps) and the
161
+ envelope a `motion.level` (`full`, `subtle`, `none`). Names map to the
162
+ motion tokens: a blueprint never carries CSS, keyframes, milliseconds or
163
+ code, and anything else is refused. The `0.5.0 → 0.6.0` migration only
164
+ stamps `spec`.
165
+ - **Streams parse, never guess.** `@texuli/spec/stream` (new in `0.6.0`)
166
+ reads a blueprint split anywhere in linear time under a byte budget and a
167
+ depth cap, reports a node only once its shell (everything but
168
+ `children`) has closed, and refuses duplicate and prototype keys. It
169
+ judges no rule: the catalog checks each node, and the complete draft
170
+ goes through `validateDraft`.
151
171
  - **Structure only.** This validator checks structure. URL policy, tree
152
172
  limits and rendering live in the runtime validator of
153
173
  [`@texuli/catalog`](https://www.npmjs.com/package/@texuli/catalog). The
@@ -190,11 +210,16 @@ catalog hash, and from 0.3.0 may declare the exact spec version it was
190
210
  written for (`spec`; omitted means 0.2.0). `migrateBlueprint`
191
211
  (`@texuli/spec/migrate`) brings an
192
212
  envelope up to the current spec with pure, per-version steps
193
- (`src/migrations/`: `0.2.0 → 0.3.0` and `0.3.0 → 0.4.0`, both of which only
194
- stamp `spec`); a compatible version range is planned, not built. The
195
- repository pins spec `0.4.0` to digest
196
- `fac29f542b16ddb78ab401c508e5b8346f655991c56e25aa811f4931661a8cb3` in
197
- `versions.json`.
213
+ (`src/migrations/`: `0.2.0 → 0.3.0`, `0.3.0 → 0.4.0`, `0.4.0 → 0.5.0` and
214
+ `0.5.0 → 0.6.0`, each of which only stamps `spec`); a compatible version range is planned,
215
+ not built. `@texuli/spec@0.6.0` carries spec `0.6.0` (digest
216
+ `37141b85b9ab9f02af849a62a9c97ba729d6c5c2705130290bcdf45b308ea4cc`), which
217
+ adds the closed motion vocabulary (ADR-012); `@texuli/spec@0.5.0` carried
218
+ spec `0.5.0`, which added the `data-cohort-table`,
219
+ `data-notification-inbox`, `data-filter-builder` and `time-field` catalog
220
+ ids, and `@texuli/spec@0.4.0` spec `0.4.0` (digest
221
+ `fac29f542b16ddb78ab401c508e5b8346f655991c56e25aa811f4931661a8cb3`). Every
222
+ pin is in `versions.json`.
198
223
 
199
224
  The full rules (the digest, `versions.json` pins, regeneration and the
200
225
  conformance harness) are in
@@ -209,6 +234,10 @@ conformance harness) are in
209
234
  [packages/spec](https://github.com/DailybotHQ/texuli/tree/main/packages/spec)
210
235
  - The contract in depth (the JSON Schema dialect, projections, conformance
211
236
  and versioning): [contract.md](https://github.com/DailybotHQ/texuli/blob/main/packages/spec/docs/contract.md)
237
+ - The motion vocabulary (spec 0.6.0):
238
+ [motion.md](https://github.com/DailybotHQ/texuli/blob/main/packages/spec/docs/motion.md)
239
+ - Streamed blueprints (the incremental tokenizer, the canonical order and
240
+ node shells; new in `0.6.0`): [stream.md](https://github.com/DailybotHQ/texuli/blob/main/packages/spec/docs/stream.md)
212
241
  - The Kotlin Multiplatform twin that consumes the generated models:
213
242
  [native/README.md](https://github.com/DailybotHQ/texuli/blob/main/native/README.md)
214
243
  - Architecture: [ARCHITECTURE.md](https://github.com/DailybotHQ/texuli/blob/main/docs/ARCHITECTURE.md)
@@ -47,7 +47,10 @@ export interface BlueprintEditOp {
47
47
  tree?: BlueprintNode;
48
48
  }
49
49
  export type BlueprintNodePath = readonly number[];
50
- /** One catalog node: id, props, children (each with an optional named slot), when, bind and on. */
50
+ /**
51
+ * One catalog node: id, props, children (each with an optional named slot), when, bind, on and
52
+ * motion.
53
+ */
51
54
  export interface BlueprintNode {
52
55
  /** A catalog component id. */
53
56
  id: string;
@@ -63,6 +66,7 @@ export interface BlueprintNode {
63
66
  /** Two-way bindings: a prop the component lists as bindable → a $state binding it writes back. */
64
67
  bind?: Readonly<Record<string, BlueprintBinding>>;
65
68
  on?: BlueprintEventMap;
69
+ motion?: BlueprintNodeMotion;
66
70
  }
67
71
  /**
68
72
  * Shows the node when true (audit G07): the closed Condition AST of the blocks formula grammar.
@@ -185,11 +189,47 @@ export interface BlueprintReorder {
185
189
  pull?: 'move' | 'clone' | 'none';
186
190
  disabled?: boolean;
187
191
  }
192
+ /**
193
+ * A closed motion preset (ADR-013): fade, rise (fade in from the block end), scale (fade and grow
194
+ * from 96%), slide-start or slide-end (fade in from the inline start or end, mirrored right to
195
+ * left). The runtime maps each to token keyframes; a blueprint never carries keyframes.
196
+ */
197
+ export type BlueprintMotionPreset = 'fade' | 'rise' | 'scale' | 'slide-start' | 'slide-end';
198
+ /** A duration token step (@texuli/tokens motion durations), never milliseconds. */
199
+ export type BlueprintMotionDuration = 'fast' | 'base' | 'slow';
200
+ /**
201
+ * The offset between the entering children of this node, in token steps: none, fast or base. The
202
+ * runtime caps the whole spread.
203
+ */
204
+ export type BlueprintMotionStagger = 'none' | 'fast' | 'base';
205
+ /**
206
+ * A motion ceiling (ADR-013): full, subtle (shorter, no travel or scale) or none (instant). The
207
+ * host and the reader's reduced-motion preference can only lower it.
208
+ */
209
+ export type BlueprintMotionLevel = 'full' | 'subtle' | 'none';
210
+ /**
211
+ * How a node moves (spec 0.6.0, ADR-012): named presets and token steps only. Nothing here is CSS,
212
+ * a duration in milliseconds or code. The server paints the end state, and nothing plays on
213
+ * hydration or under reduced motion.
214
+ */
215
+ export interface BlueprintNodeMotion {
216
+ enter?: BlueprintMotionPreset;
217
+ exit?: BlueprintMotionPreset;
218
+ /** Animate a change of this node's position or size among its siblings (FLIP). */
219
+ layout?: boolean;
220
+ stagger?: BlueprintMotionStagger;
221
+ duration?: BlueprintMotionDuration;
222
+ }
223
+ /** The blueprint's motion settings (spec 0.6.0): the level every node motion plays at. */
224
+ export interface BlueprintEnvelopeMotion {
225
+ level: BlueprintMotionLevel;
226
+ }
188
227
  /**
189
228
  * Envelope version 1 around one catalog tree (ADR-004, audit G13). Mirrors what
190
229
  * @texuli/catalog/envelope reads; a declared status, activation or tier is display data, never
191
230
  * authority. Spec 0.4.0 (ADR-008 + PLAN_008 deck encoding) adds an optional spec version, local
192
- * state and a flow. Planned minor additions: the native profile and several roots.
231
+ * state and a flow; spec 0.6.0 (ADR-012, ADR-013) adds the closed motion vocabulary: a node motion
232
+ * and the envelope motion level. Planned minor additions: the native profile and several roots.
193
233
  */
194
234
  export interface BlueprintEnvelope {
195
235
  format: 'texuli-blueprint';
@@ -213,11 +253,12 @@ export interface BlueprintEnvelope {
213
253
  /**
214
254
  * The spec version the envelope was written for. Omitted means 0.2.0; migrateBlueprint stamps it.
215
255
  */
216
- spec?: '0.3.0' | '0.4.0';
256
+ spec?: '0.3.0' | '0.4.0' | '0.5.0' | '0.6.0';
217
257
  /**
218
258
  * Local state declarations (ADR-008), at most 64. Names, counts and per-type rules are checked by
219
259
  * the validator.
220
260
  */
221
261
  state?: Readonly<Record<string, BlueprintStateDeclaration>>;
222
262
  flow?: BlueprintFlow;
263
+ motion?: BlueprintEnvelopeMotion;
223
264
  }
@@ -1,3 +1,3 @@
1
1
  // Generated by @texuli/spec from schemas/blueprint.schema.json. Do not edit: run the spec generator.
2
- // Spec 0.4.0, digest fac29f542b16ddb78ab401c508e5b8346f655991c56e25aa811f4931661a8cb3
2
+ // Spec 0.6.0, digest 37141b85b9ab9f02af849a62a9c97ba729d6c5c2705130290bcdf45b308ea4cc
3
3
  export {};
@@ -1,3 +1,3 @@
1
1
  // Generated by @texuli/spec from schemas/catalog.schema.json. Do not edit: run the spec generator.
2
- // Spec 0.4.0, digest fac29f542b16ddb78ab401c508e5b8346f655991c56e25aa811f4931661a8cb3
2
+ // Spec 0.6.0, digest 37141b85b9ab9f02af849a62a9c97ba729d6c5c2705130290bcdf45b308ea4cc
3
3
  export {};
@@ -1,3 +1,3 @@
1
1
  // Generated by @texuli/spec from schemas/diagnostics.schema.json. Do not edit: run the spec generator.
2
- // Spec 0.4.0, digest fac29f542b16ddb78ab401c508e5b8346f655991c56e25aa811f4931661a8cb3
2
+ // Spec 0.6.0, digest 37141b85b9ab9f02af849a62a9c97ba729d6c5c2705130290bcdf45b308ea4cc
3
3
  export {};
@@ -1,3 +1,3 @@
1
1
  // Generated by @texuli/spec from schemas/manifest.schema.json. Do not edit: run the spec generator.
2
- // Spec 0.4.0, digest fac29f542b16ddb78ab401c508e5b8346f655991c56e25aa811f4931661a8cb3
2
+ // Spec 0.6.0, digest 37141b85b9ab9f02af849a62a9c97ba729d6c5c2705130290bcdf45b308ea4cc
3
3
  export {};
@@ -1,7 +1,7 @@
1
1
  /** The built catalog this vocabulary was projected from. */
2
- export declare const SPEC_CATALOG_HASH = "8a449985d45c7bfd1dc2285f3513dfd6ca0e3e38260c0a541fdb7b17e9213006";
2
+ export declare const SPEC_CATALOG_HASH = "4ac7ecedd1c59d1999a9f7eea2f523c7653fececb97258a478f09253f6b6057f";
3
3
  /** Every closed component id, sorted. */
4
- export declare const SPEC_COMPONENT_IDS: readonly ["action-form", "alert", "alert-dialog", "analytics-grid", "app-nav", "badge", "banner", "blocks-code", "blocks-data", "blocks-diagram", "blocks-embed", "blocks-interactive", "blocks-map", "blocks-media", "blocks-structure", "blocks-text", "breadcrumb", "button", "button-group", "callout", "card", "charts-area", "charts-bar", "charts-bubble", "charts-bullet", "charts-canvas-renderer", "charts-cartesian", "charts-combo", "charts-donut", "charts-funnel", "charts-gauge", "charts-geo-map", "charts-geo-point-map", "charts-heatmap", "charts-line", "charts-network", "charts-pie", "charts-progress-ring", "charts-sankey", "charts-scatter", "charts-sparkline", "charts-stacked-bar", "charts-sunburst", "charts-treemap", "chat-message", "chat-thread", "checkbox", "chip", "code", "combobox", "command-palette", "content-layout", "context-menu", "context-menu-trigger", "control-choice", "control-filter", "control-slider", "control-toggle", "data-board", "data-calendar", "data-files", "data-grid", "data-map", "data-timeline", "data-tree-view", "deck-slide", "description", "detail-panel", "dialog", "drawer", "dropdown-menu", "dropdown-menu-content", "dropdown-menu-item", "dropdown-menu-trigger", "empty-state", "error-state", "field", "field-error", "flow-action", "flow-view", "form", "form-field", "form-submit", "host-region", "hover-card", "hover-card-content", "icon-button", "inline-text", "input", "input-group", "kbd", "kpi", "label", "layout-auth-split", "layout-blog", "layout-card", "layout-chrome", "layout-dashboard-grid", "layout-deck", "layout-doc", "layout-docs", "layout-drawer", "layout-dual-sidebar", "layout-editorial", "layout-grid", "layout-host", "layout-landing", "layout-marketing", "layout-page", "layout-pricing", "layout-row", "layout-settings", "layout-sidebar", "layout-split", "layout-stack", "layout-tile", "layout-top-nav", "layout-wizard", "media-composition-canvas", "media-composition-svg", "media-image", "menubar", "menubar-trigger", "multi-select", "nav-icon", "navigation-menu", "pagination", "popover", "popover-content", "radio", "radio-group", "responsive-modal", "screen-outlet", "select", "select-content", "select-group", "select-item", "select-label", "select-portal", "select-separator", "select-trigger", "select-value", "select-viewport", "sheet", "sidebar", "sortable-list", "split-view", "stat", "stepper", "switch", "table", "tag", "text-counter", "textarea", "timeline", "toaster", "tooltip", "workflow-graph"];
4
+ export declare const SPEC_COMPONENT_IDS: readonly ["action-form", "alert", "alert-dialog", "analytics-grid", "app-nav", "badge", "banner", "blocks-code", "blocks-data", "blocks-diagram", "blocks-embed", "blocks-interactive", "blocks-map", "blocks-media", "blocks-structure", "blocks-text", "breadcrumb", "button", "button-group", "callout", "card", "charts-area", "charts-bar", "charts-bubble", "charts-bullet", "charts-canvas-renderer", "charts-cartesian", "charts-combo", "charts-donut", "charts-funnel", "charts-gauge", "charts-geo-map", "charts-geo-point-map", "charts-heatmap", "charts-line", "charts-network", "charts-pie", "charts-progress-ring", "charts-sankey", "charts-scatter", "charts-sparkline", "charts-stacked-bar", "charts-sunburst", "charts-treemap", "chat-message", "chat-thread", "checkbox", "chip", "code", "combobox", "command-palette", "content-layout", "context-menu", "context-menu-trigger", "control-choice", "control-filter", "control-slider", "control-toggle", "data-board", "data-calendar", "data-cohort-table", "data-files", "data-filter-builder", "data-grid", "data-map", "data-notification-inbox", "data-timeline", "data-tree-view", "deck-slide", "description", "detail-panel", "dialog", "drawer", "dropdown-menu", "dropdown-menu-content", "dropdown-menu-item", "dropdown-menu-trigger", "empty-state", "error-state", "field", "field-error", "flow-action", "flow-view", "form", "form-field", "form-submit", "host-region", "hover-card", "hover-card-content", "icon-button", "inline-text", "input", "input-group", "kbd", "kpi", "label", "layout-auth-split", "layout-blog", "layout-card", "layout-chrome", "layout-dashboard-grid", "layout-deck", "layout-doc", "layout-docs", "layout-drawer", "layout-dual-sidebar", "layout-editorial", "layout-grid", "layout-host", "layout-landing", "layout-marketing", "layout-page", "layout-pricing", "layout-row", "layout-settings", "layout-sidebar", "layout-split", "layout-stack", "layout-tile", "layout-top-nav", "layout-wizard", "media-composition-canvas", "media-composition-svg", "media-image", "menubar", "menubar-trigger", "multi-select", "nav-icon", "navigation-menu", "pagination", "popover", "popover-content", "radio", "radio-group", "responsive-modal", "screen-outlet", "select", "select-content", "select-group", "select-item", "select-label", "select-portal", "select-separator", "select-trigger", "select-value", "select-viewport", "sheet", "sidebar", "sortable-list", "split-view", "stat", "stepper", "switch", "table", "tag", "text-counter", "textarea", "time-field", "timeline", "toaster", "tooltip", "workflow-graph"];
5
5
  export type SpecComponentId = (typeof SPEC_COMPONENT_IDS)[number];
6
6
  /** The props a blueprint may set on each component (platform extensions left out). */
7
7
  export interface SpecComponentProps {
@@ -481,12 +481,31 @@ export interface SpecComponentProps {
481
481
  timeZone?: string;
482
482
  view?: 'month' | 'week' | 'day' | 'agenda';
483
483
  };
484
+ 'data-cohort-table': {
485
+ average?: boolean;
486
+ caption: string;
487
+ captionHidden?: boolean;
488
+ density?: 'compact' | 'default' | 'comfortable';
489
+ heat?: boolean;
490
+ locale?: string;
491
+ max?: number;
492
+ periods?: readonly unknown[];
493
+ rows: readonly unknown[];
494
+ valueKind?: 'rate' | 'count';
495
+ };
484
496
  'data-files': {
485
497
  density?: 'comfortable' | 'compact';
486
498
  entries: readonly unknown[];
487
499
  label: string;
488
500
  layout?: 'grid' | 'list';
489
501
  };
502
+ 'data-filter-builder': {
503
+ disabled?: boolean;
504
+ fields: readonly unknown[];
505
+ label: string;
506
+ maxDepth?: number;
507
+ value?: Readonly<Record<string, unknown>>;
508
+ };
490
509
  'data-grid': {
491
510
  caption: string;
492
511
  columns: readonly unknown[];
@@ -500,6 +519,15 @@ export interface SpecComponentProps {
500
519
  listPlacement?: 'side' | 'below';
501
520
  markers: readonly unknown[];
502
521
  };
522
+ 'data-notification-inbox': {
523
+ filter?: 'all' | 'unread';
524
+ headingLevel?: number;
525
+ items: readonly unknown[];
526
+ locale?: string;
527
+ markReadOnOpen?: boolean;
528
+ now?: string;
529
+ timeZone?: string;
530
+ };
503
531
  'data-timeline': {
504
532
  dependencies?: readonly unknown[];
505
533
  label: string;
@@ -1088,6 +1116,20 @@ export interface SpecComponentProps {
1088
1116
  rows?: number;
1089
1117
  value?: string;
1090
1118
  };
1119
+ 'time-field': {
1120
+ description?: string;
1121
+ disabled?: boolean;
1122
+ error?: string;
1123
+ hourCycle?: number;
1124
+ label: string;
1125
+ labels?: Readonly<Record<string, unknown>>;
1126
+ locale?: string;
1127
+ minuteStep?: number;
1128
+ name?: string;
1129
+ required?: boolean;
1130
+ size?: 'sm' | 'md';
1131
+ value?: string;
1132
+ };
1091
1133
  timeline: {
1092
1134
  hrefTrust?: 'developer' | 'untrusted';
1093
1135
  items?: readonly unknown[];
@@ -1,7 +1,7 @@
1
1
  // Generated by @texuli/spec from catalog/components.json. Do not edit: run the spec generator.
2
- // Spec 0.4.0, digest fac29f542b16ddb78ab401c508e5b8346f655991c56e25aa811f4931661a8cb3
2
+ // Spec 0.6.0, digest 37141b85b9ab9f02af849a62a9c97ba729d6c5c2705130290bcdf45b308ea4cc
3
3
  /** The built catalog this vocabulary was projected from. */
4
- export const SPEC_CATALOG_HASH = '8a449985d45c7bfd1dc2285f3513dfd6ca0e3e38260c0a541fdb7b17e9213006';
4
+ export const SPEC_CATALOG_HASH = '4ac7ecedd1c59d1999a9f7eea2f523c7653fececb97258a478f09253f6b6057f';
5
5
  /** Every closed component id, sorted. */
6
6
  export const SPEC_COMPONENT_IDS = [
7
7
  'action-form',
@@ -64,9 +64,12 @@ export const SPEC_COMPONENT_IDS = [
64
64
  'control-toggle',
65
65
  'data-board',
66
66
  'data-calendar',
67
+ 'data-cohort-table',
67
68
  'data-files',
69
+ 'data-filter-builder',
68
70
  'data-grid',
69
71
  'data-map',
72
+ 'data-notification-inbox',
70
73
  'data-timeline',
71
74
  'data-tree-view',
72
75
  'deck-slide',
@@ -158,6 +161,7 @@ export const SPEC_COMPONENT_IDS = [
158
161
  'tag',
159
162
  'text-counter',
160
163
  'textarea',
164
+ 'time-field',
161
165
  'timeline',
162
166
  'toaster',
163
167
  'tooltip',
@@ -1,3 +1,3 @@
1
1
  // Generated by @texuli/spec from schemas/workflow.schema.json. Do not edit: run the spec generator.
2
- // Spec 0.4.0, digest fac29f542b16ddb78ab401c508e5b8346f655991c56e25aa811f4931661a8cb3
2
+ // Spec 0.6.0, digest 37141b85b9ab9f02af849a62a9c97ba729d6c5c2705130290bcdf45b308ea4cc
3
3
  export {};
@@ -0,0 +1,8 @@
1
+ import type { SpecMigration } from './types.js';
2
+ /**
3
+ * 0.4.0 → 0.5.0 is additive (PLAN_009 catalog coverage): the catalog gains
4
+ * the data-cohort-table, data-notification-inbox and data-filter-builder
5
+ * ids and the app-tier time-field id. Every 0.4.0 envelope is a valid 0.5.0
6
+ * envelope, so the step only stamps `spec`.
7
+ */
8
+ export declare const migration040to050: SpecMigration;
@@ -0,0 +1,11 @@
1
+ /**
2
+ * 0.4.0 → 0.5.0 is additive (PLAN_009 catalog coverage): the catalog gains
3
+ * the data-cohort-table, data-notification-inbox and data-filter-builder
4
+ * ids and the app-tier time-field id. Every 0.4.0 envelope is a valid 0.5.0
5
+ * envelope, so the step only stamps `spec`.
6
+ */
7
+ export const migration040to050 = Object.freeze({
8
+ from: '0.4.0',
9
+ to: '0.5.0',
10
+ migrate: (envelope) => ({ ...envelope, spec: '0.5.0' }),
11
+ });
@@ -0,0 +1,9 @@
1
+ import type { SpecMigration } from './types.js';
2
+ /**
3
+ * 0.5.0 → 0.6.0 is additive (PLAN_010 T-motion-spec, ADR-012 and ADR-013):
4
+ * nodes gain an optional closed `motion` (enter and exit presets, `layout`,
5
+ * `stagger` and `duration` in token steps) and the envelope an optional
6
+ * `motion.level`. Every 0.5.0 envelope is a valid 0.6.0 envelope, so the
7
+ * step only stamps `spec`.
8
+ */
9
+ export declare const migration050to060: SpecMigration;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * 0.5.0 → 0.6.0 is additive (PLAN_010 T-motion-spec, ADR-012 and ADR-013):
3
+ * nodes gain an optional closed `motion` (enter and exit presets, `layout`,
4
+ * `stagger` and `duration` in token steps) and the envelope an optional
5
+ * `motion.level`. Every 0.5.0 envelope is a valid 0.6.0 envelope, so the
6
+ * step only stamps `spec`.
7
+ */
8
+ export const migration050to060 = Object.freeze({
9
+ from: '0.5.0',
10
+ to: '0.6.0',
11
+ migrate: (envelope) => ({ ...envelope, spec: '0.6.0' }),
12
+ });
@@ -2,12 +2,16 @@ import { isRecord } from "../dialect.js";
2
2
  import { SPEC_VERSION } from "../version.js";
3
3
  import { migration020to030 } from "./0.2.0-0.3.0.js";
4
4
  import { migration030to040 } from "./0.3.0-0.4.0.js";
5
+ import { migration040to050 } from "./0.4.0-0.5.0.js";
6
+ import { migration050to060 } from "./0.5.0-0.6.0.js";
5
7
  /** The version an envelope without `spec` was written for. */
6
8
  export const UNDECLARED_SPEC_VERSION = '0.2.0';
7
9
  /** Every step, in order; each `to` is the next step's `from`. */
8
10
  export const SPEC_MIGRATIONS = Object.freeze([
9
11
  migration020to030,
10
12
  migration030to040,
13
+ migration040to050,
14
+ migration050to060,
11
15
  ]);
12
16
  /**
13
17
  * Bring a blueprint envelope up to `target` (the current spec by default)
@@ -0,0 +1,40 @@
1
+ /**
2
+ * `@texuli/spec/motion` — the closed motion vocabulary of spec 0.6.0
3
+ * (ADR-012, ADR-013) in code, mirrored from `schemas/blueprint.schema.json`
4
+ * (`tests/spec.test.ts` asserts they stay equal), and the one structural
5
+ * check every runtime shares: `@texuli/catalog` refuses with it, and
6
+ * `@texuli/player` paints only what it accepted.
7
+ *
8
+ * Agents name presets and token steps. Nothing here is CSS, keyframes, a
9
+ * duration in milliseconds or code. No imports at runtime.
10
+ */
11
+ import type { BlueprintEnvelopeMotion, BlueprintMotionDuration, BlueprintMotionLevel, BlueprintMotionPreset, BlueprintMotionStagger, BlueprintNodeMotion } from './generated/typescript/blueprint.js';
12
+ export type { BlueprintEnvelopeMotion, BlueprintMotionDuration, BlueprintMotionLevel, BlueprintMotionPreset, BlueprintMotionStagger, BlueprintNodeMotion, };
13
+ /** Enter and exit presets (`$defs.motionPreset`). */
14
+ export declare const SPEC_MOTION_PRESETS: readonly BlueprintMotionPreset[];
15
+ /** Duration token steps (`$defs.motionDuration`); `base` when omitted. */
16
+ export declare const SPEC_MOTION_DURATIONS: readonly BlueprintMotionDuration[];
17
+ /** Stagger token steps between entering children (`$defs.motionStagger`). */
18
+ export declare const SPEC_MOTION_STAGGERS: readonly BlueprintMotionStagger[];
19
+ /** Motion ceilings (`$defs.motionLevel`), highest first. */
20
+ export declare const SPEC_MOTION_LEVELS: readonly BlueprintMotionLevel[];
21
+ /** The fields a node `motion` takes (`$defs.nodeMotion`). */
22
+ export declare const SPEC_NODE_MOTION_FIELDS: readonly string[];
23
+ export type SpecMotionCheck<T> = {
24
+ ok: true;
25
+ motion: Readonly<T>;
26
+ }
27
+ /** `path` is relative to the motion value (`""`, `.enter` …). */
28
+ | {
29
+ ok: false;
30
+ path: string;
31
+ message: string;
32
+ };
33
+ /**
34
+ * Check a node `motion` against the closed vocabulary. Returns a frozen
35
+ * copy with only the known fields, or the first refusal. Linear in the
36
+ * number of keys, and it stops at the first unknown one.
37
+ */
38
+ export declare function checkNodeMotion(value: unknown): SpecMotionCheck<BlueprintNodeMotion>;
39
+ /** Check an envelope `motion` (`{ level }`). Returns a frozen copy, or the first refusal. */
40
+ export declare function checkEnvelopeMotion(value: unknown): SpecMotionCheck<BlueprintEnvelopeMotion>;
package/dist/motion.js ADDED
@@ -0,0 +1,133 @@
1
+ /**
2
+ * `@texuli/spec/motion` — the closed motion vocabulary of spec 0.6.0
3
+ * (ADR-012, ADR-013) in code, mirrored from `schemas/blueprint.schema.json`
4
+ * (`tests/spec.test.ts` asserts they stay equal), and the one structural
5
+ * check every runtime shares: `@texuli/catalog` refuses with it, and
6
+ * `@texuli/player` paints only what it accepted.
7
+ *
8
+ * Agents name presets and token steps. Nothing here is CSS, keyframes, a
9
+ * duration in milliseconds or code. No imports at runtime.
10
+ */
11
+ /** Enter and exit presets (`$defs.motionPreset`). */
12
+ export const SPEC_MOTION_PRESETS = Object.freeze([
13
+ 'fade',
14
+ 'rise',
15
+ 'scale',
16
+ 'slide-start',
17
+ 'slide-end',
18
+ ]);
19
+ /** Duration token steps (`$defs.motionDuration`); `base` when omitted. */
20
+ export const SPEC_MOTION_DURATIONS = Object.freeze([
21
+ 'fast',
22
+ 'base',
23
+ 'slow',
24
+ ]);
25
+ /** Stagger token steps between entering children (`$defs.motionStagger`). */
26
+ export const SPEC_MOTION_STAGGERS = Object.freeze([
27
+ 'none',
28
+ 'fast',
29
+ 'base',
30
+ ]);
31
+ /** Motion ceilings (`$defs.motionLevel`), highest first. */
32
+ export const SPEC_MOTION_LEVELS = Object.freeze([
33
+ 'full',
34
+ 'subtle',
35
+ 'none',
36
+ ]);
37
+ /** The fields a node `motion` takes (`$defs.nodeMotion`). */
38
+ export const SPEC_NODE_MOTION_FIELDS = Object.freeze([
39
+ 'enter',
40
+ 'exit',
41
+ 'layout',
42
+ 'stagger',
43
+ 'duration',
44
+ ]);
45
+ const isRecord = (value) => value !== null && typeof value === 'object' && !Array.isArray(value);
46
+ /** A short, single-line label for a refused value: never the value itself in full. */
47
+ function describe(value) {
48
+ if (typeof value === 'string')
49
+ return JSON.stringify(value.slice(0, 24));
50
+ if (Array.isArray(value))
51
+ return 'an array';
52
+ return value === null ? 'null' : typeof value;
53
+ }
54
+ function oneOf(value, allowed, field) {
55
+ if (typeof value === 'string' && allowed.includes(value)) {
56
+ return { ok: true, value: value };
57
+ }
58
+ return {
59
+ ok: false,
60
+ path: `.${field}`,
61
+ message: `"${field}" must be one of ${allowed.join(', ')} (got ${describe(value)}).`,
62
+ };
63
+ }
64
+ /**
65
+ * Check a node `motion` against the closed vocabulary. Returns a frozen
66
+ * copy with only the known fields, or the first refusal. Linear in the
67
+ * number of keys, and it stops at the first unknown one.
68
+ */
69
+ export function checkNodeMotion(value) {
70
+ if (!isRecord(value)) {
71
+ return { ok: false, path: '', message: `"motion" must be an object (got ${describe(value)}).` };
72
+ }
73
+ const out = {};
74
+ for (const key of Object.keys(value)) {
75
+ if (!SPEC_NODE_MOTION_FIELDS.includes(key)) {
76
+ return {
77
+ ok: false,
78
+ path: `.${key.slice(0, 32)}`,
79
+ message: `"motion" takes only ${SPEC_NODE_MOTION_FIELDS.join(', ')}; no CSS, keyframes or code.`,
80
+ };
81
+ }
82
+ const entry = value[key];
83
+ if (key === 'layout') {
84
+ if (typeof entry !== 'boolean') {
85
+ return {
86
+ ok: false,
87
+ path: '.layout',
88
+ message: `"layout" must be a boolean (got ${describe(entry)}).`,
89
+ };
90
+ }
91
+ out.layout = entry;
92
+ continue;
93
+ }
94
+ if (key === 'enter' || key === 'exit') {
95
+ const checked = oneOf(entry, SPEC_MOTION_PRESETS, key);
96
+ if (!checked.ok)
97
+ return checked;
98
+ out[key] = checked.value;
99
+ }
100
+ else if (key === 'stagger') {
101
+ const checked = oneOf(entry, SPEC_MOTION_STAGGERS, key);
102
+ if (!checked.ok)
103
+ return checked;
104
+ out.stagger = checked.value;
105
+ }
106
+ else {
107
+ const checked = oneOf(entry, SPEC_MOTION_DURATIONS, key);
108
+ if (!checked.ok)
109
+ return checked;
110
+ out.duration = checked.value;
111
+ }
112
+ }
113
+ return { ok: true, motion: Object.freeze(out) };
114
+ }
115
+ /** Check an envelope `motion` (`{ level }`). Returns a frozen copy, or the first refusal. */
116
+ export function checkEnvelopeMotion(value) {
117
+ if (!isRecord(value)) {
118
+ return { ok: false, path: '', message: `"motion" must be an object (got ${describe(value)}).` };
119
+ }
120
+ for (const key of Object.keys(value)) {
121
+ if (key !== 'level') {
122
+ return {
123
+ ok: false,
124
+ path: `.${key.slice(0, 32)}`,
125
+ message: 'The envelope "motion" takes only "level".',
126
+ };
127
+ }
128
+ }
129
+ const level = oneOf(value.level, SPEC_MOTION_LEVELS, 'level');
130
+ if (!level.ok)
131
+ return level;
132
+ return { ok: true, motion: Object.freeze({ level: level.value }) };
133
+ }