@nurix/etna 0.20.36 → 0.20.38

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 (212) hide show
  1. package/data/catalog/rules/frontend.md +5 -5
  2. package/data/catalog/rules/pnpm.md +2 -2
  3. package/data/catalog/skills/nustack/SKILL.md +2 -2
  4. package/data/catalog/skills/nustack/references/cli-design.md +35 -35
  5. package/data/catalog/skills/nustack/references/monorepo-master-list.md +6 -6
  6. package/data/catalog/skills/nustack/references/monorepo-setup.md +13 -13
  7. package/data/catalog/skills/nustack/references/publishing.md +40 -39
  8. package/data/harness-index.json +1 -1
  9. package/data/harnesses/code/harness.json +2 -2
  10. package/data/harnesses/code/hooks/approved-packages.md +1 -2
  11. package/data/harnesses/code/rules/frontend.md +5 -5
  12. package/data/harnesses/code/rules/pnpm.md +2 -2
  13. package/data/harnesses/code/skills/boundary/SKILL.md +33 -22
  14. package/data/harnesses/code/skills/boundary/references/index.md +2 -4
  15. package/data/harnesses/code/skills/boundary/references/mechanisms.md +3 -10
  16. package/data/harnesses/code/skills/boundary/references/packs/build-config-runtime.md +1 -1
  17. package/data/harnesses/code/skills/boundary/references/packs/frontend.md +17 -17
  18. package/data/harnesses/code/skills/boundary/references/packs/generic-tail.md +1 -1
  19. package/data/harnesses/code/skills/brainstorm/README.md +11 -13
  20. package/data/harnesses/code/skills/brainstorm/SKILL.md +150 -132
  21. package/data/harnesses/code/skills/brainstorm/references/doctrine.md +48 -51
  22. package/data/harnesses/code/skills/brainstorm/references/harvest.md +51 -56
  23. package/data/harnesses/code/skills/brainstorm/references/session-schema.md +48 -42
  24. package/data/harnesses/code/skills/design/SKILL.md +31 -35
  25. package/data/harnesses/code/skills/design/components/charts.md +121 -43
  26. package/data/harnesses/code/skills/design/components/collections.md +209 -41
  27. package/data/harnesses/code/skills/design/components/feedback.md +86 -25
  28. package/data/harnesses/code/skills/design/components/hooks.md +198 -12
  29. package/data/harnesses/code/skills/design/components/index.md +93 -71
  30. package/data/harnesses/code/skills/design/components/inputs.md +249 -157
  31. package/data/harnesses/code/skills/design/components/layout.md +98 -38
  32. package/data/harnesses/code/skills/design/components/media.md +46 -25
  33. package/data/harnesses/code/skills/design/components/motion.md +56 -46
  34. package/data/harnesses/code/skills/design/components/navigation.md +54 -15
  35. package/data/harnesses/code/skills/design/components/overlays.md +51 -41
  36. package/data/harnesses/code/skills/design/components/setup.md +84 -5
  37. package/data/harnesses/code/skills/design/components/typography.md +72 -22
  38. package/data/harnesses/code/skills/design/components/utils.md +47 -13
  39. package/data/harnesses/code/skills/design/data/chrome/board-column.md +12 -12
  40. package/data/harnesses/code/skills/design/data/chrome/data-table.md +6 -6
  41. package/data/harnesses/code/skills/design/data/chrome/side-panel.md +15 -15
  42. package/data/harnesses/code/skills/design/data/chrome/sidebar.md +25 -25
  43. package/data/harnesses/code/skills/design/data/chrome/stat-cards.md +22 -22
  44. package/data/harnesses/code/skills/design/data/chrome/topbar.md +12 -12
  45. package/data/harnesses/code/skills/design/data/iconography.md +2 -2
  46. package/data/harnesses/code/skills/design/references/compose.md +14 -16
  47. package/data/harnesses/code/skills/design/references/emission.md +6 -6
  48. package/data/harnesses/code/skills/design/references/foundations.md +5 -5
  49. package/data/harnesses/code/skills/design/references/intake.md +6 -6
  50. package/data/harnesses/code/skills/design/references/object-pass.md +1 -1
  51. package/data/harnesses/code/skills/design/references/ship-gate.md +5 -5
  52. package/data/harnesses/code/skills/design/references/ux-patterns/Readme.md +4 -4
  53. package/data/harnesses/code/skills/design/references/ux-patterns/ai-layer.md +1 -1
  54. package/data/harnesses/code/skills/design/references/ux-patterns/container-choice.md +1 -1
  55. package/data/harnesses/code/skills/design/references/ux-patterns/dashboards.md +1 -1
  56. package/data/harnesses/code/skills/design/references/ux-patterns/modes.md +1 -1
  57. package/data/harnesses/code/skills/design/references/ux-patterns/object-model.md +2 -2
  58. package/data/harnesses/code/skills/design/references/ux-patterns/promotion-demotion.md +1 -1
  59. package/data/harnesses/code/skills/design/references/ux-patterns/settings.md +1 -1
  60. package/data/harnesses/code/skills/design/references/ux-patterns/standard-pages.md +1 -1
  61. package/data/harnesses/code/skills/design/scripts/README-validator.md +20 -8
  62. package/data/harnesses/code/skills/design/scripts/validate-design-md.mjs +15 -10
  63. package/data/harnesses/code/skills/documentation/SKILL.md +5 -5
  64. package/data/harnesses/code/skills/documentation/references/examples.md +22 -22
  65. package/data/harnesses/code/skills/documentation/references/frontmatter.md +3 -3
  66. package/data/harnesses/code/skills/documentation/references/instruction-style.md +3 -3
  67. package/data/harnesses/code/skills/documentation/references/source-comments.md +3 -3
  68. package/data/harnesses/code/skills/documentation/references/style-guide.md +5 -5
  69. package/data/harnesses/code/skills/documentation/references/untemplated.md +8 -8
  70. package/data/harnesses/code/skills/documentation/scripts/doc-lint.mjs +3 -2
  71. package/data/harnesses/code/skills/documentation/templates/Readme.md +1 -1
  72. package/data/harnesses/code/skills/documentation/templates/acceptance-criteria.md +1 -1
  73. package/data/harnesses/code/skills/documentation/templates/api-reference.md +1 -1
  74. package/data/harnesses/code/skills/documentation/templates/architecture-decision.md +3 -7
  75. package/data/harnesses/code/skills/documentation/templates/behavioral-checklist.md +1 -1
  76. package/data/harnesses/code/skills/documentation/templates/changelog-entry.md +1 -1
  77. package/data/harnesses/code/skills/documentation/templates/changelog-index.md +1 -1
  78. package/data/harnesses/code/skills/documentation/templates/docs-readme.md +3 -3
  79. package/data/harnesses/code/skills/documentation/templates/doctrine.md +1 -1
  80. package/data/harnesses/code/skills/documentation/templates/domain-model.md +1 -1
  81. package/data/harnesses/code/skills/documentation/templates/environment-registry.md +1 -1
  82. package/data/harnesses/code/skills/documentation/templates/feature.md +3 -3
  83. package/data/harnesses/code/skills/documentation/templates/handover.md +2 -2
  84. package/data/harnesses/code/skills/documentation/templates/implementation-map.md +1 -1
  85. package/data/harnesses/code/skills/documentation/templates/migration-runbook.md +1 -1
  86. package/data/harnesses/code/skills/documentation/templates/non-goals.md +1 -1
  87. package/data/harnesses/code/skills/documentation/templates/operations-procedure.md +2 -2
  88. package/data/harnesses/code/skills/documentation/templates/package.md +1 -1
  89. package/data/harnesses/code/skills/documentation/templates/protected-files.md +1 -1
  90. package/data/harnesses/code/skills/documentation/templates/root-readme.md +1 -1
  91. package/data/harnesses/code/skills/documentation/templates/security-overview.md +1 -1
  92. package/data/harnesses/code/skills/documentation/templates/system-overview.md +1 -1
  93. package/data/harnesses/code/skills/documentation/templates/system.md +1 -1
  94. package/data/harnesses/code/skills/documentation/templates/testing-strategy.md +1 -1
  95. package/data/harnesses/code/skills/documentation/templates/ux-behavior.md +3 -5
  96. package/data/harnesses/code/skills/issue-bridge/SKILL.md +51 -16
  97. package/data/harnesses/code/skills/issue-bridge/references/trackers/github.md +5 -5
  98. package/data/harnesses/code/skills/issue-bridge/references/trackers/jira.md +11 -8
  99. package/data/harnesses/code/skills/issue-bridge/references/trackers/linear.md +16 -11
  100. package/data/harnesses/code/skills/nustack-graph/SKILL.md +38 -29
  101. package/data/harnesses/code/skills/polygraph/references/dispatch.md +31 -18
  102. package/data/harnesses/code/skills/polygraph/references/rows.md +11 -6
  103. package/data/harnesses/code/skills/schema-design/SKILL.md +39 -41
  104. package/data/harnesses/code/skills/test/SKILL.md +38 -42
  105. package/data/harnesses/code/skills/testing/SKILL.md +83 -73
  106. package/data/harnesses/code/skills/workflow-mapper/SKILL.md +156 -193
  107. package/data/harnesses/code/skills/workflow-mapper/references/patterns.md +15 -15
  108. package/data/harnesses/delivery/harness.json +2 -2
  109. package/data/harnesses/delivery/skills/brainstorm/README.md +11 -13
  110. package/data/harnesses/delivery/skills/brainstorm/SKILL.md +150 -132
  111. package/data/harnesses/delivery/skills/brainstorm/references/doctrine.md +48 -51
  112. package/data/harnesses/delivery/skills/brainstorm/references/harvest.md +51 -56
  113. package/data/harnesses/delivery/skills/brainstorm/references/session-schema.md +48 -42
  114. package/data/harnesses/delivery/skills/cost-report/SKILL.md +10 -10
  115. package/data/harnesses/delivery/skills/documentation/SKILL.md +5 -5
  116. package/data/harnesses/delivery/skills/documentation/references/examples.md +22 -22
  117. package/data/harnesses/delivery/skills/documentation/references/frontmatter.md +3 -3
  118. package/data/harnesses/delivery/skills/documentation/references/instruction-style.md +3 -3
  119. package/data/harnesses/delivery/skills/documentation/references/source-comments.md +3 -3
  120. package/data/harnesses/delivery/skills/documentation/references/style-guide.md +5 -5
  121. package/data/harnesses/delivery/skills/documentation/references/untemplated.md +8 -8
  122. package/data/harnesses/delivery/skills/documentation/scripts/doc-lint.mjs +3 -2
  123. package/data/harnesses/delivery/skills/documentation/templates/Readme.md +1 -1
  124. package/data/harnesses/delivery/skills/documentation/templates/acceptance-criteria.md +1 -1
  125. package/data/harnesses/delivery/skills/documentation/templates/api-reference.md +1 -1
  126. package/data/harnesses/delivery/skills/documentation/templates/architecture-decision.md +3 -7
  127. package/data/harnesses/delivery/skills/documentation/templates/behavioral-checklist.md +1 -1
  128. package/data/harnesses/delivery/skills/documentation/templates/changelog-entry.md +1 -1
  129. package/data/harnesses/delivery/skills/documentation/templates/changelog-index.md +1 -1
  130. package/data/harnesses/delivery/skills/documentation/templates/docs-readme.md +3 -3
  131. package/data/harnesses/delivery/skills/documentation/templates/doctrine.md +1 -1
  132. package/data/harnesses/delivery/skills/documentation/templates/domain-model.md +1 -1
  133. package/data/harnesses/delivery/skills/documentation/templates/environment-registry.md +1 -1
  134. package/data/harnesses/delivery/skills/documentation/templates/feature.md +3 -3
  135. package/data/harnesses/delivery/skills/documentation/templates/handover.md +2 -2
  136. package/data/harnesses/delivery/skills/documentation/templates/implementation-map.md +1 -1
  137. package/data/harnesses/delivery/skills/documentation/templates/migration-runbook.md +1 -1
  138. package/data/harnesses/delivery/skills/documentation/templates/non-goals.md +1 -1
  139. package/data/harnesses/delivery/skills/documentation/templates/operations-procedure.md +2 -2
  140. package/data/harnesses/delivery/skills/documentation/templates/package.md +1 -1
  141. package/data/harnesses/delivery/skills/documentation/templates/protected-files.md +1 -1
  142. package/data/harnesses/delivery/skills/documentation/templates/root-readme.md +1 -1
  143. package/data/harnesses/delivery/skills/documentation/templates/security-overview.md +1 -1
  144. package/data/harnesses/delivery/skills/documentation/templates/system-overview.md +1 -1
  145. package/data/harnesses/delivery/skills/documentation/templates/system.md +1 -1
  146. package/data/harnesses/delivery/skills/documentation/templates/testing-strategy.md +1 -1
  147. package/data/harnesses/delivery/skills/documentation/templates/ux-behavior.md +3 -5
  148. package/data/harnesses/delivery/skills/escalate/SKILL.md +11 -11
  149. package/data/harnesses/delivery/skills/prompt-eval/SKILL.md +8 -8
  150. package/data/harnesses/delivery/skills/prompt-library/SKILL.md +30 -9
  151. package/data/harnesses/delivery/skills/route/SKILL.md +8 -8
  152. package/data/harnesses/etna/harness.json +2 -2
  153. package/data/harnesses/etna/skills/boundary/SKILL.md +33 -22
  154. package/data/harnesses/etna/skills/boundary/references/index.md +2 -4
  155. package/data/harnesses/etna/skills/boundary/references/mechanisms.md +3 -10
  156. package/data/harnesses/etna/skills/boundary/references/packs/build-config-runtime.md +1 -1
  157. package/data/harnesses/etna/skills/boundary/references/packs/frontend.md +17 -17
  158. package/data/harnesses/etna/skills/boundary/references/packs/generic-tail.md +1 -1
  159. package/data/harnesses/etna/skills/brainstorm/README.md +11 -13
  160. package/data/harnesses/etna/skills/brainstorm/SKILL.md +150 -132
  161. package/data/harnesses/etna/skills/brainstorm/references/doctrine.md +48 -51
  162. package/data/harnesses/etna/skills/brainstorm/references/harvest.md +51 -56
  163. package/data/harnesses/etna/skills/brainstorm/references/session-schema.md +48 -42
  164. package/data/harnesses/etna/skills/documentation/SKILL.md +5 -5
  165. package/data/harnesses/etna/skills/documentation/references/examples.md +22 -22
  166. package/data/harnesses/etna/skills/documentation/references/frontmatter.md +3 -3
  167. package/data/harnesses/etna/skills/documentation/references/instruction-style.md +3 -3
  168. package/data/harnesses/etna/skills/documentation/references/source-comments.md +3 -3
  169. package/data/harnesses/etna/skills/documentation/references/style-guide.md +5 -5
  170. package/data/harnesses/etna/skills/documentation/references/untemplated.md +8 -8
  171. package/data/harnesses/etna/skills/documentation/scripts/doc-lint.mjs +3 -2
  172. package/data/harnesses/etna/skills/documentation/templates/Readme.md +1 -1
  173. package/data/harnesses/etna/skills/documentation/templates/acceptance-criteria.md +1 -1
  174. package/data/harnesses/etna/skills/documentation/templates/api-reference.md +1 -1
  175. package/data/harnesses/etna/skills/documentation/templates/architecture-decision.md +3 -7
  176. package/data/harnesses/etna/skills/documentation/templates/behavioral-checklist.md +1 -1
  177. package/data/harnesses/etna/skills/documentation/templates/changelog-entry.md +1 -1
  178. package/data/harnesses/etna/skills/documentation/templates/changelog-index.md +1 -1
  179. package/data/harnesses/etna/skills/documentation/templates/docs-readme.md +3 -3
  180. package/data/harnesses/etna/skills/documentation/templates/doctrine.md +1 -1
  181. package/data/harnesses/etna/skills/documentation/templates/domain-model.md +1 -1
  182. package/data/harnesses/etna/skills/documentation/templates/environment-registry.md +1 -1
  183. package/data/harnesses/etna/skills/documentation/templates/feature.md +3 -3
  184. package/data/harnesses/etna/skills/documentation/templates/handover.md +2 -2
  185. package/data/harnesses/etna/skills/documentation/templates/implementation-map.md +1 -1
  186. package/data/harnesses/etna/skills/documentation/templates/migration-runbook.md +1 -1
  187. package/data/harnesses/etna/skills/documentation/templates/non-goals.md +1 -1
  188. package/data/harnesses/etna/skills/documentation/templates/operations-procedure.md +2 -2
  189. package/data/harnesses/etna/skills/documentation/templates/package.md +1 -1
  190. package/data/harnesses/etna/skills/documentation/templates/protected-files.md +1 -1
  191. package/data/harnesses/etna/skills/documentation/templates/root-readme.md +1 -1
  192. package/data/harnesses/etna/skills/documentation/templates/security-overview.md +1 -1
  193. package/data/harnesses/etna/skills/documentation/templates/system-overview.md +1 -1
  194. package/data/harnesses/etna/skills/documentation/templates/system.md +1 -1
  195. package/data/harnesses/etna/skills/documentation/templates/testing-strategy.md +1 -1
  196. package/data/harnesses/etna/skills/documentation/templates/ux-behavior.md +3 -5
  197. package/data/harnesses/etna/skills/workflow-mapper/SKILL.md +156 -193
  198. package/data/harnesses/etna/skills/workflow-mapper/references/patterns.md +15 -15
  199. package/data/harnesses/hr/harness.json +2 -2
  200. package/data/harnesses/hr/skills/evaluator/SKILL.md +71 -73
  201. package/data/harnesses/hr/skills/evaluator/criteria/code-quality.md +28 -47
  202. package/data/harnesses/hr/skills/evaluator/criteria/deployment-readiness.md +33 -43
  203. package/data/harnesses/hr/skills/evaluator/criteria/maintainability.md +34 -46
  204. package/data/harnesses/hr/skills/evaluator/criteria/mechanical-ambition.md +13 -13
  205. package/data/harnesses/hr/skills/evaluator/criteria/problem-substantiveness.md +7 -7
  206. package/data/harnesses/hr/skills/evaluator/criteria/testing.md +36 -41
  207. package/data/harnesses/hr/skills/evaluator/criteria/ux-mechanics.md +25 -11
  208. package/data/harnesses/hr/skills/evaluator/criteria/value-articulation.md +7 -7
  209. package/data/harnesses/hr/skills/evaluator/preflight.md +27 -46
  210. package/data/harnesses/hr/skills/interview-eval/SKILL.md +37 -42
  211. package/data/harnesses/hr/skills/interview-eval/rubric.md +21 -23
  212. package/package.json +2 -2
