@cratis/pi 7.36.1 → 7.38.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 (96) hide show
  1. package/package/corpus/harnesses/copilot/instructions/ai-distribution.instructions.md +109 -0
  2. package/package/corpus/harnesses/copilot/instructions/application-profile.instructions.md +81 -0
  3. package/package/corpus/harnesses/copilot/instructions/capability-is-not-authority.instructions.md +45 -0
  4. package/package/corpus/harnesses/copilot/instructions/code-quality.csharp.instructions.md +92 -0
  5. package/package/corpus/harnesses/copilot/instructions/code-quality.instructions.md +83 -0
  6. package/package/corpus/harnesses/copilot/instructions/code-quality.typescript.instructions.md +90 -0
  7. package/package/corpus/harnesses/copilot/instructions/components.instructions.md +300 -0
  8. package/package/corpus/harnesses/copilot/instructions/concepts.instructions.md +138 -0
  9. package/package/corpus/harnesses/copilot/instructions/cratis-folder-is-configuration.instructions.md +29 -0
  10. package/package/corpus/harnesses/copilot/instructions/csharp.instructions.md +357 -0
  11. package/package/corpus/harnesses/copilot/instructions/dialogs.instructions.md +278 -0
  12. package/package/corpus/harnesses/copilot/instructions/documentation-structure-and-formatting.instructions.md +162 -0
  13. package/package/corpus/harnesses/copilot/instructions/documentation.instructions.md +90 -0
  14. package/package/corpus/harnesses/copilot/instructions/editing-cratis-docs.instructions.md +74 -0
  15. package/package/corpus/harnesses/copilot/instructions/efcore.instructions.md +84 -0
  16. package/package/corpus/harnesses/copilot/instructions/efcore.specs.instructions.md +45 -0
  17. package/package/corpus/harnesses/copilot/instructions/elixir.instructions.md +50 -0
  18. package/package/corpus/harnesses/copilot/instructions/exit-codes-and-wrappers.instructions.md +34 -0
  19. package/package/corpus/harnesses/copilot/instructions/framework.instructions.md +55 -0
  20. package/package/corpus/harnesses/copilot/instructions/frontend-quality.instructions.md +60 -0
  21. package/package/corpus/harnesses/copilot/instructions/frontend-testing.instructions.md +127 -0
  22. package/package/corpus/harnesses/copilot/instructions/git-commits.instructions.md +139 -0
  23. package/package/corpus/harnesses/copilot/instructions/github-actions.instructions.md +87 -0
  24. package/package/corpus/harnesses/copilot/instructions/glossary.instructions.md +63 -0
  25. package/package/corpus/harnesses/copilot/instructions/go-engineering.instructions.md +61 -0
  26. package/package/corpus/harnesses/copilot/instructions/go.instructions.md +116 -0
  27. package/package/corpus/harnesses/copilot/instructions/guards-and-fuses.instructions.md +61 -0
  28. package/package/corpus/harnesses/copilot/instructions/java.instructions.md +138 -0
  29. package/package/corpus/harnesses/copilot/instructions/kotlin.instructions.md +175 -0
  30. package/package/corpus/harnesses/copilot/instructions/local-work-artifacts.instructions.md +56 -0
  31. package/package/corpus/harnesses/copilot/instructions/new-repository-intake.instructions.md +35 -0
  32. package/package/corpus/harnesses/copilot/instructions/orleans.instructions.md +97 -0
  33. package/package/corpus/harnesses/copilot/instructions/profiles.instructions.md +403 -0
  34. package/package/corpus/harnesses/copilot/instructions/pull-requests.instructions.md +176 -0
  35. package/package/corpus/harnesses/copilot/instructions/react.instructions.md +203 -0
  36. package/package/corpus/harnesses/copilot/instructions/reactors.instructions.md +336 -0
  37. package/package/corpus/harnesses/copilot/instructions/rtk.instructions.md +48 -0
  38. package/package/corpus/harnesses/copilot/instructions/specs.csharp.instructions.md +147 -0
  39. package/package/corpus/harnesses/copilot/instructions/specs.instructions.md +133 -0
  40. package/package/corpus/harnesses/copilot/instructions/specs.scenarios.csharp.instructions.md +281 -0
  41. package/package/corpus/harnesses/copilot/instructions/specs.typescript.instructions.md +141 -0
  42. package/package/corpus/harnesses/copilot/instructions/storybook.instructions.md +85 -0
  43. package/package/corpus/harnesses/copilot/instructions/typescript.instructions.md +151 -0
  44. package/package/corpus/harnesses/copilot/instructions/verification-discipline.instructions.md +27 -0
  45. package/package/corpus/harnesses/copilot/instructions/vertical-slices.instructions.md +414 -0
  46. package/package/corpus/harnesses/copilot/instructions/web-fetching.instructions.md +12 -0
  47. package/package/corpus/harnesses/copilot/instructions/writing-correct-examples.instructions.md +57 -0
  48. package/package/corpus/harnesses/copilot/instructions/writing-cratis-docs.instructions.md +72 -0
  49. package/package/corpus/hooks/scripts/cratis-nuget-pins.txt +6 -0
  50. package/package/corpus/product-sync.json +7 -3
  51. package/package/corpus/rules/orleans.md +4 -0
  52. package/package/corpus/rules/profiles.md +3 -3
  53. package/package/corpus/skills/cratis-arc-observable-query-http/SKILL.md +17 -0
  54. package/package/corpus/skills/cratis-arc-observable-query-http/verification.json +12 -0
  55. package/package/corpus/skills/cratis-arc-react-page/SKILL.md +5 -1
  56. package/package/corpus/skills/cratis-arc-react-page/verification.json +3 -0
  57. package/package/corpus/skills/cratis-chronicle-client-kotlin/SKILL.md +2 -2
  58. package/package/corpus/skills/cratis-components-common-controls/SKILL.md +149 -0
  59. package/package/corpus/skills/cratis-components-common-controls/verification.json +15 -0
  60. package/package/corpus/skills/cratis-components-data-surfaces/SKILL.md +185 -0
  61. package/package/corpus/skills/cratis-components-data-surfaces/verification.json +16 -0
  62. package/package/corpus/skills/cratis-fundamentals-telemetry/SKILL.md +125 -0
  63. package/package/corpus/skills/cratis-fundamentals-telemetry/verification.json +15 -0
  64. package/package/corpus/skills/cratis-orleans-jobs/SKILL.md +115 -0
  65. package/package/corpus/skills/cratis-orleans-jobs/references/state-machines.md +64 -0
  66. package/package/corpus/skills/cratis-orleans-jobs/verification.json +20 -0
  67. package/package/corpus/skills/cratis-screenplay-discovery/SKILL.md +11 -6
  68. package/package/corpus/skills/cratis-screenplay-discovery/references/facilitation.md +9 -5
  69. package/package/corpus/skills/cratis-screenplay-discovery/references/human-workshop.md +1 -1
  70. package/package/corpus/skills/cratis-screenplay-discovery/references/plotting-and-handoff.md +28 -4
  71. package/package/corpus/skills/cratis-screenplay-discovery/references/storming-loop.md +2 -1
  72. package/package/corpus/skills/cratis-screenplay-discovery/verification.json +5 -0
  73. package/package/corpus/skills/cratis-screenplay-model-authoring/SKILL.md +23 -29
  74. package/package/corpus/skills/cratis-screenplay-model-authoring/references/mcp-loop.md +15 -1
  75. package/package/corpus/skills/cratis-screenplay-model-authoring/references/model-notes.md +208 -0
  76. package/package/corpus/skills/cratis-screenplay-model-authoring/verification.json +20 -1
  77. package/package/corpus/skills/cratis-screenplay-model-review/SKILL.md +19 -36
  78. package/package/corpus/skills/cratis-screenplay-model-review/references/report-template.md +26 -0
  79. package/package/corpus/skills/cratis-screenplay-model-review/verification.json +7 -0
  80. package/package/corpus/skills/cratis-screenplay-modeling-lifecycle/SKILL.md +14 -16
  81. package/package/corpus/skills/cratis-screenplay-modeling-lifecycle/references/core-rules-digest.md +8 -3
  82. package/package/corpus/skills/cratis-screenplay-modeling-lifecycle/references/handoff-template.md +5 -2
  83. package/package/corpus/skills/cratis-screenplay-modeling-lifecycle/references/intake.md +21 -0
  84. package/package/corpus/skills/cratis-screenplay-modeling-lifecycle/references/reasoning-notes.md +10 -6
  85. package/package/corpus/skills/cratis-screenplay-modeling-lifecycle/references/stop-or-assume.md +32 -1
  86. package/package/corpus/skills/cratis-screenplay-modeling-lifecycle/references/worked-example.md +5 -2
  87. package/package/corpus/skills/cratis-screenplay-modeling-lifecycle/verification.json +5 -0
  88. package/package/corpus/skills/cratis-screenplay-toolchain/SKILL.md +4 -0
  89. package/package/corpus/skills/cratis-screenplay-toolchain/references/screenplay-contract.json +1716 -183
  90. package/package/corpus/skills/cratis-screenplay-toolchain/references/screenplay-contract.version +1 -1
  91. package/package/corpus/skills/cratis-screenplay-toolchain/references/versions.md +40 -4
  92. package/package/corpus/skills/cratis-screenplay-toolchain/verification.json +6 -0
  93. package/package/corpus/skills/cratis-stage-rendering-and-sandbox/references/sandbox-and-specrunner.md +26 -0
  94. package/package/corpus/skills/cratis-stage-rendering-and-sandbox/verification.json +3 -0
  95. package/package/profile-catalog.json +6 -2
  96. package/package.json +1 -1
