tempest-react-sdk 0.25.0 → 0.26.1

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 (322) hide show
  1. package/README.md +14 -1
  2. package/bin/lib/alias/collect.mjs +94 -0
  3. package/bin/lib/alias/collect.test.mjs +95 -0
  4. package/bin/lib/alias/discover.mjs +77 -0
  5. package/bin/lib/alias/discover.test.mjs +189 -0
  6. package/bin/lib/alias/index.mjs +88 -0
  7. package/bin/lib/alias/index.test.mjs +138 -0
  8. package/bin/lib/alias/rewrite-css.mjs +76 -0
  9. package/bin/lib/alias/rewrite-css.test.mjs +85 -0
  10. package/bin/lib/alias/rewrite.mjs +141 -0
  11. package/bin/lib/alias/rewrite.test.mjs +148 -0
  12. package/bin/lib/alias/specifier.mjs +49 -0
  13. package/bin/lib/alias/specifier.test.mjs +85 -0
  14. package/bin/lib/alias/tsconfig.mjs +116 -0
  15. package/bin/lib/alias/typescript.mjs +23 -0
  16. package/bin/lib/doctor/lucide.mjs +74 -0
  17. package/bin/lib/doctor/lucide.test.mjs +107 -0
  18. package/bin/lib/icons/generate.mjs +156 -0
  19. package/bin/lib/icons/generate.test.mjs +125 -0
  20. package/bin/tempest.mjs +174 -13
  21. package/dist/audio/use-audio.cjs +1 -1
  22. package/dist/audio/use-audio.cjs.map +1 -1
  23. package/dist/audio/use-audio.js +12 -12
  24. package/dist/audio/use-audio.js.map +1 -1
  25. package/dist/charts/palette.cjs +1 -1
  26. package/dist/charts/palette.cjs.map +1 -1
  27. package/dist/charts/palette.js +2 -2
  28. package/dist/charts/palette.js.map +1 -1
  29. package/dist/charts.d.ts +5 -3
  30. package/dist/components/ClickOutside/ClickOutside.cjs +1 -1
  31. package/dist/components/ClickOutside/ClickOutside.cjs.map +1 -1
  32. package/dist/components/ClickOutside/ClickOutside.js +3 -1
  33. package/dist/components/ClickOutside/ClickOutside.js.map +1 -1
  34. package/dist/components/DataTable/DataTable.cjs +1 -1
  35. package/dist/components/DataTable/DataTable.cjs.map +1 -1
  36. package/dist/components/DataTable/DataTable.js +21 -23
  37. package/dist/components/DataTable/DataTable.js.map +1 -1
  38. package/dist/components/Kanban/Kanban.cjs +2 -0
  39. package/dist/components/Kanban/Kanban.cjs.map +1 -0
  40. package/dist/components/Kanban/Kanban.js +102 -0
  41. package/dist/components/Kanban/Kanban.js.map +1 -0
  42. package/dist/components/Kanban/Kanban.module.cjs +2 -0
  43. package/dist/components/Kanban/Kanban.module.cjs.map +1 -0
  44. package/dist/components/Kanban/Kanban.module.js +19 -0
  45. package/dist/components/Kanban/Kanban.module.js.map +1 -0
  46. package/dist/components/NotificationCenter/NotificationCenter.cjs +2 -0
  47. package/dist/components/NotificationCenter/NotificationCenter.cjs.map +1 -0
  48. package/dist/components/NotificationCenter/NotificationCenter.js +83 -0
  49. package/dist/components/NotificationCenter/NotificationCenter.js.map +1 -0
  50. package/dist/components/NotificationCenter/NotificationCenter.module.cjs +2 -0
  51. package/dist/components/NotificationCenter/NotificationCenter.module.cjs.map +1 -0
  52. package/dist/components/NotificationCenter/NotificationCenter.module.js +21 -0
  53. package/dist/components/NotificationCenter/NotificationCenter.module.js.map +1 -0
  54. package/dist/components/NotificationCenter/use-notification-inbox.cjs +2 -0
  55. package/dist/components/NotificationCenter/use-notification-inbox.cjs.map +1 -0
  56. package/dist/components/NotificationCenter/use-notification-inbox.js +63 -0
  57. package/dist/components/NotificationCenter/use-notification-inbox.js.map +1 -0
  58. package/dist/components/RefreshIndicator/RefreshIndicator.cjs +1 -1
  59. package/dist/components/RefreshIndicator/RefreshIndicator.cjs.map +1 -1
  60. package/dist/components/RefreshIndicator/RefreshIndicator.js +17 -17
  61. package/dist/components/RefreshIndicator/RefreshIndicator.js.map +1 -1
  62. package/dist/components/Tooltip/Tooltip.cjs +1 -1
  63. package/dist/components/Tooltip/Tooltip.cjs.map +1 -1
  64. package/dist/components/Tooltip/Tooltip.js +26 -20
  65. package/dist/components/Tooltip/Tooltip.js.map +1 -1
  66. package/dist/components/VirtualTable/VirtualTable.cjs +2 -0
  67. package/dist/components/VirtualTable/VirtualTable.cjs.map +1 -0
  68. package/dist/components/VirtualTable/VirtualTable.js +116 -0
  69. package/dist/components/VirtualTable/VirtualTable.js.map +1 -0
  70. package/dist/components/VirtualTable/VirtualTable.module.cjs +2 -0
  71. package/dist/components/VirtualTable/VirtualTable.module.cjs.map +1 -0
  72. package/dist/components/VirtualTable/VirtualTable.module.js +20 -0
  73. package/dist/components/VirtualTable/VirtualTable.module.js.map +1 -0
  74. package/dist/components/Wizard/Wizard.cjs +1 -1
  75. package/dist/components/Wizard/Wizard.cjs.map +1 -1
  76. package/dist/components/Wizard/Wizard.js +53 -53
  77. package/dist/components/Wizard/Wizard.js.map +1 -1
  78. package/dist/geo/use-position-tracker.cjs +1 -1
  79. package/dist/geo/use-position-tracker.cjs.map +1 -1
  80. package/dist/geo/use-position-tracker.js +3 -1
  81. package/dist/geo/use-position-tracker.js.map +1 -1
  82. package/dist/hooks/use-async.cjs +1 -1
  83. package/dist/hooks/use-async.cjs.map +1 -1
  84. package/dist/hooks/use-async.js +3 -1
  85. package/dist/hooks/use-async.js.map +1 -1
  86. package/dist/hooks/use-click-outside.cjs +1 -1
  87. package/dist/hooks/use-click-outside.cjs.map +1 -1
  88. package/dist/hooks/use-click-outside.js +3 -1
  89. package/dist/hooks/use-click-outside.js.map +1 -1
  90. package/dist/hooks/use-deep-memo.cjs.map +1 -1
  91. package/dist/hooks/use-deep-memo.js.map +1 -1
  92. package/dist/hooks/use-event-listener.cjs +1 -1
  93. package/dist/hooks/use-event-listener.cjs.map +1 -1
  94. package/dist/hooks/use-event-listener.js +3 -1
  95. package/dist/hooks/use-event-listener.js.map +1 -1
  96. package/dist/hooks/use-interval.cjs +1 -1
  97. package/dist/hooks/use-interval.cjs.map +1 -1
  98. package/dist/hooks/use-interval.js +3 -1
  99. package/dist/hooks/use-interval.js.map +1 -1
  100. package/dist/hooks/use-is-first-render.cjs.map +1 -1
  101. package/dist/hooks/use-is-first-render.js.map +1 -1
  102. package/dist/hooks/use-long-press-handlers.cjs +1 -1
  103. package/dist/hooks/use-long-press-handlers.cjs.map +1 -1
  104. package/dist/hooks/use-long-press-handlers.js +25 -23
  105. package/dist/hooks/use-long-press-handlers.js.map +1 -1
  106. package/dist/hooks/use-long-press.cjs +1 -1
  107. package/dist/hooks/use-long-press.cjs.map +1 -1
  108. package/dist/hooks/use-long-press.js +3 -1
  109. package/dist/hooks/use-long-press.js.map +1 -1
  110. package/dist/hooks/use-online.cjs +1 -1
  111. package/dist/hooks/use-online.cjs.map +1 -1
  112. package/dist/hooks/use-online.js +6 -4
  113. package/dist/hooks/use-online.js.map +1 -1
  114. package/dist/hooks/use-previous.cjs.map +1 -1
  115. package/dist/hooks/use-previous.js.map +1 -1
  116. package/dist/hooks/use-service-worker-update.cjs +1 -1
  117. package/dist/hooks/use-service-worker-update.cjs.map +1 -1
  118. package/dist/hooks/use-service-worker-update.js +3 -1
  119. package/dist/hooks/use-service-worker-update.js.map +1 -1
  120. package/dist/hooks/use-sortable.cjs +2 -0
  121. package/dist/hooks/use-sortable.cjs.map +1 -0
  122. package/dist/hooks/use-sortable.js +169 -0
  123. package/dist/hooks/use-sortable.js.map +1 -0
  124. package/dist/hooks/use-stable-callback.cjs.map +1 -1
  125. package/dist/hooks/use-stable-callback.js.map +1 -1
  126. package/dist/hooks/use-timeout.cjs +1 -1
  127. package/dist/hooks/use-timeout.cjs.map +1 -1
  128. package/dist/hooks/use-timeout.js +3 -1
  129. package/dist/hooks/use-timeout.js.map +1 -1
  130. package/dist/icons/Icon.cjs +2 -0
  131. package/dist/icons/Icon.cjs.map +1 -0
  132. package/dist/icons/Icon.js +24 -0
  133. package/dist/icons/Icon.js.map +1 -0
  134. package/dist/icons/IconProvider.cjs +2 -0
  135. package/dist/icons/IconProvider.cjs.map +1 -0
  136. package/dist/icons/IconProvider.js +24 -0
  137. package/dist/icons/IconProvider.js.map +1 -0
  138. package/dist/icons/generated/aliases.cjs +2 -0
  139. package/dist/icons/generated/aliases.cjs.map +1 -0
  140. package/dist/icons/generated/aliases.js +255 -0
  141. package/dist/icons/generated/aliases.js.map +1 -0
  142. package/dist/icons/generated/icon-names.cjs +2 -0
  143. package/dist/icons/generated/icon-names.cjs.map +1 -0
  144. package/dist/icons/generated/icon-names.js +6 -0
  145. package/dist/icons/generated/icon-names.js.map +1 -0
  146. package/dist/icons/generated/loaders.cjs +2 -0
  147. package/dist/icons/generated/loaders.cjs.map +1 -0
  148. package/dist/icons/generated/loaders.js +32 -0
  149. package/dist/icons/generated/loaders.js.map +1 -0
  150. package/dist/icons/generated/shard-a.cjs +2 -0
  151. package/dist/icons/generated/shard-a.cjs.map +1 -0
  152. package/dist/icons/generated/shard-a.js +114 -0
  153. package/dist/icons/generated/shard-a.js.map +1 -0
  154. package/dist/icons/generated/shard-b.cjs +2 -0
  155. package/dist/icons/generated/shard-b.cjs.map +1 -0
  156. package/dist/icons/generated/shard-b.js +156 -0
  157. package/dist/icons/generated/shard-b.js.map +1 -0
  158. package/dist/icons/generated/shard-c.cjs +2 -0
  159. package/dist/icons/generated/shard-c.cjs.map +1 -0
  160. package/dist/icons/generated/shard-c.js +269 -0
  161. package/dist/icons/generated/shard-c.js.map +1 -0
  162. package/dist/icons/generated/shard-d.cjs +2 -0
  163. package/dist/icons/generated/shard-d.cjs.map +1 -0
  164. package/dist/icons/generated/shard-d.js +62 -0
  165. package/dist/icons/generated/shard-d.js.map +1 -0
  166. package/dist/icons/generated/shard-e.cjs +2 -0
  167. package/dist/icons/generated/shard-e.cjs.map +1 -0
  168. package/dist/icons/generated/shard-e.js +32 -0
  169. package/dist/icons/generated/shard-e.js.map +1 -0
  170. package/dist/icons/generated/shard-f.cjs +2 -0
  171. package/dist/icons/generated/shard-f.cjs.map +1 -0
  172. package/dist/icons/generated/shard-f.js +139 -0
  173. package/dist/icons/generated/shard-f.js.map +1 -0
  174. package/dist/icons/generated/shard-g.cjs +2 -0
  175. package/dist/icons/generated/shard-g.cjs.map +1 -0
  176. package/dist/icons/generated/shard-g.js +61 -0
  177. package/dist/icons/generated/shard-g.js.map +1 -0
  178. package/dist/icons/generated/shard-h.cjs +2 -0
  179. package/dist/icons/generated/shard-h.cjs.map +1 -0
  180. package/dist/icons/generated/shard-h.js +63 -0
  181. package/dist/icons/generated/shard-h.js.map +1 -0
  182. package/dist/icons/generated/shard-i.cjs +2 -0
  183. package/dist/icons/generated/shard-i.cjs.map +1 -0
  184. package/dist/icons/generated/shard-i.js +30 -0
  185. package/dist/icons/generated/shard-i.js.map +1 -0
  186. package/dist/icons/generated/shard-j.cjs +2 -0
  187. package/dist/icons/generated/shard-j.cjs.map +1 -0
  188. package/dist/icons/generated/shard-j.js +10 -0
  189. package/dist/icons/generated/shard-j.js.map +1 -0
  190. package/dist/icons/generated/shard-k.cjs +2 -0
  191. package/dist/icons/generated/shard-k.cjs.map +1 -0
  192. package/dist/icons/generated/shard-k.js +16 -0
  193. package/dist/icons/generated/shard-k.js.map +1 -0
  194. package/dist/icons/generated/shard-l.cjs +2 -0
  195. package/dist/icons/generated/shard-l.cjs.map +1 -0
  196. package/dist/icons/generated/shard-l.js +88 -0
  197. package/dist/icons/generated/shard-l.js.map +1 -0
  198. package/dist/icons/generated/shard-m.cjs +2 -0
  199. package/dist/icons/generated/shard-m.cjs.map +1 -0
  200. package/dist/icons/generated/shard-m.js +140 -0
  201. package/dist/icons/generated/shard-m.js.map +1 -0
  202. package/dist/icons/generated/shard-n.cjs +2 -0
  203. package/dist/icons/generated/shard-n.cjs.map +1 -0
  204. package/dist/icons/generated/shard-n.js +24 -0
  205. package/dist/icons/generated/shard-n.js.map +1 -0
  206. package/dist/icons/generated/shard-o.cjs +2 -0
  207. package/dist/icons/generated/shard-o.cjs.map +1 -0
  208. package/dist/icons/generated/shard-o.js +17 -0
  209. package/dist/icons/generated/shard-o.js.map +1 -0
  210. package/dist/icons/generated/shard-p.cjs +2 -0
  211. package/dist/icons/generated/shard-p.cjs.map +1 -0
  212. package/dist/icons/generated/shard-p.js +115 -0
  213. package/dist/icons/generated/shard-p.js.map +1 -0
  214. package/dist/icons/generated/shard-q.cjs +2 -0
  215. package/dist/icons/generated/shard-q.cjs.map +1 -0
  216. package/dist/icons/generated/shard-q.js +10 -0
  217. package/dist/icons/generated/shard-q.js.map +1 -0
  218. package/dist/icons/generated/shard-r.cjs +2 -0
  219. package/dist/icons/generated/shard-r.cjs.map +1 -0
  220. package/dist/icons/generated/shard-r.js +77 -0
  221. package/dist/icons/generated/shard-r.js.map +1 -0
  222. package/dist/icons/generated/shard-s.cjs +2 -0
  223. package/dist/icons/generated/shard-s.cjs.map +1 -0
  224. package/dist/icons/generated/shard-s.js +255 -0
  225. package/dist/icons/generated/shard-s.js.map +1 -0
  226. package/dist/icons/generated/shard-t.cjs +2 -0
  227. package/dist/icons/generated/shard-t.cjs.map +1 -0
  228. package/dist/icons/generated/shard-t.js +109 -0
  229. package/dist/icons/generated/shard-t.js.map +1 -0
  230. package/dist/icons/generated/shard-u.cjs +2 -0
  231. package/dist/icons/generated/shard-u.cjs.map +1 -0
  232. package/dist/icons/generated/shard-u.js +49 -0
  233. package/dist/icons/generated/shard-u.js.map +1 -0
  234. package/dist/icons/generated/shard-v.cjs +2 -0
  235. package/dist/icons/generated/shard-v.cjs.map +1 -0
  236. package/dist/icons/generated/shard-v.js +30 -0
  237. package/dist/icons/generated/shard-v.js.map +1 -0
  238. package/dist/icons/generated/shard-w.cjs +2 -0
  239. package/dist/icons/generated/shard-w.cjs.map +1 -0
  240. package/dist/icons/generated/shard-w.js +48 -0
  241. package/dist/icons/generated/shard-w.js.map +1 -0
  242. package/dist/icons/generated/shard-x.cjs +2 -0
  243. package/dist/icons/generated/shard-x.cjs.map +1 -0
  244. package/dist/icons/generated/shard-x.js +10 -0
  245. package/dist/icons/generated/shard-x.js.map +1 -0
  246. package/dist/icons/generated/shard-z.cjs +2 -0
  247. package/dist/icons/generated/shard-z.cjs.map +1 -0
  248. package/dist/icons/generated/shard-z.js +25 -0
  249. package/dist/icons/generated/shard-z.js.map +1 -0
  250. package/dist/icons/icon-context.cjs +2 -0
  251. package/dist/icons/icon-context.cjs.map +1 -0
  252. package/dist/icons/icon-context.js +13 -0
  253. package/dist/icons/icon-context.js.map +1 -0
  254. package/dist/icons/is-icon-name.cjs +2 -0
  255. package/dist/icons/is-icon-name.cjs.map +1 -0
  256. package/dist/icons/is-icon-name.js +10 -0
  257. package/dist/icons/is-icon-name.js.map +1 -0
  258. package/dist/icons/shard-cache.cjs +2 -0
  259. package/dist/icons/shard-cache.cjs.map +1 -0
  260. package/dist/icons/shard-cache.js +41 -0
  261. package/dist/icons/shard-cache.js.map +1 -0
  262. package/dist/icons/use-icon.cjs +2 -0
  263. package/dist/icons/use-icon.cjs.map +1 -0
  264. package/dist/icons/use-icon.js +20 -0
  265. package/dist/icons/use-icon.js.map +1 -0
  266. package/dist/icons-virtual.d.ts +22 -0
  267. package/dist/icons.cjs +1 -0
  268. package/dist/icons.d.ts +220 -0
  269. package/dist/icons.js +9 -0
  270. package/dist/offline/use-offline-sync.cjs +1 -1
  271. package/dist/offline/use-offline-sync.cjs.map +1 -1
  272. package/dist/offline/use-offline-sync.js +3 -1
  273. package/dist/offline/use-offline-sync.js.map +1 -1
  274. package/dist/push/use-push-subscription.cjs +1 -1
  275. package/dist/push/use-push-subscription.cjs.map +1 -1
  276. package/dist/push/use-push-subscription.js +3 -1
  277. package/dist/push/use-push-subscription.js.map +1 -1
  278. package/dist/sse/use-event-stream.cjs +1 -1
  279. package/dist/sse/use-event-stream.cjs.map +1 -1
  280. package/dist/sse/use-event-stream.js +3 -1
  281. package/dist/sse/use-event-stream.js.map +1 -1
  282. package/dist/styles.css +1 -1
  283. package/dist/tempest-react-sdk.cjs +1 -1
  284. package/dist/tempest-react-sdk.d.ts +585 -3
  285. package/dist/tempest-react-sdk.js +189 -183
  286. package/dist/theme/ThemeProvider.cjs +1 -1
  287. package/dist/theme/ThemeProvider.cjs.map +1 -1
  288. package/dist/theme/ThemeProvider.js +6 -2
  289. package/dist/theme/ThemeProvider.js.map +1 -1
  290. package/dist/theme/color.cjs +1 -1
  291. package/dist/theme/color.cjs.map +1 -1
  292. package/dist/theme/color.js +77 -46
  293. package/dist/theme/color.js.map +1 -1
  294. package/dist/theme/create-theme.cjs +1 -1
  295. package/dist/theme/create-theme.cjs.map +1 -1
  296. package/dist/theme/create-theme.js +9 -3
  297. package/dist/theme/create-theme.js.map +1 -1
  298. package/dist/utils/compare-values.cjs +2 -0
  299. package/dist/utils/compare-values.cjs.map +1 -0
  300. package/dist/utils/compare-values.js +8 -0
  301. package/dist/utils/compare-values.js.map +1 -0
  302. package/dist/vision/use-camera-stream.cjs +1 -1
  303. package/dist/vision/use-camera-stream.cjs.map +1 -1
  304. package/dist/vision/use-camera-stream.js +3 -1
  305. package/dist/vision/use-camera-stream.js.map +1 -1
  306. package/dist/vite/create-vite-config.cjs +1 -1
  307. package/dist/vite/create-vite-config.cjs.map +1 -1
  308. package/dist/vite/create-vite-config.js +30 -22
  309. package/dist/vite/create-vite-config.js.map +1 -1
  310. package/dist/vite/tempest-icons.cjs +9 -0
  311. package/dist/vite/tempest-icons.cjs.map +1 -0
  312. package/dist/vite/tempest-icons.js +99 -0
  313. package/dist/vite/tempest-icons.js.map +1 -0
  314. package/dist/vite.cjs +1 -1
  315. package/dist/vite.d.ts +79 -0
  316. package/dist/vite.js +6 -5
  317. package/dist/ws/use-web-socket.cjs +1 -1
  318. package/dist/ws/use-web-socket.cjs.map +1 -1
  319. package/dist/ws/use-web-socket.js +3 -1
  320. package/dist/ws/use-web-socket.js.map +1 -1
  321. package/package.json +12 -3
  322. package/template/package.json +1 -1
