@kitn.ai/ui 0.28.0 → 0.29.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (376) hide show
  1. package/README.md +1 -1
  2. package/dist/agent-tooling/construct/public.d.ts +15 -0
  3. package/dist/agent-tooling/construct/schema.d.ts +111 -0
  4. package/dist/construct-cli.es.js +3 -3
  5. package/dist/construct.d.ts +5 -0
  6. package/dist/construct.js +327 -0
  7. package/dist/core-C8fzo39E.js +1 -12
  8. package/dist/engine-javascript-C1x7zo1_.js +1 -141
  9. package/dist/mcp.es.js +1 -1
  10. package/package.json +16 -16
  11. package/bin/route.test.js +0 -58
  12. package/src/agent-tooling/README.md +0 -192
  13. package/src/agent-tooling/archetypes.ts +0 -205
  14. package/src/agent-tooling/catalog/README.md +0 -676
  15. package/src/agent-tooling/catalog/catalog-types.ts +0 -231
  16. package/src/agent-tooling/catalog/fabrications.ts +0 -96
  17. package/src/agent-tooling/catalog/invariants.ts +0 -284
  18. package/src/agent-tooling/catalog/labs-titles.ts +0 -114
  19. package/src/agent-tooling/catalog/scenarios.ts +0 -87
  20. package/src/agent-tooling/catalog/surfaces.ts +0 -347
  21. package/src/agent-tooling/construct/cli-entry.ts +0 -8
  22. package/src/agent-tooling/construct/cli.ts +0 -147
  23. package/src/agent-tooling/construct/codegen.ts +0 -1635
  24. package/src/agent-tooling/construct/construct.v1.schema.json +0 -319
  25. package/src/agent-tooling/construct/dev.ts +0 -148
  26. package/src/agent-tooling/construct/fixtures/demo-widget.construct.json +0 -7
  27. package/src/agent-tooling/construct/fixtures/ops-console.construct.json +0 -44
  28. package/src/agent-tooling/construct/fixtures/owner-widget.construct.json +0 -32
  29. package/src/agent-tooling/construct/schema.ts +0 -452
  30. package/src/agent-tooling/integrations/anthropic.ts +0 -442
  31. package/src/agent-tooling/integrations/cloudflare.ts +0 -218
  32. package/src/agent-tooling/integrations/langgraph.ts +0 -133
  33. package/src/agent-tooling/integrations/mastra.ts +0 -182
  34. package/src/agent-tooling/integrations/mock.ts +0 -148
  35. package/src/agent-tooling/integrations/ollama.ts +0 -97
  36. package/src/agent-tooling/integrations/openai.ts +0 -95
  37. package/src/agent-tooling/integrations/openrouter.ts +0 -82
  38. package/src/agent-tooling/integrations/pi.ts +0 -176
  39. package/src/agent-tooling/integrations/pydantic-ai.ts +0 -108
  40. package/src/agent-tooling/integrations/vercel-ai-sdk.ts +0 -398
  41. package/src/agent-tooling/mcp/css-raw.d.ts +0 -12
  42. package/src/agent-tooling/mcp/manifest.ts +0 -422
  43. package/src/agent-tooling/mcp/server.ts +0 -116
  44. package/src/agent-tooling/mcp/stdio.ts +0 -18
  45. package/src/agent-tooling/mcp/tools/construct.ts +0 -130
  46. package/src/agent-tooling/mcp/tools/debug.ts +0 -621
  47. package/src/agent-tooling/mcp/tools/reference.ts +0 -980
  48. package/src/agent-tooling/mcp/tools/scaffold.ts +0 -6708
  49. package/src/agent-tooling/mcp/tools/theme.ts +0 -456
  50. package/src/agent-tooling/mcp/tools/types.ts +0 -14
  51. package/src/agent-tooling/mcp/validate-args.ts +0 -141
  52. package/src/agent-tooling/recipes/composed-thread.ts +0 -714
  53. package/src/agent-tooling/recipes/index.ts +0 -22
  54. package/src/agent-tooling/recipes/types.ts +0 -32
  55. package/src/agent-tooling/registry.ts +0 -132
  56. package/src/agent-tooling/route-emit.ts +0 -305
  57. package/src/agent-tooling/types.ts +0 -390
  58. package/src/components/artifact-card.tsx +0 -120
  59. package/src/components/artifact.tsx +0 -914
  60. package/src/components/attachment-types.ts +0 -66
  61. package/src/components/attachments.tsx +0 -536
  62. package/src/components/audio-visualizer/audio-visualizer.voice-fixture.ts +0 -257
  63. package/src/components/audio-visualizer/aurora.glsl.ts +0 -330
  64. package/src/components/audio-visualizer/fit-scale.ts +0 -112
  65. package/src/components/audio-visualizer/index.tsx +0 -528
  66. package/src/components/audio-visualizer/labs/lab-choreography.ts +0 -187
  67. package/src/components/audio-visualizer/labs/lab-shaders.ts +0 -363
  68. package/src/components/audio-visualizer/labs/lab-visualizer.tsx +0 -102
  69. package/src/components/audio-visualizer/shader-canvas.tsx +0 -876
  70. package/src/components/audio-visualizer/sizes.ts +0 -54
  71. package/src/components/audio-visualizer/variant-aurora.tsx +0 -305
  72. package/src/components/audio-visualizer/variant-bar.tsx +0 -175
  73. package/src/components/audio-visualizer/variant-custom.tsx +0 -344
  74. package/src/components/audio-visualizer/variant-grid.tsx +0 -175
  75. package/src/components/audio-visualizer/variant-radial.tsx +0 -163
  76. package/src/components/audio-visualizer/variant-wave.tsx +0 -130
  77. package/src/components/audio-visualizer/wave.glsl.ts +0 -130
  78. package/src/components/card-fallback.tsx +0 -60
  79. package/src/components/card-renderer.tsx +0 -220
  80. package/src/components/card.tsx +0 -212
  81. package/src/components/chain-of-thought.tsx +0 -348
  82. package/src/components/chat-container.tsx +0 -89
  83. package/src/components/chat-scope-picker.tsx +0 -57
  84. package/src/components/chat-thread.tsx +0 -1014
  85. package/src/components/checkpoint.tsx +0 -88
  86. package/src/components/choice-card.tsx +0 -743
  87. package/src/components/coachmark.tsx +0 -249
  88. package/src/components/code-block.tsx +0 -181
  89. package/src/components/composer-dom.ts +0 -159
  90. package/src/components/composer-highlight.ts +0 -242
  91. package/src/components/composer-history.ts +0 -82
  92. package/src/components/composer.tsx +0 -1075
  93. package/src/components/confirm-card.tsx +0 -402
  94. package/src/components/context.tsx +0 -377
  95. package/src/components/conversation-item.tsx +0 -241
  96. package/src/components/conversation-list.tsx +0 -429
  97. package/src/components/conversation-panel.tsx +0 -118
  98. package/src/components/dismissed-stub.tsx +0 -80
  99. package/src/components/embed.tsx +0 -200
  100. package/src/components/empty.tsx +0 -177
  101. package/src/components/feedback-bar.tsx +0 -166
  102. package/src/components/file-tree.tsx +0 -530
  103. package/src/components/file-upload.tsx +0 -167
  104. package/src/components/form-widgets.tsx +0 -525
  105. package/src/components/form.tsx +0 -1296
  106. package/src/components/home-panel.tsx +0 -159
  107. package/src/components/image.tsx +0 -65
  108. package/src/components/link-preview.tsx +0 -195
  109. package/src/components/loader.tsx +0 -367
  110. package/src/components/markdown.tsx +0 -162
  111. package/src/components/message-skills.tsx +0 -40
  112. package/src/components/message.tsx +0 -765
  113. package/src/components/model-switcher.tsx +0 -132
  114. package/src/components/prompt-input.tsx +0 -188
  115. package/src/components/prompt-suggestion.tsx +0 -150
  116. package/src/components/reasoning.tsx +0 -293
  117. package/src/components/response-compare-types.ts +0 -152
  118. package/src/components/response-compare.tsx +0 -455
  119. package/src/components/response-stream.tsx +0 -109
  120. package/src/components/screen.tsx +0 -181
  121. package/src/components/scroll-button.tsx +0 -93
  122. package/src/components/source.tsx +0 -192
  123. package/src/components/tasks-card.tsx +0 -801
  124. package/src/components/text-shimmer.tsx +0 -37
  125. package/src/components/thinking-bar.tsx +0 -50
  126. package/src/components/thread.tsx +0 -228
  127. package/src/components/toast.tsx +0 -503
  128. package/src/components/tool-classify.ts +0 -26
  129. package/src/components/tool-types.ts +0 -49
  130. package/src/components/tool.tsx +0 -190
  131. package/src/components/use-card-resolution.ts +0 -44
  132. package/src/components/voice-input.tsx +0 -222
  133. package/src/components/voice-output.tsx +0 -242
  134. package/src/components/widget-tab-bar.tsx +0 -73
  135. package/src/components/workspace-shell.tsx +0 -285
  136. package/src/diagnostics/hook.ts +0 -352
  137. package/src/diagnostics/index.ts +0 -82
  138. package/src/diagnostics/report-request.ts +0 -296
  139. package/src/elements/agent-card.tsx +0 -67
  140. package/src/elements/artifact.tsx +0 -194
  141. package/src/elements/attachments.tsx +0 -140
  142. package/src/elements/audio-visualizer.tsx +0 -227
  143. package/src/elements/autoloader.ts +0 -89
  144. package/src/elements/avatar.tsx +0 -33
  145. package/src/elements/badge.tsx +0 -32
  146. package/src/elements/button.tsx +0 -149
  147. package/src/elements/card.tsx +0 -133
  148. package/src/elements/cards.tsx +0 -266
  149. package/src/elements/chain-of-thought.tsx +0 -141
  150. package/src/elements/chat-scope-picker.tsx +0 -65
  151. package/src/elements/chat-types.ts +0 -102
  152. package/src/elements/chat-workspace.tsx +0 -188
  153. package/src/elements/chat.tsx +0 -304
  154. package/src/elements/checkbox-group.tsx +0 -164
  155. package/src/elements/checkbox.tsx +0 -134
  156. package/src/elements/checkpoint.tsx +0 -47
  157. package/src/elements/choice.tsx +0 -100
  158. package/src/elements/coachmark.tsx +0 -90
  159. package/src/elements/code-block.tsx +0 -60
  160. package/src/elements/command.tsx +0 -197
  161. package/src/elements/compare.tsx +0 -111
  162. package/src/elements/compiled.css +0 -2
  163. package/src/elements/composer.tsx +0 -162
  164. package/src/elements/confirm-card.tsx +0 -76
  165. package/src/elements/context-meter.tsx +0 -107
  166. package/src/elements/conversation-item.tsx +0 -120
  167. package/src/elements/conversation-list.tsx +0 -203
  168. package/src/elements/css.ts +0 -5
  169. package/src/elements/default-input.tsx +0 -392
  170. package/src/elements/define-entry.ts +0 -12
  171. package/src/elements/define.tsx +0 -563
  172. package/src/elements/diagnostic-events.ts +0 -114
  173. package/src/elements/dialog.tsx +0 -145
  174. package/src/elements/disclosure.ts +0 -95
  175. package/src/elements/dock.tsx +0 -215
  176. package/src/elements/dropdown.tsx +0 -123
  177. package/src/elements/editable-label.tsx +0 -140
  178. package/src/elements/element-data-types.ts +0 -152
  179. package/src/elements/element-diagnostics.ts +0 -392
  180. package/src/elements/element-manifest.json +0 -354
  181. package/src/elements/element-nonscalar.json +0 -187
  182. package/src/elements/element-types.d.ts +0 -4353
  183. package/src/elements/embed.tsx +0 -35
  184. package/src/elements/empty.tsx +0 -29
  185. package/src/elements/feedback-bar.tsx +0 -66
  186. package/src/elements/file-tree.tsx +0 -62
  187. package/src/elements/file-upload.tsx +0 -44
  188. package/src/elements/form.tsx +0 -94
  189. package/src/elements/hover-card.tsx +0 -80
  190. package/src/elements/icon.tsx +0 -41
  191. package/src/elements/image.tsx +0 -32
  192. package/src/elements/input.tsx +0 -492
  193. package/src/elements/kbd.tsx +0 -45
  194. package/src/elements/link-preview.tsx +0 -34
  195. package/src/elements/loader.tsx +0 -25
  196. package/src/elements/markdown.tsx +0 -38
  197. package/src/elements/menu.tsx +0 -241
  198. package/src/elements/message-skills.tsx +0 -83
  199. package/src/elements/message.tsx +0 -361
  200. package/src/elements/model-switcher.tsx +0 -121
  201. package/src/elements/nav.tsx +0 -87
  202. package/src/elements/notice.tsx +0 -53
  203. package/src/elements/pane-grid.tsx +0 -111
  204. package/src/elements/pane-group.tsx +0 -119
  205. package/src/elements/pane.tsx +0 -129
  206. package/src/elements/popover.tsx +0 -81
  207. package/src/elements/progress-bar.tsx +0 -39
  208. package/src/elements/prompt-dock.tsx +0 -82
  209. package/src/elements/prompt-input.tsx +0 -240
  210. package/src/elements/prompt-suggestions.tsx +0 -113
  211. package/src/elements/radio-group.tsx +0 -129
  212. package/src/elements/reasoning.tsx +0 -71
  213. package/src/elements/register-impl.ts +0 -148
  214. package/src/elements/register.ts +0 -76
  215. package/src/elements/remote.tsx +0 -225
  216. package/src/elements/resizable.globals.d.ts +0 -36
  217. package/src/elements/resizable.tsx +0 -704
  218. package/src/elements/response-stream.tsx +0 -40
  219. package/src/elements/screen.tsx +0 -90
  220. package/src/elements/scroll-area.tsx +0 -33
  221. package/src/elements/scroll-button.tsx +0 -179
  222. package/src/elements/search.tsx +0 -185
  223. package/src/elements/segmented.tsx +0 -129
  224. package/src/elements/select.tsx +0 -165
  225. package/src/elements/separator.tsx +0 -36
  226. package/src/elements/setting-item.tsx +0 -50
  227. package/src/elements/settings-group.tsx +0 -37
  228. package/src/elements/skeleton.tsx +0 -45
  229. package/src/elements/slider.tsx +0 -171
  230. package/src/elements/slot-text.ts +0 -73
  231. package/src/elements/slots.ts +0 -787
  232. package/src/elements/source.tsx +0 -126
  233. package/src/elements/status.tsx +0 -42
  234. package/src/elements/styles.css +0 -328
  235. package/src/elements/switch.tsx +0 -118
  236. package/src/elements/tabs.tsx +0 -95
  237. package/src/elements/tasks.tsx +0 -103
  238. package/src/elements/text-shimmer.tsx +0 -28
  239. package/src/elements/thinking-bar.tsx +0 -43
  240. package/src/elements/thread.tsx +0 -139
  241. package/src/elements/toast.tsx +0 -106
  242. package/src/elements/tool.tsx +0 -78
  243. package/src/elements/tooltip.tsx +0 -85
  244. package/src/elements/validate-messages.ts +0 -76
  245. package/src/elements/voice-input.tsx +0 -100
  246. package/src/elements/voice-output.tsx +0 -82
  247. package/src/index.ts +0 -430
  248. package/src/primitives/audio-bands.ts +0 -174
  249. package/src/primitives/card-component-types.ts +0 -63
  250. package/src/primitives/card-contract.ts +0 -80
  251. package/src/primitives/card-data-types.ts +0 -361
  252. package/src/primitives/card-host.tsx +0 -37
  253. package/src/primitives/card-recovery.ts +0 -160
  254. package/src/primitives/card-registry.tsx +0 -78
  255. package/src/primitives/card-resolution.ts +0 -36
  256. package/src/primitives/card-routing.ts +0 -85
  257. package/src/primitives/card-schemas/artifact.schema.json +0 -92
  258. package/src/primitives/card-schemas/card-envelope.schema.json +0 -14
  259. package/src/primitives/card-schemas/card-event.schema.json +0 -12
  260. package/src/primitives/card-schemas/choice.schema.json +0 -75
  261. package/src/primitives/card-schemas/confirm.schema.json +0 -65
  262. package/src/primitives/card-schemas/embed.schema.json +0 -63
  263. package/src/primitives/card-schemas/form.result.schema.json +0 -7
  264. package/src/primitives/card-schemas/form.schema.json +0 -53
  265. package/src/primitives/card-schemas/link.schema.json +0 -54
  266. package/src/primitives/card-schemas/tasks.result.schema.json +0 -16
  267. package/src/primitives/card-schemas/tasks.schema.json +0 -83
  268. package/src/primitives/card-tags.ts +0 -61
  269. package/src/primitives/card-validate-cards.ts +0 -255
  270. package/src/primitives/card-validate-schemas.ts +0 -58
  271. package/src/primitives/card-validate.ts +0 -131
  272. package/src/primitives/chat-config.tsx +0 -103
  273. package/src/primitives/composer-model.ts +0 -49
  274. package/src/primitives/composer-triggers.ts +0 -35
  275. package/src/primitives/controllable.ts +0 -21
  276. package/src/primitives/conversation-store.ts +0 -248
  277. package/src/primitives/create-kai-chat.ts +0 -66
  278. package/src/primitives/create-tween.ts +0 -266
  279. package/src/primitives/embed-providers.ts +0 -254
  280. package/src/primitives/field-mask.ts +0 -256
  281. package/src/primitives/field-semantics.ts +0 -115
  282. package/src/primitives/highlighter.ts +0 -157
  283. package/src/primitives/input-mask.ts +0 -853
  284. package/src/primitives/link-preview.ts +0 -87
  285. package/src/primitives/message-feedback.ts +0 -124
  286. package/src/primitives/pdf-preview.ts +0 -121
  287. package/src/primitives/toast-store.ts +0 -338
  288. package/src/primitives/url-scheme-policy.ts +0 -70
  289. package/src/primitives/use-audio-analysis.ts +0 -340
  290. package/src/primitives/use-auto-resize.ts +0 -31
  291. package/src/primitives/use-resize-observer.ts +0 -45
  292. package/src/primitives/use-sequencer.ts +0 -72
  293. package/src/primitives/use-speech-recognition.ts +0 -146
  294. package/src/primitives/use-stick-to-bottom.ts +0 -75
  295. package/src/primitives/use-text-stream.ts +0 -143
  296. package/src/primitives/use-voice-recorder.ts +0 -62
  297. package/src/primitives/visualizer-sequences.ts +0 -350
  298. package/src/remote/host-embed.ts +0 -345
  299. package/src/remote/index.ts +0 -2
  300. package/src/remote/origin.ts +0 -30
  301. package/src/remote/provider-runtime.ts +0 -262
  302. package/src/remote/provider.ts +0 -2
  303. package/src/remote/validate.ts +0 -22
  304. package/src/remote/version.ts +0 -12
  305. package/src/remote/wire.ts +0 -48
  306. package/src/schemas/from-tool-call.ts +0 -151
  307. package/src/schemas/index.ts +0 -341
  308. package/src/schemas/provider-subsets.ts +0 -538
  309. package/src/schemas/registry.ts +0 -355
  310. package/src/schemas/tool-defs.ts +0 -793
  311. package/src/solid.ts +0 -173
  312. package/src/state/index.ts +0 -44
  313. package/src/state/messages.ts +0 -53
  314. package/src/state/mock.ts +0 -297
  315. package/src/state/parts.ts +0 -295
  316. package/src/state/persistence.ts +0 -180
  317. package/src/state/stream.ts +0 -246
  318. package/src/state/suggestions.ts +0 -9
  319. package/src/state/threads.ts +0 -142
  320. package/src/test-utils/fake-clock.ts +0 -88
  321. package/src/types.ts +0 -95
  322. package/src/ui/action-icons.ts +0 -53
  323. package/src/ui/agent-card.tsx +0 -218
  324. package/src/ui/avatar.tsx +0 -23
  325. package/src/ui/badge.tsx +0 -21
  326. package/src/ui/button.tsx +0 -49
  327. package/src/ui/card.tsx +0 -245
  328. package/src/ui/checkbox-group.tsx +0 -153
  329. package/src/ui/checkbox.tsx +0 -63
  330. package/src/ui/collapsible.tsx +0 -163
  331. package/src/ui/command.tsx +0 -103
  332. package/src/ui/dialog.tsx +0 -223
  333. package/src/ui/dock.tsx +0 -630
  334. package/src/ui/dropdown.tsx +0 -636
  335. package/src/ui/editable-label.tsx +0 -112
  336. package/src/ui/hover-card.tsx +0 -345
  337. package/src/ui/icon.tsx +0 -167
  338. package/src/ui/input.tsx +0 -391
  339. package/src/ui/kbd.tsx +0 -118
  340. package/src/ui/nav.tsx +0 -282
  341. package/src/ui/notice.tsx +0 -84
  342. package/src/ui/overlay.tsx +0 -277
  343. package/src/ui/pane-grid.tsx +0 -117
  344. package/src/ui/pane-group.tsx +0 -291
  345. package/src/ui/pane.tsx +0 -193
  346. package/src/ui/popover.tsx +0 -106
  347. package/src/ui/progress-bar.tsx +0 -83
  348. package/src/ui/prompt-dock.tsx +0 -145
  349. package/src/ui/radio.tsx +0 -150
  350. package/src/ui/resizable.tsx +0 -693
  351. package/src/ui/scroll-area.tsx +0 -26
  352. package/src/ui/segmented.tsx +0 -109
  353. package/src/ui/select.tsx +0 -168
  354. package/src/ui/separator.tsx +0 -10
  355. package/src/ui/settings-group.tsx +0 -67
  356. package/src/ui/skeleton.tsx +0 -73
  357. package/src/ui/slider.tsx +0 -178
  358. package/src/ui/status.tsx +0 -48
  359. package/src/ui/switch.tsx +0 -119
  360. package/src/ui/tabs.tsx +0 -168
  361. package/src/ui/textarea.tsx +0 -21
  362. package/src/ui/tooltip.tsx +0 -118
  363. package/src/utils/cn.ts +0 -30
  364. package/src/wire/chunk.ts +0 -248
  365. package/src/wire/consume.ts +0 -599
  366. package/src/wire/diagnostics.ts +0 -727
  367. package/src/wire/encode-probe.ts +0 -214
  368. package/src/wire/encode.ts +0 -947
  369. package/src/wire/files.ts +0 -342
  370. package/src/wire/formats/anthropic.ts +0 -274
  371. package/src/wire/formats/openai.ts +0 -228
  372. package/src/wire/index.ts +0 -95
  373. package/src/wire/media-types.ts +0 -344
  374. package/src/wire/read.ts +0 -396
  375. package/src/wire/sink-helpers.ts +0 -50
  376. package/src/wire/sse.ts +0 -153
