@cratis/pi 2.21.3 → 2.23.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 (102) hide show
  1. package/package/corpus/agents/backend-developer.md +6 -5
  2. package/package/corpus/agents/code-reviewer.md +7 -7
  3. package/package/corpus/agents/coordinator.md +5 -4
  4. package/package/corpus/agents/frontend-developer.md +34 -40
  5. package/package/corpus/agents/orchestrator.md +5 -4
  6. package/package/corpus/agents/performance-reviewer.md +5 -4
  7. package/package/corpus/agents/planner.md +5 -4
  8. package/package/corpus/agents/repository-investigation-reviewer.md +2 -1
  9. package/package/corpus/agents/repository-investigator.md +2 -1
  10. package/package/corpus/agents/security-reviewer.md +5 -4
  11. package/package/corpus/agents/slice-implementer.md +8 -2
  12. package/package/corpus/agents/spec-writer.md +6 -4
  13. package/package/corpus/harnesses/pi/extensions/subagent/agents.ts +52 -7
  14. package/package/corpus/harnesses/pi/extensions/subagent/index.ts +14 -0
  15. package/package/corpus/hooks/README.md +29 -27
  16. package/package/corpus/hooks/agent-stop.md +1 -5
  17. package/package/corpus/hooks/scripts/cratis-patterns.json +1 -1
  18. package/package/corpus/hooks/scripts/validate-package-subpaths.sh +1 -2
  19. package/package/corpus/prompts/verify-ai-setup.prompt.md +17 -7
  20. package/package/corpus/rules/ai-distribution.md +58 -0
  21. package/package/corpus/rules/components.md +77 -44
  22. package/package/corpus/rules/dialogs.md +24 -20
  23. package/package/corpus/rules/efcore.md +35 -187
  24. package/package/corpus/rules/framework.md +1 -1
  25. package/package/corpus/rules/frontend-quality.md +1 -1
  26. package/package/corpus/rules/frontend-testing.md +4 -4
  27. package/package/corpus/rules/general.md +29 -105
  28. package/package/corpus/rules/new-repository-intake.md +34 -0
  29. package/package/corpus/rules/orleans.md +13 -27
  30. package/package/corpus/rules/react.md +12 -12
  31. package/package/corpus/rules/reactors.md +28 -5
  32. package/package/corpus/rules/specs.scenarios.csharp.md +18 -3
  33. package/package/corpus/rules/specs.typescript.md +1 -1
  34. package/package/corpus/rules/storybook.md +2 -2
  35. package/package/corpus/rules/typescript.md +1 -0
  36. package/package/corpus/rules/vertical-slices.md +20 -15
  37. package/package/corpus/rules/writing-correct-examples.md +1 -0
  38. package/package/corpus/skills/cratis-application-react-specifications/SKILL.md +4 -4
  39. package/package/corpus/skills/cratis-application-slice-specifications/SKILL.md +16 -8
  40. package/package/corpus/skills/cratis-arc-authentication-authorization-and-identity/SKILL.md +6 -4
  41. package/package/corpus/skills/cratis-arc-authentication-authorization-and-identity/references/authentication.md +1 -1
  42. package/package/corpus/skills/cratis-arc-authentication-authorization-and-identity/references/authorization.md +1 -1
  43. package/package/corpus/skills/cratis-arc-authentication-authorization-and-identity/references/frontend.md +1 -1
  44. package/package/corpus/skills/cratis-arc-authentication-authorization-and-identity/references/local-development.md +1 -1
  45. package/package/corpus/skills/cratis-arc-authentication-authorization-and-identity/references/tenancy.md +7 -4
  46. package/package/corpus/skills/cratis-arc-command/SKILL.md +9 -7
  47. package/package/corpus/skills/cratis-arc-command/references/command-result.md +1 -1
  48. package/package/corpus/skills/cratis-arc-command/references/handler-shapes.md +2 -2
  49. package/package/corpus/skills/cratis-arc-command/references/proxy-generation.md +6 -3
  50. package/package/corpus/skills/cratis-arc-command/references/read-model-injection.md +1 -1
  51. package/package/corpus/skills/cratis-arc-command-execution/SKILL.md +12 -2
  52. package/package/corpus/skills/cratis-arc-command-validation/SKILL.md +5 -3
  53. package/package/corpus/skills/cratis-arc-ef-core-migration/SKILL.md +7 -4
  54. package/package/corpus/skills/cratis-arc-observable-query-http/SKILL.md +10 -3
  55. package/package/corpus/skills/cratis-arc-query-paging/SKILL.md +13 -8
  56. package/package/corpus/skills/cratis-arc-react-page/SKILL.md +27 -24
  57. package/package/corpus/skills/cratis-arc-react-page/references/data-page.md +15 -11
  58. package/package/corpus/skills/cratis-arc-react-page/references/data-tables.md +9 -2
  59. package/package/corpus/skills/cratis-arc-react-page/references/dialogs.md +11 -9
  60. package/package/corpus/skills/cratis-arc-react-page/references/queries-and-commands.md +26 -2
  61. package/package/corpus/skills/cratis-chronicle-cli-operations/SKILL.md +19 -13
  62. package/package/corpus/skills/cratis-chronicle-client-dotnet/SKILL.md +17 -15
  63. package/package/corpus/skills/cratis-chronicle-client-elixir/SKILL.md +24 -20
  64. package/package/corpus/skills/cratis-chronicle-client-kotlin/SKILL.md +26 -15
  65. package/package/corpus/skills/cratis-chronicle-client-typescript/SKILL.md +15 -11
  66. package/package/corpus/skills/cratis-chronicle-compliance/SKILL.md +14 -3
  67. package/package/corpus/skills/cratis-chronicle-event-constraints/SKILL.md +13 -6
  68. package/package/corpus/skills/cratis-chronicle-event-modeling/SKILL.md +2 -2
  69. package/package/corpus/skills/cratis-chronicle-event-specifications/SKILL.md +11 -5
  70. package/package/corpus/skills/cratis-chronicle-event-type-migration/SKILL.md +2 -2
  71. package/package/corpus/skills/cratis-chronicle-multi-tenancy/SKILL.md +2 -2
  72. package/package/corpus/skills/cratis-chronicle-projection/SKILL.md +23 -4
  73. package/package/corpus/skills/cratis-chronicle-projection/references/fluent-builder.md +1 -1
  74. package/package/corpus/skills/cratis-chronicle-projection/references/model-bound-attributes.md +2 -2
  75. package/package/corpus/skills/cratis-chronicle-reactor/SKILL.md +22 -8
  76. package/package/corpus/skills/cratis-chronicle-read-model/SKILL.md +1 -1
  77. package/package/corpus/skills/cratis-chronicle-read-model/references/queries.md +1 -1
  78. package/package/corpus/skills/cratis-chronicle-read-model-specifications/SKILL.md +3 -1
  79. package/package/corpus/skills/cratis-chronicle-reducer/SKILL.md +1 -1
  80. package/package/corpus/skills/cratis-chronicle-web-workbench/SKILL.md +19 -11
  81. package/package/corpus/skills/cratis-cli-terminal-workbench/SKILL.md +11 -6
  82. package/package/corpus/skills/cratis-code-review/SKILL.md +8 -6
  83. package/package/corpus/skills/cratis-components-accessibility/SKILL.md +112 -82
  84. package/package/corpus/skills/cratis-components-schema-editor/SKILL.md +12 -10
  85. package/package/corpus/skills/cratis-components-stepper-command-dialog/SKILL.md +11 -12
  86. package/package/corpus/skills/cratis-components-styling/SKILL.md +204 -170
  87. package/package/corpus/skills/cratis-components-toolbar/SKILL.md +32 -26
  88. package/package/corpus/skills/cratis-event-model-diagram/SKILL.md +1 -1
  89. package/package/corpus/skills/cratis-fundamentals-concept/SKILL.md +2 -2
  90. package/package/corpus/skills/cratis-fundamentals-type-discovery/SKILL.md +2 -2
  91. package/package/corpus/skills/cratis-lens-browser-extension/SKILL.md +4 -2
  92. package/package/corpus/skills/cratis-performance-review/SKILL.md +4 -2
  93. package/package/corpus/skills/cratis-screenplay-model-authoring/SKILL.md +15 -5
  94. package/package/corpus/skills/cratis-security-review/SKILL.md +20 -3
  95. package/package/corpus/skills/cratis-specifications-csharp/SKILL.md +3 -3
  96. package/package/corpus/skills/cratis-specifications-csharp/references/application-scenarios.md +12 -8
  97. package/package/corpus/skills/cratis-specifications-csharp/references/csharp-patterns.md +1 -1
  98. package/package/corpus/skills/cratis-specifications-csharp/references/integration-specs.md +7 -3
  99. package/package/corpus/skills/cratis-specifications-typescript/SKILL.md +6 -3
  100. package/package/corpus/skills/cratis-specifications-typescript/references/typescript-patterns.md +21 -4
  101. package/package/corpus/skills/cratis-stage-rendering-and-sandbox/SKILL.md +29 -17
  102. package/package.json +1 -1
