@nsaa/core 0.1.0-alpha.1 → 0.1.0-alpha.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (286) hide show
  1. package/README.md +30 -8
  2. package/dist/_virtual/_rolldown/runtime.js +13 -0
  3. package/dist/accordion/accordion.d.ts +7 -0
  4. package/dist/accordion/accordion.js +54 -0
  5. package/dist/accordion/index.d.ts +3 -0
  6. package/dist/alert/alert.d.ts +19 -0
  7. package/dist/alert/alert.js +58 -0
  8. package/dist/alert/index.d.ts +3 -0
  9. package/dist/alert-dialog/alert-dialog.d.ts +19 -0
  10. package/dist/alert-dialog/alert-dialog.js +106 -0
  11. package/dist/alert-dialog/index.d.ts +3 -0
  12. package/dist/avatar/avatar.d.ts +12 -0
  13. package/dist/avatar/avatar.js +50 -0
  14. package/dist/avatar/index.d.ts +3 -0
  15. package/dist/badge/badge.d.ts +8 -0
  16. package/dist/badge/badge.js +29 -0
  17. package/dist/badge/index.d.ts +3 -0
  18. package/dist/button/button.d.ts +25 -0
  19. package/dist/button/button.js +136 -0
  20. package/dist/button/index.d.ts +3 -0
  21. package/dist/calendar/calendar.d.ts +11 -0
  22. package/dist/calendar/calendar.js +131 -0
  23. package/dist/calendar/index.d.ts +3 -0
  24. package/dist/card/card.d.ts +12 -0
  25. package/dist/card/card.js +55 -0
  26. package/dist/card/index.d.ts +3 -0
  27. package/dist/checkbox/checkbox.d.ts +4 -0
  28. package/dist/checkbox/checkbox.js +35 -0
  29. package/dist/checkbox/index.d.ts +3 -0
  30. package/dist/collapsible/collapsible.d.ts +6 -0
  31. package/dist/collapsible/collapsible.js +24 -0
  32. package/dist/collapsible/index.d.ts +3 -0
  33. package/dist/combobox/combobox.d.ts +27 -0
  34. package/dist/combobox/combobox.js +226 -0
  35. package/dist/combobox/index.d.ts +3 -0
  36. package/dist/command/command.d.ts +20 -0
  37. package/dist/command/command.js +116 -0
  38. package/dist/command/index.d.ts +3 -0
  39. package/dist/context-menu/context-menu.d.ts +30 -0
  40. package/dist/context-menu/context-menu.js +173 -0
  41. package/dist/context-menu/index.d.ts +3 -0
  42. package/dist/dialog/dialog.d.ts +18 -0
  43. package/dist/dialog/dialog.js +112 -0
  44. package/dist/dialog/index.d.ts +3 -0
  45. package/dist/direction.d.ts +3 -0
  46. package/dist/direction.js +2 -0
  47. package/dist/drawer/drawer.d.ts +17 -0
  48. package/dist/drawer/drawer.js +136 -0
  49. package/dist/drawer/index.d.ts +3 -0
  50. package/dist/dropdown-menu/dropdown-menu.d.ts +30 -0
  51. package/dist/dropdown-menu/dropdown-menu.js +177 -0
  52. package/dist/dropdown-menu/index.d.ts +3 -0
  53. package/dist/empty/empty.d.ts +35 -0
  54. package/dist/empty/empty.js +93 -0
  55. package/dist/empty/index.d.ts +3 -0
  56. package/dist/field/field.d.ts +26 -0
  57. package/dist/field/field.js +109 -0
  58. package/dist/field/index.d.ts +3 -0
  59. package/dist/index.d.ts +90 -674
  60. package/dist/index.js +50 -3877
  61. package/dist/input/index.d.ts +3 -0
  62. package/dist/input/input.d.ts +4 -0
  63. package/dist/input/input.js +14 -0
  64. package/dist/input-group/index.d.ts +3 -0
  65. package/dist/input-group/input-group.d.ts +19 -0
  66. package/dist/input-group/input-group.js +78 -0
  67. package/dist/input-otp/index.d.ts +3 -0
  68. package/dist/input-otp/input-otp.d.ts +12 -0
  69. package/dist/input-otp/input-otp.js +73 -0
  70. package/dist/kbd/index.d.ts +3 -0
  71. package/dist/kbd/kbd.d.ts +5 -0
  72. package/dist/kbd/kbd.js +19 -0
  73. package/dist/label/index.d.ts +3 -0
  74. package/dist/label/label.d.ts +4 -0
  75. package/dist/label/label.js +13 -0
  76. package/dist/legacy/icon-button/icon-swap.js +58 -0
  77. package/dist/lib/cn.d.ts +3 -0
  78. package/dist/{cn-DxFBFdwT.js → lib/cn.js} +1 -1
  79. package/dist/lib/control-defaults.js +8 -0
  80. package/dist/lib/icon-swap.d.ts +15 -0
  81. package/dist/lib/icon-swap.js +26 -0
  82. package/dist/lib/input-modality.js +39 -0
  83. package/dist/lib/merge-refs.js +21 -0
  84. package/dist/lib/opener.js +29 -0
  85. package/dist/lib/shortcut.js +50 -0
  86. package/dist/lib/track-input-modality.d.ts +2 -0
  87. package/dist/lib/track-input-modality.js +4 -0
  88. package/dist/lib/tv.js +7 -0
  89. package/dist/lib/use-press.js +48 -0
  90. package/dist/lib/user-action.js +13 -0
  91. package/dist/loaders/arc.d.ts +6 -0
  92. package/dist/loaders/arc.js +31 -0
  93. package/dist/loaders/atom.d.ts +5 -0
  94. package/dist/loaders/atom.js +70 -0
  95. package/dist/loaders/blocks.d.ts +8 -0
  96. package/dist/loaders/blocks.js +74 -0
  97. package/dist/loaders/bouncing-dots.d.ts +3 -0
  98. package/dist/loaders/bouncing-dots.js +45 -0
  99. package/dist/loaders/cap.d.ts +6 -0
  100. package/dist/loaders/cap.js +7 -0
  101. package/dist/loaders/cascade.d.ts +5 -0
  102. package/dist/loaders/cascade.js +50 -0
  103. package/dist/loaders/circular-dots.d.ts +3 -0
  104. package/dist/loaders/circular-dots.js +40 -0
  105. package/dist/loaders/classic-v2.d.ts +3 -0
  106. package/dist/loaders/classic-v2.js +41 -0
  107. package/dist/loaders/classic.d.ts +3 -0
  108. package/dist/loaders/classic.js +48 -0
  109. package/dist/loaders/clock.d.ts +5 -0
  110. package/dist/loaders/clock.js +31 -0
  111. package/dist/loaders/comet.d.ts +5 -0
  112. package/dist/loaders/comet.js +52 -0
  113. package/dist/loaders/compass.d.ts +3 -0
  114. package/dist/loaders/compass.js +39 -0
  115. package/dist/loaders/dual.d.ts +6 -0
  116. package/dist/loaders/dual.js +47 -0
  117. package/dist/loaders/easing.d.ts +6 -0
  118. package/dist/loaders/easing.js +34 -0
  119. package/dist/loaders/eclipse.d.ts +3 -0
  120. package/dist/loaders/eclipse.js +69 -0
  121. package/dist/loaders/fade.js +28 -0
  122. package/dist/loaders/flip.d.ts +3 -0
  123. package/dist/loaders/flip.js +41 -0
  124. package/dist/{frame-CK9IBOZB.js → loaders/frame.js} +2 -50
  125. package/dist/loaders/gather.d.ts +3 -0
  126. package/dist/loaders/gather.js +85 -0
  127. package/dist/loaders/leap.d.ts +3 -0
  128. package/dist/loaders/leap.js +67 -0
  129. package/dist/loaders/linear-dots.d.ts +3 -0
  130. package/dist/loaders/linear-dots.js +54 -0
  131. package/dist/loaders/loading.d.ts +3 -0
  132. package/dist/loaders/loading.js +64 -0
  133. package/dist/loaders/morph.d.ts +3 -0
  134. package/dist/loaders/morph.js +40 -0
  135. package/dist/loaders/motion.d.ts +35 -0
  136. package/dist/loaders/motion.js +51 -0
  137. package/dist/loaders/orbit.d.ts +5 -0
  138. package/dist/loaders/orbit.js +44 -0
  139. package/dist/loaders/pulse.d.ts +3 -0
  140. package/dist/loaders/pulse.js +52 -0
  141. package/dist/loaders/radar.d.ts +5 -0
  142. package/dist/loaders/radar.js +64 -0
  143. package/dist/loaders/ring.d.ts +6 -0
  144. package/dist/loaders/ring.js +38 -0
  145. package/dist/loaders/ripple.d.ts +8 -0
  146. package/dist/loaders/ripple.js +57 -0
  147. package/dist/loaders/slide.d.ts +3 -0
  148. package/dist/loaders/slide.js +74 -0
  149. package/dist/loaders/snake.d.ts +6 -0
  150. package/dist/loaders/snake.js +59 -0
  151. package/dist/loaders/spinners.d.ts +61 -0
  152. package/dist/loaders/spinners.js +63 -0
  153. package/dist/loaders/swirl.d.ts +3 -0
  154. package/dist/loaders/swirl.js +50 -0
  155. package/dist/loaders/text/shared.js +25 -0
  156. package/dist/loaders/text/text-loaders.d.ts +48 -0
  157. package/dist/loaders/text/text-loaders.js +723 -0
  158. package/dist/loaders/trace.d.ts +6 -0
  159. package/dist/loaders/trace.js +46 -0
  160. package/dist/loaders/types.d.ts +10 -0
  161. package/dist/loaders/wave.d.ts +8 -0
  162. package/dist/loaders/wave.js +59 -0
  163. package/dist/loaders-text.d.ts +2 -47
  164. package/dist/loaders-text.js +1 -743
  165. package/dist/loaders.d.ts +36 -132
  166. package/dist/loaders.js +35 -1096
  167. package/dist/native-select/index.d.ts +3 -0
  168. package/dist/native-select/native-select.d.ts +9 -0
  169. package/dist/native-select/native-select.js +44 -0
  170. package/dist/pagination/index.d.ts +3 -0
  171. package/dist/pagination/pagination.d.ts +18 -0
  172. package/dist/pagination/pagination.js +134 -0
  173. package/dist/popover/index.d.ts +3 -0
  174. package/dist/popover/popover.d.ts +10 -0
  175. package/dist/popover/popover.js +54 -0
  176. package/dist/radio-group/index.d.ts +3 -0
  177. package/dist/radio-group/radio-group.d.ts +6 -0
  178. package/dist/radio-group/radio-group.js +33 -0
  179. package/dist/select/index.d.ts +3 -0
  180. package/dist/select/select.d.ts +16 -0
  181. package/dist/select/select.js +148 -0
  182. package/dist/separator/index.d.ts +3 -0
  183. package/dist/separator/separator.d.ts +4 -0
  184. package/dist/separator/separator.js +15 -0
  185. package/dist/skeleton/index.d.ts +3 -0
  186. package/dist/skeleton/skeleton.d.ts +10 -0
  187. package/dist/skeleton/skeleton.js +24 -0
  188. package/dist/slider/index.d.ts +3 -0
  189. package/dist/slider/slider.d.ts +4 -0
  190. package/dist/slider/slider.js +39 -0
  191. package/dist/sound/lazy-uisfx.d.ts +20 -0
  192. package/dist/sound/lazy-uisfx.js +68 -0
  193. package/dist/sound/sound-map.d.ts +72 -0
  194. package/dist/sound/sound-map.js +76 -0
  195. package/dist/sound/sound.d.ts +38 -0
  196. package/dist/sound/sound.js +136 -0
  197. package/dist/sound/uisfx.d.ts +41 -0
  198. package/dist/sound/uisfx.js +59 -0
  199. package/dist/sound-uisfx.d.ts +2 -37
  200. package/dist/sound-uisfx.js +1 -58
  201. package/dist/sources/accordion.css +3 -0
  202. package/dist/sources/alert-dialog.css +26 -0
  203. package/dist/sources/alert.css +8 -0
  204. package/dist/sources/avatar.css +3 -0
  205. package/dist/sources/badge.css +3 -0
  206. package/dist/sources/button.css +24 -0
  207. package/dist/sources/calendar.css +25 -0
  208. package/dist/sources/card.css +3 -0
  209. package/dist/sources/checkbox.css +6 -0
  210. package/dist/sources/collapsible.css +2 -0
  211. package/dist/sources/combobox.css +28 -0
  212. package/dist/sources/command.css +30 -0
  213. package/dist/sources/context-menu.css +6 -0
  214. package/dist/sources/dialog.css +26 -0
  215. package/dist/sources/drawer.css +6 -0
  216. package/dist/sources/dropdown-menu.css +6 -0
  217. package/dist/sources/empty.css +3 -0
  218. package/dist/sources/field.css +5 -0
  219. package/dist/sources/input-group.css +27 -0
  220. package/dist/sources/input-otp.css +8 -0
  221. package/dist/sources/input.css +3 -0
  222. package/dist/sources/kbd.css +3 -0
  223. package/dist/sources/label.css +3 -0
  224. package/dist/sources/loaders-text.css +8 -0
  225. package/dist/sources/loaders.css +40 -0
  226. package/dist/sources/native-select.css +3 -0
  227. package/dist/sources/pagination.css +25 -0
  228. package/dist/sources/popover.css +3 -0
  229. package/dist/sources/radio-group.css +6 -0
  230. package/dist/sources/select.css +6 -0
  231. package/dist/sources/separator.css +3 -0
  232. package/dist/sources/skeleton.css +3 -0
  233. package/dist/sources/slider.css +6 -0
  234. package/dist/sources/spinner.css +20 -0
  235. package/dist/sources/switch.css +6 -0
  236. package/dist/sources/table.css +3 -0
  237. package/dist/sources/tabs.css +3 -0
  238. package/dist/sources/textarea.css +3 -0
  239. package/dist/sources/toast.css +25 -0
  240. package/dist/sources/toggle-group.css +32 -0
  241. package/dist/sources/toolbar.css +34 -0
  242. package/dist/sources/tooltip.css +3 -0
  243. package/dist/spinner/index.d.ts +5 -0
  244. package/dist/spinner/loader-provider.d.ts +16 -0
  245. package/dist/spinner/loader-provider.js +28 -0
  246. package/dist/spinner/registry.d.ts +9 -0
  247. package/dist/spinner/registry.js +15 -0
  248. package/dist/spinner/spinner.d.ts +26 -0
  249. package/dist/spinner/spinner.js +143 -0
  250. package/dist/switch/index.d.ts +3 -0
  251. package/dist/switch/switch.d.ts +6 -0
  252. package/dist/switch/switch.js +25 -0
  253. package/dist/table/index.d.ts +3 -0
  254. package/dist/table/table.d.ts +17 -0
  255. package/dist/table/table.js +66 -0
  256. package/dist/tabs/index.d.ts +3 -0
  257. package/dist/tabs/tabs.d.ts +11 -0
  258. package/dist/tabs/tabs.js +46 -0
  259. package/dist/textarea/index.d.ts +3 -0
  260. package/dist/textarea/textarea.d.ts +4 -0
  261. package/dist/textarea/textarea.js +12 -0
  262. package/dist/toast/index.d.ts +3 -0
  263. package/dist/toast/toast.d.ts +17 -0
  264. package/dist/toast/toast.js +211 -0
  265. package/dist/toggle-group/index.d.ts +3 -0
  266. package/dist/toggle-group/toggle-group.d.ts +64 -0
  267. package/dist/toggle-group/toggle-group.js +95 -0
  268. package/dist/tokens/dist/motion.d.ts +24 -0
  269. package/dist/{motion-NQq2BA78.js → tokens/dist/motion.js} +1 -1
  270. package/dist/toolbar/index.d.ts +3 -0
  271. package/dist/toolbar/overflow.d.ts +4 -0
  272. package/dist/toolbar/overflow.js +242 -0
  273. package/dist/toolbar/toolbar-item.d.ts +29 -0
  274. package/dist/toolbar/toolbar-item.js +71 -0
  275. package/dist/toolbar/toolbar.d.ts +73 -0
  276. package/dist/toolbar/toolbar.js +250 -0
  277. package/dist/tooltip/index.d.ts +3 -0
  278. package/dist/tooltip/tooltip.d.ts +7 -0
  279. package/dist/tooltip/tooltip.js +41 -0
  280. package/package.json +9 -5
  281. package/src/feel.css +52 -0
  282. package/src/styles-base.css +142 -0
  283. package/src/styles.css +3 -133
  284. package/dist/registry-2PyPW9Xw.js +0 -390
  285. package/dist/registry-DjfWNw3R.d.ts +0 -18
  286. package/dist/sound-DiYNAnR_.d.ts +0 -61
