@nomosui/react 0.8.0 → 0.8.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.
Files changed (2) hide show
  1. package/SKILL.md +185 -192
  2. package/package.json +1 -1
package/SKILL.md CHANGED
@@ -1,171 +1,166 @@
1
1
  ---
2
2
  name: nomos
3
- description: Choisir la brique du design system Nomos pour un usage donné — atomes (Badge, Chip, Meter, Freshness, Table, Button, Input, champ, blocs de formulaire), coquille de table, vocabulaire de tons, tokens et densité — et interroger le catalogue par le serveur MCP local, y compris les vues `ui://` rendues en conversation.
3
+ description: Pick the right Nomos design-system brick for a given use — atoms (Badge, Chip, Meter, Freshness, Table, Button, Input, field, form blocks), table shell, tone vocabulary, tokens and density — and query the catalogue through the local MCP server, including `ui://` views rendered in conversation.
4
4
  ---
5
5
 
6
- # Le design system Nomos
7
-
8
- Un **cœur app-agnostique** (la racine de ce dépôt) : il ne connaît ni l'état d'une
9
- app, ni son i18n, ni son router, ni ses query-params. Dès qu'il faut nommer un produit
10
- pour expliquer une brique, elle appartient à l'app, pas au cœur.
11
-
12
- ## Quelle brique prendre
13
-
14
- ### Qualifier, situer
15
-
16
- - **Qualifier d'un mot court** (un statut, un domaine) → `Badge`. Le ton vient d'une
17
- classe de `toneClasses`, jamais d'un mot produit.
18
- - **Qualifier de façon retirable** (un filtre actif) → `Chip`, avec `onRemove` ; sans
19
- action, un `Chip` nu. Un statut qu'on ne retire pas reste un `Badge`.
20
- - **Situer une valeur sur une échelle**, avec un seuil → `Meter` (barre large) ou
21
- `CompactMeter` (cellule dense). Le texte du seuil est injecté par l'appelant.
22
- - **Dire l'âge d'une donnée** → `Freshness` ; l'âge est déjà formaté, `stale` est décidé
23
- par l'appelant.
24
- - **Montrer l'avancement d'une tâche** (un total, sans seuil) → `ProgressBar`, sémantique
25
- `progressbar`.
26
- - **Mettre un chiffre en avant** → `Counter` (suffixe et libellé fournis par l'app).
27
- - **Recueillir ou montrer une note** → `Rating` : interactif seulement si l'app fournit
28
- `onValueChange`, sinon un affichage.
29
- - **Lister des événements datés** → `Timeline` (frise, présentation seule ; le format de
30
- la date vient de l'app). Un point porte un état — `done` (défaut) ou `past` (grisé) —
31
- et un `past` exige un `stateLabel` fourni par l'app : la couleur seule ne suffit pas.
32
- - **Nommer un contrôle iconique, glisser une aide courte** → `Tooltip` en famille (un
33
- `TooltipProvider` autour de plusieurs, `Tooltip`, `TooltipTrigger`, `TooltipContent`).
34
- Jamais pour une information essentielle : elle n'est ni au clavier seul ni au tactile.
35
- - **Représenter une personne par son image** → `Avatar` (le texte alternatif et le repli
36
- viennent de l'app ; natif `<img>` + `onError`, sans dépendance).
37
- - **Coiffer un titre d'une accroche courte** → `Kicker` (un libellé en majuscules, un point
38
- de ton optionnel via `dot` ; le texte est injecté par l'appelant).
39
-
40
- ### Agir, saisir
41
-
42
- - **Agir** → `Button` (`variant` = le ton, `size` = la densité ; `asChild` pour poser le
43
- style sur un lien).
44
- - **Naviguer vers une URL** → `Link` (un `<a>` ; `href` et libellé injectés, le cœur ne
45
- porte aucun routing ; `asChild` pour poser le style sur un composant de routing de l'app).
46
- - **Copier un texte** → `CopyButton` (`value` à copier, `label`/`copiedLabel` et `icon`
47
- injectés ; le libellé transitoire revient seul après ~2 s).
48
- - **Saisir une ligne** → `Input` ; **un nombre borné** → `NumberField` (`step`, `min`,
49
- `max` du natif) ; **chercher avec icône et effacement** → `SearchField` ; **plusieurs
50
- lignes** → `Textarea`.
51
- - **Cocher** → `Checkbox` (option indépendante) ; **basculer tout de suite** → `Switch` ;
52
- **choisir une seule option parmi quelques-unes** → `RadioGroup` ; **une bascule
53
- ponctuelle** (mode, filtre) → `Toggle`, **un segment** → `ToggleGroup`. Tous sont
54
- contrôlés par props : l'état reste dans l'app.
55
- - **Choisir une valeur unique parmi une liste d'options** → `Select` en parts
56
- (`SelectTrigger`, `SelectValue`, `SelectContent`, `SelectItem` ; les libellés et les
57
- valeurs viennent de l'app). Pour des actions, c'est un `DropdownMenu`.
58
-
59
- ### Structurer
60
-
61
- - **Une surface** (titre, corps, pied) → les parts de `Card`.
62
- - **L'en-tête d'un site** (marque, navigation, actions) → `Navbar` : une barre sticky en
63
- haut, bordure basse, fond du cœur. Les slots `brand`, `nav` et `actions` sont injectés
64
- par l'app — aucun `href` ni mot produit dans le cœur (ADR 0030).
65
- - **Un état vide, ou une panne à nommer** → `EmptyState` (la carte centrée ; le `detail`
66
- brut nomme la vraie panne, plutôt qu'un zéro silencieux).
67
- - **Un pied de page** (marque, rangée de liens, ligne légale) → `Footer` : des emplacements
68
- injectés (`brand`, `links`, `legal`), sans routing ni libellé propre au cœur.
69
- - **Une information inline** qui demande l'attention sans bloquer → `Alert` (le ton vient
70
- du cœur ; `onClose` va avec `closeLabel`).
71
- - **Séparer deux contenus** → `Separator` ; **tenir la place d'un contenu qui charge** →
6
+ # The Nomos design system
7
+
8
+ An **app-agnostic core** (the root of this repo): it knows neither an app's state, nor its
9
+ i18n, nor its router, nor its query params. As soon as a product word is needed to explain a
10
+ brick, it belongs to the app, not the core.
11
+
12
+ ## Which brick to pick
13
+
14
+ ### Qualify, situate
15
+
16
+ - **Qualify with a short word** (a status, a domain) → `Badge`. The tone comes from a
17
+ `toneClasses` class, never from a product word.
18
+ - **Qualify removably** (an active filter) → `Chip`, with `onRemove`; with no action, a bare
19
+ `Chip`. A status you can't remove stays a `Badge`.
20
+ - **Place a value on a scale**, with a threshold → `Meter` (wide bar) or `CompactMeter`
21
+ (dense cell). The threshold text is injected by the caller.
22
+ - **State the age of a datum** → `Freshness`; the age is already formatted, `stale` is
23
+ decided by the caller.
24
+ - **Show a task's progress** (a total, no threshold) → `ProgressBar`, `progressbar`
25
+ semantics.
26
+ - **Highlight a number** → `Counter` (suffix and label provided by the app).
27
+ - **Collect or show a rating** → `Rating`: interactive only if the app provides
28
+ `onValueChange`, otherwise a display.
29
+ - **List dated events** → `Timeline` (a rail, presentation only; the date format comes from
30
+ the app). A point carries a state — `done` (default) or `past` (greyed) — and a `past`
31
+ requires a `stateLabel` provided by the app: colour alone is not enough.
32
+ - **Name an icon control, tuck away a short hint** → `Tooltip` as a family (a
33
+ `TooltipProvider` around several, `Tooltip`, `TooltipTrigger`, `TooltipContent`). Never for
34
+ essential information: it is neither keyboard-only nor touch accessible.
35
+ - **Represent a person by their image** → `Avatar` (alt text and fallback come from the app;
36
+ native `<img>` + `onError`, no dependency).
37
+ - **Cap a title with a short hook** → `Kicker` (an uppercase label, an optional tone dot via
38
+ `dot`; the text is injected by the caller).
39
+
40
+ ### Act, input
41
+
42
+ - **Act** → `Button` (`variant` = the tone, `size` = the density; `asChild` to put the style
43
+ on a link).
44
+ - **Navigate to a URL** → `Link` (an `<a>`; `href` and label injected, the core carries no
45
+ routing; `asChild` to put the style on an app routing component).
46
+ - **Copy a text** → `CopyButton` (`value` to copy, `label`/`copiedLabel` and `icon`
47
+ injected; the transient label returns on its own after ~2 s).
48
+ - **Input one line** → `Input`; **a bounded number** → `NumberField` (`step`, `min`, `max`
49
+ from the native element); **search with an icon and a clear button** → `SearchField`;
50
+ **several lines** → `Textarea`.
51
+ - **Check** → `Checkbox` (independent option); **toggle right away** → `Switch`; **choose a
52
+ single option among a few** → `RadioGroup`; **a one-off toggle** (mode, filter) → `Toggle`,
53
+ **a segment** → `ToggleGroup`. All are controlled by props: the state stays in the app.
54
+ - **Choose a single value from a list of options** → `Select` as parts (`SelectTrigger`,
55
+ `SelectValue`, `SelectContent`, `SelectItem`; labels and values come from the app). For
56
+ actions, it's a `DropdownMenu`.
57
+
58
+ ### Structure
59
+
60
+ - **A surface** (title, body, footer) → the parts of `Card`.
61
+ - **A site header** (brand, navigation, actions) → `Navbar`: a sticky bar at the top, a
62
+ bottom border, the core's background. The `brand`, `nav` and `actions` slots are injected
63
+ by the app — no `href` and no product word in the core (ADR 0030).
64
+ - **An empty state, or a failure to name** → `EmptyState` (the centred card; the raw `detail`
65
+ names the real failure, rather than a silent zero).
66
+ - **A footer** (brand, link row, legal line) → `Footer`: injected slots (`brand`, `links`,
67
+ `legal`), with no routing or core-specific label.
68
+ - **Inline information** that asks for attention without blocking → `Alert` (the tone comes
69
+ from the core; `onClose` goes with `closeLabel`).
70
+ - **Separate two contents** → `Separator`; **hold the place of loading content** →
72
71
  `Skeleton`.
73
- - **Un contenu riche ou interactif dans une surface flottante au clic** → `Popover`
74
- (`PopoverTrigger`, `PopoverContent`, `PopoverAnchor` pour s'ancrer ailleurs). Pour un
75
- texte court au survol, c'est une `Tooltip`.
76
- - **Révéler un détail à la demande** → `Collapsible` (`CollapsibleTrigger`,
77
- `CollapsibleContent`) ; **des sections repliables, une à la fois** → `Accordion`
78
- (`AccordionItem`, `AccordionTrigger`, `AccordionContent`, `type` single/multiple).
79
- - **Basculer entre des vues sœurs** → `Tabs` (`TabsList`, `TabsTrigger`, `TabsContent`) ;
80
- **borner une sous-vue dense dans une hauteur fixe** → `ScrollArea` (purement cosmétique,
81
- le défilement reste natif).
82
- - **Prévenir sans bloquer** → `Toast` via `useToast().show({ message })` (la file et
83
- l'auto-dismiss vivent dans `ToastProvider`).
84
- - **Regrouper des actions derrière un déclencheur compact** → `DropdownMenu` en parts
85
- (`DropdownMenuTrigger`, `DropdownMenuContent`, `DropdownMenuItem`, à cocher, radio,
86
- sous-menu ; les libellés et les actions viennent de l'app). Pour choisir une valeur de
87
- formulaire, c'est un `Select`.
88
- - **Demander une décision dans une modale centrée** → `Dialog` (déclencheur, titre,
89
- description, corps, pied ; le libellé de fermeture vient de l'app). L'empilement, le
90
- voile et le mouvement viennent des tokens, jamais d'une valeur en dur.
91
- - **Confirmer une action destructive** → `AlertDialog` (`AlertDialogTrigger`,
92
- `AlertDialogContent`, `AlertDialogAction`, `AlertDialogCancel` ; deux issues
93
- explicites). Il ne se ferme pas au clic hors surface.
94
- - **Montrer un contenu ancré à un bord** → `Sheet` en parts (`SheetTrigger`, `SheetContent`
95
- avec `side`, `SheetHeader`, `SheetFooter`) ; `side="bottom"` est le **drawer** — le même
96
- panneau, un côté différent, pas un atome à part.
97
- - **Parcourir une hiérarchie** → `Tree` (navigation, un nœud actif) ou `SelectionTree`
98
- (sélection multiple) ; ouverture et sélection sont contrôlées par props, le focus
99
- clavier (rôle `tree`, flèches) est interne.
100
-
101
- ### Écrire
102
-
103
- - **Titrer une section** → `Heading` : `level` choisit la balise `h1`..`h6` **et** la taille
104
- sémantique (display, title, lead, body, caption, micro). La hiérarchie du document se
105
- décide par le niveau, jamais par la taille.
106
- - **Écrire le texte courant** → `Text` : `size` lit l'échelle sémantique (`lead`, `body`,
107
- `caption`, `micro`), `as` choisit `p` (défaut) ou `span` pour un texte inline. Les tailles
108
- viennent des tokens, jamais d'un utilitaire ad-hoc.
109
- - **Montrer un extrait de code** → `Code` (inline, dans une phrase) ou `CodeBlock` (bloc
110
- scrollable avec bouton de copie ; `code` est le texte copié, `children` le rendu, les
111
- libellés sont injectés). La coloration syntaxique reste à l'app.
112
-
113
- ### Formulaire
114
-
115
- - **Mettre en page** → `Form` (grille de champs + zone d'actions, `columns` pour deux
116
- colonnes). Aucune validation, aucun état, aucun texte.
117
- - **Un emplacement de champ** (libellé, contrôle, aide ou erreur) → `Field` ; **nommer un
118
- contrôle seul** → `Label` (`htmlFor`) ; **regrouper des champs apparentés** →
119
- `Fieldset` (`legend` fourni par l'app). Le message d'erreur est injecté, jamais calculé
120
- par le cœur. Le `Field` associe lui-même le contrôle à son message (`aria-invalid`,
121
- `aria-describedby`) pour les contrôles du cœur : `Input`, `Textarea`, `NumberField`,
122
- `SearchField` et le déclencheur de `Select`.
72
+ - **Rich or interactive content in a floating surface on click** → `Popover`
73
+ (`PopoverTrigger`, `PopoverContent`, `PopoverAnchor` to anchor elsewhere). For a short text
74
+ on hover, it's a `Tooltip`.
75
+ - **Reveal a detail on demand** → `Collapsible` (`CollapsibleTrigger`, `CollapsibleContent`);
76
+ **collapsible sections, one at a time** → `Accordion` (`AccordionItem`, `AccordionTrigger`,
77
+ `AccordionContent`, `type` single/multiple).
78
+ - **Switch between sibling views** → `Tabs` (`TabsList`, `TabsTrigger`, `TabsContent`);
79
+ **bound a dense sub-view to a fixed height** → `ScrollArea` (purely cosmetic, scrolling
80
+ stays native).
81
+ - **Warn without blocking** → `Toast` via `useToast().show({ message })` (the queue and
82
+ auto-dismiss live in `ToastProvider`).
83
+ - **Group actions behind a compact trigger** → `DropdownMenu` as parts
84
+ (`DropdownMenuTrigger`, `DropdownMenuContent`, `DropdownMenuItem`, checkable, radio,
85
+ submenu; labels and actions come from the app). To choose a form value, it's a `Select`.
86
+ - **Ask for a decision in a centred modal** → `Dialog` (trigger, title, description, body,
87
+ footer; the close label comes from the app). Stacking, overlay and motion come from the
88
+ tokens, never from a hard-coded value.
89
+ - **Confirm a destructive action** → `AlertDialog` (`AlertDialogTrigger`,
90
+ `AlertDialogContent`, `AlertDialogAction`, `AlertDialogCancel`; two explicit outcomes). It
91
+ does not close on an outside click.
92
+ - **Show content anchored to an edge** → `Sheet` as parts (`SheetTrigger`, `SheetContent`
93
+ with `side`, `SheetHeader`, `SheetFooter`); `side="bottom"` is the **drawer** — the same
94
+ panel, a different side, not a separate atom.
95
+ - **Browse a hierarchy** → `Tree` (navigation, one active node) or `SelectionTree`
96
+ (multi-selection); opening and selection are controlled by props, keyboard focus (`tree`
97
+ role, arrows) is internal.
98
+
99
+ ### Write
100
+
101
+ - **Title a section** → `Heading`: `level` chooses the `h1`..`h6` tag **and** the semantic
102
+ size (display, title, lead, body, caption, micro). Document hierarchy is decided by the
103
+ level, never by the size.
104
+ - **Write body text** → `Text`: `size` reads the semantic scale (`lead`, `body`, `caption`,
105
+ `micro`), `as` chooses `p` (default) or `span` for inline text. Sizes come from the tokens,
106
+ never from an ad-hoc utility.
107
+ - **Show a code excerpt** → `Code` (inline, in a sentence) or `CodeBlock` (scrollable block
108
+ with a copy button; `code` is the copied text, `children` the rendering, labels are
109
+ injected). Syntax highlighting stays with the app.
110
+
111
+ ### Form
112
+
113
+ - **Lay out** → `Form` (grid of fields + action area, `columns` for two columns). No
114
+ validation, no state, no text.
115
+ - **A field slot** (label, control, hint or error) → `Field`; **name a control alone** →
116
+ `Label` (`htmlFor`); **group related fields** → `Fieldset` (`legend` provided by the app).
117
+ The error message is injected, never computed by the core. `Field` itself associates the
118
+ control with its message (`aria-invalid`, `aria-describedby`) for the core controls:
119
+ `Input`, `Textarea`, `NumberField`, `SearchField` and the `Select` trigger.
123
120
 
124
121
  ### Tables
125
122
 
126
- - **Une grille dense de lignes** → les parts de `Table` (`Table`, `TableHeader`,
127
- `TableBody`, `TableRow`, `TableHead`, `TableCell`), importables séparément.
128
- - **Filtrer par une dimension** → `FacetFilter` (options, sélection et libellé « effacer »
129
- injectés) ; **rechercher, compter, gérer les colonnes** → `DataTableToolbar` ;
130
- **naviguer dans une page** → `DataTablePagination` (indépendante de la table).
131
- - **Une table pilotée** (colonnes, facettes, tri, pagination, détail de ligne) → la
132
- feature `FacetedDataTable` : elle possède le comportement et le **placement** du détail
133
- (surcouche `overlay` ou dépli `inline`), l'app fournit le contenu et les `labels`.
123
+ - **A dense grid of rows** → the parts of `Table` (`Table`, `TableHeader`, `TableBody`,
124
+ `TableRow`, `TableHead`, `TableCell`), importable separately.
125
+ - **Filter by a dimension** → `FacetFilter` (options, selection and the "clear" label
126
+ injected); **search, count, manage columns** → `DataTableToolbar`; **navigate a page** →
127
+ `DataTablePagination` (independent of the table).
128
+ - **A driven table** (columns, facets, sorting, pagination, row detail) → the
129
+ `FacetedDataTable` feature: it owns the behaviour and the **placement** of the detail
130
+ (`overlay` layer or `inline` expansion), the app provides the content and the `labels`.
134
131
 
135
132
  ### Conversation
136
133
 
137
- - **Brancher une conversation** → la feature `useChatThread({ transport, initialMessages })` :
138
- elle possède la **machine à états** — ajout optimiste du tour, assemblage des deltas,
139
- `send`, `stop`, `retry`/`regenerate`, `replace`, erreur — et **rien du transport**
140
- (ADR 0032). L'app injecte un `ChatTransport` (une fonction `send`) : le cœur n'appelle
141
- aucun modèle, ne connaît aucun endpoint, n'affiche aucune erreur réseau (ADR 0018). Un
142
- message porte un rôle et des **parts** (`text`, `reasoning`, `tool`, `data`) : un signal
143
- structuré est une part de premier ordre, jamais un sentinelle pêché dans la prose.
144
- - **La fenêtre du fil** → `Conversation` : une région `log` vivante ancrée en bas, le
145
- défilement tenu par le cœur, la hauteur par l'app. **Un message** → `Message` : le
146
- placement et le ton du rôle, le contenu en `parts`, et `renderPart` pour que l'app
147
- garde markdown, coloration et artefact métier. **La saisie** → `Composer` : contrôlé par
148
- props (`value` + `onChange`), `Entrée` envoie, `Maj+Entrée` coupe la ligne, garde IME ;
149
- `busy` + `onStop` pour interrompre. **Attendre la réponse** → `TypingIndicator` (libellé
150
- injecté, région `status`).
151
-
152
- ### Tons et tokens
153
-
154
- - **Un ton de statut** → `toneClasses` (`neutral`, `info`, `progress`, `attention`,
155
- `warning`, `danger`, `success`). C'est une **intention**, pas une couleur.
156
- - **Couleurs, espacement, typographie** → les tokens (`tokens.json`, source unique). Le
157
- thème et la densité se posent sur la **racine du rendu** (`data-theme`, `data-density`),
158
- jamais sur `:root`.
159
-
160
- Le tri par défaut est porté par la colonne (`meta.defaultSort`), jamais par le cœur.
161
-
162
- ## L'inventaire
163
-
164
- La liste exacte des briques du catalogue, tenue par test (`src/mcp/skill.test.ts`) : une
165
- brique ajoutée au catalogue sans être ici fait rougir, et une ligne qui n'existe plus
166
- aussi.
167
-
168
- <!-- inventaire : début — tenu par src/mcp/skill.test.ts -->
134
+ - **Wire up a conversation** → the `useChatThread({ transport, initialMessages })` feature:
135
+ it owns the **state machine** — optimistic turn append, delta assembly, `send`, `stop`,
136
+ `retry`/`regenerate`, `replace`, error — and **nothing of the transport** (ADR 0032). The
137
+ app injects a `ChatTransport` (a `send` function): the core calls no model, knows no
138
+ endpoint, shows no network error (ADR 0018). A message carries a role and **parts**
139
+ (`text`, `reasoning`, `tool`, `data`): a structured signal is a first-class part, never a
140
+ sentinel fished out of the prose.
141
+ - **The thread window** → `Conversation`: a live `log` region anchored at the bottom, the
142
+ scrolling held by the core, the height by the app. **A message** → `Message`: the placement
143
+ and tone of the role, the content in `parts`, and `renderPart` so the app keeps markdown,
144
+ highlighting and business artefacts. **The input** → `Composer`: controlled by props
145
+ (`value` + `onChange`), `Enter` sends, `Shift+Enter` breaks the line, IME guard; `busy` +
146
+ `onStop` to interrupt. **Wait for the reply** → `TypingIndicator` (label injected, `status`
147
+ region).
148
+
149
+ ### Tones and tokens
150
+
151
+ - **A status tone** → `toneClasses` (`neutral`, `info`, `progress`, `attention`, `warning`,
152
+ `danger`, `success`). It is an **intention**, not a colour.
153
+ - **Colours, spacing, typography** → the tokens (`tokens.json`, single source). The theme and
154
+ density are set on the **render root** (`data-theme`, `data-density`), never on `:root`.
155
+
156
+ The default sort is carried by the column (`meta.defaultSort`), never by the core.
157
+
158
+ ## The inventory
159
+
160
+ The exact list of catalogue bricks, held by test (`src/mcp/skill.test.ts`): a brick added to
161
+ the catalogue without being here turns red, and a line that no longer exists does too.
162
+
163
+ <!-- inventory: start — kept by src/mcp/skill.test.ts -->
169
164
  accordion
170
165
  alert
171
166
  alert-dialog
@@ -224,44 +219,42 @@ toggle-group
224
219
  tooltip
225
220
  tree
226
221
  typing-indicator
227
- <!-- inventaire : fin -->
222
+ <!-- inventory: end -->
228
223
 
229
- ## Interroger le catalogue
224
+ ## Querying the catalogue
230
225
 
231
- Le serveur MCP local sert le même inventaire que la page de style, en lecture seule :
226
+ The local MCP server serves the same inventory as the style page, read-only:
232
227
 
233
228
  ```sh
234
- npm run mcp # stdio, sans jeton
229
+ npm run mcp # stdio, no token
235
230
  ```
236
231
 
237
- - `list_components { query? }` — quelles briques existent, et laquelle correspond à un mot.
238
- - `get_component { name }` — le contrat complet d'une brique (props, variantes, usages).
239
- - `preview_component { name }` — sa recette de rendu (exemple de props, variantes, usages).
240
- - `list_scenes` — les **scènes composites** disponibles.
241
- - `render_<nom>` / `render_scene_<nom>` — renvoient la recette, **portent les données**
242
- (`props`, optionnel) et **référencent la vue** par `_meta.ui.resourceUri`.
243
-
244
- Les ressources portent les mêmes données que les outils : `nomos://tokens` (l'inventaire
245
- aplati par mode) et `nomos://component/<nom>` (le manifeste).
246
-
247
- ## Le rendu en conversation
248
-
249
- Au-delà du manifeste, chaque brique a une **vue** servie en `ui://nomos/<nom>` — un
250
- document auto-suffisant (`text/html;profile=mcp-app`) que l'hôte rend dans un iframe
251
- sandboxé. Une **scène composite** (`ui://nomos/composite/<nom>`) assemble plusieurs
252
- briques en un écran qui a du sens (un formulaire, une carte de statut). Les scènes
253
- sont aussi exportées sur la surface publique JS (`composites`, `compositeNames`,
254
- `findComposite`, ADR 0031) : le serveur MCP et le site rendent la **même** source,
255
- et leur copie par défaut est neutre — elle s'injecte par props.
256
-
257
- La vue **émet des intentions** (`ready`, `select`, `change`, `error`) et ne mute jamais
258
- l'état : l'hôte décide. Il lui pousse l'apparence (`set-view` : thème, densité) et les
259
- **données** (`set-data`, ADR 0023) — les `props` que l'outil de rendu a portées.
260
-
261
- ## Si tu changes le design system
262
-
263
- Un changement qui change **l'usage** d'une brique met cette skill à jour dans le même
264
- changement : la liste de l'inventaire et la prose. Un composant ajouté au catalogue sans
265
- son manifeste (props, variantes, usages) fait rougir le test de cohérence
266
- (`src/catalogue/coherence.test.ts`) ; une brique absente de l'inventaire fait rougir
267
- `src/mcp/skill.test.ts`.
232
+ - `list_components { query? }` — which bricks exist, and which one matches a word.
233
+ - `get_component { name }` — the full contract of a brick (props, variants, usages).
234
+ - `preview_component { name }` — its rendering recipe (example props, variants, usages).
235
+ - `list_scenes` — the available **composite scenes**.
236
+ - `render_<name>` / `render_scene_<name>` — return the recipe, **carry the data** (`props`,
237
+ optional) and **reference the view** via `_meta.ui.resourceUri`.
238
+
239
+ Resources carry the same data as the tools: `nomos://tokens` (the inventory flattened by
240
+ mode) and `nomos://component/<name>` (the manifest).
241
+
242
+ ## Rendering in conversation
243
+
244
+ Beyond the manifest, each brick has a **view** served at `ui://nomos/<name>` — a
245
+ self-contained document (`text/html;profile=mcp-app`) the host renders in a sandboxed iframe.
246
+ A **composite scene** (`ui://nomos/composite/<name>`) assembles several bricks into a screen
247
+ that makes sense (a form, a status card). Scenes are also exported on the public JS surface
248
+ (`composites`, `compositeNames`, `findComposite`, ADR 0031): the MCP server and the site
249
+ render the **same** source, and their default copy is neutral — it is injected via props.
250
+
251
+ The view **emits intentions** (`ready`, `select`, `change`, `error`) and never mutates state:
252
+ the host decides. It pushes it the appearance (`set-view`: theme, density) and the **data**
253
+ (`set-data`, ADR 0023) — the `props` the render tool carried.
254
+
255
+ ## If you change the design system
256
+
257
+ A change that changes a brick's **usage** updates this skill in the same change: the inventory
258
+ list and the prose. A component added to the catalogue without its manifest (props, variants,
259
+ usages) turns the coherence test red (`src/catalogue/coherence.test.ts`); a brick missing from
260
+ the inventory turns `src/mcp/skill.test.ts` red.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@nomosui/react",
3
3
  "private": false,
4
- "version": "0.8.0",
4
+ "version": "0.8.1",
5
5
  "type": "module",
6
6
  "description": "An app-agnostic design system, built in public: a core of tokens and components exposed to humans and agents.",
7
7
  "repository": {