@@ -1,19 +1,29 @@
1
1
  ---
2
2
  agent: agent
3
- description: Validate AI framework setup integrity, canonical source conventions, and symlink health.
3
+ description: Check the installed Cratis AI corpus for drift, conflicts, and healthy harness adapters.
4
4
  ---
5
5
 
6
6
  # Verify AI Setup
7
7
 
8
- Validate the repository AI setup by running:
8
+ Check the repository's installed Cratis AI setup by running:
9
9
 
10
10
  ```bash
11
- bash .cratis/ai/hooks/scripts/validate-ai-setup.sh
11
+ cratis ai status
12
12
  ```
13
13
 
14
- If anything fails:
14
+ It reports the configured harnesses, profiles and languages, the installed corpus revision against the
15
+ available one (`updateAvailable`), and every managed file that was modified locally — exiting non-zero
16
+ when there is any. (A user-owned path that collides with a managed one is reported and refused by
17
+ `cratis ai install` / `cratis ai update`, not by `status`.) Then confirm
18
+ the harness adapters this repository uses (`.claude/`, `.agents/`, `.github/`, `.pi/`, `.cursor/`,
19
+ `.opencode/` as applicable) still resolve into `.cratis/ai/` — a broken or dangling symlink is a setup
20
+ fault, not corpus drift.
15
21
 
16
- 1. List every failure with the exact file path.
17
- 2. Explain whether the issue is canonical-source drift, missing metadata, or broken links.
18
- 3. Propose the smallest safe fix.
22
+ If anything is reported:
23
+
24
+ 1. List every finding with the exact file path.
25
+ 2. Explain whether it is a hand-edited managed file, a pending update, or a broken adapter.
26
+ 3. Propose the smallest safe fix. A managed file is never patched by hand — `cratis ai update`
27
+ replaces it (with `--force` only for content already recorded as Cratis-managed); a user-owned
28
+ file is the repository's and stays.
19
29
  4. Apply fixes if requested.
