@nordmanai/nif 0.2.2 → 0.2.4

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 (358) hide show
  1. package/README.md +1 -1
  2. package/dist/index.d.ts +6 -0
  3. package/dist/index.js +31353 -11545
  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-field/nm-field.d.ts +2 -0
  19. package/dist/ui/components/nm-hover-card/nm-hover-card.d.ts +1 -1
  20. package/dist/ui/components/nm-icon/animated/activity.d.ts +72 -0
  21. package/dist/ui/components/nm-icon/animated/archive.d.ts +31 -0
  22. package/dist/ui/components/nm-icon/animated/arrow-down.d.ts +128 -0
  23. package/dist/ui/components/nm-icon/animated/arrow-left.d.ts +128 -0
  24. package/dist/ui/components/nm-icon/animated/arrow-right.d.ts +128 -0
  25. package/dist/ui/components/nm-icon/animated/arrow-up-down.d.ts +119 -0
  26. package/dist/ui/components/nm-icon/animated/arrow-up.d.ts +128 -0
  27. package/dist/ui/components/nm-icon/animated/audio-lines.d.ts +99 -0
  28. package/dist/ui/components/nm-icon/animated/bookmark-plus.d.ts +35 -0
  29. package/dist/ui/components/nm-icon/animated/bookmark.d.ts +52 -0
  30. package/dist/ui/components/nm-icon/animated/bot.d.ts +90 -0
  31. package/dist/ui/components/nm-icon/animated/calendar-days.d.ts +87 -0
  32. package/dist/ui/components/nm-icon/animated/calendar.d.ts +36 -0
  33. package/dist/ui/components/nm-icon/animated/camera.d.ts +50 -0
  34. package/dist/ui/components/nm-icon/animated/chart-column-increasing.d.ts +37 -0
  35. package/dist/ui/components/nm-icon/animated/chart-column.d.ts +86 -0
  36. package/dist/ui/components/nm-icon/animated/chart-line.d.ts +51 -0
  37. package/dist/ui/components/nm-icon/animated/check-check.d.ts +50 -0
  38. package/dist/ui/components/nm-icon/animated/check.d.ts +25 -0
  39. package/dist/ui/components/nm-icon/animated/chevron-down.d.ts +39 -0
  40. package/dist/ui/components/nm-icon/animated/chevron-left.d.ts +39 -0
  41. package/dist/ui/components/nm-icon/animated/chevron-right.d.ts +39 -0
  42. package/dist/ui/components/nm-icon/animated/chevron-up.d.ts +39 -0
  43. package/dist/ui/components/nm-icon/animated/circle-alert.d.ts +23 -0
  44. package/dist/ui/components/nm-icon/animated/circle-check-big.d.ts +26 -0
  45. package/dist/ui/components/nm-icon/animated/circle-check.d.ts +26 -0
  46. package/dist/ui/components/nm-icon/animated/circle-plus.d.ts +44 -0
  47. package/dist/ui/components/nm-icon/animated/circle-question-mark.d.ts +22 -0
  48. package/dist/ui/components/nm-icon/animated/circle-x.d.ts +44 -0
  49. package/dist/ui/components/nm-icon/animated/clock-3.d.ts +44 -0
  50. package/dist/ui/components/nm-icon/animated/clock-4.d.ts +44 -0
  51. package/dist/ui/components/nm-icon/animated/clock.d.ts +44 -0
  52. package/dist/ui/components/nm-icon/animated/cloud-sun.d.ts +9 -0
  53. package/dist/ui/components/nm-icon/animated/cloud-upload.d.ts +45 -0
  54. package/dist/ui/components/nm-icon/animated/copy.d.ts +135 -0
  55. package/dist/ui/components/nm-icon/animated/download.d.ts +45 -0
  56. package/dist/ui/components/nm-icon/animated/ellipsis.d.ts +141 -0
  57. package/dist/ui/components/nm-icon/animated/external-link.d.ts +49 -0
  58. package/dist/ui/components/nm-icon/animated/eye-off.d.ts +23 -0
  59. package/dist/ui/components/nm-icon/animated/eye.d.ts +39 -0
  60. package/dist/ui/components/nm-icon/animated/fast-forward.d.ts +34 -0
  61. package/dist/ui/components/nm-icon/animated/file-clock.d.ts +48 -0
  62. package/dist/ui/components/nm-icon/animated/file-text.d.ts +56 -0
  63. package/dist/ui/components/nm-icon/animated/file.d.ts +67 -0
  64. package/dist/ui/components/nm-icon/animated/folder.d.ts +92 -0
  65. package/dist/ui/components/nm-icon/animated/gauge.d.ts +47 -0
  66. package/dist/ui/components/nm-icon/animated/globe.d.ts +21 -0
  67. package/dist/ui/components/nm-icon/animated/graduation-cap.d.ts +41 -0
  68. package/dist/ui/components/nm-icon/animated/headphones.d.ts +50 -0
  69. package/dist/ui/components/nm-icon/animated/image.d.ts +40 -0
  70. package/dist/ui/components/nm-icon/animated/index.d.ts +2 -0
  71. package/dist/ui/components/nm-icon/animated/info.d.ts +37 -0
  72. package/dist/ui/components/nm-icon/animated/key-round.d.ts +37 -0
  73. package/dist/ui/components/nm-icon/animated/layers.d.ts +61 -0
  74. package/dist/ui/components/nm-icon/animated/layout-dashboard.d.ts +131 -0
  75. package/dist/ui/components/nm-icon/animated/library-big.d.ts +29 -0
  76. package/dist/ui/components/nm-icon/animated/lightbulb.d.ts +51 -0
  77. package/dist/ui/components/nm-icon/animated/link.d.ts +193 -0
  78. package/dist/ui/components/nm-icon/animated/list-filter.d.ts +13 -0
  79. package/dist/ui/components/nm-icon/animated/list.d.ts +111 -0
  80. package/dist/ui/components/nm-icon/animated/loader-circle.d.ts +24 -0
  81. package/dist/ui/components/nm-icon/animated/lock.d.ts +94 -0
  82. package/dist/ui/components/nm-icon/animated/log-out.d.ts +45 -0
  83. package/dist/ui/components/nm-icon/animated/mail.d.ts +100 -0
  84. package/dist/ui/components/nm-icon/animated/map-pin.d.ts +67 -0
  85. package/dist/ui/components/nm-icon/animated/menu.d.ts +57 -0
  86. package/dist/ui/components/nm-icon/animated/message-circle-dashed.d.ts +34 -0
  87. package/dist/ui/components/nm-icon/animated/message-circle.d.ts +23 -0
  88. package/dist/ui/components/nm-icon/animated/message-square-plus.d.ts +49 -0
  89. package/dist/ui/components/nm-icon/animated/message-square-text.d.ts +104 -0
  90. package/dist/ui/components/nm-icon/animated/message-square.d.ts +23 -0
  91. package/dist/ui/components/nm-icon/animated/mic-off.d.ts +23 -0
  92. package/dist/ui/components/nm-icon/animated/mic.d.ts +37 -0
  93. package/dist/ui/components/nm-icon/animated/minimize.d.ts +139 -0
  94. package/dist/ui/components/nm-icon/animated/minus.d.ts +54 -0
  95. package/dist/ui/components/nm-icon/animated/moon.d.ts +62 -0
  96. package/dist/ui/components/nm-icon/animated/music.d.ts +22 -0
  97. package/dist/ui/components/nm-icon/animated/notebook-pen.d.ts +41 -0
  98. package/dist/ui/components/nm-icon/animated/panel-left.d.ts +29 -0
  99. package/dist/ui/components/nm-icon/animated/panel-right.d.ts +29 -0
  100. package/dist/ui/components/nm-icon/animated/paperclip.d.ts +49 -0
  101. package/dist/ui/components/nm-icon/animated/pause.d.ts +59 -0
  102. package/dist/ui/components/nm-icon/animated/pencil.d.ts +25 -0
  103. package/dist/ui/components/nm-icon/animated/pin-off.d.ts +44 -0
  104. package/dist/ui/components/nm-icon/animated/pin.d.ts +85 -0
  105. package/dist/ui/components/nm-icon/animated/plane-takeoff.d.ts +25 -0
  106. package/dist/ui/components/nm-icon/animated/play.d.ts +43 -0
  107. package/dist/ui/components/nm-icon/animated/plug-zap.d.ts +49 -0
  108. package/dist/ui/components/nm-icon/animated/plus.d.ts +95 -0
  109. package/dist/ui/components/nm-icon/animated/radio.d.ts +24 -0
  110. package/dist/ui/components/nm-icon/animated/refresh-cw.d.ts +55 -0
  111. package/dist/ui/components/nm-icon/animated/rocket.d.ts +48 -0
  112. package/dist/ui/components/nm-icon/animated/rotate-ccw.d.ts +51 -0
  113. package/dist/ui/components/nm-icon/animated/rotate-cw.d.ts +71 -0
  114. package/dist/ui/components/nm-icon/animated/save.d.ts +21 -0
  115. package/dist/ui/components/nm-icon/animated/search.d.ts +42 -0
  116. package/dist/ui/components/nm-icon/animated/send.d.ts +39 -0
  117. package/dist/ui/components/nm-icon/animated/settings.d.ts +39 -0
  118. package/dist/ui/components/nm-icon/animated/share-2.d.ts +122 -0
  119. package/dist/ui/components/nm-icon/animated/share.d.ts +40 -0
  120. package/dist/ui/components/nm-icon/animated/shield-check.d.ts +36 -0
  121. package/dist/ui/components/nm-icon/animated/shopping-bag.d.ts +35 -0
  122. package/dist/ui/components/nm-icon/animated/sliders-horizontal.d.ts +239 -0
  123. package/dist/ui/components/nm-icon/animated/sparkles.d.ts +205 -0
  124. package/dist/ui/components/nm-icon/animated/star.d.ts +48 -0
  125. package/dist/ui/components/nm-icon/animated/sun.d.ts +9 -0
  126. package/dist/ui/components/nm-icon/animated/terminal.d.ts +22 -0
  127. package/dist/ui/components/nm-icon/animated/trash-2.d.ts +61 -0
  128. package/dist/ui/components/nm-icon/animated/trash.d.ts +37 -0
  129. package/dist/ui/components/nm-icon/animated/trending-up.d.ts +40 -0
  130. package/dist/ui/components/nm-icon/animated/triangle-alert.d.ts +37 -0
  131. package/dist/ui/components/nm-icon/animated/trophy.d.ts +23 -0
  132. package/dist/ui/components/nm-icon/animated/type.d.ts +57 -0
  133. package/dist/ui/components/nm-icon/animated/upload.d.ts +49 -0
  134. package/dist/ui/components/nm-icon/animated/user-round.d.ts +33 -0
  135. package/dist/ui/components/nm-icon/animated/user.d.ts +33 -0
  136. package/dist/ui/components/nm-icon/animated/users-round.d.ts +93 -0
  137. package/dist/ui/components/nm-icon/animated/users.d.ts +93 -0
  138. package/dist/ui/components/nm-icon/animated/volume-2.d.ts +10 -0
  139. package/dist/ui/components/nm-icon/animated/wand-sparkles.d.ts +71 -0
  140. package/dist/ui/components/nm-icon/animated/x.d.ts +95 -0
  141. package/dist/ui/components/nm-icon/animated/zap.d.ts +65 -0
  142. package/dist/ui/components/nm-icon/animated/zoom-in.d.ts +35 -0
  143. package/dist/ui/components/nm-icon/animated/zoom-out.d.ts +53 -0
  144. package/dist/ui/components/nm-icon/nm-icon.d.ts +6 -3
  145. package/dist/ui/components/nm-icon/nm-lucide-extended.d.ts +3 -0
  146. package/dist/ui/components/nm-icon/nm-lucide.d.ts +7 -3
  147. package/dist/ui/components/nm-menubar/nm-menubar.d.ts +2 -2
  148. package/dist/ui/components/nm-panel/nm-panel.d.ts +5 -1
  149. package/dist/ui/components/nm-popover/nm-popover.d.ts +2 -2
  150. package/dist/ui/components/nm-progress/nm-progress.d.ts +1 -1
  151. package/dist/ui/components/nm-radio-group/nm-radio-group.d.ts +2 -2
  152. package/dist/ui/components/nm-select/nm-select.d.ts +1 -1
  153. package/dist/ui/components/nm-skeleton/nm-skeleton.d.ts +3 -0
  154. package/dist/ui/components/nm-slider/nm-slider.d.ts +1 -1
  155. package/dist/ui/components/nm-small-icon-button/nm-small-icon-button.d.ts +3 -3
  156. package/dist/ui/components/nm-spinner/nm-spinner.d.ts +3 -0
  157. package/dist/ui/components/nm-switch/nm-switch.d.ts +1 -1
  158. package/dist/ui/components/nm-tabs/nm-tabs.d.ts +6 -0
  159. package/dist/ui/components/nm-textarea/nm-textarea.d.ts +12 -2
  160. package/dist/ui/components/nm-toggle/nm-toggle.d.ts +1 -1
  161. package/dist/ui/components/nm-tooltip/nm-tooltip.d.ts +1 -1
  162. package/dist/ui/components/nm-voice-orb/nm-voice-orb.d.ts +18 -0
  163. package/dist/ui/components/nm-voice-orb/use-nm-voice-orb-theme.d.ts +2 -0
  164. package/dist/ui/config/nm-app-panel.d.ts +2 -0
  165. package/dist/ui/config/nm-button.d.ts +6 -0
  166. package/dist/ui/config/nm-composer.d.ts +11 -0
  167. package/dist/ui/config/nm-icons-extended.d.ts +1 -0
  168. package/dist/ui/config/nm-icons.d.ts +4 -1
  169. package/dist/ui/config/nm-voice-orb.d.ts +22 -0
  170. package/dist/ui/lib/nm-motion.d.ts +29 -0
  171. package/package.json +12 -8
  172. package/registry/index.json +5277 -73
  173. package/registry/ui/components/nm-ai/nm-ai.tsx +7 -4
  174. package/registry/ui/components/nm-alert/nm-alert.css +85 -0
  175. package/registry/ui/components/nm-alert/nm-alert.tsx +35 -0
  176. package/registry/ui/components/nm-button/nm-button.css +24 -0
  177. package/registry/ui/components/nm-button/nm-button.tsx +25 -11
  178. package/registry/ui/components/nm-checkbox/nm-checkbox.tsx +1 -1
  179. package/registry/ui/components/nm-chip/nm-chip.css +25 -0
  180. package/registry/ui/components/nm-chip/nm-chip.tsx +9 -3
  181. package/registry/ui/components/nm-collapsible/nm-collapsible.css +3 -0
  182. package/registry/ui/components/nm-collapsible/nm-collapsible.tsx +16 -0
  183. package/registry/ui/components/nm-combobox/nm-combobox.tsx +1 -1
  184. package/registry/ui/components/nm-command/nm-command.css +48 -0
  185. package/registry/ui/components/nm-command/nm-command.tsx +19 -5
  186. package/registry/ui/components/nm-composer/nm-composer.css +87 -3
  187. package/registry/ui/components/nm-composer/nm-composer.tsx +321 -33
  188. package/registry/ui/components/nm-context-menu/nm-context-menu.tsx +1 -1
  189. package/registry/ui/components/nm-dialog/nm-dialog.css +5 -6
  190. package/registry/ui/components/nm-dialog/nm-dialog.tsx +1 -1
  191. package/registry/ui/components/nm-dropdown/nm-dropdown.css +5 -0
  192. package/registry/ui/components/nm-dropdown/nm-dropdown.tsx +28 -249
  193. package/registry/ui/components/nm-dropdown-menu/nm-dropdown-menu.css +8 -0
  194. package/registry/ui/components/nm-dropdown-menu/nm-dropdown-menu.tsx +85 -6
  195. package/registry/ui/components/nm-field/nm-field.tsx +6 -1
  196. package/registry/ui/components/nm-hover-card/nm-hover-card.tsx +1 -1
  197. package/registry/ui/components/nm-icon/animated/activity.tsx +109 -0
  198. package/registry/ui/components/nm-icon/animated/archive.tsx +85 -0
  199. package/registry/ui/components/nm-icon/animated/arrow-down.tsx +149 -0
  200. package/registry/ui/components/nm-icon/animated/arrow-left.tsx +149 -0
  201. package/registry/ui/components/nm-icon/animated/arrow-right.tsx +149 -0
  202. package/registry/ui/components/nm-icon/animated/arrow-up-down.tsx +168 -0
  203. package/registry/ui/components/nm-icon/animated/arrow-up.tsx +149 -0
  204. package/registry/ui/components/nm-icon/animated/audio-lines.tsx +190 -0
  205. package/registry/ui/components/nm-icon/animated/bookmark-plus.tsx +65 -0
  206. package/registry/ui/components/nm-icon/animated/bookmark.tsx +77 -0
  207. package/registry/ui/components/nm-icon/animated/bot.tsx +167 -0
  208. package/registry/ui/components/nm-icon/animated/calendar-days.tsx +128 -0
  209. package/registry/ui/components/nm-icon/animated/calendar.tsx +83 -0
  210. package/registry/ui/components/nm-icon/animated/camera.tsx +85 -0
  211. package/registry/ui/components/nm-icon/animated/chart-column-increasing.tsx +150 -0
  212. package/registry/ui/components/nm-icon/animated/chart-column.tsx +179 -0
  213. package/registry/ui/components/nm-icon/animated/chart-line.tsx +88 -0
  214. package/registry/ui/components/nm-icon/animated/check-check.tsx +105 -0
  215. package/registry/ui/components/nm-icon/animated/check.tsx +70 -0
  216. package/registry/ui/components/nm-icon/animated/chevron-down.tsx +73 -0
  217. package/registry/ui/components/nm-icon/animated/chevron-left.tsx +73 -0
  218. package/registry/ui/components/nm-icon/animated/chevron-right.tsx +73 -0
  219. package/registry/ui/components/nm-icon/animated/chevron-up.tsx +73 -0
  220. package/registry/ui/components/nm-icon/animated/circle-alert.tsx +84 -0
  221. package/registry/ui/components/nm-icon/animated/circle-check-big.tsx +75 -0
  222. package/registry/ui/components/nm-icon/animated/circle-check.tsx +77 -0
  223. package/registry/ui/components/nm-icon/animated/circle-plus.tsx +93 -0
  224. package/registry/ui/components/nm-icon/animated/circle-question-mark.tsx +62 -0
  225. package/registry/ui/components/nm-icon/animated/circle-x.tsx +93 -0
  226. package/registry/ui/components/nm-icon/animated/clock-3.tsx +95 -0
  227. package/registry/ui/components/nm-icon/animated/clock-4.tsx +95 -0
  228. package/registry/ui/components/nm-icon/animated/clock.tsx +95 -0
  229. package/registry/ui/components/nm-icon/animated/cloud-sun.tsx +130 -0
  230. package/registry/ui/components/nm-icon/animated/cloud-upload.tsx +93 -0
  231. package/registry/ui/components/nm-icon/animated/copy.tsx +153 -0
  232. package/registry/ui/components/nm-icon/animated/download.tsx +93 -0
  233. package/registry/ui/components/nm-icon/animated/ellipsis.tsx +189 -0
  234. package/registry/ui/components/nm-icon/animated/external-link.tsx +97 -0
  235. package/registry/ui/components/nm-icon/animated/eye-off.tsx +63 -0
  236. package/registry/ui/components/nm-icon/animated/eye.tsx +82 -0
  237. package/registry/ui/components/nm-icon/animated/fast-forward.tsx +72 -0
  238. package/registry/ui/components/nm-icon/animated/file-clock.tsx +105 -0
  239. package/registry/ui/components/nm-icon/animated/file-text.tsx +102 -0
  240. package/registry/ui/components/nm-icon/animated/file.tsx +124 -0
  241. package/registry/ui/components/nm-icon/animated/folder.tsx +132 -0
  242. package/registry/ui/components/nm-icon/animated/gauge.tsx +84 -0
  243. package/registry/ui/components/nm-icon/animated/globe.tsx +61 -0
  244. package/registry/ui/components/nm-icon/animated/graduation-cap.tsx +92 -0
  245. package/registry/ui/components/nm-icon/animated/headphones.tsx +85 -0
  246. package/registry/ui/components/nm-icon/animated/image.tsx +88 -0
  247. package/registry/ui/components/nm-icon/animated/index.ts +251 -0
  248. package/registry/ui/components/nm-icon/animated/info.tsx +75 -0
  249. package/registry/ui/components/nm-icon/animated/key-round.tsx +67 -0
  250. package/registry/ui/components/nm-icon/animated/layers.tsx +116 -0
  251. package/registry/ui/components/nm-icon/animated/layout-dashboard.tsx +176 -0
  252. package/registry/ui/components/nm-icon/animated/library-big.tsx +89 -0
  253. package/registry/ui/components/nm-icon/animated/lightbulb.tsx +106 -0
  254. package/registry/ui/components/nm-icon/animated/link.tsx +210 -0
  255. package/registry/ui/components/nm-icon/animated/list-filter.tsx +78 -0
  256. package/registry/ui/components/nm-icon/animated/list.tsx +184 -0
  257. package/registry/ui/components/nm-icon/animated/loader-circle.tsx +68 -0
  258. package/registry/ui/components/nm-icon/animated/lock.tsx +152 -0
  259. package/registry/ui/components/nm-icon/animated/log-out.tsx +93 -0
  260. package/registry/ui/components/nm-icon/animated/mail.tsx +131 -0
  261. package/registry/ui/components/nm-icon/animated/map-pin.tsx +111 -0
  262. package/registry/ui/components/nm-icon/animated/menu.tsx +121 -0
  263. package/registry/ui/components/nm-icon/animated/message-circle-dashed.tsx +140 -0
  264. package/registry/ui/components/nm-icon/animated/message-circle.tsx +66 -0
  265. package/registry/ui/components/nm-icon/animated/message-square-plus.tsx +106 -0
  266. package/registry/ui/components/nm-icon/animated/message-square-text.tsx +153 -0
  267. package/registry/ui/components/nm-icon/animated/message-square.tsx +66 -0
  268. package/registry/ui/components/nm-icon/animated/mic-off.tsx +65 -0
  269. package/registry/ui/components/nm-icon/animated/mic.tsx +77 -0
  270. package/registry/ui/components/nm-icon/animated/minimize.tsx +164 -0
  271. package/registry/ui/components/nm-icon/animated/minus.tsx +84 -0
  272. package/registry/ui/components/nm-icon/animated/moon.tsx +98 -0
  273. package/registry/ui/components/nm-icon/animated/music.tsx +62 -0
  274. package/registry/ui/components/nm-icon/animated/notebook-pen.tsx +114 -0
  275. package/registry/ui/components/nm-icon/animated/panel-left.tsx +77 -0
  276. package/registry/ui/components/nm-icon/animated/panel-right.tsx +77 -0
  277. package/registry/ui/components/nm-icon/animated/paperclip.tsx +83 -0
  278. package/registry/ui/components/nm-icon/animated/pause.tsx +116 -0
  279. package/registry/ui/components/nm-icon/animated/pencil.tsx +70 -0
  280. package/registry/ui/components/nm-icon/animated/pin-off.tsx +102 -0
  281. package/registry/ui/components/nm-icon/animated/pin.tsx +124 -0
  282. package/registry/ui/components/nm-icon/animated/plane-takeoff.tsx +62 -0
  283. package/registry/ui/components/nm-icon/animated/play.tsx +74 -0
  284. package/registry/ui/components/nm-icon/animated/plug-zap.tsx +117 -0
  285. package/registry/ui/components/nm-icon/animated/plus.tsx +122 -0
  286. package/registry/ui/components/nm-icon/animated/radio.tsx +137 -0
  287. package/registry/ui/components/nm-icon/animated/refresh-cw.tsx +103 -0
  288. package/registry/ui/components/nm-icon/animated/rocket.tsx +93 -0
  289. package/registry/ui/components/nm-icon/animated/rotate-ccw.tsx +87 -0
  290. package/registry/ui/components/nm-icon/animated/rotate-cw.tsx +98 -0
  291. package/registry/ui/components/nm-icon/animated/save.tsx +58 -0
  292. package/registry/ui/components/nm-icon/animated/search.tsx +90 -0
  293. package/registry/ui/components/nm-icon/animated/send.tsx +87 -0
  294. package/registry/ui/components/nm-icon/animated/settings.tsx +92 -0
  295. package/registry/ui/components/nm-icon/animated/share-2.tsx +148 -0
  296. package/registry/ui/components/nm-icon/animated/share.tsx +76 -0
  297. package/registry/ui/components/nm-icon/animated/shield-check.tsx +70 -0
  298. package/registry/ui/components/nm-icon/animated/shopping-bag.tsx +71 -0
  299. package/registry/ui/components/nm-icon/animated/sliders-horizontal.tsx +243 -0
  300. package/registry/ui/components/nm-icon/animated/sparkles.tsx +227 -0
  301. package/registry/ui/components/nm-icon/animated/star.tsx +86 -0
  302. package/registry/ui/components/nm-icon/animated/sun.tsx +148 -0
  303. package/registry/ui/components/nm-icon/animated/terminal.tsx +71 -0
  304. package/registry/ui/components/nm-icon/animated/trash-2.tsx +136 -0
  305. package/registry/ui/components/nm-icon/animated/trash.tsx +94 -0
  306. package/registry/ui/components/nm-icon/animated/trending-up.tsx +75 -0
  307. package/registry/ui/components/nm-icon/animated/triangle-alert.tsx +75 -0
  308. package/registry/ui/components/nm-icon/animated/trophy.tsx +62 -0
  309. package/registry/ui/components/nm-icon/animated/type.tsx +106 -0
  310. package/registry/ui/components/nm-icon/animated/upload.tsx +94 -0
  311. package/registry/ui/components/nm-icon/animated/user-round.tsx +84 -0
  312. package/registry/ui/components/nm-icon/animated/user.tsx +84 -0
  313. package/registry/ui/components/nm-icon/animated/users-round.tsx +156 -0
  314. package/registry/ui/components/nm-icon/animated/users.tsx +156 -0
  315. package/registry/ui/components/nm-icon/animated/volume-2.tsx +119 -0
  316. package/registry/ui/components/nm-icon/animated/wand-sparkles.tsx +111 -0
  317. package/registry/ui/components/nm-icon/animated/x.tsx +122 -0
  318. package/registry/ui/components/nm-icon/animated/zap.tsx +86 -0
  319. package/registry/ui/components/nm-icon/animated/zoom-in.tsx +66 -0
  320. package/registry/ui/components/nm-icon/animated/zoom-out.tsx +85 -0
  321. package/registry/ui/components/nm-icon/nm-icon.css +0 -1
  322. package/registry/ui/components/nm-icon/nm-icon.tsx +20 -5
  323. package/registry/ui/components/nm-icon/nm-lucide-extended.tsx +284 -0
  324. package/registry/ui/components/nm-icon/nm-lucide.tsx +55 -5
  325. package/registry/ui/components/nm-input/nm-input.css +1 -0
  326. package/registry/ui/components/nm-menubar/nm-menubar.tsx +2 -2
  327. package/registry/ui/components/nm-panel/nm-panel.css +7 -0
  328. package/registry/ui/components/nm-panel/nm-panel.tsx +19 -2
  329. package/registry/ui/components/nm-popover/nm-popover.tsx +4 -3
  330. package/registry/ui/components/nm-progress/nm-progress.tsx +1 -1
  331. package/registry/ui/components/nm-radio-group/nm-radio-group.tsx +2 -2
  332. package/registry/ui/components/nm-select/nm-select.tsx +1 -1
  333. package/registry/ui/components/nm-skeleton/nm-skeleton.css +17 -0
  334. package/registry/ui/components/nm-skeleton/nm-skeleton.tsx +7 -0
  335. package/registry/ui/components/nm-slider/nm-slider.tsx +1 -1
  336. package/registry/ui/components/nm-small-icon-button/nm-small-icon-button.tsx +3 -3
  337. package/registry/ui/components/nm-spinner/nm-spinner.css +22 -0
  338. package/registry/ui/components/nm-spinner/nm-spinner.tsx +7 -0
  339. package/registry/ui/components/nm-switch/nm-switch.css +20 -3
  340. package/registry/ui/components/nm-switch/nm-switch.tsx +1 -1
  341. package/registry/ui/components/nm-tabs/nm-tabs.css +4 -0
  342. package/registry/ui/components/nm-tabs/nm-tabs.tsx +19 -0
  343. package/registry/ui/components/nm-textarea/nm-textarea.css +200 -0
  344. package/registry/ui/components/nm-textarea/nm-textarea.tsx +417 -3
  345. package/registry/ui/components/nm-toggle/nm-toggle.tsx +1 -1
  346. package/registry/ui/components/nm-toolbar/nm-toolbar.tsx +4 -1
  347. package/registry/ui/components/nm-tooltip/nm-tooltip.tsx +1 -1
  348. package/registry/ui/components/nm-voice-orb/nm-voice-orb.css +17 -0
  349. package/registry/ui/components/nm-voice-orb/nm-voice-orb.tsx +112 -0
  350. package/registry/ui/components/nm-voice-orb/use-nm-voice-orb-theme.ts +34 -0
  351. package/registry/ui/components/nordman-icon-button/nordman-icon-button.tsx +7 -3
  352. package/registry/ui/config/nm-button.ts +6 -0
  353. package/registry/ui/config/nm-composer.ts +12 -0
  354. package/registry/ui/config/nm-icons-extended.ts +141 -0
  355. package/registry/ui/config/nm-icons.ts +6 -2
  356. package/registry/ui/config/nm-voice-orb.ts +50 -0
  357. package/registry/ui/lib/nm-motion.ts +30 -0
  358. package/registry/ui/styles/tokens.css +7 -0