@@ -0,0 +1,20 @@
1
+
2
+ import { UisfxPlayerOptions, UisfxSoundPlayer, uisfx_d_exports } from "./uisfx.js";
3
+ type Engine = typeof uisfx_d_exports;
4
+ /**
5
+ * The recommended sound setup (#144): a `SoundPlayer` that loads the UISFX engine on first use, so
6
+ * it stays out of the app's first load. The first trusted pointer or key action starts the import
7
+ * (cues before it finishes are skipped, as they would be before unlocking anyway). If the import
8
+ * fails, say offline, it stays silent and tries again on the next interaction; a cue never throws.
9
+ *
10
+ * Create one per app, at module scope:
11
+ *
12
+ * ```tsx
13
+ * const player = lazyUisfxPlayer();
14
+ * <SoundProvider player={player}>…</SoundProvider>
15
+ * ```
16
+ */
17
+ export declare function lazyUisfxPlayer(options?: UisfxPlayerOptions,
18
+ /** @internal Tests swap the import. */
19
+ importEngine?: () => Promise<Engine>): UisfxSoundPlayer;
20
+ export {};
@@ -0,0 +1,68 @@
1
+ "use client";
2
+ import { sound } from "../tokens/dist/motion.js";
3
+ // src/sound/lazy-uisfx.ts
4
+ const GESTURES = ["pointerdown", "keydown"];
5
+ /**
6
+ * The recommended sound setup (#144): a `SoundPlayer` that loads the UISFX engine on first use, so
7
+ * it stays out of the app's first load. The first trusted pointer or key action starts the import
8
+ * (cues before it finishes are skipped, as they would be before unlocking anyway). If the import
9
+ * fails, say offline, it stays silent and tries again on the next interaction; a cue never throws.
10
+ *
11
+ * Create one per app, at module scope:
12
+ *
13
+ * ```tsx
14
+ * const player = lazyUisfxPlayer();
15
+ * <SoundProvider player={player}>…</SoundProvider>
16
+ * ```
17
+ */
18
+ function lazyUisfxPlayer(options = {}, importEngine = () => import("./uisfx.js")) {
19
+ let engine = null;
20
+ let loading = false;
21
+ let pack = options.pack;
22
+ let gestured = false;
23
+ const listenOn = options.target ?? (typeof window === "undefined" ? void 0 : window);
24
+ const stopListening = () => {
25
+ for (const type of GESTURES) listenOn?.removeEventListener(type, onGesture, true);
26
+ };
27
+ const load = () => {
28
+ if (engine || loading) return;
29
+ loading = true;
30
+ importEngine().then(({ createUisfxPlayer }) => {
31
+ engine = createUisfxPlayer({
32
+ ...options,
33
+ pack
34
+ });
35
+ stopListening();
36
+ if (gestured) engine.unlock();
37
+ }).catch(() => {}).finally(() => {
38
+ loading = false;
39
+ });
40
+ };
41
+ function onGesture(event) {
42
+ if (!event.isTrusted) return;
43
+ gestured = true;
44
+ load();
45
+ }
46
+ for (const type of GESTURES) listenOn?.addEventListener(type, onGesture, true);
47
+ const play = ((cue, playOptions) => {
48
+ if (engine) return engine(cue, playOptions);
49
+ load();
50
+ });
51
+ play.setPack = (next) => {
52
+ pack = next;
53
+ engine?.setPack(next);
54
+ };
55
+ play.getPack = () => engine?.getPack() ?? pack ?? sound.feel;
56
+ play.isUnlocked = () => engine?.isUnlocked() ?? false;
57
+ play.unlock = () => {
58
+ gestured = true;
59
+ if (engine) engine.unlock();
60
+ else load();
61
+ };
62
+ play.destroy = async () => {
63
+ stopListening();
64
+ await engine?.destroy();
65
+ };
66
+ return play;
67
+ }
68
+ export { lazyUisfxPlayer };
@@ -0,0 +1,72 @@
1
+
2
+ import "./sound.js";
3
+ import { ReactNode } from "react";
4
+ /**
5
+ * The one table of interface sounds: component × event → cue (feel layer, §4). Components play
6
+ * from here, so the map is reviewable in one place. Surfaces that aren't listed (Popover, Tooltip,
7
+ * Command, menu open and close, Select open, Tabs, Accordion, Collapsible, inputs) are silent: opening a light surface isn't a commit,
8
+ * and the palette is a 100+/day tool. Nothing plays on hover, load or typing.
9
+ */
10
+ export declare const SOUND_MAP: {
11
+ readonly button: {
12
+ readonly press: "input.press";
13
+ };
14
+ readonly checkbox: {
15
+ readonly on: "input.toggle-on";
16
+ readonly off: "input.toggle-off";
17
+ };
18
+ readonly "radio-group": {
19
+ readonly change: "selection.select";
20
+ };
21
+ readonly switch: {
22
+ readonly on: "input.toggle-on";
23
+ readonly off: "input.toggle-off";
24
+ };
25
+ readonly slider: {
26
+ readonly commit: "movement.snap";
27
+ };
28
+ readonly "input-otp": {
29
+ readonly invalid: "feedback.error";
30
+ };
31
+ readonly alert: {
32
+ readonly destructive: "feedback.error";
33
+ readonly success: "feedback.success";
34
+ readonly warning: "feedback.warning";
35
+ };
36
+ readonly dialog: {
37
+ readonly open: "navigation.open";
38
+ readonly close: "navigation.close";
39
+ };
40
+ readonly "alert-dialog": {
41
+ readonly open: "navigation.open";
42
+ readonly close: "navigation.close";
43
+ };
44
+ readonly drawer: {
45
+ readonly open: "navigation.open";
46
+ readonly close: "navigation.close";
47
+ readonly snap: "movement.snap";
48
+ };
49
+ readonly select: {
50
+ readonly choose: "selection.select";
51
+ };
52
+ readonly calendar: {
53
+ readonly select: "selection.select";
54
+ };
55
+ readonly combobox: {
56
+ readonly choose: "selection.select";
57
+ readonly clear: "input.press";
58
+ readonly "chip-remove": "input.press";
59
+ };
60
+ readonly "dropdown-menu": {
61
+ readonly check: "selection.select";
62
+ };
63
+ readonly "context-menu": {
64
+ readonly check: "selection.select";
65
+ };
66
+ readonly toast: {
67
+ readonly success: "feedback.success";
68
+ readonly warning: "feedback.warning";
69
+ readonly error: "feedback.error";
70
+ };
71
+ };
72
+ export type SoundComponent = keyof typeof SOUND_MAP;
@@ -0,0 +1,76 @@
1
+ "use client";
2
+ import { useSound } from "./sound.js";
3
+ import { createContext, createElement, useCallback, useContext } from "react";
4
+ // src/sound/sound-map.ts
5
+ /**
6
+ * The one table of interface sounds: component × event → cue (feel layer, §4). Components play
7
+ * from here, so the map is reviewable in one place. Surfaces that aren't listed (Popover, Tooltip,
8
+ * Command, menu open and close, Select open, Tabs, Accordion, Collapsible, inputs) are silent: opening a light surface isn't a commit,
9
+ * and the palette is a 100+/day tool. Nothing plays on hover, load or typing.
10
+ */
11
+ const SOUND_MAP = {
12
+ button: { press: "input.press" },
13
+ checkbox: {
14
+ on: "input.toggle-on",
15
+ off: "input.toggle-off"
16
+ },
17
+ "radio-group": { change: "selection.select" },
18
+ switch: {
19
+ on: "input.toggle-on",
20
+ off: "input.toggle-off"
21
+ },
22
+ slider: { commit: "movement.snap" },
23
+ "input-otp": { invalid: "feedback.error" },
24
+ alert: {
25
+ destructive: "feedback.error",
26
+ success: "feedback.success",
27
+ warning: "feedback.warning"
28
+ },
29
+ dialog: {
30
+ open: "navigation.open",
31
+ close: "navigation.close"
32
+ },
33
+ "alert-dialog": {
34
+ open: "navigation.open",
35
+ close: "navigation.close"
36
+ },
37
+ drawer: {
38
+ open: "navigation.open",
39
+ close: "navigation.close",
40
+ snap: "movement.snap"
41
+ },
42
+ select: { choose: "selection.select" },
43
+ calendar: { select: "selection.select" },
44
+ combobox: {
45
+ choose: "selection.select",
46
+ clear: "input.press",
47
+ "chip-remove": "input.press"
48
+ },
49
+ "dropdown-menu": { check: "selection.select" },
50
+ "context-menu": { check: "selection.select" },
51
+ toast: {
52
+ success: "feedback.success",
53
+ warning: "feedback.warning",
54
+ error: "feedback.error"
55
+ }
56
+ };
57
+ const SilentContext = createContext(false);
58
+ /** Silences the sound map below it (CommandDialog reuses Dialog but stays silent, §4). */
59
+ function SoundSilence({ children }) {
60
+ return createElement(SilentContext.Provider, { value: true }, children);
61
+ }
62
+ /** Plays a component's mapped cue for an event, through `useSound()` and its safeguards. */
63
+ function useCue(component) {
64
+ const { play } = useSound();
65
+ const silent = useContext(SilentContext);
66
+ return useCallback((event, options) => {
67
+ if (silent) return;
68
+ const cue = SOUND_MAP[component][event];
69
+ if (cue) play(cue, options);
70
+ }, [
71
+ component,
72
+ play,
73
+ silent
74
+ ]);
75
+ }
76
+ export { SOUND_MAP, SoundSilence, useCue };
@@ -0,0 +1,38 @@
1
+
2
+ import { sound } from "../tokens/dist/motion.js";
3
+ import { ReactNode } from "react";
4
+ /** Cue names come from the tokens (`sound.cue` in packages/tokens/src/effects.json). */
5
+ export type SoundCue = keyof typeof sound.cue;
6
+ /**
7
+ * Plays a cue. App-supplied, so the library makes no network calls and the sound engine
8
+ * (UISFX or anything else) stays swappable. `asset` is the engine's cue name from the tokens.
9
+ */
10
+ export type SoundPlayer = (cue: SoundCue, options: {
11
+ asset: string;
12
+ volume: number;
13
+ }) => void | (() => void);
14
+ export interface PlayOptions {
15
+ /** Give way to any other cue played during the same event (a Button press under a Dialog close). */
16
+ yield?: boolean | undefined;
17
+ }
18
+ export interface SoundProviderProps {
19
+ children: ReactNode;
20
+ /** Plays cues. Without one every cue is silent (the current stub behaviour). */
21
+ player?: SoundPlayer | undefined;
22
+ /** Apps can turn the default off (decision #30). */
23
+ enabled?: boolean | undefined;
24
+ volume?: number | undefined;
25
+ }
26
+ /** Global sound settings: one remembered mute for the whole app. */
27
+ export declare function SoundProvider({ children, player, enabled, volume }: SoundProviderProps): import("react").JSX.Element;
28
+ /**
29
+ * Plays interface sounds with manners: only from a user action (call `play` in an event handler),
30
+ * never when muted, turned off, or when the user asks for reduced motion; repeats are throttled.
31
+ */
32
+ export declare function useSound(): {
33
+ play: (cue: SoundCue, options?: PlayOptions) => void;
34
+ loop: (cue: SoundCue) => (() => void);
35
+ muted: boolean;
36
+ setMuted: (muted: boolean) => void;
37
+ enabled: boolean;
38
+ };
@@ -0,0 +1,136 @@
1
+ "use client";
2
+ import { sound } from "../tokens/dist/motion.js";
3
+ import { jsx } from "react/jsx-runtime";
4
+ import { createContext, useCallback, useContext, useMemo, useRef, useState } from "react";
5
+ import { useReducedMotionConfig } from "motion/react";
6
+ // src/sound/sound.tsx
7
+ /** One moment per player (per SoundProvider), outside React state: one cue per moment (#71). */
8
+ const moments = /* @__PURE__ */ new WeakMap();
9
+ function momentOf(player) {
10
+ let moment = moments.get(player);
11
+ if (!moment) {
12
+ moment = {
13
+ lastAt: 0,
14
+ pending: null
15
+ };
16
+ moments.set(player, moment);
17
+ }
18
+ return moment;
19
+ }
20
+ const MUTE_KEY = "nsaa-sound-muted";
21
+ function readMuted() {
22
+ try {
23
+ return localStorage.getItem(MUTE_KEY) === "true";
24
+ } catch {
25
+ return false;
26
+ }
27
+ }
28
+ function writeMuted(muted) {
29
+ try {
30
+ localStorage.setItem(MUTE_KEY, String(muted));
31
+ } catch {}
32
+ }
33
+ const SoundContext = createContext(null);
34
+ /** The app's own switch on <html data-motion> (feel.css reads it too): "reduce" silences sound,
35
+ * "full" overrides the OS setting. */
36
+ function htmlMotion() {
37
+ return typeof document === "undefined" ? void 0 : document.documentElement.dataset.motion;
38
+ }
39
+ /** Global sound settings: one remembered mute for the whole app. */
40
+ function SoundProvider({ children, player, enabled = sound.enabled, volume = sound.volume }) {
41
+ const [muted, setMutedState] = useState(readMuted);
42
+ const setMuted = useCallback((next) => {
43
+ setMutedState(next);
44
+ writeMuted(next);
45
+ }, []);
46
+ const value = useMemo(() => ({
47
+ enabled,
48
+ volume,
49
+ muted,
50
+ setMuted,
51
+ player
52
+ }), [
53
+ enabled,
54
+ volume,
55
+ muted,
56
+ setMuted,
57
+ player
58
+ ]);
59
+ return /* @__PURE__ */ jsx(SoundContext.Provider, {
60
+ value,
61
+ children
62
+ });
63
+ }
64
+ /**
65
+ * Plays interface sounds with manners: only from a user action (call `play` in an event handler),
66
+ * never when muted, turned off, or when the user asks for reduced motion; repeats are throttled.
67
+ */
68
+ function useSound() {
69
+ const context = useContext(SoundContext);
70
+ const reducedMotion = useReducedMotionConfig() ?? false;
71
+ const enabled = context?.enabled ?? sound.enabled;
72
+ const muted = context?.muted ?? false;
73
+ const player = context?.player;
74
+ const volume = context?.volume ?? sound.volume;
75
+ const last = useRef(null);
76
+ return {
77
+ play: useCallback((cue, options) => {
78
+ if (!enabled || muted || !player) return;
79
+ const html = htmlMotion();
80
+ if (html === "reduce" || reducedMotion && html !== "full") return;
81
+ const moment = momentOf(player);
82
+ const fire = () => {
83
+ const now = Date.now();
84
+ if (last.current?.cue === cue && now - last.current.at < 80) return;
85
+ last.current = {
86
+ cue,
87
+ at: now
88
+ };
89
+ moment.lastAt = now;
90
+ player(cue, {
91
+ asset: sound.cue[cue],
92
+ volume
93
+ });
94
+ };
95
+ if (!options?.yield) {
96
+ if (moment.pending) clearTimeout(moment.pending);
97
+ moment.pending = null;
98
+ fire();
99
+ return;
100
+ }
101
+ const scheduledAt = Date.now();
102
+ if (moment.pending) clearTimeout(moment.pending);
103
+ moment.pending = setTimeout(() => {
104
+ moment.pending = null;
105
+ if (scheduledAt - moment.lastAt < 100) return;
106
+ fire();
107
+ }, 0);
108
+ }, [
109
+ enabled,
110
+ muted,
111
+ reducedMotion,
112
+ player,
113
+ volume
114
+ ]),
115
+ loop: useCallback((cue) => {
116
+ if (!enabled || muted || !player) return () => {};
117
+ const html = htmlMotion();
118
+ if (html === "reduce" || reducedMotion && html !== "full") return () => {};
119
+ const stop = player(cue, {
120
+ asset: sound.cue[cue],
121
+ volume
122
+ });
123
+ return typeof stop === "function" ? stop : () => {};
124
+ }, [
125
+ enabled,
126
+ muted,
127
+ reducedMotion,
128
+ player,
129
+ volume
130
+ ]),
131
+ muted,
132
+ setMuted: context?.setMuted ?? writeMuted,
133
+ enabled
134
+ };
135
+ }
136
+ export { SoundProvider, useSound };
@@ -0,0 +1,41 @@
1
+
2
+ import { SoundPlayer } from "./sound.js";
3
+ import { PackName } from "uisfx";
4
+ declare namespace uisfx_d_exports {
5
+ export { SoundPack, UisfxPlayerOptions, UisfxSoundPlayer, createUisfxPlayer, soundPacks };
6
+ }
7
+ export type SoundPack = PackName;
8
+ /** Every UISFX pack, for pickers. */
9
+ export declare const soundPacks: readonly SoundPack[];
10
+ export interface UisfxPlayerOptions {
11
+ /** UISFX pack (sound family). Defaults to the `sound.feel` token. */
12
+ pack?: SoundPack | undefined;
13
+ /** Where to listen for the first user gesture. Defaults to `window`. */
14
+ target?: EventTarget | undefined;
15
+ }
16
+ /** A `SoundPlayer` for `SoundProvider`, plus controls for the engine. */
17
+ export interface UisfxSoundPlayer extends SoundPlayer {
18
+ setPack: (pack: SoundPack) => void;
19
+ getPack: () => SoundPack;
20
+ /** True once a trusted pointer or key action has happened (or `unlock` was called from one). */
21
+ isUnlocked: () => boolean;
22
+ /** Unlock audio from your own user-gesture handler. Never call it on load. */
23
+ unlock: () => void;
24
+ /** Stops sounds, removes listeners and closes the audio context. */
25
+ destroy: () => Promise<void>;
26
+ }
27
+ /**
28
+ * Real sound for `SoundProvider`, synthesised live by UISFX (no network, no audio files).
29
+ * Manners: nothing plays, and no audio context is created, until the first trusted pointer or
30
+ * key action; mute, volume and reduced motion are handled by `SoundProvider`/`useSound`.
31
+ *
32
+ * Create one per app, at module scope (not inside a component, where StrictMode or re-renders
33
+ * would make extra engines):
34
+ *
35
+ * ```tsx
36
+ * const player = createUisfxPlayer({ pack: "soft" });
37
+ * <SoundProvider player={player}>…</SoundProvider>
38
+ * ```
39
+ */
40
+ export declare function createUisfxPlayer({ pack, target }?: UisfxPlayerOptions): UisfxSoundPlayer;
41
+ export { uisfx_d_exports };
@@ -0,0 +1,59 @@
1
+ "use client";
2
+ import { sound } from "../tokens/dist/motion.js";
3
+ import { createUISFX, cueNames, getPlaybackMode, packNames } from "uisfx";
4
+ // src/sound/uisfx.ts
5
+ /** Every UISFX pack, for pickers. */
6
+ const soundPacks = packNames;
7
+ const GESTURES = ["pointerdown", "keydown"];
8
+ const CUES = new Set(cueNames);
9
+ function isPack(value) {
10
+ return packNames.includes(value);
11
+ }
12
+ /**
13
+ * Real sound for `SoundProvider`, synthesised live by UISFX (no network, no audio files).
14
+ * Manners: nothing plays, and no audio context is created, until the first trusted pointer or
15
+ * key action; mute, volume and reduced motion are handled by `SoundProvider`/`useSound`.
16
+ *
17
+ * Create one per app, at module scope (not inside a component, where StrictMode or re-renders
18
+ * would make extra engines):
19
+ *
20
+ * ```tsx
21
+ * const player = createUisfxPlayer({ pack: "soft" });
22
+ * <SoundProvider player={player}>…</SoundProvider>
23
+ * ```
24
+ */
25
+ function createUisfxPlayer({ pack, target } = {}) {
26
+ const initialPack = pack ?? (isPack(sound.feel) ? sound.feel : "minimal");
27
+ const engine = createUISFX({
28
+ pack: initialPack,
29
+ volume: sound.volume
30
+ });
31
+ const listenOn = target ?? (typeof window === "undefined" ? void 0 : window);
32
+ let unlocked = false;
33
+ const unlock = () => {
34
+ if (unlocked) return;
35
+ unlocked = true;
36
+ for (const type of GESTURES) listenOn?.removeEventListener(type, onGesture, true);
37
+ engine.unlock();
38
+ };
39
+ function onGesture(event) {
40
+ if (event.isTrusted) unlock();
41
+ }
42
+ for (const type of GESTURES) listenOn?.addEventListener(type, onGesture, true);
43
+ const play = ((_cue, { asset, volume }) => {
44
+ if (!unlocked || !CUES.has(asset)) return;
45
+ if (engine.getVolume() !== volume) engine.setVolume(volume);
46
+ const playing = engine.play(asset);
47
+ if (getPlaybackMode(asset) === "loop") return () => playing?.stop();
48
+ });
49
+ play.setPack = (next) => engine.setPack(next);
50
+ play.getPack = () => engine.getPack();
51
+ play.isUnlocked = () => unlocked;
52
+ play.unlock = unlock;
53
+ play.destroy = async () => {
54
+ for (const type of GESTURES) listenOn?.removeEventListener(type, onGesture, true);
55
+ await engine.destroy();
56
+ };
57
+ return play;
58
+ }
59
+ export { createUisfxPlayer, soundPacks };
@@ -1,38 +1,3 @@
1
1
 