@@ -0,0 +1,58 @@
1
+ ---
2
+ applyTo: "**/*"
3
+ ---
4
+
5
+ # Shared AI Distribution
6
+
7
+ How the shared Cratis AI corpus reaches a repository, and what a consuming
8
+ repository must and must not do with it. The always-loaded instructions keep only
9
+ the two invariants; this rule carries the mechanics.
10
+
11
+ Do not copy or synchronize shared `.cratis/ai`, `.agents`, `.claude`, `.github`, or
12
+ `.pi` trees from one Cratis repository to another. A consuming repository must
13
+ never become an accidental source that republishes its local AI corpus.
14
+
15
+ Shared Cratis capabilities are authored and reviewed in `Cratis/AI` and reach a
16
+ repository through one of three channels, each with its own version semantics:
17
+
18
+ - **`cratis ai install` / `cratis ai update`** copy the corpus into `.cratis/ai/`
19
+ from the current `main` of `Cratis/AI` (or an explicit `--source` path), record
20
+ the source commit and every installed file's hash in `.cratis/ai.manifest.json`,
21
+ and create native harness adapters as symlinks into that managed copy. There is
22
+ no version pin on this channel: an update takes whatever `main` holds. Agents,
23
+ prompts, and hooks are always installed; rules are filtered by profile and
24
+ language; skills by the profile catalog; `harnesses/<harness>/` only for the
25
+ harnesses selected.
26
+ - **`@cratis/pi`** is the one versioned channel: an npm package cut per release,
27
+ pinned and rolled back by package version. It yields to a managed install when
28
+ `.cratis/ai.manifest.json` exists.
29
+ - **Native plugin marketplaces** (Claude Code, Codex, Copilot, Cursor) expose the
30
+ skills only, from the unpinned GitHub source.
31
+
32
+ Keep existing repository-local AI files in place while a replacement corpus is
33
+ under canary; do not restart legacy all-to-all propagation and do not delete
34
+ legacy adapters before reviewed retirement evidence exists.
35
+
36
+ Shared public product and `engineering-*` packages contain only public-safe
37
+ Cratis behavior. The `engineering-` prefix identifies the maintainer audience;
38
+ it does not imply confidential package contents or a private registry.
39
+
40
+ The consuming repository owns its project facts, confidential behavior, local
41
+ skills, and minimal host bootstraps. Use repository-owned documentation as canonical
42
+ project context when that migration is active, `.agents/skills/` for private or
43
+ repository-specific local workflows, and repository-owned documentation only as the
44
+ documented legacy context fallback. Never merge, overwrite, or remove these
45
+ local files as a side effect of installing, updating, rolling back, or
46
+ uninstalling shared AI capabilities.
47
+
48
+ Keep confidential and repository-specific behavior local. Generalize and remove
49
+ private facts before proposing a reusable improvement to `Cratis/AI`; never
50
+ reverse-sync a private repository's AI tree or generated adapters.
51
+
52
+ Update shared AI through the channel's own mechanism (`cratis ai update`, or the
53
+ `@cratis/pi` package version). Canary the change, observe its behavior and gates,
54
+ and roll back the same way — by package version where one exists, otherwise by
55
+ reinstalling from a known-good source commit with `--source`. Never patch managed
56
+ files under `.cratis/ai/`, generated adapters, or marketplace wrappers by hand;
57
+ `cratis ai status` reports such drift and `update` refuses it without `--force`.
58
+
@@ -7,54 +7,66 @@ profile: application
7
7
 
8
8
  # Building React Components
9
9
 
10
- ## Cratis Components — pick the wrapper, import from subpaths
10
+ The frontend is composed from **Cratis Components** (`@cratis/components` **4.x**). Components owns its markup, styling contract and public types; React Aria supplies focus, overlay, collection and date behavior *internally* — you never import or style it. Components declares **no** PrimeReact, PrimeIcons or PrimeUI dependency or peer, and it ships no vendor CSS. PrimeReact (11 and 10) and MUI exist only as optional *presentation adapters* (`@cratis/components.primereact`, `.primereact10`, `.mui`) that adapt nine primitive slots — button, icon-button, text input, text area, checkbox, radio, switch, progress bar, surface — and nothing else. Dialogs, dropdowns, tables, date pickers, toasts and steppers are always Components-owned.
11
11
 
12
- Reach PrimeReact almost exclusively through Cratis Components wrappers. Import from **subpaths**, not the root barrel (the barrel pulls optional-peer-heavy exports):
12
+ ## Import from subpaths — the root is setup-only
13
+
14
+ The package root exports **only** setup (`CratisComponentsProvider`, configuration and message types). Every component ships from its own subpath; the Components 3 root namespaces are gone, and `@cratis/eslint-plugin-components`' `no-root-barrel-import` keeps them gone.
13
15
 
14
16
  | Need | Use | Subpath |
15
17
  |---|---|---|
16
- | Page chrome | `Page` | `@cratis/components/Common` |
17
- | Query list page | `DataPage` | `@cratis/components/DataPage` |
18
- | Standalone query table | `DataTableForQuery` / `DataTableForObservableQuery` | `@cratis/components/DataTables` |
19
- | Form section | `FormElement` | `@cratis/components/Common` |
20
- | Icon | `Icon` / `IconDisplay` | `@cratis/components/Common` |
18
+ | App root | `CratisComponentsProvider` | `@cratis/components` |
19
+ | Page chrome, buttons, basic controls | `Page`, `Button`, `IconButton`, `TextInput`, `TextArea`, `NumberInput`, `Checkbox`, `Radio`, `Switch`, `Tooltip`, `ErrorBoundary`, `FormElement`, `ComboBox` (≥ 4.6.0 — single-selection, text-searching entity picker) | `@cratis/components/Common` |
20
+ | Icon | `Icon` (type) / `IconDisplay` | `@cratis/components/Common` |
21
+ | Query list page | `DataPage`, `MenuItem`, `Column` | `@cratis/components/DataPage` |
22
+ | Standalone query table | `DataTableForQuery` / `DataTableForObservableQuery`, `Column` | `@cratis/components/DataTables` |
21
23
  | Dropdown | `Dropdown` | `@cratis/components/Dropdown` |