@@ -0,0 +1,125 @@
1
+ import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
2
+ import { tmpdir } from "node:os";
3
+ import { join } from "node:path";
4
+ import { afterEach, beforeEach, describe, expect, it } from "vitest";
5
+
6
+ import { iconAliases, iconNames } from "@/icons";
7
+
8
+ import { generateRegistry, renderRegistry, scanIconSlugs } from "./generate.mjs";
9
+
10
+ const known = new Set(iconNames);
11
+
12
+ describe("scanIconSlugs", () => {
13
+ it("finds literal name props, braced or not", () => {
14
+ expect(
15
+ scanIconSlugs(`<Icon name="save" /><Icon name={"trash-2"} />`, known).sort(),
16
+ ).toEqual(["save", "trash-2"]);
17
+ });
18
+ it("finds an object field", () => {
19
+ expect(scanIconSlugs(`{ name: "house" }`, known)).toEqual(["house"]);
20
+ });
21
+ it("ignores a name that is not a lucide slug", () => {
22
+ expect(scanIconSlugs(`<Field name="email" />`, known)).toEqual([]);
23
+ });
24
+ it("ignores a dynamic name", () => {
25
+ expect(scanIconSlugs(`<Icon name={row.icon} />`, known)).toEqual([]);
26
+ });
27
+ });
28
+
29
+ describe("renderRegistry", () => {
30
+ it("emits a createIconRegistry call with static imports", () => {
31
+ const out = renderRegistry(["save", "trash-2"], iconAliases);
32
+ expect(out).toContain(`import { createIconRegistry } from "tempest-react-sdk/icons";`);
33
+ expect(out).toContain(`import { Save, Trash2 } from "lucide-react";`);
34
+ expect(out).toContain(` "save": Save,`);
35
+ });
36
+ it("imports the canonical component for an alias but keeps the alias key", () => {
37
+ const out = renderRegistry(["alert-circle"], iconAliases);
38
+ expect(out).toContain(`import { CircleAlert } from "lucide-react";`);
39
+ expect(out).toContain(` "alert-circle": CircleAlert,`);
40
+ });
41
+ it("emits an empty registry rather than an empty import", () => {
42
+ expect(renderRegistry([], iconAliases)).toContain("createIconRegistry({})");
43
+ });
44
+ it("is deterministic regardless of input order", () => {
45
+ expect(renderRegistry(["trash-2", "save"], iconAliases)).toBe(
46
+ renderRegistry(["save", "trash-2"], iconAliases),
47
+ );
48
+ });
49
+ });
50
+
51
+ describe("generateRegistry", () => {
52
+ let root;
53
+
54
+ beforeEach(() => {
55
+ root = mkdtempSync(join(tmpdir(), "tempest-gen-icons-"));
56
+ });
57
+
58
+ afterEach(() => {
59
+ rmSync(root, { recursive: true, force: true });
60
+ });
61
+
62
+ /** Write a source file under the fixture root. */
63
+ function write(rel, contents) {
64
+ const path = join(root, rel);
65
+ mkdirSync(join(path, ".."), { recursive: true });
66
+ writeFileSync(path, contents);
67
+ }
68
+
69
+ it("collects slugs across a nested tree", async () => {
70
+ write("pages/Home.tsx", `<Icon name="house" />`);
71
+ write("pages/admin/Users.tsx", `<Icon name="users" />`);
72
+ const { slugs, files } = await generateRegistry({ dir: root, known, aliases: iconAliases });
73
+ expect(slugs).toEqual(["house", "users"]);
74
+ expect(files).toBe(2);
75
+ });
76
+
77
+ it("skips node_modules, dist and dot directories", async () => {
78
+ write("node_modules/dep/index.ts", `<Icon name="bug" />`);
79
+ write("dist/out.js", `<Icon name="bomb" />`);
80
+ write(".cache/x.ts", `<Icon name="box" />`);
81
+ write("App.tsx", `<Icon name="house" />`);
82
+ const { slugs } = await generateRegistry({ dir: root, known, aliases: iconAliases });
83
+ expect(slugs).toEqual(["house"]);
84
+ });
85
+
86
+ it("adds include slugs and drops invalid ones", async () => {
87
+ write("App.tsx", "");
88
+ const { slugs } = await generateRegistry({
89
+ dir: root,
90
+ known,
91
+ aliases: iconAliases,
92
+ include: ["rocket", "not-an-icon"],
93
+ });
94
+ expect(slugs).toEqual(["rocket"]);
95
+ });
96
+
97
+ it("yields an empty registry for a tree with no icons", async () => {
98
+ write("App.tsx", `export const App = () => null;`);
99
+ const { slugs, source } = await generateRegistry({
100
+ dir: root,
101
+ known,
102
+ aliases: iconAliases,
103
+ });
104
+ expect(slugs).toEqual([]);
105
+ expect(source).toContain("createIconRegistry({})");
106
+ });
107
+
108
+ it("returns zero files for a directory that does not exist", async () => {
109
+ const { files, slugs } = await generateRegistry({
110
+ dir: join(root, "nope"),
111
+ known,
112
+ aliases: iconAliases,
113
+ });
114
+ expect(files).toBe(0);
115
+ expect(slugs).toEqual([]);
116
+ });
117
+ });
118
+
119
+ describe("renderRegistry — reserved names", () => {
120
+ it("aliases an export name that would shadow a restricted global", () => {
121
+ const out = renderRegistry(["infinity"], iconAliases);
122
+ expect(out).toContain(`import { Infinity as InfinityIcon } from "lucide-react";`);
123
+ expect(out).toContain(`"infinity": InfinityIcon,`);
124
+ });
125
+ });
package/bin/tempest.mjs CHANGED
@@ -3,7 +3,7 @@
3
3
  //