@@ -166,9 +166,9 @@
166
166
  }
167
167
  ],
168
168
  "synced": {
169
- "version": "4.142.0",
169
+ "version": "4.144.0",
170
170
  "date": "2026-10-12",
171
- "by": "Cratis/AI#597"
171
+ "by": "Cratis/AI#627"
172
172
  },
173
173
  "covers": {
174
174
  "skills": [
@@ -230,7 +230,8 @@
230
230
  },
231
231
  "covers": {
232
232
  "skills": [
233
- "cratis-chronicle-hosting"
233
+ "cratis-chronicle-hosting",
234
+ "cratis-orleans-jobs"
234
235
  ],
235
236
  "rules": [
236
237
  "orleans.md"
@@ -384,6 +385,7 @@
384
385
  "cratis-code-review",
385
386
  "cratis-engineering-csharp-conventions",
386
387
  "cratis-fundamentals-concept",
388
+ "cratis-fundamentals-telemetry",
387
389
  "cratis-fundamentals-type-discovery"
388
390
  ],
389
391
  "rules": [
@@ -411,6 +413,8 @@
411
413
  "skills": [
412
414
  "cratis-arc-react-page",
413
415
  "cratis-components-accessibility",
416
+ "cratis-components-common-controls",
417
+ "cratis-components-data-surfaces",
414
418
  "cratis-components-schema-editor",
415
419
  "cratis-components-stepper-command-dialog",
416
420
  "cratis-components-styling",
@@ -43,6 +43,10 @@ in its resilient storage pipeline.
43
43
 
44
44
  ## Co-host a silo with Cratis.Orleans
45
45
 
46
+ For job/step authoring, stage barriers, recovery, storage scopes and state-machine
47
+ APIs, load **cratis-orleans-jobs** from `cratis/engineering/csharp`. These APIs are
48
+ framework/service guidance; they do not impose application slice architecture.
49
+
46
50
  For service authors, `Cratis.Orleans` supplies hosting, grain serializers, and the
47
51
  shared job system without requiring Chronicle. With `Cratis.Orleans` and
48
52
  `Cratis.Orleans.Storage.MongoDB` 1.16.3, this host uses durable MongoDB membership
@@ -73,8 +73,8 @@ profile includes neither Screenplay nor Stage.
73
73
  |---|---|---|
74
74
  | `cratis/arc` | Arc CQRS framework | `cratis/arc/csharp`, `cratis/arc/java`, `cratis/arc/kotlin` |
75
75
  | `cratis/chronicle` | Chronicle event sourcing engine (does not compose `cratis/chronicle/mcp`) | `cratis/chronicle/compliance`, `cratis/chronicle/csharp`, `cratis/chronicle/elixir`, `cratis/chronicle/java`, `cratis/chronicle/kotlin`, `cratis/chronicle/multi-tenancy`, `cratis/chronicle/typescript`, `cratis/chronicle/web-workbench` |
76
- | `cratis/components` | React component library | (no child profiles) |
77
- | `cratis/fundamentals` | Core primitives (`ConceptAs<T>`, `EventSourceId<T>`) | (no child profiles) |
76
+ | `cratis/components` | React component library, including `cratis-components-common-controls` and `cratis-components-data-surfaces` | (no child profiles) |
77
+ | `cratis/fundamentals` | Core primitives and generated .NET telemetry (`cratis-fundamentals-telemetry`) | (no child profiles) |
78
78
  | `cratis/arc/go` | Experimental Arc.Go HTTP CQRS (root module) | `cratis/language/go`, `cratis/fundamentals/go` |
79
79
  | `cratis/chronicle/client-go` | Experimental standalone Chronicle.Go client | `cratis/language/go`, `cratis/fundamentals/go` |
80
80
  | `cratis/fundamentals/go` | Fundamentals.Go named scalar concepts and codecs | `cratis/language/go` |
@@ -86,7 +86,7 @@ profile includes neither Screenplay nor Stage.
86
86
  | Profile ID | Description | Automatically Includes |
87
87
  |---|---|---|
88
88
  | `cratis/engineering` | Engineering conventions and workflows | `cratis/engineering/core`, `cratis/engineering/csharp`, `cratis/engineering/elixir`, `cratis/engineering/go`, `cratis/engineering/kotlin`, `cratis/engineering/react`, `cratis/engineering/typescript` |
89
- | `cratis/engineering/csharp` | C# engineering conventions | `cratis/engineering/core` |
89
+ | `cratis/engineering/csharp` | C# engineering conventions and framework Orleans jobs (`cratis-orleans-jobs`, alongside `orleans.md`) | `cratis/engineering/core` |
90
90
  | `cratis/engineering/go` | Go framework parity and immutable module releases | `cratis/engineering/core`, `cratis/language/go` |
91
91
  | `cratis/engineering/typescript` | TypeScript engineering conventions | `cratis/engineering/core` |
92
92
  | `cratis/engineering/react` | React engineering conventions | `cratis/engineering/core` |
@@ -136,6 +136,23 @@ Sources at v22.75.0: `Arc.Core/Queries/ObservableQueryHttp.cs`,
136
136
  `ObservableQueryHandler.cs` and
137
137
  [MongoDB observations](https://github.com/Cratis/Arc/blob/v22.75.0/Documentation/backend/csharp/mongodb/observing-collections.md).
138
138
 
139
+ ### Local `cratis` template ports
140
+
141
+ Templates `v1.7.4` still publishes MongoDB on host port `27017` and configures
142
+ `mongodb://localhost:27017` for the app (`Templates/Cratis/docker-compose.yml:3–13`,
143
+ `appsettings.json:35–37`). Its Vite dev server runs on `9000` and proxies `/api`,
144
+ `/.cratis` and `/swagger` to `http://localhost:5000`
145
+ (`Templates/Cratis/.frontend/vite.config.ts:24–39`). A backend started on another
146
+ port needs matching proxy targets; a healthy Chronicle container alone does not
147
+ prove the browser can reach the app or its MongoDB read models.
148
+
149
+ The bundled development MongoDB replica set advertises `localhost:27017`
150
+ (Chronicle `v19.34.0`, `Docker/entrypoint.sh:23–25`). For that setup, merely changing
151
+ Docker's host mapping to another port does not change the advertised member
152
+ address. Keep `27017` available, or deliberately reconfigure the replica set and
153
+ application connection together. These are local template defaults, not Arc
154
+ requirements or production topology advice.
155
+
139
156
  ## What comes back
140
157
 
141
158
  Every transport carries the same `QueryResult` shape:
@@ -0,0 +1,12 @@
1
+ {
2
+ "skill": "cratis-arc-observable-query-http",
3
+ "input": "Our local cratis template query is stuck Loading after changing the MongoDB and backend host ports. Which connections should we check?",
4
+ "fixtures": [],
5
+ "assertions": [
6
+ { "kind": "skill-contains", "value": "Templates `v1.7.4`" },
7
+ { "kind": "skill-contains", "value": "mongodb://localhost:27017" },
8
+ { "kind": "skill-contains", "value": "http://localhost:5000" },
9
+ { "kind": "skill-contains", "value": "does not change the advertised member" },
10
+ { "kind": "skill-contains", "value": "local template defaults, not Arc" }
11
+ ]
12
+ }
@@ -71,7 +71,11 @@ must move focus into itself.
71
71
  `@cratis/components`) above every Cratis component — it carries the locale,
72
72
  the Components-owned labels. Enable `toaster` (or mount one `<Toaster />`)
73
73
  for notifications; without it even `toastCommandResult` displays nothing.
74
- Mount exactly one toaster, not both.
74
+ Mount exactly one toaster, not both. The `cratis` template already mounts
75
+ `<CratisComponentsProvider toaster>` **inside** `<Arc>` at Templates `v1.7.4`
76
+ (`Templates/Cratis/.frontend/main.tsx:12–19`); the missing-toaster/outside-Arc
77
+ workaround for `v1.7.2` no longer applies to that template version. Check an
78
+ older application's entry point rather than adding a second toaster.
75
79
  - The app must import the stylesheets explicitly; components do not import
76
80
  their own CSS:
77
81
 
@@ -12,6 +12,9 @@
12
12
  },
13
13
  { "kind": "skill-contains", "value": "DataPage has no snapshot refresh handle" },
14
14
  { "kind": "skill-contains", "value": "Mount exactly one toaster" },
15
+ { "kind": "skill-contains", "value": "<CratisComponentsProvider toaster>" },
16
+ { "kind": "skill-contains", "value": "Templates `v1.7.4`" },
17
+ { "kind": "skill-contains", "value": "rather than adding a second toaster" },
15
18
  { "kind": "skill-contains", "value": "4.26.4" },
16
19
  { "kind": "skill-contains", "value": "checkbox multi-selection", "file": "references/data-tables.md" },
17
20
  { "kind": "skill-contains", "value": "DataPage never invokes it or refetches", "file": "references/data-page.md" }
@@ -247,8 +247,8 @@ store.eventLog.transactional.appendMany("<id>", listOf(<OtherEvent>()))
247
247
  unitOfWork.commit()
248
248
  ```
249
249
 
250
- `transactional` appends return `Unit`, not an `AppendResult`; the results are on
251
- the unit of work (`transactions/IUnitOfWork.kt:23-101`).
250
+ In a unit of work, `transactional` appends return a placeholder `AppendResult`
251
+ (sequence `-1`); real results are on the unit of work after `commit()`.
252
252
 
253
253
  ## Observing
254
254
 
@@ -0,0 +1,149 @@
1
+ ---
2
+ name: cratis-components-common-controls
3
+ description: Choose and bind Cratis Components ComboBox, ToggleGroup, Tabs, TagGroup and Breadcrumbs with controlled values, query states, keyboard semantics and localizable names. Use for entity pickers, exclusive choices, panel tabs, tag inputs or navigation trails. Do not use for Arc generated command fields, data tables, canvas palettes or theming.
4
+ license: MIT
5
+ ---
6
+
7
+ <!-- Copyright (c) Cratis. All rights reserved. -->
8
+ <!-- Licensed under the MIT license. See LICENSE file in the project root for full license information. -->
9
+
10
+ # Common controlled controls
11
+
12
+ ## Verified product sources
13
+
14
+ All claims and prop shapes are from **Cratis/Components v4.26.4**, source rather than unversioned examples.
15
+
16
+ | Package | Version | Sources |
17
+ | --- | --- | --- |
18
+ | `@cratis/components` | `4.26.4` | `Source/Common/ComboBox.tsx:44-161,223-295`; `ToggleGroup.tsx:21-87,109-145`; `Tabs.tsx:23-79,91-132`; `TagGroup.tsx:49-145,155-260`; `Breadcrumbs.tsx:23-61,81-126` |
19
+ | React | `^19.0.0` peer | `Source/package.json:319-340` |
20
+
21
+ Imports are exported by `Source/Common/index.ts:11-15`; `Source/package.json:95-99` exposes the `Common` subpath. Use **cratis-components-styling** for the provider and CSS setup and **cratis-components-accessibility** for naming/focus. These controls do not replace Arc's generated `InputTextField`/other command fields; use **cratis-arc-react-page** for those bindings.
22
+
23
+ ## Choose the semantic control first
24
+
25
+ | Need | Control | Controlled contract |
26
+ | --- | --- | --- |
27
+ | Pick one option, with search | `ComboBox` | `options: { key, label, description?, disabled? }[]`, `value: string \| null`, `onChange(key)` |
28
+ | Choose one value from a small set | `ToggleGroup` | `options: { value, label, icon?, disabled? }[]`, `value: string`, `onChange(value)` |
29
+ | Reveal a corresponding content panel | `Tabs` | `tabs: { id, label, content, disabled? }[]`, `value: string`, `onChange(id)` |
30
+ | Add/remove string values | `TagGroup` | `value: string[]`, `onChange(nextValues)` |
31
+ | Show where the page sits in navigation | `Breadcrumbs` | `items: { label, href?, onNavigate? }[]`, outermost first |
32
+
33
+ `ToggleGroup` is a single-selection value choice with radio-group semantics and one Tab stop. `Tabs` supplies real panels and tab/panel relationships. Do not use panel-less tabs as a fancy radio group (`ToggleGroup.tsx:78-87`, `Tabs.tsx:70-79`).
34
+
35
+ ## 1. Bind a picker, not its display text
36
+
37
+ A controlled presentation component; the caller owns the selected key and supplies approved/localized text. `inputValue` is a separate search draft, not the selected value.
38
+
39
+ ```tsx
40
+ // Copyright (c) Cratis. All rights reserved.
41
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
42
+
43
+ import { ComboBox, type ComboBoxOption } from '@cratis/components/Common';
44
+
45
+ interface AccountPickerProps {
46
+ options: ComboBoxOption[];
47
+ value: string | null;
48
+ onChange: (key: string | null) => void;
49
+ label: string;
50
+ loading: boolean;
51
+ loadingMessage: string;
52
+ emptyMessage: string;
53
+ }
54
+
55
+ export const AccountPicker = (props: AccountPickerProps) => (
56
+ <ComboBox
57
+ options={props.options}
58
+ value={props.value}
59
+ onChange={props.onChange}
60
+ aria-label={props.label}
61
+ loading={props.loading}
62
+ loadingMessage={props.loadingMessage}
63
+ emptyMessage={props.emptyMessage}
64
+ openOnFocus
65
+ />
66
+ );
67
+ ```
68
+
69
+ The host loads options; ComboBox never fetches them. For server-filtered results, supply `inputValue`, `onInputChange` and `filter='none'`; the caller debounces/cancels requests and discards stale answers. Local `filter` defaults to `'contains'`; `'startsWith'` is also supported. Explicit open/focus shows all options rather than filtering to the selected label (`ComboBox.tsx:170-177,223-266`).
70
+
71
+ Useful props (`:106-161`):
72
+
73
+ - `failure: ReactNode` replaces options with failure content. `loading` shows `loadingMessage`; `emptyMessage` is for a genuine empty result. Do not map denied/failed loads to an empty list without an explanation.
74
+ - `optionLayout='inline' | 'stacked'` controls secondary `description` placement.
75
+ - `action={{ label, onAction }}` offers a footer action receiving the current input text. It **does not select or create an option for you**. The handler may return a promise, but the component fires it without awaiting; the caller owns busy/error handling (`:268-287`).
76
+ - `required`, `invalid`, `errorMessage`, `description`, `disabled`, `readOnly`, `name` and `id` cover field state. `id` belongs to the input for `<label htmlFor>`. `name` carries the selected key, not search text (`:133-156`).
77
+
78
+ ## 2. Choose a value or a panel
79
+
80
+ JSX binding excerpts inside an existing view. They assume caller-owned string values, change callbacks, and localized `labels` with the named properties:
81
+
82
+ ```tsx
83
+ import { ToggleGroup, Tabs } from '@cratis/components/Common';
84
+
85
+ <ToggleGroup
86
+ aria-label={labels.viewMode}
87
+ value={viewMode}
88
+ onChange={changeViewMode}
89
+ options={[
90
+ { value: 'compact', label: labels.compact },
91
+ { value: 'expanded', label: labels.expanded }
92
+ ]}
93
+ />
94
+
95
+ <Tabs
96
+ aria-label={labels.details}
97
+ value={selectedTab}
98
+ onChange={changeTab}
99
+ tabs={[
100
+ { id: 'overview', label: labels.overview, content: overview },
101
+ { id: 'history', label: labels.history, content: history }
102
+ ]}
103
+ />
104
+ ```
105
+
106
+ `ToggleGroup.onChange` receives a string, not a DOM event; selecting the already-selected value is ignored. `Tabs.onChange` also receives an ID string. Both offer `disabled`, accessible labeling, `className` and typed `pt` parts. ToggleGroup additionally exposes `invalid` and `aria-describedby` (`ToggleGroup.tsx:52-76,116-121`; `Tabs.tsx:51-68`). Do not infer route navigation from changing a tab; its job is revealing content.
107
+
108
+ ## 3. Add tags without confusing display with read-only
109
+
110
+ JSX excerpt in a view with caller-owned `tags: string[]`, `changeTags`, and localized labels:
111
+
112
+ ```tsx
113
+ import { TagGroup } from '@cratis/components/Common';
114
+
115
+ <TagGroup
116
+ aria-label={labels.tags}
117
+ value={tags}
118
+ onChange={changeTags}
119
+ separators={[',', ';']}
120
+ removeLabel={tag => labels.removeTag.replace('{tag}', tag)}
121
+ />
122
+ ```
123
+
124
+ TagGroup defaults to `editable=true`, comma separators and `allowDuplicates=false`. Enter or separators commit trimmed nonempty values; paste splits on separators. Empty-entry Backspace removes the last tag (`TagGroup.tsx:94-109,127-145,190-260`).
125
+
126
+ **`editable=false` only removes the adding input; existing tags remain removable.** Use `disabled` when removal must also be unavailable, or a display-only surface for noninteractive tags. Supply `removeLabel(value)` for localized accessible names. `removeIcon` overrides that group's glyph (ahead of provider `icons.remove`) without changing the button's name or keyboard behavior (`TagGroup.tsx:54-91,155-190`). Prefer unique values: removal is value-keyed, so this is not an identity-preserving collection editor (`:139-140`).
127
+
128
+ ## 4. Build the breadcrumb trail from actual navigation
129
+
130
+ JSX excerpt in a view whose `labels` are localized:
131
+
132
+ ```tsx
133
+ import { Breadcrumbs } from '@cratis/components/Common';
134
+
135
+ <Breadcrumbs
136
+ aria-label={labels.location}
137
+ items={[
138
+ { label: labels.home, href: '/' },
139
+ { label: labels.accounts, href: '/accounts' },
140
+ { label: labels.currentAccount }
141
+ ]}
142
+ />
143
+ ```
144
+
145
+ Earlier entries can supply `href` or router-owned `onNavigate`. The last item is current: its destination/callback is suppressed and React Aria supplies `aria-current='page'`. Separators are decorative; `separator` accepts any React node (`Breadcrumbs.tsx:97-126`). Supply a distinct landmark name if a page has multiple trails. Breadcrumbs do not discover routes or enforce access.
146
+
147
+ ## Verify the binding
148
+
149
+ Test caller state changes using the real payload shapes (key/string/string array), not invented `{ value }` events. Exercise null selection, query failure, disabled options, panel content, tag removal and the final breadcrumb. Browser-check keyboard/focus and accessible names. `pt` keys belong to each exported `*Parts` interface; style those parts rather than overriding internal React Aria markup. Do not add undocumented props to work around a failed interaction.
@@ -0,0 +1,15 @@
1
+ {
2
+ "skill": "cratis-components-common-controls",
3
+ "input": "Choose between a value toggle and panel tabs, wire a nullable entity picker and disable tag editing correctly.",
4
+ "fixtures": [],
5
+ "assertions": [
6
+ { "kind": "skill-contains", "value": "@cratis/components/Common" },
7
+ { "kind": "skill-contains", "value": "ComboBoxOption" },
8
+ { "kind": "skill-contains", "value": "inputValue" },
9
+ { "kind": "skill-contains", "value": "radio-group semantics" },
10
+ { "kind": "skill-contains", "value": "content: overview" },
11
+ { "kind": "skill-contains", "value": "existing tags remain removable" },
12
+ { "kind": "skill-contains", "value": "removeLabel" },
13
+ { "kind": "skill-contains", "value": "aria-current='page'" }
14
+ ]
15
+ }
@@ -0,0 +1,185 @@
1
+ ---
2
+ name: cratis-components-data-surfaces
3
+ description: Compose Cratis Components PivotViewer exploration, TimeMachine historical snapshots, Canvas pan/zoom boards and public Chat conversation/sidebar surfaces. Use when a table is not the right presentation, when supplying Pixi-backed content, or when binding host-owned chat data and actions. Do not use for DataPage lists, backend event replay, chat persistence, canvas tool palettes or Scene authoring.
4
+ license: MIT
5
+ ---
6
+
7
+ <!-- Copyright (c) Cratis. All rights reserved. -->
8
+ <!-- Licensed under the MIT license. See LICENSE file in the project root for full license information. -->
9
+
10
+ # Data beyond a table
11
+
12
+ ## Verified product sources
13
+
14
+ All API and behavior claims below use **Cratis/Components v4.26.4**.
15
+
16
+ | Package | Version | Source |
17
+ | --- | --- | --- |
18
+ | `@cratis/components` | `4.26.4` | `Source/PivotViewer/types.ts:67-112,145-187`; `Source/TimeMachine/{TimeMachine.tsx:19-65,types.ts:8-34}`; `Source/Canvas/{Canvas.tsx:38-104,CanvasItem.tsx:8-38,CanvasHandle.ts:7-21}`; `Source/Chat/{index.ts:4-40,ChatConversation.tsx:78-180,ChatSidebar.tsx:86-203}` |
19
+ | `pixi.js` | `^8.20.0` optional peer | `Source/package.json:319-340`; required when using Canvas or PivotViewer |
20
+ | React | `^19.0.0` peer | `Source/package.json:319-340` |
21
+
22
+ Subpath exports: `Source/package.json:60-69,155-159,178-182`; each area's `index.ts` exports the types used below. Load **cratis-components-styling** for CSS/provider setup and **cratis-components-accessibility** for localized names. For tabular queries use **cratis-arc-react-page**; for a board tool palette use **cratis-components-toolbar**.
23
+
24
+ These are **presentation contracts**. The host owns loading, access control, data acquisition and persistence. A historical slider is not event replay; a chat send callback is not a durable write.
25
+
26
+ ## Choose the surface
27
+
28
+ | Surface | Fit | Avoid assuming |
29
+ | --- | --- | --- |
30
+ | `PivotViewer` | Faceted card exploration of an already-loaded collection | Server paging, JSX card rendering or controlled selection |
31
+ | `TimeMachine` | Supplied historical snapshots and event descriptions | Fetching history, rebuilding a read model or a controlled current index |
32
+ | `Canvas` | A pan/zoom board combining DOM items and optional Pixi objects | That moving the camera persists item positions |
33
+ | `ChatConversation` / `ChatSidebar` | Host-owned messages, topic selection and actions | Automatic message persistence, authorization or topic naming |
34
+
35
+ Keep Canvas/PivotViewer behind their subpath imports so ordinary pages do not acquire a Pixi import edge. Install/deduplicate the optional `pixi.js` peer for these areas, including a DOM-only Canvas: `Canvas.tsx:176` initializes Pixi through `usePixiApplication`. Do not rely on the word "optional" to omit it when importing those areas.
36
+
37
+ ## 1. PivotViewer: data and text cards, not React cards
38
+
39
+ `data`, `dimensions` and `cardRenderer` are required. Each dimension has `key`, `label`, `getValue`; optional `formatValue` and `sort` control group presentation. **`cardRenderer` returns structured text, not JSX**. `detailRenderer(item, onClose)` may return a React node (`PivotViewer/types.ts:67-78,145-163`).
40
+
41
+ This component accepts loaded domain data; its labels come from the host's localization layer:
42
+
43
+ ```tsx
44
+ // Copyright (c) Cratis. All rights reserved.
45
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
46
+
47
+ import { PivotViewer } from '@cratis/components/PivotViewer';
48
+
49
+ interface CatalogItem {
50
+ id: string;
51
+ name: string;
52
+ category: string;
53
+ }
54
+
55
+ interface CatalogExplorerProps {
56
+ items: CatalogItem[];
57
+ categoryLabel: string;
58
+ loadingLabel: string;
59
+ isLoading: boolean;
60
+ }
61
+
62
+ export const CatalogExplorer = (props: CatalogExplorerProps) => (
63
+ <PivotViewer<CatalogItem>
64
+ data={props.items}
65
+ dimensions={[{ key: 'category', label: props.categoryLabel, getValue: item => item.category }]}
66
+ getItemId={item => item.id}
67
+ cardRenderer={item => ({ title: item.name, values: [item.category] })}
68
+ searchFields={[item => item.name]}
69
+ isLoading={props.isLoading}
70
+ loadingLabel={props.loadingLabel}
71
+ />
72
+ );
73
+ ```
74
+
75
+ Other substantial props (`types.ts:93-112,145-185`):
76
+
77
+ - `filters` accepts keyed facets with `label` and `getValue`. Optional `options` are `{ key, label, value, count }`; otherwise they are computed from data. Numeric `type='number'` supports a range/histogram with `buckets`; `renderEditor` replaces a facet editor.
78
+ - `defaultDimensionKey` initializes grouping. Group/filter/selection state is internal; there is no `selectedItem`/`onSelectedItemChange` prop at this tag (`PivotViewer.tsx:54-112`).
79
+ - `searchFields` are property accessors. Use direct property paths, not computed expressions: the implementation extracts paths from accessor source (`types.ts:23-29`, `utils/search.ts`).
80
+ - `emptyContent`, `isLoading`, `loadingLabel`, `labels` and `colors` control presentation. Use the exported `PivotViewerLabels` shape for localization; do not put DOM markup in a text-card result.
81
+
82
+ Bound the dataset in the host. A worker and virtualized rendering do not make loading an unbounded collection a safe query shape (`PivotViewer.tsx:152-165`). Test actual browser rendering, resizing, zoom and keyboard behavior; a server-rendered shell does not exercise Pixi.
83
+
84
+ ## 2. TimeMachine: prepare history before rendering
85
+
86
+ `Version` is `{ id: string, timestamp: Date, label: string, content: ReactNode, events?: Event[] }`. `Event` is `{ sequenceNumber: number, type: string, occurred: Date, content: Record<string, unknown> }`; it is a presentation DTO, not a Chronicle client object (`TimeMachine/types.ts:8-34`). Supply versions in the intended history order.
87
+
88
+ Binding excerpt for an existing view with `versions: Version[]`, a caller-owned `onVersionSelected(index)` callback and translated `timelineLabels: Partial<TimeMachineLabels>`:
89
+
90
+ ```tsx
91
+ import { TimeMachine } from '@cratis/components/TimeMachine';
92
+
93
+ <TimeMachine
94
+ versions={versions}
95
+ currentVersionIndex={0}
96
+ onVersionChange={onVersionSelected}
97
+ labels={timelineLabels}
98
+ />
99
+ ```
100
+
101
+ **`currentVersionIndex` is initial-only**. Later prop changes do not replace internal selection. `onVersionChange(index)` reports timeline/wheel/previous/next selection; `scrollSensitivity` defaults to 50. The component offers read-model and event views over supplied data (`TimeMachine.tsx:19-65,73-125,133-145`). Do not promise an externally controlled scrubber or backend replay.
102
+
103
+ Load only authorized, redacted history. The presence of an event payload in this display is not permission to reveal it, and snapshot content is host-supplied React content. Exercise both modes and localization with the real DTOs.
104
+
105
+ ## 3. Canvas: distinguish camera, DOM items and Pixi data
106
+
107
+ DOM content belongs in `Canvas` children. Position measured items with `CanvasItem x/y`, optional stable `id`, and optional `zIndex` on the item root (nested z-index cannot escape its stacking context). The item registry feeds minimap measurements (`CanvasItem.tsx:8-42,65-95`).
108
+
109
+ ```tsx
110
+ // Copyright (c) Cratis. All rights reserved.
111
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
112
+
113
+ import { Canvas, CanvasItem } from '@cratis/components/Canvas';
114
+
115
+ export const Board = () => (
116
+ <Canvas style={{ height: '32rem' }} showMinimap minimapWorldWidth={1600} minimapWorldHeight={1200}>
117
+ <CanvasItem id='first-card' x={100} y={80}>
118
+ <article>First card</article>
119
+ </CanvasItem>
120
+ </Canvas>
121
+ );
122
+ ```
123
+
124
+ The literal card text is illustrative; use the host's approved/localized content. Give the board visible dimensions. For Pixi items, supply `items: T[]` extending `CanvasItemData` and `renderItem(item): PIXI.Container`; `onItemPointerDown` reports item activation, `onReady` receives `CanvasContext` (`Canvas.tsx:38-50`). Do not return React nodes from `renderItem`.
125
+
126
+ Camera/controls contract (`Canvas.tsx:51-104,119-160`, `CanvasHandle.ts:7-21`):
127
+
128
+ - `initialZoom` and `initialPan` are initial values, not a controlled camera. `onTransformChange(zoom, pan)` reports movement. Use `onHandleReady` and `CanvasHandle.smoothPanToWorld`/`smoothPanZoomToWorld` for navigation; `getContainerRect` and `getItemBounds` measure it.
129
+ - `minZoom`/`maxZoom` default to 0.1/5. `showControls` defaults true; `showMinimap` false. `minimapItems` can replace registry-derived rectangles.
130
+ - `controlsPlacement` is `'bottom-left' | 'bottom-right'`; `controlsLabels` and `controlsIcons` are typed override objects. `controlsFollowViewportInsets` defaults true, stepping aside by `--canvas-viewport-left/right`. Set false when the host already offsets the whole canvas, or the inset is applied twice.
131
+ - `readOnly` absorbs content interaction but leaves pan/zoom available. `backgroundDragPans=false` lets a product own background drag selection while wheel/middle-button/touch panning remain available.
132
+
133
+ Persist item edits in the host, not in camera callbacks. Browser-test gestures and dimensions. For jsdom tests provide a `ResizeObserver` substitute where required; Pixi integration still needs a real browser signal.
134
+
135
+ ## 4. Public Chat: bind the host's messages and outcomes
136
+
137
+ The public `@cratis/components/Chat` exports **`ChatConversation`**, **`ChatSidebar`**, **`ChatSidebarForObservableQueries`**, **`ChatTopicList`** and their types. It does **not** export the Kit `Chat` component or expose a `Chat/Kit` package subpath (`Source/Chat/index.ts:4-40`, `Source/package.json`). **`Chat` is exported through `@cratis/components/Canvas`** via the Kit barrel (`Source/Canvas/index.ts:37-40`, `Source/Chat/Kit/index.ts:4-26`). Prefer the non-spatial Chat subpath for ordinary host-data conversations; importing Canvas brings its Pixi edge.
138
+
139
+ The Kit `Chat` has a different contract: required `messages`, `onSend(text)` and `onClose`; optional `variant`, `typingAuthors`, `currentUserId`, `onReact(messageId, emoji)`, `onAct(messageId)`, `canAct` and nested `labels`. Its messages use the Kit `ChatMessage` shape, not the public Chat subpath's message type. Reactions need both `currentUserId` and `onReact`. An optional `id` additionally publishes `ChatMessageAdded` over an available Arc messenger at send time, not after persistence succeeds (`Source/Chat/Kit/Chat.tsx:58-135`, `ChatMessage.ts`). Never count that notification as durable delivery, or mix `onSend` with the public conversation's `onSendMessage`.
140
+
141
+ The public `ChatMessage` has `id`, `topicId`, `authorId`, `body`, `timestamp: Date`, optional `mentions` and `metadata`. Identifiers use `ChatIdentifier`; resolve authors through `authorOf`, not an invented `authorName` property (`ChatMessage.ts:17-37`, `ChatConversation.tsx:78-127`).
142
+
143
+ Complete presentation wrapper using the real public types:
144
+
145
+ ```tsx
146
+ // Copyright (c) Cratis. All rights reserved.
147
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
148
+
149
+ import {
150
+ ChatConversation, ChatStatus,
151
+ type ChatConversationLabels, type ChatMention, type ChatMessage
152
+ } from '@cratis/components/Chat';
153
+
154
+ interface ConversationProps {
155
+ messages: ChatMessage[];
156
+ status: ChatStatus;
157
+ labels: ChatConversationLabels;
158
+ send: (body: string, mentions: ChatMention[]) => void;
159
+ }
160
+
161
+ export const Conversation = (props: ConversationProps) => (
162
+ <ChatConversation
163
+ messages={props.messages}
164
+ status={props.status}
165
+ labels={props.labels}
166
+ onSendMessage={props.send}
167
+ />
168
+ );
169
+ ```
170
+
171
+ `onSendMessage` reports the trimmed body and mentions; persistence is the host's responsibility. Supply fresh authorized data after command success. Handle failure visibly rather than adding a fabricated message that looks saved.
172
+
173
+ `ChatStatus.Ready/Loading/Failed/Unauthorized` distinguish query outcomes. Existing messages remain during loading/failure; unauthorized replaces them and disables the composer (`ChatStatus.ts:5-14`, `ChatConversation.tsx:85`). This is UI behavior, not backend enforcement.
174
+
175
+ Additional public contracts:
176
+
177
+ - `actions` are typed `ChatMessageAction<TMessage>[]`; `renderMessageExtra(message)` supplies reaction/failure/detail content under the body. `mentionCandidates` or `resolveMentionCandidates` supplies available mentions (`ChatConversation.tsx:121-180`).
178
+ - `ChatSidebar` requires `open`, `onClose`, `topics`, `messages`, and `onSendMessage(topicId, body, mentions)`. `topicActions` is separate from inherited message `actions`; `renderHeaderActions(openTopic)` belongs to the sidebar (`ChatSidebar.tsx:86-163`).
179
+ - `selectedTopicId` is controlled when provided: ID opens a topic, null shows the list, omission uses internal selection. `onTopicSelected` reports selection. `topicsStatus` and `messagesStatus` are separate; do not show one topic's cached messages while another topic is loading (`:123-142`).
180
+ - `onStartTopic` returns a created identifier or undefined, optionally in a promise. `onRequestTopicName` asks the host to name an unnamed topic after its first message. Neither creates/saves/names anything on its own (`:144-180`).
181
+ - The sidebar defaults non-modal, right edge, `24rem`; `position`, `width`, `modal`, `labels`, and typed `pt` customize it (`:182-203`).
182
+
183
+ ## Verify the surface, not only the props
184
+
185
+ Test host orchestration separately from Components: loading/failed/denied vs empty, stable IDs, data ordering, and persistence errors. Browser-check interaction/focus and all visible/localized labels. Do not infer remote fetch, tenant isolation, history reconstruction or message delivery from a rendered surface or a passing type check.
@@ -0,0 +1,16 @@
1
+ {
2
+ "skill": "cratis-components-data-surfaces",
3
+ "input": "Present loaded faceted data, history snapshots, a pan/zoom board and host-owned chat without inventing persistence.",
4
+ "fixtures": [],
5
+ "assertions": [
6
+ { "kind": "skill-contains", "value": "cardRenderer` returns structured text, not JSX" },
7
+ { "kind": "skill-contains", "value": "currentVersionIndex` is initial-only" },
8
+ { "kind": "skill-contains", "value": "CanvasItem" },
9
+ { "kind": "skill-contains", "value": "controlsFollowViewportInsets" },
10
+ { "kind": "skill-contains", "value": "pixi.js" },
11
+ { "kind": "skill-contains", "value": "Chat` is exported through `@cratis/components/Canvas`" },
12
+ { "kind": "skill-contains", "value": "onSendMessage" },
13
+ { "kind": "skill-contains", "value": "renderHeaderActions(openTopic)" },
14
+ { "kind": "skill-contains", "value": "not backend enforcement" }
15
+ ]
16
+ }