@@ -1,12 +1,17 @@
1
1
  import { NmChip } from '../nm-chip/nm-chip.tsx';
2
- import { useRef, type KeyboardEvent, type ReactNode } from 'react';
2
+ import { useCallback, useEffect, useLayoutEffect, useRef, useState, type KeyboardEvent, type ReactNode, type Ref, type RefObject, type TextareaHTMLAttributes } from 'react';
3
3
  import {
4
4
  NM_COMPOSER_CLASSES as C,
5
5
  NM_COMPOSER_COPY as COPY,
6
+ NM_COMPOSER_INLINE_MARK as MARK,
6
7
  type NmComposerAttachment,
7
8
  type NmComposerQueued,
8
9
  } from '../../config/nm-composer.ts';
9
10
  import { NmIcon } from '../nm-icon/nm-icon.tsx';
11
+ import type { NmIconId } from '../../config/nm-icons.ts';
12
+ import { NmVoiceOrb } from '../nm-voice-orb/nm-voice-orb.tsx';
13
+ import type { OrbAdapter } from 'orb-ui';
14
+ import type { NmVoiceOrbAgentState } from '../../config/nm-voice-orb.ts';
10
15
 
11
16
  export type NmComposerProps = {
12
17
  value: string;
@@ -26,32 +31,264 @@ export type NmComposerProps = {
26
31
  onRemoveAttachment?: (id: string) => void;
27
32
  /** The add button on the left. Omit it to hide the button. */
28
33
  onAdd?: () => void;
29
- /** Dictation. Pressed while it listens. */
34
+ /** Dictation. Starts listening. */
30
35
  onMic?: () => void;
31
- micActive?: boolean;
32
- /** Voice mode. It shows while the box is empty. Typing swaps it for Send. */
36
+ /** Listening: the field gives way to `listeningContent` (the waveform), and the bar offers Cancel and Done. */
37
+ listening?: boolean;
38
+ listeningContent?: ReactNode;
39
+ onCancelListening?: () => void;
40
+ onConfirmListening?: () => void;
41
+ /** Done was pressed and the text is being transcribed. */
42
+ confirming?: boolean;
43
+ /** Voice mode. The button shows while the box is empty. Typing swaps it for Send. */
33
44
  onVoice?: () => void;
45
+ /** Voice mode is running: the orb takes the place of the voice button and ends the session when pressed. */
46
+ voiceActive?: boolean;
47
+ voiceState?: NmVoiceOrbAgentState;
48
+ voiceAdapter?: OrbAdapter;
49
+ getInputVolume?: () => number;
50
+ getOutputVolume?: () => number;
51
+ onVoiceStop?: () => void;
52
+ /** The microphone in voice mode. */
53
+ voiceMuted?: boolean;
54
+ onVoiceMute?: () => void;
34
55
  /** Replaces the add button, for a menu of what to add. Give the trigger the composer button class. */
35
56
  addControl?: ReactNode;
36
57
  /** Controls beside the add button, such as the model selector. */
37
58
  start?: ReactNode;
38
59
  disabled?: boolean;
60
+ /** Something other than text can be sent, such as an attached file. */
61
+ sendable?: boolean;
39
62
  testId?: string;
63
+ /** Chips and pills that belong to the message, such as quotes, skills and connections. They sit above the field. */
64
+ header?: ReactNode;
65
+ /** Drawn over the field, for marks that follow the text. */
66
+ overlay?: ReactNode;
67
+ /**
68
+ * Inline chips. A chip sits in the text as its label between two NM_COMPOSER_INLINE_MARK characters,
69
+ * and is drawn as a NIF chip. This says which icon or mark, and which text, a label gets.
70
+ */
71
+ inlineChip?: (label: string) => { icon?: NmIconId; leading?: ReactNode; text?: string } | null;
72
+ /** More controls at the end of the bar, before the action, such as usage and dictation. */
73
+ end?: ReactNode;
74
+ /** The field's own ref and handlers (keys, paste, focus, composition), for a host that reads the text itself. */
75
+ fieldRef?: Ref<HTMLTextAreaElement>;
76
+ fieldProps?: Omit<TextareaHTMLAttributes<HTMLTextAreaElement>, 'value' | 'className' | 'rows' | 'placeholder' | 'disabled'>;
77
+ /** Render a div instead of a form, for a host that already sits inside one. Send is then a submit button of that form. */
78
+ formless?: boolean;
79
+ /** The host owns the text: the field is not controlled by `value`, and `value` only says whether there is any. */
80
+ uncontrolled?: boolean;
40
81
  };
41
82
 
83
+ type InlinePart = { kind: 'text'; value: string } | { kind: 'chip'; label: string; occurrence: number };
84
+
85
+ function splitInline(text: string): InlinePart[] {
86
+ const parts: InlinePart[] = [];
87
+ let last = 0;
88
+ let occurrence = 0;
89
+ for (const match of text.matchAll(new RegExp(`${MARK}([^${MARK}]*)${MARK}`, 'g'))) {
90
+ const index = match.index ?? 0;
91
+ if (index > last) {
92
+ parts.push({ kind: 'text', value: text.slice(last, index) });
93
+ }
94
+ parts.push({ kind: 'chip', label: match[1] ?? '', occurrence });
95
+ occurrence += 1;
96
+ last = index + match[0].length;
97
+ }
98
+ if (last < text.length) {
99
+ parts.push({ kind: 'text', value: text.slice(last) });
100
+ }
101
+ return parts;
102
+ }
103
+
104
+ let measuring: CanvasRenderingContext2D | null = null;
105
+
106
+ /** How wide the field draws this text, so a chip can take exactly the room its label holds. */
107
+ function textWidth(field: HTMLTextAreaElement, text: string): number {
108
+ measuring ??= document.createElement('canvas').getContext('2d');
109
+ if (measuring == null) {
110
+ return 0;
111
+ }
112
+ const style = getComputedStyle(field);
113
+ measuring.font = style.font || `${style.fontSize} sans-serif`;
114
+ return measuring.measureText(text).width;
115
+ }
116
+
117
+ /** The words with the chips drawn in them. The field's own text turns transparent so the two line up. */
118
+ function NmComposerInline(props: { text: string; field: RefObject<HTMLTextAreaElement | null>; chip?: NmComposerProps['inlineChip'] }): ReactNode {
119
+ const mirror = useRef<HTMLDivElement>(null);
120
+ const [, settle] = useState(0);
121
+ const { field, text } = props;
122
+ /**
123
+ * The chip keeps its own size. The field holds the chip's label as text of about the same width, and whatever
124
+ * is left over goes after the chip, so the words that follow line up with the field's. A label of non-breaking
125
+ * spaces cut to the chip's width leaves almost nothing over.
126
+ */
127
+ useLayoutEffect(() => {
128
+ const area = field.current;
129
+ const layer = mirror.current;
130
+ if (area == null || layer == null) {
131
+ return;
132
+ }
133
+ for (const chip of layer.querySelectorAll<HTMLElement>('[data-token]')) {
134
+ chip.style.marginRight = '0px';
135
+ const room = textWidth(area, chip.dataset.token ?? '') - chip.getBoundingClientRect().width;
136
+ chip.style.marginRight = `${room}px`;
137
+ }
138
+ });
139
+ useEffect(() => {
140
+ const area = field.current;
141
+ const layer = mirror.current;
142
+ if (area == null || layer == null) {
143
+ return;
144
+ }
145
+ const sync = () => {
146
+ layer.scrollTop = area.scrollTop;
147
+ };
148
+ sync();
149
+ area.addEventListener('scroll', sync);
150
+ // The fonts may not be in when the chips first measure.
151
+ void document.fonts?.ready.then(() => settle((count) => count + 1));
152
+ return () => area.removeEventListener('scroll', sync);
153
+ }, [field, text]);
154
+ return (
155
+ <div ref={mirror} className={C.inline} aria-hidden="true">
156
+ {splitInline(text).map((part, index) => {
157
+ if (part.kind === 'text') {
158
+ return <span key={`t-${index}`}>{part.value}</span>;
159
+ }
160
+ // The field cannot break a line next to the mark, so the layer must not either.
161
+ const mark = props.chip?.(part.label);
162
+ return (
163
+ <NmChip
164
+ key={`c-${part.occurrence}`}
165
+ tone="neutral"
166
+ size="inline"
167
+ icon={mark?.icon}
168
+ leading={mark?.leading}
169
+ className={C.inlineChip}
170
+ data-token={part.label}
171
+ data-composer-link={part.occurrence}
172
+ data-testid="composer-link-chip"
173
+ >
174
+ {mark?.text ?? part.label.trim()}
175
+ </NmChip>
176
+ );
177
+ })}
178
+ </div>
179
+ );
180
+ }
181
+
42
182
  export function NmComposer(props: NmComposerProps): ReactNode {
43
- const field = useRef<HTMLTextAreaElement>(null);
44
- const hasText = props.value.trim() !== '';
183
+ const ownField = useRef<HTMLTextAreaElement>(null);
184
+ const fieldNode = useRef<HTMLTextAreaElement | null>(null);
185
+ const bindField = useCallback(
186
+ (node: HTMLTextAreaElement | null) => {
187
+ fieldNode.current = node;
188
+ ownField.current = node;
189
+ const outer = props.fieldRef;
190
+ if (typeof outer === 'function') {
191
+ outer(node);
192
+ } else if (outer != null) {
193
+ (outer as { current: HTMLTextAreaElement | null }).current = node;
194
+ }
195
+ },
196
+ [props.fieldRef],
197
+ );
198
+ /**
199
+ * The field sizes itself to its text, and nothing else sets its height. The height is measured here and set in
200
+ * pixels, so it can move smoothly: the box stays where it is and grows from its top edge, one line at a time.
201
+ * While it grows the text is held at the top, so the lines do not slide.
202
+ */
203
+ const rootRef = useRef<HTMLElement | null>(null);
204
+ /**
205
+ * The box grows upward from its resting size. It says how far in `--nm-composer-grow`, on the page, so what sits
206
+ * above it, such as a greeting, can keep clear of it and move up by the same amount, frame by frame.
207
+ */
208
+ useEffect(() => {
209
+ const root = rootRef.current;
210
+ if (root == null || typeof ResizeObserver === 'undefined') {
211
+ return undefined;
212
+ }
213
+ const page = document.documentElement;
214
+ const probe = document.createElement('div');
215
+ probe.style.cssText = 'position:absolute;visibility:hidden;pointer-events:none;height:var(--nm-composer-rest)';
216
+ const report = () => {
217
+ root.append(probe);
218
+ const rest = probe.getBoundingClientRect().height;
219
+ probe.remove();
220
+ page.style.setProperty('--nm-composer-grow', `${Math.max(0, root.getBoundingClientRect().height - rest)}px`);
221
+ };
222
+ const watch = new ResizeObserver(report);
223
+ watch.observe(root);
224
+ report();
225
+ return () => {
226
+ watch.disconnect();
227
+ page.style.removeProperty('--nm-composer-grow');
228
+ };
229
+ }, []);
230
+ const settle = useRef(0);
231
+ const fit = useCallback(() => {
232
+ const area = fieldNode.current;
233
+ if (area == null) {
234
+ return;
235
+ }
236
+ const before = area.getBoundingClientRect().height;
237
+ area.style.transition = 'none';
238
+ area.style.height = 'auto';
239
+ const limit = parseFloat(getComputedStyle(area).maxHeight);
240
+ const wanted = Math.min(area.scrollHeight, Number.isFinite(limit) ? limit : area.scrollHeight);
241
+ area.style.height = `${before}px`;
242
+ void area.offsetHeight;
243
+ area.style.transition = '';
244
+ if (Math.abs(wanted - before) < 0.5) {
245
+ area.style.height = `${wanted}px`;
246
+ return;
247
+ }
248
+ area.style.overflowY = 'hidden';
249
+ area.scrollTop = 0;
250
+ area.style.height = `${wanted}px`;
251
+ window.clearTimeout(settle.current);
252
+ settle.current = window.setTimeout(() => {
253
+ area.style.overflowY = '';
254
+ }, 300);
255
+ }, []);
256
+ const hasInline = props.value.includes(MARK);
257
+ const hasText = props.value.replaceAll(MARK, '').replaceAll('\u2003', '').trim() !== '';
258
+ const canSend = hasText || props.sendable === true;
259
+
260
+ useLayoutEffect(fit, [fit, props.value, hasInline, props.listening]);
261
+ /** A narrower or wider box wraps the text differently, so the height is measured again. */
262
+ useEffect(() => {
263
+ const area = fieldNode.current;
264
+ if (area == null || typeof ResizeObserver === 'undefined') {
265
+ return undefined;
266
+ }
267
+ let width = area.getBoundingClientRect().width;
268
+ const watch = new ResizeObserver(() => {
269
+ const next = area.getBoundingClientRect().width;
270
+ if (next !== width) {
271
+ width = next;
272
+ fit();
273
+ }
274
+ });
275
+ watch.observe(area);
276
+ return () => watch.disconnect();
277
+ }, [fit, props.listening]);
45
278
 
46
279
  function send(): void {
47
280
  // While the agent answers, Enter still sends. The host puts the message in the queue.
48
- if (props.disabled || !hasText) {
281
+ if (props.disabled || !canSend) {
49
282
  return;
50
283
  }
51
284
  props.onSend();
52
285
  }
53
286
 
54
287
  function onKeyDown(event: KeyboardEvent<HTMLTextAreaElement>): void {
288
+ props.fieldProps?.onKeyDown?.(event);
289
+ if (event.defaultPrevented) {
290
+ return;
291
+ }
55
292
  if (event.key === 'Enter' && !event.shiftKey && !event.nativeEvent.isComposing) {
56
293
  event.preventDefault();
57
294
  send();
@@ -59,35 +296,68 @@ export function NmComposer(props: NmComposerProps): ReactNode {
59
296
  }
60
297
 
61
298
  let action: ReactNode;
62
- if (props.busy) {
299
+ if (props.listening) {
63
300
  action = (
64
- <button type="button" className={`${C.button} ${C.primary}`} aria-label={COPY.stop} onClick={props.onStop}>
301
+ <>
302
+ <button type="button" className={C.button} aria-label={COPY.cancel} data-testid="dictation-cancel" disabled={props.disabled} onClick={props.onCancelListening}>
303
+ <NmIcon id="x" />
304
+ </button>
305
+ <button type="button" className={`${C.button} ${C.primary}`} aria-label={COPY.done} data-testid="dictation-confirm" data-loading={props.confirming ? 'true' : undefined} disabled={props.disabled || props.confirming} onClick={props.onConfirmListening}>
306
+ <NmIcon id={props.confirming ? 'loader' : 'check'} />
307
+ </button>
308
+ </>
309
+ );
310
+ } else if (props.voiceActive && !canSend) {
311
+ action = props.voiceState === 'connecting' ? (
312
+ <button type="button" className={C.button} aria-label={COPY.voiceEnd} data-testid="voice-orb-connecting" onClick={props.onVoiceStop}>
313
+ <NmIcon id="loader" />
314
+ </button>
315
+ ) : (
316
+ <NmVoiceOrb
317
+ adapter={props.voiceAdapter}
318
+ agentState={props.voiceState ?? 'listening'}
319
+ onClick={props.onVoiceStop}
320
+ getInputVolume={props.getInputVolume}
321
+ getOutputVolume={props.getOutputVolume}
322
+ label={COPY.voiceEnd}
323
+ testId="voice-orb"
324
+ />
325
+ );
326
+ } else if (props.busy) {
327
+ action = (
328
+ <button type="button" className={`${C.button} ${C.primary}`} aria-label={COPY.stop} data-testid="stop-button" onClick={props.onStop}>
65
329
  <span className={C.stopMark} />
66
330
  </button>
67
331
  );
68
- } else if (hasText || props.onVoice == null) {
332
+ } else if (canSend || props.onVoice == null) {
69
333
  action = (
70
- <button type="submit" className={`${C.button} ${C.primary}`} aria-label={COPY.send} disabled={props.disabled || !hasText}>
334
+ <button type="submit" className={`${C.button} ${C.primary}`} aria-label={COPY.send} data-testid="send-button" disabled={props.disabled || !canSend}>
71
335
  <NmIcon id="send" />
72
336
  </button>
73
337
  );
74
338
  } else {
75
339
  action = (
76
- <button type="button" className={C.button} aria-label={COPY.voice} disabled={props.disabled} onClick={props.onVoice}>
340
+ <button type="button" className={C.button} aria-label={COPY.voice} data-testid="voice-button" disabled={props.disabled} onClick={props.onVoice}>
77
341
  <NmIcon id="audio-lines" />
78
342
  </button>
79
343
  );
80
344
  }
81
345
 
346
+ const Root = props.formless ? 'div' : 'form';
82
347
  const form = (
83
- <form
348
+ <Root
349
+ ref={rootRef as never}
84
350
  className={C.root}
85
351
  data-disabled={props.disabled ? 'true' : undefined}
86
352
  data-testid={props.testId}
87
- onSubmit={(event) => {
88
- event.preventDefault();
89
- send();
90
- }}
353
+ {...(props.formless
354
+ ? {}
355
+ : {
356
+ onSubmit: (event: { preventDefault: () => void }) => {
357
+ event.preventDefault();
358
+ send();
359
+ },
360
+ })}
91
361
  >
92
362
  <div className={C.main}>
93
363
  {props.queue && props.queue.length > 0 ? (
@@ -115,37 +385,55 @@ export function NmComposer(props: NmComposerProps): ReactNode {
115
385
  ))}
116
386
  </div>
117
387
  ) : null}
118
- <textarea
119
- ref={field}
120
- className={C.field}
121
- rows={1}
122
- value={props.value}
123
- placeholder={props.placeholder ?? COPY.placeholder}
124
- aria-label={props.placeholder ?? COPY.placeholder}
125
- disabled={props.disabled}
126
- onChange={(event) => props.onChange(event.target.value)}
127
- onKeyDown={onKeyDown}
128
- />
388
+ {props.header}
389
+ <div className={C.fieldWrap} data-listening={props.listening ? 'true' : undefined}>
390
+ {props.overlay}
391
+ {hasInline ? <NmComposerInline text={props.value} field={fieldNode} chip={props.inlineChip} /> : null}
392
+ <textarea
393
+ aria-label={props.placeholder ?? COPY.placeholder}
394
+ {...props.fieldProps}
395
+ ref={bindField}
396
+ data-inline={hasInline ? 'true' : undefined}
397
+ className={C.field}
398
+ rows={1}
399
+ {...(props.uncontrolled ? {} : { value: props.value, onChange: (event: { target: { value: string } }) => props.onChange(event.target.value) })}
400
+ placeholder={props.placeholder ?? COPY.placeholder}
401
+ disabled={props.disabled}
402
+ onKeyDown={onKeyDown}
403
+ onInput={(event) => {
404
+ props.fieldProps?.onInput?.(event);
405
+ fit();
406
+ }}
407
+ />
408
+ {props.listening ? (
409
+ <div className={C.listening}>{props.listeningContent}</div>
410
+ ) : null}
411
+ </div>
129
412
  </div>
130
413
  <div className={C.bar}>
131
414
  <div className={C.start}>
132
- {props.addControl ?? (props.onAdd ? (
415
+ {props.listening ? null : props.addControl ?? (props.onAdd ? (
133
416
  <button type="button" className={C.button} aria-label={COPY.attach} disabled={props.disabled} onClick={props.onAdd}>
134
417
  <NmIcon id="plus" />
135
418
  </button>
136
419
  ) : null)}
137
- {props.start}
420
+ {props.listening ? null : props.start}
138
421
  </div>
139
422
  <div className={C.end}>
140
- {props.onMic ? (
141
- <button type="button" className={C.button} aria-label={COPY.mic} aria-pressed={props.micActive === true} disabled={props.disabled} onClick={props.onMic}>
423
+ {props.voiceActive && props.onVoiceMute ? (
424
+ <button type="button" className={C.button} aria-label={props.voiceMuted ? COPY.unmute : COPY.mute} aria-pressed={props.voiceMuted === true} data-testid="voice-mic-toggle" disabled={props.disabled} onClick={props.onVoiceMute}>
425
+ <NmIcon id={props.voiceMuted ? 'mic-off' : 'mic'} />
426
+ </button>
427
+ ) : props.onMic && !props.listening && !props.voiceActive ? (
428
+ <button type="button" className={C.button} aria-label={COPY.mic} disabled={props.disabled} onClick={props.onMic}>
142
429
  <NmIcon id="mic" />
143
430
  </button>
144
431
  ) : null}
432
+ {props.listening ? null : props.end}
145
433
  {action}
146
434
  </div>
147
435
  </div>
148
- </form>
436
+ </Root>
149
437
  );
150
438
  return props.notice != null ? <div className={C.stack}>{props.notice}{form}</div> : form;
151
439
  }
@@ -1,4 +1,4 @@
1
- import { ContextMenu as ContextMenuPrimitive } from '@base-ui/react/context-menu';
1
+ import { ContextMenu as ContextMenuPrimitive } from '../../lib/nm-motion.ts';
2
2
  import { nmClass } from '../../lib/nm-class.ts';
3
3
 
4
4
  /**
@@ -15,7 +15,10 @@
15
15
  position: fixed;
16
16
  z-index: var(--nm-menu-z);
17
17
  top: 20%;
18
- left: 50%;
18
+ /* Centered with margins, not a transform: the motion popup owns transform. */
19
+ right: 0;
20
+ left: 0;
21
+ margin-inline: auto;
19
22
  box-sizing: border-box;
20
23
  width: min(var(--nm-width-dialog), calc(100% - var(--nm-space-24) * 2));
21
24
  max-height: 70%;
@@ -27,16 +30,12 @@
27
30
  font-family: var(--nm-font);
28
31
  font-size: var(--nm-type-ui);
29
32
  outline: none;
30
- transform: translateX(-50%);
31
- transition:
32
- opacity 0.15s,
33
- transform 0.15s;
33
+ transition: opacity 0.15s;
34
34
  }
35
35
 
36
36
  .nm-dialog[data-starting-style],
37
37
  .nm-dialog[data-ending-style] {
38
38
  opacity: 0;
39
- transform: translateX(-50%) scale(0.98);
40
39
  }
41
40
 
42
41
  .nm-dialog__header {
@@ -1,5 +1,5 @@
1
1
  import type { ComponentProps } from 'react';
2
- import { Dialog as DialogPrimitive } from '@base-ui/react/dialog';
2
+ import { Dialog as DialogPrimitive } from '../../lib/nm-motion.ts';
3
3
  import { nmClass } from '../../lib/nm-class.ts';
4
4
 
5
5
  /** Base UI Dialog, in the shadcn composition, styled by NIF tokens. */
@@ -79,6 +79,7 @@
79
79
  flex-direction: column;
80
80
  gap: var(--nm-space-4);
81
81
  min-width: 13.5rem;
82
+ max-width: min(var(--nm-width-menu-max), calc(100vw - var(--nm-space-16) * 2));
82
83
  max-height: var(--nm-dropdown-max);
83
84
  margin: 0;
84
85
  padding: var(--nm-list-pad);
@@ -262,7 +263,11 @@
262
263
  }
263
264
 
264
265
  .nm-dropdown__item .nm-dropdown__label {
266
+ min-width: 0;
267
+ overflow: hidden;
265
268
  line-height: var(--nm-leading-tight);
269
+ text-overflow: ellipsis;
270
+ white-space: nowrap;
266
271
  }
267
272
 
268
273
  .nm-dropdown__detail {