@@ -4,79 +4,94 @@
4
4
 
5
5
  ## Shared Props
6
6
 
7
- Enhanced form components support these props where applicable:
7
+ Every Control takes one value vocabulary, so a host (a `Cell`, a field grid, a table cell) binds any of them the same way:
8
8
 
9
9
  | Prop | Type | Description |
10
10
  | --- | --- | --- |
11
- | `value` | `any` | Controlled value |
12
- | `onChange` | `(value) => void` | Change handler (text inputs receive the event) |
13
- | `onSave` | `(value) => void` | Commit on blur/Enter/selection |
14
- | `disabled` | `boolean` | Prevents interaction |
15
- | `readOnly` | `boolean` | Display-only mode |
16
- | `inline` | `boolean` | Click-to-edit mode |
11
+ | `value` / `defaultValue` | `T` | Controlled value or uncontrolled seed; binary Controls use `checked` (Checkbox, Switch) or `pressed` (Toggle) |
12
+ | `onValueChange` | `(value, details) => void` | Every change, value first; `details` is Base UI's event details, and `details.cancel()` vetoes a discrete change |
13
+ | `onValueCommitted` | `(value) => void` | The one commit channel: once per commit, never for an unchanged value. Typed fields commit on Enter (Cmd/Ctrl+Enter when multi-line) or blur and revert on Escape; discrete Controls commit in the same tick as `onValueChange` |
14
+ | `validate` | `(value) => string \| null \| Promise<string \| null>` | A message refuses the commit, keeps the draft and sets `aria-invalid` |
15
+ | `disabled`, `readOnly`, `required`, `name` | `boolean`, `string` | Field state; `required` removes the automatic Clear on Select, DatePicker and IconPicker, `name` submits through a hidden input |
16
+ | `variant="bare"`, `size` | `bare`, `xs\|sm\|default\|lg` | `bare` drops the frame, focus ring and invalid paint a surface underneath already draws; a host supplies both through host context |
17
+
18
+ Each value-holding Control also exports a `format` static (`Input.format`, `Select.format`, `DatePicker.format` …) that writes the at-rest text a host shows.
17
19
 
