@wallarm-org/claude-design 0.0.0-stage → 4.8.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 (963) hide show
  1. package/README.md +114 -2
  2. package/bin/claude-design.js +347 -0
  3. package/dist/SETUP.md +130 -0
  4. package/dist/UPDATE.md +84 -0
  5. package/dist/assets/Icons/activity.svg +1 -0
  6. package/dist/assets/Icons/alt.svg +1 -0
  7. package/dist/assets/Icons/android-neutral.svg +1 -0
  8. package/dist/assets/Icons/android.svg +1 -0
  9. package/dist/assets/Icons/api.svg +1 -0
  10. package/dist/assets/Icons/apple-music-neutral.svg +1 -0
  11. package/dist/assets/Icons/apple-music.svg +1 -0
  12. package/dist/assets/Icons/apple-neutral.svg +1 -0
  13. package/dist/assets/Icons/apple-podcasts-neutral.svg +1 -0
  14. package/dist/assets/Icons/apple-podcasts.svg +1 -0
  15. package/dist/assets/Icons/apple.svg +1 -0
  16. package/dist/assets/Icons/archive.svg +1 -0
  17. package/dist/assets/Icons/arrow-big-up-dash.svg +1 -0
  18. package/dist/assets/Icons/arrow-big-up.svg +1 -0
  19. package/dist/assets/Icons/arrow-down-left.svg +1 -0
  20. package/dist/assets/Icons/arrow-down-right.svg +1 -0
  21. package/dist/assets/Icons/arrow-down.svg +1 -0
  22. package/dist/assets/Icons/arrow-left.svg +1 -0
  23. package/dist/assets/Icons/arrow-right-alt.svg +1 -0
  24. package/dist/assets/Icons/arrow-right.svg +1 -0
  25. package/dist/assets/Icons/arrow-up-down.svg +1 -0
  26. package/dist/assets/Icons/arrow-up-left.svg +1 -0
  27. package/dist/assets/Icons/arrow-up-right.svg +1 -0
  28. package/dist/assets/Icons/arrow-up.svg +1 -0
  29. package/dist/assets/Icons/artstation-neutral.svg +1 -0
  30. package/dist/assets/Icons/artstation.svg +1 -0
  31. package/dist/assets/Icons/asterisk.svg +1 -0
  32. package/dist/assets/Icons/audio-waveform.svg +1 -0
  33. package/dist/assets/Icons/aws-neutral.svg +1 -0
  34. package/dist/assets/Icons/aws.svg +1 -0
  35. package/dist/assets/Icons/badge-check.svg +1 -0
  36. package/dist/assets/Icons/baidu-neutral.svg +1 -0
  37. package/dist/assets/Icons/baidu.svg +1 -0
  38. package/dist/assets/Icons/bandage.svg +1 -0
  39. package/dist/assets/Icons/behance-neutral.svg +1 -0
  40. package/dist/assets/Icons/behance.svg +1 -0
  41. package/dist/assets/Icons/bell-cog.svg +1 -0
  42. package/dist/assets/Icons/bell.svg +1 -0
  43. package/dist/assets/Icons/binary.svg +1 -0
  44. package/dist/assets/Icons/book-open.svg +1 -0
  45. package/dist/assets/Icons/book.svg +1 -0
  46. package/dist/assets/Icons/boosty-neutral.svg +1 -0
  47. package/dist/assets/Icons/boosty.svg +1 -0
  48. package/dist/assets/Icons/bot.svg +1 -0
  49. package/dist/assets/Icons/brain.svg +1 -0
  50. package/dist/assets/Icons/braze-neutral.svg +1 -0
  51. package/dist/assets/Icons/braze.svg +1 -0
  52. package/dist/assets/Icons/brevo-neutral.svg +1 -0
  53. package/dist/assets/Icons/brevo.svg +1 -0
  54. package/dist/assets/Icons/bug-off.svg +1 -0
  55. package/dist/assets/Icons/bug-play.svg +1 -0
  56. package/dist/assets/Icons/bug.svg +1 -0
  57. package/dist/assets/Icons/building.svg +1 -0
  58. package/dist/assets/Icons/building2.svg +1 -0
  59. package/dist/assets/Icons/calendar-check.svg +1 -0
  60. package/dist/assets/Icons/calendar-check2.svg +1 -0
  61. package/dist/assets/Icons/calendar-clock.svg +1 -0
  62. package/dist/assets/Icons/calendar-cog.svg +1 -0
  63. package/dist/assets/Icons/calendar-days.svg +1 -0
  64. package/dist/assets/Icons/calendar-fold.svg +1 -0
  65. package/dist/assets/Icons/calendar-heart.svg +1 -0
  66. package/dist/assets/Icons/calendar-minus.svg +1 -0
  67. package/dist/assets/Icons/calendar-minus2.svg +1 -0
  68. package/dist/assets/Icons/calendar-off.svg +1 -0
  69. package/dist/assets/Icons/calendar-plus.svg +1 -0
  70. package/dist/assets/Icons/calendar-plus2.svg +1 -0
  71. package/dist/assets/Icons/calendar-range.svg +1 -0
  72. package/dist/assets/Icons/calendar-search.svg +1 -0
  73. package/dist/assets/Icons/calendar-x.svg +1 -0
  74. package/dist/assets/Icons/calendar-x2.svg +1 -0
  75. package/dist/assets/Icons/calendar.svg +1 -0
  76. package/dist/assets/Icons/chart-area.svg +1 -0
  77. package/dist/assets/Icons/chart-bar.svg +1 -0
  78. package/dist/assets/Icons/chart-column.svg +1 -0
  79. package/dist/assets/Icons/chart-no-axes-column.svg +1 -0
  80. package/dist/assets/Icons/check-check.svg +1 -0
  81. package/dist/assets/Icons/check.svg +1 -0
  82. package/dist/assets/Icons/checkbox-indeterminate.svg +1 -0
  83. package/dist/assets/Icons/chevron-down.svg +1 -0
  84. package/dist/assets/Icons/chevron-first.svg +1 -0
  85. package/dist/assets/Icons/chevron-last.svg +1 -0
  86. package/dist/assets/Icons/chevron-left.svg +1 -0
  87. package/dist/assets/Icons/chevron-right.svg +1 -0
  88. package/dist/assets/Icons/chevron-up-down.svg +1 -0
  89. package/dist/assets/Icons/chevron-up.svg +1 -0
  90. package/dist/assets/Icons/chevrons-down-up.svg +1 -0
  91. package/dist/assets/Icons/chevrons-down.svg +1 -0
  92. package/dist/assets/Icons/chevrons-left-right.svg +1 -0
  93. package/dist/assets/Icons/chevrons-left.svg +1 -0
  94. package/dist/assets/Icons/chevrons-right-left.svg +1 -0
  95. package/dist/assets/Icons/chevrons-right.svg +1 -0
  96. package/dist/assets/Icons/chevrons-up-down.svg +1 -0
  97. package/dist/assets/Icons/chevrons-up.svg +1 -0
  98. package/dist/assets/Icons/circle-arrow-down.svg +1 -0
  99. package/dist/assets/Icons/circle-arrow-left.svg +1 -0
  100. package/dist/assets/Icons/circle-arrow-out-down-left.svg +1 -0
  101. package/dist/assets/Icons/circle-arrow-out-down-right.svg +1 -0
  102. package/dist/assets/Icons/circle-arrow-out-up-left.svg +1 -0
  103. package/dist/assets/Icons/circle-arrow-out-up-right.svg +1 -0
  104. package/dist/assets/Icons/circle-arrow-right.svg +1 -0
  105. package/dist/assets/Icons/circle-arrow-up.svg +1 -0
  106. package/dist/assets/Icons/circle-check-big.svg +1 -0
  107. package/dist/assets/Icons/circle-check.svg +1 -0
  108. package/dist/assets/Icons/circle-chevron-down.svg +1 -0
  109. package/dist/assets/Icons/circle-chevron-left.svg +1 -0
  110. package/dist/assets/Icons/circle-chevron-right.svg +1 -0
  111. package/dist/assets/Icons/circle-chevron-up.svg +1 -0
  112. package/dist/assets/Icons/circle-dashed.svg +1 -0
  113. package/dist/assets/Icons/circle-dot-dashed.svg +1 -0
  114. package/dist/assets/Icons/circle-ellipsis.svg +1 -0
  115. package/dist/assets/Icons/circle-gauge.svg +1 -0
  116. package/dist/assets/Icons/circle-help.svg +1 -0
  117. package/dist/assets/Icons/circle-minus.svg +1 -0
  118. package/dist/assets/Icons/circle-plus.svg +1 -0
  119. package/dist/assets/Icons/circle-slash.svg +1 -0
  120. package/dist/assets/Icons/circle-slash2.svg +1 -0
  121. package/dist/assets/Icons/circle-stop.svg +1 -0
  122. package/dist/assets/Icons/circle.svg +1 -0
  123. package/dist/assets/Icons/clipboard-check.svg +1 -0
  124. package/dist/assets/Icons/clipboard-x.svg +1 -0
  125. package/dist/assets/Icons/clipboard.svg +1 -0
  126. package/dist/assets/Icons/clock-fading.svg +1 -0
  127. package/dist/assets/Icons/clock.svg +1 -0
  128. package/dist/assets/Icons/cloud-off.svg +1 -0
  129. package/dist/assets/Icons/cloud-sync.svg +1 -0
  130. package/dist/assets/Icons/cloud.svg +1 -0
  131. package/dist/assets/Icons/code.svg +1 -0
  132. package/dist/assets/Icons/collapse.svg +1 -0
  133. package/dist/assets/Icons/columns3-cog.svg +1 -0
  134. package/dist/assets/Icons/command.svg +1 -0
  135. package/dist/assets/Icons/compass.svg +1 -0
  136. package/dist/assets/Icons/copy-slash.svg +1 -0
  137. package/dist/assets/Icons/copy.svg +1 -0
  138. package/dist/assets/Icons/corner-down-left.svg +1 -0
  139. package/dist/assets/Icons/corner-down-right.svg +1 -0
  140. package/dist/assets/Icons/corner-left-down.svg +1 -0
  141. package/dist/assets/Icons/corner-left-up.svg +1 -0
  142. package/dist/assets/Icons/corner-right-down.svg +1 -0
  143. package/dist/assets/Icons/corner-right-up.svg +1 -0
  144. package/dist/assets/Icons/corner-up-left.svg +1 -0
  145. package/dist/assets/Icons/corner-up-right.svg +1 -0
  146. package/dist/assets/Icons/cpu.svg +1 -0
  147. package/dist/assets/Icons/credit-card.svg +1 -0
  148. package/dist/assets/Icons/crosshair.svg +1 -0
  149. package/dist/assets/Icons/ctrl.svg +1 -0
  150. package/dist/assets/Icons/database.svg +1 -0
  151. package/dist/assets/Icons/devianart-neutral.svg +1 -0
  152. package/dist/assets/Icons/devianart.svg +1 -0
  153. package/dist/assets/Icons/discord-neutral.svg +1 -0
  154. package/dist/assets/Icons/discord.svg +1 -0
  155. package/dist/assets/Icons/dollar-sign.svg +1 -0
  156. package/dist/assets/Icons/dot.svg +1 -0
  157. package/dist/assets/Icons/download.svg +1 -0
  158. package/dist/assets/Icons/dprofile-neutral.svg +1 -0
  159. package/dist/assets/Icons/dprofile.svg +1 -0
  160. package/dist/assets/Icons/dribbble-neutral.svg +1 -0
  161. package/dist/assets/Icons/dribbble.svg +1 -0
  162. package/dist/assets/Icons/droplet.svg +1 -0
  163. package/dist/assets/Icons/duplicate.svg +1 -0
  164. package/dist/assets/Icons/dzen-neutral.svg +1 -0
  165. package/dist/assets/Icons/dzen.svg +1 -0
  166. package/dist/assets/Icons/earth-lock.svg +1 -0
  167. package/dist/assets/Icons/earth.svg +1 -0
  168. package/dist/assets/Icons/ellipsis.svg +1 -0
  169. package/dist/assets/Icons/expand.svg +1 -0
  170. package/dist/assets/Icons/external-link.svg +1 -0
  171. package/dist/assets/Icons/eye-off.svg +1 -0
  172. package/dist/assets/Icons/eye.svg +1 -0
  173. package/dist/assets/Icons/facebook-neutral.svg +1 -0
  174. package/dist/assets/Icons/facebook.svg +1 -0
  175. package/dist/assets/Icons/fast-forward.svg +1 -0
  176. package/dist/assets/Icons/figma-neutral.svg +1 -0
  177. package/dist/assets/Icons/figma.svg +1 -0
  178. package/dist/assets/Icons/file-down.svg +1 -0
  179. package/dist/assets/Icons/file-plus.svg +1 -0
  180. package/dist/assets/Icons/file-text.svg +1 -0
  181. package/dist/assets/Icons/file.svg +1 -0
  182. package/dist/assets/Icons/film.svg +1 -0
  183. package/dist/assets/Icons/filter-x.svg +1 -0
  184. package/dist/assets/Icons/filter.svg +1 -0
  185. package/dist/assets/Icons/fingerprint-pattern.svg +1 -0
  186. package/dist/assets/Icons/flag.svg +1 -0
  187. package/dist/assets/Icons/flask-conical.svg +1 -0
  188. package/dist/assets/Icons/folder.svg +1 -0
  189. package/dist/assets/Icons/funnel-plus.svg +1 -0
  190. package/dist/assets/Icons/ghost.svg +1 -0
  191. package/dist/assets/Icons/git-branch.svg +1 -0
  192. package/dist/assets/Icons/git-commit-horizontal.svg +1 -0
  193. package/dist/assets/Icons/git-commit-vertical.svg +1 -0
  194. package/dist/assets/Icons/git-compare-arrows-alt.svg +1 -0
  195. package/dist/assets/Icons/git-compare-arrows.svg +1 -0
  196. package/dist/assets/Icons/git-compare.svg +1 -0
  197. package/dist/assets/Icons/git-fork.svg +1 -0
  198. package/dist/assets/Icons/git-graph.svg +1 -0
  199. package/dist/assets/Icons/git-merge.svg +1 -0
  200. package/dist/assets/Icons/git-pull-request-arrow.svg +1 -0
  201. package/dist/assets/Icons/git-pull-request-closed.svg +1 -0
  202. package/dist/assets/Icons/git-pull-request-create-arrow.svg +1 -0
  203. package/dist/assets/Icons/git-pull-request-create.svg +1 -0
  204. package/dist/assets/Icons/git-pull-request-draft.svg +1 -0
  205. package/dist/assets/Icons/git-pull-request.svg +1 -0
  206. package/dist/assets/Icons/github-neutral.svg +1 -0
  207. package/dist/assets/Icons/github.svg +1 -0
  208. package/dist/assets/Icons/globe-lock.svg +1 -0
  209. package/dist/assets/Icons/globe.svg +1 -0
  210. package/dist/assets/Icons/gmail-neutral.svg +1 -0
  211. package/dist/assets/Icons/gmail.svg +1 -0
  212. package/dist/assets/Icons/google-meet-neutral.svg +1 -0
  213. package/dist/assets/Icons/google-meet.svg +1 -0
  214. package/dist/assets/Icons/google-neutral.svg +1 -0
  215. package/dist/assets/Icons/google-play-neutral.svg +1 -0
  216. package/dist/assets/Icons/google-play.svg +1 -0
  217. package/dist/assets/Icons/google-podcast-neutral.svg +1 -0
  218. package/dist/assets/Icons/google-podcast.svg +1 -0
  219. package/dist/assets/Icons/google.svg +1 -0
  220. package/dist/assets/Icons/grip-horizontal.svg +1 -0
  221. package/dist/assets/Icons/grip-vertical.svg +1 -0
  222. package/dist/assets/Icons/hammer.svg +1 -0
  223. package/dist/assets/Icons/heart-pulse.svg +1 -0
  224. package/dist/assets/Icons/history.svg +1 -0
  225. package/dist/assets/Icons/home.svg +1 -0
  226. package/dist/assets/Icons/imo-neutral.svg +1 -0
  227. package/dist/assets/Icons/imo.svg +1 -0
  228. package/dist/assets/Icons/infinity.svg +1 -0
  229. package/dist/assets/Icons/info.svg +1 -0
  230. package/dist/assets/Icons/infobip-neutral.svg +1 -0
  231. package/dist/assets/Icons/infobip.svg +1 -0
  232. package/dist/assets/Icons/instagram-neutral.svg +1 -0
  233. package/dist/assets/Icons/instagram.svg +1 -0
  234. package/dist/assets/Icons/key-round.svg +1 -0
  235. package/dist/assets/Icons/key-square.svg +1 -0
  236. package/dist/assets/Icons/keyboard.svg +1 -0
  237. package/dist/assets/Icons/kickstarter-neutral.svg +1 -0
  238. package/dist/assets/Icons/kickstarter.svg +1 -0
  239. package/dist/assets/Icons/laptop.svg +1 -0
  240. package/dist/assets/Icons/layers.svg +1 -0
  241. package/dist/assets/Icons/layers2.svg +1 -0
  242. package/dist/assets/Icons/layers3.svg +1 -0
  243. package/dist/assets/Icons/layout-dashboard.svg +1 -0
  244. package/dist/assets/Icons/layout-grid.svg +1 -0
  245. package/dist/assets/Icons/layout-list.svg +1 -0
  246. package/dist/assets/Icons/layout-panel-left.svg +1 -0
  247. package/dist/assets/Icons/layout-panel-top.svg +1 -0
  248. package/dist/assets/Icons/layout-template.svg +1 -0
  249. package/dist/assets/Icons/library-big.svg +1 -0
  250. package/dist/assets/Icons/life-buoy.svg +1 -0
  251. package/dist/assets/Icons/lightbulb-off.svg +1 -0
  252. package/dist/assets/Icons/lightbulb.svg +1 -0
  253. package/dist/assets/Icons/line-neutral.svg +1 -0
  254. package/dist/assets/Icons/line.svg +1 -0
  255. package/dist/assets/Icons/link.svg +1 -0
  256. package/dist/assets/Icons/link2-off.svg +1 -0
  257. package/dist/assets/Icons/link2.svg +1 -0
  258. package/dist/assets/Icons/linkedin-neutral.svg +1 -0
  259. package/dist/assets/Icons/linkedin.svg +1 -0
  260. package/dist/assets/Icons/list-checks.svg +1 -0
  261. package/dist/assets/Icons/list-collapse.svg +1 -0
  262. package/dist/assets/Icons/list-filter.svg +1 -0
  263. package/dist/assets/Icons/list-minus.svg +1 -0
  264. package/dist/assets/Icons/list-ordered.svg +1 -0
  265. package/dist/assets/Icons/list-plus.svg +1 -0
  266. package/dist/assets/Icons/list-restart.svg +1 -0
  267. package/dist/assets/Icons/list-start.svg +1 -0
  268. package/dist/assets/Icons/list-todo.svg +1 -0
  269. package/dist/assets/Icons/list-tree.svg +1 -0
  270. package/dist/assets/Icons/list-video.svg +1 -0
  271. package/dist/assets/Icons/list-x.svg +1 -0
  272. package/dist/assets/Icons/list.svg +1 -0
  273. package/dist/assets/Icons/loader-circle.svg +1 -0
  274. package/dist/assets/Icons/loader.svg +1 -0
  275. package/dist/assets/Icons/lock-open.svg +1 -0
  276. package/dist/assets/Icons/lock.svg +1 -0
  277. package/dist/assets/Icons/log-in.svg +1 -0
  278. package/dist/assets/Icons/log-out.svg +1 -0
  279. package/dist/assets/Icons/mail-check.svg +1 -0
  280. package/dist/assets/Icons/mail-minus.svg +1 -0
  281. package/dist/assets/Icons/mail-open.svg +1 -0
  282. package/dist/assets/Icons/mail-plus.svg +1 -0
  283. package/dist/assets/Icons/mail-question.svg +1 -0
  284. package/dist/assets/Icons/mail-search.svg +1 -0
  285. package/dist/assets/Icons/mail-warning.svg +1 -0
  286. package/dist/assets/Icons/mail-x.svg +1 -0
  287. package/dist/assets/Icons/mail.svg +1 -0
  288. package/dist/assets/Icons/mailbox.svg +1 -0
  289. package/dist/assets/Icons/mailersend-neutral.svg +1 -0
  290. package/dist/assets/Icons/mailersend.svg +1 -0
  291. package/dist/assets/Icons/mailgun-neutral.svg +1 -0
  292. package/dist/assets/Icons/mailgun.svg +1 -0
  293. package/dist/assets/Icons/mailjet-neutral.svg +1 -0
  294. package/dist/assets/Icons/mailjet.svg +1 -0
  295. package/dist/assets/Icons/mails.svg +1 -0
  296. package/dist/assets/Icons/mailtrap-neutral.svg +1 -0
  297. package/dist/assets/Icons/mailtrap.svg +1 -0
  298. package/dist/assets/Icons/mandrill-neutral.svg +1 -0
  299. package/dist/assets/Icons/mandrill.svg +1 -0
  300. package/dist/assets/Icons/map-pin.svg +1 -0
  301. package/dist/assets/Icons/maqsam-neutral.svg +1 -0
  302. package/dist/assets/Icons/maqsam.svg +1 -0
  303. package/dist/assets/Icons/mattermost-neutral.svg +1 -0
  304. package/dist/assets/Icons/mattermost.svg +1 -0
  305. package/dist/assets/Icons/maximize.svg +1 -0
  306. package/dist/assets/Icons/maximize2.svg +1 -0
  307. package/dist/assets/Icons/medium-neutral.svg +1 -0
  308. package/dist/assets/Icons/medium.svg +1 -0
  309. package/dist/assets/Icons/megaphone-off.svg +1 -0
  310. package/dist/assets/Icons/megaphone.svg +1 -0
  311. package/dist/assets/Icons/menu.svg +1 -0
  312. package/dist/assets/Icons/merge.svg +1 -0
  313. package/dist/assets/Icons/message-square-code.svg +1 -0
  314. package/dist/assets/Icons/message-square-dashed.svg +1 -0
  315. package/dist/assets/Icons/message-square-diff.svg +1 -0
  316. package/dist/assets/Icons/message-square-dot.svg +1 -0
  317. package/dist/assets/Icons/message-square-heart.svg +1 -0
  318. package/dist/assets/Icons/message-square-off.svg +1 -0
  319. package/dist/assets/Icons/message-square-plus.svg +1 -0
  320. package/dist/assets/Icons/message-square-quote.svg +1 -0
  321. package/dist/assets/Icons/message-square-reply.svg +1 -0
  322. package/dist/assets/Icons/message-square-share.svg +1 -0
  323. package/dist/assets/Icons/message-square-text.svg +1 -0
  324. package/dist/assets/Icons/message-square-warning.svg +1 -0
  325. package/dist/assets/Icons/message-square-x.svg +1 -0
  326. package/dist/assets/Icons/message-square.svg +1 -0
  327. package/dist/assets/Icons/messages-square.svg +1 -0
  328. package/dist/assets/Icons/messenger-neutral.svg +1 -0
  329. package/dist/assets/Icons/messenger.svg +1 -0
  330. package/dist/assets/Icons/microsoft-neutral.svg +1 -0
  331. package/dist/assets/Icons/microsoft-teams-neutral.svg +1 -0
  332. package/dist/assets/Icons/microsoft-teams.svg +1 -0
  333. package/dist/assets/Icons/microsoft.svg +1 -0
  334. package/dist/assets/Icons/minimize.svg +1 -0
  335. package/dist/assets/Icons/minimize2.svg +1 -0
  336. package/dist/assets/Icons/minus.svg +1 -0
  337. package/dist/assets/Icons/moon.svg +1 -0
  338. package/dist/assets/Icons/mouse-pointer-ban.svg +1 -0
  339. package/dist/assets/Icons/mouse-pointer-click.svg +1 -0
  340. package/dist/assets/Icons/mouse-pointer.svg +1 -0
  341. package/dist/assets/Icons/mouse-pointer2.svg +1 -0
  342. package/dist/assets/Icons/mouse.svg +1 -0
  343. package/dist/assets/Icons/move-diagonal.svg +1 -0
  344. package/dist/assets/Icons/move-diagonal2.svg +1 -0
  345. package/dist/assets/Icons/move-down-left.svg +1 -0
  346. package/dist/assets/Icons/move-down-right.svg +1 -0
  347. package/dist/assets/Icons/move-down.svg +1 -0
  348. package/dist/assets/Icons/move-horizontal.svg +1 -0
  349. package/dist/assets/Icons/move-left.svg +1 -0
  350. package/dist/assets/Icons/move-right.svg +1 -0
  351. package/dist/assets/Icons/move-up-left.svg +1 -0
  352. package/dist/assets/Icons/move-up-right.svg +1 -0
  353. package/dist/assets/Icons/move-up.svg +1 -0
  354. package/dist/assets/Icons/move-vertical.svg +1 -0
  355. package/dist/assets/Icons/move3-d.svg +1 -0
  356. package/dist/assets/Icons/netcore-neutral.svg +1 -0
  357. package/dist/assets/Icons/netcore.svg +1 -0
  358. package/dist/assets/Icons/notebook-pen.svg +1 -0
  359. package/dist/assets/Icons/notebook-tabs.svg +1 -0
  360. package/dist/assets/Icons/notebook-text.svg +1 -0
  361. package/dist/assets/Icons/notebook.svg +1 -0
  362. package/dist/assets/Icons/notepad-text-dashed.svg +1 -0
  363. package/dist/assets/Icons/notepad-text.svg +1 -0
  364. package/dist/assets/Icons/notion-neutral.svg +1 -0
  365. package/dist/assets/Icons/notion.svg +1 -0
  366. package/dist/assets/Icons/octagon-alert.svg +1 -0
  367. package/dist/assets/Icons/octagon-pause.svg +1 -0
  368. package/dist/assets/Icons/octagon-x.svg +1 -0
  369. package/dist/assets/Icons/octagon.svg +1 -0
  370. package/dist/assets/Icons/ok-neutral.svg +1 -0
  371. package/dist/assets/Icons/ok-only-sign-neutral.svg +1 -0
  372. package/dist/assets/Icons/ok-only-sign.svg +1 -0
  373. package/dist/assets/Icons/ok.svg +1 -0
  374. package/dist/assets/Icons/onlyfans-neutral.svg +1 -0
  375. package/dist/assets/Icons/onlyfans.svg +1 -0
  376. package/dist/assets/Icons/option.svg +1 -0
  377. package/dist/assets/Icons/outlook365-neutral.svg +1 -0
  378. package/dist/assets/Icons/outlook365.svg +1 -0
  379. package/dist/assets/Icons/paintbrush.svg +1 -0
  380. package/dist/assets/Icons/panel-bottom-open.svg +1 -0
  381. package/dist/assets/Icons/panel-bottom.svg +1 -0
  382. package/dist/assets/Icons/panel-left-close.svg +1 -0
  383. package/dist/assets/Icons/panel-left-dashed.svg +1 -0
  384. package/dist/assets/Icons/panel-left-open.svg +1 -0
  385. package/dist/assets/Icons/panel-left.svg +1 -0
  386. package/dist/assets/Icons/panel-right-animated.svg +1 -0
  387. package/dist/assets/Icons/panel-right-close.svg +1 -0
  388. package/dist/assets/Icons/panel-right-dashed.svg +1 -0
  389. package/dist/assets/Icons/panel-right-open.svg +1 -0
  390. package/dist/assets/Icons/panel-right.svg +1 -0
  391. package/dist/assets/Icons/panel-top-bottom-dashed.svg +1 -0
  392. package/dist/assets/Icons/panel-top-close.svg +1 -0
  393. package/dist/assets/Icons/panel-top-open.svg +1 -0
  394. package/dist/assets/Icons/panel-top.svg +1 -0
  395. package/dist/assets/Icons/panels-left-bottom.svg +1 -0
  396. package/dist/assets/Icons/panels-right-bottom.svg +1 -0
  397. package/dist/assets/Icons/panels-top-left.svg +1 -0
  398. package/dist/assets/Icons/paperclip.svg +1 -0
  399. package/dist/assets/Icons/patreon-neutral.svg +1 -0
  400. package/dist/assets/Icons/patreon.svg +1 -0
  401. package/dist/assets/Icons/paypal-neutral.svg +1 -0
  402. package/dist/assets/Icons/paypal.svg +1 -0
  403. package/dist/assets/Icons/pen-line.svg +1 -0
  404. package/dist/assets/Icons/pen-off.svg +1 -0
  405. package/dist/assets/Icons/pen-tool.svg +1 -0
  406. package/dist/assets/Icons/pen.svg +1 -0
  407. package/dist/assets/Icons/pencil-line.svg +1 -0
  408. package/dist/assets/Icons/pencil-off.svg +1 -0
  409. package/dist/assets/Icons/pencil-ruler.svg +1 -0
  410. package/dist/assets/Icons/pencil.svg +1 -0
  411. package/dist/assets/Icons/pentagon.svg +1 -0
  412. package/dist/assets/Icons/percentage.svg +1 -0
  413. package/dist/assets/Icons/phone.svg +1 -0
  414. package/dist/assets/Icons/pin-off.svg +1 -0
  415. package/dist/assets/Icons/pin.svg +1 -0
  416. package/dist/assets/Icons/pinterest-neutral.svg +1 -0
  417. package/dist/assets/Icons/pinterest.svg +1 -0
  418. package/dist/assets/Icons/play.svg +1 -0
  419. package/dist/assets/Icons/plug.svg +1 -0
  420. package/dist/assets/Icons/plunk-neutral.svg +1 -0
  421. package/dist/assets/Icons/plunk.svg +1 -0
  422. package/dist/assets/Icons/plus.svg +1 -0
  423. package/dist/assets/Icons/postmark-neutral.svg +1 -0
  424. package/dist/assets/Icons/postmark.svg +1 -0
  425. package/dist/assets/Icons/product-hunt-neutral.svg +1 -0
  426. package/dist/assets/Icons/product-hunt.svg +1 -0
  427. package/dist/assets/Icons/quora-neutral.svg +1 -0
  428. package/dist/assets/Icons/quora.svg +1 -0
  429. package/dist/assets/Icons/quote.svg +1 -0
  430. package/dist/assets/Icons/radar.svg +1 -0
  431. package/dist/assets/Icons/reddit-neutral.svg +1 -0
  432. package/dist/assets/Icons/reddit.svg +1 -0
  433. package/dist/assets/Icons/redo-dot.svg +1 -0
  434. package/dist/assets/Icons/redo.svg +1 -0
  435. package/dist/assets/Icons/redo2.svg +1 -0
  436. package/dist/assets/Icons/refresh-ccw.svg +1 -0
  437. package/dist/assets/Icons/refresh-cw-off.svg +1 -0
  438. package/dist/assets/Icons/resend-neutral.svg +1 -0
  439. package/dist/assets/Icons/resend.svg +1 -0
  440. package/dist/assets/Icons/rss.svg +1 -0
  441. package/dist/assets/Icons/rules.svg +1 -0
  442. package/dist/assets/Icons/scan-line.svg +1 -0
  443. package/dist/assets/Icons/scroll-text.svg +1 -0
  444. package/dist/assets/Icons/search-hint.svg +1 -0
  445. package/dist/assets/Icons/search-x.svg +1 -0
  446. package/dist/assets/Icons/search.svg +1 -0
  447. package/dist/assets/Icons/sendgrid-neutral.svg +1 -0
  448. package/dist/assets/Icons/sendgrid.svg +1 -0
  449. package/dist/assets/Icons/server.svg +1 -0
  450. package/dist/assets/Icons/ses-neutral.svg +1 -0
  451. package/dist/assets/Icons/ses.svg +1 -0
  452. package/dist/assets/Icons/settings.svg +1 -0
  453. package/dist/assets/Icons/settings2.svg +1 -0
  454. package/dist/assets/Icons/shapes.svg +1 -0
  455. package/dist/assets/Icons/share.svg +1 -0
  456. package/dist/assets/Icons/sheet.svg +1 -0
  457. package/dist/assets/Icons/shield-alert.svg +1 -0
  458. package/dist/assets/Icons/shield-ban.svg +1 -0
  459. package/dist/assets/Icons/shield-check.svg +1 -0
  460. package/dist/assets/Icons/shield-ellipsis.svg +1 -0
  461. package/dist/assets/Icons/shield-half.svg +1 -0
  462. package/dist/assets/Icons/shield-minus.svg +1 -0
  463. package/dist/assets/Icons/shield-off.svg +1 -0
  464. package/dist/assets/Icons/shield-plus.svg +1 -0
  465. package/dist/assets/Icons/shield-question.svg +1 -0
  466. package/dist/assets/Icons/shield-user.svg +1 -0
  467. package/dist/assets/Icons/shield-x.svg +1 -0
  468. package/dist/assets/Icons/shield.svg +1 -0
  469. package/dist/assets/Icons/shift.svg +1 -0
  470. package/dist/assets/Icons/signal-neutral.svg +1 -0
  471. package/dist/assets/Icons/signal.svg +1 -0
  472. package/dist/assets/Icons/signpost.svg +1 -0
  473. package/dist/assets/Icons/sina-weibo-neutral.svg +1 -0
  474. package/dist/assets/Icons/sina-weibo.svg +1 -0
  475. package/dist/assets/Icons/skull.svg +1 -0
  476. package/dist/assets/Icons/skype-neutral.svg +1 -0
  477. package/dist/assets/Icons/skype.svg +1 -0
  478. package/dist/assets/Icons/slack-neutral.svg +1 -0
  479. package/dist/assets/Icons/slack.svg +1 -0
  480. package/dist/assets/Icons/sliders-horizontal.svg +1 -0
  481. package/dist/assets/Icons/sliders-vertical.svg +1 -0
  482. package/dist/assets/Icons/smtp-neutral.svg +1 -0
  483. package/dist/assets/Icons/smtp.svg +1 -0
  484. package/dist/assets/Icons/snapchat-neutral.svg +1 -0
  485. package/dist/assets/Icons/snapchat.svg +1 -0
  486. package/dist/assets/Icons/soundcloud-neutral.svg +1 -0
  487. package/dist/assets/Icons/soundcloud.svg +1 -0
  488. package/dist/assets/Icons/space.svg +1 -0
  489. package/dist/assets/Icons/sparkpost-neutral.svg +1 -0
  490. package/dist/assets/Icons/sparkpost.svg +1 -0
  491. package/dist/assets/Icons/speed.svg +1 -0
  492. package/dist/assets/Icons/split.svg +1 -0
  493. package/dist/assets/Icons/spotify-neutral.svg +1 -0
  494. package/dist/assets/Icons/spotify.svg +1 -0
  495. package/dist/assets/Icons/square-arrow-out-up-right.svg +1 -0
  496. package/dist/assets/Icons/square-minus.svg +1 -0
  497. package/dist/assets/Icons/square-pen.svg +1 -0
  498. package/dist/assets/Icons/square-plus.svg +1 -0
  499. package/dist/assets/Icons/square-user-round.svg +1 -0
  500. package/dist/assets/Icons/stack-overflow-neutral.svg +1 -0
  501. package/dist/assets/Icons/stack-overflow.svg +1 -0
  502. package/dist/assets/Icons/star.svg +1 -0
  503. package/dist/assets/Icons/sun.svg +1 -0
  504. package/dist/assets/Icons/syringe.svg +1 -0
  505. package/dist/assets/Icons/tag.svg +1 -0
  506. package/dist/assets/Icons/target.svg +1 -0
  507. package/dist/assets/Icons/teams-neutral.svg +1 -0
  508. package/dist/assets/Icons/teams.svg +1 -0
  509. package/dist/assets/Icons/telegram-neutral.svg +1 -0
  510. package/dist/assets/Icons/telegram-only-sign-neutral.svg +1 -0
  511. package/dist/assets/Icons/telegram-only-sign.svg +1 -0
  512. package/dist/assets/Icons/telegram.svg +1 -0
  513. package/dist/assets/Icons/terminal.svg +1 -0
  514. package/dist/assets/Icons/test-policies.svg +1 -0
  515. package/dist/assets/Icons/threads-neutral.svg +1 -0
  516. package/dist/assets/Icons/threads.svg +1 -0
  517. package/dist/assets/Icons/thumbs-down.svg +1 -0
  518. package/dist/assets/Icons/thumbs-up.svg +1 -0
  519. package/dist/assets/Icons/tiktok-neutral.svg +1 -0
  520. package/dist/assets/Icons/tiktok.svg +1 -0
  521. package/dist/assets/Icons/tinder-neutral.svg +1 -0
  522. package/dist/assets/Icons/tinder.svg +1 -0
  523. package/dist/assets/Icons/trash.svg +1 -0
  524. package/dist/assets/Icons/trash2.svg +1 -0
  525. package/dist/assets/Icons/triangle-alert.svg +1 -0
  526. package/dist/assets/Icons/tumblr-neutral.svg +1 -0
  527. package/dist/assets/Icons/tumblr.svg +1 -0
  528. package/dist/assets/Icons/twitch-neutral.svg +1 -0
  529. package/dist/assets/Icons/twitch.svg +1 -0
  530. package/dist/assets/Icons/undo-dot.svg +1 -0
  531. package/dist/assets/Icons/undo.svg +1 -0
  532. package/dist/assets/Icons/undo2.svg +1 -0
  533. package/dist/assets/Icons/unplug.svg +1 -0
  534. package/dist/assets/Icons/user-check.svg +1 -0
  535. package/dist/assets/Icons/user-minus.svg +1 -0
  536. package/dist/assets/Icons/user-plus.svg +1 -0
  537. package/dist/assets/Icons/user-round-x.svg +1 -0
  538. package/dist/assets/Icons/user-round.svg +1 -0
  539. package/dist/assets/Icons/user.svg +1 -0
  540. package/dist/assets/Icons/username.svg +1 -0
  541. package/dist/assets/Icons/users-round.svg +1 -0
  542. package/dist/assets/Icons/users.svg +1 -0
  543. package/dist/assets/Icons/viber-neutral.svg +1 -0
  544. package/dist/assets/Icons/viber.svg +1 -0
  545. package/dist/assets/Icons/video.svg +1 -0
  546. package/dist/assets/Icons/vimeo-neutral.svg +1 -0
  547. package/dist/assets/Icons/vimeo.svg +1 -0
  548. package/dist/assets/Icons/vk-music-neutral.svg +1 -0
  549. package/dist/assets/Icons/vk-music.svg +1 -0
  550. package/dist/assets/Icons/vk-neutral.svg +1 -0
  551. package/dist/assets/Icons/vk-only-sign-neutral.svg +1 -0
  552. package/dist/assets/Icons/vk-only-sign.svg +1 -0
  553. package/dist/assets/Icons/vk.svg +1 -0
  554. package/dist/assets/Icons/webhook-neutral.svg +1 -0
  555. package/dist/assets/Icons/webhook.svg +1 -0
  556. package/dist/assets/Icons/wechat-neutral.svg +1 -0
  557. package/dist/assets/Icons/wechat.svg +1 -0
  558. package/dist/assets/Icons/whatsapp-badge-neutral.svg +1 -0
  559. package/dist/assets/Icons/whatsapp-badge.svg +1 -0
  560. package/dist/assets/Icons/whatsapp-neutral.svg +1 -0
  561. package/dist/assets/Icons/whatsapp.svg +1 -0
  562. package/dist/assets/Icons/workflow.svg +1 -0
  563. package/dist/assets/Icons/wrap-text.svg +1 -0
  564. package/dist/assets/Icons/wrench.svg +1 -0
  565. package/dist/assets/Icons/x-ex-twitter-neutral.svg +1 -0
  566. package/dist/assets/Icons/x-ex-twitter.svg +1 -0
  567. package/dist/assets/Icons/x.svg +1 -0
  568. package/dist/assets/Icons/xing-neutral.svg +1 -0
  569. package/dist/assets/Icons/xing.svg +1 -0
  570. package/dist/assets/Icons/yandex-music-neutral.svg +1 -0
  571. package/dist/assets/Icons/yandex-music.svg +1 -0
  572. package/dist/assets/Icons/yelp-neutral.svg +1 -0
  573. package/dist/assets/Icons/yelp.svg +1 -0
  574. package/dist/assets/Icons/youtube-music-neutral.svg +1 -0
  575. package/dist/assets/Icons/youtube-music.svg +1 -0
  576. package/dist/assets/Icons/youtube-neutral.svg +1 -0
  577. package/dist/assets/Icons/youtube-shorts-neutral.svg +1 -0
  578. package/dist/assets/Icons/youtube-shorts.svg +1 -0
  579. package/dist/assets/Icons/youtube.svg +1 -0
  580. package/dist/assets/Icons/zap.svg +1 -0
  581. package/dist/assets/Icons/zoom-in.svg +1 -0
  582. package/dist/assets/Icons/zoom-neutral.svg +1 -0
  583. package/dist/assets/Icons/zoom-out.svg +1 -0
  584. package/dist/assets/Icons/zoom.svg +1 -0
  585. package/dist/assets/Icons/zulip-neutral.svg +1 -0
  586. package/dist/assets/Icons/zulip.svg +1 -0
  587. package/dist/assets/Logos/wallarm.svg +1 -0
  588. package/dist/assets.json +4666 -0
  589. package/dist/project/README.md +265 -0
  590. package/dist/project/components/Accordion/README.md +354 -0
  591. package/dist/project/components/Accordion/preview.html +2 -0
  592. package/dist/project/components/Accordion/stories.js +12 -0
  593. package/dist/project/components/AgentAvatar/README.md +127 -0
  594. package/dist/project/components/AgentAvatar/preview.html +2 -0
  595. package/dist/project/components/AgentAvatar/stories.js +12 -0
  596. package/dist/project/components/AgentAvatar/stories.md +1805 -0
  597. package/dist/project/components/AgentGlyph/README.md +1182 -0
  598. package/dist/project/components/AgentGlyph/preview.html +2 -0
  599. package/dist/project/components/AgentGlyph/stories.js +12 -0
  600. package/dist/project/components/AgentLookBuilder/README.md +269 -0
  601. package/dist/project/components/AgentLookBuilder/preview.html +2 -0
  602. package/dist/project/components/AgentLookBuilder/stories.js +12 -0
  603. package/dist/project/components/Alert/README.md +492 -0
  604. package/dist/project/components/Alert/preview.html +2 -0
  605. package/dist/project/components/Alert/stories.js +12 -0
  606. package/dist/project/components/AnimatedBackground/README.md +124 -0
  607. package/dist/project/components/AnimatedBackground/preview.html +2 -0
  608. package/dist/project/components/AnimatedBackground/stories.js +12 -0
  609. package/dist/project/components/AppShell/README.md +75 -0
  610. package/dist/project/components/AppShell/preview.html +2 -0
  611. package/dist/project/components/AppShell/stories.js +28 -0
  612. package/dist/project/components/AppShell/stories.md +2878 -0
  613. package/dist/project/components/ApprovalCard/README.md +353 -0
  614. package/dist/project/components/ApprovalCard/preview.html +2 -0
  615. package/dist/project/components/ApprovalCard/stories.js +12 -0
  616. package/dist/project/components/AreaChart/README.md +673 -0
  617. package/dist/project/components/AreaChart/preview.html +2 -0
  618. package/dist/project/components/AreaChart/stories.js +12 -0
  619. package/dist/project/components/AssistantMessage/README.md +724 -0
  620. package/dist/project/components/AssistantMessage/preview.html +2 -0
  621. package/dist/project/components/AssistantMessage/stories.js +55 -0
  622. package/dist/project/components/Attribute/README.md +1174 -0
  623. package/dist/project/components/Attribute/preview.html +2 -0
  624. package/dist/project/components/Attribute/stories.js +12 -0
  625. package/dist/project/components/Avatar/README.md +416 -0
  626. package/dist/project/components/Avatar/preview.html +2 -0
  627. package/dist/project/components/Avatar/stories.js +12 -0
  628. package/dist/project/components/Badge/README.md +853 -0
  629. package/dist/project/components/Badge/preview.html +2 -0
  630. package/dist/project/components/Badge/stories.js +12 -0
  631. package/dist/project/components/Banner/README.md +341 -0
  632. package/dist/project/components/Banner/preview.html +2 -0
  633. package/dist/project/components/Banner/stories.js +12 -0
  634. package/dist/project/components/BarList/README.md +757 -0
  635. package/dist/project/components/BarList/preview.html +2 -0
  636. package/dist/project/components/BarList/stories.js +12 -0
  637. package/dist/project/components/Breadcrumbs/README.md +274 -0
  638. package/dist/project/components/Breadcrumbs/preview.html +2 -0
  639. package/dist/project/components/Breadcrumbs/stories.js +12 -0
  640. package/dist/project/components/BulkBar/README.md +19 -0
  641. package/dist/project/components/Button/README.md +467 -0
  642. package/dist/project/components/Button/preview.html +2 -0
  643. package/dist/project/components/Button/stories.js +12 -0
  644. package/dist/project/components/Calendar/README.md +654 -0
  645. package/dist/project/components/Calendar/preview.html +2 -0
  646. package/dist/project/components/Calendar/stories.js +12 -0
  647. package/dist/project/components/Card/README.md +367 -0
  648. package/dist/project/components/Card/preview.html +2 -0
  649. package/dist/project/components/Card/stories.js +12 -0
  650. package/dist/project/components/ChainOfThought/README.md +412 -0
  651. package/dist/project/components/ChainOfThought/preview.html +2 -0
  652. package/dist/project/components/ChainOfThought/stories.js +12 -0
  653. package/dist/project/components/Chart/README.md +224 -0
  654. package/dist/project/components/Chart/preview.html +2 -0
  655. package/dist/project/components/Chart/stories.js +12 -0
  656. package/dist/project/components/Chat/README.md +1244 -0
  657. package/dist/project/components/Chat/preview.html +2 -0
  658. package/dist/project/components/Chat/stories.js +12 -0
  659. package/dist/project/components/ChatInput/README.md +378 -0
  660. package/dist/project/components/ChatInput/preview.html +2 -0
  661. package/dist/project/components/ChatInput/stories.js +12 -0
  662. package/dist/project/components/ChatPermissions/README.md +218 -0
  663. package/dist/project/components/ChatPermissions/preview.html +2 -0
  664. package/dist/project/components/ChatPermissions/stories.js +12 -0
  665. package/dist/project/components/Checkbox/README.md +367 -0
  666. package/dist/project/components/Checkbox/preview.html +2 -0
  667. package/dist/project/components/Checkbox/stories.js +12 -0
  668. package/dist/project/components/Code/README.md +243 -0
  669. package/dist/project/components/Code/preview.html +2 -0
  670. package/dist/project/components/Code/stories.js +12 -0
  671. package/dist/project/components/CodeEditor/README.md +280 -0
  672. package/dist/project/components/CodeEditor/preview.html +2 -0
  673. package/dist/project/components/CodeEditor/stories.js +162 -0
  674. package/dist/project/components/CodeEditor/stories.md +1533 -0
  675. package/dist/project/components/CodeSnippet/README.md +1352 -0
  676. package/dist/project/components/CodeSnippet/preview.html +2 -0
  677. package/dist/project/components/CodeSnippet/stories.js +156 -0
  678. package/dist/project/components/ColumnChart/README.md +521 -0
  679. package/dist/project/components/ColumnChart/preview.html +2 -0
  680. package/dist/project/components/ColumnChart/stories.js +12 -0
  681. package/dist/project/components/Conversation/README.md +314 -0
  682. package/dist/project/components/Conversation/preview.html +2 -0
  683. package/dist/project/components/Conversation/stories.js +12 -0
  684. package/dist/project/components/Copyable/README.md +406 -0
  685. package/dist/project/components/Copyable/preview.html +2 -0
  686. package/dist/project/components/Copyable/stories.js +12 -0
  687. package/dist/project/components/Country/README.md +226 -0
  688. package/dist/project/components/Country/preview.html +2 -0
  689. package/dist/project/components/Country/stories.js +12 -0
  690. package/dist/project/components/Cover/preview.html +43 -0
  691. package/dist/project/components/DateInput/README.md +458 -0
  692. package/dist/project/components/DateInput/preview.html +2 -0
  693. package/dist/project/components/DateInput/stories.js +12 -0
  694. package/dist/project/components/DateRangeInput/README.md +412 -0
  695. package/dist/project/components/DateRangeInput/preview.html +2 -0
  696. package/dist/project/components/DateRangeInput/stories.js +12 -0
  697. package/dist/project/components/Dialog/README.md +1140 -0
  698. package/dist/project/components/Dialog/preview.html +2 -0
  699. package/dist/project/components/Dialog/stories.js +12 -0
  700. package/dist/project/components/Drawer/README.md +1163 -0
  701. package/dist/project/components/Drawer/preview.html +2 -0
  702. package/dist/project/components/Drawer/stories.js +12 -0
  703. package/dist/project/components/DropdownMenu/README.md +678 -0
  704. package/dist/project/components/DropdownMenu/preview.html +2 -0
  705. package/dist/project/components/DropdownMenu/stories.js +12 -0
  706. package/dist/project/components/EmptyState/README.md +276 -0
  707. package/dist/project/components/EmptyState/preview.html +2 -0
  708. package/dist/project/components/EmptyState/stories.js +12 -0
  709. package/dist/project/components/FeedbackPulse/README.md +125 -0
  710. package/dist/project/components/FeedbackPulse/preview.html +2 -0
  711. package/dist/project/components/FeedbackPulse/stories.js +12 -0
  712. package/dist/project/components/Field/README.md +676 -0
  713. package/dist/project/components/Field/preview.html +2 -0
  714. package/dist/project/components/Field/stories.js +12 -0
  715. package/dist/project/components/FileUpload/README.md +544 -0
  716. package/dist/project/components/FileUpload/preview.html +2 -0
  717. package/dist/project/components/FileUpload/stories.js +12 -0
  718. package/dist/project/components/FilterCascade/README.md +348 -0
  719. package/dist/project/components/FilterCascade/preview.html +2 -0
  720. package/dist/project/components/FilterCascade/stories.js +12 -0
  721. package/dist/project/components/FilterDropdown/README.md +542 -0
  722. package/dist/project/components/FilterDropdown/preview.html +2 -0
  723. package/dist/project/components/FilterDropdown/stories.js +12 -0
  724. package/dist/project/components/FilterInput/README.md +416 -0
  725. package/dist/project/components/FilterInput/preview.html +2 -0
  726. package/dist/project/components/FilterInput/stories.js +12 -0
  727. package/dist/project/components/FilterInput/stories.md +6682 -0
  728. package/dist/project/components/Flex/README.md +225 -0
  729. package/dist/project/components/Flex/preview.html +2 -0
  730. package/dist/project/components/Flex/stories.js +12 -0
  731. package/dist/project/components/FormatDateTime/README.md +315 -0
  732. package/dist/project/components/FormatDateTime/preview.html +2 -0
  733. package/dist/project/components/FormatDateTime/stories.js +12 -0
  734. package/dist/project/components/FormatNumber/README.md +317 -0
  735. package/dist/project/components/FormatNumber/preview.html +2 -0
  736. package/dist/project/components/FormatNumber/stories.js +12 -0
  737. package/dist/project/components/Heading/README.md +240 -0
  738. package/dist/project/components/Heading/preview.html +2 -0
  739. package/dist/project/components/Heading/stories.js +12 -0
  740. package/dist/project/components/HorizontalBarStack/README.md +281 -0
  741. package/dist/project/components/HorizontalBarStack/preview.html +2 -0
  742. package/dist/project/components/HorizontalBarStack/stories.js +12 -0
  743. package/dist/project/components/HttpMethod/README.md +137 -0
  744. package/dist/project/components/HttpMethod/preview.html +2 -0
  745. package/dist/project/components/HttpMethod/stories.js +12 -0
  746. package/dist/project/components/ImageCropper/README.md +332 -0
  747. package/dist/project/components/ImageCropper/preview.html +2 -0
  748. package/dist/project/components/ImageCropper/stories.js +33 -0
  749. package/dist/project/components/Indicator/README.md +110 -0
  750. package/dist/project/components/Indicator/preview.html +2 -0
  751. package/dist/project/components/Indicator/stories.js +12 -0
  752. package/dist/project/components/InlineEdit/README.md +977 -0
  753. package/dist/project/components/InlineEdit/preview.html +2 -0
  754. package/dist/project/components/InlineEdit/stories.js +12 -0
  755. package/dist/project/components/Input/README.md +187 -0
  756. package/dist/project/components/Input/preview.html +2 -0
  757. package/dist/project/components/Input/stories.js +12 -0
  758. package/dist/project/components/InputGroup/README.md +423 -0
  759. package/dist/project/components/InputGroup/preview.html +2 -0
  760. package/dist/project/components/InputGroup/stories.js +12 -0
  761. package/dist/project/components/InputOTP/README.md +201 -0
  762. package/dist/project/components/InputOTP/preview.html +2 -0
  763. package/dist/project/components/InputOTP/stories.js +12 -0
  764. package/dist/project/components/Ip/README.md +226 -0
  765. package/dist/project/components/Ip/preview.html +2 -0
  766. package/dist/project/components/Ip/stories.js +12 -0
  767. package/dist/project/components/Kbd/README.md +126 -0
  768. package/dist/project/components/Kbd/preview.html +2 -0
  769. package/dist/project/components/Kbd/stories.js +12 -0
  770. package/dist/project/components/LineChart/README.md +1479 -0
  771. package/dist/project/components/LineChart/preview.html +2 -0
  772. package/dist/project/components/LineChart/stories.js +12 -0
  773. package/dist/project/components/Link/README.md +160 -0
  774. package/dist/project/components/Link/preview.html +2 -0
  775. package/dist/project/components/Link/stories.js +12 -0
  776. package/dist/project/components/List/README.md +181 -0
  777. package/dist/project/components/List/preview.html +2 -0
  778. package/dist/project/components/List/stories.js +12 -0
  779. package/dist/project/components/Loader/README.md +145 -0
  780. package/dist/project/components/Loader/preview.html +2 -0
  781. package/dist/project/components/Loader/stories.js +12 -0
  782. package/dist/project/components/Logo/README.md +183 -0
  783. package/dist/project/components/Logo/preview.html +2 -0
  784. package/dist/project/components/Logo/stories.js +12 -0
  785. package/dist/project/components/MessageActions/README.md +255 -0
  786. package/dist/project/components/MessageActions/preview.html +2 -0
  787. package/dist/project/components/MessageActions/stories.js +12 -0
  788. package/dist/project/components/Metric/README.md +324 -0
  789. package/dist/project/components/Metric/preview.html +2 -0
  790. package/dist/project/components/Metric/stories.js +12 -0
  791. package/dist/project/components/NavRail/README.md +25 -0
  792. package/dist/project/components/NotificationCenter/README.md +91 -0
  793. package/dist/project/components/NotificationCenter/preview.html +2 -0
  794. package/dist/project/components/NotificationCenter/stories.js +12 -0
  795. package/dist/project/components/NotificationCenter/stories.md +2170 -0
  796. package/dist/project/components/NumberInput/README.md +177 -0
  797. package/dist/project/components/NumberInput/preview.html +2 -0
  798. package/dist/project/components/NumberInput/stories.js +12 -0
  799. package/dist/project/components/NumericBadge/README.md +252 -0
  800. package/dist/project/components/NumericBadge/preview.html +2 -0
  801. package/dist/project/components/NumericBadge/stories.js +12 -0
  802. package/dist/project/components/OverflowList/README.md +324 -0
  803. package/dist/project/components/OverflowList/preview.html +2 -0
  804. package/dist/project/components/OverflowList/stories.js +12 -0
  805. package/dist/project/components/OverflowTooltip/README.md +421 -0
  806. package/dist/project/components/OverflowTooltip/preview.html +2 -0
  807. package/dist/project/components/OverflowTooltip/stories.js +12 -0
  808. package/dist/project/components/Overlay/README.md +95 -0
  809. package/dist/project/components/Overlay/preview.html +2 -0
  810. package/dist/project/components/Overlay/stories.js +12 -0
  811. package/dist/project/components/Overview/README.md +445 -0
  812. package/dist/project/components/Overview/preview.html +2 -0
  813. package/dist/project/components/Overview/stories.js +12 -0
  814. package/dist/project/components/Page/README.md +215 -0
  815. package/dist/project/components/Page/preview.html +2 -0
  816. package/dist/project/components/Page/stories.js +12 -0
  817. package/dist/project/components/Pagination/README.md +302 -0
  818. package/dist/project/components/Pagination/preview.html +2 -0
  819. package/dist/project/components/Pagination/stories.js +12 -0
  820. package/dist/project/components/ParameterPath/README.md +284 -0
  821. package/dist/project/components/ParameterPath/preview.html +2 -0
  822. package/dist/project/components/ParameterPath/stories.js +12 -0
  823. package/dist/project/components/PasswordComplexity/README.md +171 -0
  824. package/dist/project/components/PasswordComplexity/preview.html +2 -0
  825. package/dist/project/components/PasswordComplexity/stories.js +12 -0
  826. package/dist/project/components/PasswordInput/README.md +254 -0
  827. package/dist/project/components/PasswordInput/preview.html +2 -0
  828. package/dist/project/components/PasswordInput/stories.js +12 -0
  829. package/dist/project/components/PieChart/README.md +895 -0
  830. package/dist/project/components/PieChart/preview.html +2 -0
  831. package/dist/project/components/PieChart/stories.js +12 -0
  832. package/dist/project/components/Popover/README.md +282 -0
  833. package/dist/project/components/Popover/preview.html +2 -0
  834. package/dist/project/components/Popover/stories.js +12 -0
  835. package/dist/project/components/Progress/README.md +174 -0
  836. package/dist/project/components/Progress/preview.html +2 -0
  837. package/dist/project/components/Progress/stories.js +12 -0
  838. package/dist/project/components/Radio/README.md +264 -0
  839. package/dist/project/components/Radio/preview.html +2 -0
  840. package/dist/project/components/Radio/stories.js +12 -0
  841. package/dist/project/components/RemoteShell/README.md +1758 -0
  842. package/dist/project/components/RemoteShell/preview.html +2 -0
  843. package/dist/project/components/RemoteShell/stories.js +28 -0
  844. package/dist/project/components/Resizable/README.md +290 -0
  845. package/dist/project/components/Resizable/preview.html +2 -0
  846. package/dist/project/components/Resizable/stories.js +12 -0
  847. package/dist/project/components/ResponseCode/README.md +205 -0
  848. package/dist/project/components/ResponseCode/preview.html +2 -0
  849. package/dist/project/components/ResponseCode/stories.js +12 -0
  850. package/dist/project/components/ScrollArea/README.md +234 -0
  851. package/dist/project/components/ScrollArea/preview.html +2 -0
  852. package/dist/project/components/ScrollArea/stories.js +12 -0
  853. package/dist/project/components/SearchInput/README.md +158 -0
  854. package/dist/project/components/SearchInput/preview.html +2 -0
  855. package/dist/project/components/SearchInput/stories.js +12 -0
  856. package/dist/project/components/SearchModal/README.md +836 -0
  857. package/dist/project/components/SearchModal/preview.html +2 -0
  858. package/dist/project/components/SearchModal/stories.js +12 -0
  859. package/dist/project/components/SegmentedControl/README.md +382 -0
  860. package/dist/project/components/SegmentedControl/preview.html +2 -0
  861. package/dist/project/components/SegmentedControl/stories.js +12 -0
  862. package/dist/project/components/SegmentedTabs/README.md +390 -0
  863. package/dist/project/components/SegmentedTabs/preview.html +2 -0
  864. package/dist/project/components/SegmentedTabs/stories.js +12 -0
  865. package/dist/project/components/Select/README.md +1311 -0
  866. package/dist/project/components/Select/preview.html +2 -0
  867. package/dist/project/components/Select/stories.js +12 -0
  868. package/dist/project/components/Selection/README.md +648 -0
  869. package/dist/project/components/Selection/preview.html +2 -0
  870. package/dist/project/components/Selection/stories.js +12 -0
  871. package/dist/project/components/Separator/README.md +77 -0
  872. package/dist/project/components/Separator/preview.html +2 -0
  873. package/dist/project/components/Separator/stories.js +12 -0
  874. package/dist/project/components/Skeleton/README.md +358 -0
  875. package/dist/project/components/Skeleton/preview.html +2 -0
  876. package/dist/project/components/Skeleton/stories.js +12 -0
  877. package/dist/project/components/Slider/README.md +433 -0
  878. package/dist/project/components/Slider/preview.html +2 -0
  879. package/dist/project/components/Slider/stories.js +12 -0
  880. package/dist/project/components/SplashScreen/README.md +192 -0
  881. package/dist/project/components/SplashScreen/preview.html +2 -0
  882. package/dist/project/components/SplashScreen/stories.js +12 -0
  883. package/dist/project/components/SplitButton/README.md +402 -0
  884. package/dist/project/components/SplitButton/preview.html +2 -0
  885. package/dist/project/components/SplitButton/stories.js +12 -0
  886. package/dist/project/components/Stack/README.md +269 -0
  887. package/dist/project/components/Stack/preview.html +2 -0
  888. package/dist/project/components/Stack/stories.js +12 -0
  889. package/dist/project/components/Stepper/README.md +682 -0
  890. package/dist/project/components/Stepper/preview.html +2 -0
  891. package/dist/project/components/Stepper/stories.js +12 -0
  892. package/dist/project/components/Switch/README.md +236 -0
  893. package/dist/project/components/Switch/preview.html +2 -0
  894. package/dist/project/components/Switch/stories.js +12 -0
  895. package/dist/project/components/Table/README.md +471 -0
  896. package/dist/project/components/Table/preview.html +2 -0
  897. package/dist/project/components/Table/stories.js +12 -0
  898. package/dist/project/components/Table/stories.md +3697 -0
  899. package/dist/project/components/Tabs/README.md +443 -0
  900. package/dist/project/components/Tabs/preview.html +2 -0
  901. package/dist/project/components/Tabs/stories.js +12 -0
  902. package/dist/project/components/Tag/README.md +324 -0
  903. package/dist/project/components/Tag/preview.html +2 -0
  904. package/dist/project/components/Tag/stories.js +12 -0
  905. package/dist/project/components/Text/README.md +200 -0
  906. package/dist/project/components/Text/preview.html +2 -0
  907. package/dist/project/components/Text/stories.js +12 -0
  908. package/dist/project/components/Textarea/README.md +273 -0
  909. package/dist/project/components/Textarea/preview.html +2 -0
  910. package/dist/project/components/Textarea/stories.js +12 -0
  911. package/dist/project/components/TimeInput/README.md +359 -0
  912. package/dist/project/components/TimeInput/preview.html +2 -0
  913. package/dist/project/components/TimeInput/stories.js +12 -0
  914. package/dist/project/components/Timeline/README.md +204 -0
  915. package/dist/project/components/Timeline/preview.html +2 -0
  916. package/dist/project/components/Timeline/stories.js +12 -0
  917. package/dist/project/components/Toast/README.md +718 -0
  918. package/dist/project/components/Toast/preview.html +2 -0
  919. package/dist/project/components/Toast/stories.js +12 -0
  920. package/dist/project/components/ToggleButton/README.md +292 -0
  921. package/dist/project/components/ToggleButton/preview.html +2 -0
  922. package/dist/project/components/ToggleButton/stories.js +12 -0
  923. package/dist/project/components/Tooltip/README.md +199 -0
  924. package/dist/project/components/Tooltip/preview.html +2 -0
  925. package/dist/project/components/Tooltip/stories.js +12 -0
  926. package/dist/project/components/TopHeader/README.md +21 -0
  927. package/dist/project/components/Tour/README.md +532 -0
  928. package/dist/project/components/Tour/preview.html +2 -0
  929. package/dist/project/components/Tour/stories.js +12 -0
  930. package/dist/project/components/Tree/README.md +450 -0
  931. package/dist/project/components/Tree/preview.html +2 -0
  932. package/dist/project/components/Tree/stories.js +12 -0
  933. package/dist/project/components/TreeView/README.md +531 -0
  934. package/dist/project/components/TreeView/preview.html +2 -0
  935. package/dist/project/components/TreeView/stories.js +12 -0
  936. package/dist/project/components/UTILITIES.md +647 -0
  937. package/dist/project/components/UserMessage/README.md +166 -0
  938. package/dist/project/components/UserMessage/preview.html +2 -0
  939. package/dist/project/components/UserMessage/stories.js +12 -0
  940. package/dist/project/components/UtilityPage/README.md +147 -0
  941. package/dist/project/components/UtilityPage/preview.html +2 -0
  942. package/dist/project/components/UtilityPage/stories.js +12 -0
  943. package/dist/project/components/WallyAssistant/README.md +1380 -0
  944. package/dist/project/components/WallyAssistant/preview.html +2 -0
  945. package/dist/project/components/WallyAssistant/stories.js +12 -0
  946. package/dist/project/components/WallyIcon/README.md +130 -0
  947. package/dist/project/components/WallyIcon/preview.html +2 -0
  948. package/dist/project/components/WallyIcon/stories.js +12 -0
  949. package/dist/project/components/WallySkeleton/README.md +96 -0
  950. package/dist/project/components/WallySkeleton/preview.html +2 -0
  951. package/dist/project/components/WallySkeleton/stories.js +12 -0
  952. package/dist/project/components/bundle.css +18734 -0
  953. package/dist/project/components/bundle.js +167 -0
  954. package/dist/project/components/index.d.ts +27226 -0
  955. package/dist/project/design-system.json +35 -0
  956. package/dist/project/fonts/Geist-Variable.woff2 +0 -0
  957. package/dist/project/fonts/GeistMono-Variable.woff2 +0 -0
  958. package/dist/project/fonts/GeistPixel-Square.woff2 +0 -0
  959. package/dist/project/sampler.dc.html +238 -0
  960. package/dist/project/tokens.css +1395 -0
  961. package/dist/project/tokens.json +5235 -0
  962. package/dist/wallarm-sampler.dc.html +239 -0
  963. package/package.json +67 -3
