@cratis/pi 7.37.0 → 7.38.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package/corpus/harnesses/copilot/instructions/ai-distribution.instructions.md +109 -0
- package/package/corpus/harnesses/copilot/instructions/application-profile.instructions.md +81 -0
- package/package/corpus/harnesses/copilot/instructions/capability-is-not-authority.instructions.md +45 -0
- package/package/corpus/harnesses/copilot/instructions/code-quality.csharp.instructions.md +92 -0
- package/package/corpus/harnesses/copilot/instructions/code-quality.instructions.md +83 -0
- package/package/corpus/harnesses/copilot/instructions/code-quality.typescript.instructions.md +90 -0
- package/package/corpus/harnesses/copilot/instructions/components.instructions.md +300 -0
- package/package/corpus/harnesses/copilot/instructions/concepts.instructions.md +138 -0
- package/package/corpus/harnesses/copilot/instructions/cratis-folder-is-configuration.instructions.md +29 -0
- package/package/corpus/harnesses/copilot/instructions/csharp.instructions.md +357 -0
- package/package/corpus/harnesses/copilot/instructions/dialogs.instructions.md +278 -0
- package/package/corpus/harnesses/copilot/instructions/documentation-structure-and-formatting.instructions.md +162 -0
- package/package/corpus/harnesses/copilot/instructions/documentation.instructions.md +90 -0
- package/package/corpus/harnesses/copilot/instructions/editing-cratis-docs.instructions.md +74 -0
- package/package/corpus/harnesses/copilot/instructions/efcore.instructions.md +84 -0
- package/package/corpus/harnesses/copilot/instructions/efcore.specs.instructions.md +45 -0
- package/package/corpus/harnesses/copilot/instructions/elixir.instructions.md +50 -0
- package/package/corpus/harnesses/copilot/instructions/exit-codes-and-wrappers.instructions.md +34 -0
- package/package/corpus/harnesses/copilot/instructions/framework.instructions.md +55 -0
- package/package/corpus/harnesses/copilot/instructions/frontend-quality.instructions.md +60 -0
- package/package/corpus/harnesses/copilot/instructions/frontend-testing.instructions.md +127 -0
- package/package/corpus/harnesses/copilot/instructions/git-commits.instructions.md +139 -0
- package/package/corpus/harnesses/copilot/instructions/github-actions.instructions.md +87 -0
- package/package/corpus/harnesses/copilot/instructions/glossary.instructions.md +63 -0
- package/package/corpus/harnesses/copilot/instructions/go-engineering.instructions.md +61 -0
- package/package/corpus/harnesses/copilot/instructions/go.instructions.md +116 -0
- package/package/corpus/harnesses/copilot/instructions/guards-and-fuses.instructions.md +61 -0
- package/package/corpus/harnesses/copilot/instructions/java.instructions.md +138 -0
- package/package/corpus/harnesses/copilot/instructions/kotlin.instructions.md +175 -0
- package/package/corpus/harnesses/copilot/instructions/local-work-artifacts.instructions.md +56 -0
- package/package/corpus/harnesses/copilot/instructions/new-repository-intake.instructions.md +35 -0
- package/package/corpus/harnesses/copilot/instructions/orleans.instructions.md +97 -0
- package/package/corpus/harnesses/copilot/instructions/profiles.instructions.md +403 -0
- package/package/corpus/harnesses/copilot/instructions/pull-requests.instructions.md +176 -0
- package/package/corpus/harnesses/copilot/instructions/react.instructions.md +203 -0
- package/package/corpus/harnesses/copilot/instructions/reactors.instructions.md +336 -0
- package/package/corpus/harnesses/copilot/instructions/rtk.instructions.md +48 -0
- package/package/corpus/harnesses/copilot/instructions/specs.csharp.instructions.md +147 -0
- package/package/corpus/harnesses/copilot/instructions/specs.instructions.md +133 -0
- package/package/corpus/harnesses/copilot/instructions/specs.scenarios.csharp.instructions.md +281 -0
- package/package/corpus/harnesses/copilot/instructions/specs.typescript.instructions.md +141 -0
- package/package/corpus/harnesses/copilot/instructions/storybook.instructions.md +85 -0
- package/package/corpus/harnesses/copilot/instructions/typescript.instructions.md +151 -0
- package/package/corpus/harnesses/copilot/instructions/verification-discipline.instructions.md +27 -0
- package/package/corpus/harnesses/copilot/instructions/vertical-slices.instructions.md +414 -0
- package/package/corpus/harnesses/copilot/instructions/web-fetching.instructions.md +12 -0
- package/package/corpus/harnesses/copilot/instructions/writing-correct-examples.instructions.md +57 -0
- package/package/corpus/harnesses/copilot/instructions/writing-cratis-docs.instructions.md +72 -0
- package/package/corpus/product-sync.json +2 -2
- package/package/corpus/skills/cratis-screenplay-discovery/SKILL.md +11 -6
- package/package/corpus/skills/cratis-screenplay-discovery/references/facilitation.md +9 -5
- package/package/corpus/skills/cratis-screenplay-discovery/references/human-workshop.md +1 -1
- package/package/corpus/skills/cratis-screenplay-discovery/references/plotting-and-handoff.md +28 -4
- package/package/corpus/skills/cratis-screenplay-discovery/references/storming-loop.md +2 -1
- package/package/corpus/skills/cratis-screenplay-discovery/verification.json +5 -0
- package/package/corpus/skills/cratis-screenplay-model-authoring/SKILL.md +23 -29
- package/package/corpus/skills/cratis-screenplay-model-authoring/references/mcp-loop.md +15 -1
- package/package/corpus/skills/cratis-screenplay-model-authoring/references/model-notes.md +208 -0
- package/package/corpus/skills/cratis-screenplay-model-authoring/verification.json +20 -1
- package/package/corpus/skills/cratis-screenplay-model-review/SKILL.md +19 -36
- package/package/corpus/skills/cratis-screenplay-model-review/references/report-template.md +26 -0
- package/package/corpus/skills/cratis-screenplay-model-review/verification.json +7 -0
- package/package/corpus/skills/cratis-screenplay-modeling-lifecycle/SKILL.md +14 -16
- package/package/corpus/skills/cratis-screenplay-modeling-lifecycle/references/core-rules-digest.md +8 -3
- package/package/corpus/skills/cratis-screenplay-modeling-lifecycle/references/handoff-template.md +5 -2
- package/package/corpus/skills/cratis-screenplay-modeling-lifecycle/references/intake.md +21 -0
- package/package/corpus/skills/cratis-screenplay-modeling-lifecycle/references/reasoning-notes.md +10 -6
- package/package/corpus/skills/cratis-screenplay-modeling-lifecycle/references/stop-or-assume.md +32 -1
- package/package/corpus/skills/cratis-screenplay-modeling-lifecycle/references/worked-example.md +5 -2
- package/package/corpus/skills/cratis-screenplay-modeling-lifecycle/verification.json +5 -0
- package/package/corpus/skills/cratis-screenplay-toolchain/SKILL.md +4 -0
- package/package/corpus/skills/cratis-screenplay-toolchain/references/screenplay-contract.json +1716 -183
- package/package/corpus/skills/cratis-screenplay-toolchain/references/screenplay-contract.version +1 -1
- package/package/corpus/skills/cratis-screenplay-toolchain/references/versions.md +40 -4
- package/package/corpus/skills/cratis-screenplay-toolchain/verification.json +6 -0
- package/package/corpus/skills/cratis-stage-rendering-and-sandbox/references/admission.md +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
---
|
|
2
|
+
applyTo: "**/*.tsx"
|
|
3
|
+
paths:
|
|
4
|
+
- "**/*.tsx"
|
|
5
|
+
profile: application
|
|
6
|
+
---
|
|
7
|
+
<!-- cratis-ai: generated Copilot instructions. Do not edit; rules/ is the source. -->
|
|
8
|
+
|
|
9
|
+
# Building React Components
|
|
10
|
+
|
|
11
|
+
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.
|
|
12
|
+
|
|
13
|
+
## Import from subpaths — the root is setup-only
|
|
14
|
+
|
|
15
|
+
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.
|
|
16
|
+
|
|
17
|
+
| Need | Use | Subpath |
|
|
18
|
+
|---|---|---|
|
|
19
|
+
| App root | `CratisComponentsProvider` | `@cratis/components` |
|
|
20
|
+
| Page chrome, buttons, basic controls | `Page`, `Button`, `IconButton`, `TextInput`, `TextArea`, `NumberInput`, `Checkbox`, `Radio`, `Switch`, `Tooltip`, `ErrorBoundary`, `FormElement`, `ComboBox`, `ToggleGroup`, `Tabs`, `TagGroup`, `Breadcrumbs`, `DatePickerInput`, `ActionMenubar`, `ToolbarFocusMode`, `Surface` | `@cratis/components/Common` |
|
|
21
|
+
| Icon | `Icon` (type) / `IconDisplay` | `@cratis/components/Common` |
|
|
22
|
+
| Query list page | `DataPage`, `MenuItem`, `Column` | `@cratis/components/DataPage` |
|
|
23
|
+
| Standalone query table | `DataTableForQuery` / `DataTableForObservableQuery`, `Column` | `@cratis/components/DataTables` |
|
|
24
|
+
| Dropdown | `Dropdown` | `@cratis/components/Dropdown` |
|
|
25
|
+
| Command dialog | `CommandDialog`, `StepperCommandDialog`, `StepperPanel` | `@cratis/components/CommandDialog` |
|
|
26
|
+
| Standalone stepper | `CommandStepper` | `@cratis/components/CommandStepper` |
|
|
27
|
+
| Data / confirmation / busy dialog | `Dialog`, `ConfirmationDialog`, `BusyIndicatorDialog` | `@cratis/components/Dialogs` |
|
|
28
|
+
| Command form fields | `InputTextField`, `NumberField`, `DropdownField`, `CheckboxField`, `CalendarField`, …, `AutoCommandForm` | `@cratis/components/CommandForm` |
|
|
29
|
+
| Notifications | `Toaster`, `toast`, `toastCommandResult` | `@cratis/components/Notifications` |
|
|
30
|
+
| Status & display | `Tag`, `Badge`, `Chip`, `Skeleton`, `Avatar`, `ProgressBar`, `ProgressSpinner`, `Message` | `@cratis/components/Display` |
|
|
31
|
+
| Canvas tool palette | `Toolbar`, `ToolbarButton`, … | `@cratis/components/Toolbar` |
|
|
32
|
+
| Schema structure / object content | `SchemaEditor` / `ObjectContentEditor` / `ObjectNavigationalBar` | `@cratis/components/SchemaEditor`, `/ObjectContentEditor`, `/ObjectNavigationalBar` |
|
|
33
|
+
| Spatial canvas / pivot data | `Canvas` / `PivotViewer` | `@cratis/components/Canvas`, `/PivotViewer` |
|
|
34
|
+
| Chat | `Chat` | `@cratis/components/Chat` |
|
|
35
|
+
| Ordered configuration | `OrderedItemEditor`, `PropertyControls` | `@cratis/components/ConfigurationEditor` |
|
|
36
|
+
| Filter panel | `FilterPanel` | `@cratis/components/Filter` |
|
|
37
|
+
| Icon / Markdown editing | `IconPicker` / `MarkdownEditor` | `@cratis/components/IconPicker`, `/MarkdownEditor` |
|
|
38
|
+
| Time navigation | `TimeMachine` | `@cratis/components/TimeMachine` |
|
|
39
|
+
| Renderer contracts / built-in | renderer exports | `@cratis/components/renderer`, `/renderer/builtin` |
|
|
40
|
+
| CSS foundation / area sheets | layer order / structural CSS | `@cratis/components/styles/base`, `<subpath>/styles` |
|
|
41
|
+
| Shared types | `JsonSchema`, `Json`, … | `@cratis/components/types` |
|
|
42
|
+
|
|
43
|
+
At Components 4.26.4, React and React DOM are `^19.0.0`, Arc and Arc React are
|
|
44
|
+
`>=20.3.1 <23`, Fundamentals is `^7.10.3`, and `reflect-metadata` `0.2.2` is required
|
|
45
|
+
(import it at the entry point). `tsyringe` `4.10.0` is optional for Components;
|
|
46
|
+
Arc React installs it. `Canvas` and `PivotViewer` need the optional `pixi.js ^8.20.0`
|
|
47
|
+
peer; ordinary pages do not. No `motion-dom` or `motion-utils` resolution pin is needed.
|
|
48
|
+
|
|
49
|
+
### Common controls
|
|
50
|
+
|
|
51
|
+
All import from `@cratis/components/Common`:
|
|
52
|
+
|
|
53
|
+
| Control | Main props / choice |
|
|
54
|
+
| --- | --- |
|
|
55
|
+
| `ComboBox` | `options`, `value: string \| null`, `onChange(key)`, `filter`, `openOnFocus`, `loading`, `emptyMessage`, `failure`, `action`, `optionLayout` — a single-select entity picker |
|
|
56
|
+
| `ToggleGroup` | `options`, `value`, `onChange` — choose a value; radiogroup semantics with one Tab stop |
|
|
57
|
+
| `Tabs` | `tabs`, `value`, `onChange` — choose a panel, not a domain value |
|
|
58
|
+
| `TagGroup` | `value: string[]`, `onChange`, `editable`, `separators`, `allowDuplicates`, `removeIcon` |
|
|
59
|
+
| `Breadcrumbs` | `items`, `separator` — last item is `aria-current='page'` |
|
|
60
|
+
|
|
61
|
+
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.
|
|
62
|
+
|
|
63
|
+
### Notifications — feedback for commands run outside a dialog
|
|
64
|
+
|
|
65
|
+
`CommandDialog` handles success/error feedback itself. For a command executed
|
|
66
|
+
**programmatically** (`command.execute()` outside a dialog), mount the toast region once —
|
|
67
|
+
`toaster` on `CratisComponentsProvider`, or one `<Toaster />` near the root — and surface the result
|
|
68
|
+
with `toastCommandResult` (`@cratis/components/Notifications`). It maps the granular
|
|
69
|
+
`ICommandResult` flags to the right toast (success, not-authorized, validation with per-field
|
|
70
|
+
messages, exceptions — never stack traces):
|
|
71
|
+
|
|
72
|
+
```tsx
|
|
73
|
+
const result = await command.execute();
|
|
74
|
+
if (toastCommandResult(result, { successTitle: 'Author registered' })) refresh();
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Without a mounted toaster, neither `toast` nor `toastCommandResult` displays anything.
|
|
78
|
+
Mount **one**, not both: two toasters show every toast twice. The provider has no
|
|
79
|
+
Arc dependency; its position relative to `<Arc>` is not what enables notifications.
|
|
80
|
+
|
|
81
|
+
`ErrorBoundary` (`Common`) contains a failing section with a neutral message, never
|
|
82
|
+
exception text. Supply `fallback` (a node or `(reset) => node`), `onError`, and
|
|
83
|
+
`onReset` when needed; retry remounts only the protected section.
|
|
84
|
+
|
|
85
|
+
For ad-hoc notifications, call the imperative `toast.success/info/warn/error(...)` — each takes an
|
|
86
|
+
**options object**, not a bare string: `toast.info({ title: 'Saved', description: '…' })`.
|
|
87
|
+
|
|
88
|
+
### Column filtering & display components
|
|
89
|
+
|
|
90
|
+
`<Column>` supports `sortable` and `filter` (a per-column filter popup with match modes), and
|
|
91
|
+
`DataPage` / the data tables show a search box when `globalFilterFields` is set. **Sorting and
|
|
92
|
+
filtering apply to the currently loaded page**; complete-result filtering belongs in the query's
|
|
93
|
+
arguments and the server-side query, before paging (`clientFiltering` is a deprecated no-op). Use the
|
|
94
|
+
`Display` components (`Tag`, `Badge`, `Skeleton`, …) for status indicators and loading states.
|
|
95
|
+
|
|
96
|
+
### `DataPage` — query list pages
|
|
97
|
+
|
|
98
|
+
`DataPage` (from `@cratis/components/DataPage`) owns the data table's subscription, paging,
|
|
99
|
+
selection, action menubar, and details split — **do not pre-fetch rows and pass an `items` array**.
|
|
100
|
+
Required props: `title`, `query` (`Constructor<TQuery>`; snapshot and observable queries are
|
|
101
|
+
auto-detected), `emptyMessage`, and `children`. Other props: `queryArguments`, `dataKey` (pass
|
|
102
|
+
whenever the read model has an identity), `selection` / `onSelectionChange`, `globalFilterFields` /
|
|
103
|
+
`defaultFilters`, `detailsComponent` (`React.FC<IDetailsComponentProps<T>>` = `{ item, onRefresh? }`),
|
|
104
|
+
`onRefresh` (forwarded unchanged to the details component; it never refetches the list),
|
|
105
|
+
`selectionMode='multiple'` with `selectedItems` / `onSelectedItemsChange`, and
|
|
106
|
+
`loadingMessage` / `failureMessage` / `unauthorizedMessage` for query-state rows.
|
|
107
|
+
Use an observable query for a live list; DataPage exposes no snapshot refresh handle.
|
|
108
|
+
Global search uses `globalSearchPlaceholder` / `globalSearchAriaLabel`; paginator
|
|
109
|
+
styling and labels use `paginatorClassName` / `paginatorAriaLabels`. The Cratis part props are `tablePt: DataTableParts` / `paginatorPt: TablePaginatorParts`
|
|
110
|
+
/ `menubarPt: ButtonParts` (plus `tableClassName` / `menubarClassName`).
|
|
111
|
+
|
|
112
|
+
Columns and toolbar actions are compositional children:
|
|
113
|
+
|
|
114
|
+
```tsx
|
|
115
|
+
import { DataPage, MenuItem, Column } from '@cratis/components/DataPage';
|
|
116
|
+
|
|
117
|
+
<DataPage title="Accounts" query={AllAccounts} emptyMessage="No accounts yet.">
|
|
118
|
+
<DataPage.Columns>
|
|
119
|
+
<Column field="name" header="Name" />
|
|
120
|
+
</DataPage.Columns>
|
|
121
|
+
<DataPage.MenuItems>
|
|
122
|
+
<MenuItem label="Add" command={() => showAdd()} disableOnUnselected={false} />
|
|
123
|
+
</DataPage.MenuItems>
|
|
124
|
+
</DataPage>
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
`MenuItem` is a declarative Components element read by `DataPage.MenuItems` (use `command`, not
|
|
128
|
+
`onClick`); `disableOnUnselected` greys it out until a row is selected; its `icon` is a **React
|
|
129
|
+
component type** (e.g. a `react-icons` icon), not an `Icon`. See the **cratis-arc-react-page** skill
|
|
130
|
+
for the full page workflow.
|
|
131
|
+
|
|
132
|
+
## Composition over Monoliths
|
|
133
|
+
|
|
134
|
+
A well-built component tree is like a well-organized kitchen — every tool has a place, and you can find what you need without opening every drawer. Large components that do everything are hard to understand, hard to test, and hard to change without breaking something unrelated.
|
|
135
|
+
|
|
136
|
+
- Split components into small, focused pieces and compose them together. Each component should have a single, clear responsibility.
|
|
137
|
+
- Parent components own state and event handlers; children receive props. This makes data flow predictable and debuggable.
|
|
138
|
+
- If you find yourself writing a block comment like `// Author list section` inside a component, that section should be its own component. The comment is a code smell — the component name should provide that context instead.
|
|
139
|
+
|
|
140
|
+
## Folder Structure
|
|
141
|
+
|
|
142
|
+
- Single-file component → place directly in the parent feature folder.
|
|
143
|
+
- Multi-file component (sub-components, hooks, CSS) → create a folder named after the component:
|
|
144
|
+
|
|
145
|
+
```
|
|
146
|
+
PrototypeWindow/
|
|
147
|
+
PrototypeWindow.tsx ← composition root
|
|
148
|
+
PrototypeWindow.css ← styles for the composition
|
|
149
|
+
TitleBar.tsx ← sub-component
|
|
150
|
+
CanvasArea.tsx ← sub-component
|
|
151
|
+
ResizeHandle.tsx ← sub-component
|
|
152
|
+
index.ts ← re-exports public API
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Add an `index.ts` that re-exports the public surface so import paths stay stable.
|
|
156
|
+
|
|
157
|
+
## Styling
|
|
158
|
+
|
|
159
|
+
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.
|
|
160
|
+
|
|
161
|
+
### App setup — the imports Cratis Components needs
|
|
162
|
+
|
|
163
|
+
An app does three things, once, at its root — or the components render unstyled:
|
|
164
|
+
|
|
165
|
+
1. **Import the stylesheets, in this order** — components do not import their own CSS:
|
|
166
|
+
|
|
167
|
+
In `index.css`, imported once by the entry point with `import './index.css'`:
|
|
168
|
+
|
|
169
|
+
```css
|
|
170
|
+
@import '@cratis/components/tokens';
|
|
171
|
+
@import '@cratis/components/styles';
|
|
172
|
+
@import '@cratis/components/theme'; /* optional baseline light/dark look */
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Direct CSS subpath imports from TypeScript need ambient declarations on TypeScript 6
|
|
176
|
+
(otherwise TS2882). To reduce the roughly 32 KiB gzip aggregate, replace `styles`
|
|
177
|
+
with `styles/base` plus the needed `<subpath>/styles` sheets. Area order does not matter.
|
|
178
|
+
|
|
179
|
+
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.
|
|
180
|
+
|
|
181
|
+
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`, `filter`, `columnFilter`, `toolbar`); `icons` replaces the
|
|
182
|
+
product-wide glyphs (`close`, `remove`, `expand`, `sortAscending`, `sortDescending`,
|
|
183
|
+
`previous`, `next`, `clear`). Resolution is component prop → provider → built-in;
|
|
184
|
+
`null` hides only the glyph. `useCratisIcon()` (package root) resolves this vocabulary.
|
|
185
|
+
`locales` is deprecated; use `messages`. `toaster` accepts `boolean | ToasterProps`
|
|
186
|
+
and mounts the toast region. Provider props also include `overlayEnvironment`,
|
|
187
|
+
`rendererSetup` (non-secret booleans), experimental `libraryMode` (`strict` / `degrade`)
|
|
188
|
+
and `rendererFallback` (`core` / `throw`). 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.
|
|
189
|
+
|
|
190
|
+
```tsx
|
|
191
|
+
import { CratisComponentsProvider } from '@cratis/components';
|
|
192
|
+
|
|
193
|
+
export const App = () => (
|
|
194
|
+
<CratisComponentsProvider value={{ locale: 'en-US' }} toaster>
|
|
195
|
+
<Application />
|
|
196
|
+
</CratisComponentsProvider>
|
|
197
|
+
);
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
Components 4.22.1+ with `theme` imported maps Arc's `--color-error` and
|
|
201
|
+
`--color-error-bg` in dark schemes; a product theme omitting it must define both.
|
|
202
|
+
|
|
203
|
+
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.
|
|
204
|
+
|
|
205
|
+
### Writing styles
|
|
206
|
+
|
|
207
|
+
- 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.
|
|
208
|
+
- The composition root's CSS only contains layout/grid rules for positioning children — it should not style the children themselves.
|
|
209
|
+
- Use inline `style` props **only** for runtime-dynamic values (pixel positions, computed sizes).
|
|
210
|
+
- Use the **`--cratis-*` tokens** for every color, background, border, shadow and radius, so theming and dark mode work automatically:
|
|
211
|
+
- 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)`
|
|
212
|
+
- 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)`
|
|
213
|
+
- status — `var(--cratis-success-background)`/`-text`, `var(--cratis-warning-*)`, `var(--cratis-danger-*)`, `var(--cratis-info-*)`
|
|
214
|
+
- shape — `var(--cratis-border-radius)`, `var(--cratis-control-height)`, `var(--cratis-shadow-subtle)`/`-overlay`/`-dialog`
|
|
215
|
+
- Never hard-code hex or `rgb()` for UI chrome. Only hard-code colors that are intentionally theme-independent (brand accent dots, traffic-light indicators).
|
|
216
|
+
- **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.
|
|
217
|
+
- Name CSS classes with a BEM-like prefix matching the component name.
|
|
218
|
+
|
|
219
|
+
## Props
|
|
220
|
+
|
|
221
|
+
Props are a component's public API. They should be clear, minimal, and well-documented.
|
|
222
|
+
|
|
223
|
+
- Each sub-component declares its own `*Props` interface with JSDoc on every prop.
|
|
224
|
+
- Pass only needed props — avoid threading large prop bags through component trees.
|
|
225
|
+
- Event handlers follow `on*` naming: `onPointerDown`, `onSelect`.
|
|
226
|
+
- 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.
|
|
227
|
+
|
|
228
|
+
## Dialogs
|
|
229
|
+
|
|
230
|
+
See [dialogs.md](./dialogs.instructions.md) for the full dialog guide.
|
|
231
|
+
|
|
232
|
+
**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.
|
|
233
|
+
|
|
234
|
+
## Icons
|
|
235
|
+
|
|
236
|
+
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.
|
|
237
|
+
|
|
238
|
+
Use provider `value.icons` for Components' own glyphs across the product;
|
|
239
|
+
`Dialog.closeIcon`, `TagGroup.removeIcon`, and other component props still win at one site.
|
|
240
|
+
|
|
241
|
+
For the application's own SVG icons:
|
|
242
|
+
|
|
243
|
+
- **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`).
|
|
244
|
+
- **Store each SVG as a separate `.svg` file** inside the icon's folder. Do not embed SVG markup directly in `.tsx` files.
|
|
245
|
+
- Import SVG files with the `?raw` suffix to get the raw SVG string: `import iconSvg from './Icon.svg?raw';`
|
|
246
|
+
- Render inline using `dangerouslySetInnerHTML={{ __html: iconSvg }}` so that CSS `currentColor` is honored.
|
|
247
|
+
- **Use subfolders for grouping related icons or complex components.**
|
|
248
|
+
- A folder named `SliceStatus/` groups the four status SVG files together with the interactive `SliceStatus` component that uses them.
|
|
249
|
+
- Simple, standalone icons may live directly in the `icons/` root if they have no related siblings.
|
|
250
|
+
- **Every icon folder must have an `index.ts`** that re-exports the public API, keeping import paths for consumers stable.
|
|
251
|
+
- **Barrel-export all icons through `icons/index.ts`** so consumers import from the `icons` path alias, not from deep paths.
|
|
252
|
+
|
|
253
|
+
**Example structure:**
|
|
254
|
+
```
|
|
255
|
+
icons/
|
|
256
|
+
SliceStatus/
|
|
257
|
+
NotStarted.svg ← raw SVG file
|
|
258
|
+
InProgress.svg ← raw SVG file
|
|
259
|
+
ReadyForReview.svg ← raw SVG file
|
|
260
|
+
Done.svg ← raw SVG file
|
|
261
|
+
SliceStatus.tsx ← interactive component using the SVG files
|
|
262
|
+
SliceStatus.css
|
|
263
|
+
index.ts
|
|
264
|
+
CogWheelIcon/
|
|
265
|
+
CogWheel.svg ← raw SVG file
|
|
266
|
+
CogWheelIcon.tsx ← thin wrapper component
|
|
267
|
+
CogWheelIcon.css
|
|
268
|
+
index.ts
|
|
269
|
+
WireframeIcon.tsx ← simple component with no SVG (stays at root)
|
|
270
|
+
WireframeIcon.css
|
|
271
|
+
index.ts ← re-exports everything
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
## Storybook
|
|
275
|
+
|
|
276
|
+
- Storybook runs at **http://localhost:6006** — never restart it.
|
|
277
|
+
- Use the `click` tool to interact with Storybook for visual verification.
|
|
278
|
+
|
|
279
|
+
## Verification
|
|
280
|
+
|
|
281
|
+
After every task, run both:
|
|
282
|
+
1. `yarn lint`
|
|
283
|
+
2. `npx tsc -b`
|
|
284
|
+
|
|
285
|
+
## README.md for Complex Components
|
|
286
|
+
|
|
287
|
+
Complex components accumulate knowledge that lives nowhere else — why a particular state structure was chosen, how sub-components divide responsibilities, what conventions the CSS follows. Without a README, the next developer (or AI) has to reverse-engineer all of this from the code.
|
|
288
|
+
|
|
289
|
+
Every component folder with sub-components, hooks, or non-trivial architecture **must** have a `README.md`.
|
|
290
|
+
|
|
291
|
+
**Before starting work:** Check for an existing README and read it first. It may contain context that changes your approach.
|
|
292
|
+
|
|
293
|
+
**A README must cover:**
|
|
294
|
+
- Component hierarchy — tree of components and what each owns
|
|
295
|
+
- Architecture decisions — what was chosen and why
|
|
296
|
+
- State management — where state lives, what each piece controls
|
|
297
|
+
- CSS conventions — patterns used across children
|
|
298
|
+
- How to extend — steps for common modifications
|
|
299
|
+
|
|
300
|
+
**Keep READMEs current** — update in the same commit when changing architecture, layout, or state structure.
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
---
|
|
2
|
+
applyTo: "**/*.cs"
|
|
3
|
+
paths:
|
|
4
|
+
- "**/*.cs"
|
|
5
|
+
---
|
|
6
|
+
<!-- cratis-ai: generated Copilot instructions. Do not edit; rules/ is the source. -->
|
|
7
|
+
|
|
8
|
+
# Concepts — Strongly Typed Domain Values
|
|
9
|
+
|
|
10
|
+
## Why
|
|
11
|
+
|
|
12
|
+
A primitive such as `Guid` or `string` does not tell the compiler whether a
|
|
13
|
+
value is an `AuthorId`, `UserId`, or `InvoiceNumber`. A Cratis concept gives a
|
|
14
|
+
meaningful value its own type and keeps that meaning in method signatures,
|
|
15
|
+
serialization, validation, events, and read models.
|
|
16
|
+
|
|
17
|
+
Use a concept for a real domain value, not mechanically for every primitive in a
|
|
18
|
+
DTO or framework API. An enum already represents a closed domain concept and
|
|
19
|
+
does not need a `ConceptAs<T>` wrapper.
|
|
20
|
+
|
|
21
|
+
## Product contracts
|
|
22
|
+
|
|
23
|
+
- A value concept derives from `ConceptAs<T>` in `Cratis.Fundamentals`.
|
|
24
|
+
- A value actually used as a Chronicle event-source/stream identity derives from
|
|
25
|
+
`EventSourceId<T>` in `Cratis.Chronicle`.
|
|
26
|
+
- An arbitrary entity ID that does not identify a Chronicle stream remains a
|
|
27
|
+
value concept; do not derive it from `EventSourceId<T>` merely because its name
|
|
28
|
+
ends in `Id`.
|
|
29
|
+
- Both generic bases require an underlying type implementing `IComparable`.
|
|
30
|
+
|
|
31
|
+
## Value concept rules
|
|
32
|
+
|
|
33
|
+
- Use a positional record containing exactly one wrapped value. Fundamentals
|
|
34
|
+
serialization assumes a single-value concept; do not add extra properties.
|
|
35
|
+
- `ConceptAs<T>` supplies concept → `T` conversion.
|
|
36
|
+
- Add `T` → derived concept conversion only when it improves the domain API. It
|
|
37
|
+
is optional, not a framework requirement.
|
|
38
|
+
- `ConceptAs<T>` rejects a null wrapped value. Use a nullable concept reference
|
|
39
|
+
such as `AuthorName?` when absence is valid.
|
|
40
|
+
- `NotSet` or `Empty` is optional domain policy. Add one only when the backing
|
|
41
|
+
value is impossible or explicitly reserved in that domain. Do not assume
|
|
42
|
+
empty string, zero, or `Guid.Empty` is universally invalid.
|
|
43
|
+
- Mark a concept that holds **personal data** `[PII]` alone, and one that holds an
|
|
44
|
+
**operational secret** (API key, token, password, a company's bank account
|
|
45
|
+
number) `[Encrypted]` (`Cratis.Chronicle.ProtectedValues`, Chronicle 19.32.0+)
|
|
46
|
+
**plus** `[NotAudited]`. The markings travel with the type, so marking it
|
|
47
|
+
once covers every command and event that uses it - which is the point of
|
|
48
|
+
having the concept. A command's property values are written to the causation
|
|
49
|
+
chain of every event it appends, so an unmarked `ApiKey` or `AccessToken`
|
|
50
|
+
concept reaches the event log in the clear and stays there. `[Encrypted]`
|
|
51
|
+
protects the value at rest on the event; `[NotAudited]` keeps the command
|
|
52
|
+
input off the causation chain - either alone leaves a gap. `[PII]` already
|
|
53
|
+
encrypts, enrolls the value in erasure and withholds it from the chain, so
|
|
54
|
+
never combine it with `[Encrypted]` (analyzer `CHR0053`). The two kinds are
|
|
55
|
+
not interchangeable: `[PII]` is wrong for a password because it enrolls it in
|
|
56
|
+
erasure, and `[Encrypted]` does not erase a name.
|
|
57
|
+
|
|
58
|
+
```csharp
|
|
59
|
+
public record AuthorName(string Value) : ConceptAs<string>(Value)
|
|
60
|
+
{
|
|
61
|
+
public static implicit operator AuthorName(string value) => new(value);
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Chronicle stream identity rules
|
|
66
|
+
|
|
67
|
+
```csharp
|
|
68
|
+
public record AuthorId(Guid Value) : EventSourceId<Guid>(Value), IGeneratable<AuthorId>
|
|
69
|
+
{
|
|
70
|
+
public static AuthorId New() => new(GenerateValue.Uuid());
|
|
71
|
+
public static implicit operator AuthorId(Guid value) => new(value);
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
- `New()`, primitive → derived ID conversion, and sentinels are optional domain
|
|
76
|
+
conveniences; `EventSourceId<T>` does not create an arbitrary derived type.
|
|
77
|
+
- When the system creates the value, implement `IGeneratable<TSelf>` and use
|
|
78
|
+
`GenerateValue.Uuid()` (or `GenerateValue.UuidV7()` when index locality
|
|
79
|
+
matters), both from `Cratis.Concepts` in Cratis.Fundamentals 7.23.0 or later.
|
|
80
|
+
Never `System.Random`. A client-supplied identifier is an input, not something
|
|
81
|
+
to generate; see the **cratis-fundamentals-concept** skill.
|
|
82
|
+
- Arc and Chronicle can resolve an older Cratis.Fundamentals. Check the resolved
|
|
83
|
+
version first; if it is below 7.23.0, add a direct `Cratis.Fundamentals`
|
|
84
|
+
7.23.0 or later reference, or keep the existing `static New()` (without the interface) until you can.
|
|
85
|
+
- The exact `EventSourceId<T>` base supports conversions among its underlying
|
|
86
|
+
`T`, string, typed ID, and untyped `EventSourceId`, but those operators do not
|
|
87
|
+
construct your derived domain record from every source form.
|
|
88
|
+
- String and Guid remain the safest stream-id primitives. Chronicle v19.50.0
|
|
89
|
+
uses invariant numeric conversion, `"O"` for date/time values and `"c"` for
|
|
90
|
+
TimeSpan (since 19.37.3; `Source/Clients/DotNET/Events/EventSourceId.cs:148-174`).
|
|
91
|
+
Preserve the old untyped id for streams written under legacy date/time or
|
|
92
|
+
decimal-comma culture formats; the new conversion can otherwise open a new stream.
|
|
93
|
+
- Pass the identity explicitly to Chronicle append/read operations. Declaring an
|
|
94
|
+
`EventSourceId<T>` property does not select the stream.
|
|
95
|
+
- Do not put `[Key]` or `[Subject]` on an `EventSourceId<T>`-derived member;
|
|
96
|
+
Chronicle analyzer `CHR0026` reports it.
|
|
97
|
+
- Do not put `[PII]` or `[Encrypted]` on an event-source ID; analyzers `CHR0034`
|
|
98
|
+
and `CHR0052` reject them.
|
|
99
|
+
Sensitive natural identifiers use a random surrogate stream ID and a separate
|
|
100
|
+
compliance-managed value.
|
|
101
|
+
- `EventSourceId.Unspecified` is the untyped string-backed sentinel. Typed empty
|
|
102
|
+
or zero values convert to real specified stream IDs and are not equivalent to
|
|
103
|
+
`Unspecified`.
|
|
104
|
+
|
|
105
|
+
## Application placement convention
|
|
106
|
+
|
|
107
|
+
In a Cratis application, place a concept with the feature/module that owns its
|
|
108
|
+
meaning rather than in a generic `Concepts/` folder. Put genuinely cross-feature
|
|
109
|
+
concepts in `Common/`. Do not introduce a top-level `Features/` wrapper.
|
|
110
|
+
|
|
111
|
+
This is a Cratis application convention, not a Fundamentals or Chronicle API
|
|
112
|
+
contract. Framework and client repositories follow their own structure.
|
|
113
|
+
|
|
114
|
+
## Promote a value deliberately
|
|
115
|
+
|
|
116
|
+
Promote a value when its meaning or cross-cutting characteristics must travel
|
|
117
|
+
with it—for example validation, compliance classification, or a domain-specific
|
|
118
|
+
format. Reuse an existing shared concept when it already owns that meaning.
|
|
119
|
+
|
|
120
|
+
## Call-site guidance
|
|
121
|
+
|
|
122
|
+
Use the constructors, conversions, factories, or sentinels the domain type
|
|
123
|
+
actually provides. Do not require `NotSet`, `New()`, or a reverse conversion on
|
|
124
|
+
every concept. If a sentinel exists, reference the named sentinel rather than
|
|
125
|
+
reconstructing its backing primitive.
|
|
126
|
+
|
|
127
|
+
## Geospatial values
|
|
128
|
+
|
|
129
|
+
Use GeoJSON types from `Cratis.Geospatial`: `Point` for a location,
|
|
130
|
+
`LineString` for a route, and `Polygon` for an area. Do not use the removed
|
|
131
|
+
experimental `Coordinate` type. Model geospatial absence according to the
|
|
132
|
+
owning domain contract; do not invent a nullable event payload that conflicts
|
|
133
|
+
with Chronicle event rules. TypeScript imports `Point`, `LineString`, `LinearRing`,
|
|
134
|
+
and `Polygon` from `@cratis/fundamentals/geospatial` (verified at 7.23.0).
|
|
135
|
+
The wire shape is GeoJSON, for example
|
|
136
|
+
`{ "type": "Point", "coordinates": [longitude, latitude] }`: longitude first.
|
|
137
|
+
The removal of `Coordinate` in 7.15.1 is a stored-data compatibility break;
|
|
138
|
+
plan a migration rather than assuming the old shape deserializes as GeoJSON.
|
package/package/corpus/harnesses/copilot/instructions/cratis-folder-is-configuration.instructions.md
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
---
|
|
2
|
+
applyTo: "**/*"
|
|
3
|
+
---
|
|
4
|
+
<!-- cratis-ai: generated Copilot instructions. Do not edit; rules/ is the source. -->
|
|
5
|
+
|
|
6
|
+
# `.cratis/` is configuration, never a place to produce things
|
|
7
|
+
|
|
8
|
+
The `.cratis/` folder is a configuration folder, like `.vscode/` or `.github/`. It holds
|
|
9
|
+
project configuration and the shared AI corpus itself, and nothing else.
|
|
10
|
+
|
|
11
|
+
- **Allowed:** `.cratis/ai.json` and `.cratis/ai.manifest.json`, the installed corpus under
|
|
12
|
+
`.cratis/ai/`, and other tool configuration a Cratis tool reads (settings, profiles,
|
|
13
|
+
connection and environment descriptors).
|
|
14
|
+
- **Never produce anything there that can end up in production or that a person or
|
|
15
|
+
product consumes as a deliverable:** Screenplay models and `.play` files, source code,
|
|
16
|
+
generated code, specifications, documentation, migrations, assets, data, build output.
|
|
17
|
+
Those belong in the project's own structure: `Source/` or `src/`, `Documentation/`,
|
|
18
|
+
the product's own folders. A Screenplay model goes where the project's `.play` files
|
|
19
|
+
already are, else under `Source/` or `src/`, else in a `Screenplay/` folder at the
|
|
20
|
+
repository root.
|
|
21
|
+
- **Work records are not exempt from a home.** Plans, handovers and scratch notes go in
|
|
22
|
+
`.ai-work/`, never in `.cratis/`; see [local-work-artifacts.md](./local-work-artifacts.instructions.md).
|
|
23
|
+
- **Never hand-edit installed corpus files under `.cratis/ai/`** in a consuming
|
|
24
|
+
repository; they are managed by the channel that installed them.
|
|
25
|
+
- If a request would put a deliverable under `.cratis/`, put it in the project structure
|
|
26
|
+
instead and say so in one line; do not ask permission to follow this rule.
|
|
27
|
+
- A repository that already keeps a model under `.cratis/screenplay/` keeps working: read
|
|
28
|
+
it where it is, but do not add new deliverables there, and offer to move it to the
|
|
29
|
+
project structure when the user is changing it anyway.
|