18
20
  ## Cell
19
21
 
20
- Compound component for form field layout with label, content, and helpers.
22
+ Form-field host: label, control, addons, helper and error in one block, with field state set once on the root (`@nurix/components/compound/cell`, plus `useCellContext`).
23
+
24
+ **Props:** `mode` [default|inline], `size` [xs|sm|default|lg], `orientation` [vertical|horizontal], `disabled`, `readOnly`, `required`, `invalid`
21
25
 
22
- **Props:** `mode` [default|inline], `size` [sm|md|lg], `editable`, `labelPosition` [top|left], `disabled`
26
+ **Sub-components:** `Cell.Label`, `Cell.Content`, `Cell.Field` (`type` [text|textarea|number|checkbox|switch|rating|slider|select], `value`, `onValueCommitted`, `options`, `format`), `Cell.Prefix`, `Cell.Suffix`, `Cell.Stepper`, `Cell.Helper`, `Cell.Error`
23
27
 
24
- **Sub-components:** `Cell.Label`, `Cell.Content`, `Cell.Prefix`, `Cell.Suffix`, `Cell.Stepper`, `Cell.Helper`, `Cell.Error`
28
+ `Cell.Error` renders only while the root's `invalid` is true; `Cell.Stepper` drives a hosted NumberInput; `disabled` makes the whole `Cell.Content` inert, Prefix/Suffix buttons included. `mode="inline"` shows text-like types as text until clicked, while marks (checkbox, switch, rating) stay live in place.
25
29
 