22
- | Command dialog | `CommandDialog` / `StepperCommandDialog` | `@cratis/components/CommandDialog` |
23
- | Data/confirmation dialog | `Dialog` / `ConfirmationDialog` / `BusyIndicatorDialog` | `@cratis/components/Dialogs` |
24
- | Command form fields | `InputTextField`, `PasswordField`, `ToggleSwitchField`, `RatingField`, … | `@cratis/components/CommandForm` |
25
- | Notifications (toasts) | `Toaster` / `toast` / `toastCommandResult` | `@cratis/components/Notifications` |
26
- | Status & display | `Tag` / `Badge` / `Chip` / `Skeleton` / `Avatar` / `ProgressBar` | `@cratis/components/Display` |
27
- | Canvas tool palette | `Toolbar` | `@cratis/components/Toolbar` |
24
+ | Command dialog | `CommandDialog`, `StepperCommandDialog`, `StepperPanel` | `@cratis/components/CommandDialog` |
25
+ | Standalone stepper | `CommandStepper` | `@cratis/components/CommandStepper` |
26
+ | Data / confirmation / busy dialog | `Dialog`, `ConfirmationDialog`, `BusyIndicatorDialog` | `@cratis/components/Dialogs` |
27
+ | Command form fields | `InputTextField`, `NumberField`, `DropdownField`, `CheckboxField`, `CalendarField`, …, `AutoCommandForm` | `@cratis/components/CommandForm` |
28
+ | Notifications | `Toaster`, `toast`, `toastCommandResult` | `@cratis/components/Notifications` |
29
+ | Status & display | `Tag`, `Badge`, `Chip`, `Skeleton`, `Avatar`, `ProgressBar`, `ProgressSpinner`, `Message` | `@cratis/components/Display` |
30
+ | Canvas tool palette | `Toolbar`, `ToolbarButton`, … | `@cratis/components/Toolbar` |
31
+ | Shared types | `JsonSchema`, `Json`, … | `@cratis/components/types` |
28
32
 
29
- Use `Dropdown` from `@cratis/components/Dropdown` (not raw `primereact/dropdown`) — it appends to the document body and stacks correctly above overlays, avoiding the z-index issues raw PrimeReact dropdowns have inside dialogs.
33
+ Use `Dropdown` from `@cratis/components/Dropdown`, never a vendor select: it renders through the Components overlay environment, so it stacks correctly above dialogs, and it follows the WAI-ARIA button/listbox (or combobox, when `filter` is on) pattern. Its `onChange` receives the **value**, not an event — as do every CommandForm field's.
30
34
 
31
35
  ### Notifications — feedback for commands run outside a dialog
32
36
 
33
37
  `CommandDialog` handles success/error feedback itself. For a command executed
34
- **programmatically** (`command.execute()` outside a dialog), mount one
35
- `<Toaster />` near the app root and surface the result with `toastCommandResult`
36
- (both from `@cratis/components/Notifications`) — it maps the granular
37
- `ICommandResult` flags to the right toast (success, not-authorized, validation
38
- with per-field messages, exceptions — never stack traces):
38
+ **programmatically** (`command.execute()` outside a dialog), mount the toast region once —
39
+ `toaster` on `CratisComponentsProvider`, or one `<Toaster />` near the root — and surface the result
40
+ with `toastCommandResult` (`@cratis/components/Notifications`). It maps the granular
41
+ `ICommandResult` flags to the right toast (success, not-authorized, validation with per-field
42
+ messages, exceptions — never stack traces):
39
43
 
40
44
  ```tsx
41
45
  const result = await command.execute();
42
46
  if (toastCommandResult(result, { successTitle: 'Author registered' })) refresh();
43
47
  ```
44
48
 
45
- For ad-hoc notifications, call the imperative `toast.success/info/warn/error(...)`
46
- — each takes an **options object**, not a bare string: `toast.info({ title: 'Saved', description: '…' })`.
49
+ For ad-hoc notifications, call the imperative `toast.success/info/warn/error(...)` — each takes an
50
+ **options object**, not a bare string: `toast.info({ title: 'Saved', description: '…' })`.
47
51
 
48
52
  ### Column filtering & display components
49
53
 
50
- `<Column>` supports `filter` (a per-column filter menu with match modes) and
51
- `DataPage` / the data tables show a global search box when `globalFilterFields`
52
- is set. Use the `Display` components (`Tag`, `Badge`, `Skeleton`, …) for status
53
- indicators and loading states in tables and detail views.
54
+ `<Column>` supports `sortable` and `filter` (a per-column filter popup with match modes), and
55
+ `DataPage` / the data tables show a search box when `globalFilterFields` is set. **Sorting and
56
+ filtering apply to the currently loaded page**; complete-result filtering belongs in the query's
57
+ arguments and the server-side query, before paging (`clientFiltering` is a deprecated no-op). Use the
58
+ `Display` components (`Tag`, `Badge`, `Skeleton`, …) for status indicators and loading states.
54
59
 
55
60
  ### `DataPage` — query list pages
56
61
 
57
- `DataPage` (from `@cratis/components/DataPage`) owns the data table's subscription, paging, selection, action menubar, and details split — **do not pre-fetch rows and pass an `items` array**. Required props: `title`, `query` (`Constructor<TQuery>`; snapshot and observable queries are auto-detected), `emptyMessage`, and `children`. Other props: `queryArguments`, `dataKey` (pass whenever the read model has an identity), `selection` / `onSelectionChange`, `globalFilterFields` / `defaultFilters` / `clientFiltering`, `detailsComponent` (`React.FC<IDetailsComponentProps<T>>` = `{ item, onRefresh? }`), `onRefresh`, and PrimeReact pass-through `tablePt`/`tableClassName`/`menubarPt`/`menubarClassName`.
62
+ `DataPage` (from `@cratis/components/DataPage`) owns the data table's subscription, paging,
63
+ selection, action menubar, and details split — **do not pre-fetch rows and pass an `items` array**.
64
+ Required props: `title`, `query` (`Constructor<TQuery>`; snapshot and observable queries are
65
+ auto-detected), `emptyMessage`, and `children`. Other props: `queryArguments`, `dataKey` (pass
66
+ whenever the read model has an identity), `selection` / `onSelectionChange`, `globalFilterFields` /
67
+ `defaultFilters`, `detailsComponent` (`React.FC<IDetailsComponentProps<T>>` = `{ item, onRefresh? }`),
68
+ `onRefresh`, and the Cratis part props `tablePt: DataTableParts` / `paginatorPt: TablePaginatorParts`
69
+ / `menubarPt: ButtonParts` (plus `tableClassName` / `menubarClassName`).
58
70
 
59
71
  Columns and toolbar actions are compositional children:
