@bestagentkits/render 0.1.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/CHANGELOG.md +29 -0
- package/LICENSE +21 -0
- package/README.md +166 -0
- package/dist/cli.d.ts +18 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +336 -0
- package/dist/cli.js.map +1 -0
- package/dist/diagnostics.d.ts +45 -0
- package/dist/diagnostics.d.ts.map +1 -0
- package/dist/diagnostics.js +57 -0
- package/dist/diagnostics.js.map +1 -0
- package/dist/diagram/adapter.d.ts +71 -0
- package/dist/diagram/adapter.d.ts.map +1 -0
- package/dist/diagram/adapter.js +87 -0
- package/dist/diagram/adapter.js.map +1 -0
- package/dist/errors.d.ts +37 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +42 -0
- package/dist/errors.js.map +1 -0
- package/dist/hash.d.ts +14 -0
- package/dist/hash.d.ts.map +1 -0
- package/dist/hash.js +27 -0
- package/dist/hash.js.map +1 -0
- package/dist/index.d.ts +43 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +25 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/yaml-error.d.ts +14 -0
- package/dist/internal/yaml-error.d.ts.map +1 -0
- package/dist/internal/yaml-error.js +12 -0
- package/dist/internal/yaml-error.js.map +1 -0
- package/dist/ir.d.ts +77 -0
- package/dist/ir.d.ts.map +1 -0
- package/dist/ir.js +20 -0
- package/dist/ir.js.map +1 -0
- package/dist/json.d.ts +23 -0
- package/dist/json.d.ts.map +1 -0
- package/dist/json.js +47 -0
- package/dist/json.js.map +1 -0
- package/dist/registry/actions.d.ts +48 -0
- package/dist/registry/actions.d.ts.map +1 -0
- package/dist/registry/actions.js +274 -0
- package/dist/registry/actions.js.map +1 -0
- package/dist/registry/prop-schema.d.ts +108 -0
- package/dist/registry/prop-schema.d.ts.map +1 -0
- package/dist/registry/prop-schema.js +400 -0
- package/dist/registry/prop-schema.js.map +1 -0
- package/dist/registry/registry.d.ts +47 -0
- package/dist/registry/registry.d.ts.map +1 -0
- package/dist/registry/registry.js +201 -0
- package/dist/registry/registry.js.map +1 -0
- package/dist/registry/roster.d.ts +53 -0
- package/dist/registry/roster.d.ts.map +1 -0
- package/dist/registry/roster.js +692 -0
- package/dist/registry/roster.js.map +1 -0
- package/dist/render/blocks.d.ts +41 -0
- package/dist/render/blocks.d.ts.map +1 -0
- package/dist/render/blocks.js +749 -0
- package/dist/render/blocks.js.map +1 -0
- package/dist/render/charts.d.ts +29 -0
- package/dist/render/charts.d.ts.map +1 -0
- package/dist/render/charts.js +235 -0
- package/dist/render/charts.js.map +1 -0
- package/dist/render/document.d.ts +40 -0
- package/dist/render/document.d.ts.map +1 -0
- package/dist/render/document.js +90 -0
- package/dist/render/document.js.map +1 -0
- package/dist/render/escape.d.ts +46 -0
- package/dist/render/escape.d.ts.map +1 -0
- package/dist/render/escape.js +108 -0
- package/dist/render/escape.js.map +1 -0
- package/dist/render/render.d.ts +52 -0
- package/dist/render/render.d.ts.map +1 -0
- package/dist/render/render.js +216 -0
- package/dist/render/render.js.map +1 -0
- package/dist/render/runtime.d.ts +25 -0
- package/dist/render/runtime.d.ts.map +1 -0
- package/dist/render/runtime.js +451 -0
- package/dist/render/runtime.js.map +1 -0
- package/dist/render/styles.d.ts +15 -0
- package/dist/render/styles.d.ts.map +1 -0
- package/dist/render/styles.js +163 -0
- package/dist/render/styles.js.map +1 -0
- package/dist/render/verify.d.ts +25 -0
- package/dist/render/verify.d.ts.map +1 -0
- package/dist/render/verify.js +168 -0
- package/dist/render/verify.js.map +1 -0
- package/dist/spec/bindings.d.ts +19 -0
- package/dist/spec/bindings.d.ts.map +1 -0
- package/dist/spec/bindings.js +51 -0
- package/dist/spec/bindings.js.map +1 -0
- package/dist/spec/bounds.d.ts +43 -0
- package/dist/spec/bounds.d.ts.map +1 -0
- package/dist/spec/bounds.js +144 -0
- package/dist/spec/bounds.js.map +1 -0
- package/dist/spec/forbidden.d.ts +27 -0
- package/dist/spec/forbidden.d.ts.map +1 -0
- package/dist/spec/forbidden.js +91 -0
- package/dist/spec/forbidden.js.map +1 -0
- package/dist/spec/migrate.d.ts +33 -0
- package/dist/spec/migrate.d.ts.map +1 -0
- package/dist/spec/migrate.js +113 -0
- package/dist/spec/migrate.js.map +1 -0
- package/dist/spec/normalize.d.ts +29 -0
- package/dist/spec/normalize.d.ts.map +1 -0
- package/dist/spec/normalize.js +649 -0
- package/dist/spec/normalize.js.map +1 -0
- package/dist/spec/parse.d.ts +22 -0
- package/dist/spec/parse.d.ts.map +1 -0
- package/dist/spec/parse.js +62 -0
- package/dist/spec/parse.js.map +1 -0
- package/dist/spec/providers.d.ts +30 -0
- package/dist/spec/providers.d.ts.map +1 -0
- package/dist/spec/providers.js +64 -0
- package/dist/spec/providers.js.map +1 -0
- package/dist/spec/validate.d.ts +28 -0
- package/dist/spec/validate.d.ts.map +1 -0
- package/dist/spec/validate.js +68 -0
- package/dist/spec/validate.js.map +1 -0
- package/dist/theme/load-theme.d.ts +55 -0
- package/dist/theme/load-theme.d.ts.map +1 -0
- package/dist/theme/load-theme.js +219 -0
- package/dist/theme/load-theme.js.map +1 -0
- package/dist/theme/presets.d.ts +50 -0
- package/dist/theme/presets.d.ts.map +1 -0
- package/dist/theme/presets.js +304 -0
- package/dist/theme/presets.js.map +1 -0
- package/dist/theme/theme-catalog.d.ts +68 -0
- package/dist/theme/theme-catalog.d.ts.map +1 -0
- package/dist/theme/theme-catalog.js +198 -0
- package/dist/theme/theme-catalog.js.map +1 -0
- package/dist/theme/tokens.d.ts +38 -0
- package/dist/theme/tokens.d.ts.map +1 -0
- package/dist/theme/tokens.js +190 -0
- package/dist/theme/tokens.js.map +1 -0
- package/dist/version.d.ts +12 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +12 -0
- package/dist/version.js.map +1 -0
- package/package.json +80 -0
|
@@ -0,0 +1,692 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The block roster.
|
|
3
|
+
*
|
|
4
|
+
* Each entry is the machine-readable contract the issue requires: stable type
|
|
5
|
+
* and version, purpose, compact prompt-facing summary, props schema with
|
|
6
|
+
* defaults and bounds, slots and child constraints, responsive sizing behavior,
|
|
7
|
+
* accessibility contract, supported actions, runtime features, network
|
|
8
|
+
* capability, migration policy, and serializer behavior.
|
|
9
|
+
*
|
|
10
|
+
* The roster is data. Rendering lives in the renderer; validation, the JSON
|
|
11
|
+
* Schema, and `catalog()`/`describe()` all derive from these definitions so they
|
|
12
|
+
* cannot drift apart.
|
|
13
|
+
*/
|
|
14
|
+
import { EMBED_PROVIDER_NAMES } from '../spec/providers.js';
|
|
15
|
+
const DEFAULT_MIGRATION = 'Props are additive; removed props are reported as diagnostics, never silently dropped.';
|
|
16
|
+
const DEFAULT_SERIALIZER = 'Serializes back to the same Page Spec shape; child blocks keep their order and slots.';
|
|
17
|
+
const DEFAULT_SIZING = {
|
|
18
|
+
sizes: ['medium', 'large'],
|
|
19
|
+
default: 'medium',
|
|
20
|
+
responsive: 'Fills its parent slot and reflows its content; never clips or scales by transform.',
|
|
21
|
+
};
|
|
22
|
+
const str = (o = {}) => ({
|
|
23
|
+
kind: 'string',
|
|
24
|
+
...o,
|
|
25
|
+
});
|
|
26
|
+
const txt = (o = {}) => ({
|
|
27
|
+
kind: 'text',
|
|
28
|
+
...o,
|
|
29
|
+
});
|
|
30
|
+
const num = (o = {}) => ({ kind: 'number', ...o });
|
|
31
|
+
const urlProp = (o = {}) => ({
|
|
32
|
+
kind: 'url',
|
|
33
|
+
...o,
|
|
34
|
+
});
|
|
35
|
+
const list = (of, o = {}) => ({ kind: 'list', of, ...o });
|
|
36
|
+
const obj = (fields, o = {}) => ({ kind: 'object', fields, ...o });
|
|
37
|
+
const oneOf = (options, o = {}) => ({ kind: 'oneOf', options, ...o });
|
|
38
|
+
const enumStr = (values, o = {}) => ({
|
|
39
|
+
kind: 'string',
|
|
40
|
+
enum: values,
|
|
41
|
+
...o,
|
|
42
|
+
});
|
|
43
|
+
const idProp = (description) => ({ kind: 'string', id: true, description });
|
|
44
|
+
const actionMap = (description) => ({
|
|
45
|
+
kind: 'json',
|
|
46
|
+
description,
|
|
47
|
+
maxBytes: 8_000,
|
|
48
|
+
schemaRef: '#/$defs/actionMap',
|
|
49
|
+
});
|
|
50
|
+
/** Reusable prop fragments. */
|
|
51
|
+
const onProp = (description = 'Declarative action bindings for this block.') => actionMap(description);
|
|
52
|
+
const anchorProps = {
|
|
53
|
+
id: idProp('Stable node id. Author-provided ids are used verbatim and must be unique.'),
|
|
54
|
+
};
|
|
55
|
+
const itemsOf = (fields, o = {}) => list(obj(fields), { required: true, minItems: o.minItems ?? 1, maxItems: o.maxItems ?? 200 });
|
|
56
|
+
const TITLE = str({ required: true, maxLength: 200 });
|
|
57
|
+
const OPTIONAL_TITLE = str({ maxLength: 200 });
|
|
58
|
+
const LABEL = str({ required: true, maxLength: 200 });
|
|
59
|
+
/** Chart kinds, SVG-first and deterministic. */
|
|
60
|
+
export const CHART_KINDS = [
|
|
61
|
+
'bar',
|
|
62
|
+
'line',
|
|
63
|
+
'area',
|
|
64
|
+
'pie',
|
|
65
|
+
'donut',
|
|
66
|
+
'sparkline',
|
|
67
|
+
'progress',
|
|
68
|
+
];
|
|
69
|
+
function define(specification) {
|
|
70
|
+
return {
|
|
71
|
+
version: 1,
|
|
72
|
+
kind: 'primitive',
|
|
73
|
+
sizing: DEFAULT_SIZING,
|
|
74
|
+
a11y: 'Renders semantic markup; text alternatives come from the authored content.',
|
|
75
|
+
actions: [],
|
|
76
|
+
runtimeFeatures: [],
|
|
77
|
+
assets: [],
|
|
78
|
+
network: 'none',
|
|
79
|
+
migration: DEFAULT_MIGRATION,
|
|
80
|
+
serializer: DEFAULT_SERIALIZER,
|
|
81
|
+
...specification,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
function semantic(specification) {
|
|
85
|
+
return define({ kind: 'semantic', ...specification });
|
|
86
|
+
}
|
|
87
|
+
export const BLOCK_DEFINITIONS = [
|
|
88
|
+
// --- root ---------------------------------------------------------------
|
|
89
|
+
define({
|
|
90
|
+
type: 'page',
|
|
91
|
+
kind: 'semantic',
|
|
92
|
+
purpose: 'The document root. Synthesized by the normalizer from the spec envelope.',
|
|
93
|
+
summary: 'Page root: title, description, locale, and top-level blocks.',
|
|
94
|
+
props: {
|
|
95
|
+
title: str({ required: true, maxLength: 200 }),
|
|
96
|
+
description: str({ maxLength: 400 }),
|
|
97
|
+
locale: str({ maxLength: 35, default: 'en' }),
|
|
98
|
+
},
|
|
99
|
+
slots: { children: { accepts: '*', min: 1, max: 200 } },
|
|
100
|
+
a11y: 'Emits one <main> landmark, a document <title>, and a language attribute.',
|
|
101
|
+
}),
|
|
102
|
+
// --- layout primitives --------------------------------------------------
|
|
103
|
+
define({
|
|
104
|
+
type: 'section',
|
|
105
|
+
purpose: 'A titled group of blocks.',
|
|
106
|
+
summary: 'Section with an optional heading wrapping child blocks.',
|
|
107
|
+
props: { title: OPTIONAL_TITLE, ...anchorProps },
|
|
108
|
+
slots: { children: { accepts: '*', min: 1, max: 200 } },
|
|
109
|
+
}),
|
|
110
|
+
define({
|
|
111
|
+
type: 'stack',
|
|
112
|
+
purpose: 'Vertical flow container.',
|
|
113
|
+
summary: 'Stack: children flow vertically with a controlled gap.',
|
|
114
|
+
props: { gap: enumStr(['tight', 'normal', 'loose'], { default: 'normal' }), ...anchorProps },
|
|
115
|
+
slots: { children: { accepts: '*', min: 1, max: 200 } },
|
|
116
|
+
}),
|
|
117
|
+
define({
|
|
118
|
+
type: 'grid',
|
|
119
|
+
purpose: 'Responsive grid container.',
|
|
120
|
+
summary: 'Grid: children in N columns that collapse on narrow viewports.',
|
|
121
|
+
props: {
|
|
122
|
+
columns: num({ integer: true, min: 1, max: 6, default: 3 }),
|
|
123
|
+
...anchorProps,
|
|
124
|
+
},
|
|
125
|
+
slots: { children: { accepts: '*', min: 1, max: 200 } },
|
|
126
|
+
}),
|
|
127
|
+
define({
|
|
128
|
+
type: 'split',
|
|
129
|
+
purpose: 'Two-column split container.',
|
|
130
|
+
summary: 'Split: two child groups side by side, stacking when narrow.',
|
|
131
|
+
props: {
|
|
132
|
+
ratio: enumStr(['even', 'wide-left', 'wide-right'], { default: 'even' }),
|
|
133
|
+
...anchorProps,
|
|
134
|
+
},
|
|
135
|
+
slots: { children: { accepts: '*', min: 2, max: 2 } },
|
|
136
|
+
}),
|
|
137
|
+
define({
|
|
138
|
+
type: 'spacer',
|
|
139
|
+
purpose: 'Vertical rhythm spacer.',
|
|
140
|
+
summary: 'Spacer: vertical space without content.',
|
|
141
|
+
props: { size: enumStr(['small', 'medium', 'large'], { default: 'medium' }), ...anchorProps },
|
|
142
|
+
a11y: 'Presentational only; hidden from assistive technology.',
|
|
143
|
+
}),
|
|
144
|
+
define({
|
|
145
|
+
type: 'divider',
|
|
146
|
+
purpose: 'Horizontal rule with an optional label.',
|
|
147
|
+
summary: 'Divider: semantic separator with an optional label.',
|
|
148
|
+
props: { label: str({ maxLength: 120 }), ...anchorProps },
|
|
149
|
+
a11y: 'Renders <hr>; a label is exposed as text, never as the only separator cue.',
|
|
150
|
+
}),
|
|
151
|
+
// --- typography and content --------------------------------------------
|
|
152
|
+
define({
|
|
153
|
+
type: 'heading',
|
|
154
|
+
purpose: 'Section heading at a chosen level.',
|
|
155
|
+
summary: 'Heading: h1–h6 with the author-chosen level.',
|
|
156
|
+
props: {
|
|
157
|
+
level: num({ integer: true, min: 1, max: 6, default: 2 }),
|
|
158
|
+
text: TITLE,
|
|
159
|
+
...anchorProps,
|
|
160
|
+
},
|
|
161
|
+
a11y: 'Heading levels are preserved as authored; the compiler warns on skipped levels.',
|
|
162
|
+
}),
|
|
163
|
+
define({
|
|
164
|
+
type: 'text',
|
|
165
|
+
purpose: 'A paragraph, lead sentence, or caption.',
|
|
166
|
+
summary: 'Text: single paragraph with body, lead, or caption treatment.',
|
|
167
|
+
props: {
|
|
168
|
+
variant: enumStr(['body', 'lead', 'caption'], { default: 'body' }),
|
|
169
|
+
text: txt({ required: true }),
|
|
170
|
+
...anchorProps,
|
|
171
|
+
},
|
|
172
|
+
}),
|
|
173
|
+
define({
|
|
174
|
+
type: 'rich-text',
|
|
175
|
+
purpose: 'Multi-paragraph prose.',
|
|
176
|
+
summary: 'RichText: multiple paragraphs from line breaks.',
|
|
177
|
+
props: { text: txt({ required: true }), ...anchorProps },
|
|
178
|
+
}),
|
|
179
|
+
define({
|
|
180
|
+
type: 'quote',
|
|
181
|
+
purpose: 'Pulled quotation.',
|
|
182
|
+
summary: 'Quote: blockquote with an optional attribution.',
|
|
183
|
+
props: { text: txt({ required: true }), cite: str({ maxLength: 200 }), ...anchorProps },
|
|
184
|
+
}),
|
|
185
|
+
define({
|
|
186
|
+
type: 'code',
|
|
187
|
+
purpose: 'Code or configuration sample.',
|
|
188
|
+
summary: 'Code: preformatted block with optional language and copy target.',
|
|
189
|
+
props: {
|
|
190
|
+
language: str({ maxLength: 32, default: 'text' }),
|
|
191
|
+
title: OPTIONAL_TITLE,
|
|
192
|
+
text: txt({ required: true, maxLength: 100_000 }),
|
|
193
|
+
...anchorProps,
|
|
194
|
+
},
|
|
195
|
+
assets: ['code'],
|
|
196
|
+
a11y: 'Renders <pre><code> with the authored text; no syntax highlighting requires network access.',
|
|
197
|
+
serializer: 'Emits the code text verbatim inside the fenced block when serialized back to Markdown.',
|
|
198
|
+
}),
|
|
199
|
+
define({
|
|
200
|
+
type: 'badge',
|
|
201
|
+
purpose: 'Short status or category label.',
|
|
202
|
+
summary: 'Badge: inline status chip.',
|
|
203
|
+
props: {
|
|
204
|
+
text: str({ required: true, maxLength: 60 }),
|
|
205
|
+
tone: enumStr(['neutral', 'info', 'success', 'warning', 'danger'], { default: 'neutral' }),
|
|
206
|
+
...anchorProps,
|
|
207
|
+
},
|
|
208
|
+
}),
|
|
209
|
+
define({
|
|
210
|
+
type: 'kbd',
|
|
211
|
+
purpose: 'Keyboard key sequence.',
|
|
212
|
+
summary: 'Kbd: one or more keys rendered as <kbd> elements.',
|
|
213
|
+
props: {
|
|
214
|
+
keys: list(str({ maxLength: 24 }), { required: true, minItems: 1, maxItems: 8 }),
|
|
215
|
+
...anchorProps,
|
|
216
|
+
},
|
|
217
|
+
a11y: 'Keys are read as text in order, not as an image.',
|
|
218
|
+
}),
|
|
219
|
+
define({
|
|
220
|
+
type: 'key-value',
|
|
221
|
+
purpose: 'Compact definition list.',
|
|
222
|
+
summary: 'KeyValue: label/value pairs rendered as a description list.',
|
|
223
|
+
props: {
|
|
224
|
+
title: OPTIONAL_TITLE,
|
|
225
|
+
items: itemsOf({ key: LABEL, value: str({ required: true, maxLength: 400 }) }),
|
|
226
|
+
...anchorProps,
|
|
227
|
+
},
|
|
228
|
+
a11y: 'Renders <dl> with <dt>/<dd> pairs.',
|
|
229
|
+
}),
|
|
230
|
+
// --- information --------------------------------------------------------
|
|
231
|
+
define({
|
|
232
|
+
type: 'card',
|
|
233
|
+
purpose: 'Bounded content container.',
|
|
234
|
+
summary: 'Card: titled container that holds child blocks.',
|
|
235
|
+
props: { title: OPTIONAL_TITLE, text: txt(), ...anchorProps },
|
|
236
|
+
slots: { children: { accepts: '*', min: 0, max: 40 } },
|
|
237
|
+
}),
|
|
238
|
+
define({
|
|
239
|
+
type: 'alert',
|
|
240
|
+
purpose: 'Inline notice.',
|
|
241
|
+
summary: 'Alert: notice with tone and title.',
|
|
242
|
+
props: {
|
|
243
|
+
tone: enumStr(['info', 'success', 'warning', 'danger'], { default: 'info' }),
|
|
244
|
+
title: TITLE,
|
|
245
|
+
text: txt({ required: true }),
|
|
246
|
+
...anchorProps,
|
|
247
|
+
},
|
|
248
|
+
a11y: 'Danger and warning tones use role="alert"; informational tones are not announced.',
|
|
249
|
+
}),
|
|
250
|
+
semantic({
|
|
251
|
+
type: 'callout',
|
|
252
|
+
purpose: 'Emphasized aside for a key point or risk.',
|
|
253
|
+
summary: 'Callout: highlighted aside with tone, title, and body.',
|
|
254
|
+
props: {
|
|
255
|
+
tone: enumStr(['info', 'success', 'warning', 'danger'], { default: 'info' }),
|
|
256
|
+
title: TITLE,
|
|
257
|
+
text: txt({ required: true }),
|
|
258
|
+
...anchorProps,
|
|
259
|
+
},
|
|
260
|
+
}),
|
|
261
|
+
semantic({
|
|
262
|
+
type: 'hero',
|
|
263
|
+
purpose: 'Page opener that states the artifact subject.',
|
|
264
|
+
summary: 'Hero: eyebrow, title, and one-line description.',
|
|
265
|
+
props: {
|
|
266
|
+
eyebrow: str({ maxLength: 80 }),
|
|
267
|
+
title: TITLE,
|
|
268
|
+
description: txt(),
|
|
269
|
+
...anchorProps,
|
|
270
|
+
},
|
|
271
|
+
sizing: {
|
|
272
|
+
sizes: ['medium', 'large'],
|
|
273
|
+
default: 'large',
|
|
274
|
+
responsive: 'Full width; text reflows.',
|
|
275
|
+
},
|
|
276
|
+
a11y: 'The hero title is an <h1>; the eyebrow is supporting text, not a heading.',
|
|
277
|
+
}),
|
|
278
|
+
semantic({
|
|
279
|
+
type: 'stats',
|
|
280
|
+
purpose: 'KPI or summary figures.',
|
|
281
|
+
summary: 'Stats: labeled values as a figure list.',
|
|
282
|
+
props: {
|
|
283
|
+
title: OPTIONAL_TITLE,
|
|
284
|
+
items: itemsOf({ label: LABEL, value: str({ required: true, maxLength: 60 }) }),
|
|
285
|
+
...anchorProps,
|
|
286
|
+
},
|
|
287
|
+
a11y: 'Each figure pairs its label and value as text; no color-only meaning.',
|
|
288
|
+
}),
|
|
289
|
+
define({
|
|
290
|
+
type: 'progress',
|
|
291
|
+
purpose: 'Single progress or completeness value.',
|
|
292
|
+
summary: 'Progress: single bar with an accessible value range.',
|
|
293
|
+
props: {
|
|
294
|
+
label: LABEL,
|
|
295
|
+
value: num({ required: true, min: 0, max: 1_000_000 }),
|
|
296
|
+
max: num({ min: 1, max: 1_000_000, default: 100 }),
|
|
297
|
+
...anchorProps,
|
|
298
|
+
},
|
|
299
|
+
runtimeFeatures: ['chart'],
|
|
300
|
+
a11y: 'Renders role="progressbar" with aria-valuenow/min/max and a text value.',
|
|
301
|
+
}),
|
|
302
|
+
semantic({
|
|
303
|
+
type: 'steps',
|
|
304
|
+
purpose: 'Ordered procedure.',
|
|
305
|
+
summary: 'Steps: numbered sequence of titled steps.',
|
|
306
|
+
props: {
|
|
307
|
+
title: OPTIONAL_TITLE,
|
|
308
|
+
items: itemsOf({ title: LABEL, text: txt() }),
|
|
309
|
+
...anchorProps,
|
|
310
|
+
},
|
|
311
|
+
a11y: 'Renders an ordered list so sequence is conveyed without numbering characters.',
|
|
312
|
+
serializer: 'Serializes to an ordered Markdown list.',
|
|
313
|
+
}),
|
|
314
|
+
semantic({
|
|
315
|
+
type: 'timeline',
|
|
316
|
+
purpose: 'Chronological or staged events.',
|
|
317
|
+
summary: 'Timeline: ordered entries with a when/title/body shape.',
|
|
318
|
+
props: {
|
|
319
|
+
title: OPTIONAL_TITLE,
|
|
320
|
+
items: itemsOf({ when: str({ required: true, maxLength: 80 }), title: LABEL, text: txt() }),
|
|
321
|
+
...anchorProps,
|
|
322
|
+
},
|
|
323
|
+
a11y: 'Ordered list; the `when` value is text, not a colour or position cue.',
|
|
324
|
+
}),
|
|
325
|
+
// --- collections --------------------------------------------------------
|
|
326
|
+
define({
|
|
327
|
+
type: 'list',
|
|
328
|
+
purpose: 'Short item list with optional badges.',
|
|
329
|
+
summary: 'List: bulleted items with optional status badges.',
|
|
330
|
+
props: {
|
|
331
|
+
title: OPTIONAL_TITLE,
|
|
332
|
+
items: itemsOf({ text: txt({ required: true }), badge: str({ maxLength: 40 }) }),
|
|
333
|
+
...anchorProps,
|
|
334
|
+
},
|
|
335
|
+
runtimeFeatures: ['filter'],
|
|
336
|
+
actions: ['filter'],
|
|
337
|
+
a11y: 'Renders <ul>; filtering also hides items from assistive technology and announces the count.',
|
|
338
|
+
}),
|
|
339
|
+
semantic({
|
|
340
|
+
type: 'card-grid',
|
|
341
|
+
purpose: 'Grid of short cards.',
|
|
342
|
+
summary: 'CardGrid: titled cards in a responsive grid.',
|
|
343
|
+
props: {
|
|
344
|
+
title: OPTIONAL_TITLE,
|
|
345
|
+
items: itemsOf({ title: LABEL, text: txt() }),
|
|
346
|
+
...anchorProps,
|
|
347
|
+
},
|
|
348
|
+
}),
|
|
349
|
+
define({
|
|
350
|
+
type: 'table',
|
|
351
|
+
purpose: 'Tabular data with a header row.',
|
|
352
|
+
summary: 'Table: columns and rows with a real header row.',
|
|
353
|
+
props: {
|
|
354
|
+
title: OPTIONAL_TITLE,
|
|
355
|
+
columns: list(str({ maxLength: 120 }), { required: true, minItems: 1, maxItems: 12 }),
|
|
356
|
+
rows: list(list(str({ maxLength: 400 })), { required: true, maxItems: 500 }),
|
|
357
|
+
...anchorProps,
|
|
358
|
+
},
|
|
359
|
+
a11y: 'Renders <table> with <thead>/<th scope="col">; rows with the wrong width are a diagnostic.',
|
|
360
|
+
serializer: 'Serializes to a Markdown table.',
|
|
361
|
+
}),
|
|
362
|
+
semantic({
|
|
363
|
+
type: 'comparison',
|
|
364
|
+
purpose: 'Side-by-side comparison of two alternatives.',
|
|
365
|
+
summary: 'Comparison: two labeled columns of points.',
|
|
366
|
+
props: {
|
|
367
|
+
title: OPTIONAL_TITLE,
|
|
368
|
+
left: obj({ label: LABEL, items: list(str({ maxLength: 300 }), { required: true, maxItems: 40 }) }, { required: true }),
|
|
369
|
+
right: obj({ label: LABEL, items: list(str({ maxLength: 300 }), { required: true, maxItems: 40 }) }, { required: true }),
|
|
370
|
+
...anchorProps,
|
|
371
|
+
},
|
|
372
|
+
a11y: 'Each side is a labeled list; the labels are headings, not just column position.',
|
|
373
|
+
}),
|
|
374
|
+
semantic({
|
|
375
|
+
type: 'risk-matrix',
|
|
376
|
+
purpose: 'Impact/likelihood risk table.',
|
|
377
|
+
summary: 'RiskMatrix: risks rated by impact and likelihood.',
|
|
378
|
+
props: {
|
|
379
|
+
title: OPTIONAL_TITLE,
|
|
380
|
+
severity: list(enumStr(['low', 'medium', 'high']), { maxItems: 5 }),
|
|
381
|
+
items: itemsOf({
|
|
382
|
+
area: LABEL,
|
|
383
|
+
impact: enumStr(['low', 'medium', 'high'], { required: true }),
|
|
384
|
+
likelihood: enumStr(['low', 'medium', 'high'], { required: true }),
|
|
385
|
+
note: txt(),
|
|
386
|
+
}),
|
|
387
|
+
...anchorProps,
|
|
388
|
+
},
|
|
389
|
+
a11y: 'Ratings are text values in table cells, never colour-only.',
|
|
390
|
+
}),
|
|
391
|
+
// --- interactive collections -------------------------------------------
|
|
392
|
+
define({
|
|
393
|
+
type: 'tabs',
|
|
394
|
+
purpose: 'Tabbed groups of related content.',
|
|
395
|
+
summary: 'Tabs: roving-tabindex tablist with tabpanels.',
|
|
396
|
+
props: {
|
|
397
|
+
title: OPTIONAL_TITLE,
|
|
398
|
+
items: itemsOf({ id: idProp('Tab id.'), title: LABEL, text: txt({ required: true }) }, { minItems: 2 }),
|
|
399
|
+
on: onProp('Optional state binding fired when a tab is selected.'),
|
|
400
|
+
...anchorProps,
|
|
401
|
+
},
|
|
402
|
+
runtimeFeatures: ['tabs'],
|
|
403
|
+
actions: ['select-tab'],
|
|
404
|
+
a11y: 'role="tablist" with arrow-key roving focus, aria-selected, aria-controls, and labelled tabpanels.',
|
|
405
|
+
}),
|
|
406
|
+
define({
|
|
407
|
+
type: 'accordion',
|
|
408
|
+
purpose: 'Collapsible sections.',
|
|
409
|
+
summary: 'Accordion: disclosure sections that expand and collapse.',
|
|
410
|
+
props: {
|
|
411
|
+
title: OPTIONAL_TITLE,
|
|
412
|
+
items: itemsOf({ title: LABEL, text: txt({ required: true }) }),
|
|
413
|
+
on: onProp('Optional state binding fired when a section toggles.'),
|
|
414
|
+
...anchorProps,
|
|
415
|
+
},
|
|
416
|
+
runtimeFeatures: ['accordion'],
|
|
417
|
+
actions: ['toggle', 'expand', 'collapse'],
|
|
418
|
+
a11y: 'Native <details>/<summary> where possible; otherwise button + region with aria-expanded.',
|
|
419
|
+
}),
|
|
420
|
+
define({
|
|
421
|
+
type: 'carousel',
|
|
422
|
+
purpose: 'Sequenced slides with manual navigation.',
|
|
423
|
+
summary: 'Carousel: prev/next, keyboard, and swipe across slides.',
|
|
424
|
+
props: {
|
|
425
|
+
ariaLabel: str({ required: true, maxLength: 120 }),
|
|
426
|
+
items: itemsOf({ title: LABEL, text: txt({ required: true }) }, { minItems: 1 }),
|
|
427
|
+
on: onProp('Optional state binding fired when the active slide changes.'),
|
|
428
|
+
...anchorProps,
|
|
429
|
+
},
|
|
430
|
+
runtimeFeatures: ['carousel'],
|
|
431
|
+
actions: ['next', 'previous'],
|
|
432
|
+
a11y: 'Labeled region with prev/next buttons, arrow-key support, a slide counter, and no autoplay.',
|
|
433
|
+
}),
|
|
434
|
+
semantic({
|
|
435
|
+
type: 'toolbar',
|
|
436
|
+
purpose: 'Row of controls or chips.',
|
|
437
|
+
summary: 'Toolbar: buttons, badges, and links in one accessible row.',
|
|
438
|
+
props: {
|
|
439
|
+
title: OPTIONAL_TITLE,
|
|
440
|
+
items: list(oneOf([
|
|
441
|
+
obj({
|
|
442
|
+
type: enumStr(['button'], { required: true }),
|
|
443
|
+
label: LABEL,
|
|
444
|
+
variant: enumStr(['primary', 'secondary', 'ghost'], { default: 'secondary' }),
|
|
445
|
+
on: onProp(),
|
|
446
|
+
id: idProp('Stable node id for the button.'),
|
|
447
|
+
}),
|
|
448
|
+
obj({
|
|
449
|
+
type: enumStr(['badge'], { required: true }),
|
|
450
|
+
text: str({ required: true, maxLength: 60 }),
|
|
451
|
+
tone: enumStr(['neutral', 'info', 'success', 'warning', 'danger'], {
|
|
452
|
+
default: 'neutral',
|
|
453
|
+
}),
|
|
454
|
+
}),
|
|
455
|
+
obj({
|
|
456
|
+
type: enumStr(['link'], { required: true }),
|
|
457
|
+
label: LABEL,
|
|
458
|
+
href: urlProp({ required: true }),
|
|
459
|
+
on: onProp(),
|
|
460
|
+
}),
|
|
461
|
+
]), { required: true, minItems: 1, maxItems: 20 }),
|
|
462
|
+
...anchorProps,
|
|
463
|
+
},
|
|
464
|
+
runtimeFeatures: ['copy'],
|
|
465
|
+
actions: ['copy', 'open-url', 'download', 'toggle', 'set-value', 'theme'],
|
|
466
|
+
a11y: 'role="toolbar" with roving focus order; every control is a real button or link.',
|
|
467
|
+
}),
|
|
468
|
+
// --- data visualization -------------------------------------------------
|
|
469
|
+
define({
|
|
470
|
+
type: 'chart',
|
|
471
|
+
purpose: 'Deterministic SVG chart with a text summary.',
|
|
472
|
+
summary: 'Chart: bar, line, area, pie, donut, sparkline, or progress from labeled series.',
|
|
473
|
+
props: {
|
|
474
|
+
kind: enumStr(CHART_KINDS, { required: true }),
|
|
475
|
+
title: OPTIONAL_TITLE,
|
|
476
|
+
description: txt(),
|
|
477
|
+
labels: list(str({ maxLength: 80 }), { required: true, minItems: 1, maxItems: 200 }),
|
|
478
|
+
series: itemsOf({ label: LABEL, values: list(num(), { required: true, minItems: 1, maxItems: 200 }) }, { minItems: 1 }),
|
|
479
|
+
...anchorProps,
|
|
480
|
+
},
|
|
481
|
+
runtimeFeatures: ['chart'],
|
|
482
|
+
assets: ['chart'],
|
|
483
|
+
a11y: 'SVG carries role="img" plus a generated text summary; the same values are available as a table for assistive technology.',
|
|
484
|
+
serializer: 'Serializes to the labels/series data, never to rendered SVG.',
|
|
485
|
+
}),
|
|
486
|
+
// --- diagrams (adapter) -------------------------------------------------
|
|
487
|
+
semantic({
|
|
488
|
+
type: 'diagram-panel',
|
|
489
|
+
purpose: 'Typed diagram rendered through an adapter.',
|
|
490
|
+
summary: 'DiagramPanel: embeds trusted output from a diagram adapter (ak:diagram when installed).',
|
|
491
|
+
props: {
|
|
492
|
+
title: OPTIONAL_TITLE,
|
|
493
|
+
/** Validated by the adapter, bounded here. */
|
|
494
|
+
spec: {
|
|
495
|
+
kind: 'json',
|
|
496
|
+
required: true,
|
|
497
|
+
description: 'Typed diagram IR (ak:diagram schema).',
|
|
498
|
+
maxBytes: 200_000,
|
|
499
|
+
},
|
|
500
|
+
caption: txt(),
|
|
501
|
+
...anchorProps,
|
|
502
|
+
},
|
|
503
|
+
runtimeFeatures: ['diagram'],
|
|
504
|
+
assets: ['diagram'],
|
|
505
|
+
actions: ['toggle', 'filter'],
|
|
506
|
+
a11y: 'Requires a textual fallback summary when the adapter cannot render; the fallback is always emitted.',
|
|
507
|
+
migration: 'Adapter contract is versioned separately; a spec that no longer compiles degrades to the fallback.',
|
|
508
|
+
}),
|
|
509
|
+
// --- media --------------------------------------------------------------
|
|
510
|
+
define({
|
|
511
|
+
type: 'image',
|
|
512
|
+
purpose: 'Single image or picture.',
|
|
513
|
+
summary: 'Image: local asset or allowlisted URL with required alt text.',
|
|
514
|
+
props: {
|
|
515
|
+
src: urlProp({ required: true }),
|
|
516
|
+
alt: str({ required: true, maxLength: 300 }),
|
|
517
|
+
caption: txt(),
|
|
518
|
+
...anchorProps,
|
|
519
|
+
},
|
|
520
|
+
network: 'optional',
|
|
521
|
+
assets: ['media'],
|
|
522
|
+
a11y: 'Alt text is required; decorative images must use an empty alt explicitly.',
|
|
523
|
+
}),
|
|
524
|
+
semantic({
|
|
525
|
+
type: 'gallery',
|
|
526
|
+
purpose: 'Image grid.',
|
|
527
|
+
summary: 'Gallery: responsive image grid with captions.',
|
|
528
|
+
props: {
|
|
529
|
+
title: OPTIONAL_TITLE,
|
|
530
|
+
columns: num({ integer: true, min: 1, max: 6, default: 3 }),
|
|
531
|
+
items: itemsOf({
|
|
532
|
+
src: urlProp({ required: true }),
|
|
533
|
+
alt: str({ required: true, maxLength: 300 }),
|
|
534
|
+
caption: txt(),
|
|
535
|
+
}),
|
|
536
|
+
...anchorProps,
|
|
537
|
+
},
|
|
538
|
+
network: 'optional',
|
|
539
|
+
assets: ['media'],
|
|
540
|
+
a11y: 'Every image keeps its alt text; captions are visible text, not tooltips.',
|
|
541
|
+
}),
|
|
542
|
+
define({
|
|
543
|
+
type: 'video',
|
|
544
|
+
purpose: 'Local video or a network-denied fallback.',
|
|
545
|
+
summary: 'Video: plays local sources; a provider reference becomes a poster plus link; network sources degrade to poster plus link.',
|
|
546
|
+
props: {
|
|
547
|
+
title: LABEL,
|
|
548
|
+
src: urlProp({ required: true }),
|
|
549
|
+
poster: urlProp(),
|
|
550
|
+
provider: enumStr(EMBED_PROVIDER_NAMES, {
|
|
551
|
+
description: 'Network media provider. Requires the media capability and this provider in the page provider allowlist; the page still links out instead of embedding.',
|
|
552
|
+
}),
|
|
553
|
+
caption: txt(),
|
|
554
|
+
fallback: obj({
|
|
555
|
+
description: txt({ required: true }),
|
|
556
|
+
linkText: str({ maxLength: 120 }),
|
|
557
|
+
url: urlProp(),
|
|
558
|
+
}),
|
|
559
|
+
...anchorProps,
|
|
560
|
+
},
|
|
561
|
+
runtimeFeatures: ['media'],
|
|
562
|
+
assets: ['media'],
|
|
563
|
+
network: 'optional',
|
|
564
|
+
a11y: 'Uses <video controls> with a poster; the fallback description is always available as text.',
|
|
565
|
+
}),
|
|
566
|
+
define({
|
|
567
|
+
type: 'audio',
|
|
568
|
+
purpose: 'Local audio or a network-denied fallback.',
|
|
569
|
+
summary: 'Audio: plays local sources; a provider reference becomes metadata plus link; network sources degrade to metadata plus link.',
|
|
570
|
+
props: {
|
|
571
|
+
title: LABEL,
|
|
572
|
+
src: urlProp({ required: true }),
|
|
573
|
+
provider: enumStr(EMBED_PROVIDER_NAMES, {
|
|
574
|
+
description: 'Network media provider. Requires the media capability and this provider in the page provider allowlist; the page still links out instead of embedding.',
|
|
575
|
+
}),
|
|
576
|
+
caption: txt(),
|
|
577
|
+
fallback: obj({
|
|
578
|
+
description: txt({ required: true }),
|
|
579
|
+
linkText: str({ maxLength: 120 }),
|
|
580
|
+
url: urlProp(),
|
|
581
|
+
}),
|
|
582
|
+
...anchorProps,
|
|
583
|
+
},
|
|
584
|
+
runtimeFeatures: ['media'],
|
|
585
|
+
assets: ['media'],
|
|
586
|
+
network: 'optional',
|
|
587
|
+
a11y: 'Uses <audio controls>; the fallback description is always available as text.',
|
|
588
|
+
}),
|
|
589
|
+
// --- controls -----------------------------------------------------------
|
|
590
|
+
define({
|
|
591
|
+
type: 'button',
|
|
592
|
+
purpose: 'Single actionable control.',
|
|
593
|
+
summary: 'Button: clickable control with declarative action bindings.',
|
|
594
|
+
props: {
|
|
595
|
+
label: LABEL,
|
|
596
|
+
variant: enumStr(['primary', 'secondary', 'ghost'], { default: 'secondary' }),
|
|
597
|
+
on: onProp('Action bindings; a button without bindings is a diagnostic.'),
|
|
598
|
+
...anchorProps,
|
|
599
|
+
},
|
|
600
|
+
runtimeFeatures: ['copy'],
|
|
601
|
+
actions: ['copy', 'open-url', 'download', 'toggle', 'set-value', 'theme'],
|
|
602
|
+
a11y: 'Renders <button type="button">: pointer, Enter, and Space activation, with a visible focus ring.',
|
|
603
|
+
}),
|
|
604
|
+
define({
|
|
605
|
+
type: 'link',
|
|
606
|
+
purpose: 'Navigational link.',
|
|
607
|
+
summary: 'Link: allowlisted URL with descriptive text.',
|
|
608
|
+
props: { label: LABEL, href: urlProp({ required: true }), ...anchorProps },
|
|
609
|
+
actions: ['open-url'],
|
|
610
|
+
a11y: 'Renders <a>; external links are marked with rel="noreferrer noopener" and a visible cue.',
|
|
611
|
+
}),
|
|
612
|
+
define({
|
|
613
|
+
type: 'slider',
|
|
614
|
+
purpose: 'Bounded numeric input.',
|
|
615
|
+
summary: 'Slider: range input with pointer drag and full keyboard support.',
|
|
616
|
+
props: {
|
|
617
|
+
label: LABEL,
|
|
618
|
+
min: num({ default: 0, min: -1_000_000, max: 1_000_000 }),
|
|
619
|
+
max: num({ default: 100, min: -1_000_000, max: 1_000_000 }),
|
|
620
|
+
step: num({ default: 1, min: 0.0001, max: 1_000_000 }),
|
|
621
|
+
value: num(),
|
|
622
|
+
on: onProp('Binding fired when the value changes.'),
|
|
623
|
+
...anchorProps,
|
|
624
|
+
},
|
|
625
|
+
runtimeFeatures: ['slider'],
|
|
626
|
+
actions: ['set-value'],
|
|
627
|
+
a11y: 'Native <input type="range"> with a <label>, output text, and arrow/Home/End keyboard behavior.',
|
|
628
|
+
}),
|
|
629
|
+
define({
|
|
630
|
+
type: 'search',
|
|
631
|
+
purpose: 'Filter input for a collection.',
|
|
632
|
+
summary: 'Search: labeled filter input bound to a collection.',
|
|
633
|
+
props: {
|
|
634
|
+
label: LABEL,
|
|
635
|
+
placeholder: str({ maxLength: 120 }),
|
|
636
|
+
on: onProp('Filter binding; requires a target collection.'),
|
|
637
|
+
...anchorProps,
|
|
638
|
+
},
|
|
639
|
+
runtimeFeatures: ['filter'],
|
|
640
|
+
actions: ['filter'],
|
|
641
|
+
a11y: 'Labeled <input type="search"> with a results count announced politely.',
|
|
642
|
+
}),
|
|
643
|
+
define({
|
|
644
|
+
type: 'dialog',
|
|
645
|
+
purpose: 'Modal detail surface.',
|
|
646
|
+
summary: 'Dialog: native <dialog> with labeled title and close controls.',
|
|
647
|
+
props: {
|
|
648
|
+
title: TITLE,
|
|
649
|
+
ariaLabel: str({ maxLength: 120 }),
|
|
650
|
+
text: txt(),
|
|
651
|
+
actions: list(obj({ label: LABEL, on: onProp() }), { maxItems: 6 }),
|
|
652
|
+
...anchorProps,
|
|
653
|
+
},
|
|
654
|
+
runtimeFeatures: ['dialog'],
|
|
655
|
+
actions: ['toggle', 'copy', 'set-value'],
|
|
656
|
+
a11y: 'Native <dialog> with aria-modal, focus trapping, Escape to close, and focus restoration.',
|
|
657
|
+
}),
|
|
658
|
+
// --- review-specific semantic blocks ------------------------------------
|
|
659
|
+
semantic({
|
|
660
|
+
type: 'diff-summary',
|
|
661
|
+
purpose: 'Changed-file summary from a diff.',
|
|
662
|
+
summary: 'DiffSummary: file paths with change status and line counts.',
|
|
663
|
+
props: {
|
|
664
|
+
title: OPTIONAL_TITLE,
|
|
665
|
+
items: itemsOf({
|
|
666
|
+
path: str({ required: true, maxLength: 400 }),
|
|
667
|
+
status: enumStr(['added', 'modified', 'deleted', 'renamed'], { required: true }),
|
|
668
|
+
additions: num({ integer: true, min: 0, max: 1_000_000 }),
|
|
669
|
+
deletions: num({ integer: true, min: 0, max: 1_000_000 }),
|
|
670
|
+
}),
|
|
671
|
+
...anchorProps,
|
|
672
|
+
},
|
|
673
|
+
a11y: 'Status is a text label in the table, not a colour-only cue.',
|
|
674
|
+
}),
|
|
675
|
+
semantic({
|
|
676
|
+
type: 'code-review',
|
|
677
|
+
purpose: 'Structured review findings.',
|
|
678
|
+
summary: 'CodeReview: good/bad/ugly/concern/question findings.',
|
|
679
|
+
props: {
|
|
680
|
+
title: OPTIONAL_TITLE,
|
|
681
|
+
items: itemsOf({
|
|
682
|
+
kind: enumStr(['good', 'bad', 'ugly', 'concern', 'question'], { required: true }),
|
|
683
|
+
title: LABEL,
|
|
684
|
+
text: txt({ required: true }),
|
|
685
|
+
}),
|
|
686
|
+
...anchorProps,
|
|
687
|
+
},
|
|
688
|
+
a11y: 'The finding kind is written as text; tone colour is decorative.',
|
|
689
|
+
}),
|
|
690
|
+
];
|
|
691
|
+
export const BLOCKS_BY_TYPE = new Map(BLOCK_DEFINITIONS.map((definition) => [definition.type, definition]));
|
|
692
|
+
//# sourceMappingURL=roster.js.map
|