26
30
  ```tsx
27
- <Cell mode="default" labelPosition="top">
28
- <Cell.Label>Email</Cell.Label>
31
+ <Cell required invalid={!!seatsError}>
32
+ <Cell.Label>Seats</Cell.Label>
29
33
  <Cell.Content>
30
- <Input value={email} onChange={(e) => setEmail(e.target.value)} />
34
+ <Cell.Field type="number" value={seats} onValueCommitted={setSeats} options={{ min: 0, max: 20 }} />
35
+ <Cell.Suffix>seats</Cell.Suffix>
36
+ <Cell.Stepper />
31
37
  </Cell.Content>
32
- <Cell.Helper>We'll never share your email</Cell.Helper>
38
+ <Cell.Helper>Whole seats, 0 to 20</Cell.Helper>
39
+ <Cell.Error>{seatsError}</Cell.Error>
33
40
  </Cell>
34
41
  ```
35
42
 
36
43
  ## Input
37
44
 
38
- Text input with optional regex validation; commits on blur via `onSave`.
45
+ Single-line text field; `onValueCommitted` turns on the local draft, Enter/blur commits, Escape reverts (`@nurix/components/element/input`).
46
+
47
+ **Props:** `onValueChange`, `onValueCommitted`, `validate`, `prefix`, `suffix` (decorative glyphs only), `size`, `variant` [default|bare]
39
48
 
40
- **Props:** `onSave`, `validator`, `onValidationError`, `hideBorder`
49
+ Without `onValueCommitted`, `validate` never runs and a controlled `value` reads straight through. Labels, hints and interactive affixes belong to `Cell`.
41
50
 
42
51
  ```tsx
43
52
  <Input
44
53
  value={name}
45
- onChange={(e) => setName(e.target.value)}
46
- onSave={saveName}
54
+ onValueChange={setName}
55
+ onValueCommitted={saveName}
56
+ validate={(v) => (v.trim() ? null : "Name is required")}
47
57
  placeholder="Enter name"
48
58
  />
49
59
  ```
50
60
 
51
61
  ## NumberInput
52
62
 
53
- Numeric input with constraints; commits on blur/Enter, Escape reverts.
63
+ Typed, bounded number; commits on Enter, blur and each arrow or step press, Escape reverts (`@nurix/components/element/number-input`).
54
64
 
55
- **Props:** `min`, `max`, `step`, `allowDecimals`, `allowNegative`, `onSave`, `onValidationError`, `hideBorder`
65
+ **Props:** `min`, `max`, `step`, `format` (Intl options), `locale`, `onValueCommitted`, `validate`, `size`, `variant` [default|bare]
66
+
67
+ An empty field commits `null`, not `0`. No negatives is `min={0}`; whole numbers are `format={{ maximumFractionDigits: 0 }}`. Never `Input type="number"`.
56
68
 
57
69
  ```tsx
58
- <NumberInput value={count} onSave={setCount} min={0} max={100} step={1} />
70
+ <NumberInput value={count} onValueCommitted={setCount} min={0} max={100} step={1} />
59
71
  ```
60
72
 
61
73
  ## Textarea
62
74
 
63
- Multi-line text input. Controlled when `value` is set; otherwise `defaultValue` seeds an internal draft and Escape resets it. Blur and Cmd/Ctrl+Enter commit via `onSave`.
75
+ Multi-line field; commits on Cmd/Ctrl+Enter or blur (bare Enter is a newline), Escape reverts (`@nurix/components/element/textarea`).
64
76
 
65
- **Props:** `onSave`, `hideBorder`
77
+ **Props:** `onValueChange`, `onValueCommitted`, `validate`, `rows` (default 3), `autosize` [boolean|{ maxRows }], `variant` [default|bare]
66
78
 
67
79
  ```tsx
68
80
  <Textarea
69
81
  value={description}
70
- onChange={(e) => setDescription(e.target.value)}
71
- onSave={saveDescription}
82
+ onValueChange={setDescription}
83
+ onValueCommitted={saveDescription}
84
+ autosize={{ maxRows: 8 }}
72
85
  />
73
86
  ```
74
87
 
75
88
  ## Checkbox
76
89
 
77
- Boolean toggle supporting both `value`/`onChange` and `checked`/`onCheckedChange` APIs.
90
+ Boolean box on Base UI `Checkbox.Root` (`@nurix/components/element/checkbox`).
91
+
92
+ **Props:** `checked`, `defaultChecked`, `onCheckedChange`, `onValueCommitted`, `indeterminate`, `size` [sm|default], `variant` [default|bare]
78
93
 
79
- **Props:** `checked`, `onCheckedChange`, `value`, `onChange`, `indeterminate`
94
+ `indeterminate` is the caller's to clear, and while set the box emits only `data-indeterminate`, never `data-checked`. `value` is the string a form posts, not the checked state.
80
95
 
81
96
  ```tsx
82
97
  <Checkbox checked={agreed} onCheckedChange={setAgreed} />
@@ -84,244 +99,321 @@ Boolean toggle supporting both `value`/`onChange` and `checked`/`onCheckedChange
84
99
 
85
100
  ## Switch
86
101
 
87
- Toggle switch supporting both `value`/`onChange` and `checked`/`onCheckedChange` APIs.
102
+ On/off setting that applies the moment it flips; ships no label (`@nurix/components/element/switch`).
88
103
 
89
- **Props:** `checked`, `onCheckedChange`, `value`, `onChange`, `size` [sm|default]
104
+ **Props:** `checked`, `defaultChecked`, `onCheckedChange`, `onValueCommitted`, `size` [sm|default], `variant` [default|bare]
105
+
106
+ There is no `onChange`, and `value` is the submitted string, never a boolean. Name it with `aria-label` or wrap it in `Cell`.
90
107
 
