@nordmanai/nif 0.2.2 → 0.2.3

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 (354) hide show
  1. package/README.md +1 -1
  2. package/dist/index.d.ts +6 -0
  3. package/dist/index.js +31342 -11760
  4. package/dist/styles.css +1 -1
  5. package/dist/ui/components/nm-ai/nm-ai.d.ts +4 -0
  6. package/dist/ui/components/nm-alert/nm-alert.d.ts +13 -0
  7. package/dist/ui/components/nm-button/nm-button.d.ts +4 -2
  8. package/dist/ui/components/nm-checkbox/nm-checkbox.d.ts +1 -1
  9. package/dist/ui/components/nm-chip/nm-chip.d.ts +5 -2
  10. package/dist/ui/components/nm-collapsible/nm-collapsible.d.ts +6 -0
  11. package/dist/ui/components/nm-combobox/nm-combobox.d.ts +1 -1
  12. package/dist/ui/components/nm-command/nm-command.d.ts +8 -1
  13. package/dist/ui/components/nm-composer/nm-composer.d.ts +47 -4
  14. package/dist/ui/components/nm-context-menu/nm-context-menu.d.ts +1 -1
  15. package/dist/ui/components/nm-dialog/nm-dialog.d.ts +1 -1
  16. package/dist/ui/components/nm-dropdown/nm-dropdown.d.ts +1 -1
  17. package/dist/ui/components/nm-dropdown-menu/nm-dropdown-menu.d.ts +11 -2
  18. package/dist/ui/components/nm-hover-card/nm-hover-card.d.ts +1 -1
  19. package/dist/ui/components/nm-icon/animated/activity.d.ts +72 -0
  20. package/dist/ui/components/nm-icon/animated/archive.d.ts +31 -0
  21. package/dist/ui/components/nm-icon/animated/arrow-down.d.ts +128 -0
  22. package/dist/ui/components/nm-icon/animated/arrow-left.d.ts +128 -0
  23. package/dist/ui/components/nm-icon/animated/arrow-right.d.ts +128 -0
  24. package/dist/ui/components/nm-icon/animated/arrow-up-down.d.ts +119 -0
  25. package/dist/ui/components/nm-icon/animated/arrow-up.d.ts +128 -0
  26. package/dist/ui/components/nm-icon/animated/audio-lines.d.ts +99 -0
  27. package/dist/ui/components/nm-icon/animated/bookmark-plus.d.ts +35 -0
  28. package/dist/ui/components/nm-icon/animated/bookmark.d.ts +52 -0
  29. package/dist/ui/components/nm-icon/animated/bot.d.ts +90 -0
  30. package/dist/ui/components/nm-icon/animated/calendar-days.d.ts +87 -0
  31. package/dist/ui/components/nm-icon/animated/calendar.d.ts +36 -0
  32. package/dist/ui/components/nm-icon/animated/camera.d.ts +50 -0
  33. package/dist/ui/components/nm-icon/animated/chart-column-increasing.d.ts +37 -0
  34. package/dist/ui/components/nm-icon/animated/chart-column.d.ts +86 -0
  35. package/dist/ui/components/nm-icon/animated/chart-line.d.ts +51 -0
  36. package/dist/ui/components/nm-icon/animated/check-check.d.ts +50 -0
  37. package/dist/ui/components/nm-icon/animated/check.d.ts +25 -0
  38. package/dist/ui/components/nm-icon/animated/chevron-down.d.ts +39 -0
  39. package/dist/ui/components/nm-icon/animated/chevron-left.d.ts +39 -0
  40. package/dist/ui/components/nm-icon/animated/chevron-right.d.ts +39 -0
  41. package/dist/ui/components/nm-icon/animated/chevron-up.d.ts +39 -0
  42. package/dist/ui/components/nm-icon/animated/circle-alert.d.ts +23 -0
  43. package/dist/ui/components/nm-icon/animated/circle-check-big.d.ts +26 -0
  44. package/dist/ui/components/nm-icon/animated/circle-check.d.ts +26 -0
  45. package/dist/ui/components/nm-icon/animated/circle-plus.d.ts +44 -0
  46. package/dist/ui/components/nm-icon/animated/circle-question-mark.d.ts +22 -0
  47. package/dist/ui/components/nm-icon/animated/circle-x.d.ts +44 -0
  48. package/dist/ui/components/nm-icon/animated/clock-3.d.ts +44 -0
  49. package/dist/ui/components/nm-icon/animated/clock-4.d.ts +44 -0
  50. package/dist/ui/components/nm-icon/animated/clock.d.ts +44 -0
  51. package/dist/ui/components/nm-icon/animated/cloud-sun.d.ts +9 -0
  52. package/dist/ui/components/nm-icon/animated/cloud-upload.d.ts +45 -0
  53. package/dist/ui/components/nm-icon/animated/copy.d.ts +135 -0
  54. package/dist/ui/components/nm-icon/animated/download.d.ts +45 -0
  55. package/dist/ui/components/nm-icon/animated/ellipsis.d.ts +141 -0
  56. package/dist/ui/components/nm-icon/animated/external-link.d.ts +49 -0
  57. package/dist/ui/components/nm-icon/animated/eye-off.d.ts +23 -0
  58. package/dist/ui/components/nm-icon/animated/eye.d.ts +39 -0
  59. package/dist/ui/components/nm-icon/animated/fast-forward.d.ts +34 -0
  60. package/dist/ui/components/nm-icon/animated/file-clock.d.ts +48 -0
  61. package/dist/ui/components/nm-icon/animated/file-text.d.ts +56 -0
  62. package/dist/ui/components/nm-icon/animated/file.d.ts +67 -0
  63. package/dist/ui/components/nm-icon/animated/folder.d.ts +92 -0
  64. package/dist/ui/components/nm-icon/animated/gauge.d.ts +47 -0
  65. package/dist/ui/components/nm-icon/animated/globe.d.ts +21 -0
  66. package/dist/ui/components/nm-icon/animated/graduation-cap.d.ts +41 -0
  67. package/dist/ui/components/nm-icon/animated/headphones.d.ts +50 -0
  68. package/dist/ui/components/nm-icon/animated/image.d.ts +40 -0
  69. package/dist/ui/components/nm-icon/animated/index.d.ts +2 -0
  70. package/dist/ui/components/nm-icon/animated/info.d.ts +37 -0
  71. package/dist/ui/components/nm-icon/animated/key-round.d.ts +37 -0
  72. package/dist/ui/components/nm-icon/animated/layers.d.ts +61 -0
  73. package/dist/ui/components/nm-icon/animated/layout-dashboard.d.ts +131 -0
  74. package/dist/ui/components/nm-icon/animated/library-big.d.ts +29 -0
  75. package/dist/ui/components/nm-icon/animated/lightbulb.d.ts +51 -0
  76. package/dist/ui/components/nm-icon/animated/link.d.ts +193 -0
  77. package/dist/ui/components/nm-icon/animated/list-filter.d.ts +13 -0
  78. package/dist/ui/components/nm-icon/animated/list.d.ts +111 -0
  79. package/dist/ui/components/nm-icon/animated/loader-circle.d.ts +24 -0
  80. package/dist/ui/components/nm-icon/animated/lock.d.ts +94 -0
  81. package/dist/ui/components/nm-icon/animated/log-out.d.ts +45 -0
  82. package/dist/ui/components/nm-icon/animated/mail.d.ts +100 -0
  83. package/dist/ui/components/nm-icon/animated/map-pin.d.ts +67 -0
  84. package/dist/ui/components/nm-icon/animated/menu.d.ts +57 -0
  85. package/dist/ui/components/nm-icon/animated/message-circle-dashed.d.ts +34 -0
  86. package/dist/ui/components/nm-icon/animated/message-circle.d.ts +23 -0
  87. package/dist/ui/components/nm-icon/animated/message-square-plus.d.ts +49 -0
  88. package/dist/ui/components/nm-icon/animated/message-square-text.d.ts +104 -0
  89. package/dist/ui/components/nm-icon/animated/message-square.d.ts +23 -0
  90. package/dist/ui/components/nm-icon/animated/mic-off.d.ts +23 -0
  91. package/dist/ui/components/nm-icon/animated/mic.d.ts +37 -0
  92. package/dist/ui/components/nm-icon/animated/minimize.d.ts +139 -0
  93. package/dist/ui/components/nm-icon/animated/minus.d.ts +54 -0
  94. package/dist/ui/components/nm-icon/animated/moon.d.ts +62 -0
  95. package/dist/ui/components/nm-icon/animated/music.d.ts +22 -0
  96. package/dist/ui/components/nm-icon/animated/notebook-pen.d.ts +41 -0
  97. package/dist/ui/components/nm-icon/animated/panel-left.d.ts +29 -0
  98. package/dist/ui/components/nm-icon/animated/panel-right.d.ts +29 -0
  99. package/dist/ui/components/nm-icon/animated/paperclip.d.ts +49 -0
  100. package/dist/ui/components/nm-icon/animated/pause.d.ts +59 -0
  101. package/dist/ui/components/nm-icon/animated/pencil.d.ts +25 -0
  102. package/dist/ui/components/nm-icon/animated/pin-off.d.ts +44 -0
  103. package/dist/ui/components/nm-icon/animated/pin.d.ts +85 -0
  104. package/dist/ui/components/nm-icon/animated/plane-takeoff.d.ts +25 -0
  105. package/dist/ui/components/nm-icon/animated/play.d.ts +43 -0
  106. package/dist/ui/components/nm-icon/animated/plug-zap.d.ts +49 -0
  107. package/dist/ui/components/nm-icon/animated/plus.d.ts +95 -0
  108. package/dist/ui/components/nm-icon/animated/radio.d.ts +24 -0
  109. package/dist/ui/components/nm-icon/animated/refresh-cw.d.ts +55 -0
  110. package/dist/ui/components/nm-icon/animated/rocket.d.ts +48 -0
  111. package/dist/ui/components/nm-icon/animated/rotate-ccw.d.ts +51 -0
  112. package/dist/ui/components/nm-icon/animated/rotate-cw.d.ts +71 -0
  113. package/dist/ui/components/nm-icon/animated/save.d.ts +21 -0
  114. package/dist/ui/components/nm-icon/animated/search.d.ts +42 -0
  115. package/dist/ui/components/nm-icon/animated/send.d.ts +39 -0
  116. package/dist/ui/components/nm-icon/animated/settings.d.ts +39 -0
  117. package/dist/ui/components/nm-icon/animated/share-2.d.ts +122 -0
  118. package/dist/ui/components/nm-icon/animated/share.d.ts +40 -0
  119. package/dist/ui/components/nm-icon/animated/shield-check.d.ts +36 -0
  120. package/dist/ui/components/nm-icon/animated/shopping-bag.d.ts +35 -0
  121. package/dist/ui/components/nm-icon/animated/sliders-horizontal.d.ts +239 -0
  122. package/dist/ui/components/nm-icon/animated/sparkles.d.ts +205 -0
  123. package/dist/ui/components/nm-icon/animated/star.d.ts +48 -0
  124. package/dist/ui/components/nm-icon/animated/sun.d.ts +9 -0
  125. package/dist/ui/components/nm-icon/animated/terminal.d.ts +22 -0
  126. package/dist/ui/components/nm-icon/animated/trash-2.d.ts +61 -0
  127. package/dist/ui/components/nm-icon/animated/trash.d.ts +37 -0
  128. package/dist/ui/components/nm-icon/animated/trending-up.d.ts +40 -0
  129. package/dist/ui/components/nm-icon/animated/triangle-alert.d.ts +37 -0
  130. package/dist/ui/components/nm-icon/animated/trophy.d.ts +23 -0
  131. package/dist/ui/components/nm-icon/animated/type.d.ts +57 -0
  132. package/dist/ui/components/nm-icon/animated/upload.d.ts +49 -0
  133. package/dist/ui/components/nm-icon/animated/user-round.d.ts +33 -0
  134. package/dist/ui/components/nm-icon/animated/user.d.ts +33 -0
  135. package/dist/ui/components/nm-icon/animated/users-round.d.ts +93 -0
  136. package/dist/ui/components/nm-icon/animated/users.d.ts +93 -0
  137. package/dist/ui/components/nm-icon/animated/volume-2.d.ts +10 -0
  138. package/dist/ui/components/nm-icon/animated/wand-sparkles.d.ts +71 -0
  139. package/dist/ui/components/nm-icon/animated/x.d.ts +95 -0
  140. package/dist/ui/components/nm-icon/animated/zap.d.ts +65 -0
  141. package/dist/ui/components/nm-icon/animated/zoom-in.d.ts +35 -0
  142. package/dist/ui/components/nm-icon/animated/zoom-out.d.ts +53 -0
  143. package/dist/ui/components/nm-icon/nm-icon.d.ts +6 -3
  144. package/dist/ui/components/nm-icon/nm-lucide-extended.d.ts +3 -0
  145. package/dist/ui/components/nm-icon/nm-lucide.d.ts +7 -3
  146. package/dist/ui/components/nm-menubar/nm-menubar.d.ts +2 -2
  147. package/dist/ui/components/nm-panel/nm-panel.d.ts +5 -1
  148. package/dist/ui/components/nm-popover/nm-popover.d.ts +2 -2
  149. package/dist/ui/components/nm-progress/nm-progress.d.ts +1 -1
  150. package/dist/ui/components/nm-radio-group/nm-radio-group.d.ts +2 -2
  151. package/dist/ui/components/nm-select/nm-select.d.ts +1 -1
  152. package/dist/ui/components/nm-skeleton/nm-skeleton.d.ts +3 -0
  153. package/dist/ui/components/nm-slider/nm-slider.d.ts +1 -1
  154. package/dist/ui/components/nm-small-icon-button/nm-small-icon-button.d.ts +3 -3
  155. package/dist/ui/components/nm-spinner/nm-spinner.d.ts +3 -0
  156. package/dist/ui/components/nm-switch/nm-switch.d.ts +1 -1
  157. package/dist/ui/components/nm-tabs/nm-tabs.d.ts +6 -0
  158. package/dist/ui/components/nm-toggle/nm-toggle.d.ts +1 -1
  159. package/dist/ui/components/nm-tooltip/nm-tooltip.d.ts +1 -1
  160. package/dist/ui/components/nm-voice-orb/nm-voice-orb.d.ts +18 -0
  161. package/dist/ui/components/nm-voice-orb/use-nm-voice-orb-theme.d.ts +2 -0
  162. package/dist/ui/config/nm-app-panel.d.ts +2 -0
  163. package/dist/ui/config/nm-button.d.ts +6 -0
  164. package/dist/ui/config/nm-composer.d.ts +11 -0
  165. package/dist/ui/config/nm-icons-extended.d.ts +1 -0
  166. package/dist/ui/config/nm-icons.d.ts +4 -1
  167. package/dist/ui/config/nm-voice-orb.d.ts +22 -0
  168. package/dist/ui/lib/nm-motion.d.ts +29 -0
  169. package/package.json +12 -8
  170. package/registry/index.json +5099 -71
  171. package/registry/ui/components/nm-ai/nm-ai.tsx +7 -4
  172. package/registry/ui/components/nm-alert/nm-alert.css +85 -0
  173. package/registry/ui/components/nm-alert/nm-alert.tsx +35 -0
  174. package/registry/ui/components/nm-button/nm-button.css +24 -0
  175. package/registry/ui/components/nm-button/nm-button.tsx +25 -11
  176. package/registry/ui/components/nm-checkbox/nm-checkbox.tsx +1 -1
  177. package/registry/ui/components/nm-chip/nm-chip.css +25 -0
  178. package/registry/ui/components/nm-chip/nm-chip.tsx +9 -3
  179. package/registry/ui/components/nm-collapsible/nm-collapsible.css +3 -0
  180. package/registry/ui/components/nm-collapsible/nm-collapsible.tsx +16 -0
  181. package/registry/ui/components/nm-combobox/nm-combobox.tsx +1 -1
  182. package/registry/ui/components/nm-command/nm-command.css +48 -0
  183. package/registry/ui/components/nm-command/nm-command.tsx +19 -5
  184. package/registry/ui/components/nm-composer/nm-composer.css +87 -3
  185. package/registry/ui/components/nm-composer/nm-composer.tsx +321 -33
  186. package/registry/ui/components/nm-context-menu/nm-context-menu.tsx +1 -1
  187. package/registry/ui/components/nm-dialog/nm-dialog.css +5 -6
  188. package/registry/ui/components/nm-dialog/nm-dialog.tsx +1 -1
  189. package/registry/ui/components/nm-dropdown/nm-dropdown.css +5 -0
  190. package/registry/ui/components/nm-dropdown/nm-dropdown.tsx +28 -249
  191. package/registry/ui/components/nm-dropdown-menu/nm-dropdown-menu.css +8 -0
  192. package/registry/ui/components/nm-dropdown-menu/nm-dropdown-menu.tsx +85 -6
  193. package/registry/ui/components/nm-hover-card/nm-hover-card.tsx +1 -1
  194. package/registry/ui/components/nm-icon/animated/activity.tsx +109 -0
  195. package/registry/ui/components/nm-icon/animated/archive.tsx +85 -0
  196. package/registry/ui/components/nm-icon/animated/arrow-down.tsx +149 -0
  197. package/registry/ui/components/nm-icon/animated/arrow-left.tsx +149 -0
  198. package/registry/ui/components/nm-icon/animated/arrow-right.tsx +149 -0
  199. package/registry/ui/components/nm-icon/animated/arrow-up-down.tsx +168 -0
  200. package/registry/ui/components/nm-icon/animated/arrow-up.tsx +149 -0
  201. package/registry/ui/components/nm-icon/animated/audio-lines.tsx +190 -0
  202. package/registry/ui/components/nm-icon/animated/bookmark-plus.tsx +65 -0
  203. package/registry/ui/components/nm-icon/animated/bookmark.tsx +77 -0
  204. package/registry/ui/components/nm-icon/animated/bot.tsx +167 -0
  205. package/registry/ui/components/nm-icon/animated/calendar-days.tsx +128 -0
  206. package/registry/ui/components/nm-icon/animated/calendar.tsx +83 -0
  207. package/registry/ui/components/nm-icon/animated/camera.tsx +85 -0
  208. package/registry/ui/components/nm-icon/animated/chart-column-increasing.tsx +150 -0
  209. package/registry/ui/components/nm-icon/animated/chart-column.tsx +179 -0
  210. package/registry/ui/components/nm-icon/animated/chart-line.tsx +88 -0
  211. package/registry/ui/components/nm-icon/animated/check-check.tsx +105 -0
  212. package/registry/ui/components/nm-icon/animated/check.tsx +70 -0
  213. package/registry/ui/components/nm-icon/animated/chevron-down.tsx +73 -0
  214. package/registry/ui/components/nm-icon/animated/chevron-left.tsx +73 -0
  215. package/registry/ui/components/nm-icon/animated/chevron-right.tsx +73 -0
  216. package/registry/ui/components/nm-icon/animated/chevron-up.tsx +73 -0
  217. package/registry/ui/components/nm-icon/animated/circle-alert.tsx +84 -0
  218. package/registry/ui/components/nm-icon/animated/circle-check-big.tsx +75 -0
  219. package/registry/ui/components/nm-icon/animated/circle-check.tsx +77 -0
  220. package/registry/ui/components/nm-icon/animated/circle-plus.tsx +93 -0
  221. package/registry/ui/components/nm-icon/animated/circle-question-mark.tsx +62 -0
  222. package/registry/ui/components/nm-icon/animated/circle-x.tsx +93 -0
  223. package/registry/ui/components/nm-icon/animated/clock-3.tsx +95 -0
  224. package/registry/ui/components/nm-icon/animated/clock-4.tsx +95 -0
  225. package/registry/ui/components/nm-icon/animated/clock.tsx +95 -0
  226. package/registry/ui/components/nm-icon/animated/cloud-sun.tsx +130 -0
  227. package/registry/ui/components/nm-icon/animated/cloud-upload.tsx +93 -0
  228. package/registry/ui/components/nm-icon/animated/copy.tsx +153 -0
  229. package/registry/ui/components/nm-icon/animated/download.tsx +93 -0
  230. package/registry/ui/components/nm-icon/animated/ellipsis.tsx +189 -0
  231. package/registry/ui/components/nm-icon/animated/external-link.tsx +97 -0
  232. package/registry/ui/components/nm-icon/animated/eye-off.tsx +63 -0
  233. package/registry/ui/components/nm-icon/animated/eye.tsx +82 -0
  234. package/registry/ui/components/nm-icon/animated/fast-forward.tsx +72 -0
  235. package/registry/ui/components/nm-icon/animated/file-clock.tsx +105 -0
  236. package/registry/ui/components/nm-icon/animated/file-text.tsx +102 -0
  237. package/registry/ui/components/nm-icon/animated/file.tsx +124 -0
  238. package/registry/ui/components/nm-icon/animated/folder.tsx +132 -0
  239. package/registry/ui/components/nm-icon/animated/gauge.tsx +84 -0
  240. package/registry/ui/components/nm-icon/animated/globe.tsx +61 -0
  241. package/registry/ui/components/nm-icon/animated/graduation-cap.tsx +92 -0
  242. package/registry/ui/components/nm-icon/animated/headphones.tsx +85 -0
  243. package/registry/ui/components/nm-icon/animated/image.tsx +88 -0
  244. package/registry/ui/components/nm-icon/animated/index.ts +251 -0
  245. package/registry/ui/components/nm-icon/animated/info.tsx +75 -0
  246. package/registry/ui/components/nm-icon/animated/key-round.tsx +67 -0
  247. package/registry/ui/components/nm-icon/animated/layers.tsx +116 -0
  248. package/registry/ui/components/nm-icon/animated/layout-dashboard.tsx +176 -0
  249. package/registry/ui/components/nm-icon/animated/library-big.tsx +89 -0
  250. package/registry/ui/components/nm-icon/animated/lightbulb.tsx +106 -0
  251. package/registry/ui/components/nm-icon/animated/link.tsx +210 -0
  252. package/registry/ui/components/nm-icon/animated/list-filter.tsx +78 -0
  253. package/registry/ui/components/nm-icon/animated/list.tsx +184 -0
  254. package/registry/ui/components/nm-icon/animated/loader-circle.tsx +68 -0
  255. package/registry/ui/components/nm-icon/animated/lock.tsx +152 -0
  256. package/registry/ui/components/nm-icon/animated/log-out.tsx +93 -0
  257. package/registry/ui/components/nm-icon/animated/mail.tsx +131 -0
  258. package/registry/ui/components/nm-icon/animated/map-pin.tsx +111 -0
  259. package/registry/ui/components/nm-icon/animated/menu.tsx +121 -0
  260. package/registry/ui/components/nm-icon/animated/message-circle-dashed.tsx +140 -0
  261. package/registry/ui/components/nm-icon/animated/message-circle.tsx +66 -0
  262. package/registry/ui/components/nm-icon/animated/message-square-plus.tsx +106 -0
  263. package/registry/ui/components/nm-icon/animated/message-square-text.tsx +153 -0
  264. package/registry/ui/components/nm-icon/animated/message-square.tsx +66 -0
  265. package/registry/ui/components/nm-icon/animated/mic-off.tsx +65 -0
  266. package/registry/ui/components/nm-icon/animated/mic.tsx +77 -0
  267. package/registry/ui/components/nm-icon/animated/minimize.tsx +164 -0
  268. package/registry/ui/components/nm-icon/animated/minus.tsx +84 -0
  269. package/registry/ui/components/nm-icon/animated/moon.tsx +98 -0
  270. package/registry/ui/components/nm-icon/animated/music.tsx +62 -0
  271. package/registry/ui/components/nm-icon/animated/notebook-pen.tsx +114 -0
  272. package/registry/ui/components/nm-icon/animated/panel-left.tsx +77 -0
  273. package/registry/ui/components/nm-icon/animated/panel-right.tsx +77 -0
  274. package/registry/ui/components/nm-icon/animated/paperclip.tsx +83 -0
  275. package/registry/ui/components/nm-icon/animated/pause.tsx +116 -0
  276. package/registry/ui/components/nm-icon/animated/pencil.tsx +70 -0
  277. package/registry/ui/components/nm-icon/animated/pin-off.tsx +102 -0
  278. package/registry/ui/components/nm-icon/animated/pin.tsx +124 -0
  279. package/registry/ui/components/nm-icon/animated/plane-takeoff.tsx +62 -0
  280. package/registry/ui/components/nm-icon/animated/play.tsx +74 -0
  281. package/registry/ui/components/nm-icon/animated/plug-zap.tsx +117 -0
  282. package/registry/ui/components/nm-icon/animated/plus.tsx +122 -0
  283. package/registry/ui/components/nm-icon/animated/radio.tsx +137 -0
  284. package/registry/ui/components/nm-icon/animated/refresh-cw.tsx +103 -0
  285. package/registry/ui/components/nm-icon/animated/rocket.tsx +93 -0
  286. package/registry/ui/components/nm-icon/animated/rotate-ccw.tsx +87 -0
  287. package/registry/ui/components/nm-icon/animated/rotate-cw.tsx +98 -0
  288. package/registry/ui/components/nm-icon/animated/save.tsx +58 -0
  289. package/registry/ui/components/nm-icon/animated/search.tsx +90 -0
  290. package/registry/ui/components/nm-icon/animated/send.tsx +87 -0
  291. package/registry/ui/components/nm-icon/animated/settings.tsx +92 -0
  292. package/registry/ui/components/nm-icon/animated/share-2.tsx +148 -0
  293. package/registry/ui/components/nm-icon/animated/share.tsx +76 -0
  294. package/registry/ui/components/nm-icon/animated/shield-check.tsx +70 -0
  295. package/registry/ui/components/nm-icon/animated/shopping-bag.tsx +71 -0
  296. package/registry/ui/components/nm-icon/animated/sliders-horizontal.tsx +243 -0
  297. package/registry/ui/components/nm-icon/animated/sparkles.tsx +227 -0
  298. package/registry/ui/components/nm-icon/animated/star.tsx +86 -0
  299. package/registry/ui/components/nm-icon/animated/sun.tsx +148 -0
  300. package/registry/ui/components/nm-icon/animated/terminal.tsx +71 -0
  301. package/registry/ui/components/nm-icon/animated/trash-2.tsx +136 -0
  302. package/registry/ui/components/nm-icon/animated/trash.tsx +94 -0
  303. package/registry/ui/components/nm-icon/animated/trending-up.tsx +75 -0
  304. package/registry/ui/components/nm-icon/animated/triangle-alert.tsx +75 -0
  305. package/registry/ui/components/nm-icon/animated/trophy.tsx +62 -0
  306. package/registry/ui/components/nm-icon/animated/type.tsx +106 -0
  307. package/registry/ui/components/nm-icon/animated/upload.tsx +94 -0
  308. package/registry/ui/components/nm-icon/animated/user-round.tsx +84 -0
  309. package/registry/ui/components/nm-icon/animated/user.tsx +84 -0
  310. package/registry/ui/components/nm-icon/animated/users-round.tsx +156 -0
  311. package/registry/ui/components/nm-icon/animated/users.tsx +156 -0
  312. package/registry/ui/components/nm-icon/animated/volume-2.tsx +119 -0
  313. package/registry/ui/components/nm-icon/animated/wand-sparkles.tsx +111 -0
  314. package/registry/ui/components/nm-icon/animated/x.tsx +122 -0
  315. package/registry/ui/components/nm-icon/animated/zap.tsx +86 -0
  316. package/registry/ui/components/nm-icon/animated/zoom-in.tsx +66 -0
  317. package/registry/ui/components/nm-icon/animated/zoom-out.tsx +85 -0
  318. package/registry/ui/components/nm-icon/nm-icon.css +0 -1
  319. package/registry/ui/components/nm-icon/nm-icon.tsx +20 -5
  320. package/registry/ui/components/nm-icon/nm-lucide-extended.tsx +284 -0
  321. package/registry/ui/components/nm-icon/nm-lucide.tsx +55 -5
  322. package/registry/ui/components/nm-input/nm-input.css +1 -0
  323. package/registry/ui/components/nm-menubar/nm-menubar.tsx +2 -2
  324. package/registry/ui/components/nm-panel/nm-panel.css +7 -0
  325. package/registry/ui/components/nm-panel/nm-panel.tsx +19 -2
  326. package/registry/ui/components/nm-popover/nm-popover.tsx +4 -3
  327. package/registry/ui/components/nm-progress/nm-progress.tsx +1 -1
  328. package/registry/ui/components/nm-radio-group/nm-radio-group.tsx +2 -2
  329. package/registry/ui/components/nm-select/nm-select.tsx +1 -1
  330. package/registry/ui/components/nm-skeleton/nm-skeleton.css +17 -0
  331. package/registry/ui/components/nm-skeleton/nm-skeleton.tsx +7 -0
  332. package/registry/ui/components/nm-slider/nm-slider.tsx +1 -1
  333. package/registry/ui/components/nm-small-icon-button/nm-small-icon-button.tsx +3 -3
  334. package/registry/ui/components/nm-spinner/nm-spinner.css +22 -0
  335. package/registry/ui/components/nm-spinner/nm-spinner.tsx +7 -0
  336. package/registry/ui/components/nm-switch/nm-switch.css +20 -3
  337. package/registry/ui/components/nm-switch/nm-switch.tsx +1 -1
  338. package/registry/ui/components/nm-tabs/nm-tabs.css +4 -0
  339. package/registry/ui/components/nm-tabs/nm-tabs.tsx +19 -0
  340. package/registry/ui/components/nm-textarea/nm-textarea.css +1 -0
  341. package/registry/ui/components/nm-toggle/nm-toggle.tsx +1 -1
  342. package/registry/ui/components/nm-toolbar/nm-toolbar.tsx +4 -1
  343. package/registry/ui/components/nm-tooltip/nm-tooltip.tsx +1 -1
  344. package/registry/ui/components/nm-voice-orb/nm-voice-orb.css +17 -0
  345. package/registry/ui/components/nm-voice-orb/nm-voice-orb.tsx +112 -0
  346. package/registry/ui/components/nm-voice-orb/use-nm-voice-orb-theme.ts +34 -0
  347. package/registry/ui/components/nordman-icon-button/nordman-icon-button.tsx +7 -3
  348. package/registry/ui/config/nm-button.ts +6 -0
  349. package/registry/ui/config/nm-composer.ts +12 -0
  350. package/registry/ui/config/nm-icons-extended.ts +141 -0
  351. package/registry/ui/config/nm-icons.ts +6 -2
  352. package/registry/ui/config/nm-voice-orb.ts +50 -0
  353. package/registry/ui/lib/nm-motion.ts +30 -0
  354. package/registry/ui/styles/tokens.css +7 -0