@@ -1,402 +0,0 @@
1
- import {
2
- type JSX,
3
- For,
4
- Show,
5
- splitProps,
6
- mergeProps,
7
- createMemo,
8
- createEffect,
9
- on,
10
- onMount,
11
- ErrorBoundary,
12
- } from 'solid-js';
13
- import { cn } from '../utils/cn';
14
- import { Button } from '../ui/button';
15
- import { Card } from './card';
16
- import { DismissedStub } from './dismissed-stub';
17
- import type { CardEnvelope, CardEvent, CardHost, CardResolution } from '../primitives/card-contract';
18
- import { useCardResolution } from './use-card-resolution';
19
- import { emitCardEvent } from '../primitives/card-routing';
20
- import { useCardHost } from '../primitives/card-host';
21
- import { AlertTriangle, Check } from 'lucide-solid';
22
-
23
- // ─────────────────────────────────────────────────────────────────────────────
24
- // Types (confirm.schema.json) — AUTHORED IN ../primitives/card-data-types.ts.
25
- //
26
- // They used to be declared here, and moving them is the fourth instance of the
27
- // split card-tags.ts and card-component-types.ts already made twice: a type
28
- // import still has to RESOLVE, so while these lived in a `.tsx` no Node/no-DOM
29
- // project could name them (TS6142), which meant `@kitn.ai/ui/schemas` could not
30
- // re-export them and a TypeScript backend building a card envelope got
31
- // `data: unknown` while Python and Go read the full shape out of the published
32
- // JSON Schemas. They are also `type` aliases rather than `interface`s now, which
33
- // is what makes `el.data = myConfirmCardData` compile — see that file's header
34
- // for both measurements. Re-exported here, so this module's surface is unchanged.
35
- // ─────────────────────────────────────────────────────────────────────────────
36
-
37
- import type {
38
- ConfirmAction,
39
- ConfirmActionStyle,
40
- ConfirmCardData,
41
- ConfirmTone,
42
- } from '../primitives/card-data-types';
43
-
44
- export type {
45
- ConfirmAction,
46
- ConfirmActionStyle,
47
- ConfirmCardData,
48
- ConfirmCardEnvelope,
49
- ConfirmTone,
50
- } from '../primitives/card-data-types';
51
-
52
- export const CONFIRM_CARD_TYPE = 'confirm' as const;
53
-
54
- const VALID_STYLES = new Set<ConfirmActionStyle>(['primary', 'default', 'destructive']);
55
-
56
- // ─────────────────────────────────────────────────────────────────────────────
57
- // Pure helpers (unit-tested in isolation).
58
- // ─────────────────────────────────────────────────────────────────────────────
59
-
60
- /** Map an action's `style` to a Button variant (falls back + warns on unknown). */
61
- export function buttonVariantForStyle(
62
- style: ConfirmActionStyle | undefined,
63
- ): 'default' | 'outline' | 'destructive' {
64
- switch (style) {
65
- case 'primary':
66
- return 'default';
67
- case 'destructive':
68
- return 'destructive';
69
- case 'default':
70
- case undefined:
71
- return 'outline';
72
- default:
73
- // eslint-disable-next-line no-console
74
- console.warn(`[kai-confirm] unknown action style "${style as string}"; using default`);
75
- return 'outline';
76
- }
77
- }
78
-
79
- /** De-dupe actions by id (first wins) and validate their shape. Returns the usable
80
- * list + an optional error message when there's nothing renderable. */
81
- export function normalizeActions(actions: unknown): {
82
- actions: ConfirmAction[];
83
- error?: string;
84
- } {
85
- if (!Array.isArray(actions) || actions.length === 0) {
86
- return { actions: [], error: "This card couldn't be displayed." };
87
- }
88
- const seen = new Set<string>();
89
- const out: ConfirmAction[] = [];
90
- for (const a of actions) {
91
- if (!a || typeof a !== 'object') continue;
92
- const action = a as Partial<ConfirmAction>;
93
- if (typeof action.id !== 'string' || action.id.length === 0) continue;
94
- if (typeof action.label !== 'string' || action.label.length === 0) continue;
95
- if (seen.has(action.id)) {
96
- // eslint-disable-next-line no-console
97
- console.warn(`[kai-confirm] duplicate action id "${action.id}" ignored`);
98
- continue;
99
- }
100
- seen.add(action.id);
101
- const style = VALID_STYLES.has(action.style as ConfirmActionStyle)
102
- ? (action.style as ConfirmActionStyle)
103
- : undefined;
104
- if (action.style !== undefined && style === undefined) {
105
- // eslint-disable-next-line no-console
106
- console.warn(`[kai-confirm] unknown action style "${String(action.style)}"; using default`);
107
- }
108
- out.push({
109
- id: action.id,
110
- label: action.label,
111
- style,
112
- payload: action.payload,
113
- default: action.default === true,
114
- });
115
- }
116
- if (out.length === 0) return { actions: [], error: "This card couldn't be displayed." };
117
- return { actions: out };
118
- }
119
-
120
- /** The id of the default action (first with `default:true`), if any. */
121
- export function defaultActionId(actions: ConfirmAction[]): string | undefined {
122
- return actions.find((a) => a.default)?.id;
123
- }
124
-
125
- // ─────────────────────────────────────────────────────────────────────────────
126
- // The <ConfirmCard> component.
127
- // ─────────────────────────────────────────────────────────────────────────────
128
-
129
- /** Imperative handle exposed via `controllerRef` — surfaces the confirm card's
130
- * latent action/dismiss capabilities so the `<kai-confirm>` facade can forward
131
- * them as instance methods (focus/confirm/dismiss/reopen). */
132
- export interface ConfirmController {
133
- /** Focus the default action button (or the first action if none default). */
134
- focus(options?: FocusOptions): void;
135
- /** Activate an action by id (or the default action when omitted) — emits the
136
- * `action` verb + resolves single-shot. */
137
- confirm(actionId?: string): void;
138
- /** Dismiss the card — emits `dismiss` + optimistically collapses to the stub. */
139
- dismiss(): void;
140
- /** Re-open a dismissed card from its stub — emits `reopen`. */
141
- reopen(): void;
142
- }
143
-
144
- export interface ConfirmCardProps {
145
- /** The confirm definition (CardEnvelope.data). */
146
- data?: ConfirmCardData;
147
- /** The card id used to correlate every emitted CardEvent. */
148
- cardId?: string;
149
- /** The envelope title rendered in the card chrome. */
150
- heading?: string;
151
- /** Optional explicit CardHost (otherwise read from a CardProvider, otherwise the
152
- * bubbling `kai-card` CustomEvent off `hostElement`). */
153
- host?: CardHost;
154
- /** The custom-element host node, for the bubbling `kai-card` fallback emit. */
155
- hostElement?: HTMLElement;
156
- /** Focus the default action on mount. Default OFF (no focus-stealing mid-stream). */
157
- autofocus?: boolean;
158
- class?: string;
159
- /** When set, render the chromed read-only view instead of the buttons. */
160
- resolution?: CardResolution;
161
- /** Receive the imperative controller once mounted. The `<kai-confirm>` facade
162
- * forwards these as element methods (focus/confirm/dismiss/reopen). */
163
- controllerRef?: (controller: ConfirmController) => void;
164
- }
165
-
166
- /**
167
- * `ConfirmCard` — a named-intent approval card. Renders a title + body + a small
168
- * set of action buttons inside `Card` chrome. Activating an action emits the Card
169
- * contract's `action` verb (`{ kind:'action', cardId, action, payload }`) and
170
- * resolves the card (other actions disabled, the chosen one marked) so the same
171
- * approval can't double-fire. Emits `ready` on mount, `dismiss` for the optional
172
- * close affordance, and `error` for an unusable definition (inline error state).
173
- */
174
- export function ConfirmCard(props: ConfirmCardProps): JSX.Element {
175
- const merged = mergeProps({ cardId: 'kai-confirm', autofocus: false }, props);
176
- const [local] = splitProps(merged, [
177
- 'data',
178
- 'cardId',
179
- 'heading',
180
- 'host',
181
- 'hostElement',
182
- 'autofocus',
183
- 'class',
184
- 'resolution',
185
- 'controllerRef',
186
- ]);
187
-
188
- const ctxHost = useCardHost();
189
-
190
- const emit = (event: CardEvent): void => {
191
- const h = local.host ?? ctxHost;
192
- if (h) h.emit(event);
193
- else if (local.hostElement) emitCardEvent(local.hostElement, event);
194
- };
195
-
196
- const normalized = createMemo(() => normalizeActions(local.data?.actions));
197
- const valid = createMemo(() => normalized().error === undefined);
198
- const errorMessage = createMemo(() => normalized().error ?? '');
199
- const actions = createMemo(() => normalized().actions);
200
- const tone = (): ConfirmTone => local.data?.tone ?? 'default';
201
- const isDanger = () => tone() === 'danger';
202
- const defaultId = createMemo(() => defaultActionId(actions()));
203
-
204
- const res = useCardResolution({ prop: () => local.resolution, data: () => local.data });
205
-
206
- const resolvedAction = createMemo(() => {
207
- const r = res.resolution();
208
- if (!r || r.kind !== 'action') return undefined;
209
- const found = actions().find((a) => a.id === r.action);
210
- return { label: found?.label ?? r.action };
211
- });
212
-
213
- // ready / error lifecycle emits.
214
- createEffect(
215
- on(valid, (ok) => {
216
- if (ok) emit({ kind: 'ready', cardId: local.cardId });
217
- else emit({ kind: 'error', cardId: local.cardId, message: errorMessage() });
218
- }),
219
- );
220
-
221
- const onAction = (action: ConfirmAction): void => {
222
- if (res.isResolved()) return; // single-shot
223
- emit({
224
- kind: 'action',
225
- cardId: local.cardId,
226
- action: action.id,
227
- ...(action.payload !== undefined ? { payload: action.payload } : {}),
228
- });
229
- res.setLocal({
230
- kind: 'action',
231
- action: action.id,
232
- ...(action.payload !== undefined ? { payload: action.payload } : {}),
233
- });
234
- };
235
-
236
- // Dismiss: emit the contract `dismiss` AND optimistically flip to a `dismissed`
237
- // resolution so the card collapses to its re-openable stub immediately (the host
238
- // confirms/overrides via the resolution prop on the next render).
239
- const onDismiss = (): void => {
240
- if (res.isResolved()) return;
241
- emit({ kind: 'dismiss', cardId: local.cardId });
242
- res.setLocal({ kind: 'dismissed' });
243
- };
244
- const onReopen = (): void => emit({ kind: 'reopen', cardId: local.cardId });
245
-
246
- let bodyRef: HTMLDivElement | undefined;
247
- let actionsRef: HTMLDivElement | undefined;
248
-
249
- // The default action button (or the first action button if none is marked
250
- // default) — the same target the `autofocus` prop focuses on mount.
251
- const focusTarget = (): HTMLButtonElement | null => {
252
- if (!actionsRef) return null;
253
- return (
254
- actionsRef.querySelector<HTMLButtonElement>('button[data-kai-default="true"]') ??
255
- actionsRef.querySelector<HTMLButtonElement>('button[data-action-id]')
256
- );
257
- };
258
-
259
- // ── Imperative controller (Pattern C): hand the facade a handle over the
260
- // card's latent action/dismiss capabilities. Every method drives the SAME
261
- // internal path the buttons drive, so the same kai-card events fire. ──────────
262
- onMount(() => {
263
- local.controllerRef?.({
264
- // Focus the default (or first) action button on demand.
265
- focus: (options) => focusTarget()?.focus(options),
266
- // Activate an action by id (or the default action with no id) — the button path.
267
- confirm: (actionId) => {
268
- const list = actions();
269
- const id = actionId ?? defaultId();
270
- if (id === undefined) return;
271
- const action = list.find((a) => a.id === id);
272
- if (action) onAction(action);
273
- },
274
- // Trigger the dismiss path (the X-button path).
275
- dismiss: () => onDismiss(),
276
- // Re-open a dismissed card (the stub's affordance path).
277
- reopen: () => onReopen(),
278
- });
279
- });
280
-
281
- // Surface the resolved action id for host styling.
282
- createEffect(() => {
283
- const el = local.hostElement;
284
- if (!el) return;
285
- const r = res.resolution();
286
- if (r && r.kind === 'action') el.setAttribute('data-kai-resolved', r.action);
287
- else el.removeAttribute('data-kai-resolved');
288
- });
289
-
290
- return (
291
- <Show when={valid()} fallback={<Card heading={local.heading} errorMessage={errorMessage()} />}>
292
- <ErrorBoundary
293
- fallback={() => {
294
- emit({ kind: 'error', cardId: local.cardId, message: 'The card failed to render.' });
295
- return <Card heading={local.heading} errorMessage="The card failed to render." />;
296
- }}
297
- >
298
- <Show
299
- when={!res.isDeferred()}
300
- fallback={<DismissedStub type={CONFIRM_CARD_TYPE} title={local.heading} onReopen={onReopen} />}
301
- >
302
- <Card
303
- heading={local.heading}
304
- actions={
305
- <Show
306
- when={!res.isResolved()}
307
- fallback={
308
- <div
309
- class="ml-auto flex items-center gap-2 text-sm font-medium text-foreground"
310
- role={res.isOptimistic() ? 'status' : undefined}
311
- >
312
- <Check size={16} aria-hidden="true" />
313
- <span>{resolvedAction()?.label}</span>
314
- </div>
315
- }
316
- >
317
- <div class="flex w-full flex-wrap items-center justify-between gap-2">
318
- <Show when={local.data?.dismissible === true}>
319
- {/* The contract `dismiss` verb — a footer ACTION (it emits
320
- `{kind:'dismiss'}` and collapses the card to a re-openable
321
- stub), not the chrome close in `Card`'s own `dismissible`
322
- prop, which the contract cards deliberately never set.
323
- Labelled rather than a bare ghost ✕: every other control in
324
- this row says what it does, and an unlabelled 28px glyph
325
- 300px from the primary button reads as stray chrome. Same
326
- shape in all four cards — `form` already looked like this.
327
- `aria-label` is kept (redundant with the text) so the
328
- element tests that select on it keep working. */}
329
- <Button
330
- type="button"
331
- variant="ghost"
332
- aria-label="Dismiss"
333
- onClick={onDismiss}
334
- >
335
- Dismiss
336
- </Button>
337
- </Show>
338
- <div ref={actionsRef} class="ml-auto flex flex-wrap items-center gap-2">
339
- <For each={actions()}>
340
- {(action) => (
341
- <Button
342
- type="button"
343
- variant={buttonVariantForStyle(action.style)}
344
- data-action-id={action.id}
345
- data-kai-default={action.default ? 'true' : undefined}
346
- ref={(el) => {
347
- if (local.autofocus && action.id === defaultId()) {
348
- queueMicrotask(() => el.focus());
349
- }
350
- }}
351
- onClick={() => onAction(action)}
352
- >
353
- {action.label}
354
- </Button>
355
- )}
356
- </For>
357
- </div>
358
- </div>
359
- </Show>
360
- }
361
- >
362
- <div
363
- ref={bodyRef}
364
- class={cn('flex flex-col gap-2', local.class)}
365
- onKeyDown={(e) => {
366
- // Enter on the card body (not on a focused button) invokes the default action.
367
- if (e.key !== 'Enter') return;
368
- const target = e.target as HTMLElement;
369
- if (target.tagName === 'BUTTON') return;
370
- const id = defaultId();
371
- if (id === undefined) return;
372
- const action = actions().find((a) => a.id === id);
373
- if (action) onAction(action);
374
- }}
375
- tabindex={defaultId() !== undefined ? 0 : undefined}
376
- >
377
- <Show when={isDanger()}>
378
- <div class="flex items-center gap-2 text-sm font-medium text-destructive dark:text-red-400">
379
- <AlertTriangle size={16} class="shrink-0" aria-hidden="true" />
380
- <span>Heads up</span>
381
- </div>
382
- </Show>
383
- <Show when={local.data?.heading}>
384
- <p class="text-sm font-semibold text-foreground">{local.data?.heading}</p>
385
- </Show>
386
- <Show when={local.data?.body}>
387
- {/* The supporting sentence sits directly under the heading it
388
- supports, muted — the same contrast step the shell header makes
389
- between `heading` and `description`. It stays in the BODY (not
390
- the shell's `description` slot, where `form`'s and `choice`'s
391
- live) because `data.heading` is a documented IN-BODY heading
392
- that this sentence has to stay beneath, and because this div is
393
- the Enter-to-confirm tab stop. */}
394
- <p class="text-sm text-muted-foreground">{local.data?.body}</p>
395
- </Show>
396
- </div>
397
- </Card>
398
- </Show>
399
- </ErrorBoundary>
400
- </Show>
401
- );
402
- }