@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.
- package/README.md +1 -1
- package/dist/index.d.ts +6 -0
- package/dist/index.js +31353 -11545
- package/dist/styles.css +1 -1
- package/dist/ui/components/nm-ai/nm-ai.d.ts +4 -0
- package/dist/ui/components/nm-alert/nm-alert.d.ts +13 -0
- package/dist/ui/components/nm-button/nm-button.d.ts +4 -2
- package/dist/ui/components/nm-checkbox/nm-checkbox.d.ts +1 -1
- package/dist/ui/components/nm-chip/nm-chip.d.ts +5 -2
- package/dist/ui/components/nm-collapsible/nm-collapsible.d.ts +6 -0
- package/dist/ui/components/nm-combobox/nm-combobox.d.ts +1 -1
- package/dist/ui/components/nm-command/nm-command.d.ts +8 -1
- package/dist/ui/components/nm-composer/nm-composer.d.ts +47 -4
- package/dist/ui/components/nm-context-menu/nm-context-menu.d.ts +1 -1
- package/dist/ui/components/nm-dialog/nm-dialog.d.ts +1 -1
- package/dist/ui/components/nm-dropdown/nm-dropdown.d.ts +1 -1
- package/dist/ui/components/nm-dropdown-menu/nm-dropdown-menu.d.ts +11 -2
- package/dist/ui/components/nm-field/nm-field.d.ts +2 -0
- package/dist/ui/components/nm-hover-card/nm-hover-card.d.ts +1 -1
- package/dist/ui/components/nm-icon/animated/activity.d.ts +72 -0
- package/dist/ui/components/nm-icon/animated/archive.d.ts +31 -0
- package/dist/ui/components/nm-icon/animated/arrow-down.d.ts +128 -0
- package/dist/ui/components/nm-icon/animated/arrow-left.d.ts +128 -0
- package/dist/ui/components/nm-icon/animated/arrow-right.d.ts +128 -0
- package/dist/ui/components/nm-icon/animated/arrow-up-down.d.ts +119 -0
- package/dist/ui/components/nm-icon/animated/arrow-up.d.ts +128 -0
- package/dist/ui/components/nm-icon/animated/audio-lines.d.ts +99 -0
- package/dist/ui/components/nm-icon/animated/bookmark-plus.d.ts +35 -0
- package/dist/ui/components/nm-icon/animated/bookmark.d.ts +52 -0
- package/dist/ui/components/nm-icon/animated/bot.d.ts +90 -0
- package/dist/ui/components/nm-icon/animated/calendar-days.d.ts +87 -0
- package/dist/ui/components/nm-icon/animated/calendar.d.ts +36 -0
- package/dist/ui/components/nm-icon/animated/camera.d.ts +50 -0
- package/dist/ui/components/nm-icon/animated/chart-column-increasing.d.ts +37 -0
- package/dist/ui/components/nm-icon/animated/chart-column.d.ts +86 -0
- package/dist/ui/components/nm-icon/animated/chart-line.d.ts +51 -0
- package/dist/ui/components/nm-icon/animated/check-check.d.ts +50 -0
- package/dist/ui/components/nm-icon/animated/check.d.ts +25 -0
- package/dist/ui/components/nm-icon/animated/chevron-down.d.ts +39 -0
- package/dist/ui/components/nm-icon/animated/chevron-left.d.ts +39 -0
- package/dist/ui/components/nm-icon/animated/chevron-right.d.ts +39 -0
- package/dist/ui/components/nm-icon/animated/chevron-up.d.ts +39 -0
- package/dist/ui/components/nm-icon/animated/circle-alert.d.ts +23 -0
- package/dist/ui/components/nm-icon/animated/circle-check-big.d.ts +26 -0
- package/dist/ui/components/nm-icon/animated/circle-check.d.ts +26 -0
- package/dist/ui/components/nm-icon/animated/circle-plus.d.ts +44 -0
- package/dist/ui/components/nm-icon/animated/circle-question-mark.d.ts +22 -0
- package/dist/ui/components/nm-icon/animated/circle-x.d.ts +44 -0
- package/dist/ui/components/nm-icon/animated/clock-3.d.ts +44 -0
- package/dist/ui/components/nm-icon/animated/clock-4.d.ts +44 -0
- package/dist/ui/components/nm-icon/animated/clock.d.ts +44 -0
- package/dist/ui/components/nm-icon/animated/cloud-sun.d.ts +9 -0
- package/dist/ui/components/nm-icon/animated/cloud-upload.d.ts +45 -0
- package/dist/ui/components/nm-icon/animated/copy.d.ts +135 -0
- package/dist/ui/components/nm-icon/animated/download.d.ts +45 -0
- package/dist/ui/components/nm-icon/animated/ellipsis.d.ts +141 -0
- package/dist/ui/components/nm-icon/animated/external-link.d.ts +49 -0
- package/dist/ui/components/nm-icon/animated/eye-off.d.ts +23 -0
- package/dist/ui/components/nm-icon/animated/eye.d.ts +39 -0
- package/dist/ui/components/nm-icon/animated/fast-forward.d.ts +34 -0
- package/dist/ui/components/nm-icon/animated/file-clock.d.ts +48 -0
- package/dist/ui/components/nm-icon/animated/file-text.d.ts +56 -0
- package/dist/ui/components/nm-icon/animated/file.d.ts +67 -0
- package/dist/ui/components/nm-icon/animated/folder.d.ts +92 -0
- package/dist/ui/components/nm-icon/animated/gauge.d.ts +47 -0
- package/dist/ui/components/nm-icon/animated/globe.d.ts +21 -0
- package/dist/ui/components/nm-icon/animated/graduation-cap.d.ts +41 -0
- package/dist/ui/components/nm-icon/animated/headphones.d.ts +50 -0
- package/dist/ui/components/nm-icon/animated/image.d.ts +40 -0
- package/dist/ui/components/nm-icon/animated/index.d.ts +2 -0
- package/dist/ui/components/nm-icon/animated/info.d.ts +37 -0
- package/dist/ui/components/nm-icon/animated/key-round.d.ts +37 -0
- package/dist/ui/components/nm-icon/animated/layers.d.ts +61 -0
- package/dist/ui/components/nm-icon/animated/layout-dashboard.d.ts +131 -0
- package/dist/ui/components/nm-icon/animated/library-big.d.ts +29 -0
- package/dist/ui/components/nm-icon/animated/lightbulb.d.ts +51 -0
- package/dist/ui/components/nm-icon/animated/link.d.ts +193 -0
- package/dist/ui/components/nm-icon/animated/list-filter.d.ts +13 -0
- package/dist/ui/components/nm-icon/animated/list.d.ts +111 -0
- package/dist/ui/components/nm-icon/animated/loader-circle.d.ts +24 -0
- package/dist/ui/components/nm-icon/animated/lock.d.ts +94 -0
- package/dist/ui/components/nm-icon/animated/log-out.d.ts +45 -0
- package/dist/ui/components/nm-icon/animated/mail.d.ts +100 -0
- package/dist/ui/components/nm-icon/animated/map-pin.d.ts +67 -0
- package/dist/ui/components/nm-icon/animated/menu.d.ts +57 -0
- package/dist/ui/components/nm-icon/animated/message-circle-dashed.d.ts +34 -0
- package/dist/ui/components/nm-icon/animated/message-circle.d.ts +23 -0
- package/dist/ui/components/nm-icon/animated/message-square-plus.d.ts +49 -0
- package/dist/ui/components/nm-icon/animated/message-square-text.d.ts +104 -0
- package/dist/ui/components/nm-icon/animated/message-square.d.ts +23 -0
- package/dist/ui/components/nm-icon/animated/mic-off.d.ts +23 -0
- package/dist/ui/components/nm-icon/animated/mic.d.ts +37 -0
- package/dist/ui/components/nm-icon/animated/minimize.d.ts +139 -0
- package/dist/ui/components/nm-icon/animated/minus.d.ts +54 -0
- package/dist/ui/components/nm-icon/animated/moon.d.ts +62 -0
- package/dist/ui/components/nm-icon/animated/music.d.ts +22 -0
- package/dist/ui/components/nm-icon/animated/notebook-pen.d.ts +41 -0
- package/dist/ui/components/nm-icon/animated/panel-left.d.ts +29 -0
- package/dist/ui/components/nm-icon/animated/panel-right.d.ts +29 -0
- package/dist/ui/components/nm-icon/animated/paperclip.d.ts +49 -0
- package/dist/ui/components/nm-icon/animated/pause.d.ts +59 -0
- package/dist/ui/components/nm-icon/animated/pencil.d.ts +25 -0
- package/dist/ui/components/nm-icon/animated/pin-off.d.ts +44 -0
- package/dist/ui/components/nm-icon/animated/pin.d.ts +85 -0
- package/dist/ui/components/nm-icon/animated/plane-takeoff.d.ts +25 -0
- package/dist/ui/components/nm-icon/animated/play.d.ts +43 -0
- package/dist/ui/components/nm-icon/animated/plug-zap.d.ts +49 -0
- package/dist/ui/components/nm-icon/animated/plus.d.ts +95 -0
- package/dist/ui/components/nm-icon/animated/radio.d.ts +24 -0
- package/dist/ui/components/nm-icon/animated/refresh-cw.d.ts +55 -0
- package/dist/ui/components/nm-icon/animated/rocket.d.ts +48 -0
- package/dist/ui/components/nm-icon/animated/rotate-ccw.d.ts +51 -0
- package/dist/ui/components/nm-icon/animated/rotate-cw.d.ts +71 -0
- package/dist/ui/components/nm-icon/animated/save.d.ts +21 -0
- package/dist/ui/components/nm-icon/animated/search.d.ts +42 -0
- package/dist/ui/components/nm-icon/animated/send.d.ts +39 -0
- package/dist/ui/components/nm-icon/animated/settings.d.ts +39 -0
- package/dist/ui/components/nm-icon/animated/share-2.d.ts +122 -0
- package/dist/ui/components/nm-icon/animated/share.d.ts +40 -0
- package/dist/ui/components/nm-icon/animated/shield-check.d.ts +36 -0
- package/dist/ui/components/nm-icon/animated/shopping-bag.d.ts +35 -0
- package/dist/ui/components/nm-icon/animated/sliders-horizontal.d.ts +239 -0
- package/dist/ui/components/nm-icon/animated/sparkles.d.ts +205 -0
- package/dist/ui/components/nm-icon/animated/star.d.ts +48 -0
- package/dist/ui/components/nm-icon/animated/sun.d.ts +9 -0
- package/dist/ui/components/nm-icon/animated/terminal.d.ts +22 -0
- package/dist/ui/components/nm-icon/animated/trash-2.d.ts +61 -0
- package/dist/ui/components/nm-icon/animated/trash.d.ts +37 -0
- package/dist/ui/components/nm-icon/animated/trending-up.d.ts +40 -0
- package/dist/ui/components/nm-icon/animated/triangle-alert.d.ts +37 -0
- package/dist/ui/components/nm-icon/animated/trophy.d.ts +23 -0
- package/dist/ui/components/nm-icon/animated/type.d.ts +57 -0
- package/dist/ui/components/nm-icon/animated/upload.d.ts +49 -0
- package/dist/ui/components/nm-icon/animated/user-round.d.ts +33 -0
- package/dist/ui/components/nm-icon/animated/user.d.ts +33 -0
- package/dist/ui/components/nm-icon/animated/users-round.d.ts +93 -0
- package/dist/ui/components/nm-icon/animated/users.d.ts +93 -0
- package/dist/ui/components/nm-icon/animated/volume-2.d.ts +10 -0
- package/dist/ui/components/nm-icon/animated/wand-sparkles.d.ts +71 -0
- package/dist/ui/components/nm-icon/animated/x.d.ts +95 -0
- package/dist/ui/components/nm-icon/animated/zap.d.ts +65 -0
- package/dist/ui/components/nm-icon/animated/zoom-in.d.ts +35 -0
- package/dist/ui/components/nm-icon/animated/zoom-out.d.ts +53 -0
- package/dist/ui/components/nm-icon/nm-icon.d.ts +6 -3
- package/dist/ui/components/nm-icon/nm-lucide-extended.d.ts +3 -0
- package/dist/ui/components/nm-icon/nm-lucide.d.ts +7 -3
- package/dist/ui/components/nm-menubar/nm-menubar.d.ts +2 -2
- package/dist/ui/components/nm-panel/nm-panel.d.ts +5 -1
- package/dist/ui/components/nm-popover/nm-popover.d.ts +2 -2
- package/dist/ui/components/nm-progress/nm-progress.d.ts +1 -1
- package/dist/ui/components/nm-radio-group/nm-radio-group.d.ts +2 -2
- package/dist/ui/components/nm-select/nm-select.d.ts +1 -1
- package/dist/ui/components/nm-skeleton/nm-skeleton.d.ts +3 -0
- package/dist/ui/components/nm-slider/nm-slider.d.ts +1 -1
- package/dist/ui/components/nm-small-icon-button/nm-small-icon-button.d.ts +3 -3
- package/dist/ui/components/nm-spinner/nm-spinner.d.ts +3 -0
- package/dist/ui/components/nm-switch/nm-switch.d.ts +1 -1
- package/dist/ui/components/nm-tabs/nm-tabs.d.ts +6 -0
- package/dist/ui/components/nm-textarea/nm-textarea.d.ts +12 -2
- package/dist/ui/components/nm-toggle/nm-toggle.d.ts +1 -1
- package/dist/ui/components/nm-tooltip/nm-tooltip.d.ts +1 -1
- package/dist/ui/components/nm-voice-orb/nm-voice-orb.d.ts +18 -0
- package/dist/ui/components/nm-voice-orb/use-nm-voice-orb-theme.d.ts +2 -0
- package/dist/ui/config/nm-app-panel.d.ts +2 -0
- package/dist/ui/config/nm-button.d.ts +6 -0
- package/dist/ui/config/nm-composer.d.ts +11 -0
- package/dist/ui/config/nm-icons-extended.d.ts +1 -0
- package/dist/ui/config/nm-icons.d.ts +4 -1
- package/dist/ui/config/nm-voice-orb.d.ts +22 -0
- package/dist/ui/lib/nm-motion.d.ts +29 -0
- package/package.json +12 -8
- package/registry/index.json +5277 -73
- package/registry/ui/components/nm-ai/nm-ai.tsx +7 -4
- package/registry/ui/components/nm-alert/nm-alert.css +85 -0
- package/registry/ui/components/nm-alert/nm-alert.tsx +35 -0
- package/registry/ui/components/nm-button/nm-button.css +24 -0
- package/registry/ui/components/nm-button/nm-button.tsx +25 -11
- package/registry/ui/components/nm-checkbox/nm-checkbox.tsx +1 -1
- package/registry/ui/components/nm-chip/nm-chip.css +25 -0
- package/registry/ui/components/nm-chip/nm-chip.tsx +9 -3
- package/registry/ui/components/nm-collapsible/nm-collapsible.css +3 -0
- package/registry/ui/components/nm-collapsible/nm-collapsible.tsx +16 -0
- package/registry/ui/components/nm-combobox/nm-combobox.tsx +1 -1
- package/registry/ui/components/nm-command/nm-command.css +48 -0
- package/registry/ui/components/nm-command/nm-command.tsx +19 -5
- package/registry/ui/components/nm-composer/nm-composer.css +87 -3
- package/registry/ui/components/nm-composer/nm-composer.tsx +321 -33
- package/registry/ui/components/nm-context-menu/nm-context-menu.tsx +1 -1
- package/registry/ui/components/nm-dialog/nm-dialog.css +5 -6
- package/registry/ui/components/nm-dialog/nm-dialog.tsx +1 -1
- package/registry/ui/components/nm-dropdown/nm-dropdown.css +5 -0
- package/registry/ui/components/nm-dropdown/nm-dropdown.tsx +28 -249
- package/registry/ui/components/nm-dropdown-menu/nm-dropdown-menu.css +8 -0
- package/registry/ui/components/nm-dropdown-menu/nm-dropdown-menu.tsx +85 -6
- package/registry/ui/components/nm-field/nm-field.tsx +6 -1
- package/registry/ui/components/nm-hover-card/nm-hover-card.tsx +1 -1
- package/registry/ui/components/nm-icon/animated/activity.tsx +109 -0
- package/registry/ui/components/nm-icon/animated/archive.tsx +85 -0
- package/registry/ui/components/nm-icon/animated/arrow-down.tsx +149 -0
- package/registry/ui/components/nm-icon/animated/arrow-left.tsx +149 -0
- package/registry/ui/components/nm-icon/animated/arrow-right.tsx +149 -0
- package/registry/ui/components/nm-icon/animated/arrow-up-down.tsx +168 -0
- package/registry/ui/components/nm-icon/animated/arrow-up.tsx +149 -0
- package/registry/ui/components/nm-icon/animated/audio-lines.tsx +190 -0
- package/registry/ui/components/nm-icon/animated/bookmark-plus.tsx +65 -0
- package/registry/ui/components/nm-icon/animated/bookmark.tsx +77 -0
- package/registry/ui/components/nm-icon/animated/bot.tsx +167 -0
- package/registry/ui/components/nm-icon/animated/calendar-days.tsx +128 -0
- package/registry/ui/components/nm-icon/animated/calendar.tsx +83 -0
- package/registry/ui/components/nm-icon/animated/camera.tsx +85 -0
- package/registry/ui/components/nm-icon/animated/chart-column-increasing.tsx +150 -0
- package/registry/ui/components/nm-icon/animated/chart-column.tsx +179 -0
- package/registry/ui/components/nm-icon/animated/chart-line.tsx +88 -0
- package/registry/ui/components/nm-icon/animated/check-check.tsx +105 -0
- package/registry/ui/components/nm-icon/animated/check.tsx +70 -0
- package/registry/ui/components/nm-icon/animated/chevron-down.tsx +73 -0
- package/registry/ui/components/nm-icon/animated/chevron-left.tsx +73 -0
- package/registry/ui/components/nm-icon/animated/chevron-right.tsx +73 -0
- package/registry/ui/components/nm-icon/animated/chevron-up.tsx +73 -0
- package/registry/ui/components/nm-icon/animated/circle-alert.tsx +84 -0
- package/registry/ui/components/nm-icon/animated/circle-check-big.tsx +75 -0
- package/registry/ui/components/nm-icon/animated/circle-check.tsx +77 -0
- package/registry/ui/components/nm-icon/animated/circle-plus.tsx +93 -0
- package/registry/ui/components/nm-icon/animated/circle-question-mark.tsx +62 -0
- package/registry/ui/components/nm-icon/animated/circle-x.tsx +93 -0
- package/registry/ui/components/nm-icon/animated/clock-3.tsx +95 -0
- package/registry/ui/components/nm-icon/animated/clock-4.tsx +95 -0
- package/registry/ui/components/nm-icon/animated/clock.tsx +95 -0
- package/registry/ui/components/nm-icon/animated/cloud-sun.tsx +130 -0
- package/registry/ui/components/nm-icon/animated/cloud-upload.tsx +93 -0
- package/registry/ui/components/nm-icon/animated/copy.tsx +153 -0
- package/registry/ui/components/nm-icon/animated/download.tsx +93 -0
- package/registry/ui/components/nm-icon/animated/ellipsis.tsx +189 -0
- package/registry/ui/components/nm-icon/animated/external-link.tsx +97 -0
- package/registry/ui/components/nm-icon/animated/eye-off.tsx +63 -0
- package/registry/ui/components/nm-icon/animated/eye.tsx +82 -0
- package/registry/ui/components/nm-icon/animated/fast-forward.tsx +72 -0
- package/registry/ui/components/nm-icon/animated/file-clock.tsx +105 -0
- package/registry/ui/components/nm-icon/animated/file-text.tsx +102 -0
- package/registry/ui/components/nm-icon/animated/file.tsx +124 -0
- package/registry/ui/components/nm-icon/animated/folder.tsx +132 -0
- package/registry/ui/components/nm-icon/animated/gauge.tsx +84 -0
- package/registry/ui/components/nm-icon/animated/globe.tsx +61 -0
- package/registry/ui/components/nm-icon/animated/graduation-cap.tsx +92 -0
- package/registry/ui/components/nm-icon/animated/headphones.tsx +85 -0
- package/registry/ui/components/nm-icon/animated/image.tsx +88 -0
- package/registry/ui/components/nm-icon/animated/index.ts +251 -0
- package/registry/ui/components/nm-icon/animated/info.tsx +75 -0
- package/registry/ui/components/nm-icon/animated/key-round.tsx +67 -0
- package/registry/ui/components/nm-icon/animated/layers.tsx +116 -0
- package/registry/ui/components/nm-icon/animated/layout-dashboard.tsx +176 -0
- package/registry/ui/components/nm-icon/animated/library-big.tsx +89 -0
- package/registry/ui/components/nm-icon/animated/lightbulb.tsx +106 -0
- package/registry/ui/components/nm-icon/animated/link.tsx +210 -0
- package/registry/ui/components/nm-icon/animated/list-filter.tsx +78 -0
- package/registry/ui/components/nm-icon/animated/list.tsx +184 -0
- package/registry/ui/components/nm-icon/animated/loader-circle.tsx +68 -0
- package/registry/ui/components/nm-icon/animated/lock.tsx +152 -0
- package/registry/ui/components/nm-icon/animated/log-out.tsx +93 -0
- package/registry/ui/components/nm-icon/animated/mail.tsx +131 -0
- package/registry/ui/components/nm-icon/animated/map-pin.tsx +111 -0
- package/registry/ui/components/nm-icon/animated/menu.tsx +121 -0
- package/registry/ui/components/nm-icon/animated/message-circle-dashed.tsx +140 -0
- package/registry/ui/components/nm-icon/animated/message-circle.tsx +66 -0
- package/registry/ui/components/nm-icon/animated/message-square-plus.tsx +106 -0
- package/registry/ui/components/nm-icon/animated/message-square-text.tsx +153 -0
- package/registry/ui/components/nm-icon/animated/message-square.tsx +66 -0
- package/registry/ui/components/nm-icon/animated/mic-off.tsx +65 -0
- package/registry/ui/components/nm-icon/animated/mic.tsx +77 -0
- package/registry/ui/components/nm-icon/animated/minimize.tsx +164 -0
- package/registry/ui/components/nm-icon/animated/minus.tsx +84 -0
- package/registry/ui/components/nm-icon/animated/moon.tsx +98 -0
- package/registry/ui/components/nm-icon/animated/music.tsx +62 -0
- package/registry/ui/components/nm-icon/animated/notebook-pen.tsx +114 -0
- package/registry/ui/components/nm-icon/animated/panel-left.tsx +77 -0
- package/registry/ui/components/nm-icon/animated/panel-right.tsx +77 -0
- package/registry/ui/components/nm-icon/animated/paperclip.tsx +83 -0
- package/registry/ui/components/nm-icon/animated/pause.tsx +116 -0
- package/registry/ui/components/nm-icon/animated/pencil.tsx +70 -0
- package/registry/ui/components/nm-icon/animated/pin-off.tsx +102 -0
- package/registry/ui/components/nm-icon/animated/pin.tsx +124 -0
- package/registry/ui/components/nm-icon/animated/plane-takeoff.tsx +62 -0
- package/registry/ui/components/nm-icon/animated/play.tsx +74 -0
- package/registry/ui/components/nm-icon/animated/plug-zap.tsx +117 -0
- package/registry/ui/components/nm-icon/animated/plus.tsx +122 -0
- package/registry/ui/components/nm-icon/animated/radio.tsx +137 -0
- package/registry/ui/components/nm-icon/animated/refresh-cw.tsx +103 -0
- package/registry/ui/components/nm-icon/animated/rocket.tsx +93 -0
- package/registry/ui/components/nm-icon/animated/rotate-ccw.tsx +87 -0
- package/registry/ui/components/nm-icon/animated/rotate-cw.tsx +98 -0
- package/registry/ui/components/nm-icon/animated/save.tsx +58 -0
- package/registry/ui/components/nm-icon/animated/search.tsx +90 -0
- package/registry/ui/components/nm-icon/animated/send.tsx +87 -0
- package/registry/ui/components/nm-icon/animated/settings.tsx +92 -0
- package/registry/ui/components/nm-icon/animated/share-2.tsx +148 -0
- package/registry/ui/components/nm-icon/animated/share.tsx +76 -0
- package/registry/ui/components/nm-icon/animated/shield-check.tsx +70 -0
- package/registry/ui/components/nm-icon/animated/shopping-bag.tsx +71 -0
- package/registry/ui/components/nm-icon/animated/sliders-horizontal.tsx +243 -0
- package/registry/ui/components/nm-icon/animated/sparkles.tsx +227 -0
- package/registry/ui/components/nm-icon/animated/star.tsx +86 -0
- package/registry/ui/components/nm-icon/animated/sun.tsx +148 -0
- package/registry/ui/components/nm-icon/animated/terminal.tsx +71 -0
- package/registry/ui/components/nm-icon/animated/trash-2.tsx +136 -0
- package/registry/ui/components/nm-icon/animated/trash.tsx +94 -0
- package/registry/ui/components/nm-icon/animated/trending-up.tsx +75 -0
- package/registry/ui/components/nm-icon/animated/triangle-alert.tsx +75 -0
- package/registry/ui/components/nm-icon/animated/trophy.tsx +62 -0
- package/registry/ui/components/nm-icon/animated/type.tsx +106 -0
- package/registry/ui/components/nm-icon/animated/upload.tsx +94 -0
- package/registry/ui/components/nm-icon/animated/user-round.tsx +84 -0
- package/registry/ui/components/nm-icon/animated/user.tsx +84 -0
- package/registry/ui/components/nm-icon/animated/users-round.tsx +156 -0
- package/registry/ui/components/nm-icon/animated/users.tsx +156 -0
- package/registry/ui/components/nm-icon/animated/volume-2.tsx +119 -0
- package/registry/ui/components/nm-icon/animated/wand-sparkles.tsx +111 -0
- package/registry/ui/components/nm-icon/animated/x.tsx +122 -0
- package/registry/ui/components/nm-icon/animated/zap.tsx +86 -0
- package/registry/ui/components/nm-icon/animated/zoom-in.tsx +66 -0
- package/registry/ui/components/nm-icon/animated/zoom-out.tsx +85 -0
- package/registry/ui/components/nm-icon/nm-icon.css +0 -1
- package/registry/ui/components/nm-icon/nm-icon.tsx +20 -5
- package/registry/ui/components/nm-icon/nm-lucide-extended.tsx +284 -0
- package/registry/ui/components/nm-icon/nm-lucide.tsx +55 -5
- package/registry/ui/components/nm-input/nm-input.css +1 -0
- package/registry/ui/components/nm-menubar/nm-menubar.tsx +2 -2
- package/registry/ui/components/nm-panel/nm-panel.css +7 -0
- package/registry/ui/components/nm-panel/nm-panel.tsx +19 -2
- package/registry/ui/components/nm-popover/nm-popover.tsx +4 -3
- package/registry/ui/components/nm-progress/nm-progress.tsx +1 -1
- package/registry/ui/components/nm-radio-group/nm-radio-group.tsx +2 -2
- package/registry/ui/components/nm-select/nm-select.tsx +1 -1
- package/registry/ui/components/nm-skeleton/nm-skeleton.css +17 -0
- package/registry/ui/components/nm-skeleton/nm-skeleton.tsx +7 -0
- package/registry/ui/components/nm-slider/nm-slider.tsx +1 -1
- package/registry/ui/components/nm-small-icon-button/nm-small-icon-button.tsx +3 -3
- package/registry/ui/components/nm-spinner/nm-spinner.css +22 -0
- package/registry/ui/components/nm-spinner/nm-spinner.tsx +7 -0
- package/registry/ui/components/nm-switch/nm-switch.css +20 -3
- package/registry/ui/components/nm-switch/nm-switch.tsx +1 -1
- package/registry/ui/components/nm-tabs/nm-tabs.css +4 -0
- package/registry/ui/components/nm-tabs/nm-tabs.tsx +19 -0
- package/registry/ui/components/nm-textarea/nm-textarea.css +200 -0
- package/registry/ui/components/nm-textarea/nm-textarea.tsx +417 -3
- package/registry/ui/components/nm-toggle/nm-toggle.tsx +1 -1
- package/registry/ui/components/nm-toolbar/nm-toolbar.tsx +4 -1
- package/registry/ui/components/nm-tooltip/nm-tooltip.tsx +1 -1
- package/registry/ui/components/nm-voice-orb/nm-voice-orb.css +17 -0
- package/registry/ui/components/nm-voice-orb/nm-voice-orb.tsx +112 -0
- package/registry/ui/components/nm-voice-orb/use-nm-voice-orb-theme.ts +34 -0
- package/registry/ui/components/nordman-icon-button/nordman-icon-button.tsx +7 -3
- package/registry/ui/config/nm-button.ts +6 -0
- package/registry/ui/config/nm-composer.ts +12 -0
- package/registry/ui/config/nm-icons-extended.ts +141 -0
- package/registry/ui/config/nm-icons.ts +6 -2
- package/registry/ui/config/nm-voice-orb.ts +50 -0
- package/registry/ui/lib/nm-motion.ts +30 -0
- package/registry/ui/styles/tokens.css +7 -0
|
@@ -1,6 +1,420 @@
|
|
|
1
|
-
import
|
|
1
|
+
import {
|
|
2
|
+
forwardRef,
|
|
3
|
+
useCallback,
|
|
4
|
+
useEffect,
|
|
5
|
+
useId,
|
|
6
|
+
useRef,
|
|
7
|
+
useState,
|
|
8
|
+
type ChangeEvent,
|
|
9
|
+
type ComponentProps,
|
|
10
|
+
type ReactNode,
|
|
11
|
+
} from 'react';
|
|
12
|
+
import { createPortal } from 'react-dom';
|
|
2
13
|
import { nmClass } from '../../lib/nm-class.ts';
|
|
14
|
+
import { NmIcon } from '../nm-icon/nm-icon.tsx';
|
|
15
|
+
import { NmProse } from '../nm-prose/nm-prose.tsx';
|
|
16
|
+
import { NmSegmented } from '../nm-segmented/nm-segmented.tsx';
|
|
17
|
+
import { createLogger } from '../../kit-logger.ts';
|
|
3
18
|
|
|
4
|
-
|
|
5
|
-
|
|
19
|
+
const logger = createLogger('interface-nm-textarea');
|
|
20
|
+
|
|
21
|
+
export type NmTextareaVariant = 'default' | 'panel';
|
|
22
|
+
|
|
23
|
+
export type NmTextareaProps = ComponentProps<'textarea'> & {
|
|
24
|
+
variant?: NmTextareaVariant;
|
|
25
|
+
openInPanel?: boolean;
|
|
26
|
+
panelTitle?: string;
|
|
27
|
+
panelButtonLabel?: string;
|
|
28
|
+
panelDefaultTab?: 'code' | 'preview';
|
|
29
|
+
renderMarkdown?: (markdown: string) => ReactNode;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
function renderInline(text: string): ReactNode[] {
|
|
33
|
+
const parts: ReactNode[] = [];
|
|
34
|
+
const regex = /(\*\*[^*]+\*\*|\*[^*]+\*|`[^`]+`|\[[^\]]+\]\([^)]+\))/g;
|
|
35
|
+
let lastIndex = 0;
|
|
36
|
+
let match: RegExpExecArray | null;
|
|
37
|
+
|
|
38
|
+
while ((match = regex.exec(text)) !== null) {
|
|
39
|
+
if (match.index > lastIndex) {
|
|
40
|
+
parts.push(text.slice(lastIndex, match.index));
|
|
41
|
+
}
|
|
42
|
+
const token = match[0];
|
|
43
|
+
if (token.startsWith('**') && token.endsWith('**')) {
|
|
44
|
+
parts.push(<strong key={match.index}>{token.slice(2, -2)}</strong>);
|
|
45
|
+
} else if (token.startsWith('*') && token.endsWith('*')) {
|
|
46
|
+
parts.push(<em key={match.index}>{token.slice(1, -1)}</em>);
|
|
47
|
+
} else if (token.startsWith('`') && token.endsWith('`')) {
|
|
48
|
+
parts.push(<code key={match.index}>{token.slice(1, -1)}</code>);
|
|
49
|
+
} else if (token.startsWith('[') && token.includes('](')) {
|
|
50
|
+
const closeBracket = token.indexOf('](');
|
|
51
|
+
const label = token.slice(1, closeBracket);
|
|
52
|
+
const url = token.slice(closeBracket + 2, -1);
|
|
53
|
+
parts.push(
|
|
54
|
+
<a key={match.index} href={url} target="_blank" rel="noreferrer">
|
|
55
|
+
{label}
|
|
56
|
+
</a>,
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
lastIndex = regex.lastIndex;
|
|
60
|
+
}
|
|
61
|
+
if (lastIndex < text.length) {
|
|
62
|
+
parts.push(text.slice(lastIndex));
|
|
63
|
+
}
|
|
64
|
+
return parts.length > 0 ? parts : [text];
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function parseMarkdown(md: string): ReactNode {
|
|
68
|
+
if (!md.trim()) {
|
|
69
|
+
return <p className="nm-textarea-panel__empty">Nothing to preview</p>;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const lines = md.split('\n');
|
|
73
|
+
const nodes: ReactNode[] = [];
|
|
74
|
+
let i = 0;
|
|
75
|
+
|
|
76
|
+
while (i < lines.length) {
|
|
77
|
+
const line = lines[i];
|
|
78
|
+
|
|
79
|
+
if (line.startsWith('```')) {
|
|
80
|
+
const codeLines: string[] = [];
|
|
81
|
+
i++;
|
|
82
|
+
while (i < lines.length && !lines[i].startsWith('```')) {
|
|
83
|
+
codeLines.push(lines[i]);
|
|
84
|
+
i++;
|
|
85
|
+
}
|
|
86
|
+
i++;
|
|
87
|
+
nodes.push(
|
|
88
|
+
<pre key={`code-${i}`}>
|
|
89
|
+
<code>{codeLines.join('\n')}</code>
|
|
90
|
+
</pre>,
|
|
91
|
+
);
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (line.startsWith('#### ')) {
|
|
96
|
+
nodes.push(<h4 key={`h4-${i}`}>{renderInline(line.slice(5))}</h4>);
|
|
97
|
+
i++;
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
if (line.startsWith('### ')) {
|
|
101
|
+
nodes.push(<h3 key={`h3-${i}`}>{renderInline(line.slice(4))}</h3>);
|
|
102
|
+
i++;
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
if (line.startsWith('## ')) {
|
|
106
|
+
nodes.push(<h2 key={`h2-${i}`}>{renderInline(line.slice(3))}</h2>);
|
|
107
|
+
i++;
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
if (line.startsWith('# ')) {
|
|
111
|
+
nodes.push(<h1 key={`h1-${i}`}>{renderInline(line.slice(2))}</h1>);
|
|
112
|
+
i++;
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (/^(\*\*\*|---|___)$/.test(line.trim())) {
|
|
117
|
+
nodes.push(<hr key={`hr-${i}`} />);
|
|
118
|
+
i++;
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (line.startsWith('> ')) {
|
|
123
|
+
const quoteLines: string[] = [line.slice(2)];
|
|
124
|
+
i++;
|
|
125
|
+
while (i < lines.length && lines[i].startsWith('> ')) {
|
|
126
|
+
quoteLines.push(lines[i].slice(2));
|
|
127
|
+
i++;
|
|
128
|
+
}
|
|
129
|
+
nodes.push(
|
|
130
|
+
<blockquote key={`quote-${i}`}>
|
|
131
|
+
{quoteLines.map((ql, idx) => (
|
|
132
|
+
<p key={idx}>{renderInline(ql)}</p>
|
|
133
|
+
))}
|
|
134
|
+
</blockquote>,
|
|
135
|
+
);
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (/^[-*]\s+/.test(line)) {
|
|
140
|
+
const items: string[] = [line.replace(/^[-*]\s+/, '')];
|
|
141
|
+
i++;
|
|
142
|
+
while (i < lines.length && /^[-*]\s+/.test(lines[i])) {
|
|
143
|
+
items.push(lines[i].replace(/^[-*]\s+/, ''));
|
|
144
|
+
i++;
|
|
145
|
+
}
|
|
146
|
+
nodes.push(
|
|
147
|
+
<ul key={`ul-${i}`}>
|
|
148
|
+
{items.map((item, idx) => (
|
|
149
|
+
<li key={idx}>{renderInline(item)}</li>
|
|
150
|
+
))}
|
|
151
|
+
</ul>,
|
|
152
|
+
);
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (/^\d+\.\s+/.test(line)) {
|
|
157
|
+
const items: string[] = [line.replace(/^\d+\.\s+/, '')];
|
|
158
|
+
i++;
|
|
159
|
+
while (i < lines.length && /^\d+\.\s+/.test(lines[i])) {
|
|
160
|
+
items.push(lines[i].replace(/^\d+\.\s+/, ''));
|
|
161
|
+
i++;
|
|
162
|
+
}
|
|
163
|
+
nodes.push(
|
|
164
|
+
<ol key={`ol-${i}`}>
|
|
165
|
+
{items.map((item, idx) => (
|
|
166
|
+
<li key={idx}>{renderInline(item)}</li>
|
|
167
|
+
))}
|
|
168
|
+
</ol>,
|
|
169
|
+
);
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (!line.trim()) {
|
|
174
|
+
i++;
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const paraLines: string[] = [line];
|
|
179
|
+
i++;
|
|
180
|
+
while (
|
|
181
|
+
i < lines.length &&
|
|
182
|
+
lines[i].trim() &&
|
|
183
|
+
!lines[i].startsWith('#') &&
|
|
184
|
+
!lines[i].startsWith('```') &&
|
|
185
|
+
!lines[i].startsWith('> ') &&
|
|
186
|
+
!/^[-*]\s+/.test(lines[i]) &&
|
|
187
|
+
!/^\d+\.\s+/.test(lines[i]) &&
|
|
188
|
+
!/^(\*\*\*|---|___)$/.test(lines[i].trim())
|
|
189
|
+
) {
|
|
190
|
+
paraLines.push(lines[i]);
|
|
191
|
+
i++;
|
|
192
|
+
}
|
|
193
|
+
nodes.push(<p key={`p-${i}`}>{renderInline(paraLines.join(' '))}</p>);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return nodes;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const PANEL_TABS = [
|
|
200
|
+
{ id: 'code', label: 'Code', icon: 'code' as const },
|
|
201
|
+
{ id: 'preview', label: 'Preview', icon: 'presentation' as const },
|
|
202
|
+
] as const;
|
|
203
|
+
|
|
204
|
+
export const NmTextarea = forwardRef<HTMLTextAreaElement, NmTextareaProps>(
|
|
205
|
+
function NmTextarea(
|
|
206
|
+
{
|
|
207
|
+
className,
|
|
208
|
+
variant = 'default',
|
|
209
|
+
openInPanel = false,
|
|
210
|
+
panelTitle,
|
|
211
|
+
panelButtonLabel = 'Open in panel',
|
|
212
|
+
panelDefaultTab = 'code',
|
|
213
|
+
renderMarkdown,
|
|
214
|
+
value,
|
|
215
|
+
defaultValue,
|
|
216
|
+
onChange,
|
|
217
|
+
...props
|
|
218
|
+
},
|
|
219
|
+
forwardedRef,
|
|
220
|
+
) {
|
|
221
|
+
const isPanelEnabled = variant === 'panel' || openInPanel;
|
|
222
|
+
const [panelOpen, setPanelOpen] = useState(false);
|
|
223
|
+
const [tab, setTab] = useState<'code' | 'preview'>(panelDefaultTab);
|
|
224
|
+
const [copied, setCopied] = useState(false);
|
|
225
|
+
const triggerRef = useRef<HTMLButtonElement>(null);
|
|
226
|
+
const internalTextareaRef = useRef<HTMLTextAreaElement | null>(null);
|
|
227
|
+
const [uncontrolledValue, setUncontrolledValue] = useState(() => String(defaultValue ?? ''));
|
|
228
|
+
|
|
229
|
+
const isControlled = value !== undefined;
|
|
230
|
+
const currentValue = isControlled ? String(value) : uncontrolledValue;
|
|
231
|
+
|
|
232
|
+
const handleTextareaChange = (event: ChangeEvent<HTMLTextAreaElement>) => {
|
|
233
|
+
if (!isControlled) {
|
|
234
|
+
setUncontrolledValue(event.target.value);
|
|
235
|
+
}
|
|
236
|
+
onChange?.(event);
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
const handlePanelTextChange = (nextText: string) => {
|
|
240
|
+
if (!isControlled) {
|
|
241
|
+
setUncontrolledValue(nextText);
|
|
242
|
+
}
|
|
243
|
+
if (internalTextareaRef.current) {
|
|
244
|
+
internalTextareaRef.current.value = nextText;
|
|
245
|
+
}
|
|
246
|
+
if (onChange) {
|
|
247
|
+
const syntheticEvent = {
|
|
248
|
+
target: { value: nextText, name: props.name },
|
|
249
|
+
currentTarget: { value: nextText, name: props.name },
|
|
250
|
+
} as ChangeEvent<HTMLTextAreaElement>;
|
|
251
|
+
onChange(syntheticEvent);
|
|
252
|
+
}
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
const handleCopy = () => {
|
|
256
|
+
if (typeof navigator !== 'undefined' && navigator.clipboard) {
|
|
257
|
+
void navigator.clipboard.writeText(currentValue);
|
|
258
|
+
setCopied(true);
|
|
259
|
+
setTimeout(() => setCopied(false), 1500);
|
|
260
|
+
}
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
const handleClose = useCallback(() => {
|
|
264
|
+
setPanelOpen(false);
|
|
265
|
+
triggerRef.current?.focus();
|
|
266
|
+
}, []);
|
|
267
|
+
|
|
268
|
+
useEffect(() => {
|
|
269
|
+
if (!panelOpen) return;
|
|
270
|
+
const onKeyDown = (e: KeyboardEvent) => {
|
|
271
|
+
if (e.key === 'Escape') {
|
|
272
|
+
e.preventDefault();
|
|
273
|
+
handleClose();
|
|
274
|
+
}
|
|
275
|
+
};
|
|
276
|
+
window.addEventListener('keydown', onKeyDown);
|
|
277
|
+
return () => window.removeEventListener('keydown', onKeyDown);
|
|
278
|
+
}, [panelOpen, handleClose]);
|
|
279
|
+
|
|
280
|
+
const titleId = useId();
|
|
281
|
+
const lineCount = currentValue ? currentValue.split('\n').length : 0;
|
|
282
|
+
const charCount = currentValue.length;
|
|
283
|
+
|
|
284
|
+
const textareaElement = (
|
|
285
|
+
<textarea
|
|
286
|
+
className={nmClass('nm-textarea', className)}
|
|
287
|
+
ref={(node) => {
|
|
288
|
+
internalTextareaRef.current = node;
|
|
289
|
+
if (typeof forwardedRef === 'function') {
|
|
290
|
+
forwardedRef(node);
|
|
291
|
+
} else if (forwardedRef) {
|
|
292
|
+
forwardedRef.current = node;
|
|
293
|
+
}
|
|
294
|
+
}}
|
|
295
|
+
data-slot="textarea"
|
|
296
|
+
value={value}
|
|
297
|
+
defaultValue={defaultValue}
|
|
298
|
+
onChange={handleTextareaChange}
|
|
299
|
+
{...props}
|
|
300
|
+
/>
|
|
301
|
+
);
|
|
302
|
+
|
|
303
|
+
if (!isPanelEnabled) {
|
|
304
|
+
return textareaElement;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
const panelContent = panelOpen && typeof document !== 'undefined' ? (
|
|
308
|
+
createPortal(
|
|
309
|
+
<>
|
|
310
|
+
<div
|
|
311
|
+
className="nm-textarea-panel__backdrop"
|
|
312
|
+
onClick={handleClose}
|
|
313
|
+
aria-hidden="true"
|
|
314
|
+
/>
|
|
315
|
+
<aside
|
|
316
|
+
role="dialog"
|
|
317
|
+
aria-modal="true"
|
|
318
|
+
aria-labelledby={panelTitle ? titleId : undefined}
|
|
319
|
+
aria-label={panelTitle ? undefined : 'Markdown Editor'}
|
|
320
|
+
className="nm-textarea-panel"
|
|
321
|
+
>
|
|
322
|
+
<header className="nm-textarea-panel__header">
|
|
323
|
+
<div className="nm-textarea-panel__title-group">
|
|
324
|
+
{panelTitle ? (
|
|
325
|
+
<h3 id={titleId} className="nm-textarea-panel__title">
|
|
326
|
+
{panelTitle}
|
|
327
|
+
</h3>
|
|
328
|
+
) : null}
|
|
329
|
+
<NmSegmented
|
|
330
|
+
label="Editor view"
|
|
331
|
+
tone="pills"
|
|
332
|
+
items={PANEL_TABS}
|
|
333
|
+
active={tab}
|
|
334
|
+
onSelect={(next) => setTab(next as 'code' | 'preview')}
|
|
335
|
+
/>
|
|
336
|
+
</div>
|
|
337
|
+
<div className="nm-textarea-panel__actions">
|
|
338
|
+
<button
|
|
339
|
+
type="button"
|
|
340
|
+
className="nm-textarea-panel__action-btn"
|
|
341
|
+
aria-label={copied ? 'Copied' : 'Copy markdown'}
|
|
342
|
+
title={copied ? 'Copied' : 'Copy markdown'}
|
|
343
|
+
onClick={handleCopy}
|
|
344
|
+
>
|
|
345
|
+
<NmIcon id={copied ? 'check' : 'copy'} />
|
|
346
|
+
</button>
|
|
347
|
+
<button
|
|
348
|
+
type="button"
|
|
349
|
+
className="nm-textarea-panel__action-btn"
|
|
350
|
+
aria-label="Close panel"
|
|
351
|
+
title="Close panel"
|
|
352
|
+
onClick={handleClose}
|
|
353
|
+
>
|
|
354
|
+
<NmIcon id="x" />
|
|
355
|
+
</button>
|
|
356
|
+
</div>
|
|
357
|
+
</header>
|
|
358
|
+
|
|
359
|
+
<div className="nm-textarea-panel__body">
|
|
360
|
+
{tab === 'code' ? (
|
|
361
|
+
<textarea
|
|
362
|
+
className="nm-textarea-panel__editor"
|
|
363
|
+
value={currentValue}
|
|
364
|
+
placeholder={props.placeholder ?? 'Enter markdown content...'}
|
|
365
|
+
aria-label="Markdown source"
|
|
366
|
+
autoFocus
|
|
367
|
+
onChange={(e) => handlePanelTextChange(e.target.value)}
|
|
368
|
+
/>
|
|
369
|
+
) : (
|
|
370
|
+
<div className="nm-textarea-panel__preview">
|
|
371
|
+
<NmProse>
|
|
372
|
+
{renderMarkdown ? renderMarkdown(currentValue) : parseMarkdown(currentValue)}
|
|
373
|
+
</NmProse>
|
|
374
|
+
</div>
|
|
375
|
+
)}
|
|
376
|
+
</div>
|
|
377
|
+
|
|
378
|
+
<footer className="nm-textarea-panel__footer">
|
|
379
|
+
<span>
|
|
380
|
+
{lineCount} {lineCount === 1 ? 'line' : 'lines'} · {charCount} characters
|
|
381
|
+
</span>
|
|
382
|
+
<button
|
|
383
|
+
type="button"
|
|
384
|
+
className="nm-textarea-panel__done"
|
|
385
|
+
onClick={handleClose}
|
|
386
|
+
>
|
|
387
|
+
Done
|
|
388
|
+
</button>
|
|
389
|
+
</footer>
|
|
390
|
+
</aside>
|
|
391
|
+
</>,
|
|
392
|
+
document.body,
|
|
393
|
+
)
|
|
394
|
+
) : null;
|
|
395
|
+
|
|
396
|
+
logger.debug('render', variant, { panelOpen });
|
|
397
|
+
|
|
398
|
+
return (
|
|
399
|
+
<div className="nm-textarea-wrap">
|
|
400
|
+
{textareaElement}
|
|
401
|
+
<button
|
|
402
|
+
ref={triggerRef}
|
|
403
|
+
type="button"
|
|
404
|
+
className="nm-textarea__panel-trigger"
|
|
405
|
+
aria-label={panelButtonLabel}
|
|
406
|
+
aria-expanded={panelOpen}
|
|
407
|
+
title={panelButtonLabel}
|
|
408
|
+
onClick={() => setPanelOpen(true)}
|
|
409
|
+
>
|
|
410
|
+
<NmIcon id="panel" />
|
|
411
|
+
</button>
|
|
412
|
+
{panelContent}
|
|
413
|
+
</div>
|
|
414
|
+
);
|
|
415
|
+
},
|
|
416
|
+
);
|
|
417
|
+
|
|
418
|
+
export function NmTextareaPanel(props: NmTextareaProps) {
|
|
419
|
+
return <NmTextarea variant="panel" {...props} />;
|
|
6
420
|
}
|
|
@@ -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
|
-
|
|
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 '
|
|
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
|
-
|
|
178
|
-
<
|
|
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
|
-
</
|
|
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',
|