91
108
  ```tsx
92
- <Switch checked={enabled} onCheckedChange={setEnabled} />
109
+ <Switch checked={enabled} onCheckedChange={setEnabled} aria-label="Notifications" />
93
110
  ```
94
111
 
95
112
  ## Select
96
113
 
97
- Compound select with search, multi-select, and async loading. `onChange`/`onSave` receive the selected option object(s), not raw values.
114
+ Closed listbox picking one string value, with no query field (`@nurix/components/element/select`).
115
+
116
+ **Props:** `items` (required; `{ value: label }` map or `{ value, label }[]`), `value`, `defaultValue`, `onValueChange`, `onValueCommitted`, `validate`, `required`, `variant` [default|bare], `labels` (`clear`)
98
117
 
99
- **Props:** `options`, `mode` [single|multi], `clearable`, `maxSelections`, `selectionIndicator` [checkbox|check-end|bg-only], `compact`, `loading`, `onSearch`, `onLoadMore`, `debounceMs`
118
+ **Sub-components:** `Select.Trigger` (`size` [xs|sm|default|lg]), `Select.Value` (`placeholder`), `Select.Popup` (`side`, `align`, `sideOffset`, `alignItemWithTrigger`), `Select.Group`, `Select.Label`, `Select.Item`, `Select.Separator`
100
119
 
101
- **Sub-components:** `Select.Trigger` (`display` [tags|comma|count]), `Select.Content` (`searchable`, `searchPlaceholder`, `showSelectAll`, `maxHeight`), `Select.Item`, `Select.Search`, `Select.Loading`, `Select.Empty`, `Select.SelectAll`
120
+ Values are strings only. A non-required Select with a value shows a Clear row that commits `null`. Search, async loading, multi-select or a long list is `Combobox`.
102
121
 
103
122
  ```tsx
104
- <Select
105
- options={statusOptions}
106
- value={status}
107
- onChange={(opt) => setStatus(opt?.value)}
108
- >
109
- <Select.Trigger display="tags" />
110
- <Select.Content searchable>
123
+ <Select items={statusItems} value={status} onValueChange={setStatus}>
124
+ <Select.Trigger>
125
+ <Select.Value placeholder="Status" />
126
+ </Select.Trigger>
127
+ <Select.Popup>
111
128
  <Select.Item value="active">Active</Select.Item>
112
- </Select.Content>
129
+ <Select.Item value="paused">Paused</Select.Item>
130
+ </Select.Popup>
113
131
  </Select>
114
132
  ```
115
133
 
116
- ## Option
134
+ ## Combobox
135
+
136
+ Searchable option picker: single or multi, client or remote filtering, paging (`@nurix/components/compound/combobox`, plus `useComboboxContext`).
117
137
 
118
- Standalone option display chip (exported from the `select` subpath).
138
+ **Props:** `options` (`{ value, label }[]`), `value`, `onValueChange(value, { option })`, `onValueCommitted`, `multiple`, `max`, `isClearable`, `searchValue`, `onSearchValueChange`, `loading`, `hasMore`, `loadingMore`, `onLoadMore`, `selectionIndicator` [checkbox|check-end|bg-only], `variant` [default|bare], `labels`
119
139
 
120
- **Props:** `variant` [text|pill|outline|ghost|soft], `color`, `prefix` ["dot"|ReactNode]
140
+ **Sub-components:** `Combobox.Trigger` (`display` [tags|comma|count], `size` [sm|default]), `Combobox.Popup` (`maxHeight`), `Combobox.Search`, `Combobox.List`, `Combobox.Item`, `Combobox.Loading`, `Combobox.Empty`, `Combobox.SelectAll`
141
+
142
+ Holding the query (`onSearchValueChange`) turns the built-in filter off. Multi mode commits once on close, and Escape reverts. The placeholder never names the trigger, so pass `aria-label`.
121
143
 
122
144
  ```tsx
123
- <Option variant="pill" color="#ef4444" prefix="dot">
124
- High Priority
125
- </Option>
145
+ <Combobox options={owners} value={owner} onValueChange={setOwner}>
146
+ <Combobox.Trigger aria-label="Owner" />
147
+ <Combobox.Popup>
148
+ <Combobox.Search />
149
+ <Combobox.List>
150
+ {owners.map((o) => <Combobox.Item key={o.value} value={o.value}>{o.label}</Combobox.Item>)}
151
+ </Combobox.List>
152
+ <Combobox.Empty />
153
+ </Combobox.Popup>
154
+ </Combobox>
126
155
  ```
127
156
 
128
157
  ## DatePicker
129
158
 
130
- Date selection with calendar popover; accepts `Date`, ISO string, or null values.
159
+ Date field with a calendar popover; the value is a `Date`, an ISO string or `null` (`@nurix/components/compound/date-picker`).
160
+
161
+ **Props:** `onValueChange`, `onValueCommitted`, `validate`, `placeholder`, `formatPattern`, `isRelative`, `hasIcon`, `hasPresets`, `presets`, `captionLayout` [label|dropdown|dropdown-months|dropdown-years], `numberOfMonths`, `showOutsideDays`, `min`, `max`, `weekStartsOn`, `required`, `name`, `size`, `variant` [default|bare]
131
162
 
132
- **Props:** `onSave`, `inline`, `relativeFormat`, `formatPattern`, `showIcon`, `captionLayout` [label|dropdown|dropdown-months|dropdown-years], `buttonVariant`, `showOutsideDays`, `numberOfMonths`, `fromYear`, `toYear`, `fromDate`, `toDate`, `weekStartsOn`, `compact`
163
+ A non-required picker shows a Clear action in its popup. `name` submits local `yyyy-MM-dd`. `DatePicker.format(value)` is the at-rest text.
133
164
 
134
165
  ```tsx
135
166
  <DatePicker
136
167
  value={date}
137
- onChange={setDate}
138
- onSave={handleSave}
139
- relativeFormat
140
- placeholder="Pick a date"
168
+ onValueChange={setDate}
169
+ onValueCommitted={commitDate}
170
+ placeholder="Choose date"
141
171
  />
142
172
  ```
143
173
 
174
+ ## DatePicker.Range
175
+
176
+ Date-window picker on the same namespace; the value is `{ from: Date, to: Date } | null` (`DateRangeValue`).
177
+
178
+ **Props:** the DatePicker props, with `presets` as range presets (`DateRangePreset[]`)
179
+
180
+ A window commits on the second grid press; closing after one press drops it without a commit. `DatePicker.Range.format(value)` is the at-rest text.
181
+
182
+ ```tsx
183
+ <DatePicker.Range value={range} onValueChange={setRange} onValueCommitted={commitRange} hasPresets />
184
+ ```
185
+
144
186
  ## Slider
145
187
 
146
- Range input with optional value display.
188
+ Range control for one number between `min` and `max` (`@nurix/components/element/slider`).
189
+
190
+ **Props:** `min`, `max`, `step`, `onValueChange`, `onValueCommitted`, `hasValueReadout`, `hasBoundsReadout`, `format` (Intl options), `locale`, `inputRef`, `variant` [default|bare]
147
191
 
