@snackbit/opendil 0.1.0 → 0.2.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.
Files changed (88) hide show
  1. package/README.md +74 -137
  2. package/bin/opendil.mjs +66 -11
  3. package/bin/publish-directory.d.mts +18 -0
  4. package/bin/publish-directory.mjs +173 -17
  5. package/dist/agent-spec.js +9 -0
  6. package/dist/chunk-RPZW4WVX.js +2 -0
  7. package/dist/chunk-ZSTXLIS6.js +2 -0
  8. package/dist/index.js +1 -10
  9. package/dist/runtime/frame.html +117 -44
  10. package/dist/runtime/frame.js +501 -505
  11. package/dist/spec/genui.agent.full.md +1927 -0
  12. package/dist/spec/genui.agent.md +479 -0
  13. package/dist/spec/genui.examples.md +836 -0
  14. package/dist/styled-renderer-PHI4MNPF.js +2441 -0
  15. package/dist/styles.css +115 -42
  16. package/dist/types/agent-spec.d.ts +12 -0
  17. package/dist/types/components/ui/controls/select.d.ts +14 -0
  18. package/dist/types/components/ui/controls/tabs.d.ts +7 -0
  19. package/dist/types/components/ui/controls/toggle-group.d.ts +3 -2
  20. package/dist/types/components/ui/display/chart.d.ts +6 -12
  21. package/dist/types/components/ui/media/carousel.d.ts +1 -1
  22. package/dist/types/components/ui/overlays/collapsible.d.ts +6 -0
  23. package/dist/types/components/ui/overlays/popover.d.ts +2 -1
  24. package/dist/types/components/ui/overlays/ui-scope.d.ts +16 -0
  25. package/dist/types/dil/custom-component.d.ts +1 -1
  26. package/dist/types/dil/dil-renderer.d.ts +58 -23
  27. package/dist/types/dil/isolation/frame-startup.d.ts +18 -0
  28. package/dist/types/dil/isolation/frame-theme.d.ts +14 -0
  29. package/dist/types/dil/isolation/frame-viewport.d.ts +30 -0
  30. package/dist/types/dil/isolation/isolated-renderer.d.ts +13 -7
  31. package/dist/types/dil/isolation/overlay-room.d.ts +29 -0
  32. package/dist/types/dil/isolation/viewport-scroll.d.ts +3 -2
  33. package/dist/types/dil/message-renderer.d.ts +6 -5
  34. package/dist/types/dil/parser/dil-parser.d.ts +44 -3
  35. package/dist/types/dil/parser/lexical.d.ts +13 -2
  36. package/dist/types/dil/parser/literal-tag.d.ts +16 -0
  37. package/dist/types/dil/parser/markdown-lexing.d.ts +41 -0
  38. package/dist/types/dil/parser/types.d.ts +22 -1
  39. package/dist/types/dil/parser/unfinished.d.ts +22 -0
  40. package/dist/types/dil/parser/vocabulary.d.ts +12 -0
  41. package/dist/types/dil/render-error.d.ts +47 -0
  42. package/dist/types/dil/renderer-environment.d.ts +19 -8
  43. package/dist/types/dil/runtime/document.d.ts +18 -6
  44. package/dist/types/dil/runtime/execution-queue.d.ts +2 -1
  45. package/dist/types/dil/runtime/expressions.d.ts +8 -5
  46. package/dist/types/dil/runtime/inline-worker.d.ts +33 -0
  47. package/dist/types/dil/runtime/view-cost.d.ts +2 -2
  48. package/dist/types/dil/styled-renderer.d.ts +39 -0
  49. package/dist/types/dil/view/conditional-view.d.ts +3 -3
  50. package/dist/types/dil/view/markdown-node.d.ts +3 -3
  51. package/dist/types/dil/view/node-children.d.ts +3 -3
  52. package/dist/types/dil/view/node-view.d.ts +2 -2
  53. package/dist/types/dil/view/progressive-reveal.d.ts +5 -4
  54. package/dist/types/dil/view/render-boundary.d.ts +13 -0
  55. package/dist/types/dil/view/render-limit.d.ts +1 -1
  56. package/dist/types/dil/view/repeated-nodes.d.ts +4 -4
  57. package/dist/types/dil/view/reveal/controller.d.ts +9 -2
  58. package/dist/types/dil/view/reveal/presentation.d.ts +1 -1
  59. package/dist/types/dil/view/scroll/controller.d.ts +16 -2
  60. package/dist/types/dil/view/scroll/host-viewport.d.ts +24 -0
  61. package/dist/types/dil/view/scroll/root-tracker.d.ts +17 -0
  62. package/dist/types/dil/view/scroll/scroll-root.d.ts +36 -0
  63. package/dist/types/dil/view/scroll-options.d.ts +15 -0
  64. package/dist/types/dil/view/stream-follow.d.ts +10 -3
  65. package/dist/types/dil/view/table-context.d.ts +1 -1
  66. package/dist/types/dil/view/virtual-flow.d.ts +3 -3
  67. package/dist/types/dil/widgets/action-feedback.d.ts +1 -1
  68. package/dist/types/dil/widgets/charts/chart-hover.d.ts +17 -0
  69. package/dist/types/dil/widgets/charts/chart-legend.d.ts +1 -1
  70. package/dist/types/dil/widgets/charts/chart-pie.d.ts +16 -0
  71. package/dist/types/dil/widgets/charts/chart-series.d.ts +10 -2
  72. package/dist/types/dil/widgets/charts/chart-snapping.d.ts +3 -5
  73. package/dist/types/dil/widgets/charts/snap-cursor.d.ts +3 -7
  74. package/dist/types/dil/widgets/charts/snap-tooltip.d.ts +6 -4
  75. package/dist/types/dil/widgets/code-block.d.ts +1 -1
  76. package/dist/types/dil/widgets/controls.d.ts +2 -2
  77. package/dist/types/dil/widgets/graphs/node-graph.d.ts +1 -1
  78. package/dist/types/dil/widgets/graphs/service-node.d.ts +1 -1
  79. package/dist/types/dil/widgets/primitives/primitive.d.ts +4 -4
  80. package/dist/types/index.d.ts +9 -8
  81. package/dist/types/ui.d.ts +11 -8
  82. package/dist/ui.js +1 -1
  83. package/package.json +13 -2
  84. package/styles/tailwind.css +53 -1
  85. package/styles/tokens.css +3 -0
  86. package/dist/chunk-ZWAN2JCV.js +0 -2
  87. package/dist/runtime/vm.js +0 -6
  88. package/dist/shadow-renderer-F25KL3C4.js +0 -2363
