create-lykomi-app 0.11.1 → 0.12.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 (51) hide show
  1. package/README.md +8 -0
  2. package/package.json +1 -1
  3. package/presets/foundry-iq/files/AGENTS.md +6 -0
  4. package/presets/foundry-iq/files/README.md +20 -0
  5. package/presets/foundry-iq/files/src/main.tsx +2 -1
  6. package/presets/neo4j/files/AGENTS.md +6 -0
  7. package/presets/neo4j/files/README.md +20 -0
  8. package/presets/neo4j/files/src/main.tsx +2 -1
  9. package/template/AGENTS.md +6 -0
  10. package/template/README.md +21 -4
  11. package/template/_agents/skills/lykomi-author-chat-module/SKILL.md +57 -0
  12. package/template/_agents/skills/lykomi-author-chat-module/references/PROCESS_GUIDE.md +203 -0
  13. package/template/_agents/skills/lykomi-author-chat-module/references/form-ux.md +579 -0
  14. package/template/_agents/skills/lykomi-build-agent-forms/SKILL.md +36 -0
  15. package/template/_agents/skills/lykomi-build-agent-forms/references/PROCESS_GUIDE.md +203 -0
  16. package/template/_agents/skills/lykomi-build-agent-forms/references/form-ux.md +579 -0
  17. package/template/_agents/skills/lykomi-build-workbench/SKILL.md +37 -0
  18. package/template/_agents/skills/lykomi-build-workbench/references/PROCESS_GUIDE.md +203 -0
  19. package/template/_agents/skills/lykomi-build-workbench/references/form-ux.md +579 -0
  20. package/template/_agents/skills/lykomi-verify-agent-apis/SKILL.md +42 -0
  21. package/template/_agents/skills/lykomi-verify-agent-apis/references/PROCESS_GUIDE.md +203 -0
  22. package/template/_agents/skills/lykomi-verify-agent-apis/references/form-ux.md +579 -0
  23. package/template/_agents/skills/lykomi-verify-ui/SKILL.md +31 -0
  24. package/template/_claude/skills/lykomi-author-chat-module/SKILL.md +57 -0
  25. package/template/_claude/skills/lykomi-author-chat-module/references/PROCESS_GUIDE.md +203 -0
  26. package/template/_claude/skills/lykomi-author-chat-module/references/form-ux.md +579 -0
  27. package/template/_claude/skills/lykomi-build-agent-forms/SKILL.md +36 -0
  28. package/template/_claude/skills/lykomi-build-agent-forms/references/PROCESS_GUIDE.md +203 -0
  29. package/template/_claude/skills/lykomi-build-agent-forms/references/form-ux.md +579 -0
  30. package/template/_claude/skills/lykomi-build-workbench/SKILL.md +37 -0
  31. package/template/_claude/skills/lykomi-build-workbench/references/PROCESS_GUIDE.md +203 -0
  32. package/template/_claude/skills/lykomi-build-workbench/references/form-ux.md +579 -0
  33. package/template/_claude/skills/lykomi-verify-agent-apis/SKILL.md +42 -0
  34. package/template/_claude/skills/lykomi-verify-agent-apis/references/PROCESS_GUIDE.md +203 -0
  35. package/template/_claude/skills/lykomi-verify-agent-apis/references/form-ux.md +579 -0
  36. package/template/_claude/skills/lykomi-verify-ui/SKILL.md +31 -0
  37. package/template/package.json +4 -4
  38. package/template/src/main.tsx +2 -1
  39. package/template/src/pages/starter-prompts.tsx +9 -2
  40. package/template/src/showcase/context-inspector.tsx +110 -0
  41. package/template/src/showcase/dashboard-example.tsx +157 -0
  42. package/template/src/showcase/feedback-example.tsx +135 -0
  43. package/template/src/showcase/form-example.tsx +171 -0
  44. package/template/src/showcase/form-model.ts +38 -0
  45. package/template/src/showcase/module.tsx +47 -0
  46. package/template/src/showcase/process-example.tsx +118 -0
  47. package/template/src/showcase/showcase-page.tsx +212 -0
  48. package/template/src/showcase/source-panel.tsx +50 -0
  49. package/template/src/showcase/table-example.tsx +133 -0
  50. package/template/src/showcase/use-context-preview.ts +55 -0
  51. package/template/src/showcase/windows-example.tsx +106 -0