60
72
 
@@ -71,7 +83,10 @@ import { DataPage, MenuItem, Column } from '@cratis/components/DataPage';
71
83
  </DataPage>
72
84
  ```
73
85
 
74
- `MenuItem` is a PrimeReact menu item (use `command`, not `onClick`); `disableOnUnselected` greys it out until a row is selected. See the **cratis-react-page** skill for the full page workflow.
86
+ `MenuItem` is a declarative Components element read by `DataPage.MenuItems` (use `command`, not
87
+ `onClick`); `disableOnUnselected` greys it out until a row is selected; its `icon` is a **React
88
+ component type** (e.g. a `react-icons` icon), not an `Icon`. See the **cratis-arc-react-page** skill
89
+ for the full page workflow.
75
90
 
76
91
  ## Composition over Monoliths
77
92
 
@@ -100,33 +115,48 @@ Add an `index.ts` that re-exports the public surface so import paths stay stable
100
115
 
101
116
  ## Styling
102
117
 
103
- Consistent styling comes from discipline: static styles in CSS files, dynamic values inline, and colors always from PrimeReact's design tokens. This ensures theming works automatically and no component breaks the visual language.
118
+ Consistent styling comes from discipline: static styles in CSS files, dynamic values inline, and colors always from the `--cratis-*` design tokens. This ensures theming works automatically and no component breaks the visual language.
104
119
 
105
120
  ### App setup — the imports Cratis Components needs
106
121
 
107
- From `@cratis/components` **3.0.0** (PrimeReact 11), an app must do two things or the components render unstyled and PrimeReact may end up with two React contexts:
122
+ An app does three things, once, at its root — or the components render unstyled:
108
123
 
109
- 1. **Install PrimeReact yourself** — it is a **peer dependency**, not bundled: `primereact@^11`, `@primereact/core@^11`, `@primereact/headless@^11`, `primeicons@^8`. Two copies of PrimeReact means two `PrimeReactProvider` contexts, which breaks overlays and `pt` silently. Delete any `resolutions`/`overrides` pin that used to work around this.
110
- 2. **Import the stylesheets explicitly**, in this order — components no longer import their own CSS:
124
+ 1. **Import the stylesheets, in this order** — components do not import their own CSS:
111
125
 
112
126
  ```ts