4
4
  // tempest doctor health-check the current project (à la flutter doctor)
5
5
  // tempest lint [paths…] run ESLint (report only)
6
- // tempest fix [paths…] ESLint --fix (sort imports, drop unused, tidy whitespace) + Prettier
6
+ // tempest fix [paths…] alias imports (../ @/) + ESLint --fix + Prettier
7
7
  // tempest format [paths…] Prettier --write
8
8
  // tempest --help | --version
9
9
  import { spawnSync } from "node:child_process";
@@ -11,6 +11,11 @@ import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
11
11
  import { mkdir, writeFile } from "node:fs/promises";
12
12
  import { dirname, join, resolve } from "node:path";
13
13
  import { fileURLToPath } from "node:url";
14
+ import { aliasImports } from "./lib/alias/index.mjs";
15
+ import { loadTypeScript } from "./lib/alias/typescript.mjs";
16
+ import { readTsconfig } from "./lib/alias/tsconfig.mjs";
17
+ import { checkLucide } from "./lib/doctor/lucide.mjs";
18
+ import { generateRegistry, loadIconTables } from "./lib/icons/generate.mjs";
14
19
  import { generate } from "./lib/openapi/generate.mjs";
15
20
  import { loadSpec } from "./lib/openapi/load.mjs";
16
21
 
@@ -347,6 +352,21 @@ function doctor() {
347
352
  "nested copy under tempest-react-sdk — run `npm dedupe` or align versions; two instances break hooks/context",
348
353
  ],
349
354
  );
355
+
356
+ // lucide-react gets its own check: two copies of it do not break hooks the
357
+ // way a second React does, but they duplicate bytes and can leave the
358
+ // generated icon slug tables pointing at exports the older copy lacks.
359
+ const sdkPkg = readJSON(join(ROOT, "node_modules", "tempest-react-sdk", "package.json"));
360
+ checks.push(
361
+ ...checkLucide({
362
+ appSpec: deps["lucide-react"] ? String(deps["lucide-react"]) : null,
363
+ sdkSpec: sdkPkg?.dependencies?.["lucide-react"]
364
+ ? String(sdkPkg.dependencies["lucide-react"])
365
+ : null,
366
+ installedVersion: installedVersion("lucide-react"),
367
+ nestedCopy: nestedDupe("lucide-react"),
368
+ }),
369
+ );
350
370
  }
351
371
 
352
372
  // Declared-but-not-installed (package.json ↔ node_modules drift).
@@ -414,10 +434,10 @@ function doctor() {
414
434
  }
415
435
 
416
436
  // ── TypeScript ────────────────────────────────────────────────────────────