@@ -0,0 +1,1380 @@
1
+ The closed Wally panel: pass a `ChatSnapshot` (messages, status, suggestions, permissions, context) and callbacks, and it renders the whole Figma chat — header, stick-to-bottom log, greeting, chain of thought, docked approval and input. It holds no agent state: the app folds its stream into a new snapshot on every delta. Send is blocked while the last message has a pending approval (typing still works), and a resolved card stays docked until the next message. Every rule lives in exported pieces — `ChatViewProvider`, `deriveChatState`, `ChatMessageView`, `ChatPartView` — so a custom layout reuses them (Custom Layout). Seams like `renderPart`, `renderStepContent` and `renderMessageActions` hand back the default rendering. Stories are driven by `useMockWally()` from `WallyAssistant/mocks` and keep feedback and permissions in app state. Messages render with the default `AssistantMessageMarkdown`, and fenced code highlights with the lazy Prism adapter WallyAssistant mounts unless the app provides a `CodeSnippetAdapterProvider`.
2
+
3
+ ## Props as canvas attributes
4
+
5
+ Use kebab-case attribute names. Pass non-string values through renderVals holes, including booleans. The full emitted API is in `components/index.d.ts`.
6
+
7
+ | Attribute | Type | Required | Default | Description |
8
+ | --- | --- | --- | --- | --- |
9
+ | `default-checked` | `boolean \| undefined` | no | — | |
10
+ | `auto-focus` | `boolean \| undefined` | no | — | |
11
+ | `id` | `string \| undefined` | no | — | |
12
+ | `tab-index` | `number \| undefined` | no | — | |
13
+ | `rel` | `string \| undefined` | no | — | |
14
+ | `on-change` | `ChangeEventHandler<T, Element> \| undefined` | no | — | |
15
+ | `data-testid` | `string \| undefined` | no | — | |
16
+ | `background` | `"strong" \| "none" \| "subtle"` | no | — | Decorative mesh behind the panel (Figma chat-bg 67:1254). `'strong'` is Main (12%, for an empty chat), `'subtle'` is Chat (5%, for a running conversation), `'none'` renders nothing. |
17
+ | `messages` | `complex` | yes | — | |
18
+ | `status` | `"error" \| "ready" \| "submitted" \| "streaming"` | no | `ready` | Default 'ready'. |
19
+ | `suggestions` | `complex` | no | — | |
20
+ | `permissions` | `complex` | no | — | |
21
+ | `context` | `complex` | no | — | |
22
+ | `value` | `string \| undefined` | no | — | |
23
+ | `on-value-change` | `((value: string) => void) \| undefined` | no | — | |
24
+ | `disabled` | `boolean \| undefined` | no | — | Disables the textarea, Send and the suggestion pills (e.g. `!agent.isReady`). |
25
+ | `textarea-ref` | `Ref<HTMLTextAreaElement> \| undefined` | no | — | Focus management, e.g. focus the input after a thread switch. |
26
+ | `on-submit` | `(text: string) => void` | yes | — | Trimmed text. Not called when empty, while running, while an approval is pending, or when disabled. |
27
+ | `on-stop` | `(() => void) \| undefined` | no | — | Stop button, and Esc in the textarea while running (also inside a Drawer/Dialog: ChatInput claims that Esc from the layer, so no host recipe is needed). |
28
+ | `on-suggestion-select` | `complex` | no | — | Default: onSubmit(suggestion.prompt ?? suggestion.label). |
29
+ | `on-permission-change` | `complex` | no | — | Absent, or no permissions → no permissions button in the input. |
30
+ | `on-context-remove` | `complex` | no | — | Absent → context chips aren't removable. |
31
+ | `header` | `ReactNode` | no | — | |
32
+ | `header-title` | `ReactNode` | no | — | ChatHeaderTitle children, e.g. a thread Select (Figma Variant2). |
33
+ | `header-actions` | `ReactNode` | no | — | New chat, history, close: your Buttons, so analytics land on them. |
34
+ | `empty-state` | `ReactNode` | no | — | Replaces the greeting and its pills (e.g. a history-loading state). |
35
+ | `footer-start` | `ReactNode` | no | — | Top of ChatFooter, above the suggestions (banners). |
36
+ | `input-tools` | `ReactNode` | no | — | Extra toolbar items in the input, between permissions and Send. |
37
+ | `input` | `ReactNode` | no | — | Replaces the whole ChatInput composition (you wire value and submit yourself). |
38
+ | `on-copy` | `complex` | no | — | Fired after Copy copied; Copy itself shows whenever the message has text. |
39
+ | `labels` | `complex` | no | — | |
40
+ | `icons` | `Readonly<Record<string, ReactNode>> \| undefined` | no | — | Icon key → element from src/icons; wins over the built-in Figma 483:1899 table. |
41
+ | `markdown` | `complex` | no | — | The markdown renderer, at module scope (never inline). Default: the DS `AssistantMessageMarkdown` (GFM, streaming-safe, no raw HTML, http/https/mailto/relative links only, no images). A replacement owns its own sanitizing. To change one element, wrap it at module scope: `const WallyMarkdown: FC<ChatMarkdownProps> = p => <AssistantMessageMarkdown {...p} components={MAP} />`. |
42
+ | `format-timestamp` | `((createdAt: string) => string) \| undefined` | no | — | ISO → label. Default: DS formatRelativeTime (honours DateFormatProvider). Give it a new identity on your clock tick to refresh "2 min ago" — the DS runs no clock. |
43
+ | `approval-placement` | `"inline" \| "docked" \| undefined` | no | `docked` | 'docked': the last message's approvals render in the footer. Default 'inline' here; WallyAssistant defaults to 'docked' (Figma). A custom layout passes the same value to `deriveChatState`, or the approval renders twice (provider 'inline') or never (provider 'docked'). |
44
+ | `render-part` | `complex` | no | — | Called for every part, custom and unknown types included. undefined = the default; null = nothing. A render function: no hooks inside — put them in the components you return. While it is set, every part counts as rendered for the loader and the actions row (its output is unknown up front), so a running reply made only of parts it maps to nothing shows no loader. |
45
+ | `render-step-content` | `complex` | no | — | A step's collapsible body: rich tool output, or a card for one tool via `step.name`. undefined = the default; null = no body (the row isn't collapsible). No hooks inside. |
46
+ | `render-message-actions` | `complex` | no | — | The actions row's children (Figma Wally order: Copy, ThumbsUp, ThumbsDown, Ellipsis, Timestamp). undefined = the default order; null = no row. No hooks inside. |
47
+ | `on-approval-respond` | `complex` | no | — | Absent → approval buttons are disabled. |
48
+ | `on-feedback` | `complex` | no | — | Absent → no thumbs. Clicking the pressed thumb sends null. |
49
+ | `on-retry` | `complex` | no | — | Absent → no Retry on error parts. |
50
+
51
+ ## Sub-components
52
+
53
+ - `WallarmDS.ApprovalCardView` — `ApprovalCardViewProps` in the declaration bundle.
54
+ - `WallarmDS.ChainOfThoughtView` — `ChainOfThoughtViewProps` in the declaration bundle.
55
+ - `WallarmDS.ChatMessageView` — `ChatMessageViewProps` in the declaration bundle.
56
+ - `WallarmDS.ChatPartView` — `ChatPartViewProps` in the declaration bundle.
57
+ - `WallarmDS.ChatViewProvider` — `ChatViewProviderProps` in the declaration bundle.
58
+
59
+ ## Stories
60
+
61
+ ### Empty
62
+
63
+ No messages: the greeting with Large suggestion pills on the strong background. A pill starts a run.
64
+
65
+ Source: WallyAssistant/WallyAssistant.stories.tsx#Empty
66
+
67
+ ### Conversation
68
+
69
+ Two finished turns: folded chain of thought, answers with Copy, thumbs and timestamps, follow-up pills and the permissions button.
70
+
71
+ Source: WallyAssistant/WallyAssistant.stories.tsx#Conversation
72
+
73
+ ### StreamingSimulation
74
+
75
+ The whole Wally script from `useMockWally()`: send a message or click a pill to watch the loader,
76
+ a live reasoning step, three tool steps with the Working row between them, the streamed answer,
77
+ then the fold under "Worked for 12s". Stop cancels the run. `speed` scales every delay (0 = instant).
78
+
79
+ Source: WallyAssistant/WallyAssistant.stories.tsx#StreamingSimulation
80
+
81
+ ### Approval
82
+
83
+ A pending approval docked above the input: Send is blocked (typing still works). Allow or Reject goes through `onApprovalRespond`, and the next snapshot resolves the card in place.
84
+
85
+ Source: WallyAssistant/WallyAssistant.stories.tsx#Approval
86
+
87
+ ### ErrorState
88
+
89
+ A run that failed before any output: `AssistantMessageError` with Retry, wired to `onRetry`.
90
+
91
+ Source: WallyAssistant/WallyAssistant.stories.tsx#ErrorState
92
+
93
+ ### Overrides
94
+
95
+ Every seam at once: `renderPart` draws a `custom` attack summary, `renderStepContent` puts a table
96
+ under `search_attacks`, `renderMessageActions` adds a tagged Copy and an Ellipsis menu, a thread
97
+ `Select` in `headerTitle`, an attach button in `inputTools`, and German `labels`.
98
+
99
+ Source: WallyAssistant/WallyAssistant.stories.tsx#Overrides
100
+
101
+ ### CustomLayout
102
+
103
+ A full-page chat with a centered 768px column, built from `ChatViewProvider`, `deriveChatState`, `Chat`, `Conversation*`, `ChatMessageView`, docked `ChatPartView` and its own `ChatInput` — proof the open parts reuse every rule.
104
+
105
+ Source: WallyAssistant/WallyAssistant.stories.tsx#CustomLayout
106
+
107
+ ### InDrawer
108
+
109
+ Hosted in a 480px Drawer opened by `ChatTrigger`. Esc in the textarea while a reply streams stops it without closing the drawer — ChatInput handles that itself, no host recipe needed.
110
+
111
+ Source: WallyAssistant/WallyAssistant.stories.tsx#InDrawer
112
+
113
+ ### Analytics
114
+
115
+ Analytics attributes land on real nodes: the root `<section>`, header-action Buttons and a tagged Copy from `renderMessageActions` (like, dislike and timestamp stay default). Closed targets (pills, Send, approval options) report through their callbacks — see ANALYTICS_GAPS.md.
116
+
117
+ Source: WallyAssistant/WallyAssistant.stories.tsx#Analytics
118
+
119
+ ## Story sources
120
+
121
+ ### WallyAssistant/WallyAssistant.stories.tsx
122
+
123
+ ```tsx
124
+ import { useState } from 'react';
125
+ import type { Meta, StoryFn } from 'storybook-react-rsbuild';
126
+ import { Ellipsis, History, Paperclip, Plus, X } from '../../icons';
127
+ import { Button } from '../Button';
128
+ import { Chat, ChatFooter, ChatHeader, ChatHeaderTitle, ChatTrigger } from '../Chat';
129
+ import { ChatInput, ChatInputSubmit, ChatInputTextarea } from '../ChatInput';
130
+ import {
131
+ ConversationContent,
132
+ Conversation as ConversationRoot,
133
+ ConversationScrollButton,
134
+ } from '../Conversation';
135
+ import { Copyable, CopyableIcon } from '../Copyable';
136
+ import { Drawer, DrawerClose, DrawerContent, DrawerTitle, DrawerTrigger } from '../Drawer';
137
+ import {
138
+ DropdownMenu,
139
+ DropdownMenuContent,
140
+ DropdownMenuItem,
141
+ DropdownMenuTrigger,
142
+ } from '../DropdownMenu';
143
+ import { MessageAction } from '../MessageActions';
144
+ import {
145
+ createListCollection,
146
+ Select,
147
+ SelectButton,
148
+ SelectContent,
149
+ SelectOption,
150
+ SelectOptionIndicator,
151
+ SelectOptionText,
152
+ SelectPositioner,
153
+ } from '../Select';
154
+ import { Separator } from '../Separator';
155
+ import {
156
+ type ChatAssistantMessage,
157
+ type ChatFeedback,
158
+ ChatMessageView,
159
+ ChatPartView,
160
+ type ChatSnapshot,
161
+ type ChatToolPermission,
162
+ type ChatViewConfig,
163
+ ChatViewProvider,
164
+ copyTextOf,
165
+ deriveChatState,
166
+ WallyAssistant,
167
+ } from '.';
168
+ import { APPROVAL_PENDING, CONVERSATION, EMPTY, ERROR_TURN, useMockWally } from './mocks';
169
+
170
+ const DESCRIPTION = [
171
+ 'The closed Wally panel: pass a `ChatSnapshot` (messages, status, suggestions, permissions, context) and callbacks, and it renders the whole Figma chat — header, stick-to-bottom log, greeting, chain of thought, docked approval and input.',
172
+ 'It holds no agent state: the app folds its stream into a new snapshot on every delta. Send is blocked while the last message has a pending approval (typing still works), and a resolved card stays docked until the next message.',
173
+ 'Every rule lives in exported pieces — `ChatViewProvider`, `deriveChatState`, `ChatMessageView`, `ChatPartView` — so a custom layout reuses them (Custom Layout). Seams like `renderPart`, `renderStepContent` and `renderMessageActions` hand back the default rendering.',
174
+ 'Stories are driven by `useMockWally()` from `WallyAssistant/mocks` and keep feedback and permissions in app state. Messages render with the default `AssistantMessageMarkdown`, and fenced code highlights with the lazy Prism adapter WallyAssistant mounts unless the app provides a `CodeSnippetAdapterProvider`.',
175
+ ].join(' ');
176
+
177
+ const meta = {
178
+ title: 'AI/WallyAssistant',
179
+ component: WallyAssistant,
180
+ parameters: {
181
+ layout: 'centered',
182
+ docs: { description: { component: DESCRIPTION } },
183
+ },
184
+ } satisfies Meta<typeof WallyAssistant>;
185
+
186
+ export default meta;
187
+
188
+ // --- story data and config -----------------------------------------------------------------------------
189
+
190
+ /** Fixed (UTC) clock text, so screenshots don't drift. */
191
+ const fixedTime = (iso: string) => new Date(iso).toISOString().slice(11, 16);
192
+
193
+ /**
194
+ * Merges app-held feedback into an assistant message, caching one object per (message, feedback)
195
+ * pair so a streaming delta doesn't hand untouched rows a new identity (§7.2).
196
+ */
197
+ const merged = new WeakMap<
198
+ ChatAssistantMessage,
199
+ { feedback: ChatFeedback | null; message: ChatAssistantMessage }
200
+ >();
201
+ const withFeedback = (
202
+ m: ChatAssistantMessage,
203
+ feedback: ChatFeedback | null,
204
+ ): ChatAssistantMessage => {
205
+ const hit = merged.get(m);
206
+ if (hit && hit.feedback === feedback) return hit.message;
207
+ const message = { ...m, feedback };
208
+ merged.set(m, { feedback, message });
209
+ return message;
210
+ };
211
+
212
+ interface AttackSummary {
213
+ total: number;
214
+ blocked: number;
215
+ }
216
+
217
+ const OVERRIDES: ChatSnapshot = {
218
+ ...CONVERSATION,
219
+ messages: [
220
+ ...CONVERSATION.messages,
221
+ {
222
+ role: 'user',
223
+ id: 'u3',
224
+ createdAt: '2026-10-21T09:02:00.000Z',
225
+ parts: [{ type: 'text', id: 'u3:text', text: 'Summarize the attacks' }],
226
+ },
227
+ {
228
+ role: 'assistant',
229
+ id: 'u3:reply',
230
+ createdAt: '2026-10-21T09:02:04.000Z',
231
+ parts: [
232
+ {
233
+ type: 'chain-of-thought',
234
+ id: 'u3:reply:cot:0',
235
+ steps: [
236
+ {
237
+ id: 'search',
238
+ kind: 'tool',
239
+ name: 'search_attacks',
240
+ icon: 'query',
241
+ status: 'done',
242
+ label: 'Searched attacks',
243
+ metric: '3 sources',
244
+ },
245
+ ],
246
+ },
247
+ {
248
+ type: 'custom',
249
+ id: 'u3:reply:summary',
250
+ kind: 'wallarm.attack-summary',
251
+ data: { total: 1204, blocked: 1019 } satisfies AttackSummary,
252
+ },
253
+ {
254
+ type: 'text',
255
+ id: 'u3:reply:text',
256
+ text: 'Most of it is SQL injection from `203.0.113.7`.',
257
+ },
258
+ ],
259
+ },
260
+ ],
261
+ };
262
+
263
+ const SOURCES = [
264
+ { ip: '203.0.113.7', attacks: 185 },
265
+ { ip: '198.51.100.4', attacks: 610 },
266
+ { ip: '192.0.2.10', attacks: 409 },
267
+ ];
268
+
269
+ const threads = createListCollection({
270
+ items: [
271
+ { value: 'attacks', label: 'Top attacks today' },
272
+ { value: 'rules', label: 'Rule tuning for API v2' },
273
+ ],
274
+ });
275
+
276
+ // Render seams stay at module scope so their identity is stable (ChatViewProvider memoizes on it).
277
+ const renderPart: ChatViewConfig['renderPart'] = part => {
278
+ if (part.type !== 'custom' || part.kind !== 'wallarm.attack-summary') return undefined;
279
+ const { total, blocked } = part.data as AttackSummary;
280
+ return (
281
+ <div
282
+ data-testid='attack-summary'
283
+ className='rounded-8 border border-border-primary-light p-12 text-sm text-text-primary'
284
+ >
285
+ {blocked} of {total} attacks blocked
286
+ </div>
287
+ );
288
+ };
289
+
290
+ const renderStepContent: ChatViewConfig['renderStepContent'] = step =>
291
+ step.name === 'search_attacks' ? (
292
+ <table data-testid='search-table' className='text-xs text-text-secondary'>
293
+ <tbody>
294
+ {SOURCES.map(s => (
295
+ <tr key={s.ip}>
296
+ <td className='pr-12 font-mono'>{s.ip}</td>
297
+ <td>{s.attacks}</td>
298
+ </tr>
299
+ ))}
300
+ </tbody>
301
+ </table>
302
+ ) : undefined;
303
+
304
+ // Overrides: tagged Copy with a German label, an Ellipsis menu, then the default like/dislike/timestamp.
305
+ const renderMessageActions: ChatViewConfig['renderMessageActions'] = (m, a) =>
306
+ m.role === 'user' ? undefined : (
307
+ <>
308
+ <Copyable text={copyTextOf(m)}>
309
+ <MessageAction aria-label='Kopieren' data-analytics-id='wally.copy'>
310
+ <CopyableIcon />
311
+ </MessageAction>
312
+ </Copyable>
313
+ {a.like}
314
+ {a.dislike}
315
+ <DropdownMenu positioning={{ placement: 'bottom-end' }}>
316
+ <DropdownMenuTrigger asChild>
317
+ <MessageAction aria-label='More actions' data-analytics-id='wally.more'>
318
+ <Ellipsis />
319
+ </MessageAction>
320
+ </DropdownMenuTrigger>
321
+ <DropdownMenuContent>
322
+ <DropdownMenuItem data-analytics-id='wally.report'>Report {m.id}</DropdownMenuItem>
323
+ <DropdownMenuItem>Share</DropdownMenuItem>
324
+ </DropdownMenuContent>
325
+ </DropdownMenu>
326
+ {a.timestamp}
327
+ </>
328
+ );
329
+
330
+ // Analytics: the default actions, with Copy swapped for one carrying `data-analytics-id`.
331
+ const renderAnalyticsActions: ChatViewConfig['renderMessageActions'] = (m, a) =>
332
+ m.role === 'user' ? undefined : (
333
+ <>
334
+ <Copyable text={copyTextOf(m)}>
335
+ <MessageAction aria-label='Copy' data-analytics-id='wally.copy'>
336
+ <CopyableIcon />
337
+ </MessageAction>
338
+ </Copyable>
339
+ {a.like}
340
+ {a.dislike}
341
+ {a.timestamp}
342
+ </>
343
+ );
344
+
345
+ // --- stories ------------------------------------------------------------------------------------
346
+
347
+ /** No messages: the greeting with Large suggestion pills on the strong background. A pill starts a run. */
348
+ export const Empty: StoryFn<typeof meta> = () => {
349
+ const wally = useMockWally({ initial: EMPTY });
350
+ const [feedback, setFeedback] = useState<Record<string, ChatFeedback | null>>({});
351
+ const [permissions, setPermissions] = useState(EMPTY.permissions);
352
+ const messages = wally.snapshot.messages.map(m =>
353
+ m.role === 'assistant' && m.id in feedback ? withFeedback(m, feedback[m.id] ?? null) : m,
354
+ );
355
+
356
+ const handleFeedback = (message: ChatAssistantMessage, value: ChatFeedback | null) => {
357
+ setFeedback(f => ({ ...f, [message.id]: value }));
358
+ };
359
+
360
+ const handlePermissionChange = (permission: ChatToolPermission, allowed: boolean) => {
361
+ setPermissions(list => list?.map(x => (x.id === permission.id ? { ...x, allowed } : x)));
362
+ };
363
+
364
+ return (
365
+ <div className='h-640 w-480'>
366
+ <WallyAssistant
367
+ data-testid='wally'
368
+ {...wally.snapshot}
369
+ messages={messages}
370
+ permissions={permissions}
371
+ formatTimestamp={fixedTime}
372
+ onSubmit={wally.send}
373
+ onStop={wally.stop}
374
+ onApprovalRespond={wally.respond}
375
+ onFeedback={handleFeedback}
376
+ onPermissionChange={handlePermissionChange}
377
+ />
378
+ </div>
379
+ );
380
+ };
381
+
382
+ /** Two finished turns: folded chain of thought, answers with Copy, thumbs and timestamps, follow-up pills and the permissions button. */
383
+ export const Conversation: StoryFn<typeof meta> = () => {
384
+ const wally = useMockWally({ initial: CONVERSATION });
385
+ const [feedback, setFeedback] = useState<Record<string, ChatFeedback | null>>({});
386
+ const [permissions, setPermissions] = useState(CONVERSATION.permissions);
387
+ const messages = wally.snapshot.messages.map(m =>
388
+ m.role === 'assistant' && m.id in feedback ? withFeedback(m, feedback[m.id] ?? null) : m,
389
+ );
390
+
391
+ const handleFeedback = (message: ChatAssistantMessage, value: ChatFeedback | null) => {
392
+ setFeedback(f => ({ ...f, [message.id]: value }));
393
+ };
394
+
395
+ const handlePermissionChange = (permission: ChatToolPermission, allowed: boolean) => {
396
+ setPermissions(list => list?.map(x => (x.id === permission.id ? { ...x, allowed } : x)));
397
+ };
398
+
399
+ return (
400
+ <div className='h-640 w-480'>
401
+ <WallyAssistant
402
+ data-testid='wally'
403
+ {...wally.snapshot}
404
+ messages={messages}
405
+ permissions={permissions}
406
+ formatTimestamp={fixedTime}
407
+ onSubmit={wally.send}
408
+ onStop={wally.stop}
409
+ onApprovalRespond={wally.respond}
410
+ onFeedback={handleFeedback}
411
+ onPermissionChange={handlePermissionChange}
412
+ />
413
+ </div>
414
+ );
415
+ };
416
+
417
+ /**
418
+ * The whole Wally script from `useMockWally()`: send a message or click a pill to watch the loader,
419
+ * a live reasoning step, three tool steps with the Working row between them, the streamed answer,
420
+ * then the fold under "Worked for 12s". Stop cancels the run. `speed` scales every delay (0 = instant).
421
+ */
422
+ export const StreamingSimulation: StoryFn<{ speed: number }> = ({ speed }) => {
423
+ const wally = useMockWally({ initial: CONVERSATION, speed });
424
+ const [feedback, setFeedback] = useState<Record<string, ChatFeedback | null>>({});
425
+ const [permissions, setPermissions] = useState(CONVERSATION.permissions);
426
+ const messages = wally.snapshot.messages.map(m =>
427
+ m.role === 'assistant' && m.id in feedback ? withFeedback(m, feedback[m.id] ?? null) : m,
428
+ );
429
+
430
+ const handleFeedback = (message: ChatAssistantMessage, value: ChatFeedback | null) => {
431
+ setFeedback(f => ({ ...f, [message.id]: value }));
432
+ };
433
+
434
+ const handlePermissionChange = (permission: ChatToolPermission, allowed: boolean) => {
435
+ setPermissions(list => list?.map(x => (x.id === permission.id ? { ...x, allowed } : x)));
436
+ };
437
+
438
+ return (
439
+ <div className='h-640 w-480'>
440
+ <WallyAssistant
441
+ data-testid='wally'
442
+ {...wally.snapshot}
443
+ messages={messages}
444
+ permissions={permissions}
445
+ formatTimestamp={fixedTime}
446
+ onSubmit={wally.send}
447
+ onStop={wally.stop}
448
+ onApprovalRespond={wally.respond}
449
+ onFeedback={handleFeedback}
450
+ onPermissionChange={handlePermissionChange}
451
+ />
452
+ </div>
453
+ );
454
+ };
455
+ StreamingSimulation.args = { speed: 1 };
456
+ StreamingSimulation.argTypes = {
457
+ speed: { control: { type: 'range', min: 0, max: 3, step: 0.25 } },
458
+ };
459
+
460
+ /** A pending approval docked above the input: Send is blocked (typing still works). Allow or Reject goes through `onApprovalRespond`, and the next snapshot resolves the card in place. */
461
+ export const Approval: StoryFn<typeof meta> = () => {
462
+ const wally = useMockWally({ initial: APPROVAL_PENDING });
463
+ const [feedback, setFeedback] = useState<Record<string, ChatFeedback | null>>({});
464
+ const [permissions, setPermissions] = useState(APPROVAL_PENDING.permissions);
465
+ const messages = wally.snapshot.messages.map(m =>
466
+ m.role === 'assistant' && m.id in feedback ? withFeedback(m, feedback[m.id] ?? null) : m,
467
+ );
468
+
469
+ const handleFeedback = (message: ChatAssistantMessage, value: ChatFeedback | null) => {
470
+ setFeedback(f => ({ ...f, [message.id]: value }));
471
+ };
472
+
473
+ const handlePermissionChange = (permission: ChatToolPermission, allowed: boolean) => {
474
+ setPermissions(list => list?.map(x => (x.id === permission.id ? { ...x, allowed } : x)));
475
+ };
476
+
477
+ return (
478
+ <div className='h-640 w-480'>
479
+ <WallyAssistant
480
+ data-testid='wally'
481
+ {...wally.snapshot}
482
+ messages={messages}
483
+ permissions={permissions}
484
+ formatTimestamp={fixedTime}
485
+ onSubmit={wally.send}
486
+ onStop={wally.stop}
487
+ onApprovalRespond={wally.respond}
488
+ onFeedback={handleFeedback}
489
+ onPermissionChange={handlePermissionChange}
490
+ />
491
+ </div>
492
+ );
493
+ };
494
+
495
+ /** A run that failed before any output: `AssistantMessageError` with Retry, wired to `onRetry`. */
496
+ export const ErrorState: StoryFn<typeof meta> = () => {
497
+ const wally = useMockWally({ initial: ERROR_TURN });
498
+ const [feedback, setFeedback] = useState<Record<string, ChatFeedback | null>>({});
499
+ const [permissions, setPermissions] = useState(ERROR_TURN.permissions);
500
+ const [retries, setRetries] = useState(0);
501
+ const messages = wally.snapshot.messages.map(m =>
502
+ m.role === 'assistant' && m.id in feedback ? withFeedback(m, feedback[m.id] ?? null) : m,
503
+ );
504
+
505
+ const handleFeedback = (message: ChatAssistantMessage, value: ChatFeedback | null) => {
506
+ setFeedback(f => ({ ...f, [message.id]: value }));
507
+ };
508
+
509
+ const handlePermissionChange = (permission: ChatToolPermission, allowed: boolean) => {
510
+ setPermissions(list => list?.map(x => (x.id === permission.id ? { ...x, allowed } : x)));
511
+ };
512
+
513
+ const handleRetry = () => {
514
+ setRetries(n => n + 1);
515
+ };
516
+
517
+ return (
518
+ <div className='h-640 w-480'>
519
+ <WallyAssistant
520
+ data-testid='wally'
521
+ {...wally.snapshot}
522
+ messages={messages}
523
+ permissions={permissions}
524
+ formatTimestamp={fixedTime}
525
+ onSubmit={wally.send}
526
+ onStop={wally.stop}
527
+ onApprovalRespond={wally.respond}
528
+ onFeedback={handleFeedback}
529
+ onPermissionChange={handlePermissionChange}
530
+ onRetry={handleRetry}
531
+ footerStart={
532
+ retries ? <p className='text-xs text-text-secondary'>Retried {retries}×</p> : null
533
+ }
534
+ />
535
+ </div>
536
+ );
537
+ };
538
+
539
+ /**
540
+ * Every seam at once: `renderPart` draws a `custom` attack summary, `renderStepContent` puts a table
541
+ * under `search_attacks`, `renderMessageActions` adds a tagged Copy and an Ellipsis menu, a thread
542
+ * `Select` in `headerTitle`, an attach button in `inputTools`, and German `labels`.
543
+ */
544
+ export const Overrides: StoryFn<typeof meta> = () => {
545
+ const wally = useMockWally({ initial: OVERRIDES });
546
+ const [feedback, setFeedback] = useState<Record<string, ChatFeedback | null>>({});
547
+ const [permissions, setPermissions] = useState(OVERRIDES.permissions);
548
+ const messages = wally.snapshot.messages.map(m =>
549
+ m.role === 'assistant' && m.id in feedback ? withFeedback(m, feedback[m.id] ?? null) : m,
550
+ );
551
+
552
+ const handleFeedback = (message: ChatAssistantMessage, value: ChatFeedback | null) => {
553
+ setFeedback(f => ({ ...f, [message.id]: value }));
554
+ };
555
+
556
+ const handlePermissionChange = (permission: ChatToolPermission, allowed: boolean) => {
557
+ setPermissions(list => list?.map(x => (x.id === permission.id ? { ...x, allowed } : x)));
558
+ };
559
+
560
+ return (
561
+ <div className='h-640 w-480'>
562
+ <WallyAssistant
563
+ data-testid='wally'
564
+ {...wally.snapshot}
565
+ messages={messages}
566
+ permissions={permissions}
567
+ formatTimestamp={fixedTime}
568
+ onSubmit={wally.send}
569
+ onStop={wally.stop}
570
+ onApprovalRespond={wally.respond}
571
+ onFeedback={handleFeedback}
572
+ onPermissionChange={handlePermissionChange}
573
+ renderPart={renderPart}
574
+ renderStepContent={renderStepContent}
575
+ renderMessageActions={renderMessageActions}
576
+ headerTitle={
577
+ <Select collection={threads} defaultValue={['attacks']}>
578
+ <SelectButton variant='ghost' size='small' placeholder='New chat' />
579
+ <SelectPositioner>
580
+ <SelectContent>
581
+ {threads.items.map(item => (
582
+ <SelectOption key={item.value} item={item}>
583
+ <SelectOptionText>{item.label}</SelectOptionText>
584
+ <SelectOptionIndicator />
585
+ </SelectOption>
586
+ ))}
587
+ </SelectContent>
588
+ </SelectPositioner>
589
+ </Select>
590
+ }
591
+ inputTools={
592
+ <Button variant='ghost' color='neutral' size='small' aria-label='Datei anhängen'>
593
+ <Paperclip />
594
+ </Button>
595
+ }
596
+ labels={{
597
+ placeholder: 'Frag Wally...',
598
+ input: 'Nachricht an Wally',
599
+ send: 'Senden',
600
+ stop: 'Antwort stoppen',
601
+ disclaimer: 'Wally kann sich irren; bitte prüfen.',
602
+ suggestions: 'Vorschläge',
603
+ feedbackPositive: 'Gute Antwort',
604
+ feedbackNegative: 'Schlechte Antwort',
605
+ }}
606
+ />
607
+ </div>
608
+ );
609
+ };
610
+
611
+ /** A full-page chat with a centered 768px column, built from `ChatViewProvider`, `deriveChatState`, `Chat`, `Conversation*`, `ChatMessageView`, docked `ChatPartView` and its own `ChatInput` — proof the open parts reuse every rule. */
612
+ export const CustomLayout: StoryFn<typeof meta> = () => {
613
+ const wally = useMockWally({ initial: CONVERSATION });
614
+ const [draft, setDraft] = useState('');
615
+ const { messages, status } = wally.snapshot;
616
+ const state = deriveChatState(messages, status, 'docked');
617
+ const last = state.rows[state.rows.length - 1];
618
+
619
+ const handleSubmit = (text: string) => {
620
+ if (state.awaitingApproval) return;
621
+ wally.send(text);
622
+ setDraft('');
623
+ };
624
+
625
+ return (
626
+ <ChatViewProvider
627
+ status={status}
628
+ approvalPlacement='docked'
629
+ formatTimestamp={fixedTime}
630
+ onApprovalRespond={wally.respond}
631
+ >
632
+ <div className='h-screen'>
633
+ <Chat data-testid='wally' background={state.empty ? 'strong' : 'subtle'}>
634
+ <ChatHeader>
635
+ <ChatHeaderTitle label='Wally workspace' />
636
+ </ChatHeader>
637
+ <ConversationRoot autoScrollKey={state.lastUserMessageId}>
638
+ <ConversationContent aria-busy={state.busy} contentClassName='mx-auto w-full max-w-768'>
639
+ {state.rows.map((m, i) => (
640
+ <ChatMessageView
641
+ key={m.id}
642
+ message={m}
643
+ index={i}
644
+ isLast={i === state.rows.length - 1}
645
+ />
646
+ ))}
647
+ </ConversationContent>
648
+ <ConversationScrollButton />
649
+ </ConversationRoot>
650
+ <ChatFooter className='mx-auto w-full max-w-768'>
651
+ {last &&
652
+ state.dockedApprovals.map(a => (
653
+ <ChatPartView key={a.id} part={a} message={last} isLast placement='docked' />
654
+ ))}
655
+ <ChatInput
656
+ value={draft}
657
+ onValueChange={setDraft}
658
+ onSubmit={handleSubmit}
659
+ onStop={wally.stop}
660
+ isRunning={state.running}
661
+ >
662
+ <ChatInputTextarea />
663
+ <ChatInputSubmit disabled={state.awaitingApproval && !state.running} />
664
+ </ChatInput>
665
+ </ChatFooter>
666
+ </Chat>
667
+ </div>
668
+ </ChatViewProvider>
669
+ );
670
+ };
671
+ CustomLayout.parameters = { layout: 'fullscreen' };
672
+
673
+ /** Hosted in a 480px Drawer opened by `ChatTrigger`. Esc in the textarea while a reply streams stops it without closing the drawer — ChatInput handles that itself, no host recipe needed. */
674
+ export const InDrawer: StoryFn<typeof meta> = () => {
675
+ const wally = useMockWally({ initial: CONVERSATION });
676
+
677
+ return (
678
+ <Drawer width={480}>
679
+ <DrawerTrigger asChild>
680
+ <ChatTrigger data-testid='chat-trigger' />
681
+ </DrawerTrigger>
682
+ <DrawerContent>
683
+ {/* Names the dialog "Wally" (Ark points aria-labelledby at the title); hidden because
684
+ the panel header already shows it. */}
685
+ <div className='sr-only'>
686
+ <DrawerTitle>Wally</DrawerTitle>
687
+ </div>
688
+ <WallyAssistant
689
+ data-testid='wally'
690
+ {...wally.snapshot}
691
+ formatTimestamp={fixedTime}
692
+ onSubmit={wally.send}
693
+ onStop={wally.stop}
694
+ onApprovalRespond={wally.respond}
695
+ headerActions={
696
+ <>
697
+ <Button variant='ghost' color='neutral' size='small' aria-label='New chat'>
698
+ <Plus />
699
+ </Button>
700
+ <Button variant='ghost' color='neutral' size='small' aria-label='Chat history'>
701
+ <History />
702
+ </Button>
703
+ <Separator orientation='vertical' />
704
+ <DrawerClose asChild>
705
+ <Button variant='ghost' color='neutral' size='small' aria-label='Close chat'>
706
+ <X />
707
+ </Button>
708
+ </DrawerClose>
709
+ </>
710
+ }
711
+ />
712
+ </DrawerContent>
713
+ </Drawer>
714
+ );
715
+ };
716
+
717
+ /** Analytics attributes land on real nodes: the root `<section>`, header-action Buttons and a tagged Copy from `renderMessageActions` (like, dislike and timestamp stay default). Closed targets (pills, Send, approval options) report through their callbacks — see ANALYTICS_GAPS.md. */
718
+ export const Analytics: StoryFn<typeof meta> = () => {
719
+ const wally = useMockWally({ initial: CONVERSATION });
720
+ const [feedback, setFeedback] = useState<Record<string, ChatFeedback | null>>({});
721
+ const [permissions, setPermissions] = useState(CONVERSATION.permissions);
722
+ const messages = wally.snapshot.messages.map(m =>
723
+ m.role === 'assistant' && m.id in feedback ? withFeedback(m, feedback[m.id] ?? null) : m,
724
+ );
725
+
726
+ const handleFeedback = (message: ChatAssistantMessage, value: ChatFeedback | null) => {
727
+ setFeedback(f => ({ ...f, [message.id]: value }));
728
+ };
729
+
730
+ const handlePermissionChange = (permission: ChatToolPermission, allowed: boolean) => {
731
+ setPermissions(list => list?.map(x => (x.id === permission.id ? { ...x, allowed } : x)));
732
+ };
733
+
734
+ return (
735
+ <div className='h-640 w-480'>
736
+ <WallyAssistant
737
+ data-testid='wally'
738
+ data-analytics-id='wally.panel'
739
+ data-analytics-props='{"surface":"story"}'
740
+ {...wally.snapshot}
741
+ messages={messages}
742
+ permissions={permissions}
743
+ formatTimestamp={fixedTime}
744
+ onSubmit={wally.send}
745
+ onStop={wally.stop}
746
+ onApprovalRespond={wally.respond}
747
+ onFeedback={handleFeedback}
748
+ onPermissionChange={handlePermissionChange}
749
+ headerActions={
750
+ <Button
751
+ variant='ghost'
752
+ color='neutral'
753
+ size='small'
754
+ aria-label='New chat'
755
+ data-analytics-id='wally.new-chat'
756
+ >
757
+ <Plus />
758
+ </Button>
759
+ }
760
+ renderMessageActions={renderAnalyticsActions}
761
+ />
762
+ </div>
763
+ );
764
+ };
765
+ ```
766
+
767
+ ### WallyAssistant/mocks/index.ts
768
+
769
+ ````tsx
770
+ /**
771
+ * Story and test fixtures for WallyAssistant, plus the custom SSE/REST mapper example from spec §10.2.
772
+ * Excluded from the build and the d.ts (`**\/mocks.{ts,tsx}`), like `Table/mocks.tsx`.
773
+ */
774
+ import { useEffect, useReducer, useRef } from 'react';
775
+ import { isRunningStatus } from '../model/deriveChatState';
776
+ import type {
777
+ ChatApprovalPart,
778
+ ChatApprovalResponse,
779
+ ChatAssistantMessage,
780
+ ChatAssistantPart,
781
+ ChatChainOfThoughtPart,
782
+ ChatMessage,
783
+ ChatSnapshot,
784
+ ChatStatus,
785
+ ChatStep,
786
+ ChatStepStatus,
787
+ } from '../model/model';
788
+
789
+ export type WallyEvent =
790
+ | { t: 'user'; id: string; text: string; at?: string }
791
+ /** Insert, or replace by id anywhere in the reply. */
792
+ | { t: 'step'; step: ChatStep; parentId?: string }
793
+ /** Deltas aren't idempotent: real streams carry offsets or snapshots. */
794
+ | { t: 'text'; delta: string }
795
+ /** Insert, or replace by id (resolving it too). */
796
+ | { t: 'approval'; approval: ChatApprovalPart }
797
+ /** "Worked for 12s", stamped on the first block. */
798
+ | { t: 'summary'; text: string }
799
+ | { t: 'error'; text: string }
800
+ | { t: 'end'; stopped?: boolean };
801
+
802
+ const LIVE = new Set<ChatStepStatus>(['pending', 'running', 'awaiting-approval']);
803
+
804
+ const upsert = <T extends { id: string }>(list: readonly T[], item: T): readonly T[] => {
805
+ const i = list.findIndex(x => x.id === item.id);
806
+ return i < 0 ? [...list, item] : list.map((x, j) => (j === i ? item : x));
807
+ };
808
+
809
+ const isCot = (p: ChatAssistantPart): p is ChatChainOfThoughtPart => p.type === 'chain-of-thought';
810
+
811
+ const hasStep = (steps: readonly ChatStep[], id: string): boolean =>
812
+ steps.some(s => s.id === id || hasStep(s.steps ?? [], id));
813
+
814
+ /** Depth-first map; untouched branches keep their identity. */
815
+ const mapSteps = (
816
+ steps: readonly ChatStep[],
817
+ f: (s: ChatStep) => ChatStep,
818
+ ): readonly ChatStep[] => {
819
+ const next = steps.map(s => {
820
+ const kids = s.steps && mapSteps(s.steps, f);
821
+ return f(kids === s.steps ? s : { ...s, steps: kids });
822
+ });
823
+ return next.every((s, i) => s === steps[i]) ? steps : next;
824
+ };
825
+
826
+ const mapBlocks = (
827
+ m: ChatAssistantMessage,
828
+ f: (steps: readonly ChatStep[]) => readonly ChatStep[],
829
+ ): ChatAssistantMessage => {
830
+ const parts = m.parts.map(p => {
831
+ if (!isCot(p)) return p;
832
+ const steps = f(p.steps);
833
+ return steps === p.steps ? p : { ...p, steps };
834
+ });
835
+ return parts.every((p, i) => p === m.parts[i]) ? m : { ...m, parts };
836
+ };
837
+
838
+ const addStep = (
839
+ m: ChatAssistantMessage,
840
+ step: ChatStep,
841
+ parentId?: string,
842
+ ): ChatAssistantMessage => {
843
+ const has = (id: string) => m.parts.some(p => isCot(p) && hasStep(p.steps, id));
844
+ if (has(step.id)) {
845
+ return mapBlocks(m, s =>
846
+ mapSteps(s, x => (x.id === step.id ? { ...step, steps: step.steps ?? x.steps } : x)),
847
+ );
848
+ }
849
+ if (parentId && has(parentId)) {
850
+ return mapBlocks(m, s =>
851
+ mapSteps(s, x => (x.id === parentId ? { ...x, steps: [...(x.steps ?? []), step] } : x)),
852
+ );
853
+ }
854
+ const tail = [...m.parts].reverse().find(p => p.type !== 'approval'); // approvals don't close a block
855
+ if (tail && isCot(tail)) {
856
+ return {
857
+ ...m,
858
+ parts: m.parts.map(p => (p === tail ? { ...tail, steps: [...tail.steps, step] } : p)),
859
+ };
860
+ }
861
+ // After text (or at the start): a new block.
862
+ return {
863
+ ...m,
864
+ parts: [
865
+ ...m.parts,
866
+ { type: 'chain-of-thought', id: `${m.id}:cot:${m.parts.length}`, steps: [step] },
867
+ ],
868
+ };
869
+ };
870
+
871
+ const settle = (m: ChatAssistantMessage, to: ChatStepStatus) =>
872
+ mapBlocks(m, s => mapSteps(s, x => (LIVE.has(x.status) ? { ...x, status: to } : x)));
873
+
874
+ const cancelApprovals = (m: ChatAssistantMessage): ChatAssistantMessage => ({
875
+ ...m,
876
+ parts: m.parts.map(p =>
877
+ p.type === 'approval' && p.status === 'pending'
878
+ ? { ...p, status: 'rejected' as const, resolvedLabel: 'Cancelled' }
879
+ : p,
880
+ ),
881
+ });
882
+
883
+ const lastUserId = (s: ChatSnapshot) => {
884
+ for (let i = s.messages.length - 1; i >= 0; i--) {
885
+ if (s.messages[i]?.role === 'user') return s.messages[i]?.id;
886
+ }
887
+ return undefined;
888
+ };
889
+
890
+ /** The reply to the last user message, created on demand; its id is the one WallyAssistant's loading row uses. */
891
+ const withReply = (
892
+ s: ChatSnapshot,
893
+ f: (m: ChatAssistantMessage) => ChatAssistantMessage,
894
+ status: ChatStatus = 'streaming',
895
+ ): ChatSnapshot => {
896
+ const id = `${lastUserId(s) ?? 'boot'}:reply`;
897
+ const prev = s.messages.find(m => m.id === id);
898
+ const reply: ChatAssistantMessage =
899
+ prev?.role === 'assistant' ? prev : { role: 'assistant', id, parts: [] };
900
+ return { ...s, status, messages: upsert(s.messages, f(reply)) }; // untouched messages keep their identity
901
+ };
902
+
903
+ /** Folds one backend event into the next immutable snapshot (spec §10.2). */
904
+ export function reduceWally(s: ChatSnapshot, e: WallyEvent): ChatSnapshot {
905
+ switch (e.t) {
906
+ case 'user':
907
+ return {
908
+ ...s,
909
+ status: 'submitted',
910
+ messages: upsert(s.messages, {
911
+ role: 'user',
912
+ id: e.id,
913
+ createdAt: e.at,
914
+ parts: [{ type: 'text', id: `${e.id}:text`, text: e.text }],
915
+ }),
916
+ };
917
+ case 'step':
918
+ return withReply(s, m => addStep(m, e.step, e.parentId));
919
+ case 'text':
920
+ return withReply(s, m => {
921
+ const last = m.parts[m.parts.length - 1];
922
+ return last?.type === 'text'
923
+ ? { ...m, parts: [...m.parts.slice(0, -1), { ...last, text: last.text + e.delta }] }
924
+ : {
925
+ ...m,
926
+ parts: [
927
+ ...m.parts,
928
+ { type: 'text', id: `${m.id}:text:${m.parts.length}`, text: e.delta },
929
+ ],
930
+ };
931
+ });
932
+ case 'approval':
933
+ return withReply(s, m => {
934
+ const n = { ...m, parts: upsert(m.parts, e.approval) };
935
+ if (e.approval.status === 'pending') return n;
936
+ // ponytail: approvals don't name their step, so a resolution settles every HITL step in the reply
937
+ const to = e.approval.status === 'approved' ? 'done' : 'denied';
938
+ return mapBlocks(n, st =>
939
+ mapSteps(st, x => (x.status === 'awaiting-approval' ? { ...x, status: to } : x)),
940
+ );
941
+ });
942
+ case 'summary':
943
+ return withReply(s, m => {
944
+ const first = m.parts.find(isCot);
945
+ return first
946
+ ? { ...m, parts: m.parts.map(p => (p === first ? { ...first, summary: e.text } : p)) }
947
+ : m;
948
+ });
949
+ case 'error':
950
+ return withReply(
951
+ s,
952
+ m => {
953
+ const n = settle(m, 'error');
954
+ return {
955
+ ...n,
956
+ parts: upsert(n.parts, {
957
+ type: 'error',
958
+ id: `${m.id}:error`,
959
+ text: e.text,
960
+ retryable: true,
961
+ }),
962
+ };
963
+ },
964
+ 'error',
965
+ );
966
+ case 'end':
967
+ return e.stopped
968
+ ? withReply(s, m => cancelApprovals(settle(m, 'cancelled')), 'ready')
969
+ : { ...s, status: 'ready' };
970
+ default:
971
+ return s; // unknown events are ignored: forward compatible
972
+ }
973
+ }
974
+
975
+ // ---------------------------------------------------------------------------------------------------------------
976
+ // Script
977
+ // ---------------------------------------------------------------------------------------------------------------
978
+
979
+ /** About 30ms per token at `speed: 1`. */
980
+ const TOKEN_MS = 30;
981
+ const tokens = (text: string) => text.match(/\S+\s*/g) ?? [];
982
+
983
+ const REASONING =
984
+ 'The user wants the noisiest attack sources. I will query the last 24 hours of attack events, ' +
985
+ 'check which applications they hit, then read the policy to see what is already blocked.';
986
+ const ANSWER =
987
+ 'Over the last 24 hours `api.example.com` received 1,204 attacks, mostly SQL injection from 3 IPs.\n\n' +
988
+ 'Two of those IPs are already on the denylist. I can propose a rule for the third one, `203.0.113.7`.';
989
+
990
+ const reasoningStep = (status: ChatStepStatus, label: string, content?: string): ChatStep => ({
991
+ id: 'reasoning',
992
+ kind: 'reasoning',
993
+ status,
994
+ label,
995
+ content,
996
+ });
997
+
998
+ const tool = (id: string, icon: string, running: string, done: string, metric?: string) =>
999
+ [
1000
+ [500, { t: 'step', step: { id, kind: 'tool', icon, status: 'running', label: running } }],
1001
+ [900, { t: 'step', step: { id, kind: 'tool', icon, status: 'done', label: done, metric } }],
1002
+ ] satisfies [number, WallyEvent][];
1003
+
1004
+ /** `[delayMs, event]` pairs replayed by `useMockWally` after the user event (Streaming Simulation story). */
1005
+ export const WALLY_SCRIPT: readonly (readonly [number, WallyEvent])[] = [
1006
+ [400, { t: 'step', step: reasoningStep('running', 'Thinking...') }],
1007
+ ...tokens(REASONING).map(
1008
+ (_, i, all) =>
1009
+ [
1010
+ TOKEN_MS,
1011
+ { t: 'step', step: reasoningStep('running', 'Thinking...', all.slice(0, i + 1).join('')) },
1012
+ ] as const,
1013
+ ),
1014
+ [200, { t: 'step', step: reasoningStep('done', 'Thought for 3s', REASONING) }],
1015
+ ...tool('query', 'query', 'Querying attack events...', 'Queried attack events', '1,204'),
1016
+ ...tool('apps', 'scan', 'Checking applications...', 'Checked 3 applications'),
1017
+ ...tool(
1018
+ 'policy',
1019
+ 'read',
1020
+ 'Reading the policy for api.example.com...',
1021
+ 'Read the policy for api.example.com',
1022
+ ),
1023
+ ...tokens(ANSWER).map((delta, i) => [i === 0 ? 400 : TOKEN_MS, { t: 'text', delta }] as const),
1024
+ [300, { t: 'summary', text: 'Worked for 12s' }],
1025
+ [100, { t: 'end' }],
1026
+ ];
1027
+
1028
+ // ---------------------------------------------------------------------------------------------------------------
1029
+ // Fixtures
1030
+ // ---------------------------------------------------------------------------------------------------------------
1031
+
1032
+ const userText = (id: string, text: string, createdAt?: string): ChatMessage => ({
1033
+ role: 'user',
1034
+ id,
1035
+ createdAt,
1036
+ parts: [{ type: 'text', id: `${id}:text`, text }],
1037
+ });
1038
+
1039
+ const SUGGESTIONS: ChatSnapshot['suggestions'] = [
1040
+ { id: 'top-attacks', label: 'Show top attacks today' },
1041
+ {
1042
+ id: 'blocked',
1043
+ label: 'Which requests were blocked?',
1044
+ prompt: 'Which requests were blocked in the last hour?',
1045
+ },
1046
+ { id: 'rule', label: 'Create a mitigation rule' },
1047
+ ];
1048
+
1049
+ const FOLLOW_UPS: ChatSnapshot['suggestions'] = [
1050
+ { id: 'deny', label: 'Add 203.0.113.7 to the denylist' },
1051
+ { id: 'details', label: 'Show attack details' },
1052
+ ];
1053
+
1054
+ /** No messages: the greeting with suggestion pills. */
1055
+ export const EMPTY: ChatSnapshot = { messages: [], status: 'ready', suggestions: SUGGESTIONS };
1056
+
1057
+ /** Two finished turns plus follow-up suggestions and tool permissions. Timestamps are fixed: pass a fixed
1058
+ * `formatTimestamp` in screenshots. */
1059
+ export const CONVERSATION: ChatSnapshot = {
1060
+ status: 'ready',
1061
+ messages: [
1062
+ userText('u1', 'Show me the top attacks from the last 24 hours', '2026-10-21T09:00:00.000Z'),
1063
+ {
1064
+ role: 'assistant',
1065
+ id: 'u1:reply',
1066
+ createdAt: '2026-10-21T09:00:12.000Z',
1067
+ parts: [
1068
+ {
1069
+ type: 'chain-of-thought',
1070
+ id: 'u1:reply:cot:0',
1071
+ summary: 'Worked for 12s',
1072
+ steps: [
1073
+ reasoningStep('done', 'Thought for 3s', REASONING),
1074
+ {
1075
+ id: 'query',
1076
+ kind: 'tool',
1077
+ icon: 'query',
1078
+ status: 'done',
1079
+ label: 'Queried attack events',
1080
+ metric: '1,204',
1081
+ },
1082
+ {
1083
+ id: 'apps',
1084
+ kind: 'tool',
1085
+ icon: 'scan',
1086
+ status: 'done',
1087
+ label: 'Checked 3 applications',
1088
+ },
1089
+ ],
1090
+ },
1091
+ { type: 'text', id: 'u1:reply:text:1', text: ANSWER },
1092
+ ],
1093
+ },
1094
+ userText('u2', 'Which of them were blocked?', '2026-10-21T09:01:00.000Z'),
1095
+ {
1096
+ role: 'assistant',
1097
+ id: 'u2:reply',
1098
+ createdAt: '2026-10-21T09:01:05.000Z',
1099
+ feedback: 'positive',
1100
+ parts: [
1101
+ {
1102
+ type: 'chain-of-thought',
1103
+ id: 'u2:reply:cot:0',
1104
+ steps: [{ id: 'reasoning', kind: 'reasoning', status: 'done', label: 'Thought for 2s' }],
1105
+ },
1106
+ {
1107
+ type: 'text',
1108
+ id: 'u2:reply:text:1',
1109
+ text: '1,019 of the 1,204 attacks were blocked by the `sqli-default` rule. The other 185 came from `203.0.113.7`, which is not on the denylist yet.',
1110
+ },
1111
+ ],
1112
+ },
1113
+ ],
1114
+ suggestions: FOLLOW_UPS,
1115
+ permissions: [
1116
+ {
1117
+ id: 'deny',
1118
+ title: 'propose_add_to_denylist',
1119
+ description: 'Propose a denylist entry for an IP',
1120
+ allowed: true,
1121
+ },
1122
+ { id: 'rules', title: 'create_mitigation_rule', allowed: false },
1123
+ ],
1124
+ };
1125
+
1126
+ /** A reply waiting on a pending approval. `status` stays 'ready', as with CopilotKit HITL tools (§7.5). */
1127
+ export const APPROVAL_PENDING: ChatSnapshot = {
1128
+ status: 'ready',
1129
+ messages: [
1130
+ userText('u1', 'Block 203.0.113.7 for a day'),
1131
+ {
1132
+ role: 'assistant',
1133
+ id: 'u1:reply',
1134
+ parts: [
1135
+ {
1136
+ type: 'chain-of-thought',
1137
+ id: 'u1:reply:cot:0',
1138
+ steps: [
1139
+ {
1140
+ id: 'policy',
1141
+ kind: 'tool',
1142
+ icon: 'read',
1143
+ status: 'done',
1144
+ label: 'Read the policy for api.example.com',
1145
+ },
1146
+ {
1147
+ id: 'deny',
1148
+ kind: 'tool',
1149
+ name: 'propose_add_to_denylist',
1150
+ icon: 'create',
1151
+ status: 'awaiting-approval',
1152
+ label: 'Proposing a denylist entry...',
1153
+ },
1154
+ ],
1155
+ },
1156
+ {
1157
+ type: 'approval',
1158
+ id: 'u1:reply:approval',
1159
+ status: 'pending',
1160
+ content:
1161
+ 'Wally wants to run `propose_add_to_denylist`:\n\n```json\n{ "ip": "203.0.113.7", "duration": "24h" }\n```',
1162
+ },
1163
+ ],
1164
+ },
1165
+ ],
1166
+ };
1167
+
1168
+ /** A run that failed before any output: an assistant message with only a retryable error part. */
1169
+ export const ERROR_TURN: ChatSnapshot = {
1170
+ status: 'error',
1171
+ messages: [
1172
+ userText('u1', 'Show me the top attacks from the last 24 hours'),
1173
+ {
1174
+ role: 'assistant',
1175
+ id: 'u1:reply',
1176
+ parts: [
1177
+ {
1178
+ type: 'error',
1179
+ id: 'u1:reply:error',
1180
+ text: "Wally couldn't reach the attack store.",
1181
+ retryable: true,
1182
+ },
1183
+ ],
1184
+ },
1185
+ ],
1186
+ };
1187
+
1188
+ /** `n` finished messages, alternating user and assistant, for the perf test. */
1189
+ export const longThread = (n: number): ChatSnapshot => ({
1190
+ status: 'ready',
1191
+ messages: Array.from({ length: n }, (_, i): ChatMessage => {
1192
+ const turn = Math.floor(i / 2) + 1;
1193
+ return i % 2 === 0
1194
+ ? userText(`u${turn}`, `Question ${turn}`)
1195
+ : {
1196
+ role: 'assistant',
1197
+ id: `u${turn}:reply`,
1198
+ parts: [{ type: 'text', id: `u${turn}:reply:text:0`, text: `Answer ${turn}` }],
1199
+ };
1200
+ }),
1201
+ });
1202
+
1203
+ // ---------------------------------------------------------------------------------------------------------------
1204
+ // Hook
1205
+ // ---------------------------------------------------------------------------------------------------------------
1206
+
1207
+ export interface UseMockWallyOptions {
1208
+ /** Default `EMPTY`. */
1209
+ initial?: ChatSnapshot;
1210
+ /** Default `WALLY_SCRIPT`. */
1211
+ script?: readonly (readonly [number, WallyEvent])[];
1212
+ /** Delay multiplier: 1 = about 30ms per token, 0 = as fast as timers allow. Default 1. */
1213
+ speed?: number;
1214
+ }
1215
+
1216
+ export interface MockWally {
1217
+ snapshot: ChatSnapshot;
1218
+ /** Appends a user message (`u1`, `u2`, …) and replays the script. */
1219
+ send: (text: string) => void;
1220
+ /** Clears the timers, cancels live steps and pending approvals. */
1221
+ stop: () => void;
1222
+ /** Same signature as `onApprovalRespond`: resolves the approval in the next snapshot. */
1223
+ respond: (response: ChatApprovalResponse, approval: ChatApprovalPart) => void;
1224
+ }
1225
+
1226
+ /** Replays `script` against `reduceWally` with a `setTimeout` chain. Deterministic ids for stable screenshots. */
1227
+ export function useMockWally({
1228
+ initial = EMPTY,
1229
+ script = WALLY_SCRIPT,
1230
+ speed = 1,
1231
+ }: UseMockWallyOptions = {}): MockWally {
1232
+ const [snapshot, dispatch] = useReducer(reduceWally, initial);
1233
+ const timer = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);
1234
+
1235
+ useEffect(() => () => clearTimeout(timer.current), []);
1236
+
1237
+ const play = (i: number) => {
1238
+ const entry = script[i];
1239
+ if (!entry) return;
1240
+ const [delay, event] = entry;
1241
+ timer.current = setTimeout(() => {
1242
+ dispatch(event);
1243
+ play(i + 1);
1244
+ }, delay * speed);
1245
+ };
1246
+
1247
+ const send = (text: string) => {
1248
+ clearTimeout(timer.current);
1249
+ const n = snapshot.messages.filter(m => m.role === 'user').length + 1;
1250
+ dispatch({ t: 'user', id: `u${n}`, text });
1251
+ play(0);
1252
+ };
1253
+
1254
+ const stop = () => {
1255
+ clearTimeout(timer.current);
1256
+ dispatch({ t: 'end', stopped: true });
1257
+ };
1258
+
1259
+ const respond = (response: ChatApprovalResponse, approval: ChatApprovalPart) => {
1260
+ dispatch({
1261
+ t: 'approval',
1262
+ approval: { ...approval, status: response.approved ? 'approved' : 'rejected' },
1263
+ });
1264
+ // The reducer marks the reply as streaming; outside a run, put the status back to 'ready'.
1265
+ if (!isRunningStatus(snapshot.status)) dispatch({ t: 'end' });
1266
+ };
1267
+
1268
+ return { snapshot, send, stop, respond };
1269
+ }
1270
+ ````
1271
+
1272
+ ### WallyAssistant/model/deriveChatState.ts
1273
+
1274
+ ```tsx
1275
+ import type {
1276
+ ChatApprovalPart,
1277
+ ChatId,
1278
+ ChatMessage,
1279
+ ChatStatus,
1280
+ ChatStep,
1281
+ ChatStepStatus,
1282
+ } from './model';
1283
+
1284
+ export interface ChatViewState {
1285
+ /** `messages`, plus an empty reply `{ role: 'assistant', id: `${lastUserMessageId}:reply`, parts: [] }` while
1286
+ * running and the last message is the user's (the loading row; the same id as the recommended mapper reply). */
1287
+ rows: readonly ChatMessage[];
1288
+ /** `messages.length === 0`. */
1289
+ empty: boolean;
1290
+ /** `status` is 'submitted' or 'streaming'. */
1291
+ running: boolean;
1292
+ /** The last row has a pending approval part. */
1293
+ awaitingApproval: boolean;
1294
+ /** `running && !awaitingApproval` → `aria-busy`. */
1295
+ busy: boolean;
1296
+ /** → Conversation `autoScrollKey`. */
1297
+ lastUserMessageId?: ChatId;
1298
+ /** Placement 'docked' only (§7.5). Render each with
1299
+ * `<ChatPartView part message={rows.at(-1)} isLast placement='docked' />`. */
1300
+ dockedApprovals: readonly ChatApprovalPart[];
1301
+ }
1302
+
1303
+ const LIVE_STEP_STATUSES: ReadonlySet<string> = new Set<ChatStepStatus>([
1304
+ 'pending',
1305
+ 'running',
1306
+ 'awaiting-approval',
1307
+ ]);
1308
+ const NO_APPROVALS: readonly ChatApprovalPart[] = [];
1309
+
1310
+ /** 'submitted' | 'streaming'. Unknown statuses behave like 'ready'. */
1311
+ export const isRunningStatus = (status: ChatStatus | undefined): boolean =>
1312
+ status === 'submitted' || status === 'streaming';
1313
+
1314
+ /** The step's own status is pending, running or awaiting-approval. */
1315
+ export const isLiveStepStatus = (status: ChatStepStatus): boolean => LIVE_STEP_STATUSES.has(status);
1316
+
1317
+ /** The step or any nested step is live (§7.3). */
1318
+ export const isLiveStep = (step: ChatStep): boolean =>
1319
+ isLiveStepStatus(step.status) || (step.steps?.some(isLiveStep) ?? false);
1320
+
1321
+ const approvalsOf = (message: ChatMessage | undefined): ChatApprovalPart[] =>
1322
+ message?.role === 'assistant'
1323
+ ? message.parts.filter((p): p is ChatApprovalPart => p.type === 'approval')
1324
+ : [];
1325
+
1326
+ export const hasPendingApproval = (message: ChatMessage | undefined): boolean =>
1327
+ approvalsOf(message).some(p => p.status === 'pending');
1328
+
1329
+ /** The last row's pending approvals; when none is pending, its last approval (decision 11). */
1330
+ export const getDockedApprovals = (
1331
+ lastRow: ChatMessage | undefined,
1332
+ ): readonly ChatApprovalPart[] => {
1333
+ const approvals = approvalsOf(lastRow);
1334
+ const pending = approvals.filter(p => p.status === 'pending');
1335
+ if (pending.length) return pending;
1336
+ const last = approvals[approvals.length - 1];
1337
+ return last ? [last] : NO_APPROVALS;
1338
+ };
1339
+
1340
+ /** What Copy copies: `copyText`, or the non-empty text parts joined by a blank line. */
1341
+ export const copyTextOf = (message: ChatMessage): string =>
1342
+ (message.role === 'assistant' && message.copyText) ||
1343
+ message.parts.flatMap(p => (p.type === 'text' && p.text ? [p.text] : [])).join('\n\n');
1344
+
1345
+ const lastUserIdOf = (messages: readonly ChatMessage[]): ChatId | undefined => {
1346
+ for (let i = messages.length - 1; i >= 0; i--) {
1347
+ const m = messages[i];
1348
+ if (m?.role === 'user') return m.id;
1349
+ }
1350
+ return undefined;
1351
+ };
1352
+
1353
+ /** Every derived rule of the chat, in one pure function shared by WallyAssistant and custom layouts (§7.1).
1354
+ * `approvalPlacement` must match the `ChatViewProvider`'s, or a docked approval renders twice or never. */
1355
+ export function deriveChatState(
1356
+ messages: readonly ChatMessage[],
1357
+ status: ChatStatus = 'ready',
1358
+ approvalPlacement: 'docked' | 'inline' = 'inline',
1359
+ ): ChatViewState {
1360
+ const running = isRunningStatus(status);
1361
+ const lastUserMessageId = lastUserIdOf(messages);
1362
+ const last = messages[messages.length - 1];
1363
+ const rows: readonly ChatMessage[] =
1364
+ running && last?.role === 'user'
1365
+ ? [...messages, { role: 'assistant', id: `${last.id}:reply`, parts: [] }]
1366
+ : messages;
1367
+ const lastRow = rows[rows.length - 1];
1368
+ const awaitingApproval = hasPendingApproval(lastRow);
1369
+
1370
+ return {
1371
+ rows,
1372
+ empty: messages.length === 0,
1373
+ running,
1374
+ awaitingApproval,
1375
+ busy: running && !awaitingApproval,
1376
+ lastUserMessageId,
1377
+ dockedApprovals: approvalPlacement === 'docked' ? getDockedApprovals(lastRow) : NO_APPROVALS,
1378
+ };
1379
+ }
1380
+ ```