@wallarm-org/claude-design 0.0.0-stage → 4.9.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,2170 @@
1
+ # NotificationCenter story sources
2
+
3
+ ### NotificationCenter/NotificationCenter.stories.tsx
4
+
5
+ ```tsx
6
+ import { useState } from 'react';
7
+ import type { Meta, StoryFn } from 'storybook-react-rsbuild';
8
+ import { CheckCheck, History, Settings, TriangleAlert } from '../../icons';
9
+ import { AppShell } from '../AppShell/AppShell';
10
+ import { AppShellHeader } from '../AppShell/AppShellHeader';
11
+ import { QuickHelpDropdown, WallarmLogo } from '../AppShell/story-content';
12
+ import { Button } from '../Button';
13
+ import { ChatTrigger } from '../Chat';
14
+ import {
15
+ EmptyStateActions,
16
+ EmptyStateDescription,
17
+ EmptyStateIllustration,
18
+ EmptyStateMessage,
19
+ EmptyStateTitle,
20
+ } from '../EmptyState';
21
+ import { NumericBadge } from '../NumericBadge';
22
+ import { TabsContent, TabsTrigger } from '../Tabs';
23
+ import { TopHeader, TopHeaderActions, TopHeaderLogo, TopHeaderSeparator } from '../TopHeader';
24
+ import { NotificationCenter } from './NotificationCenter';
25
+ import {
26
+ NotificationCenterBody,
27
+ NotificationCenterEmpty,
28
+ NotificationCenterSkeleton,
29
+ } from './NotificationCenterBody';
30
+ import { NotificationCenterContent } from './NotificationCenterContent';
31
+ import {
32
+ NotificationCenterHeader,
33
+ NotificationCenterHeaderAction,
34
+ NotificationCenterHeaderActions,
35
+ NotificationCenterTitle,
36
+ } from './NotificationCenterHeader';
37
+ import {
38
+ NotificationCenterItem,
39
+ NotificationCenterItemActions,
40
+ NotificationCenterItemDescription,
41
+ NotificationCenterList,
42
+ } from './NotificationCenterItem';
43
+ import { NotificationCenterItemTime } from './NotificationCenterItemTime';
44
+ import { NotificationCenterItemTitle } from './NotificationCenterItemTitle';
45
+ import { NotificationCenterTabs, NotificationCenterTabsList } from './NotificationCenterTabs';
46
+ import { NotificationCenterTrigger } from './NotificationCenterTrigger';
47
+
48
+ const DESCRIPTION = [
49
+ 'The in-app notification inbox the bell in the top header opens: unread count on the bell, New and Read tabs, and a list where each card is one click target with its own action buttons.',
50
+ 'It is composition only — the app owns the data, decides when an item counts as read, and picks what each body shows: a list, `NotificationCenterSkeleton` while loading, or `NotificationCenterEmpty` for the empty and error states.',
51
+ ].join(' ');
52
+
53
+ const meta = {
54
+ title: 'Messaging/NotificationCenter',
55
+ component: NotificationCenter,
56
+ subcomponents: {
57
+ NotificationCenterTrigger,
58
+ NotificationCenterContent,
59
+ NotificationCenterHeader,
60
+ NotificationCenterTitle,
61
+ NotificationCenterHeaderActions,
62
+ NotificationCenterHeaderAction,
63
+ NotificationCenterTabs,
64
+ NotificationCenterTabsList,
65
+ NotificationCenterBody,
66
+ NotificationCenterList,
67
+ NotificationCenterItem,
68
+ NotificationCenterItemTitle,
69
+ NotificationCenterItemTime,
70
+ NotificationCenterItemDescription,
71
+ NotificationCenterItemActions,
72
+ NotificationCenterEmpty,
73
+ NotificationCenterSkeleton,
74
+ },
75
+ parameters: {
76
+ layout: 'padded',
77
+ docs: {
78
+ description: {
79
+ component: DESCRIPTION,
80
+ },
81
+ },
82
+ },
83
+ } satisfies Meta<typeof NotificationCenter>;
84
+
85
+ export default meta;
86
+
87
+ const MINUTE = 60_000;
88
+ const minutesAgo = (minutes: number) => new Date(Date.now() - minutes * MINUTE);
89
+
90
+ /**
91
+ * The four kinds of notification are plain composition: two actions for a decision, one call to
92
+ * action for triage and digests, none for an FYI. `data-analytics-id` goes on the title and on
93
+ * each action button.
94
+ */
95
+ export const Basic: StoryFn<typeof meta> = () => (
96
+ <div className='flex h-[720px] justify-end'>
97
+ <NotificationCenter data-testid='notification-center'>
98
+ <NotificationCenterTrigger count={5} data-analytics-id='notifications-open' />
99
+ <NotificationCenterContent>
100
+ <NotificationCenterHeader>
101
+ <NotificationCenterTitle />
102
+ <NotificationCenterHeaderActions>
103
+ <NotificationCenterHeaderAction data-analytics-id='notifications-mark-all-read'>
104
+ Mark all as read
105
+ </NotificationCenterHeaderAction>
106
+ <NotificationCenterHeaderAction asChild aria-label='Notification settings'>
107
+ <a href='#notification-settings'>
108
+ <Settings />
109
+ </a>
110
+ </NotificationCenterHeaderAction>
111
+ </NotificationCenterHeaderActions>
112
+ </NotificationCenterHeader>
113
+ <NotificationCenterTabs defaultValue='new'>
114
+ <NotificationCenterTabsList>
115
+ <TabsTrigger value='new'>
116
+ New <NumericBadge size='small'>5</NumericBadge>
117
+ </TabsTrigger>
118
+ <TabsTrigger value='read'>Read</TabsTrigger>
119
+ </NotificationCenterTabsList>
120
+ <TabsContent value='new'>
121
+ <NotificationCenterBody>
122
+ <NotificationCenterList>
123
+ <NotificationCenterItem unread data-testid='notification-center--item-patch'>
124
+ <NotificationCenterItemTitle data-analytics-id='notification-open'>
125
+ Apply a virtual patch to api.example.com?
126
+ </NotificationCenterItemTitle>
127
+ <NotificationCenterItemTime value={minutesAgo(3)} />
128
+ <NotificationCenterItemDescription>
129
+ Wally drafted a patch for the SQL injection on POST /v1/orders. It blocks the
130
+ payload without touching the code.
131
+ </NotificationCenterItemDescription>
132
+ <NotificationCenterItemActions>
133
+ <Button size='small' data-analytics-id='notification-patch-approve'>
134
+ Approve
135
+ </Button>
136
+ <Button
137
+ size='small'
138
+ variant='outline'
139
+ color='neutral'
140
+ data-analytics-id='notification-patch-reject'
141
+ >
142
+ Reject
143
+ </Button>
144
+ </NotificationCenterItemActions>
145
+ </NotificationCenterItem>
146
+ <NotificationCenterItem unread>
147
+ <NotificationCenterItemTitle data-analytics-id='notification-open'>
148
+ New attack detected on api.example.com
149
+ </NotificationCenterItemTitle>
150
+ <NotificationCenterItemTime value={minutesAgo(9)} />
151
+ <NotificationCenterItemDescription>
152
+ 214 SQL injection attempts from 12 IPs in the last 10 minutes.
153
+ </NotificationCenterItemDescription>
154
+ <NotificationCenterItemActions>
155
+ <Button size='small' data-analytics-id='notification-view-attacks'>
156
+ View attacks
157
+ </Button>
158
+ </NotificationCenterItemActions>
159
+ </NotificationCenterItem>
160
+ <NotificationCenterItem unread>
161
+ <NotificationCenterItemTitle data-analytics-id='notification-open'>
162
+ Node wallarm-node-3 is offline
163
+ </NotificationCenterItemTitle>
164
+ <NotificationCenterItemTime value={minutesAgo(22)} />
165
+ <NotificationCenterItemDescription>
166
+ No heartbeat for 15 minutes. Traffic fails over to wallarm-node-1.
167
+ </NotificationCenterItemDescription>
168
+ </NotificationCenterItem>
169
+ <NotificationCenterItem unread>
170
+ <NotificationCenterItemTitle data-analytics-id='notification-open'>
171
+ Filtering rules updated
172
+ </NotificationCenterItemTitle>
173
+ <NotificationCenterItemTime value={minutesAgo(60)} />
174
+ <NotificationCenterItemDescription>
175
+ All nodes picked up the latest rule set. Nothing to do.
176
+ </NotificationCenterItemDescription>
177
+ </NotificationCenterItem>
178
+ <NotificationCenterItem unread>
179
+ <NotificationCenterItemTitle data-analytics-id='notification-open'>
180
+ Weekly API discovery report is ready
181
+ </NotificationCenterItemTitle>
182
+ <NotificationCenterItemTime value={minutesAgo(120)} />
183
+ <NotificationCenterItemDescription>
184
+ 38 new endpoints this week, 5 of them handling sensitive data.
185
+ </NotificationCenterItemDescription>
186
+ <NotificationCenterItemActions>
187
+ <Button size='small' data-analytics-id='notification-view-report'>
188
+ View report
189
+ </Button>
190
+ </NotificationCenterItemActions>
191
+ </NotificationCenterItem>
192
+ </NotificationCenterList>
193
+ </NotificationCenterBody>
194
+ </TabsContent>
195
+ <TabsContent value='read'>
196
+ <NotificationCenterBody>
197
+ <NotificationCenterList>
198
+ <NotificationCenterItem>
199
+ <NotificationCenterItemTitle>
200
+ Node wallarm-node-2 is back online
201
+ </NotificationCenterItemTitle>
202
+ <NotificationCenterItemTime value={minutesAgo(60 * 24)} />
203
+ <NotificationCenterItemDescription>
204
+ The node reconnected and is filtering traffic again.
205
+ </NotificationCenterItemDescription>
206
+ </NotificationCenterItem>
207
+ </NotificationCenterList>
208
+ </NotificationCenterBody>
209
+ </TabsContent>
210
+ </NotificationCenterTabs>
211
+ </NotificationCenterContent>
212
+ </NotificationCenter>
213
+ </div>
214
+ );
215
+
216
+ /**
217
+ * Read items drop the dot and show their age; the tab opens on Read here through `defaultValue`.
218
+ */
219
+ export const ReadTab: StoryFn<typeof meta> = () => (
220
+ <div className='flex h-[720px] justify-end'>
221
+ <NotificationCenter data-testid='notification-center'>
222
+ <NotificationCenterTrigger />
223
+ <NotificationCenterContent>
224
+ <NotificationCenterHeader>
225
+ <NotificationCenterTitle />
226
+ <NotificationCenterHeaderActions>
227
+ <NotificationCenterHeaderAction disabled>
228
+ Mark all as read
229
+ </NotificationCenterHeaderAction>
230
+ <NotificationCenterHeaderAction asChild aria-label='Notification settings'>
231
+ <a href='#notification-settings'>
232
+ <Settings />
233
+ </a>
234
+ </NotificationCenterHeaderAction>
235
+ </NotificationCenterHeaderActions>
236
+ </NotificationCenterHeader>
237
+ <NotificationCenterTabs defaultValue='read'>
238
+ <NotificationCenterTabsList>
239
+ <TabsTrigger value='new'>New</TabsTrigger>
240
+ <TabsTrigger value='read'>Read</TabsTrigger>
241
+ </NotificationCenterTabsList>
242
+ <TabsContent value='read'>
243
+ <NotificationCenterBody>
244
+ <NotificationCenterList>
245
+ <NotificationCenterItem>
246
+ <NotificationCenterItemTitle>
247
+ Apply a virtual patch to api.example.com?
248
+ </NotificationCenterItemTitle>
249
+ <NotificationCenterItemTime value={minutesAgo(60 * 24)} />
250
+ <NotificationCenterItemDescription>
251
+ Approved. The patch is live on all nodes.
252
+ </NotificationCenterItemDescription>
253
+ </NotificationCenterItem>
254
+ <NotificationCenterItem>
255
+ <NotificationCenterItemTitle>
256
+ New attack detected on api.example.com
257
+ </NotificationCenterItemTitle>
258
+ <NotificationCenterItemTime value={minutesAgo(60 * 24 * 2)} />
259
+ <NotificationCenterItemDescription>
260
+ 214 SQL injection attempts from 12 IPs in the last 10 minutes.
261
+ </NotificationCenterItemDescription>
262
+ </NotificationCenterItem>
263
+ <NotificationCenterItem>
264
+ <NotificationCenterItemTitle>
265
+ Node wallarm-node-3 is offline
266
+ </NotificationCenterItemTitle>
267
+ <NotificationCenterItemTime value={minutesAgo(60 * 24 * 3)} />
268
+ <NotificationCenterItemDescription>
269
+ No heartbeat for 15 minutes. Traffic fails over to wallarm-node-1.
270
+ </NotificationCenterItemDescription>
271
+ </NotificationCenterItem>
272
+ <NotificationCenterItem>
273
+ <NotificationCenterItemTitle>
274
+ Weekly API discovery report is ready
275
+ </NotificationCenterItemTitle>
276
+ <NotificationCenterItemTime value={minutesAgo(60 * 24 * 7)} />
277
+ <NotificationCenterItemDescription>
278
+ 38 new endpoints this week, 5 of them handling sensitive data.
279
+ </NotificationCenterItemDescription>
280
+ </NotificationCenterItem>
281
+ </NotificationCenterList>
282
+ </NotificationCenterBody>
283
+ </TabsContent>
284
+ </NotificationCenterTabs>
285
+ </NotificationCenterContent>
286
+ </NotificationCenter>
287
+ </div>
288
+ );
289
+
290
+ /**
291
+ * Each tab has its own empty state, built from `EmptyState` parts. "Mark all as read" is disabled
292
+ * by the app when nothing is unread.
293
+ */
294
+ export const Empty: StoryFn<typeof meta> = () => (
295
+ <div className='flex h-[400px] justify-end'>
296
+ <NotificationCenter data-testid='notification-center'>
297
+ <NotificationCenterTrigger />
298
+ <NotificationCenterContent>
299
+ <NotificationCenterHeader>
300
+ <NotificationCenterTitle />
301
+ <NotificationCenterHeaderActions>
302
+ <NotificationCenterHeaderAction disabled>
303
+ Mark all as read
304
+ </NotificationCenterHeaderAction>
305
+ <NotificationCenterHeaderAction asChild aria-label='Notification settings'>
306
+ <a href='#notification-settings'>
307
+ <Settings />
308
+ </a>
309
+ </NotificationCenterHeaderAction>
310
+ </NotificationCenterHeaderActions>
311
+ </NotificationCenterHeader>
312
+ <NotificationCenterTabs defaultValue='new'>
313
+ <NotificationCenterTabsList>
314
+ <TabsTrigger value='new'>New</TabsTrigger>
315
+ <TabsTrigger value='read'>Read</TabsTrigger>
316
+ </NotificationCenterTabsList>
317
+ <TabsContent value='new'>
318
+ <NotificationCenterBody>
319
+ <NotificationCenterEmpty>
320
+ <EmptyStateIllustration>
321
+ <CheckCheck size='lg' />
322
+ </EmptyStateIllustration>
323
+ <EmptyStateMessage>
324
+ <EmptyStateTitle>You're all caught up</EmptyStateTitle>
325
+ <EmptyStateDescription>No new notifications.</EmptyStateDescription>
326
+ </EmptyStateMessage>
327
+ </NotificationCenterEmpty>
328
+ </NotificationCenterBody>
329
+ </TabsContent>
330
+ <TabsContent value='read'>
331
+ <NotificationCenterBody>
332
+ <NotificationCenterEmpty>
333
+ <EmptyStateIllustration>
334
+ <History size='lg' />
335
+ </EmptyStateIllustration>
336
+ <EmptyStateMessage>
337
+ <EmptyStateTitle>Nothing read yet</EmptyStateTitle>
338
+ <EmptyStateDescription>
339
+ Notifications you've read will show up here.
340
+ </EmptyStateDescription>
341
+ </EmptyStateMessage>
342
+ </NotificationCenterEmpty>
343
+ </NotificationCenterBody>
344
+ </TabsContent>
345
+ </NotificationCenterTabs>
346
+ </NotificationCenterContent>
347
+ </NotificationCenter>
348
+ </div>
349
+ );
350
+
351
+ /**
352
+ * While the inbox loads, the body shows placeholder rows and the bell has no badge yet.
353
+ */
354
+ export const Loading: StoryFn<typeof meta> = () => (
355
+ <div className='flex h-[400px] justify-end'>
356
+ <NotificationCenter data-testid='notification-center'>
357
+ <NotificationCenterTrigger />
358
+ <NotificationCenterContent>
359
+ <NotificationCenterHeader>
360
+ <NotificationCenterTitle />
361
+ </NotificationCenterHeader>
362
+ <NotificationCenterBody>
363
+ <NotificationCenterSkeleton />
364
+ </NotificationCenterBody>
365
+ </NotificationCenterContent>
366
+ </NotificationCenter>
367
+ </div>
368
+ );
369
+
370
+ /**
371
+ * A failed load reuses `NotificationCenterEmpty` with a retry button. Tabs are optional: here the
372
+ * body sits straight under the header.
373
+ */
374
+ export const ErrorState: StoryFn<typeof meta> = () => (
375
+ <div className='flex h-[400px] justify-end'>
376
+ <NotificationCenter data-testid='notification-center'>
377
+ <NotificationCenterTrigger />
378
+ <NotificationCenterContent>
379
+ <NotificationCenterHeader>
380
+ <NotificationCenterTitle />
381
+ </NotificationCenterHeader>
382
+ <NotificationCenterBody>
383
+ <NotificationCenterEmpty>
384
+ <EmptyStateIllustration>
385
+ <TriangleAlert size='lg' />
386
+ </EmptyStateIllustration>
387
+ <EmptyStateMessage>
388
+ <EmptyStateTitle>Couldn't load notifications</EmptyStateTitle>
389
+ <EmptyStateDescription>
390
+ Something went wrong. Refresh the page or try again later.
391
+ </EmptyStateDescription>
392
+ </EmptyStateMessage>
393
+ <EmptyStateActions>
394
+ <Button variant='outline' color='neutral' data-analytics-id='notifications-retry'>
395
+ Try again
396
+ </Button>
397
+ </EmptyStateActions>
398
+ </NotificationCenterEmpty>
399
+ </NotificationCenterBody>
400
+ </NotificationCenterContent>
401
+ </NotificationCenter>
402
+ </div>
403
+ );
404
+
405
+ const MANY_NOTIFICATIONS = Array.from({ length: 24 }, (_, index) => ({
406
+ id: `node-${index}`,
407
+ title: `Node wallarm-node-${index + 1} is offline`,
408
+ description: 'No heartbeat for 15 minutes. Traffic fails over to the remaining nodes.',
409
+ createdAt: minutesAgo(index * 37 + 1),
410
+ }));
411
+
412
+ /**
413
+ * Past 640px the list scrolls inside the body while the header and tabs stay put.
414
+ */
415
+ export const ManyItems: StoryFn<typeof meta> = () => (
416
+ <div className='flex h-[720px] justify-end'>
417
+ <NotificationCenter data-testid='notification-center'>
418
+ <NotificationCenterTrigger count={MANY_NOTIFICATIONS.length} />
419
+ <NotificationCenterContent>
420
+ <NotificationCenterHeader>
421
+ <NotificationCenterTitle />
422
+ <NotificationCenterHeaderActions>
423
+ <NotificationCenterHeaderAction>Mark all as read</NotificationCenterHeaderAction>
424
+ </NotificationCenterHeaderActions>
425
+ </NotificationCenterHeader>
426
+ <NotificationCenterTabs defaultValue='new'>
427
+ <NotificationCenterTabsList>
428
+ <TabsTrigger value='new'>
429
+ New <NumericBadge size='small'>{MANY_NOTIFICATIONS.length}</NumericBadge>
430
+ </TabsTrigger>
431
+ <TabsTrigger value='read'>Read</TabsTrigger>
432
+ </NotificationCenterTabsList>
433
+ <TabsContent value='new'>
434
+ <NotificationCenterBody>
435
+ <NotificationCenterList>
436
+ {MANY_NOTIFICATIONS.map(notification => (
437
+ <NotificationCenterItem key={notification.id} unread>
438
+ <NotificationCenterItemTitle>{notification.title}</NotificationCenterItemTitle>
439
+ <NotificationCenterItemTime value={notification.createdAt} />
440
+ <NotificationCenterItemDescription>
441
+ {notification.description}
442
+ </NotificationCenterItemDescription>
443
+ </NotificationCenterItem>
444
+ ))}
445
+ </NotificationCenterList>
446
+ </NotificationCenterBody>
447
+ </TabsContent>
448
+ </NotificationCenterTabs>
449
+ </NotificationCenterContent>
450
+ </NotificationCenter>
451
+ </div>
452
+ );
453
+
454
+ /**
455
+ * Where it lives: the bell in the shell's top header, between the AI Assistant toggle and Quick
456
+ * help, opening right-aligned under the button.
457
+ */
458
+ export const InTopHeader: StoryFn<typeof meta> = () => (
459
+ <AppShell ambient={false} className='h-[720px]'>
460
+ <AppShellHeader>
461
+ <TopHeader>
462
+ <TopHeaderLogo href='/'>
463
+ <WallarmLogo />
464
+ </TopHeaderLogo>
465
+ <TopHeaderActions>
466
+ <Button variant='ghost' size='small' color='neutral'>
467
+ Tenant Name
468
+ </Button>
469
+ <TopHeaderSeparator />
470
+ <ChatTrigger />
471
+ <NotificationCenter data-testid='notification-center'>
472
+ <NotificationCenterTrigger count={2} />
473
+ <NotificationCenterContent>
474
+ <NotificationCenterHeader>
475
+ <NotificationCenterTitle />
476
+ <NotificationCenterHeaderActions>
477
+ <NotificationCenterHeaderAction>Mark all as read</NotificationCenterHeaderAction>
478
+ <NotificationCenterHeaderAction asChild aria-label='Notification settings'>
479
+ <a href='#notification-settings'>
480
+ <Settings />
481
+ </a>
482
+ </NotificationCenterHeaderAction>
483
+ </NotificationCenterHeaderActions>
484
+ </NotificationCenterHeader>
485
+ <NotificationCenterTabs defaultValue='new'>
486
+ <NotificationCenterTabsList>
487
+ <TabsTrigger value='new'>
488
+ New <NumericBadge size='small'>2</NumericBadge>
489
+ </TabsTrigger>
490
+ <TabsTrigger value='read'>Read</TabsTrigger>
491
+ </NotificationCenterTabsList>
492
+ <TabsContent value='new'>
493
+ <NotificationCenterBody>
494
+ <NotificationCenterList>
495
+ <NotificationCenterItem unread>
496
+ <NotificationCenterItemTitle>
497
+ New attack detected on api.example.com
498
+ </NotificationCenterItemTitle>
499
+ <NotificationCenterItemTime value={minutesAgo(3)} />
500
+ <NotificationCenterItemDescription>
501
+ 214 SQL injection attempts from 12 IPs in the last 10 minutes.
502
+ </NotificationCenterItemDescription>
503
+ <NotificationCenterItemActions>
504
+ <Button size='small'>View attacks</Button>
505
+ </NotificationCenterItemActions>
506
+ </NotificationCenterItem>
507
+ <NotificationCenterItem unread>
508
+ <NotificationCenterItemTitle>
509
+ Node wallarm-node-3 is offline
510
+ </NotificationCenterItemTitle>
511
+ <NotificationCenterItemTime value={minutesAgo(22)} />
512
+ <NotificationCenterItemDescription>
513
+ No heartbeat for 15 minutes. Traffic fails over to wallarm-node-1.
514
+ </NotificationCenterItemDescription>
515
+ </NotificationCenterItem>
516
+ </NotificationCenterList>
517
+ </NotificationCenterBody>
518
+ </TabsContent>
519
+ <TabsContent value='read'>
520
+ <NotificationCenterBody>
521
+ <NotificationCenterList>
522
+ <NotificationCenterItem>
523
+ <NotificationCenterItemTitle>
524
+ Weekly API discovery report is ready
525
+ </NotificationCenterItemTitle>
526
+ <NotificationCenterItemTime value={minutesAgo(60 * 24 * 7)} />
527
+ <NotificationCenterItemDescription>
528
+ 38 new endpoints this week, 5 of them handling sensitive data.
529
+ </NotificationCenterItemDescription>
530
+ </NotificationCenterItem>
531
+ </NotificationCenterList>
532
+ </NotificationCenterBody>
533
+ </TabsContent>
534
+ </NotificationCenterTabs>
535
+ </NotificationCenterContent>
536
+ </NotificationCenter>
537
+ <QuickHelpDropdown />
538
+ </TopHeaderActions>
539
+ </TopHeader>
540
+ </AppShellHeader>
541
+ </AppShell>
542
+ );
543
+
544
+ InTopHeader.parameters = { layout: 'fullscreen' };
545
+
546
+ /**
547
+ * Controlled open state: the app can close the inbox itself, for example after navigating.
548
+ */
549
+ export const Controlled: StoryFn<typeof meta> = () => {
550
+ const [open, setOpen] = useState(false);
551
+ const [unread, setUnread] = useState(true);
552
+
553
+ const handleMarkAllRead = () => setUnread(false);
554
+ const handleItemClick = () => setUnread(false);
555
+
556
+ return (
557
+ <div className='flex h-[400px] justify-end'>
558
+ <NotificationCenter data-testid='notification-center' open={open} onOpenChange={setOpen}>
559
+ <NotificationCenterTrigger count={unread ? 1 : 0} />
560
+ <NotificationCenterContent>
561
+ <NotificationCenterHeader>
562
+ <NotificationCenterTitle />
563
+ <NotificationCenterHeaderActions>
564
+ <NotificationCenterHeaderAction disabled={!unread} onClick={handleMarkAllRead}>
565
+ Mark all as read
566
+ </NotificationCenterHeaderAction>
567
+ </NotificationCenterHeaderActions>
568
+ </NotificationCenterHeader>
569
+ <NotificationCenterBody>
570
+ <NotificationCenterList>
571
+ <NotificationCenterItem unread={unread}>
572
+ <NotificationCenterItemTitle onClick={handleItemClick}>
573
+ Weekly API discovery report is ready
574
+ </NotificationCenterItemTitle>
575
+ <NotificationCenterItemTime value={minutesAgo(120)} />
576
+ <NotificationCenterItemDescription>
577
+ 38 new endpoints this week, 5 of them handling sensitive data.
578
+ </NotificationCenterItemDescription>
579
+ </NotificationCenterItem>
580
+ </NotificationCenterList>
581
+ </NotificationCenterBody>
582
+ </NotificationCenterContent>
583
+ </NotificationCenter>
584
+ </div>
585
+ );
586
+ };
587
+ ```
588
+
589
+ ### AppShell/story-content/index.ts
590
+
591
+ ```tsx
592
+ export { AccountDropdown, railModeFor, type SidebarMode } from './_storyAccountDropdown';
593
+ export { ConfigRemote, type ConfigRemoteProps, RemoteForProduct } from './_storyConfigRenderer';
594
+ export { HeaderActions } from './_storyHeaderActions';
595
+ export { HomeContent } from './_storyHomeContent';
596
+ export { deriveProduct, PRODUCT_CONFIGS, type Product, productLandingPath } from './_storyLib';
597
+ export {
598
+ aiHypervisorNavConfig,
599
+ edgeNavConfig,
600
+ infraDiscoveryNavConfig,
601
+ securityTestingNavConfig,
602
+ settingsNavConfig,
603
+ } from './_storyNavConfigs';
604
+ export { NavRailFooterContent } from './_storyNavRailFooter';
605
+ export { NotificationsInbox } from './_storyNotificationsInbox';
606
+ export { ProductNavItems } from './_storyProductNavItems';
607
+ export { QuickHelpDropdown } from './_storyQuickHelpDropdown';
608
+ export { RecentDropdown } from './_storyRecentDropdown';
609
+ export { StoryRouter, useStoryPathname, useStoryProductNavigation } from './_storyRouter';
610
+ export { WallarmLogo } from './_storyWallarmLogo';
611
+ ```
612
+
613
+ ### AppShell/story-content/_storyAccountDropdown.tsx
614
+
615
+ ```tsx
616
+ import { Fragment } from 'react';
617
+ import { LogOut, PanelLeftDashed, PencilRuler, Settings, UserRound } from '../../../icons';
618
+ import { cn } from '../../../utils/cn';
619
+ import {
620
+ Avatar,
621
+ AvatarContent,
622
+ AvatarDescription,
623
+ AvatarFallback,
624
+ AvatarImage,
625
+ AvatarName,
626
+ } from '../../Avatar';
627
+ import {
628
+ DropdownMenu,
629
+ DropdownMenuContent,
630
+ DropdownMenuItem,
631
+ DropdownMenuItemContent,
632
+ DropdownMenuItemIcon,
633
+ DropdownMenuItemText,
634
+ DropdownMenuRadioGroup,
635
+ DropdownMenuRadioItem,
636
+ DropdownMenuSeparator,
637
+ DropdownMenuTrigger,
638
+ DropdownMenuTriggerItem,
639
+ } from '../../DropdownMenu';
640
+ import { NavRailItem, type NavRailMode } from '../../NavRail';
641
+ import { Text } from '../../Text';
642
+ import { type FrameStyle, type Theme, useTheme } from '../../ThemeProvider';
643
+
644
+ export type SidebarMode = 'adaptive' | 'expanded' | 'collapsed' | 'compact';
645
+
646
+ /** Adaptive collapses the rail inside a product and expands it on Home; the other modes pin it. */
647
+ export const railModeFor = (mode: SidebarMode, onHome: boolean): NavRailMode => {
648
+ if (mode === 'adaptive') return onHome ? 'expanded' : 'collapsed';
649
+ return mode;
650
+ };
651
+
652
+ const USER_NAME = 'Meow Meow';
653
+ const USER_EMAIL = 'meow@meow.com';
654
+
655
+ const THEMES: Theme[] = ['light', 'dark'];
656
+ // Adaptive on its own, then the two pinned widths, then compact.
657
+ const SIDEBAR_MODE_GROUPS: SidebarMode[][] = [['adaptive'], ['expanded', 'collapsed'], ['compact']];
658
+ const SIDEBAR_MODES = SIDEBAR_MODE_GROUPS.flat();
659
+
660
+ const THEME_LABELS: Record<Theme, string> = {
661
+ light: 'Light',
662
+ dark: 'Dark',
663
+ };
664
+
665
+ const FRAME_STYLES: FrameStyle[] = ['neutral', 'branded'];
666
+
667
+ const FRAME_STYLE_LABELS: Record<FrameStyle, string> = {
668
+ neutral: 'Neutral',
669
+ branded: 'Branded',
670
+ };
671
+
672
+ // The swatch in front of each Frame style option, as in Figma.
673
+ const FRAME_STYLE_SWATCH: Record<FrameStyle, string> = {
674
+ neutral: 'bg-icon-secondary',
675
+ branded: 'bg-icon-brand',
676
+ };
677
+
678
+ const SIDEBAR_MODE_LABELS: Record<SidebarMode, string> = {
679
+ adaptive: 'Adaptive',
680
+ expanded: 'Always expanded',
681
+ collapsed: 'Always collapsed',
682
+ compact: 'Compact',
683
+ };
684
+
685
+ export const AccountDropdown = ({
686
+ sidebarMode,
687
+ onSidebarModeChange,
688
+ theme,
689
+ onThemeChange,
690
+ }: {
691
+ sidebarMode: SidebarMode;
692
+ onSidebarModeChange: (mode: SidebarMode) => void;
693
+ theme: Theme;
694
+ onThemeChange: (theme: Theme) => void;
695
+ }) => {
696
+ const { frameStyle, setFrameStyle } = useTheme();
697
+
698
+ return (
699
+ <DropdownMenu positioning={{ placement: 'right-end', gutter: 6, offset: { crossAxis: 12 } }}>
700
+ <DropdownMenuTrigger>
701
+ <NavRailItem icon={UserRound} label={USER_NAME} avatar />
702
+ </DropdownMenuTrigger>
703
+ <DropdownMenuContent className='w-256'>
704
+ <div className='rounded-6 border border-border-primary-light bg-states-primary-hover px-8 py-8 mb-4'>
705
+ <Avatar size='md'>
706
+ <AvatarImage />
707
+ <AvatarFallback name={USER_NAME} />
708
+ <AvatarContent>
709
+ <AvatarName>{USER_NAME}</AvatarName>
710
+ <AvatarDescription>{USER_EMAIL}</AvatarDescription>
711
+ </AvatarContent>
712
+ </Avatar>
713
+ </div>
714
+ <DropdownMenuItem>
715
+ <DropdownMenuItemIcon>
716
+ <Settings />
717
+ </DropdownMenuItemIcon>
718
+ <DropdownMenuItemText>Profile settings</DropdownMenuItemText>
719
+ </DropdownMenuItem>
720
+ <DropdownMenu>
721
+ <DropdownMenuTriggerItem>
722
+ <DropdownMenuItemIcon>
723
+ <PencilRuler />
724
+ </DropdownMenuItemIcon>
725
+ <DropdownMenuItemContent>
726
+ <DropdownMenuItemText>Appearance</DropdownMenuItemText>
727
+ <Text size='xs' color='secondary'>
728
+ {THEME_LABELS[theme]} and {FRAME_STYLE_LABELS[frameStyle].toLowerCase()}
729
+ </Text>
730
+ </DropdownMenuItemContent>
731
+ </DropdownMenuTriggerItem>
732
+ <DropdownMenuContent className='w-132'>
733
+ <DropdownMenuRadioGroup
734
+ value={theme}
735
+ onValueChange={({ value }) => {
736
+ const next = THEMES.find(t => t === value);
737
+ if (next) onThemeChange(next);
738
+ }}
739
+ >
740
+ {THEMES.map(t => (
741
+ <DropdownMenuRadioItem key={t} value={t}>
742
+ <DropdownMenuItemText>{THEME_LABELS[t]}</DropdownMenuItemText>
743
+ </DropdownMenuRadioItem>
744
+ ))}
745
+ </DropdownMenuRadioGroup>
746
+ <DropdownMenuSeparator />
747
+ <DropdownMenuRadioGroup
748
+ value={frameStyle}
749
+ onValueChange={({ value }) => {
750
+ const next = FRAME_STYLES.find(s => s === value);
751
+ if (next) setFrameStyle(next);
752
+ }}
753
+ >
754
+ {FRAME_STYLES.map(s => (
755
+ <DropdownMenuRadioItem key={s} value={s}>
756
+ <DropdownMenuItemIcon>
757
+ <span className='flex size-16 items-center justify-center'>
758
+ <span className={cn('size-8 rounded-full', FRAME_STYLE_SWATCH[s])} />
759
+ </span>
760
+ </DropdownMenuItemIcon>
761
+ <DropdownMenuItemText>{FRAME_STYLE_LABELS[s]}</DropdownMenuItemText>
762
+ </DropdownMenuRadioItem>
763
+ ))}
764
+ </DropdownMenuRadioGroup>
765
+ </DropdownMenuContent>
766
+ </DropdownMenu>
767
+ <DropdownMenu>
768
+ <DropdownMenuTriggerItem>
769
+ <DropdownMenuItemIcon>
770
+ <PanelLeftDashed />
771
+ </DropdownMenuItemIcon>
772
+ <DropdownMenuItemContent>
773
+ <DropdownMenuItemText>Sidebar mode</DropdownMenuItemText>
774
+ <Text size='xs' color='secondary'>
775
+ {SIDEBAR_MODE_LABELS[sidebarMode]}
776
+ </Text>
777
+ </DropdownMenuItemContent>
778
+ </DropdownMenuTriggerItem>
779
+ <DropdownMenuContent className='w-200'>
780
+ <DropdownMenuRadioGroup
781
+ value={sidebarMode}
782
+ onValueChange={({ value }) => {
783
+ const next = SIDEBAR_MODES.find(m => m === value);
784
+ if (next) onSidebarModeChange(next);
785
+ }}
786
+ >
787
+ {SIDEBAR_MODE_GROUPS.map((group, index) => (
788
+ <Fragment key={group.join('-')}>
789
+ {index > 0 && <DropdownMenuSeparator />}
790
+ {group.map(m => (
791
+ <DropdownMenuRadioItem key={m} value={m}>
792
+ <DropdownMenuItemText>{SIDEBAR_MODE_LABELS[m]}</DropdownMenuItemText>
793
+ </DropdownMenuRadioItem>
794
+ ))}
795
+ </Fragment>
796
+ ))}
797
+ </DropdownMenuRadioGroup>
798
+ </DropdownMenuContent>
799
+ </DropdownMenu>
800
+ <DropdownMenuSeparator />
801
+ <DropdownMenuItem>
802
+ <DropdownMenuItemIcon>
803
+ <LogOut />
804
+ </DropdownMenuItemIcon>
805
+ <DropdownMenuItemText>Sign out</DropdownMenuItemText>
806
+ </DropdownMenuItem>
807
+ </DropdownMenuContent>
808
+ </DropdownMenu>
809
+ );
810
+ };
811
+ ```
812
+
813
+ ### AppShell/story-content/_storyConfigRenderer.tsx
814
+
815
+ ```tsx
816
+ import { type FC, type ReactNode, useEffect, useState } from 'react';
817
+ import { useNavigate } from '@tanstack/react-router';
818
+ import { Page, PageContent, PageHeader, PageTitle } from '../../Page';
819
+ import {
820
+ type NavConfig,
821
+ RemoteShell,
822
+ RemoteShellBreadcrumb,
823
+ RemoteShellContent,
824
+ RemoteShellPanel,
825
+ useRemoteShellContext,
826
+ } from '../../RemoteShell';
827
+ import { HomeContent } from './_storyHomeContent';
828
+ import { PRODUCT_CONFIGS, type Product } from './_storyLib';
829
+ import { useStoryPathname } from './_storyRouter';
830
+
831
+ export interface ConfigRemoteProps {
832
+ config: NavConfig;
833
+ basePath?: string;
834
+ /** Fake a 2s load when the product opens, to show the panel and content skeletons. */
835
+ simulateLoading?: boolean;
836
+ /** Page content; defaults to a placeholder page titled after the breadcrumbs. */
837
+ children?: ReactNode;
838
+ }
839
+
840
+ const RemotePageContent: FC = () => {
841
+ const { breadcrumbSegments } = useRemoteShellContext();
842
+
843
+ const lastSegment = breadcrumbSegments[breadcrumbSegments.length - 1];
844
+ const pageTitle = lastSegment?.label ?? '';
845
+ const fullPath = breadcrumbSegments.map(s => s.label).join(' / ');
846
+
847
+ return (
848
+ <Page title={pageTitle} fixedHeight>
849
+ <PageHeader>
850
+ <PageTitle>{pageTitle}</PageTitle>
851
+ </PageHeader>
852
+ <PageContent>
853
+ <p className='text-sm text-text-secondary'>Placeholder page for {fullPath}.</p>
854
+ </PageContent>
855
+ </Page>
856
+ );
857
+ };
858
+
859
+ /**
860
+ * A product remote wired to TanStack Router the recommended way: the router owns the
861
+ * URL, `RemoteShell` gets the router's pathname (`pathname`) and hands navigations
862
+ * back to it (`onNavigate`). Must render inside a router (`StoryRouter`).
863
+ */
864
+ export const ConfigRemote: FC<ConfigRemoteProps> = ({
865
+ config,
866
+ basePath,
867
+ simulateLoading = true,
868
+ children,
869
+ }) => {
870
+ const navigate = useNavigate();
871
+ const pathname = useStoryPathname();
872
+ const [loading, setLoading] = useState(simulateLoading);
873
+
874
+ // Remounted per product (see `RemoteForProduct`), so this runs once per product.
875
+ useEffect(() => {
876
+ if (!simulateLoading) return;
877
+ const timer = setTimeout(() => setLoading(false), 2000);
878
+ return () => clearTimeout(timer);
879
+ }, [simulateLoading]);
880
+
881
+ return (
882
+ <RemoteShell
883
+ config={config}
884
+ basePath={basePath}
885
+ pathname={pathname}
886
+ onNavigate={to => navigate({ to })}
887
+ >
888
+ {loading ? (
889
+ <>
890
+ <RemoteShellPanel isLoading />
891
+ <RemoteShellContent isLoading />
892
+ </>
893
+ ) : (
894
+ <>
895
+ <RemoteShellPanel resizable />
896
+ <RemoteShellBreadcrumb />
897
+ <RemoteShellContent>{children ?? <RemotePageContent />}</RemoteShellContent>
898
+ </>
899
+ )}
900
+ </RemoteShell>
901
+ );
902
+ };
903
+
904
+ export const RemoteForProduct = ({
905
+ product,
906
+ simulateLoading = true,
907
+ }: {
908
+ product: Product;
909
+ simulateLoading?: boolean;
910
+ }) => {
911
+ if (product === 'home') return <HomeContent />;
912
+ const { config } = PRODUCT_CONFIGS[product];
913
+ // Keyed by product: each product is its own remote, so its drill-transition and
914
+ // loading state never carry over to the next one.
915
+ return (
916
+ <ConfigRemote
917
+ key={product}
918
+ config={config}
919
+ basePath={`/${product}`}
920
+ simulateLoading={simulateLoading}
921
+ />
922
+ );
923
+ };
924
+ ```
925
+
926
+ ### AppShell/story-content/_storyHeaderActions.tsx
927
+
928
+ ```tsx
929
+ import type { ReactNode } from 'react';
930
+ import { ChevronUpDown } from '../../../icons';
931
+ import { Button } from '../../Button';
932
+ import { Code } from '../../Code';
933
+ import { Text } from '../../Text';
934
+ import { TopHeaderSeparator } from '../../TopHeader';
935
+ import { NotificationsInbox } from './_storyNotificationsInbox';
936
+ import { QuickHelpDropdown } from './_storyQuickHelpDropdown';
937
+ import { StorySearchModal } from './_storySearchModal';
938
+
939
+ interface HeaderActionsProps {
940
+ /** Rendered before the notifications bell — where the host puts its AI Assistant toggle. */
941
+ assistantToggle?: ReactNode;
942
+ }
943
+
944
+ export const HeaderActions = ({ assistantToggle }: HeaderActionsProps) => (
945
+ <>
946
+ <StorySearchModal />
947
+
948
+ <TopHeaderSeparator />
949
+
950
+ <Button variant='ghost' size='small' color='neutral' className='py-4 rounded-6'>
951
+ <Text size='xs' weight='medium'>
952
+ Tenant Name
953
+ </Text>
954
+ <span className='text-text-tertiary mx-[-2px]'>•</span>
955
+ <Code size='s' color='secondary'>
956
+ 12345
957
+ </Code>
958
+ <ChevronUpDown className='!icon-sm' />
959
+ </Button>
960
+
961
+ {assistantToggle}
962
+
963
+ <NotificationsInbox />
964
+
965
+ <QuickHelpDropdown />
966
+ </>
967
+ );
968
+ ```
969
+
970
+ ### AppShell/story-content/_storyHomeContent.tsx
971
+
972
+ ```tsx
973
+ import type { FC } from 'react';
974
+
975
+ export const HomeContent: FC = () => (
976
+ <div className='p-24'>
977
+ <h1 className='text-xl font-semibold text-text-primary'>Home</h1>
978
+ <p className='mt-4 text-sm text-text-secondary'>
979
+ Cross-product summary. Click any product in the rail to drill into it.
980
+ </p>
981
+ </div>
982
+ );
983
+ ```
984
+
985
+ ### AppShell/story-content/_storyLib.ts
986
+
987
+ ```tsx
988
+ import { findFirstLinkPath } from '../../RemoteShell';
989
+ import {
990
+ aiHypervisorNavConfig,
991
+ edgeNavConfig,
992
+ infraDiscoveryNavConfig,
993
+ securityTestingNavConfig,
994
+ settingsNavConfig,
995
+ } from './_storyNavConfigs';
996
+
997
+ const KNOWN_PRODUCTS = [
998
+ 'home',
999
+ 'edge',
1000
+ 'ai-hypervisor',
1001
+ 'infra-discovery',
1002
+ 'security-testing',
1003
+ 'settings',
1004
+ ] as const;
1005
+
1006
+ export type Product = (typeof KNOWN_PRODUCTS)[number];
1007
+
1008
+ export const PRODUCT_CONFIGS: Record<Exclude<Product, 'home'>, { config: typeof edgeNavConfig }> = {
1009
+ edge: { config: edgeNavConfig },
1010
+ 'ai-hypervisor': { config: aiHypervisorNavConfig },
1011
+ 'infra-discovery': { config: infraDiscoveryNavConfig },
1012
+ 'security-testing': { config: securityTestingNavConfig },
1013
+ settings: { config: settingsNavConfig },
1014
+ };
1015
+
1016
+ export function deriveProduct(pathname: string): Product {
1017
+ const segment = pathname.split('/').filter(Boolean)[0];
1018
+ if (segment && (KNOWN_PRODUCTS as readonly string[]).includes(segment)) {
1019
+ return segment as Product;
1020
+ }
1021
+ return 'home';
1022
+ }
1023
+
1024
+ /** Landing URL of a product: its first link, or `/home`. */
1025
+ export function productLandingPath(product: Product): string {
1026
+ if (product === 'home') return '/home';
1027
+ const { config } = PRODUCT_CONFIGS[product];
1028
+ const firstPath = findFirstLinkPath(config.items) ?? '';
1029
+ return `/${product}/${firstPath}`;
1030
+ }
1031
+ ```
1032
+
1033
+ ### AppShell/story-content/_storyNavConfigs.ts
1034
+
1035
+ ```tsx
1036
+ import { CircleDashed, Filter, Plus } from '../../../icons';
1037
+ import type { NavConfig } from '../../RemoteShell';
1038
+
1039
+ export const edgeNavConfig: NavConfig = {
1040
+ productLabel: 'Edge',
1041
+ productPath: '/edge',
1042
+ headerActions: [
1043
+ { icon: Filter, label: 'Filter', onClick: () => alert('Filter clicked') },
1044
+ { icon: Plus, label: 'Add', onClick: () => alert('Add clicked') },
1045
+ ],
1046
+ items: [
1047
+ { type: 'link', id: 'overview', label: 'Overview', path: 'overview', icon: CircleDashed },
1048
+ {
1049
+ type: 'drill',
1050
+ id: 'data-planes',
1051
+ label: 'Data planes',
1052
+ path: 'data-planes',
1053
+ icon: CircleDashed,
1054
+ param: 'dataPlaneId',
1055
+ entities: [
1056
+ { id: 'production', label: 'Production', description: 'Main production environment' },
1057
+ { id: 'staging', label: 'Staging', description: 'Pre-production testing' },
1058
+ { id: 'edge-eu', label: 'Edge EU', description: 'European edge nodes' },
1059
+ { id: 'edge-us', label: 'Edge US', description: 'US edge nodes' },
1060
+ ],
1061
+ children: [
1062
+ {
1063
+ type: 'link',
1064
+ id: 'dp-overview',
1065
+ label: 'Overview',
1066
+ path: 'overview',
1067
+ icon: CircleDashed,
1068
+ },
1069
+ {
1070
+ type: 'drill',
1071
+ id: 'dp-nodes',
1072
+ label: 'Nodes',
1073
+ path: 'nodes',
1074
+ icon: CircleDashed,
1075
+ param: 'nodeId',
1076
+ entities: [
1077
+ { id: 'node-1', label: 'Node 1', description: 'Primary node' },
1078
+ { id: 'node-2', label: 'Node 2', description: 'Secondary node' },
1079
+ { id: 'node-3', label: 'Node 3', description: 'Tertiary node' },
1080
+ ],
1081
+ children: [
1082
+ {
1083
+ type: 'link',
1084
+ id: 'node-overview',
1085
+ label: 'Overview',
1086
+ path: 'overview',
1087
+ icon: CircleDashed,
1088
+ },
1089
+ {
1090
+ type: 'link',
1091
+ id: 'node-metrics',
1092
+ label: 'Metrics',
1093
+ path: 'metrics',
1094
+ icon: CircleDashed,
1095
+ },
1096
+ { type: 'link', id: 'node-logs', label: 'Logs', path: 'logs', icon: CircleDashed },
1097
+ {
1098
+ type: 'link',
1099
+ id: 'node-config',
1100
+ label: 'Configuration',
1101
+ path: 'config',
1102
+ icon: CircleDashed,
1103
+ },
1104
+ ],
1105
+ },
1106
+ {
1107
+ type: 'link',
1108
+ id: 'dp-services',
1109
+ label: 'Services',
1110
+ path: 'services',
1111
+ icon: CircleDashed,
1112
+ dividerAfter: true,
1113
+ },
1114
+ { type: 'link', id: 'dp-govern', label: 'Govern', path: 'govern', icon: CircleDashed },
1115
+ {
1116
+ type: 'group',
1117
+ id: 'dp-operations',
1118
+ label: 'Operations',
1119
+ icon: CircleDashed,
1120
+ children: [
1121
+ {
1122
+ type: 'link',
1123
+ id: 'dp-routing-rules',
1124
+ label: 'Routing rules',
1125
+ path: 'routing-rules',
1126
+ },
1127
+ { type: 'link', id: 'dp-settings', label: 'Settings', path: 'settings' },
1128
+ ],
1129
+ },
1130
+ ],
1131
+ },
1132
+ {
1133
+ type: 'link',
1134
+ id: 'dashboards',
1135
+ label: 'Dashboards',
1136
+ path: 'dashboards',
1137
+ icon: CircleDashed,
1138
+ dividerAfter: true,
1139
+ },
1140
+ { type: 'section-header', id: 'section-detect', label: 'Detect & Respond' },
1141
+ {
1142
+ type: 'group',
1143
+ id: 'events',
1144
+ label: 'Events',
1145
+ icon: CircleDashed,
1146
+ defaultExpanded: true,
1147
+ children: [
1148
+ { type: 'link', id: 'attacks', label: 'Attacks', path: 'attacks' },
1149
+ { type: 'link', id: 'incidents', label: 'Incidents', path: 'incidents' },
1150
+ { type: 'link', id: 'security-issues', label: 'Security Issues', path: 'security-issues' },
1151
+ { type: 'link', id: 'api-sessions', label: 'API Sessions', path: 'api-sessions' },
1152
+ ],
1153
+ },
1154
+ {
1155
+ type: 'group',
1156
+ id: 'api-security',
1157
+ label: 'API Security',
1158
+ icon: CircleDashed,
1159
+ children: [
1160
+ {
1161
+ type: 'link',
1162
+ id: 'api-attack-surface',
1163
+ label: 'API Attack Surface',
1164
+ path: 'api-attack-surface',
1165
+ },
1166
+ { type: 'link', id: 'api-discovery', label: 'API Discovery', path: 'api-discovery' },
1167
+ {
1168
+ type: 'link',
1169
+ id: 'api-abuse-prevention',
1170
+ label: 'API Abuse Prevention',
1171
+ path: 'api-abuse-prevention',
1172
+ },
1173
+ {
1174
+ type: 'link',
1175
+ id: 'api-specifications',
1176
+ label: 'API Specifications',
1177
+ path: 'api-specifications',
1178
+ },
1179
+ ],
1180
+ },
1181
+ {
1182
+ type: 'group',
1183
+ id: 'security-controls',
1184
+ label: 'Security controls',
1185
+ icon: CircleDashed,
1186
+ children: [
1187
+ {
1188
+ type: 'link',
1189
+ id: 'ip-session-lists',
1190
+ label: 'IP & Session Lists',
1191
+ path: 'ip-session-lists',
1192
+ },
1193
+ { type: 'link', id: 'triggers', label: 'Triggers', path: 'triggers' },
1194
+ { type: 'link', id: 'rules', label: 'Rules', path: 'rules' },
1195
+ {
1196
+ type: 'link',
1197
+ id: 'mitigation-controls',
1198
+ label: 'Mitigation Controls',
1199
+ path: 'mitigation-controls',
1200
+ },
1201
+ {
1202
+ type: 'link',
1203
+ id: 'credential-stuffing',
1204
+ label: 'Credential Stuffing',
1205
+ path: 'credential-stuffing',
1206
+ },
1207
+ ],
1208
+ },
1209
+ { type: 'section-header', id: 'section-protect', label: 'Protect' },
1210
+ {
1211
+ type: 'group',
1212
+ id: 'security-testing',
1213
+ label: 'Security Testing',
1214
+ icon: CircleDashed,
1215
+ children: [
1216
+ { type: 'link', id: 'threat-replay', label: 'Threat Replay', path: 'threat-replay' },
1217
+ { type: 'link', id: 'schema-based', label: 'Schema-Based', path: 'schema-based' },
1218
+ ],
1219
+ },
1220
+ {
1221
+ type: 'group',
1222
+ id: 'configuration',
1223
+ label: 'Configuration',
1224
+ icon: CircleDashed,
1225
+ children: [
1226
+ {
1227
+ type: 'group',
1228
+ id: 'nodes',
1229
+ label: 'Nodes',
1230
+ children: [
1231
+ {
1232
+ type: 'link',
1233
+ id: 'nodes-overview',
1234
+ label: 'Overview',
1235
+ path: 'nodes-overview',
1236
+ },
1237
+ { type: 'link', id: 'nodes-pools', label: 'Pools', path: 'nodes-pools' },
1238
+ {
1239
+ type: 'link',
1240
+ id: 'nodes-health-checks',
1241
+ label: 'Health checks',
1242
+ path: 'nodes-health-checks',
1243
+ },
1244
+ ],
1245
+ },
1246
+ { type: 'link', id: 'security-edge', label: 'Security Edge', path: 'security-edge' },
1247
+ { type: 'link', id: 'integrations', label: 'Integrations', path: 'integrations' },
1248
+ ],
1249
+ },
1250
+ ],
1251
+ };
1252
+
1253
+ export const aiHypervisorNavConfig: NavConfig = {
1254
+ productLabel: 'AI Hypervisor',
1255
+ productPath: '/ai-hypervisor',
1256
+ items: [
1257
+ { type: 'link', id: 'heatmap', label: 'Heatmap', path: 'heatmap', icon: CircleDashed },
1258
+ { type: 'link', id: 'registry', label: 'Registry', path: 'registry', icon: CircleDashed },
1259
+ {
1260
+ type: 'group',
1261
+ id: 'topology',
1262
+ label: 'Topology',
1263
+ icon: CircleDashed,
1264
+ defaultExpanded: true,
1265
+ children: [
1266
+ {
1267
+ type: 'link',
1268
+ id: 'topology-overview',
1269
+ label: 'Overview',
1270
+ path: 'topology-overview',
1271
+ },
1272
+ {
1273
+ type: 'link',
1274
+ id: 'topology-services',
1275
+ label: 'Services',
1276
+ path: 'topology-services',
1277
+ },
1278
+ {
1279
+ type: 'group',
1280
+ id: 'graphs',
1281
+ label: 'Graphs',
1282
+ children: [
1283
+ { type: 'link', id: 'flow-graph', label: 'Flow graph', path: 'flow-graph' },
1284
+ {
1285
+ type: 'link',
1286
+ id: 'dependency-graph',
1287
+ label: 'Dependency graph',
1288
+ path: 'dependency-graph',
1289
+ },
1290
+ {
1291
+ type: 'link',
1292
+ id: 'traffic-graph',
1293
+ label: 'Traffic graph',
1294
+ path: 'traffic-graph',
1295
+ },
1296
+ ],
1297
+ },
1298
+ ],
1299
+ },
1300
+ {
1301
+ type: 'link',
1302
+ id: 'data-tracks',
1303
+ label: 'Data Tracks',
1304
+ path: 'data-tracks',
1305
+ icon: CircleDashed,
1306
+ },
1307
+ {
1308
+ type: 'link',
1309
+ id: 'user-tracks',
1310
+ label: 'User Tracks',
1311
+ path: 'user-tracks',
1312
+ icon: CircleDashed,
1313
+ },
1314
+ {
1315
+ type: 'link',
1316
+ id: 'supply-chain',
1317
+ label: 'Supply Chain',
1318
+ path: 'supply-chain',
1319
+ icon: CircleDashed,
1320
+ },
1321
+ {
1322
+ type: 'link',
1323
+ id: 'enforcement',
1324
+ label: 'Enforcement',
1325
+ path: 'enforcement',
1326
+ icon: CircleDashed,
1327
+ },
1328
+ {
1329
+ type: 'link',
1330
+ id: 'ai-integrations',
1331
+ label: 'Integrations',
1332
+ path: 'ai-integrations',
1333
+ icon: CircleDashed,
1334
+ },
1335
+ { type: 'link', id: 'red-team', label: 'Red Team', path: 'red-team', icon: CircleDashed },
1336
+ { type: 'link', id: 'debugger', label: 'Debugger', path: 'debugger', icon: CircleDashed },
1337
+ ],
1338
+ };
1339
+
1340
+ export const infraDiscoveryNavConfig: NavConfig = {
1341
+ productLabel: 'Infra Discovery',
1342
+ productPath: '/infra-discovery',
1343
+ headerActions: [{ icon: Filter, label: 'Filter', onClick: () => alert('Filter clicked') }],
1344
+ items: [
1345
+ { type: 'link', id: 'overview', label: 'Overview', path: 'overview', icon: CircleDashed },
1346
+ {
1347
+ type: 'group',
1348
+ id: 'inventory',
1349
+ label: 'Inventory',
1350
+ icon: CircleDashed,
1351
+ defaultExpanded: true,
1352
+ children: [
1353
+ { type: 'link', id: 'all-assets', label: 'All assets', path: 'all-assets' },
1354
+ { type: 'link', id: 'untagged', label: 'Untagged', path: 'untagged' },
1355
+ {
1356
+ type: 'group',
1357
+ id: 'by-source',
1358
+ label: 'By source',
1359
+ children: [
1360
+ {
1361
+ type: 'link',
1362
+ id: 'cloud-accounts',
1363
+ label: 'Cloud accounts',
1364
+ path: 'cloud-accounts',
1365
+ },
1366
+ { type: 'link', id: 'kubernetes', label: 'Kubernetes', path: 'kubernetes' },
1367
+ {
1368
+ type: 'link',
1369
+ id: 'source-control',
1370
+ label: 'Source control',
1371
+ path: 'source-control',
1372
+ },
1373
+ ],
1374
+ },
1375
+ ],
1376
+ },
1377
+ { type: 'link', id: 'topology', label: 'Topology', path: 'topology', icon: CircleDashed },
1378
+ { type: 'link', id: 'findings', label: 'Findings', path: 'findings', icon: CircleDashed },
1379
+ { type: 'link', id: 'risks', label: 'Risks', path: 'risks', icon: CircleDashed },
1380
+ {
1381
+ type: 'link',
1382
+ id: 'infra-integrations',
1383
+ label: 'Integrations',
1384
+ path: 'infra-integrations',
1385
+ icon: CircleDashed,
1386
+ },
1387
+ {
1388
+ type: 'link',
1389
+ id: 'infra-settings',
1390
+ label: 'Settings',
1391
+ path: 'infra-settings',
1392
+ icon: CircleDashed,
1393
+ },
1394
+ ],
1395
+ };
1396
+
1397
+ export const securityTestingNavConfig: NavConfig = {
1398
+ productLabel: 'Security Testing',
1399
+ productPath: '/security-testing',
1400
+ items: [
1401
+ { type: 'link', id: 'overview', label: 'Overview', path: 'overview', icon: CircleDashed },
1402
+ {
1403
+ type: 'link',
1404
+ id: 'test-suites',
1405
+ label: 'Test Suites',
1406
+ path: 'test-suites',
1407
+ icon: CircleDashed,
1408
+ },
1409
+ {
1410
+ type: 'group',
1411
+ id: 'templates',
1412
+ label: 'Templates',
1413
+ icon: CircleDashed,
1414
+ children: [
1415
+ { type: 'link', id: 'owasp', label: 'OWASP', path: 'owasp' },
1416
+ { type: 'link', id: 'pci-dss', label: 'PCI DSS', path: 'pci-dss' },
1417
+ { type: 'link', id: 'internal', label: 'Internal', path: 'internal' },
1418
+ ],
1419
+ },
1420
+ { type: 'link', id: 'schedules', label: 'Schedules', path: 'schedules', icon: CircleDashed },
1421
+ { type: 'link', id: 'results', label: 'Results', path: 'results', icon: CircleDashed },
1422
+ { type: 'link', id: 'coverage', label: 'Coverage', path: 'coverage', icon: CircleDashed },
1423
+ {
1424
+ type: 'link',
1425
+ id: 'testing-settings',
1426
+ label: 'Settings',
1427
+ path: 'testing-settings',
1428
+ icon: CircleDashed,
1429
+ },
1430
+ ],
1431
+ };
1432
+
1433
+ export const settingsNavConfig: NavConfig = {
1434
+ productLabel: 'Settings',
1435
+ productPath: '/settings',
1436
+ items: [
1437
+ { type: 'link', id: 'profile', label: 'Profile', path: 'profile', icon: CircleDashed },
1438
+ { type: 'link', id: 'general', label: 'General', path: 'general', icon: CircleDashed },
1439
+ {
1440
+ type: 'link',
1441
+ id: 'subscriptions',
1442
+ label: 'Subscriptions',
1443
+ path: 'subscriptions',
1444
+ icon: CircleDashed,
1445
+ },
1446
+ {
1447
+ type: 'link',
1448
+ id: 'applications',
1449
+ label: 'Applications',
1450
+ path: 'applications',
1451
+ icon: CircleDashed,
1452
+ },
1453
+ {
1454
+ type: 'group',
1455
+ id: 'users',
1456
+ label: 'Users',
1457
+ icon: CircleDashed,
1458
+ defaultExpanded: true,
1459
+ children: [
1460
+ { type: 'link', id: 'users-all', label: 'All users', path: 'users-all' },
1461
+ {
1462
+ type: 'link',
1463
+ id: 'users-invites',
1464
+ label: 'Pending invites',
1465
+ path: 'users-invites',
1466
+ },
1467
+ { type: 'link', id: 'users-roles', label: 'Roles', path: 'users-roles' },
1468
+ ],
1469
+ },
1470
+ { type: 'section-header', id: 'section-access', label: 'Access Management' },
1471
+ { type: 'link', id: 'groups', label: 'Groups', path: 'groups', icon: CircleDashed },
1472
+ { type: 'link', id: 'api-tokens', label: 'API Tokens', path: 'api-tokens', icon: CircleDashed },
1473
+ {
1474
+ type: 'link',
1475
+ id: 'activity-log',
1476
+ label: 'Activity Log',
1477
+ path: 'activity-log',
1478
+ icon: CircleDashed,
1479
+ dividerAfter: true,
1480
+ },
1481
+ {
1482
+ type: 'group',
1483
+ id: 'admin-zone',
1484
+ label: 'Admin Zone',
1485
+ icon: CircleDashed,
1486
+ defaultExpanded: true,
1487
+ children: [
1488
+ {
1489
+ type: 'link',
1490
+ id: 'customer-settings',
1491
+ label: 'Customer Settings',
1492
+ path: 'customer-settings',
1493
+ },
1494
+ {
1495
+ type: 'group',
1496
+ id: 'system-configuration',
1497
+ label: 'System Configuration',
1498
+ children: [
1499
+ { type: 'link', id: 'sys-general', label: 'General', path: 'sys-general' },
1500
+ { type: 'link', id: 'sys-storage', label: 'Storage', path: 'sys-storage' },
1501
+ {
1502
+ type: 'link',
1503
+ id: 'sys-networking',
1504
+ label: 'Networking',
1505
+ path: 'sys-networking',
1506
+ },
1507
+ ],
1508
+ },
1509
+ {
1510
+ type: 'link',
1511
+ id: 'bola-triggers',
1512
+ label: 'BOLA Triggers',
1513
+ path: 'bola-triggers',
1514
+ },
1515
+ { type: 'link', id: 'experiments', label: 'Experiments', path: 'experiments' },
1516
+ ],
1517
+ },
1518
+ ],
1519
+ };
1520
+ ```
1521
+
1522
+ ### AppShell/story-content/_storyNavRailFooter.tsx
1523
+
1524
+ ```tsx
1525
+ import type { FC } from 'react';
1526
+ import { Settings } from '../../../icons';
1527
+ import { NavRailFooter, NavRailItem } from '../../NavRail';
1528
+ import type { Theme } from '../../ThemeProvider';
1529
+ import { AccountDropdown, type SidebarMode } from './_storyAccountDropdown';
1530
+ import type { Product } from './_storyLib';
1531
+
1532
+ interface NavRailFooterContentProps {
1533
+ activeProduct: Product;
1534
+ onSelectProduct: (product: Product) => void;
1535
+ sidebarMode: SidebarMode;
1536
+ onSidebarModeChange: (mode: SidebarMode) => void;
1537
+ theme: Theme;
1538
+ onThemeChange: (theme: Theme) => void;
1539
+ }
1540
+
1541
+ export const NavRailFooterContent: FC<NavRailFooterContentProps> = ({
1542
+ activeProduct,
1543
+ onSelectProduct,
1544
+ sidebarMode,
1545
+ onSidebarModeChange,
1546
+ theme,
1547
+ onThemeChange,
1548
+ }) => (
1549
+ <NavRailFooter>
1550
+ <NavRailItem
1551
+ icon={Settings}
1552
+ label='Settings'
1553
+ shortcut={['G', 'S']}
1554
+ active={activeProduct === 'settings'}
1555
+ onClick={() => onSelectProduct('settings')}
1556
+ />
1557
+ <AccountDropdown
1558
+ sidebarMode={sidebarMode}
1559
+ onSidebarModeChange={onSidebarModeChange}
1560
+ theme={theme}
1561
+ onThemeChange={onThemeChange}
1562
+ />
1563
+ </NavRailFooter>
1564
+ );
1565
+ ```
1566
+
1567
+ ### AppShell/story-content/_storyNotificationsInbox.tsx
1568
+
1569
+ ```tsx
1570
+ import { Settings } from '../../../icons';
1571
+ import {
1572
+ NotificationCenter,
1573
+ NotificationCenterBody,
1574
+ NotificationCenterContent,
1575
+ NotificationCenterHeader,
1576
+ NotificationCenterHeaderAction,
1577
+ NotificationCenterHeaderActions,
1578
+ NotificationCenterItem,
1579
+ NotificationCenterItemDescription,
1580
+ NotificationCenterItemTime,
1581
+ NotificationCenterItemTitle,
1582
+ NotificationCenterList,
1583
+ NotificationCenterSkeleton,
1584
+ NotificationCenterTabs,
1585
+ NotificationCenterTabsList,
1586
+ NotificationCenterTitle,
1587
+ NotificationCenterTrigger,
1588
+ } from '../../NotificationCenter';
1589
+ import { NumericBadge } from '../../NumericBadge';
1590
+ import { TabsContent, TabsTrigger } from '../../Tabs';
1591
+
1592
+ const MINUTE = 60_000;
1593
+ const minutesAgo = (minutes: number) => new Date(Date.now() - minutes * MINUTE);
1594
+
1595
+ const NEW = [
1596
+ {
1597
+ id: 'attack',
1598
+ title: 'New attack detected on api.example.com',
1599
+ description: '214 SQL injection attempts from 12 IPs in the last 10 minutes.',
1600
+ createdAt: minutesAgo(3),
1601
+ },
1602
+ {
1603
+ id: 'node',
1604
+ title: 'Node wallarm-node-3 is offline',
1605
+ description: 'No heartbeat for 15 minutes. Traffic fails over to wallarm-node-1.',
1606
+ createdAt: minutesAgo(22),
1607
+ },
1608
+ ];
1609
+
1610
+ const READ = [
1611
+ {
1612
+ id: 'report',
1613
+ title: 'Weekly API discovery report is ready',
1614
+ description: '38 new endpoints this week, 5 of them handling sensitive data.',
1615
+ createdAt: minutesAgo(60 * 24 * 7),
1616
+ },
1617
+ ];
1618
+
1619
+ /** The host's inbox, as core-frontend's NotificationsInbox: bell with the unread count, New/Read tabs. */
1620
+ export const NotificationsInbox = ({ loading = false }: { loading?: boolean }) => (
1621
+ <NotificationCenter data-testid='notifications'>
1622
+ <NotificationCenterTrigger count={loading ? 0 : NEW.length} />
1623
+ <NotificationCenterContent>
1624
+ <NotificationCenterHeader>
1625
+ <NotificationCenterTitle />
1626
+ <NotificationCenterHeaderActions>
1627
+ <NotificationCenterHeaderAction disabled={loading}>
1628
+ Mark all as read
1629
+ </NotificationCenterHeaderAction>
1630
+ <NotificationCenterHeaderAction asChild aria-label='Notification settings'>
1631
+ <a href='#notification-settings'>
1632
+ <Settings />
1633
+ </a>
1634
+ </NotificationCenterHeaderAction>
1635
+ </NotificationCenterHeaderActions>
1636
+ </NotificationCenterHeader>
1637
+ {loading ? (
1638
+ <NotificationCenterBody>
1639
+ <NotificationCenterSkeleton />
1640
+ </NotificationCenterBody>
1641
+ ) : (
1642
+ <NotificationCenterTabs defaultValue='new'>
1643
+ <NotificationCenterTabsList>
1644
+ <TabsTrigger value='new'>
1645
+ New <NumericBadge size='small'>{NEW.length}</NumericBadge>
1646
+ </TabsTrigger>
1647
+ <TabsTrigger value='read'>Read</TabsTrigger>
1648
+ </NotificationCenterTabsList>
1649
+ {[
1650
+ { value: 'new', items: NEW },
1651
+ { value: 'read', items: READ },
1652
+ ].map(tab => (
1653
+ <TabsContent key={tab.value} value={tab.value}>
1654
+ <NotificationCenterBody>
1655
+ <NotificationCenterList>
1656
+ {tab.items.map(item => (
1657
+ <NotificationCenterItem key={item.id} unread={tab.value === 'new'}>
1658
+ <NotificationCenterItemTitle>{item.title}</NotificationCenterItemTitle>
1659
+ <NotificationCenterItemTime value={item.createdAt} />
1660
+ <NotificationCenterItemDescription>
1661
+ {item.description}
1662
+ </NotificationCenterItemDescription>
1663
+ </NotificationCenterItem>
1664
+ ))}
1665
+ </NotificationCenterList>
1666
+ </NotificationCenterBody>
1667
+ </TabsContent>
1668
+ ))}
1669
+ </NotificationCenterTabs>
1670
+ )}
1671
+ </NotificationCenterContent>
1672
+ </NotificationCenter>
1673
+ );
1674
+ ```
1675
+
1676
+ ### AppShell/story-content/_storyProductNavItems.tsx
1677
+
1678
+ ```tsx
1679
+ import type { FC } from 'react';
1680
+ import { CircleDashed } from '../../../icons';
1681
+ import { NavRailItem } from '../../NavRail';
1682
+ import type { Product } from './_storyLib';
1683
+
1684
+ interface ProductNavItemsProps {
1685
+ activeProduct: Product;
1686
+ onSelectProduct: (product: Product) => void;
1687
+ }
1688
+
1689
+ export const ProductNavItems: FC<ProductNavItemsProps> = ({ activeProduct, onSelectProduct }) => (
1690
+ <>
1691
+ <NavRailItem
1692
+ icon={CircleDashed}
1693
+ label='Edge'
1694
+ shortcut={['G', 'E']}
1695
+ active={activeProduct === 'edge'}
1696
+ onClick={() => onSelectProduct('edge')}
1697
+ />
1698
+ <NavRailItem
1699
+ icon={CircleDashed}
1700
+ label='AI Hypervisor'
1701
+ shortcut={['G', 'A']}
1702
+ active={activeProduct === 'ai-hypervisor'}
1703
+ onClick={() => onSelectProduct('ai-hypervisor')}
1704
+ />
1705
+ <NavRailItem
1706
+ icon={CircleDashed}
1707
+ label='Infra Discovery'
1708
+ shortcut={['G', 'I']}
1709
+ active={activeProduct === 'infra-discovery'}
1710
+ onClick={() => onSelectProduct('infra-discovery')}
1711
+ />
1712
+ <NavRailItem
1713
+ icon={CircleDashed}
1714
+ label='Security Testing'
1715
+ shortcut={['G', 'T']}
1716
+ active={activeProduct === 'security-testing'}
1717
+ onClick={() => onSelectProduct('security-testing')}
1718
+ />
1719
+ </>
1720
+ );
1721
+ ```
1722
+
1723
+ ### AppShell/story-content/_storyQuickHelpDropdown.tsx
1724
+
1725
+ ```tsx
1726
+ import {
1727
+ Activity,
1728
+ Book,
1729
+ CircleHelp,
1730
+ LifeBuoy,
1731
+ NotebookText,
1732
+ SquareArrowOutUpRight,
1733
+ } from '../../../icons';
1734
+ import { Button } from '../../Button';
1735
+ import {
1736
+ DropdownMenu,
1737
+ DropdownMenuContent,
1738
+ DropdownMenuItem,
1739
+ DropdownMenuItemContent,
1740
+ DropdownMenuItemIcon,
1741
+ DropdownMenuItemText,
1742
+ DropdownMenuSeparator,
1743
+ DropdownMenuTrigger,
1744
+ } from '../../DropdownMenu';
1745
+ import { Text } from '../../Text';
1746
+ import { Tooltip, TooltipContent, TooltipTrigger } from '../../Tooltip';
1747
+
1748
+ export const QuickHelpDropdown = () => (
1749
+ <DropdownMenu positioning={{ placement: 'bottom-end', gutter: 4 }}>
1750
+ <Tooltip>
1751
+ <TooltipTrigger asChild>
1752
+ <span className='inline-flex'>
1753
+ <DropdownMenuTrigger asChild>
1754
+ <Button variant='ghost' size='small' color='neutral' aria-label='Quick Help'>
1755
+ <CircleHelp />
1756
+ </Button>
1757
+ </DropdownMenuTrigger>
1758
+ </span>
1759
+ </TooltipTrigger>
1760
+ <TooltipContent>Quick help</TooltipContent>
1761
+ </Tooltip>
1762
+
1763
+ <DropdownMenuContent className='w-320'>
1764
+ <DropdownMenuItem onSelect={() => window.open('https://docs.wallarm.com/', '_blank')}>
1765
+ <DropdownMenuItemIcon>
1766
+ <Book />
1767
+ </DropdownMenuItemIcon>
1768
+
1769
+ <DropdownMenuItemContent>
1770
+ <DropdownMenuItemText>Documentation</DropdownMenuItemText>
1771
+ <Text size='xs' color='secondary'>
1772
+ Setup, deployment, and how-tos
1773
+ </Text>
1774
+ </DropdownMenuItemContent>
1775
+ </DropdownMenuItem>
1776
+
1777
+ <DropdownMenuItem onSelect={() => window.open('https://status.wallarm.com/', '_blank')}>
1778
+ <DropdownMenuItemIcon>
1779
+ <Activity />
1780
+ </DropdownMenuItemIcon>
1781
+
1782
+ <DropdownMenuItemContent>
1783
+ <DropdownMenuItemText>Service status</DropdownMenuItemText>
1784
+ <Text size='xs' color='secondary'>
1785
+ Live uptime and alerts
1786
+ </Text>
1787
+ </DropdownMenuItemContent>
1788
+ </DropdownMenuItem>
1789
+
1790
+ <DropdownMenuItem onSelect={() => window.open('mailto:support@wallarm.com')}>
1791
+ <DropdownMenuItemIcon>
1792
+ <LifeBuoy />
1793
+ </DropdownMenuItemIcon>
1794
+
1795
+ <DropdownMenuItemContent>
1796
+ <DropdownMenuItemText>Support</DropdownMenuItemText>
1797
+ <Text size='xs' color='secondary'>
1798
+ Talk to our team
1799
+ </Text>
1800
+ </DropdownMenuItemContent>
1801
+ </DropdownMenuItem>
1802
+
1803
+ <DropdownMenuItem onSelect={() => window.open('https://lab.wallarm.com', '_blank')}>
1804
+ <DropdownMenuItemIcon>
1805
+ <NotebookText />
1806
+ </DropdownMenuItemIcon>
1807
+
1808
+ <DropdownMenuItemContent>
1809
+ <DropdownMenuItemText>Blog</DropdownMenuItemText>
1810
+ <Text size='xs' color='secondary'>
1811
+ Security news and updates
1812
+ </Text>
1813
+ </DropdownMenuItemContent>
1814
+ </DropdownMenuItem>
1815
+
1816
+ <DropdownMenuSeparator />
1817
+
1818
+ <DropdownMenuItem onSelect={() => window.open('https://playground.wallarm.com', '_blank')}>
1819
+ <DropdownMenuItemIcon>
1820
+ <SquareArrowOutUpRight />
1821
+ </DropdownMenuItemIcon>
1822
+
1823
+ <DropdownMenuItemContent>
1824
+ <DropdownMenuItemText>Wallarm Playground</DropdownMenuItemText>
1825
+ <Text size='xs' color='secondary'>
1826
+ Try the platform without any setup
1827
+ </Text>
1828
+ </DropdownMenuItemContent>
1829
+ </DropdownMenuItem>
1830
+ </DropdownMenuContent>
1831
+ </DropdownMenu>
1832
+ );
1833
+ ```
1834
+
1835
+ ### AppShell/story-content/_storyRecentDropdown.tsx
1836
+
1837
+ ```tsx
1838
+ import { History } from '../../../icons';
1839
+ import {
1840
+ DropdownMenu,
1841
+ DropdownMenuContent,
1842
+ DropdownMenuItem,
1843
+ DropdownMenuItemContent,
1844
+ DropdownMenuItemText,
1845
+ DropdownMenuLabel,
1846
+ DropdownMenuTrigger,
1847
+ } from '../../DropdownMenu';
1848
+ import { NavRailItem } from '../../NavRail';
1849
+ import { Text } from '../../Text';
1850
+
1851
+ const RECENT_ITEMS = [
1852
+ { pageName: 'WAF Rules', productName: 'Edge' },
1853
+ { pageName: 'API Sessions', dataPlane: 'US Cloud', productName: 'Edge' },
1854
+ { pageName: 'Prompt Inspector', productName: 'AI Hypervisor' },
1855
+ { pageName: 'Scanner Results', dataPlane: 'EU Cloud', productName: 'Security Testing' },
1856
+ { pageName: 'Asset Inventory', productName: 'Infra Discovery' },
1857
+ { pageName: 'Pupu', productName: 'Edge' },
1858
+ { pageName: 'Pupupu', dataPlane: 'US Cloud', productName: 'Edge' },
1859
+ { pageName: 'PuPuPuuu', productName: 'AI Hypervisor' },
1860
+ { pageName: 'MeowMeow', dataPlane: 'EU Cloud', productName: 'Security Testing' },
1861
+ { pageName: '...', productName: '....' },
1862
+ ];
1863
+
1864
+ export const RecentDropdown = () => (
1865
+ <DropdownMenu positioning={{ placement: 'right-start', gutter: 8 }}>
1866
+ <DropdownMenuTrigger>
1867
+ <NavRailItem icon={History} label='Recent' />
1868
+ </DropdownMenuTrigger>
1869
+
1870
+ <DropdownMenuContent className='w-304 h-290 overscroll-none'>
1871
+ <DropdownMenuLabel className='sticky top-0 z-10 bg-bg-surface-2'>Recent</DropdownMenuLabel>
1872
+
1873
+ {RECENT_ITEMS.map(item => (
1874
+ <DropdownMenuItem key={`${item.pageName}-${item.productName}`}>
1875
+ <DropdownMenuItemContent>
1876
+ <DropdownMenuItemText>{item.pageName}</DropdownMenuItemText>
1877
+ <Text size='xs' color='secondary'>
1878
+ {item.dataPlane ? `${item.dataPlane} \u00b7 ${item.productName}` : item.productName}
1879
+ </Text>
1880
+ </DropdownMenuItemContent>
1881
+ </DropdownMenuItem>
1882
+ ))}
1883
+ </DropdownMenuContent>
1884
+ </DropdownMenu>
1885
+ );
1886
+ ```
1887
+
1888
+ ### AppShell/story-content/_storyRouter.tsx
1889
+
1890
+ ```tsx
1891
+ import { createContext, type FC, type ReactNode, useCallback, useContext, useState } from 'react';
1892
+ import {
1893
+ createMemoryHistory,
1894
+ createRootRoute,
1895
+ createRoute,
1896
+ createRouter,
1897
+ RouterProvider,
1898
+ useNavigate,
1899
+ useRouterState,
1900
+ } from '@tanstack/react-router';
1901
+ import { deriveProduct, type Product, productLandingPath } from './_storyLib';
1902
+
1903
+ // Story-only harness: every story gets its own TanStack Router on a memory history,
1904
+ // so navigation never touches Storybook's iframe URL and stories don't share history.
1905
+ // `story-content/**` is excluded from the published build (see rslib.config.ts).
1906
+
1907
+ const StoryContentContext = createContext<ReactNode>(null);
1908
+
1909
+ const StoryContent: FC = () => <>{useContext(StoryContentContext)}</>;
1910
+
1911
+ const createStoryRouter = (initialPath: string) => {
1912
+ const rootRoute = createRootRoute({ component: StoryContent });
1913
+ // Catch-all so every product URL matches; the shell reads the pathname itself.
1914
+ const splatRoute = createRoute({ getParentRoute: () => rootRoute, path: '$' });
1915
+
1916
+ return createRouter({
1917
+ routeTree: rootRoute.addChildren([splatRoute]),
1918
+ history: createMemoryHistory({ initialEntries: [initialPath] }),
1919
+ });
1920
+ };
1921
+
1922
+ interface StoryRouterProps {
1923
+ /** Starting URL of the memory history. */
1924
+ initialPath?: string;
1925
+ children: ReactNode;
1926
+ }
1927
+
1928
+ /** Renders `children` inside a per-render TanStack Router with memory history. */
1929
+ export const StoryRouter: FC<StoryRouterProps> = ({ initialPath = '/home', children }) => {
1930
+ const [router] = useState(() => createStoryRouter(initialPath));
1931
+
1932
+ return (
1933
+ <StoryContentContext.Provider value={children}>
1934
+ <RouterProvider router={router} />
1935
+ </StoryContentContext.Provider>
1936
+ );
1937
+ };
1938
+
1939
+ StoryRouter.displayName = 'StoryRouter';
1940
+
1941
+ /** Current router pathname — what a host passes to `RemoteShell`'s `pathname` prop. */
1942
+ export const useStoryPathname = (): string =>
1943
+ useRouterState({ select: state => state.location.pathname });
1944
+
1945
+ /** Router-driven product navigation for the rail. */
1946
+ export const useStoryProductNavigation = () => {
1947
+ const navigate = useNavigate();
1948
+ const pathname = useStoryPathname();
1949
+
1950
+ const goToProduct = useCallback(
1951
+ (product: Product) => {
1952
+ navigate({ to: productLandingPath(product) });
1953
+ },
1954
+ [navigate],
1955
+ );
1956
+
1957
+ return { pathname, activeProduct: deriveProduct(pathname), goToProduct };
1958
+ };
1959
+ ```
1960
+
1961
+ ### AppShell/story-content/_storyWallarmLogo.tsx
1962
+
1963
+ ```tsx
1964
+ export const WallarmLogo = () => (
1965
+ <svg width='89' height='16' viewBox='0 0 89 16' fill='none' xmlns='http://www.w3.org/2000/svg'>
1966
+ <path
1967
+ fillRule='evenodd'
1968
+ clipRule='evenodd'
1969
+ d='M33.9026 4.87835H35.5876L37.698 11.2096L39.383 4.87835H41.4934L38.5405 14.1625H36.8555L34.7451 7.83127L32.6346 14.1625H30.9496L27.9884 4.87835H30.0988L31.7838 11.2096L33.8942 4.87835H33.9026ZM44.9886 12.7862C46.3733 12.7862 47.4076 12.0104 47.4076 10.8676V10.292C46.8738 10.0835 46.1314 9.92502 45.3139 9.92502C44.0042 9.92502 43.2201 10.4589 43.2201 11.3931V11.4265C43.2201 12.294 43.9959 12.7778 44.9802 12.7778L44.9886 12.7862ZM42.7613 5.35382C43.5371 5.07855 44.388 4.90337 45.5057 4.90337C48.2001 4.90337 49.518 6.24637 49.518 8.68212V14.1709H47.4076V13.0531C46.782 13.7955 45.8728 14.3627 44.4881 14.3627C42.7614 14.3627 41.2432 13.3784 41.2432 11.5433V11.5099C41.2432 9.5413 42.8197 8.57368 44.9385 8.57368C46.0479 8.57368 46.6986 8.73217 47.4076 8.95739V8.73217C47.4076 7.47258 46.6652 6.78023 45.2138 6.78023C44.2712 6.78023 43.5204 6.98043 42.7613 7.28073V5.3705V5.35382ZM50.786 14.1625V1.5H52.8964V14.1625H50.786ZM54.1643 14.1625V1.5H56.2747L56.3164 14.1625H54.1643ZM61.0378 12.7862C62.4225 12.7862 63.4569 12.0104 63.4569 10.8676V10.292C62.923 10.0835 62.1806 9.92502 61.3631 9.92502C60.0535 9.92502 59.2694 10.4589 59.2694 11.3931V11.4265C59.2694 12.294 60.0451 12.7778 61.0295 12.7778L61.0378 12.7862ZM58.8106 5.35382C59.578 5.07855 60.4372 4.90337 61.555 4.90337C64.2493 4.90337 65.5589 6.24637 65.5589 8.68212V14.1709H63.4485V13.0531C62.8229 13.7955 61.9137 14.3627 60.529 14.3627C58.8022 14.3627 57.2841 13.3784 57.2841 11.5433V11.5099C57.2841 9.5413 58.8606 8.57368 60.9794 8.57368C62.0888 8.57368 62.7395 8.73217 63.4485 8.95739V8.73217C63.4485 7.47258 62.7061 6.78023 61.2547 6.78023C60.3121 6.78023 59.5613 6.98043 58.8022 7.28073V5.3705L58.8106 5.35382ZM66.8269 14.1625V4.87835H68.9373V6.93872C69.5045 5.5707 70.5389 4.63644 72.1405 4.71152V6.92204H72.0237C70.1885 6.92204 68.9373 8.11489 68.9373 10.534V14.1709H66.8269V14.1625ZM87.0969 8.18162V14.1709H84.9865V8.94071C84.9865 7.48927 84.3359 6.58838 83.1597 6.58838C81.9835 6.58838 81.1911 7.47258 81.1911 8.94071V14.1709H79.0807V8.80724C79.0807 7.38917 78.43 6.58838 77.2705 6.58838C76.1111 6.58838 75.2769 7.52263 75.2769 8.94071V14.1709H73.1665V4.87835H75.2769V6.26305C75.8692 5.45392 76.695 4.68649 78.0964 4.68649C79.406 4.68649 80.3236 5.32879 80.8074 6.29642C81.5498 5.32879 82.5174 4.68649 83.9021 4.68649C85.8874 4.68649 87.0969 5.94607 87.0969 8.18162Z'
1970
+ fill='currentColor'
1971
+ />
1972
+ <path
1973
+ d='M0.849998 6.83954L9.8954 3V8.4134L16.4618 5.62599V9.59236L21.2757 7.54864V10.4542L16.4618 12.498V10.754L9.8954 13.5414V11.1662L0.849998 15V6.83954Z'
1974
+ fill='#FF441C'
1975
+ />
1976
+ </svg>
1977
+ );
1978
+ ```
1979
+
1980
+ ### ../utils/cn.ts
1981
+
1982
+ ```tsx
1983
+ import { type ClassArray, clsx } from 'clsx';
1984
+ import { twMerge } from 'tailwind-merge';
1985
+
1986
+ export const cn = (...inputs: ClassArray): string => {
1987
+ return twMerge(clsx(inputs));
1988
+ };
1989
+ ```
1990
+
1991
+ ### AppShell/story-content/_storySearchModal.tsx
1992
+
1993
+ ```tsx
1994
+ import { useState } from 'react';
1995
+ import { Building, CircleDashed, Plus } from '../../../icons';
1996
+ import { Button } from '../../Button';
1997
+ import { Code } from '../../Code';
1998
+ import { EmptyState, EmptyStateDescription, EmptyStateMessage } from '../../EmptyState';
1999
+ import { Kbd } from '../../Kbd';
2000
+ import {
2001
+ SearchModal,
2002
+ SearchModalBody,
2003
+ SearchModalContent,
2004
+ SearchModalEmpty,
2005
+ SearchModalFooter,
2006
+ SearchModalGroup,
2007
+ SearchModalGroupLabel,
2008
+ SearchModalInput,
2009
+ SearchModalItem,
2010
+ SearchModalTrigger,
2011
+ } from '../../SearchModal';
2012
+ import { Text } from '../../Text';
2013
+ import { WallyIcon } from '../../WallyIcon';
2014
+
2015
+ const recentItems = [
2016
+ { page: 'Attacks', product: 'Cloud WAF', date: '2 hours ago', href: '/attacks' },
2017
+ { page: 'Rules', product: 'Cloud WAF', date: '5 hours ago', href: '/rules' },
2018
+ {
2019
+ page: 'Endpoints',
2020
+ product: 'US East',
2021
+ dataPlane: true,
2022
+ productExtra: 'API Discovery',
2023
+ date: 'Yesterday',
2024
+ href: '/endpoints',
2025
+ },
2026
+ { page: 'Dashboards', product: 'Cloud WAF', date: '2 days ago', href: '/dashboards' },
2027
+ { page: 'Triggers', product: 'Cloud WAF', date: '3 days ago', href: '/triggers' },
2028
+ ];
2029
+
2030
+ const jumpToItems = [
2031
+ { name: 'Cloud WAF', href: '/cloud-waf' },
2032
+ { name: 'API Discovery', href: '/api-discovery' },
2033
+ ];
2034
+
2035
+ const filterRecent = (query: string) => {
2036
+ if (!query) return recentItems;
2037
+ return recentItems.filter(item => {
2038
+ const text = `${item.page} ${item.product} ${item.productExtra ?? ''}`.toLowerCase();
2039
+ return text.includes(query);
2040
+ });
2041
+ };
2042
+
2043
+ const filterJumpTo = (query: string) => {
2044
+ if (!query) return jumpToItems;
2045
+ return jumpToItems.filter(item => item.name.toLowerCase().includes(query));
2046
+ };
2047
+
2048
+ export const StorySearchModal = () => {
2049
+ const [open, setOpen] = useState(false);
2050
+ const [query, setQuery] = useState('');
2051
+ const lowerQuery = query.toLowerCase();
2052
+
2053
+ const filteredRecent = filterRecent(lowerQuery);
2054
+ const filteredJumpTo = filterJumpTo(lowerQuery);
2055
+ const hasResults = filteredRecent.length > 0 || filteredJumpTo.length > 0 || !lowerQuery;
2056
+
2057
+ const handleOpenChange = (nextOpen: boolean) => {
2058
+ setOpen(nextOpen);
2059
+ if (!nextOpen) {
2060
+ setQuery('');
2061
+ }
2062
+ };
2063
+
2064
+ return (
2065
+ <SearchModal open={open} onOpenChange={handleOpenChange} onQueryChange={setQuery}>
2066
+ <SearchModalTrigger asChild>
2067
+ <Button
2068
+ variant='ghost'
2069
+ size='small'
2070
+ color='neutral'
2071
+ className='p-4 gap-6 rounded-6'
2072
+ data-slot='top-header-search'
2073
+ >
2074
+ <Code size='s' color='secondary'>
2075
+ Search Wallarm
2076
+ </Code>
2077
+ <Kbd size='xsmall'>⌘ K</Kbd>
2078
+ </Button>
2079
+ </SearchModalTrigger>
2080
+
2081
+ <SearchModalContent>
2082
+ <SearchModalInput placeholder='Search products, features, and more...' />
2083
+
2084
+ {lowerQuery && !hasResults && (
2085
+ <SearchModalEmpty>
2086
+ <EmptyState type='no-results' className='pb-8'>
2087
+ <EmptyStateMessage>
2088
+ <EmptyStateDescription>No results</EmptyStateDescription>
2089
+ </EmptyStateMessage>
2090
+ </EmptyState>
2091
+ </SearchModalEmpty>
2092
+ )}
2093
+
2094
+ <SearchModalBody>
2095
+ <SearchModalGroup>
2096
+ <SearchModalGroupLabel>
2097
+ <Text size='xs' color='secondary' weight='medium'>
2098
+ Quick actions
2099
+ </Text>
2100
+ </SearchModalGroupLabel>
2101
+ <SearchModalItem onSelect={() => {}}>
2102
+ <WallyIcon variant='circle' size='sm' />
2103
+ <Text size='sm'>Ask Wally</Text>
2104
+ </SearchModalItem>
2105
+
2106
+ {!lowerQuery && (
2107
+ <>
2108
+ <SearchModalItem onSelect={() => {}}>
2109
+ <Building className='text-text-secondary !icon-md' />
2110
+ <Text size='sm'>Switch tenant</Text>
2111
+ </SearchModalItem>
2112
+
2113
+ <SearchModalItem onSelect={() => {}}>
2114
+ <Plus className='text-text-secondary !icon-md' />
2115
+ <Text size='sm'>Create...</Text>
2116
+ </SearchModalItem>
2117
+ </>
2118
+ )}
2119
+ </SearchModalGroup>
2120
+
2121
+ {filteredRecent.length > 0 && (
2122
+ <SearchModalGroup>
2123
+ <SearchModalGroupLabel>
2124
+ <Text size='xs' color='secondary' weight='medium'>
2125
+ Recent
2126
+ </Text>
2127
+ </SearchModalGroupLabel>
2128
+ {filteredRecent.map(item => (
2129
+ <SearchModalItem key={`${item.page}-${item.product}`} onSelect={() => {}}>
2130
+ <CircleDashed className='text-text-primary shrink-0 !icon-md' />
2131
+ <Text size='sm' grow truncate>
2132
+ {item.page}
2133
+ <Text size='sm' color='secondary' inline>
2134
+ {' '}
2135
+ — {item.dataPlane ? `${item.productExtra} · ${item.product}` : item.product}
2136
+ </Text>
2137
+ </Text>
2138
+ <span className='shrink-0'>
2139
+ <Text size='xs' color='secondary'>
2140
+ {item.date}
2141
+ </Text>
2142
+ </span>
2143
+ </SearchModalItem>
2144
+ ))}
2145
+ </SearchModalGroup>
2146
+ )}
2147
+
2148
+ {filteredJumpTo.length > 0 && (
2149
+ <SearchModalGroup>
2150
+ <SearchModalGroupLabel>
2151
+ <Text size='xs' color='secondary' weight='medium'>
2152
+ Jump to
2153
+ </Text>
2154
+ </SearchModalGroupLabel>
2155
+ {filteredJumpTo.map(item => (
2156
+ <SearchModalItem key={item.href} onSelect={() => {}}>
2157
+ <CircleDashed className='text-text-primary shrink-0 !icon-md' />
2158
+ <Text size='sm'>{item.name}</Text>
2159
+ </SearchModalItem>
2160
+ ))}
2161
+ </SearchModalGroup>
2162
+ )}
2163
+ </SearchModalBody>
2164
+
2165
+ <SearchModalFooter />
2166
+ </SearchModalContent>
2167
+ </SearchModal>
2168
+ );
2169
+ };
2170
+ ```