@@ -1,4 +1,16 @@
1
1
  import {
2
+ Archive,
3
+ Camera,
4
+ CloudUpload,
5
+ Download,
6
+ Lock,
7
+ LogOut,
8
+ Minimize,
9
+ Pencil,
10
+ Pin,
11
+ Share,
12
+ Trash2,
13
+ Volume2,
2
14
  AppWindowMac,
3
15
  ArrowLeft,
4
16
  ArrowRight,
@@ -72,15 +84,29 @@ import {
72
84
  X,
73
85
  type LucideIcon,
74
86
  } from 'lucide-react';
75
- import type { ReactNode } from 'react';
87
+ import type { CSSProperties, ReactNode, SVGAttributes } from 'react';
88
+ import { NM_ANIMATED_ICONS } from './animated/index.ts';
89
+ import { NM_LUCIDE_EXTENDED } from './nm-lucide-extended.tsx';
76
90
  import type { NmIconId } from '../../config/nm-icons.ts';
77
91
 
78
92
  export const NM_LUCIDE_FILLED_IDS = new Set<string>(['stop']);
79
93
 
80
- const LUCIDE: Partial<
94
+ const LUCIDE_CORE: Partial<
81
95
  Record<NmIconId | 'square-dashed-bottom-code' | 'layout-grid' | 'stop' | 'scan' | 'pause', LucideIcon>
82
96
  > = {
83
97
  plus: Plus,
98
+ volume: Volume2,
99
+ camera: Camera,
100
+ minimize: Minimize,
101
+ download: Download,
102
+ publish: CloudUpload,
103
+ share: Share,
104
+ pen: Pencil,
105
+ pin: Pin,
106
+ archive: Archive,
107
+ trash: Trash2,
108
+ logout: LogOut,
109
+ lock: Lock,
84
110
  expand: Maximize2,
85
111
  scan: Scan,
86
112
  reload: RotateCcw,
@@ -156,25 +182,49 @@ const LUCIDE: Partial<
156
182
  rows: Rows2,
157
183
  };
158
184
 
185
+ const LUCIDE: Partial<Record<string, LucideIcon>> = { ...NM_LUCIDE_EXTENDED, ...LUCIDE_CORE };
186
+
187
+ /** True when the icon plays an animation on hover: a lucide icon Animate UI has an animation for. */
188
+ export function isNmIconAnimated(id: string): boolean {
189
+ const Icon = LUCIDE[id];
190
+ return Icon != null && !NM_LUCIDE_FILLED_IDS.has(id) && (Icon as { displayName?: string }).displayName! in NM_ANIMATED_ICONS;
191
+ }
192
+
159
193
  export function hasNmLucide(id: string): boolean {
160
194
  return id in LUCIDE;
161
195
  }
162
196
 
163
- export function NmLucide({ id, className }: { id: string; className?: string }): ReactNode {
164
- const Icon = LUCIDE[id as NmIconId | 'square-dashed-bottom-code' | 'layout-grid' | 'stop'];
197
+ function sized(size: number | string | undefined, style: CSSProperties | undefined): CSSProperties | undefined {
198
+ if (size == null) {
199
+ return style;
200
+ }
201
+ const length = typeof size === 'number' ? `${size}px` : size;
202
+ return { width: length, height: length, ...style };
203
+ }
204
+
205
+ export function NmLucide({ id, className, size, style, ...rest }: { id: string; className?: string; size?: number | string; style?: CSSProperties } & Omit<SVGAttributes<SVGElement>, 'id' | 'className' | 'style'>): ReactNode {
206
+ const Icon = LUCIDE[id];
165
207
  if (Icon == null) {
166
208
  return null;
167
209
  }
168
210
  const filled = NM_LUCIDE_FILLED_IDS.has(id);
169
211
  const baseClass = filled ? 'nm-icon nm-icon--filled' : 'nm-icon';
170
212
  const iconClass = className == null ? baseClass : `${baseClass} ${className}`;
213
+ // An icon Animate UI has an animation for plays it when the control around it is hovered. The rest stay lucide.
214
+ const Animated = filled ? undefined : NM_ANIMATED_ICONS[(Icon as { displayName?: string }).displayName ?? ''];
215
+ if (Animated) {
216
+ return <Animated className={iconClass} data-icon={id} aria-hidden={rest['aria-label'] == null} focusable="false" size={typeof size === 'number' ? size : undefined} {...rest} style={sized(size, style)} />;
217
+ }
171
218
  return (
172
219
  <Icon
173
220
  className={iconClass}
174
221
  data-icon={id}
175
222
  strokeWidth={filled ? undefined : 2}
176
- aria-hidden
223
+ aria-hidden={rest['aria-label'] == null}
177
224
  focusable="false"
225
+ size={typeof size === 'number' ? size : undefined}
226
+ {...(rest as object)}
227
+ style={sized(size, style)}
178
228
  />
179
229
  );
180
230
  }
@@ -1,3 +1,4 @@
1
+
1
2
  .nm-input {
2
3
  box-sizing: border-box;
3
4
  width: 100%;
@@ -1,5 +1,5 @@
1
- import { Menubar as MenubarPrimitive } from '@base-ui/react/menubar';
2
- import { Menu as MenuPrimitive } from '@base-ui/react/menu';
1
+ import { Menubar as MenubarPrimitive } from '../../lib/nm-motion.ts';
2
+ import { Menu as MenuPrimitive } from '../../lib/nm-motion.ts';
3
3
  import { nmClass } from '../../lib/nm-class.ts';
4
4
 
5
5
  /**
@@ -21,6 +21,13 @@
21
21
  background: var(--nm-canvas);
22
22
  }
23
23
 
24
+ .nm-panel-region[data-measure='column'] {
25
+ width: var(--nm-width-column);
26
+ min-width: min(100%, var(--nm-width-column-min));
27
+ max-width: var(--nm-width-column-max);
28
+ margin-inline: auto;
29
+ }
30
+
24
31
  .nm-panel-region__header {
25
32
  display: flex;
26
33
  flex-shrink: 0;
@@ -4,9 +4,26 @@ import { nmClass } from '../../lib/nm-class.ts';
4
4
  export const NM_PANEL_VARIANTS = ['default', 'inset', 'plain'] as const;
5
5
  export type NmPanelVariant = (typeof NM_PANEL_VARIANTS)[number];
6
6
 
7
+ /** `full` fills the room it is given. `column` is a centred share of it, held between a minimum and a maximum width. */
8
+ export const NM_PANEL_MEASURES = ['full', 'column'] as const;
9
+ export type NmPanelMeasure = (typeof NM_PANEL_MEASURES)[number];
10
+
7
11
  /** A region of the app, such as an inspector or a side panel: a header, content that scrolls, and a footer. */
8
- export function NmPanel({ className, variant = 'default', ...props }: ComponentProps<'section'> & { variant?: NmPanelVariant }) {
9
- return <section data-slot="panel" data-variant={variant} className={nmClass('nm-panel-region', className)} {...props} />;
12
+ export function NmPanel({
13
+ className,
14
+ variant = 'default',
15
+ measure = 'full',
16
+ ...props
17
+ }: ComponentProps<'section'> & { variant?: NmPanelVariant; measure?: NmPanelMeasure }) {
18
+ return (
19
+ <section
20
+ data-slot="panel"
21
+ data-variant={variant}
22
+ data-measure={measure}
23
+ className={nmClass('nm-panel-region', className)}
24
+ {...props}
25
+ />
26
+ );
10
27
  }
11
28
 
12
29
  export function NmPanelHeader({ className, ...props }: ComponentProps<'header'>) {
@@ -1,5 +1,5 @@
1
1
  import type { ComponentProps } from 'react';
2
- import { Popover as PopoverPrimitive } from '@base-ui/react/popover';
2
+ import { Popover as PopoverPrimitive } from '../../lib/nm-motion.ts';
3
3
  import { nmClass } from '../../lib/nm-class.ts';
4
4
 
5
5
  export function NmPopover(props: PopoverPrimitive.Root.Props) {
@@ -16,11 +16,12 @@ export function NmPopoverContent({
16
16
  alignOffset = 0,
17
17
  side = 'bottom',
18
18
  sideOffset = 8,
19
+ anchor,
19
20
  ...props
20
- }: PopoverPrimitive.Popup.Props & Pick<PopoverPrimitive.Positioner.Props, 'align' | 'alignOffset' | 'side' | 'sideOffset'>) {
21
+ }: PopoverPrimitive.Popup.Props & Pick<PopoverPrimitive.Positioner.Props, 'align' | 'alignOffset' | 'side' | 'sideOffset' | 'anchor'>) {
21
22
  return (
22
23
  <PopoverPrimitive.Portal>
23
- <PopoverPrimitive.Positioner align={align} alignOffset={alignOffset} side={side} sideOffset={sideOffset} className="nm-popover__positioner">
24
+ <PopoverPrimitive.Positioner anchor={anchor} align={align} alignOffset={alignOffset} side={side} sideOffset={sideOffset} className="nm-popover__positioner">
24
25
  <PopoverPrimitive.Popup data-slot="popover-content" className={nmClass('nm-popover', className as string)} {...props} />
25
26
  </PopoverPrimitive.Positioner>
26
27
  </PopoverPrimitive.Portal>
@@ -1,4 +1,4 @@
1
- import { Progress as ProgressPrimitive } from '@base-ui/react/progress';
1
+ import { Progress as ProgressPrimitive } from '../../lib/nm-motion.ts';
2
2
  import { nmClass } from '../../lib/nm-class.ts';
3
3
 
4
4
  /** Base UI Progress, in the shadcn composition, styled by NIF tokens. */
@@ -1,5 +1,5 @@
1
- import { Radio as RadioPrimitive } from '@base-ui/react/radio';
2
- import { RadioGroup as RadioGroupPrimitive } from '@base-ui/react/radio-group';
1
+ import { Radio as RadioPrimitive } from '../../lib/nm-motion.ts';
2
+ import { RadioGroup as RadioGroupPrimitive } from '../../lib/nm-motion.ts';
3
3
  import { nmClass } from '../../lib/nm-class.ts';
4
4
 
5
5
  export function NmRadioGroup({ className, ...props }: RadioGroupPrimitive.Props) {
@@ -1,5 +1,5 @@
1
1
  import type { ReactNode } from 'react';
2
- import { Select as SelectPrimitive } from '@base-ui/react/select';
2
+ import { Select as SelectPrimitive } from '../../lib/nm-motion.ts';
3
3
  import { nmClass } from '../../lib/nm-class.ts';
4
4
  import { NmIcon } from '../nm-icon/nm-icon.tsx';
5
5
 
@@ -0,0 +1,17 @@
1
+ .nm-skeleton {
2
+ border-radius: var(--nm-radius-md);
3
+ background: var(--nm-panel);
4
+ animation: nm-skeleton-pulse 1.6s ease-in-out infinite;
5
+ }
6
+
7
+ @keyframes nm-skeleton-pulse {
8
+ 50% {
9
+ opacity: 0.5;
10
+ }
11
+ }
12
+
13
+ @media (prefers-reduced-motion: reduce) {
14
+ .nm-skeleton {
15
+ animation: none;
16
+ }
17
+ }
@@ -0,0 +1,7 @@
1
+ import type { ComponentProps, ReactNode } from 'react';
2
+ import { nmClass } from '../../lib/nm-class.ts';
3
+
4
+ /** A pulsing placeholder in the shape of the content that is loading. Size it with a class. */
5
+ export function NmSkeleton({ className, ...props }: ComponentProps<'div'>): ReactNode {
6
+ return <div data-slot="skeleton" className={nmClass('nm-skeleton', className)} {...props} />;
7
+ }
@@ -1,4 +1,4 @@
1
- import { Slider as SliderPrimitive } from '@base-ui/react/slider';
1
+ import { Slider as SliderPrimitive } from '../../lib/nm-motion.ts';
2
2
  import { nmClass } from '../../lib/nm-class.ts';
3
3
 
4
4
  /** `label` and `output` add a caption above the track, set like the caption of a Field. */
@@ -1,4 +1,4 @@
1
- import { forwardRef, type ButtonHTMLAttributes, type ReactNode } from 'react';
1
+ import { forwardRef, type ButtonHTMLAttributes, type MouseEvent, type ReactNode } from 'react';
2
2
  import type { NmIconId } from '../../config/nm-icons.ts';
3
3
  import {
4
4
  NM_SMALL_ICON_BUTTON_TONES,
@@ -22,7 +22,7 @@ export type NmSmallIconButtonProps = {
22
22
  active?: boolean;
23
23
  iconContent?: ReactNode;
24
24
  testId?: string;
25
- onClick?: () => void;
25
+ onClick?: (event: MouseEvent<HTMLButtonElement>) => void;
26
26
  } & Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'onClick' | 'aria-label' | 'children'>;
27
27
 
28
28
  export const NmSmallIconButton = forwardRef<HTMLButtonElement, NmSmallIconButtonProps>(
@@ -56,7 +56,7 @@ export const NmSmallIconButton = forwardRef<HTMLButtonElement, NmSmallIconButton
56
56
  if (disabled === true) {
57
57
  return;
58
58
  }
59
- onClick?.();
59
+ onClick?.(event);
60
60
  }}
61
61
  >
62
62
  {iconContent}
@@ -0,0 +1,22 @@
1
+ .nm-spinner {
2
+ display: inline-block;
3
+ box-sizing: border-box;
4
+ width: var(--nm-icon);
5
+ height: var(--nm-icon);
6
+ border: 2px solid var(--nm-line-strong);
7
+ border-top-color: var(--nm-fg);
8
+ border-radius: 50%;
9
+ animation: nm-spinner-turn 0.8s linear infinite;
10
+ }
11
+
12
+ @keyframes nm-spinner-turn {
13
+ to {
14
+ transform: rotate(360deg);
15
+ }
16
+ }
17
+
18
+ @media (prefers-reduced-motion: reduce) {
19
+ .nm-spinner {
20
+ animation-duration: 2.4s;
21
+ }
22
+ }
@@ -0,0 +1,7 @@
1
+ import type { ComponentProps, ReactNode } from 'react';
2
+ import { nmClass } from '../../lib/nm-class.ts';
3
+
4
+ /** A ring that turns while something loads. It is the themed icon size; a class or style sets another. */
5
+ export function NmSpinner({ className, ...props }: ComponentProps<'span'>): ReactNode {
6
+ return <span role="status" aria-live="polite" data-slot="spinner" className={nmClass('nm-spinner', className)} {...props} />;
7
+ }
@@ -7,11 +7,16 @@
7
7
  width: calc(var(--nm-switch-thumb) * 2);
8
8
  height: var(--nm-switch-thumb);
9
9
  padding: var(--nm-space-2);
10
- border: 0;
10
+ border: var(--nm-rule) solid var(--nm-line-strong);
11
11
  border-radius: var(--toggle-r);
12
12
  background: var(--nm-panel);
13
13
  cursor: pointer;
14
- transition: background 0.15s;
14
+ user-select: none;
15
+ -webkit-user-select: none;
16
+ touch-action: none;
17
+ transition:
18
+ background 0.15s,
19
+ border-color 0.15s;
15
20
  }
16
21
 
17
22
  .nm-switch:focus-visible {
@@ -20,6 +25,7 @@
20
25
  }
21
26
 
22
27
  .nm-switch[data-checked] {
28
+ border-color: var(--nm-fg);
23
29
  background: var(--nm-fg);
24
30
  }
25
31
 
@@ -30,12 +36,16 @@
30
36
 
31
37
  .nm-switch__thumb {
32
38
  display: block;
39
+ flex-shrink: 0;
40
+ pointer-events: none;
33
41
  width: var(--nm-switch-thumb);
34
42
  height: var(--nm-switch-thumb);
35
43
  border-radius: 50%;
36
44
  background: var(--nm-fg);
45
+ transform: translateX(0);
46
+ will-change: transform;
37
47
  transition:
38
- transform 0.15s,
48
+ transform 0.15s var(--ease, ease),
39
49
  background 0.15s;
40
50
  }
41
51
 
@@ -43,3 +53,10 @@
43
53
  transform: translateX(var(--nm-switch-thumb));
44
54
  background: var(--nm-canvas);
45
55
  }
56
+
57
+ @media (prefers-reduced-motion: reduce) {
58
+ .nm-switch,
59
+ .nm-switch__thumb {
60
+ transition: none;
61
+ }
62
+ }
@@ -1,4 +1,4 @@
1
- import { Switch as SwitchPrimitive } from '@base-ui/react/switch';
1
+ import { Switch as SwitchPrimitive } from '../../lib/nm-motion.ts';
2
2
  import { nmClass } from '../../lib/nm-class.ts';
3
3
 
4
4
  export function NmSwitch({ className, ...props }: SwitchPrimitive.Root.Props) {
@@ -26,3 +26,7 @@
26
26
  border-bottom-color: var(--nm-fg);
27
27
  color: var(--nm-fg);
28
28
  }
29
+
30
+ .nm-tabs__panel {
31
+ outline: none;
32
+ }
@@ -1,4 +1,6 @@
1
1
  import type { ReactNode } from 'react';
2
+ import { Tabs as TabsPrimitive } from '../../lib/nm-motion.ts';
3
+ import { nmClass } from '../../lib/nm-class.ts';
2
4
  import { NM_TABS_CLASSES, type NmTabItem } from '../../config/nm-tabs.ts';
3
5
  import { createLogger } from '../../kit-logger.ts';
4
6
 
@@ -30,3 +32,20 @@ export function NmTabs(props: NmTabsProps): ReactNode {
30
32
  </div>
31
33
  );
32
34
  }
35
+
36
+ /** The parts of a tab set, for a view that lays out its own list and panels. */
37
+ export function NmTabsRoot(props: TabsPrimitive.Root.Props): ReactNode {
38
+ return <TabsPrimitive.Root data-slot="tabs" {...props} />;
39
+ }
40
+
41
+ export function NmTabsList({ className, ...props }: TabsPrimitive.List.Props): ReactNode {
42
+ return <TabsPrimitive.List data-slot="tabs-list" className={nmClass(NM_TABS_CLASSES.root, className as string)} {...props} />;
43
+ }
44
+
45
+ export function NmTabsTrigger({ className, ...props }: TabsPrimitive.Tab.Props): ReactNode {
46
+ return <TabsPrimitive.Tab data-slot="tabs-trigger" className={nmClass(NM_TABS_CLASSES.item, className as string)} {...props} />;
47
+ }
48
+
49
+ export function NmTabsContent({ className, ...props }: TabsPrimitive.Panel.Props): ReactNode {
50
+ return <TabsPrimitive.Panel data-slot="tabs-content" className={nmClass('nm-tabs__panel', className as string)} {...props} />;
51
+ }
@@ -1,3 +1,4 @@
1
+
1
2
  .nm-textarea {
2
3
  box-sizing: border-box;
3
4
  display: block;
@@ -1,4 +1,4 @@
1
- import { Toggle as TogglePrimitive } from '@base-ui/react/toggle';
1
+ import { Toggle as TogglePrimitive } from '../../lib/nm-motion.ts';
2
2
  import { nmClass } from '../../lib/nm-class.ts';
3
3
 
4
4
  export const NM_TOGGLE_VARIANTS = ['default', 'outline'] as const;
@@ -4,6 +4,7 @@ import { nmClass } from '../../lib/nm-class.ts';
4
4
  import type { NmIconId } from '../../config/nm-icons.ts';
5
5
  import { NM_BUTTON_TONES, nmButtonClassName, type NmButtonTone } from '../../config/nm-button.ts';
6
6
  import { nmIconButtonClassName, type NmIconButtonTone } from '../../config/nm-icon-button.ts';
7
+ import { AnimateIcon } from '../../lib/nm-motion.ts';
7
8
  import { NmIcon } from '../nm-icon/nm-icon.tsx';
8
9
 
9
10
  /** Base UI Toolbar, in the shadcn composition, styled by NIF tokens. One tab stop, arrow keys move between controls. */
@@ -40,7 +41,7 @@ export function NmToolbarButton({
40
41
  }: Omit<ToolbarPrimitive.Button.Props, 'children'> & { icon?: NmIconId; label: string; tone?: NmIconButtonTone | NmButtonTone; pressed?: boolean; children?: ReactNode }) {
41
42
  const withText = children != null;
42
43
  const buttonTone = (Object.values(NM_BUTTON_TONES) as string[]).includes(tone ?? '') ? (tone as NmButtonTone) : 'muted';
43
- return (
44
+ const button = (
44
45
  <ToolbarPrimitive.Button
45
46
  data-slot="toolbar-button"
46
47
  aria-label={label}
@@ -52,6 +53,8 @@ export function NmToolbarButton({
52
53
  {withText ? <span className="nm-button__label">{children}</span> : null}
53
54
  </ToolbarPrimitive.Button>
54
55
  );
56
+ // Always wrapped, so the control keeps its place in the tree when its icon is added or removed.
57
+ return <AnimateIcon animateOnHover={Boolean(icon)} asChild>{button}</AnimateIcon>;
55
58
  }
56
59
 
57
60
  /** A field in the bar that takes the free width, such as an address. */
@@ -1,4 +1,4 @@
1
- import { Tooltip as TooltipPrimitive } from '@base-ui/react/tooltip';
1
+ import { Tooltip as TooltipPrimitive } from '../../lib/nm-motion.ts';
2
2
  import { nmClass } from '../../lib/nm-class.ts';
3
3
 
4
4
  export function NmTooltipProvider({ delay = 0, ...props }: TooltipPrimitive.Provider.Props) {
@@ -0,0 +1,17 @@
1
+ .nm-voice-orb {
2
+ --orb-ui-size: var(--nm-icon-control);
3
+ flex-shrink: 0;
4
+ border-radius: var(--nm-control-radius);
5
+ overflow: hidden;
6
+ }
7
+
8
+ .nm-voice-orb .orb-ui__content {
9
+ overflow: hidden;
10
+ border-radius: inherit;
11
+ }
12
+
13
+ .nm-voice-orb:focus-visible {
14
+ outline: var(--nm-rule) solid var(--nm-fg);
15
+ outline-offset: var(--nm-space-2);
16
+ }
17
+
@@ -0,0 +1,112 @@
1
+ import { useMemo, type ReactNode } from 'react';
2
+ import { Orb, type OrbAdapter, type OrbSignal, type OrbTheme } from 'orb-ui';
3
+ import {
4
+ NM_VOICE_ORB_CLASSES,
5
+ NM_VOICE_ORB_SIZE,
6
+ toOrbUiState,
7
+ type NmVoiceOrbAgentState,
8
+ } from '../../config/nm-voice-orb.ts';
9
+ import { useNmVoiceOrbTheme } from './use-nm-voice-orb-theme.ts';
10
+ import { createLogger } from '../../kit-logger.ts';
11
+
12
+ const logger = createLogger('interface-nm-voice-orb');
13
+
14
+ export type NmVoiceOrbProps = {
15
+ adapter?: OrbAdapter;
16
+ signal?: OrbSignal;
17
+ agentState?: NmVoiceOrbAgentState;
18
+ theme?: OrbTheme;
19
+ size?: number;
20
+ inputVolume?: number;
21
+ outputVolume?: number;
22
+ getInputVolume?: () => number;
23
+ getOutputVolume?: () => number;
24
+ label?: string;
25
+ testId?: string;
26
+ onClick?: () => void;
27
+ };
28
+
29
+ function clamp01(value: number | undefined): number {
30
+ if (typeof value !== 'number' || !Number.isFinite(value)) {
31
+ return 0;
32
+ }
33
+ return Math.min(1, Math.max(0, value));
34
+ }
35
+
36
+ function ensureMatchMedia(): void {
37
+ if (typeof window === 'undefined' || typeof window.matchMedia === 'function') {
38
+ return;
39
+ }
40
+ Object.defineProperty(window, 'matchMedia', {
41
+ writable: true,
42
+ configurable: true,
43
+ value: (query: string) => ({
44
+ matches: false,
45
+ media: query,
46
+ onchange: null,
47
+ addListener: () => {},
48
+ removeListener: () => {},
49
+ addEventListener: () => {},
50
+ removeEventListener: () => {},
51
+ dispatchEvent: () => false,
52
+ }),
53
+ });
54
+ }
55
+
56
+ export function NmVoiceOrb(props: NmVoiceOrbProps): ReactNode {
57
+ ensureMatchMedia();
58
+ const appearanceTheme = useNmVoiceOrbTheme();
59
+
60
+ const {
61
+ signal,
62
+ adapter,
63
+ inputVolume: propInputVolume,
64
+ outputVolume: propOutputVolume,
65
+ getInputVolume,
66
+ getOutputVolume,
67
+ } = props;
68
+ const mappedState = toOrbUiState(props.agentState);
69
+ const fallbackSignal = useMemo<OrbSignal | undefined>(() => {
70
+ if (signal) {
71
+ return signal;
72
+ }
73
+ if (adapter) {
74
+ return undefined;
75
+ }
76
+ const inputVolume = clamp01(propInputVolume ?? getInputVolume?.() ?? 0);
77
+ const outputVolume = clamp01(
78
+ propOutputVolume ?? getOutputVolume?.() ?? (mappedState === 'speaking' ? 0.72 : 0),
79
+ );
80
+ return {
81
+ state: mappedState,
82
+ inputVolume,
83
+ outputVolume,
84
+ };
85
+ }, [
86
+ mappedState,
87
+ adapter,
88
+ getInputVolume,
89
+ getOutputVolume,
90
+ propInputVolume,
91
+ propOutputVolume,
92
+ signal,
93
+ ]);
94
+
95
+ logger.debug('render', 'orb-ui', { state: mappedState });
96
+
97
+ return (
98
+ <Orb
99
+ adapter={props.adapter}
100
+ signal={fallbackSignal}
101
+ state={props.agentState != null ? mappedState : undefined}
102
+ theme={props.theme ?? appearanceTheme}
103
+ size={props.size ?? NM_VOICE_ORB_SIZE}
104
+ className={NM_VOICE_ORB_CLASSES.root}
105
+ aria-label={props.label ?? 'Avsluta röstläge'}
106
+ onStart={props.onClick}
107
+ onStop={props.onClick}
108
+ data-testid={props.testId ?? 'voice-orb'}
109
+ data-agent-state={props.agentState ?? mappedState}
110
+ />
111
+ );
112
+ }
@@ -0,0 +1,34 @@
1
+ import { useEffect, useMemo, useState } from 'react';
2
+ import { defineOrbTheme, type OrbTheme } from 'orb-ui';
3
+ import { deriveCloudOrbAppearance } from '../../lib/appearance/cloudOrbAppearance.ts';
4
+ import { NM_APPEARANCE_APPLIED_EVENT } from '../../config/nm-appearance.ts';
5
+ import { NM_ORB_UI_THEME } from '../../config/nm-voice-orb.ts';
6
+
7
+ function readAppearanceChannel(name: string, fallback: string): string {
8
+ if (typeof document === 'undefined') {
9
+ return fallback;
10
+ }
11
+ const value = getComputedStyle(document.documentElement).getPropertyValue(name).trim();
12
+ return value.length > 0 ? value : fallback;
13
+ }
14
+
15
+ export function useNmVoiceOrbTheme(): OrbTheme {
16
+ const [revision, setRevision] = useState(0);
17
+
18
+ useEffect(() => {
19
+ const sync = () => setRevision((value) => value + 1);
20
+ window.addEventListener(NM_APPEARANCE_APPLIED_EVENT, sync);
21
+ return () => window.removeEventListener(NM_APPEARANCE_APPLIED_EVENT, sync);
22
+ }, []);
23
+
24
+ return useMemo(() => {
25
+ void revision;
26
+ const accent = readAppearanceChannel('--nm-accent', '#D2BB9C');
27
+ const canvas = readAppearanceChannel('--nm-canvas', '#000000');
28
+ const fg = readAppearanceChannel('--nm-fg', '#FFFFFF');
29
+ return defineOrbTheme({
30
+ ...NM_ORB_UI_THEME,
31
+ appearance: deriveCloudOrbAppearance(accent, canvas, fg),
32
+ });
33
+ }, [revision]);
34
+ }
@@ -7,6 +7,7 @@ import {
7
7
  } from '../../config/nm-icon-button.ts';
8
8
  import { NORDMAN_CHROME_ICONS, type NordmanChromeIconId } from '../../config/nordman-chrome.ts';
9
9
  import { createLogger } from '../../kit-logger.ts';
10
+ import { AnimateIcon, Button as MotionButton } from '../../lib/nm-motion.ts';
10
11
  import { NmIcon } from '../nm-icon/nm-icon.tsx';
11
12
 
12
13
  const logger = createLogger('interface-nordman-icon-button');
@@ -174,11 +175,14 @@ export const NordmanIconButton = forwardRef<HTMLButtonElement, NordmanIconButton
174
175
  </a>
175
176
  );
176
177
  }
177
- return (
178
- <button {...rest} {...shared} ref={ref} type={type ?? 'button'}>
178
+ const button = (
179
+ <MotionButton {...(rest as object)} {...shared} ref={ref} type={type ?? 'button'} hoverScale={rest.disabled ? 1 : 1.08} tapScale={rest.disabled ? 1 : 0.92}>
179
180
  {face}
180
- </button>
181
+ </MotionButton>
181
182
  );
183
+ // The icon plays its animation when the button is hovered.
184
+ // Always wrapped: a control that swaps its glyph for an image must stay the same element.
185
+ return <AnimateIcon animateOnHover={glyph != null} asChild>{button}</AnimateIcon>;
182
186
  },
183
187
  );
184
188
 
@@ -4,6 +4,10 @@ export const NM_BUTTON_TONES = {
4
4
  muted: 'muted',
5
5
  panel: 'panel',
6
6
  danger: 'danger',
7
+ /** No fill until hovered. For actions that sit inside other content. */
8
+ ghost: 'ghost',
9
+ /** Text with an underline on hover. */
10
+ link: 'link',
7
11
  } as const;
8
12
 
9
13
  export type NmButtonTone = (typeof NM_BUTTON_TONES)[keyof typeof NM_BUTTON_TONES];
@@ -24,6 +28,8 @@ export const NM_BUTTON_CLASSES = {
24
28
  muted: 'nm-button--muted',
25
29
  panel: 'nm-button--panel',
26
30
  danger: 'nm-button--danger',
31
+ ghost: 'nm-button--ghost',
32
+ link: 'nm-button--link',
27
33
  default: 'nm-button--default',
28
34
  compact: 'nm-button--compact',
29
35
  chip: 'nm-button--chip',