148
- **Props:** `min`, `max`, `step`, `defaultValue`, `onSave`, `inline`, `showValue`, `showMinMax`, `formatValue`, `compact`
192
+ `value` is always a single number. `onValueCommitted` fires once per release or key press that changes the value, and Escape reverts an uncommitted drag.
149
193
 
150
194
  ```tsx
151
- <Slider
152
- value={volume}
153
- onChange={setVolume}
154
- onSave={handleSave}
155
- min={0}
156
- max={100}
157
- />
195
+ <Slider value={volume} onValueChange={setVolume} onValueCommitted={saveVolume} min={0} max={100} aria-label="Volume" />
158
196
  ```
159
197
 
160
198
  ## Rating
161
199
 
162
- Icon-based rating input.
200
+ Icon score picker (`@nurix/components/element/rating`, plus `RATING_ICONS`).
201
+
202
+ **Props:** `max`, `step` [1|0.5], `size` [xs|sm|default|lg], `icon` [Star|Heart|ThumbsUp|Circle|Flame|Zap|Trophy|Diamond], `hasValueText`, `onValueCommitted`, `validate`, `name`, `variant` [default|bare], `labels`
163
203
 
164
- **Props:** `maxRating`, `size`, `icon` [Star|Heart|ThumbsUp|Circle|Flame|Zap|Trophy|Diamond], `allowHalf`, `showValue`, `onSave`, `inline`, `compact`
204
+ A controlled `value` moves no star until the parent updates it. No click brings a set score below one `step`, so clearing means setting `value` to `0`. It ships no group name.
165
205
 
166
206
  ```tsx
167
- <Rating
168
- value={rating}
169
- onChange={setRating}
170
- onSave={handleSave}
171
- maxRating={5}
172
- allowHalf
173
- />
207
+ <Rating value={score} onValueChange={setScore} max={5} step={0.5} icon="Heart" aria-label="Score" />
174
208
  ```
175
209
 
176
210
  ## Button
177
211
 
178
- Standard action button.
212
+ Action button; the element changes through `render`, never by wrapping (`@nurix/components/element/button`, plus `buttonVariants`).
213
+
214
+ **Props:** `variant` [default|secondary|outline|ghost|link], `intent` [neutral|danger], `size` [xs|sm|default|lg], `isIconOnly`, `loading`, `pressed`, `focusableWhenDisabled`, `render`, `labels` (`pending`)
179
215
 
180
- **Props:** `variant` [default|destructive|outline|secondary|ghost|link], `size` [default|sm|lg|icon], `asChild`
216
+ An irreversible action takes `intent="danger"` on any variant. An icon-only button adds `isIconOnly` plus an `aria-label`. `loading` keeps focus and width; `pressed` is a controlled latch rendering `aria-pressed`. Inside a host, omit `size`.
181
217
 
182
218
  ```tsx
183
- <Button variant="outline" size="sm">
184
- Cancel
219
+ <Button variant="outline" intent="danger" loading={isDeleting} onClick={handleDelete}>
220
+ Delete
185
221
  </Button>
186
222
  ```
187
223
 
188
224
  ## Calendar
189
225
 
190
- Calendar grid built on `react-day-picker`; accepts all `DayPicker` props. Also exports `CalendarDayButton`.
191
-
192
- **Props:** `mode` [single|multiple|range], `selected`, `onSelect`, `captionLayout` [label|dropdown|dropdown-months|dropdown-years], `numberOfMonths`, `showOutsideDays`, `buttonVariant`
193
-
194
- ```tsx
195
- <Calendar
196
- mode="single"
197
- selected={date}
198
- onSelect={setDate}
199
- captionLayout="dropdown"
200
- />
201
- ```
226
+ Inline month grid on `react-day-picker` (`@nurix/components/compound/calendar`); a form field wants `DatePicker` instead.
202
227
 
203
- ## DateRangePicker
228
+ **Props:** `value`, `onValueChange`, `onValueCommitted`, `multiple`, `min`, `max`, `disabledDates` (react-day-picker matchers), `disabled`, `readOnly`, `required`, `name`, `validate`, `captionLayout`, `numberOfMonths`
204
229
 
205
- Date range selection with calendar popover and optional presets sidebar; `onSave` fires only when both ends are selected, and closing with a partial selection reverts.
230
+ **Sub-components:** `Calendar.Range` (value `{ from, to? } | null`, `excludeDisabled`, `resetOnSelect`), `Calendar.DayButton`
206
231
 
207
- **Props:** `onSave`, `inline`, `showPresets`, `presets`, `relativeFormat`, `formatPattern`, `showIcon`, `captionLayout`, `numberOfMonths`, `fromDate`, `toDate`, `weekStartsOn`, `compact`
232
+ Re-pressing the selected day clears to `null` unless `required`. `Calendar.Range` commits only a complete or cleared range. Returned dates are local midnight.
208
233
 
209
234
  ```tsx
210
- <DateRangePicker
211
- value={range}
212
- onChange={setRange}
213
- onSave={handleSave}
214
- showPresets
215
- />
235
+ <Calendar value={date} onValueChange={setDate} captionLayout="dropdown" min={today} />
216
236
  ```
217
237
 
218
238
  ## IconPicker
219
239
 
220
- Searchable Lucide icon picker popover; also exports `Icon` for rendering a picked icon by name.
240
+ Searchable lucide icon picker popover over caller-loaded records (`@nurix/components/compound/icon-picker`; optional peers `@tanstack/react-virtual` and `fuse.js`).
221
241
 
222
- **Props:** `value`, `defaultValue`, `onValueChange`, `open`, `onOpenChange`, `searchable`, `searchPlaceholder`, `triggerPlaceholder`, `iconsList`, `categorized`, `modal`
242
+ **Props:** `data` (required; `iconsData` from `lib/icons-data`, loaded lazily), `loading`, `value` (`IconName | null`, kebab-case), `onValueChange`, `onValueCommitted`, `isSearchable`, `isCategorized`, `modal`, `render`, `required`, `size`, `variant` [default|bare], `labels`
243
+
244
+ A non-required picker carries an automatic Clear. Draw the pick with `Icon` from `element/icon`.
223
245
 
224
246
  ```tsx
225
- <IconPicker value={iconName} onValueChange={setIconName} />
247
+ <IconPicker data={icons} loading={isLoading} value={icon} onValueChange={setIcon} />
226
248
  ```
227
249
 
228
250
  ## InputOTP
229
251
 
230
- One-time code input built on `input-otp`.
252
+ Segmented one-time-code field on Base UI's OTP field (`@nurix/components/compound/input-otp`).
253
+
254
+ **Props:** `length` (must equal the slot count), `value`, `onValueChange`, `onValueCommitted` (fires when the last slot fills), `status` [pending|error|success], `mask`, `autoSubmit`, `labels`
231
255
 
232
- **Props:** `maxLength`, `value`, `onChange`, `containerClassName`
256
+ **Sub-components:** `InputOTP.Group`, `InputOTP.Slot`, `InputOTP.Separator`, `InputOTP.Check`
233
257
 