417
- const tsc = readJSON(join(ROOT, "tsconfig.json"));
437
+ const tsc = readTsconfig({ root: ROOT, ts: loadTypeScript(ROOT) });
418
438
  if (tsc) {
419
439
  checks.push(["section", "TypeScript"]);
420
- const co = tsc.compilerOptions ?? {};
440
+ const co = tsc.compilerOptions;
421
441
  checks.push(
422
442
  co.paths?.["@/*"]
423
443
  ? ["ok", 'tsconfig "@/*" alias']
@@ -625,12 +645,93 @@ function requireBin(name) {
625
645
  return bin;
626
646
  }
627
647
 
628
- function lint(paths) {
629
- return run(requireBin("eslint"), paths.length ? paths : ["."]);
648
+ /**
649
+ * Split argv into flags and positional paths.
650
+ *
651
+ * Needed because these commands used to hand `rest` straight through as their
652
+ * path list, so `tempest lint --max-warnings 0` counted the flag as a path and
653
+ * ESLint ran with a flag but no pattern. Flags are kept in order for forwarding.
654
+ *
655
+ * @param {string[]} args - The argv tail for the command.
656
+ * @returns {{ flags: string[], paths: string[] }}
657
+ */
658
+ function splitArgs(args) {
659
+ const flags = [];
660
+ const paths = [];
661
+ for (const arg of args) (arg.startsWith("-") ? flags : paths).push(arg);
662
+ return { flags, paths };
663
+ }
664
+
665
+ function lint(args) {
666
+ const { flags, paths } = splitArgs(args);
667
+ return run(requireBin("eslint"), [...(paths.length ? paths : ["."]), ...flags]);
630
668
  }
631
669
 
632
- function fix(paths) {
670
+ /** Flags `fix` owns itself; anything else is rejected rather than forwarded. */
671
+ const FIX_FLAGS = new Set(["--no-alias", "--dry-run"]);
672
+
673
+ /**
674
+ * Print the alias pass result.
675
+ *
676
+ * @param {object} result - Return value of `aliasImports`.
677
+ * @param {boolean} dryRun - Whether the pass wrote anything.
678
+ * @returns {void}
679
+ */
680
+ function reportAlias(result, dryRun) {
681
+ if (result.status === "no-typescript") {
682
+ console.log(
683
+ `${c.yellow}! typescript not installed — skipping alias pass${c.reset} ${c.dim}(npm i -D typescript)${c.reset}`,
684
+ );
685
+ return;
686
+ }
687
+ if (result.status === "no-alias") {
688
+ console.log(
689
+ `${c.yellow}! no path alias found — skipping alias pass${c.reset} ${c.dim}add "paths": { "@/*": ["./src/*"] } to tsconfig.json${c.reset}`,
690
+ );
691
+ return;
692
+ }
693
+ for (const file of result.files) {
694
+ console.log(` ${file.path} ${c.dim}${file.changes.length}${c.reset}`);
695
+ if (dryRun) {
696
+ for (const ch of file.changes) {
697
+ console.log(` ${c.dim}${ch.line}:${c.reset} "${ch.from}" → "${ch.to}"`);
698
+ }
699
+ }
700
+ }
701
+ for (const err of result.errors) {
702
+ console.log(` ${c.red}✗ ${err.path}${c.reset} ${c.dim}${err.message}${c.reset}`);
703
+ }
704
+ if (!result.total) {
705
+ console.log(` ${c.dim}nothing to convert${c.reset}`);
706
+ return;
707
+ }
708
+ const verb = dryRun ? "would convert" : "converted";
709
+ console.log(
710
+ ` ${c.green}✓${c.reset} ${verb} ${result.total} import(s) in ${result.files.length} file(s)`,
711
+ );
712
+ }
713
+
714
+ function fix(args) {
715
+ const { flags, paths } = splitArgs(args);
716
+ const unknown = flags.filter((f) => !FIX_FLAGS.has(f));
717
+ if (unknown.length) {
718
+ console.error(
719
+ `${c.red}✗ Unknown flag for fix: ${unknown.join(", ")}${c.reset} — supported: ${[...FIX_FLAGS].join(", ")}`,
720
+ );
721
+ return 1;
722
+ }
633
723
  const targets = paths.length ? paths : ["."];
724
+ const dryRun = flags.includes("--dry-run");
725
+
726
+ let aliasStatus = 0;
727
+ if (!flags.includes("--no-alias")) {
728
+ console.log(`${c.dim}→ alias imports (../ → @/)${dryRun ? " [dry-run]" : ""}${c.reset}`);
729
+ const result = aliasImports({ root: ROOT, targets, dryRun });
730
+ reportAlias(result, dryRun);
731
+ aliasStatus = result.status === "error" ? 1 : 0;
732
+ }
733
+ if (dryRun) return aliasStatus;
734
+
634
735
  console.log(`${c.dim}→ eslint --fix (sort imports · drop unused · tidy whitespace)${c.reset}`);
635
736
  const eslintStatus = run(requireBin("eslint"), [...targets, "--fix"]);
636
737
  const prettier = localBin("prettier");
@@ -641,11 +742,12 @@ function fix(paths) {
641
742
  } else {
642
743
  console.log(`${c.yellow}! prettier not installed — skipping format pass${c.reset}`);
643
744
  }
644
- return eslintStatus || prettierStatus;
745
+ return aliasStatus || eslintStatus || prettierStatus;
645
746
  }
646
747
 
647
- function format(paths) {
648
- return run(requireBin("prettier"), ["--write", ...(paths.length ? paths : ["."])]);
748
+ function format(args) {
749
+ const { flags, paths } = splitArgs(args);
750
+ return run(requireBin("prettier"), ["--write", ...(paths.length ? paths : ["."]), ...flags]);
649
751
  }
650
752
 
651
753
  // -------------------------------------------------------------- gen api ----
@@ -701,15 +803,67 @@ async function genApi(args) {
701
803
  return 0;
702
804
  }
703
805
 
806
+ // ------------------------------------------------------------ gen icons ----
807
+
808
+ /**
809
+ * `tempest gen icons [--out src/icons.generated.ts] [--dir src]` — write a static
810
+ * icon registry from the slugs the project's source mentions.
811
+ *
812
+ * The `tempestIcons()` Vite plugin does the same thing as a virtual module and
813
+ * needs no file; this exists for a project that is not on Vite, or that prefers a
814
+ * registry it can read and commit.
815
+ */
816
+ async function genIcons(args) {
817
+ const outRel = parseOut(args, "src/icons.generated.ts");
818
+ const dirIndex = args.indexOf("--dir");
819
+ const dirRel = dirIndex >= 0 && args[dirIndex + 1] ? args[dirIndex + 1] : "src";
820
+ const dir = resolve(ROOT, dirRel);
821
+
822
+ let tables;
823
+ try {
824
+ tables = await loadIconTables(SELF_DIR);
825
+ } catch {
826
+ console.error(
827
+ `${c.red}✗ Could not read the icon tables from the installed SDK.${c.reset} Is tempest-react-sdk installed and built?`,
828
+ );
829
+ return 1;
830
+ }
831
+
832
+ console.log(`${c.dim}→ scanning ${dirRel}${c.reset}`);
833
+ const { source, slugs, files } = await generateRegistry({
834
+ dir,
835
+ known: tables.known,
836
+ aliases: tables.aliases,
837
+ });
838
+
839
+ const dest = resolve(ROOT, outRel);
840
+ await mkdir(dirname(dest), { recursive: true });
841
+ await writeFile(dest, source);
842
+
843
+ console.log(
844
+ `\n${c.green}✓ ${slugs.length} icon(s)${c.reset} from ${files} file(s) → ${c.bold}${outRel}${c.reset}`,
845
+ );
846
+ const prettier = localBin("prettier");
847
+ if (prettier) run(prettier, ["--write", outRel]);
848
+ console.log(`\n${c.dim}Wire it up:${c.reset}`);
849
+ console.log(` import { IconProvider } from "tempest-react-sdk/icons";`);
850
+ console.log(` import { icons } from "@/icons.generated";`);
851
+ console.log(` <IconProvider registry={icons}>…</IconProvider>`);
852
+ return 0;
853
+ }
854
+
855
+ const GEN_TARGETS = { api: genApi, icons: genIcons };
856
+
704
857
  function gen(args) {
705
858
  const what = args[0];
706
- if (what !== "api") {
859
+ const target = GEN_TARGETS[what];
860
+ if (!target) {
707
861
  console.error(
708
- `${c.red}✗ Unknown gen target: ${what ?? "(none)"}${c.reset} — only \`gen api\` is supported.`,
862
+ `${c.red}✗ Unknown gen target: ${what ?? "(none)"}${c.reset} — supported: ${Object.keys(GEN_TARGETS).join(", ")}.`,
709
863
  );
710
864
  return 1;
711
865
  }
712
- return genApi(args.slice(1));
866
+ return target(args.slice(1));
713
867
  }
714
868
 
715
869
  // ------------------------------------------------------------------ main ----
@@ -724,10 +878,17 @@ ${c.bold}Usage${c.reset}
724
878
  ${c.bold}Commands${c.reset}
725
879
  ${c.bold}doctor${c.reset} Health-check the current project
726
880
  ${c.bold}lint${c.reset} [paths] Run ESLint (report only)
727
- ${c.bold}fix${c.reset} [paths] ESLint --fix (sort imports, remove unused, tidy whitespace) + Prettier
881
+ ${c.bold}fix${c.reset} [paths] Alias imports (../ → @/) + ESLint --fix (sort imports, remove
882
+ unused, tidy whitespace) + Prettier
728
883
  ${c.bold}format${c.reset} [paths] Prettier --write
729
884
  ${c.bold}gen api${c.reset} <src> Generate Zod schemas + types + service classes from an OpenAPI spec
730
885
  (e.g. tempest gen api http://127.0.0.1:8000/openapi.json --out src/api)
886
+ ${c.bold}gen icons${c.reset} Write a static icon registry from the slugs your source uses
887
+ (e.g. tempest gen icons --out src/icons.generated.ts --dir src)
888
+
889
+ ${c.bold}fix options${c.reset}
890
+ --dry-run List the imports that would be rewritten; write nothing
891
+ --no-alias Skip the alias pass (ESLint + Prettier only)
731
892
 
732
893
  ${c.bold}Options${c.reset}
733
894
  -h, --help Show this help
@@ -1,2 +1,2 @@
1
- const e=require("./audio-player.cjs");let t=require("react");function n(){let n=(0,t.useRef)(null),[r,i]=(0,t.useState)(!1);return n.current||=e.createAudioPlayer(),(0,t.useEffect)(()=>()=>{n.current?.stop()},[]),{play:(0,t.useCallback)(async(e,t)=>{await n.current.play(e,t)&&i(!0)},[]),stop:(0,t.useCallback)(()=>{n.current?.stop()},[]),unlocked:r}}exports.useAudio=n;
1
+ const e=require("./audio-player.cjs");let t=require("react");function n(){let[n]=(0,t.useState)(()=>e.createAudioPlayer()),[r,i]=(0,t.useState)(!1);return(0,t.useEffect)(()=>()=>{n.stop()},[n]),{play:(0,t.useCallback)(async(e,t)=>{await n.play(e,t)&&i(!0)},[n]),stop:(0,t.useCallback)(()=>{n.stop()},[n]),unlocked:r}}exports.useAudio=n;
2
2
  //# sourceMappingURL=use-audio.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-audio.cjs","names":[],"sources":["../../src/audio/use-audio.ts"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from \"react\";\nimport { createAudioPlayer, type AudioPlayer, type PlayAudioOptions } from \"./audio-player\";\n\nexport interface UseAudioResult {\n /** Play `src` on the hook's private player. */\n play: (src: string, options?: PlayAudioOptions) => Promise<void>;\n /** Stop the current clip. */\n stop: () => void;\n /**\n * Whether the browser's autoplay policy has been satisfied. Becomes\n * `true` after the first successful `play()`.\n */\n unlocked: boolean;\n}\n\n/**\n * Hook-managed audio player. Each component instance gets its own\n * {@link AudioPlayer}, so unmounting cleanly stops playback. Useful for\n * notification chimes, UI feedback sounds, and per-component soundtracks.\n */\nexport function useAudio(): UseAudioResult {\n const playerRef = useRef<AudioPlayer | null>(null);\n const [unlocked, setUnlocked] = useState<boolean>(false);\n\n if (!playerRef.current) {\n playerRef.current = createAudioPlayer();\n }\n\n useEffect(() => {\n return () => {\n playerRef.current?.stop();\n };\n }, []);\n\n const play = useCallback(async (src: string, options?: PlayAudioOptions): Promise<void> => {\n const result = await playerRef.current!.play(src, options);\n if (result) setUnlocked(true);\n }, []);\n\n const stop = useCallback((): void => {\n playerRef.current?.stop();\n }, []);\n\n return { play, stop, unlocked };\n}\n"],"mappings":"6DAoBA,SAAgB,GAA2B,CACvC,IAAM,GAAA,EAAA,EAAA,OAAA,CAAuC,IAAI,EAC3C,CAAC,EAAU,IAAA,EAAA,EAAA,SAAA,CAAiC,EAAK,EAqBvD,MAnBA,CACI,EAAU,UAAU,EAAA,kBAAkB,GAG1C,EAAA,EAAA,UAAA,SACiB,CACT,EAAU,SAAS,KAAK,CAC5B,EACD,CAAC,CAAC,EAWE,CAAE,MAAA,EAAA,EAAA,YAAA,CATgB,MAAO,EAAa,IAA8C,CAEnF,MADiB,EAAU,QAAS,KAAK,EAAK,CAAO,GAC7C,EAAY,EAAI,CAChC,EAAG,CAAC,CAMK,EAAM,MAAA,EAAA,EAAA,YAAA,KAJsB,CACjC,EAAU,SAAS,KAAK,CAC5B,EAAG,CAAC,CAEW,EAAM,UAAS,CAClC"}
1
+ {"version":3,"file":"use-audio.cjs","names":[],"sources":["../../src/audio/use-audio.ts"],"sourcesContent":["import { useCallback, useEffect, useState } from \"react\";\nimport { createAudioPlayer, type AudioPlayer, type PlayAudioOptions } from \"./audio-player\";\n\nexport interface UseAudioResult {\n /** Play `src` on the hook's private player. */\n play: (src: string, options?: PlayAudioOptions) => Promise<void>;\n /** Stop the current clip. */\n stop: () => void;\n /**\n * Whether the browser's autoplay policy has been satisfied. Becomes\n * `true` after the first successful `play()`.\n */\n unlocked: boolean;\n}\n\n/**\n * The player lives in a state initializer, not in a lazily-written ref: the\n * initializer runs exactly once and is not a side effect during render, which is\n * what the ref version was. The player identity never changes, so state holds it\n * fine and the value is non-null from the first render.\n *\n * Hook-managed audio player. Each component instance gets its own\n * {@link AudioPlayer}, so unmounting cleanly stops playback. Useful for\n * notification chimes, UI feedback sounds, and per-component soundtracks.\n */\nexport function useAudio(): UseAudioResult {\n const [player] = useState<AudioPlayer>(() => createAudioPlayer());\n const [unlocked, setUnlocked] = useState<boolean>(false);\n\n useEffect(() => {\n return () => {\n player.stop();\n };\n }, [player]);\n\n const play = useCallback(\n async (src: string, options?: PlayAudioOptions): Promise<void> => {\n const result = await player.play(src, options);\n if (result) setUnlocked(true);\n },\n [player],\n );\n\n const stop = useCallback((): void => {\n player.stop();\n }, [player]);\n\n return { play, stop, unlocked };\n}\n"],"mappings":"6DAyBA,SAAgB,GAA2B,CACvC,GAAM,CAAC,IAAA,EAAA,EAAA,SAAA,KAAsC,EAAA,kBAAkB,CAAC,EAC1D,CAAC,EAAU,IAAA,EAAA,EAAA,SAAA,CAAiC,EAAK,EAoBvD,OAlBA,EAAA,EAAA,UAAA,SACiB,CACT,EAAO,KAAK,CAChB,EACD,CAAC,CAAM,CAAC,EAcJ,CAAE,MAAA,EAAA,EAAA,YAAA,CAXL,MAAO,EAAa,IAA8C,CAE1D,MADiB,EAAO,KAAK,EAAK,CAAO,GACjC,EAAY,EAAI,CAChC,EACA,CAAC,CAAM,CAOF,EAAM,MAAA,EAAA,EAAA,YAAA,KAJsB,CACjC,EAAO,KAAK,CAChB,EAAG,CAAC,CAAM,CAEK,EAAM,UAAS,CAClC"}
@@ -1,21 +1,21 @@
1
1
  import { createAudioPlayer as e } from "./audio-player.js";
2
- import { useCallback as t, useEffect as n, useRef as r, useState as i } from "react";
2
+ import { useCallback as t, useEffect as n, useState as r } from "react";
3
3
  //#region src/audio/use-audio.ts
4
- function a() {
5
- let a = r(null), [o, s] = i(!1);
6
- return a.current ||= e(), n(() => () => {
7
- a.current?.stop();
8
- }, []), {
4
+ function i() {
5
+ let [i] = r(() => e()), [a, o] = r(!1);
6
+ return n(() => () => {
7
+ i.stop();
8
+ }, [i]), {
9
9
  play: t(async (e, t) => {
10
- await a.current.play(e, t) && s(!0);
11
- }, []),
10
+ await i.play(e, t) && o(!0);
11
+ }, [i]),
12
12
  stop: t(() => {
13
- a.current?.stop();
14
- }, []),
15
- unlocked: o
13
+ i.stop();
14
+ }, [i]),
15
+ unlocked: a
16
16
  };
17
17
  }
18
18
  //#endregion
19
- export { a as useAudio };
19
+ export { i as useAudio };
20
20
 
21
21
  //# sourceMappingURL=use-audio.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-audio.js","names":[],"sources":["../../src/audio/use-audio.ts"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from \"react\";\nimport { createAudioPlayer, type AudioPlayer, type PlayAudioOptions } from \"./audio-player\";\n\nexport interface UseAudioResult {\n /** Play `src` on the hook's private player. */\n play: (src: string, options?: PlayAudioOptions) => Promise<void>;\n /** Stop the current clip. */\n stop: () => void;\n /**\n * Whether the browser's autoplay policy has been satisfied. Becomes\n * `true` after the first successful `play()`.\n */\n unlocked: boolean;\n}\n\n/**\n * Hook-managed audio player. Each component instance gets its own\n * {@link AudioPlayer}, so unmounting cleanly stops playback. Useful for\n * notification chimes, UI feedback sounds, and per-component soundtracks.\n */\nexport function useAudio(): UseAudioResult {\n const playerRef = useRef<AudioPlayer | null>(null);\n const [unlocked, setUnlocked] = useState<boolean>(false);\n\n if (!playerRef.current) {\n playerRef.current = createAudioPlayer();\n }\n\n useEffect(() => {\n return () => {\n playerRef.current?.stop();\n };\n }, []);\n\n const play = useCallback(async (src: string, options?: PlayAudioOptions): Promise<void> => {\n const result = await playerRef.current!.play(src, options);\n if (result) setUnlocked(true);\n }, []);\n\n const stop = useCallback((): void => {\n playerRef.current?.stop();\n }, []);\n\n return { play, stop, unlocked };\n}\n"],"mappings":";;;AAoBA,SAAgB,IAA2B;CACvC,IAAM,IAAY,EAA2B,IAAI,GAC3C,CAAC,GAAU,KAAe,EAAkB,EAAK;CAqBvD,OAnBA,AACI,EAAU,YAAU,EAAkB,GAG1C,cACiB;EACT,EAAU,SAAS,KAAK;CAC5B,GACD,CAAC,CAAC,GAWE;EAAE,MATI,EAAY,OAAO,GAAa,MAA8C;GAEvF,AAAI,MADiB,EAAU,QAAS,KAAK,GAAK,CAAO,KAC7C,EAAY,EAAI;EAChC,GAAG,CAAC,CAMK;EAAM,MAJF,QAAwB;GACjC,EAAU,SAAS,KAAK;EAC5B,GAAG,CAAC,CAEW;EAAM;CAAS;AAClC"}
1
+ {"version":3,"file":"use-audio.js","names":[],"sources":["../../src/audio/use-audio.ts"],"sourcesContent":["import { useCallback, useEffect, useState } from \"react\";\nimport { createAudioPlayer, type AudioPlayer, type PlayAudioOptions } from \"./audio-player\";\n\nexport interface UseAudioResult {\n /** Play `src` on the hook's private player. */\n play: (src: string, options?: PlayAudioOptions) => Promise<void>;\n /** Stop the current clip. */\n stop: () => void;\n /**\n * Whether the browser's autoplay policy has been satisfied. Becomes\n * `true` after the first successful `play()`.\n */\n unlocked: boolean;\n}\n\n/**\n * The player lives in a state initializer, not in a lazily-written ref: the\n * initializer runs exactly once and is not a side effect during render, which is\n * what the ref version was. The player identity never changes, so state holds it\n * fine and the value is non-null from the first render.\n *\n * Hook-managed audio player. Each component instance gets its own\n * {@link AudioPlayer}, so unmounting cleanly stops playback. Useful for\n * notification chimes, UI feedback sounds, and per-component soundtracks.\n */\nexport function useAudio(): UseAudioResult {\n const [player] = useState<AudioPlayer>(() => createAudioPlayer());\n const [unlocked, setUnlocked] = useState<boolean>(false);\n\n useEffect(() => {\n return () => {\n player.stop();\n };\n }, [player]);\n\n const play = useCallback(\n async (src: string, options?: PlayAudioOptions): Promise<void> => {\n const result = await player.play(src, options);\n if (result) setUnlocked(true);\n },\n [player],\n );\n\n const stop = useCallback((): void => {\n player.stop();\n }, [player]);\n\n return { play, stop, unlocked };\n}\n"],"mappings":";;;AAyBA,SAAgB,IAA2B;CACvC,IAAM,CAAC,KAAU,QAA4B,EAAkB,CAAC,GAC1D,CAAC,GAAU,KAAe,EAAkB,EAAK;CAoBvD,OAlBA,cACiB;EACT,EAAO,KAAK;CAChB,GACD,CAAC,CAAM,CAAC,GAcJ;EAAE,MAZI,EACT,OAAO,GAAa,MAA8C;GAE9D,AAAI,MADiB,EAAO,KAAK,GAAK,CAAO,KACjC,EAAY,EAAI;EAChC,GACA,CAAC,CAAM,CAOF;EAAM,MAJF,QAAwB;GACjC,EAAO,KAAK;EAChB,GAAG,CAAC,CAAM,CAEK;EAAM;CAAS;AAClC"}
@@ -1,2 +1,2 @@
1
- var e=[`#2563eb`,`#16a34a`,`#f59e0b`,`#7c3aed`,`#ec4899`,`#06b6d4`],t=8;function n(t){if(typeof window>`u`||typeof document>`u`)return e;let n=t??document.documentElement;if(!n)return e;let r=window.getComputedStyle(n),i=[];for(let e=1;e<=8;e+=1){let t=r.getPropertyValue(`--tempest-chart-${e}`).trim();if(!t)break;i.push(t)}return i.length>0?i:e}function r(e,t,n=e===`grid`?`#e4e7ec`:`#667085`){if(typeof window>`u`||typeof document>`u`)return n;let r=t??document.documentElement;return r&&window.getComputedStyle(r).getPropertyValue(`--tempest-chart-${e}`).trim()||n}exports.CHART_COLOR_TOKEN_COUNT=t,exports.DEFAULT_CHART_COLORS=e,exports.resolveChartChrome=r,exports.resolveChartColors=n;
1
+ var e=[`#2563eb`,`#16a34a`,`#f59e0b`,`#7c3aed`,`#ec4899`,`#06b6d4`],t=8;function n(t){if(typeof window>`u`||typeof document>`u`)return e;let n=t??document.documentElement;if(!n)return e;let r=window.getComputedStyle(n),i=[],a=Number.parseInt(r.getPropertyValue(`--tempest-chart-count`).trim(),10),o=Number.isFinite(a)&&a>0?Math.min(a,8):8;for(let e=1;e<=o;e+=1){let t=r.getPropertyValue(`--tempest-chart-${e}`).trim();if(!t)break;i.push(t)}return i.length>0?i:e}function r(e,t,n=e===`grid`?`#e4e7ec`:`#667085`){if(typeof window>`u`||typeof document>`u`)return n;let r=t??document.documentElement;return r&&window.getComputedStyle(r).getPropertyValue(`--tempest-chart-${e}`).trim()||n}exports.CHART_COLOR_TOKEN_COUNT=t,exports.DEFAULT_CHART_COLORS=e,exports.resolveChartChrome=r,exports.resolveChartColors=n;
2
2
  //# sourceMappingURL=palette.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"palette.cjs","names":[],"sources":["../../src/charts/palette.ts"],"sourcesContent":["/**\n * Series colors for Tempest charts, resolved from the theme tokens.\n *\n * Recharts sets colors as SVG presentation attributes (`fill=\"…\"`), where\n * `var(--token)` is **not** substituted — browsers only resolve custom properties\n * in CSS declarations. So the tokens are read from the computed style and handed\n * over as literal colors, which is what keeps a re-theme (and dark mode)\n * reflected in the charts.\n */\n\n/**\n * Fallback palette: six visually distinct hex colors (blue, green, amber,\n * violet, pink, cyan) in cycle order.\n *\n * Used when the `--tempest-chart-*` tokens cannot be read — no stylesheet\n * imported, a non-browser environment (tests, build scripts), or a host page that\n * dropped the tokens. Exported so callers can start from it and pass a tweaked\n * array to any chart's `colors` prop.\n */\nexport const DEFAULT_CHART_COLORS: string[] = [\n \"#2563eb\", // blue\n \"#16a34a\", // green\n \"#f59e0b\", // amber\n \"#7c3aed\", // violet\n \"#ec4899\", // pink\n \"#06b6d4\", // cyan\n];\n\n/** How many `--tempest-chart-N` tokens the SDK ships. */\nexport const CHART_COLOR_TOKEN_COUNT = 8;\n\n/**\n * Read `--tempest-chart-1` … `--tempest-chart-8` into a color array.\n *\n * Stops at the first unset token, so an app that overrides only four series gets\n * exactly those four cycled instead of a half-themed tail. Returns\n * {@link DEFAULT_CHART_COLORS} when nothing is resolvable.\n *\n * @param element - Element to resolve the tokens against. Default `<html>`; pass\n * a subtree root when a section carries a scoped theme.\n * @returns Literal color strings in cycle order.\n */\nexport function resolveChartColors(element?: Element | null): string[] {\n if (typeof window === \"undefined\" || typeof document === \"undefined\") {\n return DEFAULT_CHART_COLORS;\n }\n\n const target = element ?? document.documentElement;\n if (!target) return DEFAULT_CHART_COLORS;\n\n const styles = window.getComputedStyle(target);\n const resolved: string[] = [];\n\n for (let index = 1; index <= CHART_COLOR_TOKEN_COUNT; index += 1) {\n const value = styles.getPropertyValue(`--tempest-chart-${index}`).trim();\n if (!value) break;\n resolved.push(value);\n }\n\n return resolved.length > 0 ? resolved : DEFAULT_CHART_COLORS;\n}\n\n/**\n * Read one piece of chart chrome (`--tempest-chart-grid` / `--tempest-chart-axis`).\n *\n * @param part - Which chrome token to read.\n * @param element - Element to resolve against. Default `<html>`.\n * @param fallback - Value returned when the token is unset.\n * @returns The resolved color, or `fallback`.\n */\nexport function resolveChartChrome(\n part: \"grid\" | \"axis\",\n element?: Element | null,\n fallback = part === \"grid\" ? \"#e4e7ec\" : \"#667085\",\n): string {\n if (typeof window === \"undefined\" || typeof document === \"undefined\") return fallback;\n const target = element ?? document.documentElement;\n if (!target) return fallback;\n const value = window\n .getComputedStyle(target)\n .getPropertyValue(`--tempest-chart-${part}`)\n .trim();\n return value || fallback;\n}\n"],"mappings":"AAmBA,IAAa,EAAiC,CAC1C,UACA,UACA,UACA,UACA,UACA,SACJ,EAGa,EAA0B,EAavC,SAAgB,EAAmB,EAAoC,CACnE,GAAI,OAAO,OAAW,KAAe,OAAO,SAAa,IACrD,OAAO,EAGX,IAAM,EAAS,GAAW,SAAS,gBACnC,GAAI,CAAC,EAAQ,OAAO,EAEpB,IAAM,EAAS,OAAO,iBAAiB,CAAM,EACvC,EAAqB,CAAC,EAE5B,IAAK,IAAI,EAAQ,EAAG,GAAA,EAAkC,GAAS,EAAG,CAC9D,IAAM,EAAQ,EAAO,iBAAiB,mBAAmB,GAAO,CAAC,CAAC,KAAK,EACvE,GAAI,CAAC,EAAO,MACZ,EAAS,KAAK,CAAK,CACvB,CAEA,OAAO,EAAS,OAAS,EAAI,EAAW,CAC5C,CAUA,SAAgB,EACZ,EACA,EACA,EAAW,IAAS,OAAS,UAAY,UACnC,CACN,GAAI,OAAO,OAAW,KAAe,OAAO,SAAa,IAAa,OAAO,EAC7E,IAAM,EAAS,GAAW,SAAS,gBAMnC,OALK,GACS,OACT,iBAAiB,CAAM,CAAC,CACxB,iBAAiB,mBAAmB,GAAM,CAAC,CAC3C,KACE,GALa,CAMxB"}
1
+ {"version":3,"file":"palette.cjs","names":[],"sources":["../../src/charts/palette.ts"],"sourcesContent":["/**\n * Series colors for Tempest charts, resolved from the theme tokens.\n *\n * Recharts sets colors as SVG presentation attributes (`fill=\"…\"`), where\n * `var(--token)` is **not** substituted — browsers only resolve custom properties\n * in CSS declarations. So the tokens are read from the computed style and handed\n * over as literal colors, which is what keeps a re-theme (and dark mode)\n * reflected in the charts.\n */\n\n/**\n * Fallback palette: six visually distinct hex colors (blue, green, amber,\n * violet, pink, cyan) in cycle order.\n *\n * Used when the `--tempest-chart-*` tokens cannot be read — no stylesheet\n * imported, a non-browser environment (tests, build scripts), or a host page that\n * dropped the tokens. Exported so callers can start from it and pass a tweaked\n * array to any chart's `colors` prop.\n */\nexport const DEFAULT_CHART_COLORS: string[] = [\n \"#2563eb\", // blue\n \"#16a34a\", // green\n \"#f59e0b\", // amber\n \"#7c3aed\", // violet\n \"#ec4899\", // pink\n \"#06b6d4\", // cyan\n];\n\n/** How many `--tempest-chart-N` tokens the SDK ships. */\nexport const CHART_COLOR_TOKEN_COUNT = 8;\n\n/**\n * Read `--tempest-chart-1` … `--tempest-chart-8` into a color array.\n *\n * Respects `--tempest-chart-count` when the theme declares one (`createTheme`\n * writes it), so a six-color brand palette is read as six and not padded with the\n * SDK's leftover `-7`/`-8` defaults. Otherwise it walks the tokens and stops at\n * the first unset one. Returns {@link DEFAULT_CHART_COLORS} when nothing is\n * resolvable.\n *\n * @param element - Element to resolve the tokens against. Default `<html>`; pass\n * a subtree root when a section carries a scoped theme.\n * @returns Literal color strings in cycle order.\n */\nexport function resolveChartColors(element?: Element | null): string[] {\n if (typeof window === \"undefined\" || typeof document === \"undefined\") {\n return DEFAULT_CHART_COLORS;\n }\n\n const target = element ?? document.documentElement;\n if (!target) return DEFAULT_CHART_COLORS;\n\n const styles = window.getComputedStyle(target);\n const resolved: string[] = [];\n const declared = Number.parseInt(styles.getPropertyValue(\"--tempest-chart-count\").trim(), 10);\n const limit =\n Number.isFinite(declared) && declared > 0\n ? Math.min(declared, CHART_COLOR_TOKEN_COUNT)\n : CHART_COLOR_TOKEN_COUNT;\n\n for (let index = 1; index <= limit; index += 1) {\n const value = styles.getPropertyValue(`--tempest-chart-${index}`).trim();\n if (!value) break;\n resolved.push(value);\n }\n\n return resolved.length > 0 ? resolved : DEFAULT_CHART_COLORS;\n}\n\n/**\n * Read one piece of chart chrome (`--tempest-chart-grid` / `--tempest-chart-axis`).\n *\n * @param part - Which chrome token to read.\n * @param element - Element to resolve against. Default `<html>`.\n * @param fallback - Value returned when the token is unset.\n * @returns The resolved color, or `fallback`.\n */\nexport function resolveChartChrome(\n part: \"grid\" | \"axis\",\n element?: Element | null,\n fallback = part === \"grid\" ? \"#e4e7ec\" : \"#667085\",\n): string {\n if (typeof window === \"undefined\" || typeof document === \"undefined\") return fallback;\n const target = element ?? document.documentElement;\n if (!target) return fallback;\n const value = window\n .getComputedStyle(target)\n .getPropertyValue(`--tempest-chart-${part}`)\n .trim();\n return value || fallback;\n}\n"],"mappings":"AAmBA,IAAa,EAAiC,CAC1C,UACA,UACA,UACA,UACA,UACA,SACJ,EAGa,EAA0B,EAevC,SAAgB,EAAmB,EAAoC,CACnE,GAAI,OAAO,OAAW,KAAe,OAAO,SAAa,IACrD,OAAO,EAGX,IAAM,EAAS,GAAW,SAAS,gBACnC,GAAI,CAAC,EAAQ,OAAO,EAEpB,IAAM,EAAS,OAAO,iBAAiB,CAAM,EACvC,EAAqB,CAAC,EACtB,EAAW,OAAO,SAAS,EAAO,iBAAiB,uBAAuB,CAAC,CAAC,KAAK,EAAG,EAAE,EACtF,EACF,OAAO,SAAS,CAAQ,GAAK,EAAW,EAClC,KAAK,IAAI,EAAA,CAAiC,EAAA,EAGpD,IAAK,IAAI,EAAQ,EAAG,GAAS,EAAO,GAAS,EAAG,CAC5C,IAAM,EAAQ,EAAO,iBAAiB,mBAAmB,GAAO,CAAC,CAAC,KAAK,EACvE,GAAI,CAAC,EAAO,MACZ,EAAS,KAAK,CAAK,CACvB,CAEA,OAAO,EAAS,OAAS,EAAI,EAAW,CAC5C,CAUA,SAAgB,EACZ,EACA,EACA,EAAW,IAAS,OAAS,UAAY,UACnC,CACN,GAAI,OAAO,OAAW,KAAe,OAAO,SAAa,IAAa,OAAO,EAC7E,IAAM,EAAS,GAAW,SAAS,gBAMnC,OALK,GACS,OACT,iBAAiB,CAAM,CAAC,CACxB,iBAAiB,mBAAmB,GAAM,CAAC,CAC3C,KACE,GALa,CAMxB"}
@@ -11,8 +11,8 @@ function n(t) {
11
11
  if (typeof window > "u" || typeof document > "u") return e;
12
12
  let n = t ?? document.documentElement;
13
13
  if (!n) return e;
14
- let r = window.getComputedStyle(n), i = [];
15
- for (let e = 1; e <= 8; e += 1) {
14
+ let r = window.getComputedStyle(n), i = [], a = Number.parseInt(r.getPropertyValue("--tempest-chart-count").trim(), 10), o = Number.isFinite(a) && a > 0 ? Math.min(a, 8) : 8;
15
+ for (let e = 1; e <= o; e += 1) {
16
16
  let t = r.getPropertyValue(`--tempest-chart-${e}`).trim();
17
17
  if (!t) break;
18
18
  i.push(t);
@@ -1 +1 @@
1
- {"version":3,"file":"palette.js","names":[],"sources":["../../src/charts/palette.ts"],"sourcesContent":["/**\n * Series colors for Tempest charts, resolved from the theme tokens.\n *\n * Recharts sets colors as SVG presentation attributes (`fill=\"…\"`), where\n * `var(--token)` is **not** substituted — browsers only resolve custom properties\n * in CSS declarations. So the tokens are read from the computed style and handed\n * over as literal colors, which is what keeps a re-theme (and dark mode)\n * reflected in the charts.\n */\n\n/**\n * Fallback palette: six visually distinct hex colors (blue, green, amber,\n * violet, pink, cyan) in cycle order.\n *\n * Used when the `--tempest-chart-*` tokens cannot be read — no stylesheet\n * imported, a non-browser environment (tests, build scripts), or a host page that\n * dropped the tokens. Exported so callers can start from it and pass a tweaked\n * array to any chart's `colors` prop.\n */\nexport const DEFAULT_CHART_COLORS: string[] = [\n \"#2563eb\", // blue\n \"#16a34a\", // green\n \"#f59e0b\", // amber\n \"#7c3aed\", // violet\n \"#ec4899\", // pink\n \"#06b6d4\", // cyan\n];\n\n/** How many `--tempest-chart-N` tokens the SDK ships. */\nexport const CHART_COLOR_TOKEN_COUNT = 8;\n\n/**\n * Read `--tempest-chart-1` … `--tempest-chart-8` into a color array.\n *\n * Stops at the first unset token, so an app that overrides only four series gets\n * exactly those four cycled instead of a half-themed tail. Returns\n * {@link DEFAULT_CHART_COLORS} when nothing is resolvable.\n *\n * @param element - Element to resolve the tokens against. Default `<html>`; pass\n * a subtree root when a section carries a scoped theme.\n * @returns Literal color strings in cycle order.\n */\nexport function resolveChartColors(element?: Element | null): string[] {\n if (typeof window === \"undefined\" || typeof document === \"undefined\") {\n return DEFAULT_CHART_COLORS;\n }\n\n const target = element ?? document.documentElement;\n if (!target) return DEFAULT_CHART_COLORS;\n\n const styles = window.getComputedStyle(target);\n const resolved: string[] = [];\n\n for (let index = 1; index <= CHART_COLOR_TOKEN_COUNT; index += 1) {\n const value = styles.getPropertyValue(`--tempest-chart-${index}`).trim();\n if (!value) break;\n resolved.push(value);\n }\n\n return resolved.length > 0 ? resolved : DEFAULT_CHART_COLORS;\n}\n\n/**\n * Read one piece of chart chrome (`--tempest-chart-grid` / `--tempest-chart-axis`).\n *\n * @param part - Which chrome token to read.\n * @param element - Element to resolve against. Default `<html>`.\n * @param fallback - Value returned when the token is unset.\n * @returns The resolved color, or `fallback`.\n */\nexport function resolveChartChrome(\n part: \"grid\" | \"axis\",\n element?: Element | null,\n fallback = part === \"grid\" ? \"#e4e7ec\" : \"#667085\",\n): string {\n if (typeof window === \"undefined\" || typeof document === \"undefined\") return fallback;\n const target = element ?? document.documentElement;\n if (!target) return fallback;\n const value = window\n .getComputedStyle(target)\n .getPropertyValue(`--tempest-chart-${part}`)\n .trim();\n return value || fallback;\n}\n"],"mappings":";AAmBA,IAAa,IAAiC;CAC1C;CACA;CACA;CACA;CACA;CACA;AACJ,GAGa,IAA0B;AAavC,SAAgB,EAAmB,GAAoC;CACnE,IAAI,OAAO,SAAW,OAAe,OAAO,WAAa,KACrD,OAAO;CAGX,IAAM,IAAS,KAAW,SAAS;CACnC,IAAI,CAAC,GAAQ,OAAO;CAEpB,IAAM,IAAS,OAAO,iBAAiB,CAAM,GACvC,IAAqB,CAAC;CAE5B,KAAK,IAAI,IAAQ,GAAG,KAAA,GAAkC,KAAS,GAAG;EAC9D,IAAM,IAAQ,EAAO,iBAAiB,mBAAmB,GAAO,CAAC,CAAC,KAAK;EACvE,IAAI,CAAC,GAAO;EACZ,EAAS,KAAK,CAAK;CACvB;CAEA,OAAO,EAAS,SAAS,IAAI,IAAW;AAC5C;AAUA,SAAgB,EACZ,GACA,GACA,IAAW,MAAS,SAAS,YAAY,WACnC;CACN,IAAI,OAAO,SAAW,OAAe,OAAO,WAAa,KAAa,OAAO;CAC7E,IAAM,IAAS,KAAW,SAAS;CAMnC,OALK,KACS,OACT,iBAAiB,CAAM,CAAC,CACxB,iBAAiB,mBAAmB,GAAM,CAAC,CAC3C,KACE,KALa;AAMxB"}
1
+ {"version":3,"file":"palette.js","names":[],"sources":["../../src/charts/palette.ts"],"sourcesContent":["/**\n * Series colors for Tempest charts, resolved from the theme tokens.\n *\n * Recharts sets colors as SVG presentation attributes (`fill=\"…\"`), where\n * `var(--token)` is **not** substituted — browsers only resolve custom properties\n * in CSS declarations. So the tokens are read from the computed style and handed\n * over as literal colors, which is what keeps a re-theme (and dark mode)\n * reflected in the charts.\n */\n\n/**\n * Fallback palette: six visually distinct hex colors (blue, green, amber,\n * violet, pink, cyan) in cycle order.\n *\n * Used when the `--tempest-chart-*` tokens cannot be read — no stylesheet\n * imported, a non-browser environment (tests, build scripts), or a host page that\n * dropped the tokens. Exported so callers can start from it and pass a tweaked\n * array to any chart's `colors` prop.\n */\nexport const DEFAULT_CHART_COLORS: string[] = [\n \"#2563eb\", // blue\n \"#16a34a\", // green\n \"#f59e0b\", // amber\n \"#7c3aed\", // violet\n \"#ec4899\", // pink\n \"#06b6d4\", // cyan\n];\n\n/** How many `--tempest-chart-N` tokens the SDK ships. */\nexport const CHART_COLOR_TOKEN_COUNT = 8;\n\n/**\n * Read `--tempest-chart-1` … `--tempest-chart-8` into a color array.\n *\n * Respects `--tempest-chart-count` when the theme declares one (`createTheme`\n * writes it), so a six-color brand palette is read as six and not padded with the\n * SDK's leftover `-7`/`-8` defaults. Otherwise it walks the tokens and stops at\n * the first unset one. Returns {@link DEFAULT_CHART_COLORS} when nothing is\n * resolvable.\n *\n * @param element - Element to resolve the tokens against. Default `<html>`; pass\n * a subtree root when a section carries a scoped theme.\n * @returns Literal color strings in cycle order.\n */\nexport function resolveChartColors(element?: Element | null): string[] {\n if (typeof window === \"undefined\" || typeof document === \"undefined\") {\n return DEFAULT_CHART_COLORS;\n }\n\n const target = element ?? document.documentElement;\n if (!target) return DEFAULT_CHART_COLORS;\n\n const styles = window.getComputedStyle(target);\n const resolved: string[] = [];\n const declared = Number.parseInt(styles.getPropertyValue(\"--tempest-chart-count\").trim(), 10);\n const limit =\n Number.isFinite(declared) && declared > 0\n ? Math.min(declared, CHART_COLOR_TOKEN_COUNT)\n : CHART_COLOR_TOKEN_COUNT;\n\n for (let index = 1; index <= limit; index += 1) {\n const value = styles.getPropertyValue(`--tempest-chart-${index}`).trim();\n if (!value) break;\n resolved.push(value);\n }\n\n return resolved.length > 0 ? resolved : DEFAULT_CHART_COLORS;\n}\n\n/**\n * Read one piece of chart chrome (`--tempest-chart-grid` / `--tempest-chart-axis`).\n *\n * @param part - Which chrome token to read.\n * @param element - Element to resolve against. Default `<html>`.\n * @param fallback - Value returned when the token is unset.\n * @returns The resolved color, or `fallback`.\n */\nexport function resolveChartChrome(\n part: \"grid\" | \"axis\",\n element?: Element | null,\n fallback = part === \"grid\" ? \"#e4e7ec\" : \"#667085\",\n): string {\n if (typeof window === \"undefined\" || typeof document === \"undefined\") return fallback;\n const target = element ?? document.documentElement;\n if (!target) return fallback;\n const value = window\n .getComputedStyle(target)\n .getPropertyValue(`--tempest-chart-${part}`)\n .trim();\n return value || fallback;\n}\n"],"mappings":";AAmBA,IAAa,IAAiC;CAC1C;CACA;CACA;CACA;CACA;CACA;AACJ,GAGa,IAA0B;AAevC,SAAgB,EAAmB,GAAoC;CACnE,IAAI,OAAO,SAAW,OAAe,OAAO,WAAa,KACrD,OAAO;CAGX,IAAM,IAAS,KAAW,SAAS;CACnC,IAAI,CAAC,GAAQ,OAAO;CAEpB,IAAM,IAAS,OAAO,iBAAiB,CAAM,GACvC,IAAqB,CAAC,GACtB,IAAW,OAAO,SAAS,EAAO,iBAAiB,uBAAuB,CAAC,CAAC,KAAK,GAAG,EAAE,GACtF,IACF,OAAO,SAAS,CAAQ,KAAK,IAAW,IAClC,KAAK,IAAI,GAAA,CAAiC,IAAA;CAGpD,KAAK,IAAI,IAAQ,GAAG,KAAS,GAAO,KAAS,GAAG;EAC5C,IAAM,IAAQ,EAAO,iBAAiB,mBAAmB,GAAO,CAAC,CAAC,KAAK;EACvE,IAAI,CAAC,GAAO;EACZ,EAAS,KAAK,CAAK;CACvB;CAEA,OAAO,EAAS,SAAS,IAAI,IAAW;AAC5C;AAUA,SAAgB,EACZ,GACA,GACA,IAAW,MAAS,SAAS,YAAY,WACnC;CACN,IAAI,OAAO,SAAW,OAAe,OAAO,WAAa,KAAa,OAAO;CAC7E,IAAM,IAAS,KAAW,SAAS;CAMnC,OALK,KACS,OACT,iBAAiB,CAAM,CAAC,CACxB,iBAAiB,mBAAmB,GAAM,CAAC,CAC3C,KACE,KALa;AAMxB"}
package/dist/charts.d.ts CHANGED
@@ -153,9 +153,11 @@ export declare function resolveChartChrome(part: "grid" | "axis", element?: Elem
153
153
  /**
154
154
  * Read `--tempest-chart-1` … `--tempest-chart-8` into a color array.
155
155
  *
156
- * Stops at the first unset token, so an app that overrides only four series gets
157
- * exactly those four cycled instead of a half-themed tail. Returns
158
- * {@link DEFAULT_CHART_COLORS} when nothing is resolvable.
156
+ * Respects `--tempest-chart-count` when the theme declares one (`createTheme`
157
+ * writes it), so a six-color brand palette is read as six and not padded with the
158
+ * SDK's leftover `-7`/`-8` defaults. Otherwise it walks the tokens and stops at
159
+ * the first unset one. Returns {@link DEFAULT_CHART_COLORS} when nothing is
160
+ * resolvable.
159
161
  *
160
162
  * @param element - Element to resolve the tokens against. Default `<html>`; pass
161
163
  * a subtree root when a section carries a scoped theme.
@@ -1,2 +1,2 @@
1
- let e=require("react"),t=require("react/jsx-runtime");function n({onOutside:n,children:r,...i}){let a=(0,e.useRef)(null),o=(0,e.useRef)(n);return o.current=n,(0,e.useEffect)(()=>{if(typeof document>`u`)return;function e(e){let t=a.current,n=e.target;t&&n&&!t.contains(n)&&o.current(e)}return document.addEventListener(`mousedown`,e),document.addEventListener(`touchstart`,e),()=>{document.removeEventListener(`mousedown`,e),document.removeEventListener(`touchstart`,e)}},[]),(0,t.jsx)(`div`,{ref:a,...i,children:r})}exports.ClickOutside=n;
1
+ let e=require("react"),t=require("react/jsx-runtime");function n({onOutside:n,children:r,...i}){let a=(0,e.useRef)(null),o=(0,e.useRef)(n);return(0,e.useEffect)(()=>{o.current=n},[n]),(0,e.useEffect)(()=>{if(typeof document>`u`)return;function e(e){let t=a.current,n=e.target;t&&n&&!t.contains(n)&&o.current(e)}return document.addEventListener(`mousedown`,e),document.addEventListener(`touchstart`,e),()=>{document.removeEventListener(`mousedown`,e),document.removeEventListener(`touchstart`,e)}},[]),(0,t.jsx)(`div`,{ref:a,...i,children:r})}exports.ClickOutside=n;
2
2
  //# sourceMappingURL=ClickOutside.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"ClickOutside.cjs","names":[],"sources":["../../../src/components/ClickOutside/ClickOutside.tsx"],"sourcesContent":["import { useEffect, useRef, type HTMLAttributes, type ReactNode } from \"react\";\n\nexport interface ClickOutsideProps extends HTMLAttributes<HTMLDivElement> {\n /** Called when a pointer event lands outside the wrapped subtree. */\n onOutside: (event: MouseEvent | TouchEvent) => void;\n /** Content wrapped by the outside-click boundary. */\n children: ReactNode;\n}\n\n/**\n * Wraps its children in a `<div>` and invokes `onOutside` whenever a\n * `mousedown` or `touchstart` event occurs outside that subtree.\n *\n * Useful for dismissing popovers, dropdowns and menus on outside interaction.\n * Listeners are attached to `document` on mount and removed on unmount.\n *\n * @param props - The click-outside props, plus any `<div>` attributes.\n * @returns The wrapper element containing the children.\n */\nexport function ClickOutside({ onOutside, children, ...props }: ClickOutsideProps): ReactNode {\n const ref = useRef<HTMLDivElement>(null);\n const handlerRef = useRef(onOutside);\n handlerRef.current = onOutside;\n\n useEffect(() => {\n if (typeof document === \"undefined\") {\n return;\n }\n\n function handle(event: MouseEvent | TouchEvent): void {\n const node = ref.current;\n const target = event.target as Node | null;\n if (node && target && !node.contains(target)) {\n handlerRef.current(event);\n }\n }\n\n document.addEventListener(\"mousedown\", handle);\n document.addEventListener(\"touchstart\", handle);\n return () => {\n document.removeEventListener(\"mousedown\", handle);\n document.removeEventListener(\"touchstart\", handle);\n };\n }, []);\n\n return (\n <div ref={ref} {...props}>\n {children}\n </div>\n );\n}\n"],"mappings":"sDAmBA,SAAgB,EAAa,CAAE,YAAW,WAAU,GAAG,GAAuC,CAC1F,IAAM,GAAA,EAAA,EAAA,OAAA,CAA6B,IAAI,EACjC,GAAA,EAAA,EAAA,OAAA,CAAoB,CAAS,EAwBnC,MAvBA,GAAW,QAAU,GAErB,EAAA,EAAA,UAAA,KAAgB,CACZ,GAAI,OAAO,SAAa,IACpB,OAGJ,SAAS,EAAO,EAAsC,CAClD,IAAM,EAAO,EAAI,QACX,EAAS,EAAM,OACjB,GAAQ,GAAU,CAAC,EAAK,SAAS,CAAM,GACvC,EAAW,QAAQ,CAAK,CAEhC,CAIA,OAFA,SAAS,iBAAiB,YAAa,CAAM,EAC7C,SAAS,iBAAiB,aAAc,CAAM,MACjC,CACT,SAAS,oBAAoB,YAAa,CAAM,EAChD,SAAS,oBAAoB,aAAc,CAAM,CACrD,CACJ,EAAG,CAAC,CAAC,GAGD,EAAA,EAAA,IAAA,CAAC,MAAD,CAAU,MAAK,GAAI,EACd,UACA,CAAA,CAEb"}
1
+ {"version":3,"file":"ClickOutside.cjs","names":[],"sources":["../../../src/components/ClickOutside/ClickOutside.tsx"],"sourcesContent":["import { useEffect, useRef, type HTMLAttributes, type ReactNode } from \"react\";\n\nexport interface ClickOutsideProps extends HTMLAttributes<HTMLDivElement> {\n /** Called when a pointer event lands outside the wrapped subtree. */\n onOutside: (event: MouseEvent | TouchEvent) => void;\n /** Content wrapped by the outside-click boundary. */\n children: ReactNode;\n}\n\n/**\n * Wraps its children in a `<div>` and invokes `onOutside` whenever a\n * `mousedown` or `touchstart` event occurs outside that subtree.\n *\n * Useful for dismissing popovers, dropdowns and menus on outside interaction.\n * Listeners are attached to `document` on mount and removed on unmount.\n *\n * @param props - The click-outside props, plus any `<div>` attributes.\n * @returns The wrapper element containing the children.\n */\nexport function ClickOutside({ onOutside, children, ...props }: ClickOutsideProps): ReactNode {\n const ref = useRef<HTMLDivElement>(null);\n const handlerRef = useRef(onOutside);\n useEffect(() => {\n handlerRef.current = onOutside;\n }, [onOutside]);\n\n useEffect(() => {\n if (typeof document === \"undefined\") {\n return;\n }\n\n function handle(event: MouseEvent | TouchEvent): void {\n const node = ref.current;\n const target = event.target as Node | null;\n if (node && target && !node.contains(target)) {\n handlerRef.current(event);\n }\n }\n\n document.addEventListener(\"mousedown\", handle);\n document.addEventListener(\"touchstart\", handle);\n return () => {\n document.removeEventListener(\"mousedown\", handle);\n document.removeEventListener(\"touchstart\", handle);\n };\n }, []);\n\n return (\n <div ref={ref} {...props}>\n {children}\n </div>\n );\n}\n"],"mappings":"sDAmBA,SAAgB,EAAa,CAAE,YAAW,WAAU,GAAG,GAAuC,CAC1F,IAAM,GAAA,EAAA,EAAA,OAAA,CAA6B,IAAI,EACjC,GAAA,EAAA,EAAA,OAAA,CAAoB,CAAS,EA0BnC,OAzBA,EAAA,EAAA,UAAA,KAAgB,CACZ,EAAW,QAAU,CACzB,EAAG,CAAC,CAAS,CAAC,GAEd,EAAA,EAAA,UAAA,KAAgB,CACZ,GAAI,OAAO,SAAa,IACpB,OAGJ,SAAS,EAAO,EAAsC,CAClD,IAAM,EAAO,EAAI,QACX,EAAS,EAAM,OACjB,GAAQ,GAAU,CAAC,EAAK,SAAS,CAAM,GACvC,EAAW,QAAQ,CAAK,CAEhC,CAIA,OAFA,SAAS,iBAAiB,YAAa,CAAM,EAC7C,SAAS,iBAAiB,aAAc,CAAM,MACjC,CACT,SAAS,oBAAoB,YAAa,CAAM,EAChD,SAAS,oBAAoB,aAAc,CAAM,CACrD,CACJ,EAAG,CAAC,CAAC,GAGD,EAAA,EAAA,IAAA,CAAC,MAAD,CAAU,MAAK,GAAI,EACd,UACA,CAAA,CAEb"}
@@ -3,7 +3,9 @@ import { jsx as n } from "react/jsx-runtime";
3
3
  //#region src/components/ClickOutside/ClickOutside.tsx
4
4
  function r({ onOutside: r, children: i, ...a }) {
5
5
  let o = t(null), s = t(r);
6
- return s.current = r, e(() => {
6
+ return e(() => {
7
+ s.current = r;
8
+ }, [r]), e(() => {
7
9
  if (typeof document > "u") return;
8
10
  function e(e) {
9
11
  let t = o.current, n = e.target;
@@ -1 +1 @@
1
- {"version":3,"file":"ClickOutside.js","names":[],"sources":["../../../src/components/ClickOutside/ClickOutside.tsx"],"sourcesContent":["import { useEffect, useRef, type HTMLAttributes, type ReactNode } from \"react\";\n\nexport interface ClickOutsideProps extends HTMLAttributes<HTMLDivElement> {\n /** Called when a pointer event lands outside the wrapped subtree. */\n onOutside: (event: MouseEvent | TouchEvent) => void;\n /** Content wrapped by the outside-click boundary. */\n children: ReactNode;\n}\n\n/**\n * Wraps its children in a `<div>` and invokes `onOutside` whenever a\n * `mousedown` or `touchstart` event occurs outside that subtree.\n *\n * Useful for dismissing popovers, dropdowns and menus on outside interaction.\n * Listeners are attached to `document` on mount and removed on unmount.\n *\n * @param props - The click-outside props, plus any `<div>` attributes.\n * @returns The wrapper element containing the children.\n */\nexport function ClickOutside({ onOutside, children, ...props }: ClickOutsideProps): ReactNode {\n const ref = useRef<HTMLDivElement>(null);\n const handlerRef = useRef(onOutside);\n handlerRef.current = onOutside;\n\n useEffect(() => {\n if (typeof document === \"undefined\") {\n return;\n }\n\n function handle(event: MouseEvent | TouchEvent): void {\n const node = ref.current;\n const target = event.target as Node | null;\n if (node && target && !node.contains(target)) {\n handlerRef.current(event);\n }\n }\n\n document.addEventListener(\"mousedown\", handle);\n document.addEventListener(\"touchstart\", handle);\n return () => {\n document.removeEventListener(\"mousedown\", handle);\n document.removeEventListener(\"touchstart\", handle);\n };\n }, []);\n\n return (\n <div ref={ref} {...props}>\n {children}\n </div>\n );\n}\n"],"mappings":";;;AAmBA,SAAgB,EAAa,EAAE,cAAW,aAAU,GAAG,KAAuC;CAC1F,IAAM,IAAM,EAAuB,IAAI,GACjC,IAAa,EAAO,CAAS;CAwBnC,OAvBA,EAAW,UAAU,GAErB,QAAgB;EACZ,IAAI,OAAO,WAAa,KACpB;EAGJ,SAAS,EAAO,GAAsC;GAClD,IAAM,IAAO,EAAI,SACX,IAAS,EAAM;GACrB,AAAI,KAAQ,KAAU,CAAC,EAAK,SAAS,CAAM,KACvC,EAAW,QAAQ,CAAK;EAEhC;EAIA,OAFA,SAAS,iBAAiB,aAAa,CAAM,GAC7C,SAAS,iBAAiB,cAAc,CAAM,SACjC;GAET,AADA,SAAS,oBAAoB,aAAa,CAAM,GAChD,SAAS,oBAAoB,cAAc,CAAM;EACrD;CACJ,GAAG,CAAC,CAAC,GAGD,kBAAC,OAAD;EAAU;EAAK,GAAI;EACd;CACA,CAAA;AAEb"}
1
+ {"version":3,"file":"ClickOutside.js","names":[],"sources":["../../../src/components/ClickOutside/ClickOutside.tsx"],"sourcesContent":["import { useEffect, useRef, type HTMLAttributes, type ReactNode } from \"react\";\n\nexport interface ClickOutsideProps extends HTMLAttributes<HTMLDivElement> {\n /** Called when a pointer event lands outside the wrapped subtree. */\n onOutside: (event: MouseEvent | TouchEvent) => void;\n /** Content wrapped by the outside-click boundary. */\n children: ReactNode;\n}\n\n/**\n * Wraps its children in a `<div>` and invokes `onOutside` whenever a\n * `mousedown` or `touchstart` event occurs outside that subtree.\n *\n * Useful for dismissing popovers, dropdowns and menus on outside interaction.\n * Listeners are attached to `document` on mount and removed on unmount.\n *\n * @param props - The click-outside props, plus any `<div>` attributes.\n * @returns The wrapper element containing the children.\n */\nexport function ClickOutside({ onOutside, children, ...props }: ClickOutsideProps): ReactNode {\n const ref = useRef<HTMLDivElement>(null);\n const handlerRef = useRef(onOutside);\n useEffect(() => {\n handlerRef.current = onOutside;\n }, [onOutside]);\n\n useEffect(() => {\n if (typeof document === \"undefined\") {\n return;\n }\n\n function handle(event: MouseEvent | TouchEvent): void {\n const node = ref.current;\n const target = event.target as Node | null;\n if (node && target && !node.contains(target)) {\n handlerRef.current(event);\n }\n }\n\n document.addEventListener(\"mousedown\", handle);\n document.addEventListener(\"touchstart\", handle);\n return () => {\n document.removeEventListener(\"mousedown\", handle);\n document.removeEventListener(\"touchstart\", handle);\n };\n }, []);\n\n return (\n <div ref={ref} {...props}>\n {children}\n </div>\n );\n}\n"],"mappings":";;;AAmBA,SAAgB,EAAa,EAAE,cAAW,aAAU,GAAG,KAAuC;CAC1F,IAAM,IAAM,EAAuB,IAAI,GACjC,IAAa,EAAO,CAAS;CA0BnC,OAzBA,QAAgB;EACZ,EAAW,UAAU;CACzB,GAAG,CAAC,CAAS,CAAC,GAEd,QAAgB;EACZ,IAAI,OAAO,WAAa,KACpB;EAGJ,SAAS,EAAO,GAAsC;GAClD,IAAM,IAAO,EAAI,SACX,IAAS,EAAM;GACrB,AAAI,KAAQ,KAAU,CAAC,EAAK,SAAS,CAAM,KACvC,EAAW,QAAQ,CAAK;EAEhC;EAIA,OAFA,SAAS,iBAAiB,aAAa,CAAM,GAC7C,SAAS,iBAAiB,cAAc,CAAM,SACjC;GAET,AADA,SAAS,oBAAoB,aAAa,CAAM,GAChD,SAAS,oBAAoB,cAAc,CAAM;EACrD;CACJ,GAAG,CAAC,CAAC,GAGD,kBAAC,OAAD;EAAU;EAAK,GAAI;EACd;CACA,CAAA;AAEb"}
@@ -1,2 +1,2 @@
1
- const e=require("../../utils/cn.cjs"),t=require("../../hooks/use-pagination.cjs"),n=require("../Pagination/Pagination.cjs"),r=require("../SearchBar/SearchBar.cjs"),i=require("../Table/Table.cjs"),a=require("./DataTable.module.cjs");let o=require("react"),s=require("react/jsx-runtime");function c(e,t){return e==null&&t==null?0:e==null?-1:t==null?1:typeof e==`number`&&typeof t==`number`?e-t:e instanceof Date&&t instanceof Date?e.getTime()-t.getTime():typeof e==`boolean`&&typeof t==`boolean`?Number(e)-Number(t):String(e).localeCompare(String(t),void 0,{numeric:!0})}function l({data:l,columns:u,pageSize:d=10,searchable:f=!1,searchKeys:p,initialSort:m,rowKey:h=(e,t)=>t,emptyMessage:g,className:_,...v}){let[y,b]=(0,o.useState)(``),[x,S]=(0,o.useState)(m??null),{page:C,setPage:w}=t.usePagination(1,d),T=(0,o.useMemo)(()=>p&&p.length>0?p:u.filter(e=>{let t=l.find(t=>t[e.key]!=null),n=t?t[e.key]:void 0;return typeof n==`string`||typeof n==`number`}).map(e=>e.key),[p,u,l]),E=(0,o.useMemo)(()=>{let e=y.trim().toLowerCase();return!e||!f?l:l.filter(t=>T.some(n=>{let r=t[n];return r!=null&&String(r).toLowerCase().includes(e)}))},[l,y,f,T]),D=(0,o.useMemo)(()=>{if(!x)return E;let e=x.direction===`asc`?1:-1;return[...E].sort((t,n)=>c(t[x.key],n[x.key])*e)},[E,x]),O=Math.max(1,Math.ceil(D.length/d));(0,o.useEffect)(()=>{C>O&&w(O)},[C,O,w]);let k=Math.min(C,O),A=(0,o.useMemo)(()=>{let e=(k-1)*d;return D.slice(e,e+d)},[D,k,d]);function j(e){S(t=>!t||t.key!==e?{key:e,direction:`asc`}:t.direction===`asc`?{key:e,direction:`desc`}:null)}let M=(0,o.useMemo)(()=>u.map(e=>{let t=x?.key===e.key?x?.direction===`asc`?` ▲`:` ▼`:``,n=e.sortable?(0,s.jsxs)(`button`,{type:`button`,className:a.default.sortButton,onClick:()=>j(e.key),"aria-label":`Ordenar por ${typeof e.header==`string`?e.header:String(e.key)}`,children:[e.header,(0,s.jsx)(`span`,{className:a.default.sortIndicator,"aria-hidden":!0,children:t})]}):e.header;return{key:String(e.key),header:n,align:e.align,priority:e.priority,width:e.width,render:e.render?t=>e.render(t):t=>t[e.key]??null}}),[u,x]);return(0,s.jsxs)(`div`,{className:e.cn(a.default.wrapper,_),...v,children:[f&&(0,s.jsx)(r.SearchBar,{value:y,onChange:e=>{b(e),w(1)},wrapperClassName:a.default.search}),(0,s.jsx)(i.Table,{columns:M,data:A,rowKey:(e,t)=>h(e,(k-1)*d+t),emptyMessage:g}),O>1&&(0,s.jsx)(n.Pagination,{page:k,totalPages:O,onPageChange:w,totalItems:D.length})]})}exports.DataTable=l;
1
+ const e=require("../../utils/cn.cjs"),t=require("../../hooks/use-pagination.cjs"),n=require("../Pagination/Pagination.cjs"),r=require("../SearchBar/SearchBar.cjs"),i=require("../Table/Table.cjs"),a=require("../../utils/compare-values.cjs"),o=require("./DataTable.module.cjs");let s=require("react"),c=require("react/jsx-runtime");function l({data:l,columns:u,pageSize:d=10,searchable:f=!1,searchKeys:p,initialSort:m,rowKey:h=(e,t)=>t,emptyMessage:g,className:_,...v}){let[y,b]=(0,s.useState)(``),[x,S]=(0,s.useState)(m??null),{page:C,setPage:w}=t.usePagination(1,d),T=(0,s.useMemo)(()=>p&&p.length>0?p:u.filter(e=>{let t=l.find(t=>t[e.key]!=null),n=t?t[e.key]:void 0;return typeof n==`string`||typeof n==`number`}).map(e=>e.key),[p,u,l]),E=(0,s.useMemo)(()=>{let e=y.trim().toLowerCase();return!e||!f?l:l.filter(t=>T.some(n=>{let r=t[n];return r!=null&&String(r).toLowerCase().includes(e)}))},[l,y,f,T]),D=(0,s.useMemo)(()=>{if(!x)return E;let e=x.direction===`asc`?1:-1;return[...E].sort((t,n)=>a.compareValues(t[x.key],n[x.key])*e)},[E,x]),O=Math.max(1,Math.ceil(D.length/d));(0,s.useEffect)(()=>{C>O&&w(O)},[C,O,w]);let k=Math.min(C,O),A=(0,s.useMemo)(()=>{let e=(k-1)*d;return D.slice(e,e+d)},[D,k,d]);function j(e){S(t=>!t||t.key!==e?{key:e,direction:`asc`}:t.direction===`asc`?{key:e,direction:`desc`}:null)}let M=(0,s.useMemo)(()=>u.map(e=>{let t=x?.key===e.key?x?.direction===`asc`?` ▲`:` ▼`:``,n=e.sortable?(0,c.jsxs)(`button`,{type:`button`,className:o.default.sortButton,onClick:()=>j(e.key),"aria-label":`Ordenar por ${typeof e.header==`string`?e.header:String(e.key)}`,children:[e.header,(0,c.jsx)(`span`,{className:o.default.sortIndicator,"aria-hidden":!0,children:t})]}):e.header;return{key:String(e.key),header:n,align:e.align,priority:e.priority,width:e.width,render:e.render?t=>e.render(t):t=>t[e.key]??null}}),[u,x]);return(0,c.jsxs)(`div`,{className:e.cn(o.default.wrapper,_),...v,children:[f&&(0,c.jsx)(r.SearchBar,{value:y,onChange:e=>{b(e),w(1)},wrapperClassName:o.default.search}),(0,c.jsx)(i.Table,{columns:M,data:A,rowKey:(e,t)=>h(e,(k-1)*d+t),emptyMessage:g}),O>1&&(0,c.jsx)(n.Pagination,{page:k,totalPages:O,onPageChange:w,totalItems:D.length})]})}exports.DataTable=l;
2
2
  //# sourceMappingURL=DataTable.cjs.map