@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.
- 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/hooks/scripts/cratis-nuget-pins.txt +6 -0
- package/package/corpus/product-sync.json +7 -3
- package/package/corpus/rules/orleans.md +4 -0
- package/package/corpus/rules/profiles.md +3 -3
- package/package/corpus/skills/cratis-arc-observable-query-http/SKILL.md +17 -0
- package/package/corpus/skills/cratis-arc-observable-query-http/verification.json +12 -0
- package/package/corpus/skills/cratis-arc-react-page/SKILL.md +5 -1
- package/package/corpus/skills/cratis-arc-react-page/verification.json +3 -0
- package/package/corpus/skills/cratis-chronicle-client-kotlin/SKILL.md +2 -2
- package/package/corpus/skills/cratis-components-common-controls/SKILL.md +149 -0
- package/package/corpus/skills/cratis-components-common-controls/verification.json +15 -0
- package/package/corpus/skills/cratis-components-data-surfaces/SKILL.md +185 -0
- package/package/corpus/skills/cratis-components-data-surfaces/verification.json +16 -0
- package/package/corpus/skills/cratis-fundamentals-telemetry/SKILL.md +125 -0
- package/package/corpus/skills/cratis-fundamentals-telemetry/verification.json +15 -0
- package/package/corpus/skills/cratis-orleans-jobs/SKILL.md +115 -0
- package/package/corpus/skills/cratis-orleans-jobs/references/state-machines.md +64 -0
- package/package/corpus/skills/cratis-orleans-jobs/verification.json +20 -0
- 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/sandbox-and-specrunner.md +26 -0
- package/package/corpus/skills/cratis-stage-rendering-and-sandbox/verification.json +3 -0
- package/package/profile-catalog.json +6 -2
- package/package.json +1 -1
|
@@ -166,9 +166,9 @@
|
|
|
166
166
|
}
|
|
167
167
|
],
|
|
168
168
|
"synced": {
|
|
169
|
-
"version": "4.
|
|
169
|
+
"version": "4.144.0",
|
|
170
170
|
"date": "2026-10-12",
|
|
171
|
-
"by": "Cratis/AI#
|
|
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 (`
|
|
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
|
|
251
|
-
the unit of work (
|
|
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
|
+
}
|