234
- **Sub-components:** `InputOTPGroup`, `InputOTPSlot` (`index` required), `InputOTPSeparator`
258
+ `status` is the caller's verdict: the field never decides a code is wrong.
235
259
 
236
260
  ```tsx
237
- <InputOTP maxLength={4} value={code} onChange={setCode}>
238
- <InputOTPGroup>
239
- <InputOTPSlot index={0} />
240
- <InputOTPSlot index={1} />
241
- <InputOTPSlot index={2} />
242
- <InputOTPSlot index={3} />
243
- </InputOTPGroup>
261
+ <InputOTP length={4} value={code} onValueChange={setCode} onValueCommitted={verify} status={status}>
262
+ <InputOTP.Group>
263
+ <InputOTP.Slot /><InputOTP.Slot /><InputOTP.Slot /><InputOTP.Slot />
264
+ </InputOTP.Group>
265
+ <InputOTP.Check />
244
266
  </InputOTP>
245
267
  ```
246
268
 
247
- ## MediaFilePicker
269
+ ## FilePicker
248
270
 
249
- File upload with drag-and-drop dialog and previews. `value` is `MediaFileOption` object(s): `{ value, label, type, previewUrl, size, file }`.
271
+ Acquires and stages local files through a dropzone and a picker button (`@nurix/components/compound/file-picker`, plus `useFilePicker`).
250
272
 
251
- **Props:** `mode` [single|multi], `maxFiles`, `maxFileSize`, `acceptedTypes`, `viewMode` [card|list], `display` [thumbnails|comma|tags], `renderPlainText`, `renderComponent`
273
+ **Props:** `value` (`File[]`), `onValueChange`, `onValueCommitted`, `multiple`, `accept`, `maxFiles`, `maxSize`, `validate(file)`, `onFileReject`, `onUpload`, `readOnly`, `labels`
252
274
 
253
- ```tsx
254
- <MediaFilePicker value={files} onChange={setFiles} mode="multi" maxFiles={5} />
255
- ```
256
-
257
- ## NativeSelect
258
-
259
- Radix-based select primitive family on the `native-select` subpath (distinct from the compound `Select` above); exported symbols are named `Select*`.
260
-
261
- **Props:** `value`, `onValueChange`, `defaultValue`, `disabled` (root passes through all Radix Select props)
275
+ **Sub-components:** `FilePicker.Dropzone`, `FilePicker.Trigger`, `FilePicker.List`, `FilePicker.Item` (`value` = the File), `FilePicker.ItemPreview`, `FilePicker.ItemMetadata`, `FilePicker.ItemProgress`, `FilePicker.ItemDelete`, `FilePicker.Clear`
262
276
 
263
- **Sub-components:** `Select`, `SelectTrigger` (`size` [sm|default]), `SelectValue`, `SelectContent`, `SelectGroup`, `SelectLabel`, `SelectItem`
277
+ The dropzone is a non-interactive region, so it always holds a `FilePicker.Trigger`. Supply `onUpload` only when the product owns a transport. An existing attachment is `FilePreview` (`compound/file-preview`).
264
278
 
265
279
  ```tsx
266
- <Select value={fruit} onValueChange={setFruit}>
267
- <SelectTrigger>
268
- <SelectValue placeholder="Pick one" />
269
- </SelectTrigger>
270
- <SelectContent>
271
- <SelectItem value="apple">Apple</SelectItem>
272
- </SelectContent>
273
- </Select>
280
+ <FilePicker value={files} onValueChange={setFiles} multiple maxFiles={5}>
281
+ <FilePicker.Dropzone>
282
+ <FilePicker.Trigger>Choose files</FilePicker.Trigger>
283
+ </FilePicker.Dropzone>
284
+ <FilePicker.List>
285
+ {files.map((file) => <FilePicker.Item key={file.name} value={file}><FilePicker.ItemMetadata /><FilePicker.ItemDelete>Remove</FilePicker.ItemDelete></FilePicker.Item>)}
286
+ </FilePicker.List>
287
+ </FilePicker>
274
288
  ```
275
289
 
276
290
  ## RadioGroup
277
291
 
278
- Radix radio group with card and segmented-control variants.
292
+ Small set of mutually exclusive options, as radio marks or whole-card radios (`@nurix/components/element/radio-group`).
293
+
294
+ **Props:** `value`, `defaultValue`, `onValueChange`, `onValueCommitted`, `name` (on the root, never an item), `orientation` [vertical|horizontal], `variant` [default|bare]
279
295
 
280
- **Props:** `value`, `defaultValue`, `onValueChange`
296
+ **Sub-components:** `RadioGroup.Item` (`size` [sm|default]), `RadioGroup.Card` (`variant` [default|list]), `RadioGroup.Header`, `RadioGroup.Title`, `RadioGroup.Description`, `RadioGroup.Indicator`
281
297
 
282
- **Sub-components:** `RadioGroupItem`, `RadioGroupCard` (`variant` [card|list|chip]), `RadioGroupCardItem`, `RadioGroupLabel` (`clickable`), `RadioGroupDescription`, `RadioGroupItemHeader/Title/Subtitle/Description/Content/Indicator`, `RadioGroupSegmented`, `RadioGroupSegmentedItem` (`icon`)
298
+ An `Item`'s `id` lands on its hidden input, so aim the `Label`'s `htmlFor` (`element/label`) at it. The segmented control is `ToggleGroup`.
283
299
 
284
300
  ```tsx
285
- <RadioGroup value={plan} onValueChange={setPlan}>
286
- <RadioGroupCard>
287
- <RadioGroupItem value="free" />
288
- <RadioGroupLabel clickable>Free</RadioGroupLabel>
289
- </RadioGroupCard>
290
- <RadioGroupCard>
291
- <RadioGroupItem value="pro" />
292
- <RadioGroupLabel clickable>Pro</RadioGroupLabel>
293
- </RadioGroupCard>
301
+ <RadioGroup value={plan} onValueChange={setPlan} aria-label="Plan">
302
+ <RadioGroup.Item value="free" id="plan-free" />
303
+ <Label htmlFor="plan-free">Free</Label>
304
+ <RadioGroup.Item value="pro" id="plan-pro" />
305
+ <Label htmlFor="plan-pro">Pro</Label>
294
306
  </RadioGroup>
295
307
  ```
296
308
 
297
309
  ## Toggle
298
310
 
299
- Pressed-state button supporting both `value`/`onChange` and `pressed`/`onPressedChange` APIs.
311
+ Single control that holds its own on/off state (`@nurix/components/element/toggle`, plus `toggleVariants`).
312
+
313
+ **Props:** `pressed`, `defaultPressed`, `onPressedChange`, `onValueCommitted`, `variant` [ghost|outline|default|bare] (default `ghost`), `size` [sm|default|lg], `isIconOnly`
300
314
 
301
- **Props:** `pressed`, `onPressedChange`, `value`, `onChange`, `variant` [default|outline|primary], `size` [default|sm|lg], `density` [compact|comfortable|spacious]
315
+ `value` and `onChange` are not boolean aliases. Style off `data-pressed`.
302
316
 