@@ -0,0 +1,1927 @@
1
+ # GenUI Agent Specification: Full Reference
2
+
3
+ Use this reference for targeted lookups. Inject [the primary specification](genui.agent.md)
4
+ when the smaller instruction set is sufficient. Search `Component: NAME`,
5
+ `Composition: NAME`, `Callback: NAME`, or `Task: NAME`. Find complete runnable
6
+ recipes in [the example catalog](genui.examples.md). All names are case-sensitive.
7
+ The numbered sections and component groups match the primary specification.
8
+
9
+ ## 1. Output format and safety
10
+
11
+ Generate declarative OpenDIL source. Combine ordinary Markdown with supported
12
+ components and directives. Return the source directly when asked to generate UI.
13
+ Use a code fence only when the user asks to inspect or copy source rather than
14
+ render it. Do not include an explanation of how to install React, a component
15
+ module, a package manifest, or a web page around a generated interface.
16
+
17
+ Treat the supported vocabulary as closed unless the host explicitly supplies a
18
+ custom component contract. A familiar HTML, React, Recharts, or SVG API is not
19
+ automatically an OpenDIL API. Match every tag, prop, callback name, and data shape
20
+ in this reference. A tag being accepted by the parser does not establish that a
21
+ renderer exists for it. An unknown prop may be ignored rather than reported.
22
+ Generate valid output deliberately instead of depending on fallback behavior.
23
+
24
+ Use Markdown for prose, real components for controls, and directives for UI
25
+ structure. Do not encode an entire interface as a string expression. Strings
26
+ returned by expressions are displayed as text, not parsed as nested DIL. Do not
27
+ construct HTML strings, use `dangerouslySetInnerHTML`, or request arbitrary CSS.
28
+ There is no authorable HTML document, React component function, import statement,
29
+ stylesheet, or JavaScript module boundary.
30
+
31
+ Close elements and control directives in the reverse order in which they open.
32
+ Write leaves as self-closing tags. Finish incomplete tokens before ending the
33
+ response. Keep the source readable enough that a later agent can locate a binding
34
+ or handler. Prefer one declaration per line and modest component trees. Avoid
35
+ deep wrappers that add no layout, semantics, or interaction value.
36
+
37
+ Use only facts supplied by the user or the enclosing application. Label synthetic
38
+ examples, estimated totals, and placeholder illustrations appropriately. Do not
39
+ present a disconnected chart as live telemetry or a local form as a completed
40
+ server submission. Render supplied source titles and URLs accurately. Do not
41
+ invent citation IDs, external services, host actions, or automatic data refresh.
42
+
43
+ Give controls meaningful names. Use visible labels where possible and explicit
44
+ `aria-label` on controls that expose it. Keep status text next to status colors.
45
+ Provide textual summaries for charts, SVG drawings, and node graphs. An icon is
46
+ decorative; it cannot replace the name of an action. Avoid interactive descendants
47
+ inside an interactive ancestor, including buttons inside pressable cards.
48
+
49
+ Keep data transformations bounded. Prefer a short filter, map, or reduce over
50
+ recursive traversal or large generated allocations. Do not run arbitrary code
51
+ received in a data record. Never use an expression to discover undocumented host
52
+ globals or alter the execution environment. Do not use `eval`, `Function`, dynamic
53
+ imports, or VM implementation globals in authored output.
54
+
55
+ Execution boundaries:
56
+
57
+ | Surface | Supported author behavior |
58
+ | --------------------------- | ------------------------------------------------------------ |
59
+ | Markdown and component tree | Declare content and supported props |
60
+ | QuickJS worker | Evaluate expressions, declarations, and supported callbacks |
61
+ | Renderer | Present approved widgets and dispatch their supported events |
62
+ | Host boundary | Receive explicitly authorized clipboard requests |
63
+ | Server | No generated server code or server action API |
64
+
65
+ The document expression environment does not expose browser `window`, `document`,
66
+ DOM nodes/events, `fetch`, XMLHttpRequest, WebSocket, localStorage, IndexedDB,
67
+ browser timers, or Node facilities. Do not assume that because the renderer uses
68
+ these internally, document expressions can call them. Native JavaScript features
69
+ such as arrays, objects, functions, and arithmetic remain available inside QuickJS.
70
+ The prohibition on `eval` and `Function` is an authoring rule; do not describe it
71
+ as a proven removal of those primitives from the VM.
72
+
73
+ The default renderer uses an opaque-origin frame. Host DOM and Shadow DOM modes
74
+ are explicit consumer choices with different containment properties. Shadow DOM
75
+ provides styling separation, not iframe containment. Generated source cannot
76
+ select a rendering mode or authorize new APIs. Do not tell a user to weaken
77
+ containment to make an unsupported feature work.
78
+
79
+ Resource constraints are bounds, not generation targets. Current VM limits are
80
+ 64 MiB heap, 64 KiB stack, and an interpreter interrupt after more than 256 polls.
81
+ The worker transport also has a 1-second timeout. There is no promised 50 ms
82
+ evaluation budget. Execution speed depends on the device and work performed.
83
+ Encoded results and dependency metadata are limited to 524,288 characters each;
84
+ encoding rejects excessive structural depth. Keep data and calculations small.
85
+ Display limits can stop a document even when its JavaScript evaluation succeeds.
86
+
87
+ #### Task: output-review
88
+
89
+ Before finishing, check that every used tag and prop appears in this reference,
90
+ every named binding is declared before its consumer, each callback accepts the
91
+ documented value type, all control branches close, and every displayed claim is
92
+ supported by supplied data. Remove inert action buttons unless they intentionally
93
+ demonstrate appearance. Do not add an implementation checklist to the generated UI.
94
+
95
+ ## 2. Syntax and parsing
96
+
97
+ ### Markdown
98
+
99
+ Write paragraphs, headings, ordered/unordered lists, emphasis, blockquotes, code
100
+ fences, and GitHub-flavored tables, task lists, and strikethrough. Use Markdown
101
+ headings for the overall document outline. A DIL title is a styled h3 and does
102
+ not choose an arbitrary heading level. Use ordinary prose when no special layout
103
+ or control is needed.
104
+
105
+ Separate paragraphs and major blocks with blank lines. Keep indentation consistent
106
+ within component children; the renderer normalizes Markdown indentation but is
107
+ not an MDX compiler. Avoid relying on a large indentation offset to create code
108
+ blocks inside a layout. Use explicit fences or CodeBlock for source displays.
109
+
110
+ Markdown links are deliberately displayed as inert text spans containing their
111
+ labels. They do not open their href. Markdown images are suppressed. Use a visible
112
+ plain-text URL if the reader needs the destination, and do not promise that
113
+ clicking it navigates. There is no DIL anchor or image tag to restore browser
114
+ behavior. Bare URLs can be recognized by GFM but still render without navigation.
115
+
116
+ Task-list checkboxes are Markdown presentation. They do not provide onChange,
117
+ state binding, or an action target. Use the checkbox component for user-editable
118
+ choices. A Markdown table is useful for ordinary tabular prose; use the DIL table
119
+ composition when cells contain components or reactive controls.
120
+
121
+ Fenced code protects its contents from DIL parsing. Put triple backticks or triple
122
+ tildes at the beginning of a line, with no more than three preceding spaces.
123
+ Close the fence with the same marker on a line of its own. A longer fence, such as
124
+ four backticks, can enclose lines that start with three.
125
+ A `mermaid` language fence renders a Mermaid diagram rather than ordinary code.
126
+ Other language labels request display syntax, not code execution.
127
+
128
+ Inline code spans follow Markdown: a run of one or more backticks opens a span
129
+ that the next run of the same length closes. Braces, tags, and directives inside
130
+ a span display literally and never evaluate, so `{ leading: false }` or `<div>`
131
+ in backticks is safe in prose. A span cannot cross a blank line or continue into
132
+ a line that starts another block: a list item, heading, blockquote, fence,
133
+ component tag, or block directive such as `{:else}`. A component tag later on
134
+ the same line stays inside the span. An unmatched backtick protects nothing, and
135
+ a backslash before a backtick makes it literal. A closing fence may be followed
136
+ on its line by a component closing tag or block directive.
137
+
138
+ Lowercase HTML-like tags that are not DIL tags, such as `<div>`, `</span>`, or
139
+ `<br/>`, display as plain text up to their closing `>`, and so do autolinks such
140
+ as `<https://example.com>`. Their quoted or braced attribute values stay text,
141
+ but any other brace is an expression again, so `a<b then {total}` shows the
142
+ value. Do not rely on this to format output; use Markdown or DIL components. A
143
+ capitalized tag that is not a supported component renders an
144
+ unsupported-component error in its place. To insert a brace in prose, interpolate
145
+ a string containing it. Entity spellings can have Markdown-dependent display
146
+ behavior; use a string expression when exact text matters.
147
+
148
+ ```dil
149
+ # Review summary
150
+
151
+ **Ready** for review. The sample contains synthetic values.
152
+
153
+ - Static prose uses Markdown.
154
+ - Interactive choices use supported components.
155
+
156
+ | Item | Value |
157
+ | --- | --- |
158
+ | Example records | 2 |
159
+
160
+ <text>A literal brace: {"{"}</text>
161
+ ```
162
+
163
+ Markdown expressions are inserted as text after Markdown parsing. Do not expect a
164
+ string containing `**bold**` to become emphasis, a string containing a tag to
165
+ become a component, or an interpolated URL to become a live link. Put the needed
166
+ formatting around the expression in authored source instead.
167
+
168
+ ### JavaScript expressions
169
+
170
+ Use `{expression}` in text or `property={expression}` in attributes. The outer
171
+ braces delimit JavaScript; an object value therefore needs its own object braces,
172
+ as in `content={{chartType:"bar",data:[]}}`. Numbers written in quoted attributes
173
+ remain strings. Supply numeric props with braces, including slider min/max/step,
174
+ layout spacing, dimensions, and SVG numeric coordinates.
175
+
176
+ Attribute forms:
177
+
178
+ | Form | Meaning |
179
+ | -------------------------------------- | ------------------- |
180
+ | `color="secondary"` | String literal |
181
+ | `color='secondary'` | String literal |
182
+ | `border` | Boolean true |
183
+ | `border={false}` | Boolean false |
184
+ | `padding={4}` | Number |
185
+ | `value={name}` | Evaluated binding |
186
+ | `onChange={setName}` | Callback reference |
187
+ | `onClick={() => setCount(n => n + 1)}` | Function expression |
188
+
189
+ Quoted attributes use a simple delimiter scan. Do not rely on JavaScript-style
190
+ backslash escaping inside a quoted attribute. For text containing its surrounding
191
+ quote, use a JavaScript string expression. Do not repeat an attribute; the parser
192
+ can overwrite earlier entries, which is not a useful generation contract.
193
+ Unquoted scalar values, JSX spread syntax, shorthand object props, and arbitrary
194
+ attribute fragments are unsupported.
195
+
196
+ Expressions use ordinary JavaScript arithmetic, comparisons, logical operators,
197
+ ternaries, optional chaining, nullish coalescing, property access, computed property
198
+ names, string/array/object literals, template strings, and functions. Array
199
+ filter/map/reduce/find/some/every and string transformations work as JavaScript
200
+ operations. Keep transformations pure and their output JSON-compatible.
201
+
202
+ ```dil
203
+ {@body const item = {name:"Ada",hours:3,rate:20}}
204
+ {@body const field = "name"}
205
+ <text>{item[field]}: {item.hours * item.rate}</text>
206
+ <caption>{`Hours: ${item.hours}`}</caption>
207
+ <input aria-label="Quoted example" value={'Say "hello"'} disabled/>
208
+ ```
209
+
210
+ Interpolation display:
211
+
212
+ | Evaluated value | Text output |
213
+ | ----------------------- | ------------------- |
214
+ | String or finite number | Its displayed value |
215
+ | true / false | `true` / `false` |
216
+ | null / undefined | Empty text |
217
+ | Object or array | `[object Object]` |
218
+
219
+ Use `items.join(", ")` for a text list, `JSON.stringify(record)` for intentionally
220
+ displayed JSON, or an each directive for repeated UI. Arrays returned by map do
221
+ not become React children. JavaScript cannot contain authored JSX elements.
222
+ Do not create UI using string concatenation and expect interpolation to parse it.
223
+
224
+ The bridge serializes data using JSON and represents callback functions with
225
+ internal handles. Do not author or inspect those handles. Avoid circular records,
226
+ BigInt, sparse data that relies on undefined, Map/Set instances, prototype-based
227
+ objects, or objects with behavior in serialization. Non-finite numbers do not
228
+ retain useful numeric meaning across JSON serialization. Use plain records,
229
+ arrays, strings, booleans, null, and finite numbers.
230
+
231
+ Keep statements out of expression positions. Assignments and updates may be valid
232
+ JavaScript expressions but are forbidden generation patterns during rendering.
233
+ Use callback bodies for state changes. A handler may use local const declarations,
234
+ an if guard, and a bounded loop, but simpler immutable transformations are preferred.
235
+ Do not use top-level await, imports, exports, or async resource work.
236
+
237
+ `Intl.NumberFormat` is a limited host bridge. Its format operation uses en-US
238
+ formatting with maximumFractionDigits=0; supplied locale and options do not
239
+ configure the formatter. It is not a full Intl implementation. For fixed decimal
240
+ precision, use a bounded numeric expression and `.toFixed(n)`. Do not promise
241
+ currency/locale behavior from the bridge that the implementation does not provide.
242
+
243
+ #### Task: format-values
244
+
245
+ ```dil
246
+ {@body const amount = 1234.5}
247
+ <text>{new Intl.NumberFormat().format(amount)}</text>
248
+ <caption>{amount.toFixed(2)} units</caption>
249
+ ```
250
+
251
+ ### State declarations
252
+
253
+ Use one `{@body ...}` directive for each declaration. Place declarations before
254
+ the interface. The supported forms are deliberately narrower than a JavaScript
255
+ program body:
256
+
257
+ ```text
258
+ {@body const [name,setName] = DIL.useState(initialValue)}
259
+ {@body const name = expression}
260
+ ```
261
+
262
+ An optional final semicolon is accepted. Use names beginning with a letter or
263
+ underscore and containing only letters, digits, and underscores. Avoid keywords,
264
+ duplicate names, builtin names, DIL, GenUI, and implementation-like double-underscore
265
+ names. Though some JavaScript identifiers allow `$` or Unicode, declaration
266
+ recognition is narrower; use the stated subset for reliable generation.
267
+
268
+ Only the exact state-pair destructuring shape is supported. Do not omit the setter,
269
+ add a third item, destructure objects, use nested patterns, or declare several
270
+ bindings in one directive. Ordinary const declarations create derived expression
271
+ bindings. There is no let/var declaration mode, function statement declaration,
272
+ class statement, or arbitrary script block. A function can be a const expression.
273
+
274
+ State initialization is once per state name in the mounted document. An initializer
275
+ may be a value or a function returning the initial value. Keep it side-effect-free.
276
+ Do not put a clipboard operation or setter call in initialization. State setters
277
+ accept either a replacement value or an updater function receiving the previous
278
+ value. A function passed to the setter is treated as an updater; do not use state
279
+ as a general function store.
280
+
281
+ Ordinary const bindings are getters. Their expression is recomputed when read;
282
+ they are not once-only snapshots and do not memoize expensive work. A getter can
283
+ read state and other bindings. Keep dependency order straightforward and avoid
284
+ cycles. A derived callback can be declared as a const function expression and
285
+ passed to a supported event prop.
286
+
287
+ ```dil
288
+ {@body const [quantity,setQuantity] = DIL.useState(() => 2)}
289
+ {@body const unitPrice = 15}
290
+ {@body const total = quantity * unitPrice}
291
+ {@body const addOne = () => setQuantity(previous => previous + 1)}
292
+ <button onClick={addOne}>Add one</button>
293
+ <text>{quantity} items: {total} units</text>
294
+ ```
295
+
296
+ All body declarations belong to the document, even when their source is written
297
+ inside a conditional or each block. Parsing collects declarations independently
298
+ of which visual branch later renders. A declaration inside a loop does not run
299
+ once per item and does not inherit item/index bindings. Generate unconditional
300
+ top-level declarations and keep per-item state in a top-level keyed object/array.
301
+
302
+ Do not rely on duplicate state declarations to reset a value. A matching existing
303
+ state name retains state; collisions between state, setter, and derived names can
304
+ fail or redefine bindings. Give each state pair and derived binding a unique name.
305
+ Use a reset button calling the setter when a reset is required.
306
+
307
+ No useEffect, useMemo, useCallback, useRef, or useId hook is exposed to DIL source.
308
+ The renderer may use React hooks internally; they are not document hooks. Do not
309
+ translate a React example into DIL by retaining arbitrary hook calls. Replace
310
+ computed values with const getters and user effects with supported event handlers.
311
+
312
+ ### Conditional rendering
313
+
314
+ Use `{#if condition}`, optional `{:else if condition}` branches, optional
315
+ `{:else}`, and `{/if}`. Conditions use JavaScript truthiness. The first matching
316
+ branch is selected. With no match and no else, nothing renders. Keep else-if and
317
+ else within their enclosing if and close nested branches before outer branches.
318
+
319
+ ```dil
320
+ {@body const [level,setLevel] = DIL.useState(0)}
321
+ <slider aria-label="Level" min={0} max={10} step={1} value={level} onChange={setLevel}/>
322
+ {#if level >= 8}
323
+ <badge color="warning">High</badge>
324
+ {:else if level > 0}
325
+ <badge color="success">Active</badge>
326
+ {:else}
327
+ <caption>Not started</caption>
328
+ {/if}
329
+ ```
330
+
331
+ A conditional changes visibility, not declaration scope or document state lifetime.
332
+ Hiding an input does not erase its top-level state. If the next interaction needs
333
+ a reset, perform that reset explicitly in a callback. Keep data guards before
334
+ unsafe property reads inside rendered expressions; the renderer can evaluate
335
+ branch conditions while resolving which branch to present.
336
+
337
+ Repeat with `{#each expression as item}` or
338
+ `{#each expression as item,index}` and close with `{/each}`. The expression should
339
+ evaluate to an array. Non-arrays currently produce no items rather than a typed
340
+ error. Do not depend on that fallback to represent loading or a failed request.
341
+ Use an explicit empty-array branch and truthful status text.
342
+
343
+ Within repeated rendered content, item is the current value and index is its
344
+ zero-based position. Nested repeats can read outer scope bindings. Choose
345
+ different variable names for outer and inner items to avoid accidental shadowing.
346
+ Declare shared transformations outside the repeat only when they do not need an
347
+ item binding. Otherwise compute the small expression inline.
348
+
349
+ Use stable unique string or numeric `id` properties on repeated objects. Without
350
+ such an ID, identity falls back to index. Reordering a positional list can attach
351
+ local widget state to a different record. The language has no key prop, keyed-each
352
+ suffix, or custom key callback. Do not invent one. Allocate IDs when creating new
353
+ records, not from the current array position after filtering.
354
+
355
+ ```dil
356
+ {@body const groups = [{id:"g1",name:"Review",items:[{id:"a",name:"Alpha"}]}]}
357
+ {#each groups as group}
358
+ <box border padding={3}>
359
+ <title>{group.name}</title>
360
+ {#each group.items as entry,index}
361
+ <text>{index + 1}. {entry.name}</text>
362
+ {/each}
363
+ </box>
364
+ {/each}
365
+ ```
366
+
367
+ Streaming rules:
368
+
369
+ - Append source suffixes while preserving every previous character.
370
+ - Close useful children as they arrive; an enclosing layout may remain open.
371
+ - Leave incomplete tokens pending only while more source will follow.
372
+ - Close an each block before expecting its repeated template to display.
373
+ - End with a complete document; completion drops unfinished tokens and marks the
374
+ answer incomplete.
375
+ - Use callbacks and state for interaction; do not patch earlier streamed source.
376
+
377
+ The parser retains lexical state across chunk boundaries, including quoted strings,
378
+ templates, comments, and regular expressions inside braces. There is no need to
379
+ align transport chunks with tokens. Generation must still produce conventional,
380
+ balanced syntax. Do not count a recoverable partial prefix as valid final output.
381
+ Repeat templates wait for their closing directive so the renderer does not mount
382
+ large numbers of incomplete copies.
383
+
384
+ #### Task: incomplete-stream
385
+
386
+ An unfinished first chunk may be:
387
+
388
+ ```text
389
+ <box padding={
390
+ ```
391
+
392
+ Append `4}><text>Ready</text></box>` and then signal completion. The finished source is:
393
+
394
+ ```dil
395
+ <box padding={4}><text>Ready</text></box>
396
+ ```
397
+
398
+ Source replacement, document/message identity changes, and remounting can create a
399
+ new document and reset state. Ordinary suffix appends and virtualized offscreen
400
+ unmounts retain document-owned state. Do not use internal render timing or viewport
401
+ visibility as an authoring lifecycle API.
402
+
403
+ ## 3. Component API
404
+
405
+ Read each property block literally. `(optional)` means the property may be omitted;
406
+ `default` describes the widget's current fallback. `(required for generation)`
407
+ means supply a meaningful value even if the permissive renderer has a fallback.
408
+ Defaults do not replace type correctness. A string `"false"` is not boolean false.
409
+
410
+ Children categories: inline = text, inline Markdown, and inline components; generic
411
+ = inline or block content; raw = literal source; none = no content. Named children
412
+ restrict direct child component types. Whitespace and control directives may
413
+ surround named children. These contracts guide generation; the parser is not an
414
+ element-specific schema validator. Do not use parser acceptance as evidence of
415
+ valid browser structure.
416
+
417
+ Shared layout props:
418
+
419
+ ```text
420
+ padding: number | {x?:number,y?:number} (optional)
421
+ gap: number (optional)
422
+ radius: "sm" | "md" | "lg" | "xl" | "2xl" | "full" | string (optional)
423
+ border: boolean | {size?:number,color?:string} (optional)
424
+ background: string (optional)
425
+ clip: boolean (optional)
426
+ width: number | string (optional)
427
+ maxWidth: number | string (optional)
428
+ height: number | string (optional)
429
+ flex: number | string (optional)
430
+ columns: number (optional)
431
+ align: string (optional)
432
+ justify: string (optional)
433
+ wrap: "wrap" (optional)
434
+ ```
435
+
436
+ Only components whose Properties block lists `layout` consume these props. A table
437
+ does not become a layout container merely because table-cell accepts layout props.
438
+ Text, button, input, chart, and other components do not automatically accept them.
439
+ Wrap a component in box/row/grid when the surrounding layout needs control.
440
+
441
+ Numeric padding and gap use four-pixel units: 1=4px, 2=8px, 3=12px, 4=16px.
442
+ An x/y padding object sets inline/block padding independently. It does not accept
443
+ top/right/bottom/left keys. Numeric width, height, and maxWidth are pixels. String
444
+ dimensions may use valid CSS lengths or percentages. Flex follows CSS flex values;
445
+ it is not a width unit. Do not emit numeric strings when a number was intended.
446
+
447
+ Named radii: sm=4px, md=6px, lg=8px, xl=12px, 2xl=16px, full=9999px. A custom
448
+ string radius is passed as a CSS border radius; use a simple valid value. The
449
+ names are DIL names, not assumptions about another design system's scale.
450
+ Border true enables a 1px theme border. An object may change size and color.
451
+ Border false/omitted disables this authored border. Keep widths finite and positive.
452
+
453
+ Use semantic color tokens: default, primary, secondary, tertiary, success, warning,
454
+ danger, surface-secondary, surface-tertiary, subtle, strong. A valid CSS color is
455
+ also accepted when shorter than 128 characters and free of URL/custom-variable
456
+ or declaration syntax. A color object, gradient, arbitrary theme variable, or
457
+ stylesheet string is not accepted. Invalid colors are ignored rather than repaired.
458
+
459
+ Use align start/center/end/stretch for item alignment. Start/end map to flex-start/
460
+ flex-end. Use justify start/center/end/between for common distribution; between
461
+ maps to space-between. The reader accepts strings beyond these recommendations,
462
+ but a browser may reject them. Keep generation to meaningful CSS alignment values.
463
+ Use wrap="wrap" only when the layout benefits from wrapping.
464
+
465
+ Columns sets a responsive grid's requested column count. It is not a general
466
+ child-span/placement API. Use a small positive integer. The stock grid reduces
467
+ columns on narrow screens. A layout prop can be accepted by a shared reader
468
+ without changing a component that does not use the corresponding CSS behavior.
469
+ For example, columns does not turn a row into a grid.
470
+
471
+ ### Text components
472
+
473
+ #### Component: text
474
+
475
+ ```text
476
+ Properties:
477
+ color: string (optional; inherited color when omitted)
478
+ size: "xs" | "sm" | "md" | "lg" | "xl" | "2xl" (optional; inherited size)
479
+ weight: "medium" (optional)
480
+ tabularNums: boolean (optional; default false)
481
+
482
+ Children: inline
483
+ ```
484
+
485
+ Render a span-like text value. Use it for a value inside a row, a short reactive
486
+ sentence, or a styled phrase. Text does not accept layout props, an arbitrary
487
+ style object, href, or a strong prop. Use Markdown emphasis in children when bold
488
+ text is needed. Weight medium maps to 500. Other weight strings do not expose a
489
+ general font-weight API.
490
+
491
+ The size scale is xs=12px, sm=14px, md=16px, lg=18px, xl=24px, 2xl=30px at the
492
+ normal 16px root. tabularNums requests tabular numeral shapes; it does not format
493
+ numbers or align decimal places automatically. Format the expression separately.
494
+ Keep nested content inline; use box for a paragraph containing blocks.
495
+
496
+ ```dil
497
+ {@body const revenue = 1234.5}
498
+ <text size="lg" weight="medium" tabularNums>{revenue.toFixed(2)} units</text>
499
+ ```
500
+
501
+ #### Component: title
502
+
503
+ ```text
504
+ Properties:
505
+ color: string (optional)
506
+ size: "xs" | "sm" | "md" | "lg" | "xl" | "2xl" (optional; default lg)
507
+ weight: "medium" (optional; consumed but does not override title weight)
508
+ tabularNums: boolean (optional; default false)
509
+
510
+ Children: inline
511
+ ```
512
+
513
+ Render a styled h3 with weight 600, default size 18px, compact line height, and no
514
+ default margin. A weight prop is consumed by the shared text branch but the title
515
+ keeps weight 600. Do not use it to request a different heading rank. For a document
516
+ with h1/h2 hierarchy, use Markdown headings. Use title for a card or panel heading.
517
+ An explicit size can change appearance without changing the heading's rank.
518
+
519
+ Keep controls outside the title; compose title and actions as siblings in a row.
520
+ Do not rely on a title prop to associate it with a dialog, table, or input. Those
521
+ associations are not exposed by the DIL API. Give the surrounding content a clear
522
+ reading order using ordinary source order.
523
+
524
+ #### Component: caption
525
+
526
+ ```text
527
+ Properties:
528
+ color: string (optional; default secondary)
529
+ size: "xs" | "sm" | "md" | "lg" | "xl" | "2xl" (optional; default xs)
530
+ weight: "medium" (optional)
531
+ tabularNums: boolean (optional; default false)
532
+
533
+ Children: inline
534
+ ```
535
+
536
+ Render small secondary text, defaulting to 12px. Use it for units, context, helper
537
+ messages, or a short validation message. It is a text span, not a live-region,
538
+ tooltip, accessible description binding, or hidden-label mechanism. Keep messages
539
+ visible and sufficiently descriptive without claiming automatic announcement.
540
+
541
+ Use an explicit danger/warning color only when the text explains the state. A
542
+ red caption alone is not form validation; derive a validity value and guard the
543
+ action. Avoid shrinking essential instructions merely to fit a narrow layout.
544
+
545
+ #### Component: badge
546
+
547
+ ```text
548
+ Properties:
549
+ color: string (optional)
550
+
551
+ Children: inline
552
+ ```
553
+
554
+ Render a compact status label. Success, warning, and danger colors receive related
555
+ background treatment. Other accepted colors affect foreground but do not create
556
+ arbitrary background variants. There is no variant, size, icon, or onClick prop.
557
+ Place an icon in children when needed and keep visible status text beside it.
558
+
559
+ Do not make a badge look like a selectable control. Use button, checkbox, radio,
560
+ or segmented-control for input. For a changing status, render the badge from state
561
+ or an if directive. Labels such as Saved must reflect an actual supported action;
562
+ copying text is not a saved-server mutation.
563
+
564
+ #### Component: label
565
+
566
+ ```text
567
+ Properties: none
568
+
569
+ Children: inline
570
+ ```
571
+
572
+ Render medium-weight visual label text. It is a styled span and has no for/id
573
+ association props. Pair it with input aria-label using the same visible words.
574
+ Do not claim that clicking this component focuses a separate input. Checkbox and
575
+ radio provide their own clickable label composition through their children.
576
+
577
+ Use label for a compact field heading or a value-group label. It cannot take
578
+ layout, color, or size props. Wrap it in a suitable layout instead of inventing
579
+ styling attributes. Keep the visible label stable while validation messages change.
580
+
581
+ #### Component: CodeBlock
582
+
583
+ ```text
584
+ Properties:
585
+ language: string (optional; default empty string)
586
+ editable: boolean (optional; default false)
587
+
588
+ Children: raw
589
+ ```
590
+
591
+ Place source directly between opening and closing tags. Everything is literal
592
+ until the exact `</CodeBlock>` delimiter: braces, tags, and directives inside are
593
+ not evaluated. The source prop does not exist. Do not nest DIL components to
594
+ format the source. The display trims leading/trailing whitespace when no draft
595
+ exists, so do not use it for byte-exact whitespace preservation requirements.
596
+
597
+ Editable true exposes an internal text editor. Edits remain local to this widget's
598
+ document draft store. They do not run, update DIL state, call an authored handler,
599
+ or create a downloadable file. There is no onChange/value/defaultValue API. Choose
600
+ input for an authored string value and CodeBlock for editable source presentation.
601
+
602
+ The widget provides copy and word-wrap controls. Copy is user-triggered and can
603
+ fail due to clipboard permissions. Its built-in feedback reports that outcome.
604
+ Do not wrap the code block in another action control or duplicate its copy button
605
+ unless the separate action has a distinct task. Code copying is subject to the
606
+ 65,536-character clipboard limit; source display is limited to 131,072 characters.
607
+
608
+ ```dil
609
+ <CodeBlock language="javascript" editable>
610
+ const greeting = "Hello";
611
+ console.log(greeting);
612
+ </CodeBlock>
613
+ ```
614
+
615
+ Do not include the exact closing delimiter inside its raw source; it terminates
616
+ the component even if it occurs in a quoted source-language string. Use a fenced
617
+ code block for examples that need to show the delimiter literally. Language is a
618
+ display hint, not a compiler selection or permission to execute code.
619
+
620
+ ### Media components
621
+
622
+ #### Component: AsyncImage
623
+
624
+ ```text
625
+ Properties:
626
+ query: string (required for generation)
627
+
628
+ Children: none
629
+ ```
630
+
631
+ Render an offline, locally constructed placeholder illustration with query as
632
+ alt text. The name does not imply remote search, asynchronous model generation,
633
+ or retrieval of a verified image. The stock implementation builds a simple SVG
634
+ landscape; query text only influences a small set of color choices. Do not use it
635
+ as evidence of an actual person, place, product, or event.
636
+
637
+ The placeholder uses a 16:9 wrapper and built-in rounding. There is no src, alt,
638
+ width, height, fit, loading, or onLoad prop. Size the surrounding layout if needed.
639
+ If the browser cannot load the constructed image, the widget displays Image
640
+ unavailable. Do not set your own loaded flag; no authored load callback exists.
641
+
642
+ ```dil
643
+ <box maxWidth={480}>
644
+ <AsyncImage query="Placeholder illustration of a mountain landscape"/>
645
+ <caption>Illustrative placeholder, not a photograph.</caption>
646
+ </box>
647
+ ```
648
+
649
+ A host may override components under an explicitly supplied contract. That does
650
+ not change the stock contract documented here. Do not assume a host override can
651
+ search the web or load a supplied URL unless the prompt states that capability.
652
+ No stock video, audio player, file preview, or upload component is available.
653
+
654
+ ### Layout components
655
+
656
+ #### Component: box
657
+
658
+ ```text
659
+ Properties:
660
+ layout
661
+
662
+ Children: generic
663
+ ```
664
+
665
+ Use box for vertical flow, panels, and card surfaces. Children retain their source
666
+ order. A box with border/padding/radius/background can present a card without
667
+ inventing a card component. Keep a card's title, body, and actions as readable
668
+ siblings. Set maxWidth when a form or card should remain narrow on a wide screen.
669
+
670
+ Box participates in document flow and may virtualize long child sequences. Do not
671
+ assume every offscreen child is simultaneously mounted. Document state survives
672
+ that presentation behavior. Do not use viewport-dependent expression effects or
673
+ DOM queries to coordinate children. Use declared state and bindings.
674
+
675
+ #### Task: static-card
676
+
677
+ ```dil
678
+ <box border padding={4} radius="xl" gap={3} maxWidth={480}>
679
+ <title>Release review</title>
680
+ <text>Ready for the next review.</text>
681
+ <badge color="success">Prepared</badge>
682
+ </box>
683
+ ```
684
+
685
+ #### Component: row
686
+
687
+ ```text
688
+ Properties:
689
+ layout
690
+
691
+ Children: generic
692
+ ```
693
+
694
+ Use row for horizontal siblings such as a title and action, icon and value, or a
695
+ short control group. Set align for vertical alignment and gap for spacing.
696
+ Use wrap="wrap" when labels or controls may exceed available width. Avoid fixed
697
+ heights for rows with wrapping content. A narrow display must retain all controls.
698
+
699
+ Use justify="between" for two groups or spacer for flexible separation. Do not
700
+ insert literal spaces to align components. For multi-column data that needs
701
+ consistent row/cell relationships, use a table rather than independent rows.
702
+
703
+ #### Component: grid
704
+
705
+ ```text
706
+ Properties:
707
+ layout
708
+
709
+ Children: generic
710
+ ```
711
+
712
+ Use grid for a responsive set of peers. Set columns to a small positive integer
713
+ and gap to the desired spacing unit. The stock grid reduces the requested column
714
+ count to one at viewport widths of 520px or less. Do not compute viewport breakpoints in document
715
+ JavaScript; browser dimensions are not exposed.
716
+
717
+ Generic children are accepted; grid-item is available when a child needs its own
718
+ layout props. There is no template-string API, row-span, column-span, area name,
719
+ or media-query prop. A grid-item does not add a missing placement API. Keep the
720
+ source order meaningful for a single-column reading order.
721
+
722
+ #### Component: grid-item
723
+
724
+ ```text
725
+ Properties:
726
+ layout
727
+
728
+ Children: generic
729
+ ```
730
+
731
+ Use inside grid when a cell needs padding, border, background, or a nested layout.
732
+ It is a generic layout wrapper and does not expose grid coordinates or spanning.
733
+ The renderer does not require it around every grid child. Avoid a wrapper when
734
+ the child already owns its necessary layout.
735
+
736
+ #### Composition: grid > grid-item
737
+
738
+ ```dil
739
+ <grid columns={2} gap={3}>
740
+ <grid-item border padding={3} radius="lg">
741
+ <title>Alpha</title><text>12 sample units</text>
742
+ </grid-item>
743
+ <grid-item border padding={3} radius="lg">
744
+ <title>Beta</title><text>18 sample units</text>
745
+ </grid-item>
746
+ </grid>
747
+ ```
748
+
749
+ #### Component: spacer
750
+
751
+ ```text
752
+ Properties: none
753
+
754
+ Children: none
755
+ ```
756
+
757
+ Use as a flexible child in a row to consume remaining space. It does not accept a
758
+ size, height, width, or direction prop. Use layout gap/padding for fixed spacing.
759
+ An isolated spacer in ordinary vertical content does not promise a fixed blank
760
+ region. Keep spacing intent on the parent whenever possible.
761
+
762
+ #### Component: divider
763
+
764
+ ```text
765
+ Properties: none
766
+
767
+ Children: none
768
+ ```
769
+
770
+ Render a standard separator. No orientation, thickness, color, or layout props
771
+ are exposed. Use it between meaningful groups rather than after every line.
772
+ Do not place it directly among table rows or radio children; those parents require
773
+ their named child types. Put separation inside a cell/content region if needed.
774
+
775
+ #### Component: table
776
+
777
+ ```text
778
+ Properties: none
779
+
780
+ Children: table-row
781
+ ```
782
+
783
+ The renderer supplies the body element. Do not emit table-section, tbody, thead,
784
+ tr, td, or th. DIL table has no column sizing, header, dividers, or caption prop.
785
+ Describe the table with nearby text/title and use a first row of readable labels
786
+ when using component cells. For ordinary header semantics and prose-only cells,
787
+ prefer a Markdown table.
788
+
789
+ Keep row widths consistent by generating the same number of cells. There is no
790
+ colSpan or rowSpan. Use a two-column key/value table instead of inventing spanning
791
+ to present irregular data. For empty data, render a caption outside the table
792
+ through a conditional rather than inserting arbitrary text directly into it.
793
+
794
+ #### Component: table-row
795
+
796
+ ```text
797
+ Properties: none
798
+
799
+ Children: table-cell
800
+ ```
801
+
802
+ Place directly under table, optionally produced by each/if directives. It has no
803
+ selected, header, background, onClick, or layout API. If a row needs an action,
804
+ place a button inside a cell. A pressable cannot replace the structural row.
805
+ Do not wrap rows in box or grid-item under table.
806
+
807
+ #### Component: table-cell
808
+
809
+ ```text
810
+ Properties:
811
+ layout
812
+
813
+ Children: generic
814
+ ```
815
+
816
+ Place directly under table-row. Cells may contain text, badges, nested layout,
817
+ controls, or a small chart. Layout props style the cell; they do not change table
818
+ structure. There is no align prop with text-alignment semantics beyond the shared
819
+ layout reader, no colSpan/rowSpan, and no header role selection. Use nested rows
820
+ or text formatting only when their actual contract meets the task.
821
+
822
+ #### Composition: table > table-row > table-cell
823
+
824
+ ```text
825
+ <table>
826
+ children: table-row
827
+ <table-row>
828
+ children: table-cell
829
+ <table-cell>
830
+ children: generic
831
+ ```
832
+
833
+ ```dil
834
+ {@body const records = [{id:"a",name:"Alpha",amount:12},{id:"b",name:"Beta",amount:18}]}
835
+ <table>
836
+ <table-row><table-cell><text weight="medium">Name</text></table-cell><table-cell><text weight="medium">Amount</text></table-cell></table-row>
837
+ {#each records as item}
838
+ <table-row><table-cell>{item.name}</table-cell><table-cell><text tabularNums>{item.amount}</text></table-cell></table-row>
839
+ {/each}
840
+ </table>
841
+ ```
842
+
843
+ #### Component: popover
844
+
845
+ ```text
846
+ Properties: none
847
+
848
+ Children: popover-trigger | popover-content
849
+ ```
850
+
851
+ Compose one trigger region and one content region. Open/close behavior belongs to
852
+ the stock overlay. No open/defaultOpen/onOpenChange prop is exposed to DIL. Do not
853
+ add state solely to imitate the existing trigger behavior. Place related optional
854
+ details or controls in the content and keep the triggering label meaningful.
855
+
856
+ #### Component: popover-trigger
857
+
858
+ ```text
859
+ Properties: none
860
+
861
+ Children: one button
862
+ ```
863
+
864
+ Place exactly one button inside. The trigger context composes that button with
865
+ the overlay behavior; it does not make an arbitrary box or text node a trigger.
866
+ The button can have normal button props. It needs no onClick to open the popover.
867
+ Avoid combining unrelated privileged work with merely opening optional details.
868
+
869
+ #### Component: popover-content
870
+
871
+ ```text
872
+ Properties:
873
+ side: "top" | "bottom" (optional; default bottom)
874
+ align: "end" | "center" (optional; default center)
875
+
876
+ Children: generic
877
+ ```
878
+
879
+ Place directly under popover. The widget bounds content to the viewport. It has
880
+ no width, padding, layout, modal, placement-offset, or animation prop. Put a box
881
+ inside when the content needs layout. Side values other than top fall back to
882
+ bottom; align values other than end fall back to center. Generate only the stated
883
+ values. Content can contain inputs or actions as siblings in a normal layout.
884
+
885
+ #### Composition: popover > popover-trigger + popover-content
886
+
887
+ ```text
888
+ <popover>
889
+ children: popover-trigger | popover-content
890
+ <popover-trigger>
891
+ children: one button
892
+ <popover-content>
893
+ children: generic
894
+ ```
895
+
896
+ ```dil
897
+ <popover>
898
+ <popover-trigger><button variant="outline">Details</button></popover-trigger>
899
+ <popover-content side="bottom" align="end">
900
+ <box gap={2}><title>Review notes</title><text>No additional action is required.</text></box>
901
+ </popover-content>
902
+ </popover>
903
+ ```
904
+
905
+ #### Component: carousel
906
+
907
+ ```text
908
+ Properties: none
909
+
910
+ Children: carousel-item
911
+ ```
912
+
913
+ Render a horizontal item collection with built-in previous/next controls. Keep
914
+ items as direct children. There is no activeIndex, items data prop, onChange,
915
+ autoplay, orientation, or slide-size API. Do not use it to represent a required
916
+ stepper whose progression must be controlled by validation.
917
+
918
+ #### Component: carousel-item
919
+
920
+ ```text
921
+ Properties: none
922
+
923
+ Children: generic
924
+ ```
925
+
926
+ Place under carousel. Each slide has a stock width and vertical content wrapper.
927
+ Use a box inside for border/padding/background customization. A carousel-item
928
+ does not accept layout props itself. Keep items understandable independently and
929
+ avoid placing essential instructions only on an undiscovered later slide.
930
+
931
+ #### Composition: carousel > carousel-item
932
+
933
+ ```text
934
+ <carousel>
935
+ children: carousel-item
936
+ <carousel-item>
937
+ children: generic
938
+ ```
939
+
940
+ ```dil
941
+ <carousel>
942
+ <carousel-item><box border padding={4}><title>Alpha</title><text>First option.</text></box></carousel-item>
943
+ <carousel-item><box border padding={4}><title>Beta</title><text>Second option.</text></box></carousel-item>
944
+ </carousel>
945
+ ```
946
+
947
+ ### Input components
948
+
949
+ #### Component: button
950
+
951
+ ```text
952
+ Properties:
953
+ aria-label: string (optional)
954
+ disabled: boolean (optional; default false)
955
+ variant: "outline" | "ghost" | "default" (optional; default default)
956
+ size: "sm" | "xs" | "default" (optional; default default)
957
+ block: boolean (optional; default false)
958
+ onClick: callback () => void (optional)
959
+
960
+ Children: inline
961
+ ```
962
+
963
+ Use for explicit actions. Inline children may include icon and text. Give icon-only
964
+ buttons aria-label; keep the name about the action rather than the icon. A label
965
+ fallback exists for some stock source patterns, but generation must provide a
966
+ clear name instead of depending on those demo-oriented fallbacks.
967
+
968
+ Default is a filled action, outline is bordered, and ghost is low-chrome. Unknown
969
+ variant or size strings fall back to default; do not generate unsupported styles.
970
+ Block makes the button full-width. There is no href, type, loading, icon prop,
971
+ layout prop set, or arbitrary DOM event forwarding. Buttons are not form submits.
972
+
973
+ The authored disabled prop and runtime readiness both affect availability. A
974
+ streaming or stopped document may keep a control unavailable even when disabled
975
+ is false. Do not use enabled appearance as proof that an expression has completed.
976
+ Keep handler logic safe independently of a disabled UI guard.
977
+
978
+ ```dil
979
+ {@body const [count,setCount] = DIL.useState(0)}
980
+ <button variant="outline" onClick={() => setCount(previous => previous + 1)}>
981
+ <icon name="mouse-pointer-click"/> Increment
982
+ </button>
983
+ <text>{count}</text>
984
+ ```
985
+
986
+ #### Component: pressable
987
+
988
+ ```text
989
+ Properties:
990
+ layout
991
+ color: string (optional)
992
+ onClick: callback () => void (optional)
993
+
994
+ Children: generic
995
+ ```
996
+
997
+ Render a button-like full-card action whose children may include noninteractive
998
+ layout and block content. Keep the action name visible in its content. There is
999
+ no authored aria-label or disabled prop. Do not add them just because the backing
1000
+ element is a browser button. Use button when a task needs authored disabled state.
1001
+
1002
+ Never nest buttons, inputs, radio groups, checkbox, links, or another pressable
1003
+ inside. Place secondary actions beside the pressable, not inside its clickable
1004
+ surface. Layout props apply to the surface; color affects foreground. The widget
1005
+ provides the same action clipboard feedback as button for supported copy actions.
1006
+
1007
+ #### Component: input
1008
+
1009
+ ```text
1010
+ Properties:
1011
+ aria-label: string (optional in API; supply for generation)
1012
+ value: string (optional; default empty string)
1013
+ placeholder: string (optional; default empty string)
1014
+ disabled: boolean (optional; default false)
1015
+ onChange: callback (value:string) => void (optional)
1016
+
1017
+ Children: none
1018
+ ```
1019
+
1020
+ Use as a single-line string input. Bind value and onChange to document state when
1021
+ other content depends on edits. OnChange receives a string, not a DOM event. There
1022
+ is no type, numeric parsing, email mode, required, pattern, minLength, maxLength,
1023
+ autoComplete, onBlur, or onKeyDown prop. Use explicit string validation and number
1024
+ conversion in derived expressions if the task requires them.
1025
+
1026
+ Placeholder is a hint, not a persistent accessible label. Provide aria-label and,
1027
+ when useful, a sibling label with matching words. The visual label component does
1028
+ not associate itself through an ID. Keep validation messages as visible siblings.
1029
+
1030
+ The widget holds immediate local text drafts while worker updates are acknowledged,
1031
+ including composition input. That does not create a document binding when no
1032
+ onChange/setter exists. Bind state deliberately rather than reading DOM state.
1033
+ Avoid assuming that a local field draft is persisted beyond the document.
1034
+
1035
+ #### Component: slider
1036
+
1037
+ ```text
1038
+ Properties:
1039
+ aria-label: string (optional in API; supply for generation)
1040
+ min: number (optional; default 0)
1041
+ max: number (optional; default 100)
1042
+ step: number (optional; default 1)
1043
+ value: number (optional; default 0)
1044
+ marks: {label:string}[] (optional; default no marks)
1045
+ disabled: boolean (optional; default false)
1046
+ onChange: callback (value:number) => void (optional)
1047
+
1048
+ Children: none
1049
+ ```
1050
+
1051
+ Use a finite numeric range with min < max and step > 0. Keep value within the
1052
+ range and choose an initial value aligned with the intended step. The widget
1053
+ accepts one numeric value and emits one number; it is not a multi-thumb range
1054
+ control. There is no defaultValue, orientation, tick-value, format, or value-label
1055
+ prop. Show a sibling text value when users need the precise current number.
1056
+
1057
+ Marks are labels distributed along the control. Records do not accept position,
1058
+ value, color, or onClick fields. A mark named 50 does not itself place a tick at 50. Use a short set of endpoint/context labels consistent with the range.
1059
+
1060
+ ```dil
1061
+ {@body const [level,setLevel] = DIL.useState(40)}
1062
+ <label>Level</label>
1063
+ <slider aria-label="Level" min={0} max={100} step={5} value={level} onChange={setLevel} marks={[{label:"Low"},{label:"High"}]}/>
1064
+ <text tabularNums>{level}%</text>
1065
+ ```
1066
+
1067
+ #### Component: checkbox
1068
+
1069
+ ```text
1070
+ Properties:
1071
+ checked: boolean (optional; default false)
1072
+ disabled: boolean (optional; default false)
1073
+ lineThrough: boolean (optional; default true)
1074
+ onChange: callback (checked:boolean) => void (optional)
1075
+
1076
+ Children: inline
1077
+ ```
1078
+
1079
+ Children provide the visible clickable label. Bind checked/onChange to state for
1080
+ reactive output. The callback receives boolean, not the underlying tri-state API
1081
+ or a DOM event. There is no indeterminate, value, defaultChecked, name, aria-label,
1082
+ or form-submission prop. Use a separate string field only when the task needs it.
1083
+
1084
+ LineThrough controls the checked-label styling. Set it false for settings and
1085
+ consent-style choices; the default suits checklist completion. The label's checked
1086
+ styling uses the bound upstream checked value, while the control can show its
1087
+ immediate draft during worker acknowledgement. Keep binding complete rather than
1088
+ using local draft behavior as a state-management mechanism.
1089
+
1090
+ #### Component: radio-group
1091
+
1092
+ ```text
1093
+ Properties:
1094
+ aria-label: string (optional in API; supply for generation)
1095
+ value: string (optional; default empty string)
1096
+ direction: "row" (optional; omitted gives vertical arrangement)
1097
+ onChange: callback (value:string) => void (optional)
1098
+
1099
+ Children: radio
1100
+ ```
1101
+
1102
+ Use for one choice among visible options. Bind value/onChange to state and give
1103
+ each radio a unique nonempty string value. Use direction="row" for a wrapping
1104
+ horizontal group. A direction="column" prop is not an additional enum member;
1105
+ omit the prop for vertical. No authored disabled prop exists for the group or
1106
+ its radios. Runtime readiness may disable them while source is incomplete.
1107
+
1108
+ #### Component: radio
1109
+
1110
+ ```text
1111
+ Properties:
1112
+ value: string (required for generation)
1113
+
1114
+ Children: inline
1115
+ ```
1116
+
1117
+ Place directly under radio-group, possibly through each/if. Children supply the
1118
+ visible label. Selection and callbacks belong to the group; radio has no checked,
1119
+ onChange, disabled, name, or layout props. Do not use a standalone radio outside
1120
+ its required group context.
1121
+
1122
+ #### Composition: radio-group > radio
1123
+
1124
+ ```text
1125
+ <radio-group>
1126
+ children: radio
1127
+ <radio>
1128
+ children: inline
1129
+ ```
1130
+
1131
+ ```dil
1132
+ {@body const [billing,setBilling] = DIL.useState("monthly")}
1133
+ <radio-group aria-label="Billing" value={billing} onChange={setBilling} direction="row">
1134
+ <radio value="monthly">Monthly</radio>
1135
+ <radio value="annual">Annual</radio>
1136
+ </radio-group>
1137
+ <text>Selected: {billing}</text>
1138
+ ```
1139
+
1140
+ #### Component: segmented-control
1141
+
1142
+ ```text
1143
+ Properties:
1144
+ aria-label: string (optional in API; supply for generation)
1145
+ value: string (optional; default empty string)
1146
+ options: {value:string,label:string}[] (required for generation)
1147
+ onChange: callback (value:string) => void (optional)
1148
+
1149
+ Children: none
1150
+ ```
1151
+
1152
+ Use a short array of distinct nonempty string values and readable labels. Bind
1153
+ value/onChange to state. The component emits the selected string, ignoring an
1154
+ empty deselection value. Keep the bound selection among options unless a deliberate
1155
+ initial unselected state is required. It has no multi-select, disabled, option
1156
+ icons, per-option callbacks, or arbitrary child composition.
1157
+
1158
+ Segmented-control selects a value; it is not an authored tabs/panel component.
1159
+ Render content for the selection using if directives. Do not invent tab-panel,
1160
+ onTabChange, or content props. For long choices, prefer radio-group with readable
1161
+ labels rather than crowded segments.
1162
+
1163
+ ## 4. Specialized components
1164
+
1165
+ ### Data-linked components
1166
+
1167
+ There is no stock query, remote-data, subscription, provider, entity-card, or
1168
+ service-connected component. The supported data-bearing visualization props are
1169
+ Chart.content and NodeGraph.content. Their data shapes are specified under Charts.
1170
+ Ordinary text, tables, and repeated layouts can also display supplied records.
1171
+ None of those components fetches facts based on its label or content keys.
1172
+
1173
+ When the enclosing task supplies data, include the relevant records as literal
1174
+ values in a declaration. Keep only fields needed for the interface. Preserve
1175
+ stable IDs, units, and source labels. If the input is incomplete, show the known
1176
+ fields with an explicit unknown/empty condition rather than fabricating values.
1177
+ Do not infer a remote endpoint from a component name.
1178
+
1179
+ Separate facts from local interaction state. A const record is a good authoring
1180
+ representation of read-only supplied facts; it is not a frozen JavaScript object
1181
+ or an enforced immutable data store. Treat it as read-only by instruction. Use
1182
+ useState for a selected record, draft, filter, or local edit. Do not claim that
1183
+ changing that state updates the original external system.
1184
+
1185
+ #### Task: supplied-data-summary
1186
+
1187
+ ```dil
1188
+ {@body const records = [{id:"a",name:"Alpha",amount:12},{id:"b",name:"Beta",amount:18}]}
1189
+ {@body const [query,setQuery] = DIL.useState("")}
1190
+ {@body const visibleRecords = records.filter(item => item.name.toLowerCase().includes(query.toLowerCase()))}
1191
+ {@body const visibleTotal = visibleRecords.reduce((sum,item) => sum + item.amount,0)}
1192
+ <input aria-label="Filter records" value={query} onChange={setQuery}/>
1193
+ <text>{visibleRecords.length} records, {visibleTotal} sample units</text>
1194
+ {#if visibleRecords.length > 0}
1195
+ {#each visibleRecords as item}<text>{item.name}: {item.amount}</text>{/each}
1196
+ {:else}
1197
+ <caption>No matching records.</caption>
1198
+ {/if}
1199
+ ```
1200
+
1201
+ The filter transforms supplied data locally. It does not make a search request.
1202
+ Use wording such as Filter records rather than Search the database when no database
1203
+ operation exists. Do not add a refresh button that merely redraws unchanged facts.
1204
+ A reset-filter button can be truthful because it changes local query state.
1205
+
1206
+ ### Links and citations
1207
+
1208
+ No stock anchor, citation, reference card, source lookup, footnote resolver, or
1209
+ URL-opening action is available. Markdown recognizes link syntax but displays the
1210
+ label as an inert text span. It does not preserve the URL as a clickable target.
1211
+ Markdown images do not load. The renderer's safe behavior must not be described
1212
+ as browser navigation.
1213
+
1214
+ When a source matters, include its supplied title and visible URL in ordinary text.
1215
+ Keep source numbering local and unambiguous if the input already provides it.
1216
+ Do not invent a bracketed token that implies a host citation registry. A string
1217
+ such as [1] is just text unless the surrounding application provides another
1218
+ explicit contract. Source cards can be composed from box/title/caption, but they
1219
+ remain informational and do not gain privileged behavior.
1220
+
1221
+ #### Task: source-attribution
1222
+
1223
+ ```dil
1224
+ <box border padding={3} gap={2}>
1225
+ <title>Supplied source</title>
1226
+ <text>Example release notes</text>
1227
+ <caption>https://example.com/releases</caption>
1228
+ <caption>Reference address shown as text.</caption>
1229
+ </box>
1230
+ ```
1231
+
1232
+ If the user requests a navigable link, state the capability limit rather than
1233
+ inventing GenUI.openUrl or window.open. A copy button may copy the supplied URL
1234
+ under the documented clipboard contract. Label that action Copy URL, not Open.
1235
+ Do not automatically copy a URL when displaying its source.
1236
+
1237
+ ### Embedded application blocks
1238
+
1239
+ There is no authored iframe, app, embed, script, external widget, plugin frame,
1240
+ browser page, or sandbox block. The renderer's own isolation frame is application
1241
+ infrastructure, not a DIL component that generated source can configure. An
1242
+ interactive dashboard built from supported components remains one DIL document.
1243
+
1244
+ CodeBlock displays or locally edits source. It does not compile an app, install
1245
+ dependencies, execute a preview, create a file, or host a separate application.
1246
+ Do not present its editor as a working code runner. If the task needs a mock
1247
+ application, implement its supported local interactions with state and components
1248
+ and label any unimplemented server behavior accurately.
1249
+
1250
+ Custom components are available only under an explicit host-supplied authoring
1251
+ contract. That contract must establish exact case-sensitive names, property types,
1252
+ children, callback arguments, data boundaries, and any host actions. A demo showing
1253
+ a host component override is not an instruction to generate that component in
1254
+ another document. Do not guess a custom component because the stock vocabulary
1255
+ lacks a desired feature.
1256
+
1257
+ The stock specification validator cannot establish arbitrary custom host semantics.
1258
+ If a host contract exists, validate custom examples against that contract and the
1259
+ real host integration. Keep the distinction visible in the generation instructions.
1260
+ Without such a contract, use only stock components and state the unsupported part.
1261
+
1262
+ ## 5. Visualizations
1263
+
1264
+ ### Images
1265
+
1266
+ Use AsyncImage under its Media components contract when a placeholder illustration
1267
+ fits the task. It cannot display a supplied photograph URL. Do not put a URL in
1268
+ query and claim the result is the referenced image. Markdown image syntax is
1269
+ suppressed; there is no stock img/src escape hatch.
1270
+
1271
+ For simple authored illustrations, use the presentation-only SVG vocabulary. Put
1272
+ a text description next to the visual and keep important values available as text.
1273
+ SVG is useful for a small static schematic, not a replacement for accessible
1274
+ controls, arbitrary HTML, or a browser canvas application.
1275
+
1276
+ #### Component: svg
1277
+
1278
+ ```text
1279
+ Properties:
1280
+ svg props
1281
+
1282
+ Children: SVG elements
1283
+ ```
1284
+
1285
+ Shared SVG props:
1286
+
1287
+ ```text
1288
+ viewBox: string
1289
+ width: number | string
1290
+ height: number | string
1291
+ x: number | string
1292
+ y: number | string
1293
+ cx: number | string
1294
+ cy: number | string
1295
+ r: number | string
1296
+ rx: number | string
1297
+ ry: number | string
1298
+ d: string
1299
+ fill: string
1300
+ stroke: string
1301
+ strokeWidth: number | string
1302
+ fontSize: number | string
1303
+ fontWeight: number | string
1304
+ opacity: number | string
1305
+ id: string
1306
+ markerWidth: number | string
1307
+ markerHeight: number | string
1308
+ refX: number | string
1309
+ refY: number | string
1310
+ orient: string
1311
+ markerEnd: string
1312
+ ```
1313
+
1314
+ All SVG props are optional at the reader boundary; choose the subset required for
1315
+ the selected geometry. Give the root viewBox and practical dimensions so the
1316
+ image is visible and bounded. Use a simple fill/stroke and finite coordinates.
1317
+ SVG strings do not use the DIL semantic-color translation; choose literal safe
1318
+ paint values such as #5e4cff, currentColor, or none where appropriate.
1319
+
1320
+ Allowed SVG elements: svg, defs, marker, path, rect, circle, line, polyline,
1321
+ polygon, g, ellipse, text, tspan, linearGradient, stop. Their names are exact;
1322
+ linearGradient is not linear-gradient. These descendants are recognized only in
1323
+ an SVG subtree. A text tag there is an SVG text element, not the DIL text component.
1324
+ Do not place DIL controls or layouts inside an SVG drawing.
1325
+
1326
+ The attribute allowlist is shared, not a complete per-element SVG schema. A prop
1327
+ being allowed does not mean it has meaning on every element. Use width/height for
1328
+ rect, cx/cy/r for circle, cx/cy/rx/ry for ellipse, and d for path. The allowlist
1329
+ does not expose x1/y1/x2/y2 or points; use path for explicit lines and polygons.
1330
+ Empty line/polyline/polygon elements may parse and render without useful geometry.
1331
+
1332
+ Gradient and stop elements are recognized, but offset, stopColor, gradientUnits,
1333
+ x1/x2, and related full-SVG controls are not exposed. Do not promise an authored
1334
+ multi-stop gradient with this restricted surface. Choose solid paint for reliable
1335
+ illustrations. The examples include these recognized elements to show the actual
1336
+ supported vocabulary and its limits, not to invent the missing attributes.
1337
+
1338
+ SVG values are filtered for dangerous/external strings. External http/data targets,
1339
+ JavaScript URLs, markup-like/declaration strings, and nonlocal URL references are
1340
+ not accepted. Local references such as `url(#arrow)` are supported for paint or
1341
+ marker use. Choose simple document-local IDs containing letters, digits, underscores,
1342
+ or hyphens. Avoid colliding IDs across several authored SVGs in one document.
1343
+
1344
+ There is no href, xlink:href, style, className, transform, event handler, aria-label,
1345
+ role, foreignObject, script, image, a, animate, or arbitrary SVG element/prop API.
1346
+ Do not add them. An SVG element has no onClick bridge and cannot authorize a host
1347
+ action. Use a real button next to the drawing if the user needs an action.
1348
+
1349
+ #### Composition: svg > defs > marker; svg > g > path + text
1350
+
1351
+ ```text
1352
+ <svg>
1353
+ children: SVG elements
1354
+ <defs>
1355
+ children: marker | linearGradient
1356
+ <marker>
1357
+ children: SVG shapes
1358
+ <linearGradient>
1359
+ children: stop
1360
+ <g>
1361
+ children: SVG shapes | g | text
1362
+ <text>
1363
+ children: text content | tspan
1364
+ ```
1365
+
1366
+ The tree describes useful combinations for generation; it is not a claim that
1367
+ the parser validates a complete SVG content model. Keep definitions in defs,
1368
+ shape groupings in g, and text spans inside text. Use nested tspan only for simple
1369
+ text positioning supported by x/y; there is no dx/dy/textAnchor API.
1370
+
1371
+ ```dil
1372
+ <caption>Flow from Alpha to Beta.</caption>
1373
+ <svg viewBox="0 0 320 100" width="100%" height={100}>
1374
+ <defs>
1375
+ <marker id="flow-arrow" markerWidth={10} markerHeight={10} refX={8} refY={3} orient="auto">
1376
+ <path d="M0 0 L8 3 L0 6 Z" fill="#5e4cff"/>
1377
+ </marker>
1378
+ </defs>
1379
+ <g>
1380
+ <rect x={10} y={20} width={90} height={55} rx={8} fill="#eeeeff"/>
1381
+ <rect x={220} y={20} width={90} height={55} rx={8} fill="#eeeeff"/>
1382
+ <path d="M105 47 L210 47" stroke="#5e4cff" strokeWidth={2} markerEnd="url(#flow-arrow)"/>
1383
+ <text x={25} y={52} fontSize={16} fill="#252838">Alpha</text>
1384
+ <text x={235} y={52} fontSize={16} fill="#252838">Beta</text>
1385
+ </g>
1386
+ </svg>
1387
+ ```
1388
+
1389
+ ### Charts
1390
+
1391
+ Use Chart for quantitative plots, NodeGraph for selectable service-style node
1392
+ diagrams, and Mermaid for declarative diagram text. They have separate contracts;
1393
+ do not combine their content formats. Each is a leaf component. Place a nearby
1394
+ title, units, and summary in normal layout siblings.
1395
+
1396
+ #### Component: Chart
1397
+
1398
+ ```text
1399
+ Properties:
1400
+ content: object (required for generation)
1401
+
1402
+ Children: none
1403
+ ```
1404
+
1405
+ Line/bar/scatter content:
1406
+
1407
+ ```text
1408
+ {
1409
+ chartType: "line" | "bar" | "scatter",
1410
+ data: object[],
1411
+ xKey: string,
1412
+ series: {dataKey:string,label?:string}[]
1413
+ }
1414
+ ```
1415
+
1416
+ Pie content:
1417
+
1418
+ ```text
1419
+ {
1420
+ chartType: "pie",
1421
+ data: object[],
1422
+ nameKey: string,
1423
+ valueKey: string
1424
+ }
1425
+ ```
1426
+
1427
+ Supply chartType explicitly. An absent or unrecognized type currently follows
1428
+ the line rendering branch, but that fallback is not an invitation to generate
1429
+ other names. Only line, bar, scatter, and pie are supported chart types. Do not
1430
+ emit area, radar, bubble, candlestick, heatmap, or composed-chart components.
1431
+
1432
+ Data is an array of plain records. For line/bar, xKey identifies a category or
1433
+ numeric x field; each series dataKey identifies a finite numeric y field. Label
1434
+ is optional and falls back to dataKey. Keep field keys and series keys stable and
1435
+ distinct. Supply units in nearby text rather than inventing axis-formatting props.
1436
+ Do not place React components, functions, or expression strings inside content.
1437
+
1438
+ Scatter uses numeric x data and the first series' dataKey for the shared numeric
1439
+ y-axis configuration. Use one series for the clearest contract. If several series
1440
+ are used, keep numeric fields and units compatible; there is no per-series axis
1441
+ or arbitrary domain configuration. Line/bar may display several series from the
1442
+ same data rows. Missing numeric values are not a defined missing-data policy;
1443
+ preprocess supplied data deliberately and explain unknown values in text.
1444
+
1445
+ Pie uses nameKey for labels and valueKey for quantitative slices. Prefer unique
1446
+ nonempty string names and nonnegative finite values with a positive total. It
1447
+ renders a ring-style pie using stock radii. There is no innerRadius, label
1448
+ formatter, custom sector, selectedSlice, or click callback API.
1449
+
1450
+ Stock chart dimensions, palette, axes, tooltip, marks, and legend are internal.
1451
+ No DIL width/height/layout, color, onSelect, onClick, tooltip, axis, domain, or
1452
+ legend prop is exposed. A wrapper can constrain available width but cannot set a
1453
+ new chart-content schema. Do not infer the full Recharts API from the underlying
1454
+ implementation.
1455
+
1456
+ Multi-series line/bar legends support internal visibility selection. This widget
1457
+ state is not exposed as a DIL binding and does not invoke an authored callback.
1458
+ Scatter and pie legends do not offer the same multi-series selection contract.
1459
+ Hover/focus emphasis is presentation behavior, not data selection available to
1460
+ other document components.
1461
+
1462
+ #### Task: line-chart
1463
+
1464
+ ```dil
1465
+ <title>Sample trend</title>
1466
+ <Chart content={{chartType:"line",data:[{month:"Jan",actual:10,plan:12},{month:"Feb",actual:15,plan:14}],xKey:"month",series:[{dataKey:"actual",label:"Actual"},{dataKey:"plan",label:"Plan"}]}}/>
1467
+ <caption>Synthetic units. Actual rises from 10 to 15.</caption>
1468
+ ```
1469
+
1470
+ #### Task: pie-chart
1471
+
1472
+ ```dil
1473
+ <Chart content={{chartType:"pie",data:[{name:"Alpha",amount:30},{name:"Beta",amount:70}],nameKey:"name",valueKey:"amount"}}/>
1474
+ <caption>Synthetic shares: Alpha 30%, Beta 70%.</caption>
1475
+ ```
1476
+
1477
+ When chart data is empty, show an explicit empty-state caption instead of claiming
1478
+ the absence of marks means a zero result. Use a small textual table when values
1479
+ matter more than the visual. Do not fabricate enough points to make a supplied
1480
+ dataset appear more complete. If a transformation drops records, explain why.
1481
+
1482
+ #### Component: NodeGraph
1483
+
1484
+ ```text
1485
+ Properties:
1486
+ content: object (required for generation)
1487
+ selectedId: string (optional; no selected node when omitted)
1488
+ onSelect: callback (id:string) => void (optional)
1489
+
1490
+ Children: none
1491
+ ```
1492
+
1493
+ Content:
1494
+
1495
+ ```text
1496
+ {
1497
+ nodes: {id:string,label:string,sub?:string,x?:number,y?:number}[],
1498
+ edges: {id?:string,source:string,target:string,label?:string}[]
1499
+ }
1500
+ ```
1501
+
1502
+ Use unique nonempty string node IDs. Label is the main node text and sub is an
1503
+ optional secondary line. x/y are optional finite positioning hints. Without
1504
+ positions, nodes are arranged in a simple multi-column layout. The renderer
1505
+ adjusts columns and vertical placement to keep nodes readable, so positions are
1506
+ not a pixel-exact canvas contract.
1507
+
1508
+ Edges refer to existing node IDs by source and target. Give edges stable IDs when
1509
+ supplied; otherwise the renderer uses positional IDs. Invalid endpoints are
1510
+ filtered. Only the first 200 nodes and first 400 edges are considered; an edge
1511
+ to a truncated node will not appear. Keep author output within the limits and
1512
+ summarize oversized supplied networks rather than silently claiming full coverage.
1513
+
1514
+ SelectedId controls visible node selection. OnSelect receives the clicked node's
1515
+ string ID. Bind the pair to state if selection should update another panel or
1516
+ remain visibly selected. Without that binding, a callback alone does not update
1517
+ the selectedId prop. There is no selected-node record argument or DOM event.
1518
+
1519
+ Node dragging and connection creation are disabled. Pan/zoom controls are built
1520
+ in; no DIL camera, zoom, connect, drag, edge click, node style, node type, handle,
1521
+ or custom action prop exists. Treat the graph as a navigable selection display,
1522
+ not an editable diagramming application. State changes must use supported callbacks.
1523
+
1524
+ ```dil
1525
+ {@body const [selectedId,setSelectedId] = DIL.useState("api")}
1526
+ {@body const graph = {nodes:[{id:"api",label:"API",sub:"Entry"},{id:"db",label:"Database",sub:"Storage"}],edges:[{id:"api-db",source:"api",target:"db",label:"reads"}]}}
1527
+ <NodeGraph content={graph} selectedId={selectedId} onSelect={setSelectedId}/>
1528
+ <text>Selected service: {selectedId}</text>
1529
+ ```
1530
+
1531
+ #### Component: Mermaid
1532
+
1533
+ ```text
1534
+ Properties:
1535
+ source: string (required for generation)
1536
+
1537
+ Children: none
1538
+ ```
1539
+
1540
+ Pass Mermaid diagram text as a string expression. A fenced mermaid block is an
1541
+ alternative Markdown representation using the same diagram renderer. Do not put
1542
+ raw children between Mermaid tags; the component reads its source prop only.
1543
+ Use a simple flowchart or another valid Mermaid diagram syntax without app-level
1544
+ configuration.
1545
+
1546
+ The renderer uses strict security, neutral theme, no HTML labels, maximum text
1547
+ size 8,192 characters, and maximum 100 edges. Authored configuration directives,
1548
+ frontmatter at the start, classDef, style/linkStyle lines, click actions, and
1549
+ HTML-like source are rejected. Output is sanitized to remove unsafe SVG content,
1550
+ including links, scripts, and foreignObject. Do not depend on a sanitized link to
1551
+ become navigable.
1552
+
1553
+ Invalid or incomplete source can display a diagram error. Keep Mermaid source
1554
+ small and syntactically complete. There is no authored callback, theme, config,
1555
+ download, source editor, or node-selection API. Use NodeGraph if the task requires
1556
+ supported selection state rather than attempting Mermaid click handlers.
1557
+
1558
+ ```dil
1559
+ <Mermaid source={"flowchart LR\n Request --> Review\n Review --> Ready"}/>
1560
+ ```
1561
+
1562
+ ### Maps
1563
+
1564
+ No map component, map adapter, tiles, geocoder, geographic marker, route engine,
1565
+ or map selection callback exists. Do not emit Map, GeoMap, map, marker, or arbitrary
1566
+ HTML embeds. A node graph is not a geographic coordinate system. SVG can show a
1567
+ small authored schematic, but that does not establish a map-data integration.
1568
+
1569
+ Present supplied places as text, a table, or local filtering over records. Preserve
1570
+ given coordinates as values rather than inventing geocoding results. If location
1571
+ names lack coordinates, do not claim a computed distance or route. Show the
1572
+ capability limit when an interactive map is central to the request.
1573
+
1574
+ #### Task: location-table
1575
+
1576
+ ```dil
1577
+ {@body const places = [{id:"berlin",name:"Berlin",latitude:52.52,longitude:13.405}]}
1578
+ <title>Supplied locations</title>
1579
+ <table>
1580
+ {#each places as place}
1581
+ <table-row>
1582
+ <table-cell>{place.name}</table-cell>
1583
+ <table-cell>{place.latitude}, {place.longitude}</table-cell>
1584
+ </table-row>
1585
+ {/each}
1586
+ </table>
1587
+ <caption>Coordinates are shown as supplied; no map or geocoding service is connected.</caption>
1588
+ ```
1589
+
1590
+ ## 6. Interactivity
1591
+
1592
+ ### State management
1593
+
1594
+ Only `DIL.useState(initialValue)` is supported. It is recognized by the declaration
1595
+ syntax and does not expose the React hook object. Use one unique state pair per
1596
+ declaration. A lazy initializer runs when that state name is first initialized.
1597
+ Do not call hooks in interpolation, handler bodies, derived const expressions,
1598
+ conditional branches, or repeated templates.
1599
+
1600
+ Setters accept a replacement value or a function mapping previous state to next
1601
+ state. Use an updater whenever a new value depends on the previous value and
1602
+ several actions may be queued. Keep the updater pure. It may build a new array
1603
+ or object using ordinary JavaScript expressions; do not mutate previous state.
1604
+
1605
+ ```dil
1606
+ {@body const [count,setCount] = DIL.useState(0)}
1607
+ <row gap={2} align="center">
1608
+ <button onClick={() => setCount(previous => previous + 1)}>Increment</button>
1609
+ <button variant="outline" onClick={() => setCount(0)}>Reset</button>
1610
+ <text tabularNums>{count}</text>
1611
+ </row>
1612
+ ```
1613
+
1614
+ Replace arrays with map/filter/spread and replace changed records with object
1615
+ spread. Mutation such as items.push, record.done=true, or sort in place does not
1616
+ establish the intended setter/dependency contract. Use a copied array before sort.
1617
+ Keep source facts separate from editable state so an agent can tell which values
1618
+ are user drafts.
1619
+
1620
+ State is document-owned. It persists across appends, conditional visibility, and
1621
+ virtualized presentation within that document. A new message/document identity or
1622
+ replacement can reset it. There is no storage or cross-message state API. Do not
1623
+ promise reload persistence, multi-user synchronization, browser-tab sharing, or
1624
+ server write-back.
1625
+
1626
+ Repeated source shares top-level state. Represent per-record edits with an array
1627
+ of records or an object keyed by stable ID. Never create a body declaration inside
1628
+ each expecting an independent state instance. Use the repeated item ID in the
1629
+ handler's immutable update instead.
1630
+
1631
+ #### Task: repeated-record-state
1632
+
1633
+ ```dil
1634
+ {@body const [tasks,setTasks] = DIL.useState([{id:"a",label:"Review Alpha",done:false},{id:"b",label:"Review Beta",done:false}])}
1635
+ {@body const completeCount = tasks.filter(task => task.done).length}
1636
+ <text>{completeCount} of {tasks.length} complete</text>
1637
+ {#each tasks as task}
1638
+ <checkbox checked={task.done} onChange={checked => setTasks(previous => previous.map(entry => entry.id === task.id ? {...entry,done:checked} : entry))}>
1639
+ {task.label}
1640
+ </checkbox>
1641
+ {/each}
1642
+ ```
1643
+
1644
+ Ordinary const getters are sufficient for derived count, totals, filtered arrays,
1645
+ validity, and selected-record lookup. They are not a caching API. Do not generate
1646
+ useMemo or useCallback to optimize a DIL document. Keep expensive transformations
1647
+ small and reuse a readable binding when it improves correctness.
1648
+
1649
+ Widget-local state has a different contract. Input/slider/checkbox keep immediate
1650
+ drafts while worker callbacks complete. CodeBlock retains editable source drafts.
1651
+ Chart legends hold visibility internally. Popover/carousel maintain their own
1652
+ interaction state. Only documented bindings expose values back to authored DIL.
1653
+ Do not try to read internal widget state through refs, DOM, hidden props, or globals.
1654
+
1655
+ ### Event callbacks
1656
+
1657
+ Callback values may be setter references, derived function bindings, or function
1658
+ expressions. Pass the function; do not invoke it while assigning the prop. For
1659
+ example, onChange={setName} is a callback, while onChange={setName("Ada")} performs
1660
+ work during evaluation and does not supply a valid handler.
1661
+
1662
+ #### Callback: button.onClick and pressable.onClick
1663
+
1664
+ Signature: `() => void`. Ignore the undefined argument supplied by the generic
1665
+ invocation path. There is no MouseEvent, target, currentTarget, preventDefault,
1666
+ stopPropagation, or coordinates. Use captured declared values to determine the
1667
+ action. A user activation on these supported action controls can authorize a
1668
+ synchronous GenUI.copy request; arbitrary callbacks do not gain that authority.
1669
+
1670
+ #### Callback: input.onChange
1671
+
1672
+ Signature: `(value:string) => void`. Receive the complete current input string.
1673
+ Use a setter directly for simple controlled input. For a transform, use a function
1674
+ that calls the setter with the transformed value. Keep the value prop a string.
1675
+ Do not read event.target.value or depend on an input-type conversion API.
1676
+
1677
+ #### Callback: slider.onChange
1678
+
1679
+ Signature: `(value:number) => void`. Receive the scalar numeric value, not an array
1680
+ of thumb values or a browser event. Bind the number directly and derive formatted
1681
+ text separately. Do not put a clipboard request in this handler; slider changes
1682
+ are not privileged action-control activations.
1683
+
1684
+ #### Callback: checkbox.onChange
1685
+
1686
+ Signature: `(checked:boolean) => void`. Receive true or false. The underlying UI
1687
+ library's indeterminate representation is normalized; it is not exposed as a DIL
1688
+ value. Bind checked to boolean state. Keep the callback about changing local choice
1689
+ state, and use a separate action button for copying or other explicitly supported
1690
+ privileged work.
1691
+
1692
+ #### Callback: radio-group.onChange and segmented-control.onChange
1693
+
1694
+ Signature: `(value:string) => void`. Receive the selected option's value, not its
1695
+ label or a record. Resolve a record with a derived lookup if the UI needs more
1696
+ fields. Selection does not submit a form. Segmented-control ignores an empty
1697
+ deselection value; do not use empty option strings as ordinary choices.
1698
+
1699
+ #### Callback: NodeGraph.onSelect
1700
+
1701
+ Signature: `(id:string) => void`. Receive the node ID. Bind selectedId to state
1702
+ and derive the selected node from content.nodes if needed. There is no graph
1703
+ event object, coordinates, edge ID, or selection array. Selection callbacks do
1704
+ not authorize clipboard requests.
1705
+
1706
+ Keep callback bodies synchronous and bounded. A callback can update several local
1707
+ state values in order, but it is not a lifecycle effect or an asynchronous task
1708
+ runner. Do not depend on a Promise return value, timer, network response, or host
1709
+ callback completion. State setters have no success receipt available to authored
1710
+ source. Derive updated UI from state, not from guessed render timing.
1711
+
1712
+ Control readiness is managed by the runtime. Open/incomplete source, unresolved
1713
+ expressions, or a stopped document can make controls temporarily unavailable.
1714
+ Do not add a fake loading hook or artificial timer to work around that state.
1715
+ Generate complete source and let the existing renderer manage readiness.
1716
+
1717
+ There are no onSubmit, onInput, onBlur, onFocus, onKeyDown, onLoad, onError,
1718
+ onOpenChange, onValueChange, onDrag, or arbitrary DOM callbacks in the author API.
1719
+ Do not copy underlying React primitive names into DIL. Use the event names listed
1720
+ here, including the capitalized NodeGraph component name.
1721
+
1722
+ #### Task: validation-form
1723
+
1724
+ There is no form tag or browser validation contract. Compose a field, derived
1725
+ validity, visible error text, and guarded action. A disabled button is useful
1726
+ feedback; a handler guard ensures the action itself also checks validity.
1727
+
1728
+ ```dil
1729
+ {@body const [name,setName] = DIL.useState("")}
1730
+ {@body const trimmedName = name.trim()}
1731
+ {@body const valid = trimmedName.length >= 2 && trimmedName.length <= 80}
1732
+ <box gap={3} maxWidth={420}>
1733
+ <label>Name</label>
1734
+ <input aria-label="Name" value={name} onChange={setName} placeholder="At least two characters"/>
1735
+ {#if !valid}<caption color="danger">Enter a name between 2 and 80 characters.</caption>{/if}
1736
+ <button disabled={!valid} onClick={() => { if (valid) GenUI.copy(trimmedName); }}>Copy name</button>
1737
+ </box>
1738
+ ```
1739
+
1740
+ This copies a valid local draft. It does not submit, save, create a user, or email
1741
+ anyone. If the requested outcome requires an absent host action, state that limit
1742
+ and do not substitute a misleading success message. Do not call the copy result
1743
+ a successful form submission.
1744
+
1745
+ ### Data bindings
1746
+
1747
+ There is no predefined external-data variable, provider registry, props object,
1748
+ context hook, request result, subscription, or remote mutation API. Embed supplied
1749
+ JSON-compatible records in a top-level const declaration. A host can supply a
1750
+ separate custom contract, but stock generated source must not guess one.
1751
+
1752
+ Read fields with dot access, optional chaining, and computed keys. Use pure array
1753
+ and string operations to transform data for display. Computed property names are
1754
+ supported JavaScript expressions, not a remote query language. Only access fields
1755
+ present in the supplied data or created by the document. Use explicit null/empty
1756
+ checks when the input permits absent values.
1757
+
1758
+ Use const bindings as read-only source facts by instruction. This is not an
1759
+ enforced Object.freeze guarantee. Use state for drafts and preserve source IDs.
1760
+ Do not change source data in place to imitate a two-way binding. A state change
1761
+ updates dependent expressions; it does not write back to an external origin.
1762
+
1763
+ Binding scope:
1764
+
1765
+ | Binding | Visible where | Lifetime |
1766
+ | --------------------- | ----------------------------------------------------------- | --------------------------- |
1767
+ | Top-level const/state | Same document's expressions and callbacks | Mounted document |
1768
+ | each item/index | Rendered subtree of that iteration, including nested scopes | Corresponding item instance |
1769
+ | Callback parameter | Its function body | Callback invocation |
1770
+ | Code editor draft | Internal CodeBlock only | Document draft identity |
1771
+ | Chart legend state | Internal Chart only | Widget presentation state |
1772
+
1773
+ A body declaration is always document-scoped and never captures a loop binding.
1774
+ An event function expression inside a repeated rendered subtree can capture its
1775
+ item/index scope. Use the stable item ID in updates rather than a potentially
1776
+ stale position. Keep source declarations outside the repeated template.
1777
+
1778
+ ```dil
1779
+ {@body const supplied = [{id:"a",name:"Alpha",amount:12},{id:"b",name:"Beta",amount:18}]}
1780
+ {@body const [selected,setSelected] = DIL.useState("a")}
1781
+ {@body const current = supplied.find(item => item.id === selected)}
1782
+ {#each supplied as item}
1783
+ <button variant="outline" onClick={() => setSelected(item.id)}>{item.name}</button>
1784
+ {/each}
1785
+ {#if current}<text>{current["name"]}: {current.amount}</text>{:else}<caption>No selection.</caption>{/if}
1786
+ ```
1787
+
1788
+ Use the same principle for chart data: derive a plain content object or use bound
1789
+ arrays within content. The chart does not subscribe to the originating dataset;
1790
+ it re-renders from the supplied prop. An empty array is an empty dataset, not a
1791
+ loading indication. Loading and failed-request states require explicit supplied
1792
+ facts or a separate host contract; do not fabricate asynchronous phases.
1793
+
1794
+ ### Host actions
1795
+
1796
+ Only `GenUI.copy(text:string): undefined` is exposed. It requests a clipboard
1797
+ write of at most 65,536 characters. It does not read the clipboard or create a
1798
+ file. Pass an actual string; serialize records deliberately if the requested
1799
+ task is to copy JSON. A nonempty request is required for a dispatched copy;
1800
+ empty text currently produces no clipboard operation.
1801
+
1802
+ Invoke copy synchronously in a button or pressable onClick after explicit user
1803
+ activation. The renderer carries an action intent through the host boundary and
1804
+ associates the clipboard outcome with that initiating control. A valid-looking
1805
+ call in initialization, interpolation, a getter, a value-change handler, or a
1806
+ node-selection callback cannot confer the required authorization.
1807
+
1808
+ Do not defer copy through Promise.then, async/await, timers, or an automatic
1809
+ effect. Those are not documented action paths. Do not forge internal receipt or
1810
+ gesture fields. Copy authorization and browser/platform permission are separate:
1811
+ the platform can deny a correctly invoked request.
1812
+
1813
+ ```dil
1814
+ {@body const report = "Review complete: Alpha and Beta checked."}
1815
+ <button onClick={() => GenUI.copy(report)}><icon name="copy"/> Copy report</button>
1816
+ ```
1817
+
1818
+ Call once per action. The VM tracks one copy value for an execution; several copy
1819
+ calls do not establish an ordered batch of clipboard writes. Keep the requested
1820
+ payload below the limit and construct it before invocation with a pure binding.
1821
+ If copying a selected record, validate that selection exists in the handler.
1822
+
1823
+ The call returns undefined. It is not a Promise and does not return success or
1824
+ failure to authored source. Do not branch on its return value or set copied=true
1825
+ immediately after calling it. Button/pressable display Copied or Copy failed using
1826
+ the actual host outcome. CodeBlock's built-in copy control has its own feedback.
1827
+ Use that behavior rather than duplicate success flags or guessed timeouts.
1828
+
1829
+ Clipboard denial should leave the existing content and local draft available.
1830
+ Users can retry the same explicit action after resolving platform permissions.
1831
+ Do not clear the input merely because copy was requested. Do not promise a
1832
+ clipboard read-back or automatic retry; neither is an author API.
1833
+
1834
+ Unsupported host operations include openUrl, issueNewTurn, download, export,
1835
+ file creation, printing, network requests, storage, browser navigation, arbitrary
1836
+ postMessage, and server mutations. Mention an unavailable capability when it is
1837
+ essential to the requested task. Never replace it with an invented GenUI method.
1838
+ The renderer's own transport and widget-internal browser calls are not privileges
1839
+ granted to authored expressions.
1840
+
1841
+ #### Task: host-action-review
1842
+
1843
+ Check the control, event, payload type, payload length, and execution timing.
1844
+ A copy button with a string payload and synchronous onClick is supported. A
1845
+ slider onChange that calls copy, a const getter that calls copy while rendering,
1846
+ or an automatic initialization call is unsupported even if the text is harmless.
1847
+ Keep copy requests intentional and visible to the person activating the control.
1848
+
1849
+ ## 7. Assets
1850
+
1851
+ ### Icons
1852
+
1853
+ #### Component: icon
1854
+
1855
+ ```text
1856
+ Properties:
1857
+ name: string (required for generation)
1858
+ size: "xs" | "lg" (optional; default 17px)
1859
+ color: string (optional)
1860
+
1861
+ Children: none
1862
+ ```
1863
+
1864
+ Supported names:
1865
+
1866
+ ```text
1867
+ chart-no-axes-combined
1868
+ circle-check
1869
+ check-circle-2
1870
+ circle
1871
+ wallet
1872
+ trending-up
1873
+ users
1874
+ rotate-ccw
1875
+ copy
1876
+ sliders-horizontal
1877
+ ellipsis
1878
+ sparkles
1879
+ mouse-pointer-click
1880
+ x
1881
+ git-merge
1882
+ package-check
1883
+ shield-check
1884
+ settings-2
1885
+ info
1886
+ code-2
1887
+ alert-triangle
1888
+ ```
1889
+
1890
+ Use exactly these names. An unknown name falls back to circle, which can conceal
1891
+ a generation mistake rather than raise an error. Do not request an external
1892
+ Lucide name because the renderer internally uses Lucide. The allowlist is the
1893
+ author API. There is no arbitrary SVG path, icon URL, upload, sprite, or library
1894
+ import prop.
1895
+
1896
+ Size xs=13px, omitted=17px, lg=22px. There is no explicit md size enum; omit size
1897
+ for the normal icon. Color uses the same semantic token/CSS color reader as DIL
1898
+ text. It does not accept a CSS class, style object, or custom variable. Icons are
1899
+ inline decorative elements; put them beside meaningful text.
1900
+
1901
+ Icons are aria-hidden in the stock renderer. They cannot carry an authored
1902
+ aria-label or title prop. An icon-only button needs its own aria-label. A badge
1903
+ or status line needs visible words even when its icon seems recognizable. Do not
1904
+ communicate critical state using icon shape/color alone.
1905
+
1906
+ #### Composition: button > icon + inline text
1907
+
1908
+ ```dil
1909
+ <button onClick={() => GenUI.copy("Review complete")}>
1910
+ <icon name="copy" size="xs"/> Copy review
1911
+ </button>
1912
+ ```
1913
+
1914
+ #### Composition: button > icon with accessible action name
1915
+
1916
+ ```dil
1917
+ {@body const [count,setCount] = DIL.useState(0)}
1918
+ <button aria-label="Reset count" variant="ghost" onClick={() => setCount(0)}>
1919
+ <icon name="rotate-ccw"/>
1920
+ </button>
1921
+ <text>{count}</text>
1922
+ ```
1923
+
1924
+ Use the example catalog's icon gallery to locate all allowed names. Icons do not
1925
+ create host operations: a download-looking symbol would not add a download API,
1926
+ and the copy icon itself has no onClick. Put the supported action on the containing
1927
+ button or pressable and obey the host-action contract.