2
- import { n as SoundPlayer } from "./sound-DiYNAnR_.js";
3
- import { PackName } from "uisfx";
4
- type SoundPack = PackName;
5
- /** Every UISFX pack, for pickers. */
6
- export declare const soundPacks: readonly SoundPack[];
7
- interface UisfxPlayerOptions {
8
- /** UISFX pack (sound family). Defaults to the `sound.feel` token. */
9
- pack?: SoundPack | undefined;
10
- /** Where to listen for the first user gesture. Defaults to `window`. */
11
- target?: EventTarget | undefined;
12
- }
13
- /** A `SoundPlayer` for `SoundProvider`, plus controls for the engine. */
14
- interface UisfxSoundPlayer extends SoundPlayer {
15
- setPack: (pack: SoundPack) => void;
16
- getPack: () => SoundPack;
17
- /** True once a trusted pointer or key action has happened (or `unlock` was called from one). */
18
- isUnlocked: () => boolean;
19
- /** Unlock audio from your own user-gesture handler. Never call it on load. */
20
- unlock: () => void;
21
- /** Stops sounds, removes listeners and closes the audio context. */
22
- destroy: () => Promise<void>;
23
- }
24
- /**
25
- * Real sound for `SoundProvider`, synthesised live by UISFX (no network, no audio files).
26
- * Manners: nothing plays, and no audio context is created, until the first trusted pointer or
27
- * key action; mute, volume and reduced motion are handled by `SoundProvider`/`useSound`.
28
- *
29
- * Create one per app, at module scope (not inside a component, where StrictMode or re-renders
30
- * would make extra engines):
31
- *
32
- * ```tsx
33
- * const player = createUisfxPlayer({ pack: "soft" });
34
- * <SoundProvider player={player}>…</SoundProvider>
35
- * ```
36
- */
37
- export declare function createUisfxPlayer({ pack, target }?: UisfxPlayerOptions): UisfxSoundPlayer;
38
- export type { SoundPack, UisfxPlayerOptions, UisfxSoundPlayer };
2
+ import { SoundPack, UisfxPlayerOptions, UisfxSoundPlayer, createUisfxPlayer, soundPacks } from "./sound/uisfx.js";
3
+ export { type SoundPack, type UisfxPlayerOptions, type UisfxSoundPlayer, createUisfxPlayer, soundPacks };
@@ -1,59 +1,2 @@
1
1
  "use client";