303
317
  ```tsx
304
- <Toggle pressed={isBold} onPressedChange={setIsBold} variant="outline">
305
- B
318
+ <Toggle pressed={isBold} onPressedChange={setIsBold} isIconOnly aria-label="Toggle bold">
319
+ <Bold />
306
320
  </Toggle>
307
321
  ```
308
322
 
309
323
  ## ToggleGroup
310
324
 
311
- Group of toggles with single or multiple selection; `variant`/`size`/`density` cascade to items.
325
+ Strip of toggles picking one value, or several with `multiple` (`@nurix/components/compound/toggle-group`).
312
326
 
313
- **Props:** `type` [single|multiple], `value`, `defaultValue`, `onValueChange`, `variant` [default|outline|primary], `size` [default|sm|lg], `density` [compact|comfortable|spacious]
327
+ **Props:** `multiple`, `value`, `defaultValue`, `onValueChange`, `onValueCommitted`, `variant` [ghost|outline|default|bare], `size` [sm|default|lg], `orientation`
314
328
 
315
- **Sub-components:** `ToggleGroupItem`
329
+ **Sub-components:** `ToggleGroup.Item` (takes no `variant` or `size`; both cascade from the group)
330
+
331
+ Single mode publishes `""` when the pressed item is toggled off, so guard it when a selection is mandatory.
316
332
 
317
333
  ```tsx
318
- <ToggleGroup
319
- type="single"
320
- value={align}
321
- onValueChange={setAlign}
322
- variant="outline"
323
- >
324
- <ToggleGroupItem value="left">Left</ToggleGroupItem>
325
- <ToggleGroupItem value="right">Right</ToggleGroupItem>
334
+ <ToggleGroup variant="outline" value={view} onValueChange={(next) => next && setView(next)}>
335
+ <ToggleGroup.Item value="table">Table</ToggleGroup.Item>
336
+ <ToggleGroup.Item value="card">Card</ToggleGroup.Item>
326
337
  </ToggleGroup>
327
338
  ```
339
+
340
+ ## ThemeToggle
341
+
342
+ Light/dark toggle (`@nurix/components/element/theme-toggle`) with a circular view-transition reveal from the button; it is the library `Toggle`, with `pressed` meaning dark.
343
+
344
+ **Props:** `pressed` + `onPressedChange` (pass both when the app owns theme state; omitted, it reads and writes the `.dark` class on `document.documentElement`), `onValueCommitted`, `labels` (`{ name }`, default "Dark theme"), plus Toggle's `variant` [ghost|outline|default|bare] and `size` [sm|default|lg]. It persists nothing — store the choice yourself; the reveal is timed by the motion tokens (the `pace` trait), with no `duration` prop.
345
+
346
+ ```tsx
347
+ <ThemeToggle pressed={theme === "dark"} onPressedChange={(isDark) => setTheme(isDark ? "dark" : "light")} />
348
+ ```
349
+
350
+ ## InlineEdit
351
+
352
+ Click-to-edit single-line text: one `<span contentEditable>` that reads as text until focused (`@nurix/components/element/inline-edit`).
353
+
354
+ **Props:** `value`, `defaultValue`, `placeholder`, `onSave`, `validator` (RegExp), `onValidationError`, `hideHoverEffect`, `readOnly`
355
+
356
+ Enter or blur commits, Escape reverts, and unchanged text skips `onSave`. A labelled field that edits in place is `Cell mode="inline"`.
357
+
358
+ ```tsx
359
+ <InlineEdit value={title} onSave={setTitle} placeholder="Click to add a title" />
360
+ ```
361
+
362
+ ## Reaction
363
+
364
+ Strip of toggleable emoji chips with counts (`@nurix/components/compound/reaction`, plus `reactionChipVariants`).
365
+
366
+ **Props:** `data` (`{ emoji, count? }[]`, sets order and counts), `value` (pressed emoji), `onValueChange(next, { emoji, pressed })`, `onValueCommitted`, `maxVisible`, `size` [xs|sm], `readOnly`, `variant` [default|bare], `labels`
367
+
368
+ **Sub-components:** `Reaction.Chip`, `Reaction.Add`
369
+
370
+ Counts render exactly as `data` gives them, so an optimistic UI steps the count itself. `Reaction.Add` is an empty shell, so give it the glyph.
371
+
372
+ ```tsx
373
+ <Reaction data={counts} value={pressed} onValueChange={(next, { emoji, pressed: isOn }) => { setPressed(next); write(emoji, isOn); }} aria-label="Reactions">
374
+ <Reaction.Add aria-label="Add reaction">
375
+ <SmilePlus className="size-4" />
376
+ </Reaction.Add>
377
+ </Reaction>
378
+ ```
379
+
380
+ ## ActionCluster
381
+
382
+ Record-header action row: up to four inline buttons, pinned actions after a divider, the rest in a "More actions" dropdown (`@nurix/components/compound/action-cluster`).
383
+
384
+ **Props:** `actions` (`ClusterAction<TData>[]`: `label`, `id`, `icon`, `variant`, `intent`, `pinned`, `disabled`, `loading`, `onClick(data, event)`), `data` (required), `size` (default `sm`), `labels` (`group`, `more`)
385
+
386
+ A plain row of buttons is `Row` with `Button`s; hover-revealed icon actions are `ActionBar`.
387
+
388
+ ```tsx
389
+ <ActionCluster
390
+ data={account}
391
+ actions={[
392
+ { label: "Edit", icon: <Pencil />, onClick: openEditor },
393
+ { label: "Delete", intent: "danger", pinned: true, onClick: confirmDelete },
394
+ ]}
395
+ />
396
+ ```
397
+
398
+ ## TiptapEditor
399
+
400
+ Rich-text editor, markdown in and markdown out (`@nurix/components/block/tiptap-editor`, plus `NurixTiptapKit`, `getMarkdown`, `blockMarkdown`; import `@nurix/components/tiptap-editor.css` once; optional peers `@tiptap/*` and `tiptap-markdown`).
401
+
402
+ **Props:** `value`, `defaultValue`, `onValueChange`, `onValueCommitted` (Cmd/Ctrl+Enter or blur), `validate`, `readOnly`, `disabled`, `name`, `extensions`, `toolbar`, `tableControls`, `variant` [default|bare], `labels`
403
+
404
+ **Sub-components:** `TiptapEditor.Toolbar`, `TiptapEditor.TableControls`, `TiptapEditor.TableGridPicker`
405
+
406
+ A controlled `value` is safe, because the editor ignores an echo of its own last emission. Read markdown through `getMarkdown(editor)`, never `editor.storage.markdown`.
407
+
408
+ ```tsx
409
+ <div className="h-96">
410
+ <TiptapEditor defaultValue={doc} onValueCommitted={saveNotes} aria-label="Notes" />
411
+ </div>
412
+ ```
413
+
414
+ ## Fallback-only concepts
415
+
416
+ No nurix primitive — build each from its plain-spec fallback in [index.md](index.md):
417
+
418
+ - `form-validation`
419
+ - `code-editor`