@@ -0,0 +1,579 @@
1
+ <!-- Generated from docs/FORM_UX.md; do not edit. -->
2
+
3
+ # Opinionated form and renderer UX
4
+
5
+ This is the default contract for developers and coding agents using LYKOMI UI.
6
+ Use it for native UI, generated forms, list-item inspectors and custom renderers.
7
+ Depart from the default only when the user explicitly requests the different interaction
8
+ or layout. A request for a domain-specific renderer does not request a different UX.
9
+
10
+ ## Semantic composition: decide what belongs where
11
+
12
+ Start from the person's task and the real schema, not a visual template or an
13
+ alphabetical dump of properties. Keep one semantic order in View, Edit and
14
+ Read-only. These placements are product defaults; preserve an established domain
15
+ workflow and depart only on explicit user instruction.
16
+
17
+ | Order | Content and purpose | Placement |
18
+ | ----- | --------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
19
+ | 1 | Identity and orientation: what item is this, what is this form for? | One title, optional one-sentence purpose, parent/context when needed. Reuse identity already supplied by the host. |
20
+ | 2 | Current situation and next step: state, outcome, blocker requiring action. | Compact status/summary near the title. Put an actionable blocker before affected fields; put field-specific errors at the field. Do not invent a status from completeness. |
21
+ | 3 | Core facts needed to recognise, assign or act on the record. | First visible group: the relevant identity/name field, type/category, responsible person and date/deadline. Include only facts meaningful to this task. |
22
+ | 4 | Main work: the decision or information the person is here to supply/review. | Clearly named sections, in task order. Put a prerequisite before its dependent field. Group related fields; put narrative explanations in full-width slots. |
23
+ | 5 | Evidence and related material supporting the decision. | Citations, sources, attachments and related records near the finding they substantiate, or in a dedicated Evidence section. Keep links usable in reading mode. |
24
+ | 6 | Secondary information that rarely changes the next action. | Additional details, collapsed only when safe. Technical identifiers, timestamps, audit/version information go last in metadata/support, never as primary editable fields. |
25
+ | 7 | Complete the current transaction. | One Save/Cancel group for Edit/Create, owned by the host. Primary action last; destructive actions live separately with explicit meaning. Reading states have task actions instead of a submit bar. |
26
+
27
+ Required is a validation property, not the only ordering rule. Keep essential
28
+ inputs visible early; do not collapse a missing required input or an unresolved
29
+ error. Do not sort all required fields ahead of their prerequisites or split a
30
+ logical group. Use short, task-specific section names such as “Ownership”,
31
+ “Assessment” or “Evidence”; “Details” alone is not a useful name for every group.
32
+ One page title is level 1, sections below it level 2; an embedded form headed at
33
+ level 2 uses level 3 sections. Adjust semantic heading level without inventing
34
+ another typography style.
35
+
36
+ Before generating UI, map each applicable field to a group and explain its role
37
+ (identity, status, core fact, decision, evidence or metadata). Reuse schema labels,
38
+ help, options, applicability and persisted order; do not fabricate backend fields.
39
+ An existing empty value keeps its slot so later values and Edit do not move.
40
+ Store grouping in the schema or shared composition rather than separate read/edit
41
+ layouts. Keep summaries concise and grounded in saved values; avoid repeating
42
+ all the fields above the form.
43
+
44
+ ## Reading, Read-only and temporarily disabled
45
+
46
+ | State | Values and interactions | Actions |
47
+ | ----------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
48
+ | View, with write access | Saved values as readable text, matching the edit field slots. Text selection, copying, links and context selection work. | Explicit Edit is available. No Save/Cancel yet. |
49
+ | Edit/Create | Controls replace values within the same sections and slots. Clearly explain required fields, validation and blockers. | One Save/Create and Cancel transaction. |
50
+ | Read-only record | Same information hierarchy and normal text contrast, with readable/copyable values. Explain the reason once near the header when available, such as “Published version” or “Read-only access”. | No Edit, inline edit triggers or Save/Cancel. Reading actions, links, export and permitted discussion remain available. |
51
+ | One protected field in an editable record | Keep its saved value readable in its normal slot; explain the field-specific lock/managed-source reason. | Other writable fields still work. No editor or Save for this field. |
52
+ | Temporarily disabled control | Retain the control while a prerequisite, save or load is pending; explain what enables it. | Prevent duplicate/invalid action; restore availability when the condition resolves. |
53
+
54
+ Use display values for a read-only record, not a page of grey disabled inputs.
55
+ Do not dim the whole form, hide meaningful values or show input-required/error
56
+ badges that the reader cannot act on. Keep an explicit empty state (“Not set”,
57
+ or the schema's display convention); zero and false remain real values.
58
+ Selection keeps the same leading context marker; it never grants write access.
59
+
60
+ Read-only is enforced by application permissions and save handlers, not by CSS.
61
+ When access is unknown, withhold writes until resolved. Field locks, edit rights
62
+ and review rights are distinct: check the actual permission for each action.
63
+ Historical snapshots must not mutate or review the live record. A permission
64
+ change must stop writes without silently losing a draft. Do not use blur to save.
65
+
66
+ ## Compose the shipped presets
67
+
68
+ Import `FormLayout`, `FormHeader`, `FormSection`, `FormActions`, `Field`, `FieldSet`,
69
+ `ReadOnlyField`, `Input`, `Textarea` and `Button` from `@lykomi/web-primitives`.
70
+ Use the existing themed `FormDialog`, `SurfaceCard` and `CollectionSurface` from
71
+ `@lykomi/web-ui` for the host surface. These presets compose with them;
72
+ do not put another card around every field or another form inside a form.
73
+
74
+ ```tsx
75
+ <FormLayout>
76
+ <FormHeader title="Contact details" description="How we can reach you." />
77
+ <FormSection title="Contact" description="Use your work contact details.">
78
+ <Field label="Email" htmlFor="contact-email" hint="We use this for updates.">
79
+ <Input id="contact-email" type="email" autoComplete="email" />
80
+ </Field>
81
+ </FormSection>
82
+ <FormActions className="justify-end">
83
+ <Button variant="outline" type="button">
84
+ Cancel
85
+ </Button>
86
+ <Button type="submit">Save</Button>
87
+ </FormActions>
88
+ </FormLayout>
89
+ ```
90
+
91
+ The application supplies its save handlers and one outer `<form>` if needed.
92
+ Preset layout components do not submit, fetch or store data.
93
+
94
+ ## Automatic field context
95
+
96
+ The selected field context uses a short leading marker beside its label. This marker
97
+ stays visible when moving into chat and is separate from input and keyboard focus.
98
+ Do not replace focus indicators with the context marker.
99
+
100
+ `Field` and `ReadOnlyField` participate in agent context automatically when used
101
+ with `@lykomi/web-ui`. Focus or click a field, then enter chat: the request carries
102
+ its label, current value, target path and text selection. Values are read from the
103
+ current control at request time. No page-specific focus handlers, context store
104
+ updates or context hook are needed. `Input` and `Textarea` remain leaf controls;
105
+ wrap them in `Field` to supply their label and field boundary.
106
+
107
+ Use `name` on the control for a semantic target path. The control ID is the
108
+ fallback. Hidden/inert fields, closed windows and inactive workbench windows
109
+ cannot remain the active subject. The mobile switch to Chat retains the current
110
+ field, while changing the route clears it. Password inputs are excluded. For
111
+ other sensitive values set `agentContext={false}` on `Field` or `ReadOnlyField`,
112
+ or `data-agent-context="off"` on a containing surface.
113
+
114
+ This gives the agent context, not write access. The application owns validation,
115
+ draft updates, saving and permissions through its registered tools. The starter's
116
+ `use-draft-tools.ts` demonstrates guarded, unsaved edits using `usePageTools`.
117
+
118
+ Applications with richer field metadata can register a `FieldContextSource`
119
+ through `registerFieldContextSource(id, { selector, getRefId, ignoreSelector })`.
120
+ Their source takes precedence over nested primitive fields, preventing duplicate
121
+ context. `getActiveFieldContext()` supplies the shared element, target and caret
122
+ snapshot to their domain projection. Registration returns a cleanup function.
123
+ The shared tracker owns focus and selection; entity identifiers, change tracking
124
+ and persistence remain in the application.
125
+
126
+ ## Interaction contract
127
+
128
+ - Open (including a list double-click or keyboard activation) starts in reading mode.
129
+ Edit is an explicit, discoverable action; Create starts in editing mode.
130
+ - Labels, field order, sections, width and value origins stay in the same slots when
131
+ switching modes. Keep the field layout mounted and replace only the value/control.
132
+ Preserve expanded sections and reading position. Empty values retain their slots.
133
+ - Prefer one column. Put a small pair on one row only when its meaning requires it.
134
+ Long text gets its own full-width section. Use container width rather than screen width
135
+ when forms appear in side panels.
136
+ - Inline edits have visible Edit, Save and Cancel controls, with keyboard access and
137
+ focus restoration. Escape cancels; a multiline Enter writes a newline. Do not save
138
+ long text on blur. Do not put links or other controls inside an edit button or label.
139
+ - The host owns identity, navigation and the transactional Save/Cancel controls.
140
+ Embedded renderers do not repeat the host's title or submit controls.
141
+ - Retain dirty input on failed save or conflict. Show an actionable error and let the
142
+ person retry. Explain a disabled Save; for long forms link errors to their fields.
143
+ - Associate labels with controls; use descriptive help and errors, not placeholders
144
+ as labels. Use an accessible Dialog for modal detail views (focus trap, Escape,
145
+ return focus), including mobile.
146
+
147
+ ## Themes and spacing
148
+
149
+ Use semantic tokens (`background`, `card`, `foreground`, `muted-foreground`, `border`,
150
+ `primary`, `ring`, `destructive`) and the existing theme. Never invent a color palette,
151
+ hard-code white/black surfaces, or create a second theme in a custom renderer.
152
+ Tailwind spacing utilities resolve through the host's `--spacing` token.
153
+ The default rhythm is 8 units between sections, 4 between fields, 2 between actions
154
+ and between a heading and help. At the standard 4px unit these are 32/16/8px.
155
+ Use `density="compact"` for constrained inspectors; keep controls comfortably usable.
156
+ Use the host radius tokens. Read/edit mode does not change density.
157
+ Test light, dark, narrow containers, long labels, empty values, keyboard and errors.
158
+
159
+ ## Evidence behind the default
160
+
161
+ Checked 2026-10-06:
162
+
163
+ - [Atlassian inline edit](https://atlassian.design/components/inline-edit/usage/)
164
+ supports matched read/edit alignment and explicit controls, with special care for
165
+ long text and blur behavior.
166
+ - [Carbon forms](https://www.carbondesignsystem.com/building-blocks/core/patterns/forms)
167
+ supports logical grouping, a single-column default, contextual spacing and useful
168
+ validation timing.
169
+ - [WAI labels](https://www.w3.org/WAI/tutorials/forms/labels/) and
170
+ [WAI validation](https://www.w3.org/WAI/tutorials/forms/validation/) ground naming
171
+ and understandable errors.
172
+ - [GOV.UK error summary](https://design-system.service.gov.uk/components/error-summary/)
173
+ describes focused summaries linking errors to fields.
174
+
175
+ These are a baseline for usability checks, not a claim that the presets alone guarantee
176
+ accessibility or usability. Browser evidence must exercise the application's real flows.
177
+
178
+ ## Release boundary
179
+
180
+ New presets must be released through Changesets before downstream applications import
181
+ them. A local sibling workspace can validate this source but cannot prove registry
182
+ availability. Downstream adapters may compose the already-published `FieldSet`, `Field`
183
+ and surface components until the preset release is available; then switch their internal
184
+ composition to these presets without changing the user-facing contract.
185
+
186
+ ## Selection and active context
187
+
188
+ Use one quiet leading marker for selected blocks, fields, tabs and item panels:
189
+ 2px wide, inset 6px vertically, theme primary at 55% opacity. The shared
190
+ `--selection-marker-width` and `--selection-marker-color` overrides may adapt it
191
+ to the theme. Selection changes no layout geometry and adds no colored outer
192
+ frame, fill or shadow. Workbench chrome uses a neutral theme border. Keyboard
193
+ focus, input validation, drop targets and pending-review frames keep their
194
+ respective semantics. Compact process navigation uses a subtle primary surface for its
195
+ inspected step; overview navigation retains a primary border and subtle fill.
196
+ These task-navigation controls are the explicit exception to the leading marker. Temporary windows remain identifiable through the unpinned
197
+ icon, accessible name and tooltip.
198
+
199
+ `ReadOnlyField label hint emptyLabel` renders a semantic label/value pair with
200
+ selectable text and usable links, preserving the `Field` spacing rhythm. Use it
201
+ for View/Read-only and keep the same `FormLayout`/`FormSection` composition.
202
+ `FormSection level={2}` pairs with a page-level `FormHeader level={1}`; its default
203
+ level 3 pairs with the default header level 2. The source preview opens in View
204
+ and lets you exercise Edit, Cancel/Save and simulated read-only access.
205
+
206
+ ## Required indicators and field frames
207
+
208
+ Use quiet inline “(Required)” text in mixed Create/Edit forms; no pill or badge
209
+ frame. Do not show input requirements in View or permanently read-only records.
210
+ `Field` derives the visible marker from its single control's `required` or
211
+ `aria-required` state; use `ReadOnlyField` for reading states.
212
+ Keep `required`/`aria-required` on the actual control and actionable validation.
213
+ A star requires an explanation at the form start; when almost all fields are
214
+ mandatory, marking the exceptions “(optional)” is a valid explicit product choice.
215
+ See [W3C H90](https://www.w3.org/WAI/WCAG22/Techniques/html/H90) and
216
+ [GOV.UK question pages](https://design-system.service.gov.uk/patterns/question-pages/).
217
+
218
+ Display values use the same neutral theme border, radius and padding as their
219
+ editor, without pretending to be disabled controls. Multi-line values and editors
220
+ share line height and a minimum height; editors grow with their content. Reserve
221
+ the reading height on the actual textarea, not just its wrapper. Verify that the
222
+ textarea has no hidden lines and that following sections do not jump. Double-click
223
+ on form text stays in View so text selection works; explicit Edit enters editing.
224
+
225
+ Field frames and editors use the same theme radius, including enum/select buttons and multiline controls. Keep single-line heights consistent; chips have their own pill treatment. Group secondary metadata in a compact footer with one separator. Helpers explain meaningful decisions in everyday language, not storage formats or schema internals. Suggestion popovers anchor to the input, match its width, fit the viewport and support keyboard selection/dismissal.
226
+
227
+ ## Keeping skill guidance current
228
+
229
+ Edit this document rather than reproducing the rules in plugin skills. Run `pnpm sync:form-ux` to regenerate their bundled `references/form-ux.md` resources. `pnpm check:form-ux` (part of `pnpm test` and CI) rejects stale resources. Skills read the bundled reference even when installed outside the framework checkout.
230
+
231
+ ## Fixed content versus editable values
232
+
233
+ A catalogue question, instruction or computed explanation is content. Render it
234
+ without input chrome: `ReadOnlyField valueAppearance="content"`. The default
235
+ `valueAppearance="field"` preserves a framed value slot for records whose fields
236
+ become editable through Edit. Choose the presentation from the meaning of the
237
+ value, not the current page mode, so fixed content stays content in View and Edit.
238
+ Content remains selectable and links stay usable; do not replace it with a
239
+ disabled input. Appearance does not grant or revoke edit permission. Keep
240
+ permission enforcement and pending-change review independent of presentation.
241
+
242
+ ## Lists and data tables
243
+
244
+ Use the shared `useDataTable` / `DataTableFrame` / `DataTableToolbar` /
245
+ `DataTable` / `DataTablePagination` family. Start with the person's task:
246
+ identify records, compare values, find exceptions or review a queue. Put the
247
+ identity column first and keep the fields needed for that task visible. Keep
248
+ metadata secondary; do not size columns alphabetically or from the longest
249
+ raw value. Long names must not push status and dates off-screen.
250
+
251
+ Define each column's `size`, `minSize` and `maxSize` in its `ColumnDef`.
252
+ Give identity/narrative columns more room than dates, states and numeric
253
+ values. The shared defaults are 80px minimum and 1200px maximum. The hook's
254
+ `defaultColumn` configures shared bounds; individual
255
+ columns override them. Mark selection and action columns
256
+ `enableResizing: false` and give them fixed bounds. Use `meta.align` for
257
+ numbers and short comparable values, with consistent formatting and units.
258
+ Do not apply a nested fixed `max-width` to text that should benefit from
259
+ resizing. Use truncation for long identity text, expose the full value on
260
+ hover, and make the item inspector the place to read/edit long content.
261
+
262
+ Users can resize a header's separate separator by dragging, use arrow keys
263
+ (Shift for larger steps), Home/End for bounds, and Enter or double-click to
264
+ reset. The resize target must not also sort. Honor `enableResizing` and
265
+ individual bounds. User widths are part of `DataTableState.columnSizing`;
266
+ URL state stores them under a namespaced `widths` key. Each list on a page
267
+ needs a stable unique URL prefix. User choices override author defaults;
268
+ reset widths separately from filters and sorting.
269
+
270
+ For an ordinary bounded list, pass `maxHeight` (a responsive value such as
271
+ `min(70vh, 42rem)`) and keep the header sticky. The table owns one row
272
+ viewport, while toolbar and paging controls stay outside it. Focus mode
273
+ uses `fill` to occupy the available panel height. Preserve horizontal
274
+ position while navigating rows vertically. Keep native wheel, trackpad,
275
+ touch and keyboard scrolling; never translate all vertical wheel events
276
+ into sideways scrolling. Bounded tables fit the current viewport and leave room for their scrollbar and
277
+ action row. Compose `DataTableFrame` with `DataTablePagination` so wrapped paging
278
+ controls contribute their actual height. `fitViewport={false}` opts out for a host that deliberately owns
279
+ that sizing. The native horizontal scrollbar and bottom arrow controls remain
280
+ reachable without first scrolling through every row.
281
+ Honor reduced motion. Keep essential information visible before overflow.
282
+ A frozen identity column must leave room for the scrolling columns. At narrow
283
+ widths the shared table releases a wide frozen prefix while keeping narrow
284
+ leading utility columns fixed when they fit; column widths remain unchanged.
285
+
286
+ For a dedicated long-list view, use `DataTable scrollMode="page"` and let its
287
+ containing page own the one vertical scroll. Pass `stickyHeaderOffset` for the
288
+ height of sticky navigation and filters above it. The shared table keeps its
289
+ compact horizontal arrows above the column labels when columns overflow;
290
+ both remain reachable while the rows scroll, and the arrows disappear when
291
+ all columns fit. The mirrored sticky labels are visual only; sorting and
292
+ selection controls remain in the original header at the top of the list.
293
+ Do not add a
294
+ second vertical row viewport or a second horizontal scrollport. Keep the
295
+ sticky stack compact: let the page hero scroll away, collapse optional facet
296
+ chips behind Filters on narrow panes, and retain search, the active filter
297
+ count, horizontal navigation and column labels. Show the task's useful
298
+ columns by default and let the horizontal scroller reach the rest. A width
299
+ breakpoint may hide genuinely secondary fields, but must not make horizontal
300
+ overflow disappear by silently hiding the columns a user came to compare.
301
+ For thousands of rows, use paging or the bounded, virtualized focus mode
302
+ instead of mounting the entire dataset in the page.
303
+
304
+ `spacer={false}` lets the first content column use spare width, up to its
305
+ maximum. A user-resized column keeps its chosen width. The default spacer
306
+ keeps declared column widths and leaves spare room at the end. Explicit
307
+ responsive column gates are separate from persisted visibility; shrinking
308
+ the panel must never overwrite the user's column choices.
309
+
310
+ Configure default sorting, filters, visibility and page size through
311
+ `initialState` for an uncontrolled table, or through the URL adapter's
312
+ `defaults` when controlled. Use real schema values and stable IDs. Choose
313
+ a default sort meaningful to the workflow (for example latest activity,
314
+ nearest deadline, or priority), and show the active sort/filter controls.
315
+ An explicitly cleared filter or sort must stay cleared after reload. Do
316
+ not silently reapply author defaults over the user's state.
317
+
318
+ Check long/empty/mixed values, pointer and keyboard resizing, width reset,
319
+ filter/sort defaults and overrides, both scroll axes, sticky/frozen columns,
320
+ and light/dark themes in wide and narrow containers. Tests and screenshots
321
+ verify behavior; claims of improved enjoyment require task-based user testing.
322
+
323
+ This contract draws on [NN/g's table task research](https://www.nngroup.com/articles/data-tables/),
324
+ [React Spectrum's column resizing and persistence](https://react-spectrum.adobe.com/v3/TableView.html),
325
+ [WAI's keyboard interaction patterns](https://www.w3.org/WAI/ARIA/apg/patterns/grid/),
326
+ and [TanStack's column sizing contract](https://tanstack.com/table/v8/docs/guide/column-sizing).
327
+ These are complementary references, not a single design-system benchmark.
328
+
329
+ Paged bounded tables reserve a stable viewport when standard pagination has multiple pages, so the next/previous controls do not jump on a short final page. `stableHeight` explicitly overrides this behavior. Page changes return to the top while preserving the horizontal position; resizing columns preserves the current reading position.
330
+
331
+ Use a single native horizontal scrollport per table. Bounded tables place their arrows below the row viewport; page-owned tables keep compact arrows in the sticky header stack. Do not duplicate the horizontal scrollbar with a synchronized second track.
332
+
333
+ ### Workspace navigation loading
334
+
335
+ Use the shared navigation skeleton for the first sidebar load and compact rail.
336
+ An individual branch can resolve to no visible children: show a small loading ring
337
+ inside the existing disclosure control, not as invented child rows. Keep the
338
+ control mounted, focusable and `aria-busy` while an unknown branch loads; retain
339
+ its normal toggle behavior. A focused heading has no disclosure button: use its
340
+ existing gutter for a named busy status with the same loading ring. A known empty branch stays empty during refresh.
341
+ Selected rows and their ancestors keep the same label size, weight and spacing as
342
+ unselected rows; use the shared selected background and foreground tokens.
343
+ Set `NavGroup.loading` or `NavItem.childrenLoading` from the existing read state;
344
+ do not manufacture a navigable or disabled "Loading..." item. When workspace
345
+ children are promoted into their own group, carry their loading state with them.
346
+ Skeletons use native geometry, semantic sidebar theme tokens and reduced-motion
347
+ preferences. Keep known rows visible during refresh. Loading presentation must
348
+ not schedule another read, add a reveal timer, or resize the content shell.
349
+ Workspace availability and breadcrumb arrival must not change pane header height
350
+ or chat-body padding. These are shared layout/theme geometry, not data states.
351
+
352
+ Sidebar scrolling follows navigation intent, not every tree update. Reveal the
353
+ active row once on a route change or when its first authorized row arrives,
354
+ using the nearest clipped edge. Pointer, wheel and keyboard interaction give the
355
+ user ownership of the reading position. Branch expansion, pagination, refresh
356
+ and status updates must not recenter the selected page or restart reveal timers.
357
+ Keep reveal work scoped to that sidebar's own scrollport; never scroll the page
358
+ or move keyboard focus as a side effect of loading.
359
+
360
+ Branch pagination uses a shared native button aligned with its child rows and
361
+ the sidebar theme. Keep the control mounted and focusable while busy, expose
362
+ its busy/disabled semantics, and guard repeat activation.
363
+ On the final page, retain the focused action as a quiet completion state until
364
+ focus leaves it; removing the focused button must not strand focus on the page.
365
+ The branch owner still owns the authorized cursor and request lifecycle;
366
+ presentation must not add automatic loading loops or another observer.
367
+
368
+ Chat homes keep their static heading and helper copy mounted. While the actual
369
+ context is unknown, show a theme skeleton rather than briefly painting generic
370
+ context. Use `ActionListSkeleton` for first-load starters, workflow actions and
371
+ recent chats, driven by their existing read state. Keep successfully loaded
372
+ suggestions and chats visible on refresh; settle empty and failed reads instead
373
+ of leaving a permanent skeleton. Do not disable the composer or trigger work.
374
+ The global home has no item context: do not mount contextual starter placeholders
375
+ above its create action. Reserve the action row itself, and put history skeletons
376
+ inside the existing Recent and Pinned cards. Keep those cards and their headings
377
+ stable through pending, populated and empty states, including narrow layouts.
378
+
379
+ ## Wizards and process guidance
380
+
381
+ Use `ProcessSteps` for a connected status rail and `ProcessGuide` for that rail
382
+ plus a focused guidance/action panel. This section is the maintained component
383
+ contract; the standalone process guide and skill resources are generated from it.
384
+ Use a wizard for sequential input and a process overview for resumable work;
385
+ independent tasks belong in a task list rather than a misleading sequence.
386
+
387
+ - Start with `variant="compact"` and expanded guidance for an active task.
388
+ Collapse only genuinely secondary help; do not hide the required next action. Use `framed` to make the rail and guidance read as
389
+ one component; omit a duplicate frame inside an existing panel. Vertical
390
+ rails center as a group while labels remain aligned to the start.
391
+ - Highlight the inspected compact step with a subtle primary fill and primary
392
+ label; other steps use a quiet neutral surface. Keep the guidance unfilled,
393
+ with a primary step label identifying its owner. The compact horizontal rail
394
+ sits above the labels: completed segments and outlined checks use primary,
395
+ unfinished segments stay neutral. Vertical connectors advance only between
396
+ two completed steps. Reserve semantic warning/error colors for those states;
397
+ inspection never changes status. Overview steps retain their selected border.
398
+ - Separate actual progress (`currentStepId`) from the inspected step
399
+ (`selectedStepId`). Allow explicit navigation to completed steps so users can
400
+ revise earlier inputs. Supply Back actions when useful. The host owns
401
+ validation, persistence, permissions and invalidation of dependent results.
402
+ - Author the panel's `content` and `actions` with standard forms and Buttons.
403
+ Use filled Buttons for the main forward transaction; outlined Buttons for
404
+ revising completed input and Back. The component must not invent actions,
405
+ choose their variants, or silently run jobs when a step is inspected.
406
+ - Announce actual current-step status changes even while an earlier step is
407
+ inspected; do not announce whole forms or every progress tick. Running work
408
+ needs honest task-specific progress rather than a percentage inferred from
409
+ the step count. Use a task list for work without a meaningful sequence.
410
+ - Use explicit complete/ready/running/blocked/error/skipped/pending statuses.
411
+ Compact locked steps use a short status; a `message` explains prerequisites
412
+ on hover and keyboard focus, in detailed view and unfolded guidance. Errors
413
+ remain visible. Do not communicate state through color alone.
414
+ - Preserve unsaved values while guidance is folded. Hosts persist drafts when
415
+ navigating between steps; folding is not saving. Use theme semantic tokens,
416
+ the accessible disclosure and reduced-motion behavior provided by the component.
417
+ - Native TypeScript verifies published installed-package exports. Stored JSX
418
+ separately verifies the deployed renderer scope. New framework APIs must be
419
+ published before downstream production consumers import them.
420
+
421
+ ### Shared process and chat context
422
+
423
+ Opt in with `ProcessGuide agentContext={{ id, label, sourcePage, active }}`. Use a stable process-instance ID scoped by its canonical source page; use a readable process label. `active` is required: the host must set it to `false` for inactive tabs, background panels and builder previews, or unmount the guide. A mounted guide without this prop remains presentation-only. With multiple active guides, pointer or keyboard interaction determines which process the With control highlights; all active process snapshots remain available to the agent.
424
+
425
+ The same committed host props supply the guide, the With control and the next AG-UI request. Context carries `currentStepId` (execution position), `selectedStepId` (inspected guidance) and each step's identity, status, optionality, selectability, description and blocker. Plain string description/message values are included automatically; for JSX, provide `contextText={{ description, message }}` with the equivalent plain-text guidance. React nodes, input values and callbacks are never scraped or serialized. Expose relevant form data through the normal field/context APIs.
426
+
427
+ `ui_process_get_context` reads current active processes and identifies the focused process. `ui_process_select_step` takes `processId`, `sourcePage` and `stepId`, validates the live registration and explicit selectability, then calls the same selection handler as a user click. Its `requested` result acknowledges dispatch, not a completed host transition: read live context afterward. It never executes, skips, approves, publishes or completes work. Those transactions remain host-owned, permission-checked tools. The existing `ui_get_context` includes process references too.
428
+
429
+ Context is refreshed on each user request and by the live read tools; it is not a continuous stream of browser changes into a response already being generated. After navigation or an action, refresh context before giving the next guidance. Sent-message context retains the original snapshot. Automatic live process context is separate from optional user pins and survives the user switching into chat, while inactive/unmounted guides are excluded.
430
+
431
+ ### Status symbols, selection and optionality
432
+
433
+ Use these exact `ProcessStepStatus` values; do not infer execution state from selection or optionality.
434
+
435
+ | `status` | Default label | Symbol | Meaning |
436
+ | ---------- | --------------- | --------------------- | --------------------------------------------------------------------------------------------------- |
437
+ | `pending` | Waiting | Outlined clock | Awaiting availability or scheduling; not ready yet. Use `blocked` for a known preventing condition. |
438
+ | `ready` | Ready | Open circle | Available to start, but not started. The inspected ready step adds a centered dot. |
439
+ | `running` | In progress | Spinner | Execution has actually started; honor reduced motion. |
440
+ | `complete` | Completed | Outlined check-circle | Work was completed and accepted by the host. |
441
+ | `blocked` | Blocked | Standalone lock | A prerequisite or permission prevents execution; explain it with `message`. |
442
+ | `error` | Needs attention | Outlined alert-circle | Failure or invalid result requires correction or retry; keep `message` visible. |
443
+ | `skipped` | Skipped | Outlined minus-circle | The host recorded an explicit skip decision; do not present it as completed work. |
444
+
445
+ `currentStepId` is the actual process position. `selectedStepId` is the step being inspected; its surface highlights which guidance belongs to it. The dot is an additional selection cue only for `ready`: inspecting a completed, running, blocked or skipped step preserves its status symbol. Without a valid selection, the component inspects the current step. Avoid an ambiguous `active` or `disabled` boolean for these distinct concepts.
446
+
447
+ `selectable` explicitly permits inspection when `onStepSelect` is provided. It never means executable: a host can allow inspection of a blocked step without permitting its action. Status and `optional` never authorize navigation by themselves.
448
+
449
+ Set `optional: true` independently of status. Unstarted optional steps use a dashed-circle marker in place of the pending clock or ready open circle. Running, completed, blocked, failed and skipped steps keep their status symbols. The default visible label is `Optional`; localize it using `optionalLabel`. It remains visible at compact density and is included in interactive accessible names. An optional step can still be blocked. Provide host-owned Skip/Back actions through the guidance `actions` slot: the host checks policy, persists the decision, sets `status: "skipped"`, and chooses the new `currentStepId`. There is no automatic skip, persistence or prerequisite resolution in this component. Completion accepts both `complete` and `skipped`, but neither selection nor optionality alone marks a process finished.
450
+
451
+ ### Shared component API for builders
452
+
453
+ This self-contained contract is available in the bundled skill reference;
454
+ no framework repository checkout is needed. Verify the installed/deployed API
455
+ before authoring because a host may expose an earlier version.
456
+
457
+ `ProcessSteps` accepts `steps`, `currentStepId`, `selectedStepId`,
458
+ `onStepSelect(id)`, `variant` (`compact` or `overview`, default `compact`),
459
+ `orientation` (`responsive`, `vertical` or `horizontal`, default `responsive`),
460
+ `density` (`comfortable` or `compact`, default `comfortable`), optional
461
+ `showChevrons` (boolean, default false) and an accessible
462
+ `label` (default `Process progress`). Each step has a stable unique string `id`,
463
+ string `label`, and explicit `status`: `pending`, `ready`, `running`, `complete`,
464
+ `blocked`, `error` or `skipped`. Optional fields are `statusLabel` (localized text),
465
+ `description`, `message`, `selectable`, `optional` and `optionalLabel`. Only explicitly selectable steps with
466
+ an `onStepSelect` callback navigate; callbacks never advance actual progress.
467
+ A non-selectable blocked step with a message exposes a focusable tooltip.
468
+
469
+ `showChevrons` adds decorative separators only to horizontal `compact` navigation.
470
+ Their size and reserved gap follow `density`; the progress track stays above the
471
+ steps. Responsive narrow or explicitly vertical layouts retain their connected vertical
472
+ track, and `overview` is unchanged. Chevrons convey sequence, never status.
473
+
474
+ Horizontal rails retain readable minimum step widths and scroll inside their own viewport when needed. Overflow alone reveals previous/next buttons and subtle edge fades; swiping, trackpad scrolling and keyboard navigation also work. Scrolling never selects a step or advances progress. Selecting or focusing a step brings it into the rail viewport without scrolling the host page. Manual scrolling is not snapped back to the selected step. Buttons respect reduced motion and RTL. Localize their names with `scrollLabels={{ previous: "Previous steps", next: "Next steps" }}`.
475
+
476
+ Keep default `orientation="responsive"` for a full wizard: it stacks vertically in narrow containers. Use `orientation="horizontal"` for an embedded progress strip that should remain horizontal even on mobile. Guidance and host content remain outside the scroll viewport. Do not hide steps, truncate essential labels or make the entire page scroll sideways.
477
+
478
+ Use standalone `ProcessSteps density="compact"` for progress-only display: one
479
+ marker and label per step, with accessible status text. There is no guidance
480
+ panel. `onStepSelect(id)` is entirely host-owned: open a document picker,
481
+ navigate to a page or trigger another permitted action; it need not inspect
482
+ inline guidance. Omit the callback for a passive progress indicator.
483
+
484
+ `ProcessGuide` accepts the same props and adds per-step `title` (text), plus `content` and
485
+ `actions` (React nodes), plus these guide-level props:
486
+
487
+ | Prop | Contract |
488
+ | -------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
489
+ | `framed` | Optional themed frame; default false. |
490
+ | `headingLevel` | 2, 3 or 4; default 3. |
491
+ | `guidance` | `expanded` (default) or `collapsible`. |
492
+ | `defaultGuidanceOpen` | Initial uncontrolled disclosure state; default false. |
493
+ | `guidanceOpen`, `onGuidanceOpenChange(open)` | Optional host-controlled disclosure state. |
494
+ | `guidanceToggleLabel` | Localized disclosure text; default `Guidance & actions`. |
495
+ | `guidanceTogglePlacement` | `guidance` (default) or `step`; compact collapsible guides can place the disclosure in the inspected step. Explicit step selection opens guidance, including reselection. Initial open state remains configurable. |
496
+ | `showPosition` | Optional numeric position in guidance; default false. The owning step label remains when the task title differs. |
497
+ | `formatPosition(index, total)` | Localized position text when `showPosition` is true; index is one-based. |
498
+ | `completion` | Content shown only when no step is inspected/current and every step is complete or skipped. |
499
+
500
+ The selected step falls back to the current step if its ID is absent/invalid.
501
+ Changing inspected steps remounts their content; hosts must retain drafts outside
502
+ that content. Folding guidance keeps the same inputs mounted. Empty arrays render
503
+ nothing. The component never fetches, saves, authorizes, validates or runs a job.
504
+
505
+ Example composition, after availability is verified:
506
+
507
+ ```jsx
508
+ <ProcessGuide
509
+ framed
510
+ variant="compact"
511
+ guidance="expanded"
512
+ currentStepId="review"
513
+ selectedStepId={inspectedStep}
514
+ onStepSelect={setInspectedStep}
515
+ steps={[
516
+ {
517
+ id: "documents",
518
+ label: "Documents",
519
+ status: "complete",
520
+ selectable: true,
521
+ title: "Select the source documents",
522
+ content: documentFields,
523
+ actions: (
524
+ <Button type="button" variant="outline" onClick={openDocumentEditor}>
525
+ Change documents
526
+ </Button>
527
+ ),
528
+ },
529
+ {
530
+ id: "review",
531
+ label: "Review",
532
+ status: "ready",
533
+ selectable: true,
534
+ description: "Check the findings before confirming.",
535
+ content: reviewFields,
536
+ actions: (
537
+ <Button type="button" onClick={validateAndSaveReview}>
538
+ Confirm review
539
+ </Button>
540
+ ),
541
+ },
542
+ ]}
543
+ />
544
+ ```
545
+
546
+ The host supplies the fields and handlers in this example, including saving
547
+ changes and invalidating results after source edits. In native TypeScript import
548
+ from the published package and its stylesheet. In stored JSX use only verified
549
+ scope symbols without imports.
550
+
551
+ ### Design references
552
+
553
+ - [USWDS step indicator](https://designsystem.digital.gov/components/step-indicator/):
554
+ distinguish current/completed/pending and keep explicit headings/navigation.
555
+ - [GOV.UK multiple tasks](https://design-system.service.gov.uk/patterns/complete-multiple-tasks/):
556
+ resumable work needs clear task statuses and prerequisites.
557
+ - [Carbon progress indicator](https://www.carbondesignsystem.com/building-blocks/core/components/progress-indicator/guidelines):
558
+ a stable sequence and validation are prerequisites for linear task guidance.
559
+ - [WAI disclosure pattern](https://www.w3.org/WAI/ARIA/apg/patterns/disclosure/):
560
+ keyboard operation and expanded state for unfolding guidance.
561
+
562
+ These inform the design; usability with real users still needs validation.
563
+
564
+ ### List toolbar control density
565
+
566
+ List toolbars use the shared `Button size="sm"` density for New, grouping,
567
+ Filters, Columns and other text actions. Authored `DataTableToolbar actions`
568
+ use the same primitive and size; do not mix the default-height button into that
569
+ row. Let the button primitive own font family, weight, size, control radius,
570
+ outline surface and interaction states. Table controls must not restyle ordinary
571
+ outline buttons with a different radius, translucent fill or shadow reset.
572
+ Retain meaningful states such as active filters, dashed facet boundaries and
573
+ pressed grouping. Icon-only toolbar controls share the same height and focus
574
+ behavior. Menus and badges retain their own semantic surface treatment.
575
+
576
+ Verify the rendered toolbar in light/dark themes and at narrow widths. Compare
577
+ computed height, font, weight and radius of authored actions with built-in
578
+ Filters/Columns; using the same component name alone does not establish parity.
579
+ Use CSS and shared primitive variants, without observers or per-button requests.
@@ -0,0 +1,36 @@
1
+ ---
2
+ name: lykomi-build-agent-forms
3
+ description: Build forms in a LYKOMI UI app with automatic field context, explicit editing, validation and guarded agent tools. Use when adding or changing forms or field-level agent interactions.
4
+ ---
5
+
6
+ Read [the shared form contract](references/form-ux.md) before changing the form.
7
+ Inspect the installed `@lykomi/web-primitives` and `@lykomi/web-ui` declarations;
8
+ use the project's pinned API rather than an unreleased example.
9
+
10
+ ## Compose the form
11
+
12
+ - Use `FormLayout`, `FormHeader`, `Field` and the shared inputs. Open existing
13
+ records in view mode; Edit replaces values with controls in the same slots.
14
+ - A `Field` around a named control, or `ReadOnlyField`, automatically supplies
15
+ the selected field's label, current value and text selection to AG-UI.
16
+ Do not add page-specific focus listeners or a second field-context store.
17
+ - Give controls semantic `name` values and associate labels with IDs. An
18
+ unwrapped `Input` is deliberately not an agent field (search and chat also
19
+ use inputs).
20
+ - Use `agentContext={false}` for sensitive fields. Passwords are excluded by
21
+ the built-in collector. Custom context projections must also omit secrets.
22
+
23
+ ## Keep updates app-owned
24
+
25
+ Register edit tools with `usePageTools` only while the form can be edited.
26
+ Validate arguments at execution time, restrict writable field names, and compare
27
+ the expected value with the current draft before replacing it. A stale tool call
28
+ must return a conflict instead of overwriting a newer user edit.
29
+
30
+ Agent changes should enter the same unsaved draft as manual edits. Save, Cancel,
31
+ permissions and persistence belong to the application. Preserve dirty input on
32
+ validation or network failure. Label in-memory demo saving honestly.
33
+
34
+ In a generated starter, inspect `src/showcase/form-example.tsx` and
35
+ `src/pages/use-draft-tools.ts` for working examples. Verify the outgoing AG-UI
36
+ request after selecting text and entering chat, including on a narrow viewport.