2
- import { i as sound } from "./motion-NQq2BA78.js";
3
- import { createUISFX, cueNames, getPlaybackMode, packNames } from "uisfx";
4
- // src/sound/uisfx.ts
5
- /** Every UISFX pack, for pickers. */
6
- const soundPacks = packNames;
7
- const GESTURES = ["pointerdown", "keydown"];
8
- const CUES = new Set(cueNames);
9
- function isPack(value) {
10
- return packNames.includes(value);
11
- }
12
- /**
13
- * Real sound for `SoundProvider`, synthesised live by UISFX (no network, no audio files).
14
- * Manners: nothing plays, and no audio context is created, until the first trusted pointer or
15
- * key action; mute, volume and reduced motion are handled by `SoundProvider`/`useSound`.
16
- *
17
- * Create one per app, at module scope (not inside a component, where StrictMode or re-renders
18
- * would make extra engines):
19
- *
20
- * ```tsx
21
- * const player = createUisfxPlayer({ pack: "soft" });
22
- * <SoundProvider player={player}>…</SoundProvider>
23
- * ```
24
- */
25
- function createUisfxPlayer({ pack, target } = {}) {
26
- const initialPack = pack ?? (isPack(sound.feel) ? sound.feel : "minimal");
27
- const engine = createUISFX({
28
- pack: initialPack,
29
- volume: sound.volume
30
- });
31
- const listenOn = target ?? (typeof window === "undefined" ? void 0 : window);
32
- let unlocked = false;
33
- const unlock = () => {
34
- if (unlocked) return;
35
- unlocked = true;
36
- for (const type of GESTURES) listenOn?.removeEventListener(type, onGesture, true);
37
- engine.unlock();
38
- };
39
- function onGesture(event) {
40
- if (event.isTrusted) unlock();
41
- }
42
- for (const type of GESTURES) listenOn?.addEventListener(type, onGesture, true);
43
- const play = ((_cue, { asset, volume }) => {
44
- if (!unlocked || !CUES.has(asset)) return;
45
- if (engine.getVolume() !== volume) engine.setVolume(volume);
46
- const playing = engine.play(asset);
47
- if (getPlaybackMode(asset) === "loop") return () => playing?.stop();
48
- });
49
- play.setPack = (next) => engine.setPack(next);
50
- play.getPack = () => engine.getPack();
51
- play.isUnlocked = () => unlocked;
52
- play.unlock = unlock;
53
- play.destroy = async () => {
54
- for (const type of GESTURES) listenOn?.removeEventListener(type, onGesture, true);
55
- await engine.destroy();
56
- };
57
- return play;
58
- }
59
- export { createUisfxPlayer, soundPacks };
2
+ export { createUisfxPlayer, soundPacks } from "./sound/uisfx.js";
@@ -0,0 +1,3 @@
1
+ /* @nsaa/core: Tailwind sources for accordion (generated). */
2
+ @source "../accordion/accordion.js";
3
+ @source "../lib/cn.js";
@@ -0,0 +1,26 @@
1
+ /* @nsaa/core: Tailwind sources for alert-dialog (generated). */
2
+ @source "../alert-dialog/alert-dialog.js";
3
+ @source "../button/button.js";
4
+ @source "../lib/cn.js";
5
+ @source "../lib/control-defaults.js";
6
+ @source "../lib/opener.js";
7
+ @source "../lib/use-press.js";
8
+ @source "../loaders/arc.js";
9
+ @source "../loaders/bouncing-dots.js";
10
+ @source "../loaders/cap.js";
11
+ @source "../loaders/circular-dots.js";
12
+ @source "../loaders/classic.js";
13
+ @source "../loaders/easing.js";
14
+ @source "../loaders/fade.js";
15
+ @source "../loaders/frame.js";
16
+ @source "../loaders/motion.js";
17
+ @source "../loaders/orbit.js";
18
+ @source "../loaders/pulse.js";
19
+ @source "../loaders/ring.js";
20
+ @source "../loaders/wave.js";
21
+ @source "../sound/sound-map.js";
22
+ @source "../sound/sound.js";
23
+ @source "../spinner/loader-provider.js";
24
+ @source "../spinner/registry.js";
25
+ @source "../spinner/spinner.js";
26
+ @source "../tokens/dist/motion.js";
@@ -0,0 +1,8 @@
1
+ /* @nsaa/core: Tailwind sources for alert (generated). */
2
+ @source "../alert/alert.js";
3
+ @source "../lib/cn.js";
4
+ @source "../lib/input-modality.js";
5
+ @source "../lib/user-action.js";
6
+ @source "../sound/sound-map.js";
7
+ @source "../sound/sound.js";
8
+ @source "../tokens/dist/motion.js";
@@ -0,0 +1,3 @@
1
+ /* @nsaa/core: Tailwind sources for avatar (generated). */
2
+ @source "../avatar/avatar.js";
3
+ @source "../lib/cn.js";
@@ -0,0 +1,3 @@
1
+ /* @nsaa/core: Tailwind sources for badge (generated). */
2
+ @source "../badge/badge.js";
3
+ @source "../lib/cn.js";