113
- import '@cratis/components/tokens'; // the --cratis-* layer every component reads
114
- import '@cratis/components/styles'; // every component stylesheet, in one file
115
- import '@cratis/components/theme'; // optional: the license-free baseline look
127
+ import '@cratis/components/tokens'; // the semantic --cratis-* variables every component reads
128
+ import '@cratis/components/styles'; // structural rules for every component, in low-priority Cratis layers
129
+ import '@cratis/components/theme'; // optional: the baseline light/dark look (auto, or `cratis-dark`/`cratis-light`)
116
130
  ```
117
131
 
118
- PrimeReact 11 ships **zero CSS** — there is no `primereact/resources/themes/*.css`. A styled look comes from a `@primeuix/themes` preset passed through the provider (`value={{ theme: { preset: Aura } }}`, license-gated), from `@cratis/components/theme` (no license), or from your own `pt`/CSS.
132
+ A product with its own design system omits `theme`, defines the `--cratis-*` variables itself, and imports those values *after* the two Components entries so they win the cascade.
133
+
134
+ 2. **Mount `CratisComponentsProvider`** (from the package root) around the app. Its `value` carries `locale` (BCP 47 — drives dates, numbers, keyboard behavior and announcements) and `messages` (the Components-owned labels for `paginator`, `datePicker`, `dropdown`, `dialog`, `stepper`, `notifications`, `dataTable`, `columnFilter`, `toolbar`); `toaster` mounts the toast region. Renderer keys from Components 3 — `license`, `theme`, `pt`, `ripple`, `unstyled`, z-index — are a **type error**, on purpose. Keep the `value` object stable when it carries a large message catalog.
119
135
 
120
- ### Writing styles inside the library
136
+ ```tsx
137
+ import { CratisComponentsProvider } from '@cratis/components';
138
+
139
+ export const App = () => (
140
+ <CratisComponentsProvider value={{ locale: 'en-US' }} toaster>
141
+ <Application />
142
+ </CratisComponentsProvider>
143
+ );
144
+ ```
121
145
 
122
- - Use **CSS classes in co-located `.css` files** for static styles, and add an `@import` for each new stylesheet to `Source/styles.css` — the build fails if a component stylesheet is not reachable from that manifest. **Never** add `import './Foo.css'` to a `.tsx`: a CSS file in the JavaScript module graph is what made the published package unloadable in Node (Cratis/Components#118).
123
- - Each component must have its own CSS file — never add sub-component styles to the parent's CSS. This keeps styles co-located with the component they belong to.
146
+ 3. **Install nothing else.** No PrimeReact, no PrimeIcons, no theme preset, no vendor provider. Only if the product wants a vendor's *look* on the nine primitive slots does it install one adapter package and pass its manifest to the provider's `library` prop — and the vendor's own provider, theme and license then stay the application's, outside Components.
147
+
148
+ ### Writing styles
149
+
150
+ - Use **CSS classes in co-located `.css` files** for static styles; each component has its own CSS file — never add sub-component styles to the parent's CSS.
124
151
  - The composition root's CSS only contains layout/grid rules for positioning children — it should not style the children themselves.
125
152
  - Use inline `style` props **only** for runtime-dynamic values (pixel positions, computed sizes).
126
- - Use **PrimeReact CSS variables** for all colors, backgrounds, borders. This ensures the application respects theming and dark/light mode switches:
127
- - `var(--surface-0)` through `var(--surface-900)`, `var(--surface-card)`, `var(--surface-border)`, `var(--surface-ground)`
128
- - `var(--text-color)`, `var(--text-color-secondary)`, `var(--primary-color)`, `var(--primary-color-text)`, `var(--highlight-bg)`
129
- - Never hard-code hex or `rgb()` for UI chrome — it will break when themes change. Only hard-code colors that are intentionally theme-independent (e.g. brand-specific accent dots, traffic-light indicators).
153
+ - Use the **`--cratis-*` tokens** for every color, background, border, shadow and radius, so theming and dark mode work automatically:
154
+ - surfaces — `var(--cratis-surface-0)`, `var(--cratis-surface-100)`, `var(--cratis-surface-card)`, `var(--cratis-surface-ground)`, `var(--cratis-surface-section)`, `var(--cratis-surface-overlay)`, `var(--cratis-surface-hover)`, `var(--cratis-surface-border)`
155
+ - text & accent — `var(--cratis-text-color)`, `var(--cratis-text-color-secondary)`, `var(--cratis-primary-color)`, `var(--cratis-primary-color-text)`, `var(--cratis-highlight-bg)`, `var(--cratis-highlight-text-color)`, `var(--cratis-focus-ring)`
156
+ - status — `var(--cratis-success-background)`/`-text`, `var(--cratis-warning-*)`, `var(--cratis-danger-*)`, `var(--cratis-info-*)`
157
+ - shape — `var(--cratis-border-radius)`, `var(--cratis-control-height)`, `var(--cratis-shadow-subtle)`/`-overlay`/`-dialog`
158
+ - Never hard-code hex or `rgb()` for UI chrome. Only hard-code colors that are intentionally theme-independent (brand accent dots, traffic-light indicators).
159
+ - **Reach inside a component through its parts, not its DOM.** Every meaningful element carries `data-cratis-part="<name>"` and state attributes (`data-selected`, `data-invalid`, `data-disabled`, `data-active`, `data-position`). Style them with those selectors, or pass per-part attributes through the component's `pt` prop typed by its `*Parts` type (`DialogParts`, `StepperParts`, `DataTableParts`, `TablePaginatorParts`, `ButtonParts`, the `Toolbar*Parts` family). Never target React Aria class names or the internal element structure — they are not a contract.
130
160
  - Name CSS classes with a BEM-like prefix matching the component name.
131
161
 
132
162
  ## Props
@@ -136,16 +166,19 @@ Props are a component's public API. They should be clear, minimal, and well-docu
136
166
  - Each sub-component declares its own `*Props` interface with JSDoc on every prop.
137
167
  - Pass only needed props — avoid threading large prop bags through component trees.
138
168
  - Event handlers follow `on*` naming: `onPointerDown`, `onSelect`.
169
+ - Model visual variation the way `Button` does — one typed `variant` (`solid | outline | ghost | link`), one `tone` (`neutral | accent | positive | caution | critical`), one `shape` — not a pile of boolean flags.
139
170
 
140
171
  ## Dialogs
141
172
 
142
173
  See [dialogs.md](./dialogs.md) for the full dialog guide.
143
174
 
144
- **Summary:** Never import `Dialog` from `primereact/dialog`. Use `CommandDialog` from `@cratis/components/CommandDialog` for command-executing dialogs and `Dialog` from `@cratis/components/Dialogs` for data-collection dialogs. Do not render manual `<Button>` components for dialog actions — the dialog components handle footers.
175
+ **Summary:** Use `CommandDialog` from `@cratis/components/CommandDialog` for command-executing dialogs and `Dialog` from `@cratis/components/Dialogs` for data-collection dialogs; confirmations and busy indicators are raised through hooks, never built. Never import a vendor dialog. Do not render manual `<Button>` components for dialog actions — the dialog components handle footers.
145
176
 
146
177
  ## Icons
147
178
 
148
- Follow these rules when working with SVG icons:
179
+ Components accepts an **`Icon`** (`@cratis/components/Common`) wherever a control takes an icon: either a React node — preferred — or a complete, consumer-owned icon-font class string. Components installs **no icon font**: a string is rendered on an `<i>` unchanged, so the product must load that stylesheet itself. `react-icons` is a Components dependency and the natural source of React icon nodes (`<FaHouse aria-hidden="true" />`). Not every `icon` prop has that shape — `DataPage.MenuItem.icon` is a React component *type* because the page instantiates it; follow the exported prop type.
180
+
181
+ For the application's own SVG icons:
149
182
 
150
183
  - **Distinguish icons from status/interactive components.** A pure SVG icon is a simple presentational element. A component that wraps an icon with interactive behavior (e.g. a dropdown, tooltip, or complex state) is a _component_, not an icon — name it accordingly (e.g. `SliceStatus`, not `SliceStatusIcon`).
151
184
  - **Store each SVG as a separate `.svg` file** inside the icon's folder. Do not embed SVG markup directly in `.tsx` files.
@@ -7,14 +7,14 @@ paths:
7
7
 
8
8
  # Using Dialogs
9
9
 
10
- The Cratis dialog wrappers handle command execution, validation timing, loading states, and footer buttons consistently. Using PrimeReact's raw `Dialog` bypasses all of this and leads to inconsistent UX.
10
+ The Cratis dialogs handle command execution, validation timing, busy state, focus, dismissal and footer buttons consistently. A vendor dialog, or a hand-rolled modal, bypasses all of this and leads to inconsistent UX — and a second focus trap around a Components dialog breaks keyboard and screen-reader behavior.
11
11
 
12
12
  ## Choose the Correct Dialog Type
13
13
 
14
14
  - If confirm executes a command, use `CommandDialog` from `@cratis/components/CommandDialog`.
15
15
  - If no command is executed on confirm, use `Dialog` from `@cratis/components/Dialogs`.
16
16
  - If you are **asking the user to confirm** or **showing that something is in progress**, do not build a dialog at all — raise the host-rendered one through its hook (below).
17
- - **Never** import `Dialog` from `primereact/dialog` directly.
17
+ - **Never** use a vendor or hand-rolled modal. Dialogs are Components-owned; the optional renderer adapters do not replace them.
18
18
 
19
19
  ## Confirmations and busy indicators are host-rendered — register once, raise by hook
20
20
 
@@ -109,24 +109,24 @@ if (result === DialogResult.Ok) {
109
109
 
110
110
  ### `onBeforeExecute` is a transformer
111
111
 
112
- It receives the current command values and **must return them** (mutated or not). **Returning `void` executes the command with `undefined` values.** It runs only on submit — never use it to seed *required* values (validation runs against pre-transform state, so a value seeded here never makes the form valid and the submit button stays permanently disabled). Seed required values via `initialValues`; reserve `onBeforeExecute` for transforms that don't affect validity (e.g. a generated id).
112
+ It receives the current command values and **must return them** (mutated or not). **Returning `void` does not execute with `undefined`** — the current values are kept and a `console.warn` is logged; still, always return the values. It runs only on submit — never use it to seed *required* values (validation runs against pre-transform state, so a value seeded here never makes the form valid and the submit button stays permanently disabled). Seed required values via `initialValues`; reserve `onBeforeExecute` for transforms that don't affect validity (e.g. a generated id).
113
113
 
114
114
  ### CommandForm fields
115
115
 
116
- Use built-in `CommandForm` fields (from `@cratis/components/CommandForm`) for every user-input value — a raw PrimeReact control inside a command dialog bypasses `CommandFormFieldWrapper`, so validation never re-runs and the submit button stays **permanently disabled**. Catalog: `InputTextField`, `PasswordField`, `NumberField`, `DropdownField`, `CheckboxField`, `ToggleSwitchField`, `TextAreaField`, `CalendarField`, `RadioButtonField`, `RadioGroupField`, `ChipsField`, `MultiSelectField`, `ColorPickerField`, `SliderField`, `RatingField`.
116
+ Use built-in `CommandForm` fields (from `@cratis/components/CommandForm`) for every user-input value — a raw control (a `Common` `TextInput`, a native `<input>`) inside a command dialog is not bound to the command, so validation never re-runs and the submit button stays **permanently disabled**. Catalog: `InputTextField`, `PasswordField`, `NumberField`, `NumberInputField`, `DropdownField`, `CheckboxField`, `ToggleSwitchField`, `TextAreaField`, `CalendarField`, `RadioButtonField`, `RadioGroupField`, `ChipsField`, `MultiSelectField`, `ColorPickerField`, `SliderField`, `RatingField`. `AutoCommandForm` generates the field list from the command's properties (`string` → `InputTextField`, `number` → `NumberField`, `boolean` → `CheckboxField`, `Date` → `CalendarField`; other types need a registered field-type provider) — use it for plain forms, hand-write fields when the layout or a concept type needs it. Several `RadioButtonField`s bound to one property need the same explicit `name`.
117
117
 
118
- - The `value={c => c.name}` **accessor lambda doubles as the binding and type-checked field selection** — renaming a command property surfaces a compile error at every binding.
118
+ - The `value={c => c.name}` **accessor lambda doubles as the binding and type-checked field selection** — renaming a command property surfaces a compile error at every binding. The binding is inferred from the accessor's *source text*, so a computed accessor (`c => c[descriptor.name]`) cannot be inferred — name it explicitly with `fieldName="…"` (Arc ≥ 22.16.0).
119
119
  - **`RadioGroupField<T>`** renders a whole group from data (`options`/`optionLabel`/`optionValue`, `layout='horizontal'|'vertical'`); **`RadioButtonField<T>`** is one component per option (each takes a `buttonValue`). Both infer the value type from the accessor — no `as string` casts.
120
120
  - **`asCommandFormField(Component, opts)`** (`asCommandFormField`, `WrappedFieldProps` from `@cratis/arc.react/commands`) wraps a custom input so it participates in `CommandForm` like a built-in. `WrappedFieldProps<T>` gives `{ value, onChange, invalid, required, errors }` (`errors` is `string[]` → `errors.join(', ')`); options are `{ defaultValue, extractValue: e => ... }`.
121
- - **`useCommandInstance(Command)`** (`@cratis/arc.react/commands`) returns the live reactive instance the form is bound to — **read** it to drive dependent fields (e.g. read `command.country` to choose a `DropdownField`'s options); never mutate (mutations go through field bindings).
121
+ - **`useCommandInstance<TCommand>()`** (`@cratis/arc.react/commands`, no argument — reads the enclosing form's context) returns the live reactive instance the form is bound to — **read** it to drive dependent fields (e.g. read `command.country` to choose a `DropdownField`'s options); never mutate (mutations go through field bindings).
122
122
 
123
123
  ### Opening dialogs — `useDialog` / `useDialogContext`
124
124
 
125
- `useDialog<TResponse, TInput>(Component)` returns `[Wrapper, showFn]`: render `<Wrapper />` in JSX and call `showFn(input)` to open it; it resolves to `[DialogResult, TResponse?]` when the dialog closes. For a new dialog, prefer reading input as **plain typed props** (`<Name>Input`) and obtaining `closeDialog` from **`useDialogContext<TResponse>()`** — rather than declaring a props interface that extends `DialogProps` to thread both input and `closeDialog`. (Existing dialogs that destructure `closeDialog` from `DialogProps` remain valid.) Signal the outcome with `closeDialog(DialogResult.Ok | Cancelled, response?)`.
125
+ `useDialog<TResponse, TInput>(Component)` returns `[Wrapper, showFn, context]` (the third element is rarely needed): render `<Wrapper />` in JSX and call `showFn(input)` to open it; it resolves to `[DialogResult, TResponse?]` when the dialog closes. For a new dialog, prefer reading input as **plain typed props** (`<Name>Input`) and obtaining `closeDialog` from **`useDialogContext<TRequest, TResponse>()`** (request type first) — rather than declaring a props interface that extends `DialogProps` to thread both input and `closeDialog`. (Existing dialogs that destructure `closeDialog` from `DialogProps` remain valid.) Signal the outcome with `closeDialog(DialogResult.Ok | Cancelled, response?)`.
126
126
 
127
127
  ### Multi-step wizards — `StepperCommandDialog`
128
128
 
129
- For a command split across named steps use `StepperCommandDialog` (`@cratis/components/CommandDialog`) — see the **stepper-command-dialog** skill. Conditional steps written as `{condition && <StepperPanel/>}` are supported **from 2.7.1**: only the steps that actually render are counted, so Next and Submit appear where the user expects them. ⚠️ On earlier versions a hidden step is still counted — Submit never appears on the real last step and a dead Next takes its place, so check what you are pinned to before relying on this. ⚠️ A `<>…</>` fragment wrapping several panels still counts as **one** step — give each step its own `StepperPanel` child. When a step needs non-CommandForm inputs, or cross-step state is complex, fall back to a manual `Dialog` + PrimeReact `Stepper`.
129
+ For a command split across named steps use `StepperCommandDialog` (`@cratis/components/CommandDialog`) — see the **cratis-components-stepper-command-dialog** skill. Conditional steps written as `{condition && <StepperPanel/>}` are supported: only the steps that actually render are counted, so Next and Submit appear where the user expects them, and the active step is clamped if a late-resolving condition removes a step the user had passed. ⚠️ A `<>…</>` fragment wrapping several panels counts as **one** step — give each step its own `StepperPanel` child. Stepper parts are Cratis-owned (`root`, `list`, `step`, `header`, `number`, `title`, `separator`, `panels`, `panel`; `StepperParts` from `@cratis/components/CommandDialog`). When the wizard belongs inline in a page region rather than in a modal, use the sibling `CommandStepper` (`@cratis/components/CommandStepper`) — same steps, same command execution, no dialog chrome.
130
130
 
131
131
  ## When Using `Dialog`
132
132
 
@@ -136,7 +136,7 @@ Use this for dialogs that collect data and return it without executing a command
136
136
  import { useState } from 'react';
137
137
  import { DialogProps, DialogResult } from '@cratis/arc.react/dialogs';
138
138
  import { Dialog } from '@cratis/components/Dialogs';
139
- import { InputText } from 'primereact/inputtext';
139
+ import { TextInput } from '@cratis/components/Common';
140
140
 
141
141
  export const AddProject = ({ closeDialog }: DialogProps<{ name: string }>) => {
142
142
  const [name, setName] = useState('');
@@ -150,10 +150,10 @@ export const AddProject = ({ closeDialog }: DialogProps<{ name: string }>) => {
150
150
  onConfirm={() => closeDialog(DialogResult.Ok, { name })}
151
151
  onCancel={() => closeDialog(DialogResult.Cancelled)}
152
152
  >
153
- <InputText
153
+ <TextInput
154
154
  value={name}
155
- onChange={event => setName(event.target.value)}
156
- autoFocus
155
+ onChange={value => setName(value)}
156
+ aria-label="Project name"
157
157
  />
158
158
  </Dialog>
159
159
  );
@@ -249,16 +249,20 @@ Use `buttons={null}` for dialogs that contain their own internal actions (e.g. a
249
249
 
250
250
  | Prop | Type | Notes |
251
251
  |---|---|---|
252
- | `title` | `string` | Header text (replaces PrimeReact `header`) |
253
- | `visible` | `boolean` | Controls visibility |
252
+ | `title` | `string` | Header text |
253
+ | `visible` | `boolean` | Controlled open state; defaults to `true` |
254
254
  | `buttons` | `DialogButtons \| ReactNode \| null` | Prefer `DialogButtons` enum; `null` for no footer |
255
255
  | `isValid` | `boolean` | Disables the confirm button when `false` |
256
- | `okLabel` | `string` | Override the Ok/Confirm button label |
257
- | `cancelLabel` | `string` | Override the Cancel button label |
256
+ | `isBusy` | `boolean` | Disables every action **and every dismissal path** while work is in flight |
257
+ | `initialFocus` | `DialogInitialFocus` | Where focus lands on open; defaults to the confirmation action — the **cratis-components-accessibility** skill covers the armed-Enter hazard |
258
+ | `okLabel` / `cancelLabel` / `yesLabel` / `noLabel` | `string` | Per-dialog label; falls back to the provider's `messages.dialog`, then English |
259
+ | `closeAriaLabel` | `string` | Accessible name of the header close button; same fallback chain |
258
260
  | `onConfirm` | `() => boolean \| void \| Promise<boolean> \| Promise<void>` | Called when Ok is clicked; return `false` to keep dialog open, `true` to close |
259
- | `onCancel` | `() => void \| Promise<void>` | Called when Cancel is clicked |
261
+ | `onCancel` | `() => boolean \| void \| Promise<boolean> \| Promise<void>` | Called when Cancel is clicked |
260
262
  | `onClose` | `(result: DialogResult) => boolean \| void \| Promise<...>` | Combined handler for both Ok and Cancel |
261
- | `width` | `string` | Dialog width (e.g. `'50vw'`) — replaces PrimeReact `style={{ width }}` |
262
- | `resizable` | `boolean` | Default `false` |
263
+ | `width` | `string` | Dialog width (e.g. `'50vw'`); defaults to `450px` |
264
+ | `dismissable` | `boolean` | Header close, Escape and backdrop dismissal (when not busy) |
265
+ | `style` / `contentStyle` / `className` | | Modal-root and content-region styling |
266
+ | `pt` | `DialogParts` | Per-part attributes: `backdrop`, `positioner`, `root`, `header`, `title`, `close`, `content`, `footer`, `confirm`, `cancel` |
263
267
 
264
- `style`, `contentStyle`, and `dismissable` **are** supported. The other v10 PrimeReact Dialog props (`modal`, `dismissableMask`, `draggable`, `footer`, `onHide`) are **not** available — do not use them. (`resizable` is accepted for compatibility but is a no-op in PrimeReact 11.)
268
+ `resizable`, `ptOptions` and `unstyled` are accepted for source compatibility and do nothing. There is no `modal`, `draggable`, `footer` or `onHide` — the dialog is always modal, the footer comes from `buttons`, and closing is reported through `onCancel`/`onClose`. Style the dialog through its parts (`pt`, or `[data-cratis-part='root']` selectors), never through React Aria class names.