@uisatin/ui 0.0.0-stage → 0.1.0

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 (329) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +67 -2
  3. package/chunk-2HKBZFIK.js +339 -0
  4. package/chunk-2HKBZFIK.js.map +1 -0
  5. package/chunk-2YEWSJD7.js +95 -0
  6. package/chunk-2YEWSJD7.js.map +1 -0
  7. package/chunk-3DINAQLL.js +153 -0
  8. package/chunk-3DINAQLL.js.map +1 -0
  9. package/chunk-3ERGAHKA.js +179 -0
  10. package/chunk-3ERGAHKA.js.map +1 -0
  11. package/chunk-6C7CGJX2.js +86 -0
  12. package/chunk-6C7CGJX2.js.map +1 -0
  13. package/chunk-6YXUBP27.js +474 -0
  14. package/chunk-6YXUBP27.js.map +1 -0
  15. package/chunk-72OCFAPT.js +175 -0
  16. package/chunk-72OCFAPT.js.map +1 -0
  17. package/chunk-7TXHCRAG.js +51 -0
  18. package/chunk-7TXHCRAG.js.map +1 -0
  19. package/chunk-A5XEM3GF.js +260 -0
  20. package/chunk-A5XEM3GF.js.map +1 -0
  21. package/chunk-AN2RNJLR.js +79 -0
  22. package/chunk-AN2RNJLR.js.map +1 -0
  23. package/chunk-B2K45IUR.js +36 -0
  24. package/chunk-B2K45IUR.js.map +1 -0
  25. package/chunk-B6V4VSGH.js +82 -0
  26. package/chunk-B6V4VSGH.js.map +1 -0
  27. package/chunk-BKZBWPUH.js +59 -0
  28. package/chunk-BKZBWPUH.js.map +1 -0
  29. package/chunk-BNTPPZCK.js +220 -0
  30. package/chunk-BNTPPZCK.js.map +1 -0
  31. package/chunk-BNZVTLHV.js +98 -0
  32. package/chunk-BNZVTLHV.js.map +1 -0
  33. package/chunk-CL2II6BX.js +89 -0
  34. package/chunk-CL2II6BX.js.map +1 -0
  35. package/chunk-DETGJAIC.js +147 -0
  36. package/chunk-DETGJAIC.js.map +1 -0
  37. package/chunk-DO3TBNSC.js +130 -0
  38. package/chunk-DO3TBNSC.js.map +1 -0
  39. package/chunk-ESC5JCVQ.js +90 -0
  40. package/chunk-ESC5JCVQ.js.map +1 -0
  41. package/chunk-FDKIX4DC.js +55 -0
  42. package/chunk-FDKIX4DC.js.map +1 -0
  43. package/chunk-GICKPMJU.js +239 -0
  44. package/chunk-GICKPMJU.js.map +1 -0
  45. package/chunk-GQA24FYG.js +57 -0
  46. package/chunk-GQA24FYG.js.map +1 -0
  47. package/chunk-H4NU7NPO.js +264 -0
  48. package/chunk-H4NU7NPO.js.map +1 -0
  49. package/chunk-H63FYCMY.js +11 -0
  50. package/chunk-H63FYCMY.js.map +1 -0
  51. package/chunk-HL42K2NH.js +76 -0
  52. package/chunk-HL42K2NH.js.map +1 -0
  53. package/chunk-HNQUX43Z.js +109 -0
  54. package/chunk-HNQUX43Z.js.map +1 -0
  55. package/chunk-HRKPV66B.js +32 -0
  56. package/chunk-HRKPV66B.js.map +1 -0
  57. package/chunk-J3NSDRVH.js +83 -0
  58. package/chunk-J3NSDRVH.js.map +1 -0
  59. package/chunk-JPNWHWAQ.js +151 -0
  60. package/chunk-JPNWHWAQ.js.map +1 -0
  61. package/chunk-KNUWZNFV.js +240 -0
  62. package/chunk-KNUWZNFV.js.map +1 -0
  63. package/chunk-L6NCCSIP.js +213 -0
  64. package/chunk-L6NCCSIP.js.map +1 -0
  65. package/chunk-LLHTVIM3.js +50 -0
  66. package/chunk-LLHTVIM3.js.map +1 -0
  67. package/chunk-LW3TRU5W.js +131 -0
  68. package/chunk-LW3TRU5W.js.map +1 -0
  69. package/chunk-LZMKSFCF.js +79 -0
  70. package/chunk-LZMKSFCF.js.map +1 -0
  71. package/chunk-MR3JDI7E.js +122 -0
  72. package/chunk-MR3JDI7E.js.map +1 -0
  73. package/chunk-NN6PKJWS.js +44 -0
  74. package/chunk-NN6PKJWS.js.map +1 -0
  75. package/chunk-NSRTTYQ4.js +252 -0
  76. package/chunk-NSRTTYQ4.js.map +1 -0
  77. package/chunk-O7F3CIDV.js +45 -0
  78. package/chunk-O7F3CIDV.js.map +1 -0
  79. package/chunk-P4O6TGDR.js +166 -0
  80. package/chunk-P4O6TGDR.js.map +1 -0
  81. package/chunk-PF3YBUKT.js +81 -0
  82. package/chunk-PF3YBUKT.js.map +1 -0
  83. package/chunk-PVOLDO4G.js +47 -0
  84. package/chunk-PVOLDO4G.js.map +1 -0
  85. package/chunk-QS4N4MNP.js +215 -0
  86. package/chunk-QS4N4MNP.js.map +1 -0
  87. package/chunk-QUF2IPOZ.js +98 -0
  88. package/chunk-QUF2IPOZ.js.map +1 -0
  89. package/chunk-RTJ76X5U.js +126 -0
  90. package/chunk-RTJ76X5U.js.map +1 -0
  91. package/chunk-SH6NG6VN.js +40 -0
  92. package/chunk-SH6NG6VN.js.map +1 -0
  93. package/chunk-SHDFUXYV.js +195 -0
  94. package/chunk-SHDFUXYV.js.map +1 -0
  95. package/chunk-TKO44NDI.js +84 -0
  96. package/chunk-TKO44NDI.js.map +1 -0
  97. package/chunk-TOMSU4S4.js +149 -0
  98. package/chunk-TOMSU4S4.js.map +1 -0
  99. package/chunk-TVLGZGYN.js +59 -0
  100. package/chunk-TVLGZGYN.js.map +1 -0
  101. package/chunk-TX3H3QMA.js +520 -0
  102. package/chunk-TX3H3QMA.js.map +1 -0
  103. package/chunk-UBNHSZSE.js +88 -0
  104. package/chunk-UBNHSZSE.js.map +1 -0
  105. package/chunk-UC3ZJWOD.js +73 -0
  106. package/chunk-UC3ZJWOD.js.map +1 -0
  107. package/chunk-UGRSWP6U.js +69 -0
  108. package/chunk-UGRSWP6U.js.map +1 -0
  109. package/chunk-VAGIAREV.js +214 -0
  110. package/chunk-VAGIAREV.js.map +1 -0
  111. package/chunk-WTMZT3IT.js +105 -0
  112. package/chunk-WTMZT3IT.js.map +1 -0
  113. package/chunk-WX6BP5ZP.js +39 -0
  114. package/chunk-WX6BP5ZP.js.map +1 -0
  115. package/chunk-X3PQANZO.js +255 -0
  116. package/chunk-X3PQANZO.js.map +1 -0
  117. package/chunk-XILQ7GXE.js +66 -0
  118. package/chunk-XILQ7GXE.js.map +1 -0
  119. package/chunk-XL75JRUA.js +130 -0
  120. package/chunk-XL75JRUA.js.map +1 -0
  121. package/chunk-XZNHT6MO.js +348 -0
  122. package/chunk-XZNHT6MO.js.map +1 -0
  123. package/chunk-Y5AYU7PK.js +44 -0
  124. package/chunk-Y5AYU7PK.js.map +1 -0
  125. package/chunk-YG7JOOLJ.js +28 -0
  126. package/chunk-YG7JOOLJ.js.map +1 -0
  127. package/chunk-YPJYW624.js +195 -0
  128. package/chunk-YPJYW624.js.map +1 -0
  129. package/chunk-YPNKBE5X.js +104 -0
  130. package/chunk-YPNKBE5X.js.map +1 -0
  131. package/components/accordion.d.ts +27 -0
  132. package/components/accordion.js +4 -0
  133. package/components/accordion.js.map +1 -0
  134. package/components/agent-loading.d.ts +65 -0
  135. package/components/agent-loading.js +4 -0
  136. package/components/agent-loading.js.map +1 -0
  137. package/components/agent-picker.d.ts +82 -0
  138. package/components/agent-picker.js +5 -0
  139. package/components/agent-picker.js.map +1 -0
  140. package/components/agent-suggestion.d.ts +69 -0
  141. package/components/agent-suggestion.js +6 -0
  142. package/components/agent-suggestion.js.map +1 -0
  143. package/components/announcement-bar.d.ts +39 -0
  144. package/components/announcement-bar.js +6 -0
  145. package/components/announcement-bar.js.map +1 -0
  146. package/components/attachment.d.ts +73 -0
  147. package/components/attachment.js +5 -0
  148. package/components/attachment.js.map +1 -0
  149. package/components/avatar.d.ts +34 -0
  150. package/components/avatar.js +6 -0
  151. package/components/avatar.js.map +1 -0
  152. package/components/badge.d.ts +19 -0
  153. package/components/badge.js +4 -0
  154. package/components/badge.js.map +1 -0
  155. package/components/banner.d.ts +54 -0
  156. package/components/banner.js +5 -0
  157. package/components/banner.js.map +1 -0
  158. package/components/bar-chart.d.ts +32 -0
  159. package/components/bar-chart.js +11 -0
  160. package/components/bar-chart.js.map +1 -0
  161. package/components/breadcrumb.d.ts +32 -0
  162. package/components/breadcrumb.js +6 -0
  163. package/components/breadcrumb.js.map +1 -0
  164. package/components/button.d.ts +32 -0
  165. package/components/button.js +4 -0
  166. package/components/button.js.map +1 -0
  167. package/components/card.d.ts +28 -0
  168. package/components/card.js +5 -0
  169. package/components/card.js.map +1 -0
  170. package/components/channel-chart.d.ts +14 -0
  171. package/components/channel-chart.js +11 -0
  172. package/components/channel-chart.js.map +1 -0
  173. package/components/chart.d.ts +88 -0
  174. package/components/chart.js +10 -0
  175. package/components/chart.js.map +1 -0
  176. package/components/checkbox.d.ts +17 -0
  177. package/components/checkbox.js +4 -0
  178. package/components/checkbox.js.map +1 -0
  179. package/components/chip.d.ts +54 -0
  180. package/components/chip.js +4 -0
  181. package/components/chip.js.map +1 -0
  182. package/components/citations.d.ts +37 -0
  183. package/components/citations.js +5 -0
  184. package/components/citations.js.map +1 -0
  185. package/components/code-block.d.ts +13 -0
  186. package/components/code-block.js +5 -0
  187. package/components/code-block.js.map +1 -0
  188. package/components/combobox.d.ts +58 -0
  189. package/components/combobox.js +10 -0
  190. package/components/combobox.js.map +1 -0
  191. package/components/command-menu.d.ts +54 -0
  192. package/components/command-menu.js +6 -0
  193. package/components/command-menu.js.map +1 -0
  194. package/components/context-menu.d.ts +46 -0
  195. package/components/context-menu.js +6 -0
  196. package/components/context-menu.js.map +1 -0
  197. package/components/cookie-consent.d.ts +26 -0
  198. package/components/cookie-consent.js +5 -0
  199. package/components/cookie-consent.js.map +1 -0
  200. package/components/date-picker.d.ts +112 -0
  201. package/components/date-picker.js +9 -0
  202. package/components/date-picker.js.map +1 -0
  203. package/components/decision-card.d.ts +65 -0
  204. package/components/decision-card.js +7 -0
  205. package/components/decision-card.js.map +1 -0
  206. package/components/dialog.d.ts +68 -0
  207. package/components/dialog.js +5 -0
  208. package/components/dialog.js.map +1 -0
  209. package/components/donut-chart.d.ts +20 -0
  210. package/components/donut-chart.js +11 -0
  211. package/components/donut-chart.js.map +1 -0
  212. package/components/dot.d.ts +21 -0
  213. package/components/dot.js +4 -0
  214. package/components/dot.js.map +1 -0
  215. package/components/dropdown.d.ts +74 -0
  216. package/components/dropdown.js +5 -0
  217. package/components/dropdown.js.map +1 -0
  218. package/components/empty-state.d.ts +25 -0
  219. package/components/empty-state.js +4 -0
  220. package/components/empty-state.js.map +1 -0
  221. package/components/file-upload.d.ts +78 -0
  222. package/components/file-upload.js +7 -0
  223. package/components/file-upload.js.map +1 -0
  224. package/components/floating.d.ts +30 -0
  225. package/components/floating.js +3 -0
  226. package/components/floating.js.map +1 -0
  227. package/components/form-field.d.ts +35 -0
  228. package/components/form-field.js +4 -0
  229. package/components/form-field.js.map +1 -0
  230. package/components/hover-card.d.ts +30 -0
  231. package/components/hover-card.js +4 -0
  232. package/components/hover-card.js.map +1 -0
  233. package/components/input-otp.d.ts +49 -0
  234. package/components/input-otp.js +8 -0
  235. package/components/input-otp.js.map +1 -0
  236. package/components/input.d.ts +28 -0
  237. package/components/input.js +5 -0
  238. package/components/input.js.map +1 -0
  239. package/components/kbd.d.ts +37 -0
  240. package/components/kbd.js +4 -0
  241. package/components/kbd.js.map +1 -0
  242. package/components/line-chart.d.ts +29 -0
  243. package/components/line-chart.js +12 -0
  244. package/components/line-chart.js.map +1 -0
  245. package/components/message-actions.d.ts +24 -0
  246. package/components/message-actions.js +5 -0
  247. package/components/message-actions.js.map +1 -0
  248. package/components/message.d.ts +40 -0
  249. package/components/message.js +8 -0
  250. package/components/message.js.map +1 -0
  251. package/components/number-input.d.ts +33 -0
  252. package/components/number-input.js +8 -0
  253. package/components/number-input.js.map +1 -0
  254. package/components/pagination.d.ts +45 -0
  255. package/components/pagination.js +6 -0
  256. package/components/pagination.js.map +1 -0
  257. package/components/popover.d.ts +44 -0
  258. package/components/popover.js +4 -0
  259. package/components/popover.js.map +1 -0
  260. package/components/progress.d.ts +24 -0
  261. package/components/progress.js +4 -0
  262. package/components/progress.js.map +1 -0
  263. package/components/prompt-bar.d.ts +63 -0
  264. package/components/prompt-bar.js +6 -0
  265. package/components/prompt-bar.js.map +1 -0
  266. package/components/radio.d.ts +27 -0
  267. package/components/radio.js +4 -0
  268. package/components/radio.js.map +1 -0
  269. package/components/search-bar.d.ts +20 -0
  270. package/components/search-bar.js +5 -0
  271. package/components/search-bar.js.map +1 -0
  272. package/components/segmented-control.d.ts +32 -0
  273. package/components/segmented-control.js +5 -0
  274. package/components/segmented-control.js.map +1 -0
  275. package/components/select.d.ts +97 -0
  276. package/components/select.js +7 -0
  277. package/components/select.js.map +1 -0
  278. package/components/sheet.d.ts +37 -0
  279. package/components/sheet.js +6 -0
  280. package/components/sheet.js.map +1 -0
  281. package/components/sidebar.d.ts +52 -0
  282. package/components/sidebar.js +4 -0
  283. package/components/sidebar.js.map +1 -0
  284. package/components/skeleton.d.ts +18 -0
  285. package/components/skeleton.js +4 -0
  286. package/components/skeleton.js.map +1 -0
  287. package/components/slider.d.ts +57 -0
  288. package/components/slider.js +8 -0
  289. package/components/slider.js.map +1 -0
  290. package/components/sliding-indicator.d.ts +23 -0
  291. package/components/sliding-indicator.js +3 -0
  292. package/components/sliding-indicator.js.map +1 -0
  293. package/components/streaming-text.d.ts +33 -0
  294. package/components/streaming-text.js +4 -0
  295. package/components/streaming-text.js.map +1 -0
  296. package/components/switch.d.ts +15 -0
  297. package/components/switch.js +4 -0
  298. package/components/switch.js.map +1 -0
  299. package/components/table.d.ts +57 -0
  300. package/components/table.js +5 -0
  301. package/components/table.js.map +1 -0
  302. package/components/tabs.d.ts +45 -0
  303. package/components/tabs.js +5 -0
  304. package/components/tabs.js.map +1 -0
  305. package/components/textarea.d.ts +30 -0
  306. package/components/textarea.js +5 -0
  307. package/components/textarea.js.map +1 -0
  308. package/components/thinking.d.ts +64 -0
  309. package/components/thinking.js +5 -0
  310. package/components/thinking.js.map +1 -0
  311. package/components/toast.d.ts +71 -0
  312. package/components/toast.js +5 -0
  313. package/components/toast.js.map +1 -0
  314. package/components/toggle.d.ts +30 -0
  315. package/components/toggle.js +4 -0
  316. package/components/toggle.js.map +1 -0
  317. package/components/tooltip.d.ts +35 -0
  318. package/components/tooltip.js +5 -0
  319. package/components/tooltip.js.map +1 -0
  320. package/index.d.ts +64 -0
  321. package/index.js +230 -0
  322. package/index.js.map +1 -0
  323. package/lib/utils.d.ts +3 -0
  324. package/lib/utils.js +3 -0
  325. package/lib/utils.js.map +1 -0
  326. package/package.json +54 -4
  327. package/styles.css +2 -0
  328. package/theme/create-theme.d.ts +44 -0
  329. package/theme/index.d.ts +1 -0
@@ -0,0 +1,83 @@
1
+ import * as React from 'react';
2
+
3
+ // src/components/sliding-indicator.tsx
4
+ var SLIDE_MS = 300;
5
+ var TRAIL_MS = 30;
6
+ var STEPS = 24;
7
+ function easeOut(t) {
8
+ const [x1, y1, x2, y2] = [0.23, 1, 0.32, 1];
9
+ const bez = (a, b, s) => 3 * a * s * (1 - s) ** 2 + 3 * b * s ** 2 * (1 - s) + s ** 3;
10
+ let lo = 0;
11
+ let hi = 1;
12
+ for (let i = 0; i < 24; i++) {
13
+ const mid = (lo + hi) / 2;
14
+ if (bez(x1, x2, mid) < t) lo = mid;
15
+ else hi = mid;
16
+ }
17
+ return bez(y1, y2, (lo + hi) / 2);
18
+ }
19
+ function useSlidingIndicator(listRef, selector, selected, { animate: motionOn = true } = {}) {
20
+ const ref = React.useRef(null);
21
+ const [ready, setReady] = React.useState(false);
22
+ const placed = React.useRef(false);
23
+ const place = React.useCallback(
24
+ (animate) => {
25
+ const list = listRef.current;
26
+ const el = ref.current;
27
+ if (!list || !el) return;
28
+ const target = list.querySelector(selector);
29
+ if (!target) {
30
+ el.style.opacity = "0";
31
+ return;
32
+ }
33
+ let x = target.offsetLeft;
34
+ for (let p = target.offsetParent; p && p !== list; p = p.offsetParent) x += p.offsetLeft;
35
+ const w = target.offsetWidth;
36
+ const box = el.getBoundingClientRect();
37
+ const from = { x: box.left - list.getBoundingClientRect().left, w: box.width };
38
+ el.getAnimations().forEach((a) => a.cancel());
39
+ el.style.opacity = "1";
40
+ el.style.width = `${w}px`;
41
+ el.style.transform = `translateX(${x}px)`;
42
+ const reduced = typeof window !== "undefined" && window.matchMedia?.("(prefers-reduced-motion: reduce)").matches;
43
+ if (!animate || !motionOn || !placed.current || reduced || Math.abs(from.x - x) < 0.5 && Math.abs(from.w - w) < 0.5) {
44
+ placed.current = true;
45
+ return;
46
+ }
47
+ const right = x > from.x;
48
+ const L0 = from.x;
49
+ const R0 = from.x + from.w;
50
+ const L1 = x;
51
+ const R1 = x + w;
52
+ const lead = (t) => easeOut(t);
53
+ const trail = (t) => easeOut(Math.max(0, (t * SLIDE_MS - TRAIL_MS) / (SLIDE_MS - TRAIL_MS)));
54
+ const frames = [];
55
+ for (let i = 0; i <= STEPS; i++) {
56
+ const t = i / STEPS;
57
+ const pl = right ? trail(t) : lead(t);
58
+ const pr = right ? lead(t) : trail(t);
59
+ const l = L0 + (L1 - L0) * pl;
60
+ const r = R0 + (R1 - R0) * pr;
61
+ frames.push({ offset: t, transform: `translateX(${l}px)`, width: `${Math.max(r - l, 0)}px` });
62
+ }
63
+ el.animate(frames, { duration: SLIDE_MS, easing: "linear" });
64
+ },
65
+ [listRef, selector, motionOn]
66
+ );
67
+ React.useLayoutEffect(() => {
68
+ place(true);
69
+ setReady(true);
70
+ }, [place, selected]);
71
+ React.useEffect(() => {
72
+ const list = listRef.current;
73
+ if (!list) return;
74
+ const observer = new ResizeObserver(() => place(false));
75
+ observer.observe(list);
76
+ return () => observer.disconnect();
77
+ }, [listRef, place]);
78
+ return { ref, ready };
79
+ }
80
+
81
+ export { SLIDE_MS, useSlidingIndicator };
82
+ //# sourceMappingURL=chunk-J3NSDRVH.js.map
83
+ //# sourceMappingURL=chunk-J3NSDRVH.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/sliding-indicator.tsx"],"names":[],"mappings":"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","file":"chunk-J3NSDRVH.js","sourcesContent":["'use client'\n\nimport * as React from 'react'\n\n/**\n * The background (or underline) that slides under the selected item of Tabs, Segmented control\n * and Pagination, all on one motion: 300 ms on the kit's ease-out, the leading edge (the one\n * on the side it is heading to) moving straight away and the trailing edge 30 ms behind, so it\n * stretches a little towards the new item and settles there. A change mid-move carries on from\n * wherever it is. Only a change of selection animates; a resize just puts it in place. With\n * reduced motion it moves at once.\n *\n * `listRef` is the positioned element the items sit in, `selector` finds the selected item in\n * it, `selected` is whatever names the selection. Render the indicator absolutely at left 0 of\n * the list with the returned `ref`; until it is placed (`ready`), let the selected item paint\n * its own fill so the server render isn't bare.\n */\nexport const SLIDE_MS = 300\nconst TRAIL_MS = 30\nconst STEPS = 24\n\n/** cubic-bezier(0.23, 1, 0.32, 1), the kit's --ease-out, as a function of time 0..1. */\nfunction easeOut(t: number) {\n const [x1, y1, x2, y2] = [0.23, 1, 0.32, 1]\n const bez = (a: number, b: number, s: number) => 3 * a * s * (1 - s) ** 2 + 3 * b * s ** 2 * (1 - s) + s ** 3\n let lo = 0\n let hi = 1\n for (let i = 0; i < 24; i++) {\n const mid = (lo + hi) / 2\n if (bez(x1, x2, mid) < t) lo = mid\n else hi = mid\n }\n return bez(y1, y2, (lo + hi) / 2)\n}\n\nexport function useSlidingIndicator<T extends HTMLElement = HTMLSpanElement>(\n listRef: React.RefObject<HTMLElement | null>,\n selector: string,\n selected: unknown,\n /** Off: it moves to the new item at once. */\n { animate: motionOn = true }: { animate?: boolean } = {},\n) {\n const ref = React.useRef<T>(null)\n const [ready, setReady] = React.useState(false)\n const placed = React.useRef(false)\n\n const place = React.useCallback(\n (animate: boolean) => {\n const list = listRef.current\n const el = ref.current\n if (!list || !el) return\n const target = list.querySelector<HTMLElement>(selector)\n if (!target) {\n el.style.opacity = '0'\n return\n }\n // The target's place in the list, from layout (transforms on it don't count).\n let x = target.offsetLeft\n for (let p = target.offsetParent as HTMLElement | null; p && p !== list; p = p.offsetParent as HTMLElement | null) x += p.offsetLeft\n const w = target.offsetWidth\n // Where the indicator is on screen right now, mid-move included.\n const box = el.getBoundingClientRect()\n const from = { x: box.left - list.getBoundingClientRect().left, w: box.width }\n el.getAnimations().forEach((a) => a.cancel())\n el.style.opacity = '1'\n el.style.width = `${w}px`\n el.style.transform = `translateX(${x}px)`\n\n const reduced = typeof window !== 'undefined' && window.matchMedia?.('(prefers-reduced-motion: reduce)').matches\n if (!animate || !motionOn || !placed.current || reduced || (Math.abs(from.x - x) < 0.5 && Math.abs(from.w - w) < 0.5)) {\n placed.current = true\n return\n }\n // Two edges on their own clocks: the one leading the way goes first.\n const right = x > from.x\n const L0 = from.x\n const R0 = from.x + from.w\n const L1 = x\n const R1 = x + w\n const lead = (t: number) => easeOut(t)\n const trail = (t: number) => easeOut(Math.max(0, (t * SLIDE_MS - TRAIL_MS) / (SLIDE_MS - TRAIL_MS)))\n const frames: Keyframe[] = []\n for (let i = 0; i <= STEPS; i++) {\n const t = i / STEPS\n const pl = right ? trail(t) : lead(t)\n const pr = right ? lead(t) : trail(t)\n const l = L0 + (L1 - L0) * pl\n const r = R0 + (R1 - R0) * pr\n frames.push({ offset: t, transform: `translateX(${l}px)`, width: `${Math.max(r - l, 0)}px` })\n }\n el.animate(frames, { duration: SLIDE_MS, easing: 'linear' })\n },\n [listRef, selector, motionOn],\n )\n\n React.useLayoutEffect(() => {\n place(true)\n setReady(true)\n }, [place, selected])\n\n React.useEffect(() => {\n const list = listRef.current\n if (!list) return\n const observer = new ResizeObserver(() => place(false))\n observer.observe(list)\n return () => observer.disconnect()\n }, [listRef, place])\n\n return { ref, ready }\n}\n"]}
@@ -0,0 +1,151 @@
1
+ import { Button } from './chunk-HNQUX43Z.js';
2
+ import { cn } from './chunk-H63FYCMY.js';
3
+ import 'react';
4
+ import { Dialog as Dialog$1 } from 'radix-ui';
5
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
6
+
7
+ function CloseIcon() {
8
+ return /* @__PURE__ */ jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", "aria-hidden": true, children: /* @__PURE__ */ jsx(
9
+ "path",
10
+ {
11
+ d: "M4.50551 11.4951L8.0006 8M8.0006 8L11.4957 4.50491M8.0006 8L4.50551 4.50491M8.0006 8L11.4957 11.4951",
12
+ stroke: "currentColor",
13
+ strokeWidth: "1.25",
14
+ strokeLinecap: "round",
15
+ strokeLinejoin: "round"
16
+ }
17
+ ) });
18
+ }
19
+ function Dialog({
20
+ open,
21
+ defaultOpen,
22
+ onOpenChange,
23
+ trigger,
24
+ title,
25
+ description,
26
+ variant = "default",
27
+ alert,
28
+ footer,
29
+ footerStart,
30
+ children,
31
+ closeButton = true,
32
+ modal = true,
33
+ container,
34
+ onOpenAutoFocus,
35
+ className
36
+ }) {
37
+ const position = container ? "absolute" : "fixed";
38
+ const hasBody = children != null || alert != null;
39
+ return /* @__PURE__ */ jsxs(Dialog$1.Root, { open, defaultOpen, onOpenChange, modal, children: [
40
+ trigger && /* @__PURE__ */ jsx(Dialog$1.Trigger, { asChild: true, children: trigger }),
41
+ /* @__PURE__ */ jsxs(Dialog$1.Portal, { container, children: [
42
+ /* @__PURE__ */ jsx(
43
+ Dialog$1.Overlay,
44
+ {
45
+ className: cn(
46
+ position,
47
+ "inset-0 z-50 bg-black-a40"
48
+ )
49
+ }
50
+ ),
51
+ /* @__PURE__ */ jsx("div", { className: cn(position, "pointer-events-none inset-0 z-50 grid grid-cols-[minmax(0,1fr)] grid-rows-[minmax(0,1fr)] place-items-center p-4"), children: /* @__PURE__ */ jsxs(
52
+ Dialog$1.Content,
53
+ {
54
+ role: variant === "destructive" ? "alertdialog" : "dialog",
55
+ "data-slot": "dialog",
56
+ "data-variant": variant,
57
+ onOpenAutoFocus,
58
+ ...description ? {} : { "aria-describedby": void 0 },
59
+ className: cn(
60
+ "pointer-events-auto relative flex max-h-full w-[425px] max-w-[min(560px,100%)] min-w-[min(360px,100%)] flex-col overflow-clip",
61
+ "rounded-2xl border border-border-default bg-bg-popover",
62
+ "[box-shadow:0_4px_6px_-4px_var(--effect-shadow-sm),0_10px_15px_-3px_var(--effect-shadow-sm)]",
63
+ "focus-visible:outline-none",
64
+ "data-[state=open]:animate-[satin-enter_var(--duration-base)_var(--ease-out)]",
65
+ className
66
+ ),
67
+ children: [
68
+ /* @__PURE__ */ jsxs("div", { className: "flex min-h-0 flex-col gap-3", children: [
69
+ /* @__PURE__ */ jsxs("div", { className: "flex shrink-0 flex-col gap-0.5 px-4 pt-4 pb-2", children: [
70
+ /* @__PURE__ */ jsx(Dialog$1.Title, { className: "pr-8 text-base leading-[14px] font-medium tracking-normal text-text-default", children: title }),
71
+ description ? /* @__PURE__ */ jsx(Dialog$1.Description, { className: "text-body text-text-muted", children: description }) : null
72
+ ] }),
73
+ hasBody && /* @__PURE__ */ jsxs("div", { className: "flex min-h-0 flex-col gap-3 overflow-y-auto px-4 pb-3", children: [
74
+ typeof children === "string" ? /* @__PURE__ */ jsx("p", { className: "text-body text-text-default", children }) : children,
75
+ alert
76
+ ] })
77
+ ] }),
78
+ (footer || footerStart) && /* @__PURE__ */ jsxs("div", { className: "flex shrink-0 flex-wrap items-center justify-end gap-3 p-4", children: [
79
+ footerStart && /* @__PURE__ */ jsx("div", { className: "flex min-w-0 flex-1 items-center", children: footerStart }),
80
+ /* @__PURE__ */ jsx("div", { className: "flex shrink-0 items-center gap-2", children: footer })
81
+ ] }),
82
+ closeButton && /* @__PURE__ */ jsx(Dialog$1.Close, { asChild: true, children: /* @__PURE__ */ jsx(
83
+ Button,
84
+ {
85
+ variant: "tertiary",
86
+ size: "xl",
87
+ iconOnly: true,
88
+ "aria-label": "Close",
89
+ className: "absolute top-[7px] right-[7px] rounded-lg",
90
+ children: /* @__PURE__ */ jsx(CloseIcon, {})
91
+ }
92
+ ) })
93
+ ]
94
+ }
95
+ ) })
96
+ ] })
97
+ ] });
98
+ }
99
+ var DialogClose = Dialog$1.Close;
100
+ var alertTint = {
101
+ info: "bg-status-sky-bg text-status-sky-fg",
102
+ success: "bg-status-green-bg text-status-green-fg",
103
+ warning: "bg-status-amber-bg text-status-amber-fg",
104
+ error: "bg-status-red-bg text-status-red-fg"
105
+ };
106
+ function StatusIcon({ intent }) {
107
+ return /* @__PURE__ */ jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", "aria-hidden": true, className: "shrink-0", children: /* @__PURE__ */ jsx("g", { stroke: "currentColor", style: { strokeWidth: 1.25 }, strokeLinecap: "round", strokeLinejoin: "round", children: intent === "success" ? /* @__PURE__ */ jsx("path", { d: "M3.33333 8.66667L6 11.3333L12.6667 4.66667" }) : /* @__PURE__ */ jsxs(Fragment, { children: [
108
+ intent === "info" ? /* @__PURE__ */ jsxs(Fragment, { children: [
109
+ /* @__PURE__ */ jsx("path", { d: "M8 7.70494V10.6557" }),
110
+ /* @__PURE__ */ jsx("path", { d: "M8 5.35022L8.0059 5.34366" })
111
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
112
+ /* @__PURE__ */ jsx("path", { d: "M8 5.34428V8.29506" }),
113
+ /* @__PURE__ */ jsx("path", { d: "M8 10.6563L8.0059 10.6498" })
114
+ ] }),
115
+ /* @__PURE__ */ jsx("path", { d: "M8 13.9015C11.2593 13.9015 13.9015 11.2593 13.9015 8C13.9015 4.74066 11.2593 2.09845 8 2.09845C4.74066 2.09845 2.09845 4.74066 2.09845 8C2.09845 11.2593 4.74066 13.9015 8 13.9015Z" })
116
+ ] }) }) });
117
+ }
118
+ function DialogAlert({
119
+ intent = "info",
120
+ title,
121
+ description,
122
+ icon = true,
123
+ onDismiss,
124
+ className,
125
+ ...props
126
+ }) {
127
+ return /* @__PURE__ */ jsxs(
128
+ "div",
129
+ {
130
+ role: intent === "error" || intent === "warning" ? "alert" : "status",
131
+ "data-slot": "dialog-alert",
132
+ "data-intent": intent,
133
+ className: cn("flex w-full items-center gap-3 rounded-xl px-4 py-3", alertTint[intent], className),
134
+ ...props,
135
+ children: [
136
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 items-center gap-2.5", children: [
137
+ icon && /* @__PURE__ */ jsx(StatusIcon, { intent }),
138
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 flex-col", children: [
139
+ /* @__PURE__ */ jsx("p", { className: "text-base font-medium text-text-default", children: title }),
140
+ description ? /* @__PURE__ */ jsx("p", { className: "text-body text-text-muted", children: description }) : null
141
+ ] })
142
+ ] }),
143
+ onDismiss && /* @__PURE__ */ jsx(Button, { variant: "tertiary", iconOnly: true, "aria-label": "Dismiss", onClick: onDismiss, children: /* @__PURE__ */ jsx(CloseIcon, {}) })
144
+ ]
145
+ }
146
+ );
147
+ }
148
+
149
+ export { CloseIcon, Dialog, DialogAlert, DialogClose, StatusIcon };
150
+ //# sourceMappingURL=chunk-JPNWHWAQ.js.map
151
+ //# sourceMappingURL=chunk-JPNWHWAQ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/dialog.tsx"],"names":["DialogPrimitive"],"mappings":"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","file":"chunk-JPNWHWAQ.js","sourcesContent":["'use client'\n\nimport * as React from 'react'\nimport { Dialog as DialogPrimitive } from 'radix-ui'\nimport { cn } from '../lib/utils'\nimport { Button } from './button'\n\n/**\n * Dialog, 1:1 with the uiKit `Dialog` set (465:66873): variant default or destructive.\n * A 425px popover surface (min 360, max 560, never wider or taller than the screen less 16px a side; the body scrolls), radius-2xl, border/default, the shadcn `lg`\n * shadow. Header 16 / 16 / 8 with a 14px medium title and a 13px muted description, a body\n * 16px in with a 12px gap (body text, any content, an optional `DialogAlert`), then a\n * right-aligned footer with 16px padding on both variants, on the same inset as the header and\n * body. The close button is a 36px tertiary icon button 8px from the top-right corner.\n *\n * Built on the Radix Dialog. The destructive variant keeps the same primitive but takes\n * `role=\"alertdialog\"`, so screen readers announce it as an alert. Pass `container` to portal\n * it into a positioned element (the docs stage) instead of the page; the dialog then centres\n * inside that element rather than the viewport.\n */\nexport type DialogVariant = 'default' | 'destructive'\n\nexport interface DialogProps {\n open?: boolean\n defaultOpen?: boolean\n onOpenChange?: (open: boolean) => void\n /** Optional element that opens the dialog (rendered with `asChild`, so pass a Button). */\n trigger?: React.ReactNode\n title: React.ReactNode\n description?: React.ReactNode\n variant?: DialogVariant\n /** A `DialogAlert` shown under the body, for a side effect the user must know about. */\n alert?: React.ReactNode\n /** The actions on the right of the footer. Leave it out for a read-only dialog. */\n footer?: React.ReactNode\n /** Figma's `footerSlot`: content on the left of the footer, e.g. a \"Don't ask again\" checkbox. */\n footerStart?: React.ReactNode\n /** Body. A string renders as body text (13px, text/default). */\n children?: React.ReactNode\n /** Show the close button in the corner. */\n closeButton?: boolean\n /** Modal blocks the page and shows the overlay; turn it off for inline previews. */\n modal?: boolean\n /** Portal target. Defaults to `document.body`. */\n container?: HTMLElement | null\n /** Called when focus moves into the dialog on open; `preventDefault()` keeps it where it was. */\n onOpenAutoFocus?: (event: Event) => void\n className?: string\n}\n\n/** The X from Figma's close button (`icon` 16px, 1.25 stroke). */\nexport function CloseIcon() {\n return (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" aria-hidden>\n <path\n d=\"M4.50551 11.4951L8.0006 8M8.0006 8L11.4957 4.50491M8.0006 8L4.50551 4.50491M8.0006 8L11.4957 11.4951\"\n stroke=\"currentColor\"\n strokeWidth=\"1.25\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n )\n}\n\nexport function Dialog({\n open,\n defaultOpen,\n onOpenChange,\n trigger,\n title,\n description,\n variant = 'default',\n alert,\n footer,\n footerStart,\n children,\n closeButton = true,\n modal = true,\n container,\n onOpenAutoFocus,\n className,\n}: DialogProps) {\n // Inside a container the dialog is laid out against it, not the viewport.\n const position = container ? 'absolute' : 'fixed'\n const hasBody = children != null || alert != null\n\n return (\n <DialogPrimitive.Root open={open} defaultOpen={defaultOpen} onOpenChange={onOpenChange} modal={modal}>\n {trigger && <DialogPrimitive.Trigger asChild>{trigger}</DialogPrimitive.Trigger>}\n <DialogPrimitive.Portal container={container}>\n {/* No overlay in Figma; black-a40 is the kit's scrim colour. Only drawn when modal. */}\n <DialogPrimitive.Overlay\n className={cn(\n position,\n 'inset-0 z-50 bg-black-a40',\n )}\n />\n <div className={cn(position, 'pointer-events-none inset-0 z-50 grid grid-cols-[minmax(0,1fr)] grid-rows-[minmax(0,1fr)] place-items-center p-4')}>\n <DialogPrimitive.Content\n role={variant === 'destructive' ? 'alertdialog' : 'dialog'}\n data-slot=\"dialog\"\n data-variant={variant}\n onOpenAutoFocus={onOpenAutoFocus}\n // No description: tell Radix on purpose, so it doesn't warn.\n {...(description ? {} : { 'aria-describedby': undefined })}\n className={cn(\n 'pointer-events-auto relative flex max-h-full w-[425px] max-w-[min(560px,100%)] min-w-[min(360px,100%)] flex-col overflow-clip',\n 'rounded-2xl border border-border-default bg-bg-popover',\n '[box-shadow:0_4px_6px_-4px_var(--effect-shadow-sm),0_10px_15px_-3px_var(--effect-shadow-sm)]',\n 'focus-visible:outline-none',\n 'data-[state=open]:animate-[satin-enter_var(--duration-base)_var(--ease-out)]',\n className,\n )}\n >\n <div className=\"flex min-h-0 flex-col gap-3\">\n <div className=\"flex shrink-0 flex-col gap-0.5 px-4 pt-4 pb-2\">\n <DialogPrimitive.Title className=\"pr-8 text-base leading-[14px] font-medium tracking-normal text-text-default\">\n {title}\n </DialogPrimitive.Title>\n {description ? (\n <DialogPrimitive.Description className=\"text-body text-text-muted\">{description}</DialogPrimitive.Description>\n ) : null}\n </div>\n {hasBody && (\n <div className=\"flex min-h-0 flex-col gap-3 overflow-y-auto px-4 pb-3\">\n {typeof children === 'string' ? <p className=\"text-body text-text-default\">{children}</p> : children}\n {alert}\n </div>\n )}\n </div>\n\n {(footer || footerStart) && (\n <div className=\"flex shrink-0 flex-wrap items-center justify-end gap-3 p-4\">\n {footerStart && <div className=\"flex min-w-0 flex-1 items-center\">{footerStart}</div>}\n <div className=\"flex shrink-0 items-center gap-2\">{footer}</div>\n </div>\n )}\n\n {closeButton && (\n <DialogPrimitive.Close asChild>\n <Button\n variant=\"tertiary\"\n size=\"xl\"\n iconOnly\n aria-label=\"Close\"\n className=\"absolute top-[7px] right-[7px] rounded-lg\"\n >\n <CloseIcon />\n </Button>\n </DialogPrimitive.Close>\n )}\n </DialogPrimitive.Content>\n </div>\n </DialogPrimitive.Portal>\n </DialogPrimitive.Root>\n )\n}\n\n/** Wrap a footer button in this to make it close the dialog: `<DialogClose asChild><Button…/></DialogClose>`. */\nexport const DialogClose = DialogPrimitive.Close\n\n/* ------------------------------------------------------------------------------------------ */\n\nexport type DialogAlertIntent = 'info' | 'success' | 'warning' | 'error'\n\n/**\n * Alert, 1:1 with the uiKit `Alert` set (475:27871): the inline notice inside a dialog.\n * radius-xl, 16 / 12 padding, the intent's status bg, a 16px status icon in the intent's\n * status fg, a 14px medium title over a 13px muted description, and an optional 28px close.\n */\nexport interface DialogAlertProps extends Omit<React.ComponentProps<'div'>, 'title'> {\n intent?: DialogAlertIntent\n title: React.ReactNode\n description?: React.ReactNode\n /** Hide the status icon. */\n icon?: boolean\n /** Shows the close button; called when it is pressed. */\n onDismiss?: () => void\n}\n\nconst alertTint: Record<DialogAlertIntent, string> = {\n info: 'bg-status-sky-bg text-status-sky-fg',\n success: 'bg-status-green-bg text-status-green-fg',\n warning: 'bg-status-amber-bg text-status-amber-fg',\n error: 'bg-status-red-bg text-status-red-fg',\n}\n\n/** The four status glyphs from Figma (16px, 1.25 stroke), drawn in currentColor. */\nexport function StatusIcon({ intent }: { intent: DialogAlertIntent }) {\n return (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" aria-hidden className=\"shrink-0\">\n <g stroke=\"currentColor\" style={{ strokeWidth: 1.25 }} strokeLinecap=\"round\" strokeLinejoin=\"round\">\n {intent === 'success' ? (\n <path d=\"M3.33333 8.66667L6 11.3333L12.6667 4.66667\" />\n ) : (\n <>\n {intent === 'info' ? (\n <>\n <path d=\"M8 7.70494V10.6557\" />\n <path d=\"M8 5.35022L8.0059 5.34366\" />\n </>\n ) : (\n <>\n <path d=\"M8 5.34428V8.29506\" />\n <path d=\"M8 10.6563L8.0059 10.6498\" />\n </>\n )}\n <path d=\"M8 13.9015C11.2593 13.9015 13.9015 11.2593 13.9015 8C13.9015 4.74066 11.2593 2.09845 8 2.09845C4.74066 2.09845 2.09845 4.74066 2.09845 8C2.09845 11.2593 4.74066 13.9015 8 13.9015Z\" />\n </>\n )}\n </g>\n </svg>\n )\n}\n\nexport function DialogAlert({\n intent = 'info',\n title,\n description,\n icon = true,\n onDismiss,\n className,\n ...props\n}: DialogAlertProps) {\n return (\n <div\n role={intent === 'error' || intent === 'warning' ? 'alert' : 'status'}\n data-slot=\"dialog-alert\"\n data-intent={intent}\n className={cn('flex w-full items-center gap-3 rounded-xl px-4 py-3', alertTint[intent], className)}\n {...props}\n >\n <div className=\"flex min-w-0 flex-1 items-center gap-2.5\">\n {icon && <StatusIcon intent={intent} />}\n <div className=\"flex min-w-0 flex-1 flex-col\">\n <p className=\"text-base font-medium text-text-default\">{title}</p>\n {description ? <p className=\"text-body text-text-muted\">{description}</p> : null}\n </div>\n </div>\n {onDismiss && (\n <Button variant=\"tertiary\" iconOnly aria-label=\"Dismiss\" onClick={onDismiss}>\n <CloseIcon />\n </Button>\n )}\n </div>\n )\n}\n"]}
@@ -0,0 +1,240 @@
1
+ import { cn } from './chunk-H63FYCMY.js';
2
+ import * as React from 'react';
3
+ import { jsx, jsxs } from 'react/jsx-runtime';
4
+
5
+ var COLS = 5;
6
+ var ROWS = 4;
7
+ var PIECES = {
8
+ I: [[0, 0], [0, 1], [0, 2], [0, 3]],
9
+ "I|": [[0, 0], [1, 0], [2, 0], [3, 0]],
10
+ O: [[0, 0], [0, 1], [1, 0], [1, 1]],
11
+ T: [[0, 1], [1, 0], [1, 1], [1, 2]],
12
+ "T\u2193": [[0, 0], [0, 1], [0, 2], [1, 1]],
13
+ "T>": [[0, 0], [1, 0], [1, 1], [2, 0]],
14
+ "T<": [[0, 1], [1, 0], [1, 1], [2, 1]],
15
+ S: [[0, 1], [0, 2], [1, 0], [1, 1]],
16
+ "S|": [[0, 0], [1, 0], [1, 1], [2, 1]],
17
+ Z: [[0, 0], [0, 1], [1, 1], [1, 2]],
18
+ "Z|": [[0, 1], [1, 0], [1, 1], [2, 0]],
19
+ L: [[0, 2], [1, 0], [1, 1], [1, 2]],
20
+ "L|": [[0, 0], [1, 0], [2, 0], [2, 1]],
21
+ "L\u2193": [[0, 0], [0, 1], [0, 2], [1, 0]],
22
+ "L|\u2193": [[0, 0], [0, 1], [1, 1], [2, 1]],
23
+ J: [[0, 0], [1, 0], [1, 1], [1, 2]],
24
+ "J|": [[0, 1], [1, 1], [2, 0], [2, 1]],
25
+ "J\u2193": [[0, 0], [0, 1], [0, 2], [1, 2]],
26
+ "J|\u2193": [[0, 0], [0, 1], [1, 0], [2, 0]]
27
+ };
28
+ var ROUNDS = [
29
+ // after Figma's frames: two Js build a tower in the middle, then the sides fill in and it goes in two clears
30
+ [["J|", 1], ["J|\u2193", 1], ["O", 3], ["I|", 0], ["O", 3]],
31
+ // a square and an L lay the floor; four clears, nearly a row a piece
32
+ [["O", 0], ["L", 2], ["S", 2], ["J", 0], ["I", 1]],
33
+ // a T to start, a J standing in the corner, a bar laid over both
34
+ [["T", 0], ["J|", 3], ["I", 0], ["S", 2], ["L\u2193", 0]],
35
+ // a pillar on the left, an L and a J lean on it, a Z and a T close it
36
+ [["I|", 0], ["L|", 1], ["J\u2193", 2], ["Z|", 3], ["T\u2193", 1]],
37
+ // a bar off the left edge, a Z tucked under the gap, a square in the corner
38
+ [["I", 1], ["Z|", 0], ["J", 2], ["O", 3], ["L\u2193", 0]]
39
+ ];
40
+ var T = {
41
+ step: 90,
42
+ glide: 60,
43
+ /** after a piece rests, before a clear or the next piece */
44
+ lock: 220,
45
+ /** a cleared row fading back to the board */
46
+ clear: 260,
47
+ /** what was above it coming down */
48
+ settle: 200,
49
+ /** between the settle and the next piece */
50
+ next: 80,
51
+ /** the empty board between rounds */
52
+ rest: 150,
53
+ /** the glimm band crossing the mark at a clear, and its fade once across */
54
+ sweep: 520,
55
+ outro: 120,
56
+ /** how long before the fade the band sets off */
57
+ lead: 120,
58
+ /** the same band in the label: it picks up after the mark, a little slower across the longer text */
59
+ textDelay: 220,
60
+ textSweep: 600
61
+ };
62
+ var PITCH = 3;
63
+ function playRounds(rounds = ROUNDS) {
64
+ const falls2 = [];
65
+ const clears2 = [];
66
+ let t = 0;
67
+ for (const [n, round] of rounds.entries()) {
68
+ const board = [];
69
+ for (const [name, x] of round) {
70
+ const shape = PIECES[name];
71
+ const height = Math.max(...shape.map(([r]) => r)) + 1;
72
+ const taken = (r, c) => r >= ROWS || board.some((b) => b.row === r && b.dot.col === c);
73
+ let top = -height;
74
+ while (!shape.some(([r, c]) => taken(top + 1 + r, x + c))) top++;
75
+ if (shape.some(([r]) => top + r < 0)) throw new Error(`tetris round ${n + 1}: ${name} at ${x} sticks out of the top`);
76
+ if (shape.some(([, c]) => x + c >= COLS)) throw new Error(`tetris round ${n + 1}: ${name} at ${x} sticks out of the side`);
77
+ const rows = top + height;
78
+ const fall = { keys: [{ t: 0, y: -rows * PITCH }], dots: [] };
79
+ for (let s = 0; s < rows; s++) {
80
+ fall.keys.push({ t, y: (s - rows) * PITCH, glide: true }, { t: t + T.glide, y: (s + 1 - rows) * PITCH });
81
+ t += T.step;
82
+ }
83
+ for (const [r, c] of shape) {
84
+ const dot = { col: x + c, row: top + r, keys: [{ t: 0, y: 0, on: 1 }] };
85
+ fall.dots.push(dot);
86
+ board.push({ dot, row: dot.row });
87
+ }
88
+ falls2.push(fall);
89
+ t += T.lock;
90
+ const full = [...new Set(board.map((b) => b.row))].filter((r) => board.filter((b) => b.row === r).length === COLS);
91
+ if (!full.length) continue;
92
+ clears2.push(t);
93
+ for (const { dot, row } of board.filter((b) => full.includes(b.row))) {
94
+ const y = (row - dot.row) * PITCH;
95
+ dot.keys.push({ t, y, on: 1, glide: true }, { t: t + T.clear, y, on: 0 });
96
+ }
97
+ t += T.clear;
98
+ const kept = board.filter((b) => !full.includes(b.row));
99
+ for (const b of kept) {
100
+ const down = full.filter((r) => r > b.row).length;
101
+ if (!down) continue;
102
+ b.dot.keys.push({ t, y: (b.row - b.dot.row) * PITCH, on: 1, glide: true }, { t: t + T.settle, y: (b.row + down - b.dot.row) * PITCH, on: 1 });
103
+ b.row += down;
104
+ }
105
+ board.splice(0, board.length, ...kept);
106
+ t += T.settle + T.next;
107
+ }
108
+ if (board.length) throw new Error(`tetris round ${n + 1} leaves ${board.length} dots on the board`);
109
+ t += T.rest;
110
+ }
111
+ return { falls: falls2, clears: clears2, loop: t };
112
+ }
113
+ var GLIDE = "animation-timing-function:cubic-bezier(0.22,1,0.36,1)";
114
+ var FADE = "animation-timing-function:ease";
115
+ function keyframes(falls2, clears2, loop2) {
116
+ const pct = (t) => `${+(t / loop2 * 100).toFixed(3)}%`;
117
+ const block = (name, keys) => {
118
+ const all = [...keys, { ...keys[keys.length - 1], t: loop2, glide: false }];
119
+ const frames = all.map((k) => {
120
+ const props = [`transform:translateY(${k.y}px)`];
121
+ if (k.on !== void 0) props.push(`opacity:${k.on}`);
122
+ if (k.glide) props.push(k.on === 1 && keys[keys.indexOf(k) + 1]?.on === 0 ? FADE : GLIDE);
123
+ return `${pct(k.t)}{${props.join(";")}}`;
124
+ });
125
+ return `@keyframes ${name}{${frames.join("")}}`;
126
+ };
127
+ let d = 0;
128
+ const pieces = falls2.flatMap((f, i) => [block(`satin-tetris-f${i}`, f.keys), ...f.dots.map((dot) => block(`satin-tetris-d${d++}`, dot.keys))]);
129
+ const band = (x, on, ease = "") => `{transform:translateX(${x}%);opacity:${on}${ease}}`;
130
+ const sweeps = clears2.map((c) => c - T.lead).flatMap((t) => [
131
+ `${pct(t - 1)}${band(-100, 0)}`,
132
+ `${pct(t)}${band(-100, 1, ";animation-timing-function:ease")}`,
133
+ `${pct(t + T.sweep)}${band(100, 1, ";animation-timing-function:ease-out")}`,
134
+ `${pct(t + T.sweep + T.outro)}${band(100, 0)}`
135
+ ]);
136
+ const glimm = `@keyframes satin-tetris-glimm{0%${band(-100, 0)}${sweeps.join("")}100%${band(100, 0)}}`;
137
+ const text = (x, on, ease = "") => `{background-position:${x}% 0;opacity:${on}${ease}}`;
138
+ const textSweeps = clears2.map((c) => c - T.lead + T.textDelay).flatMap((t) => [
139
+ `${pct(t - 1)}${text(100, 0)}`,
140
+ `${pct(t)}${text(100, 1, ";animation-timing-function:ease")}`,
141
+ `${pct(t + T.textSweep)}${text(0, 1, ";animation-timing-function:ease-out")}`,
142
+ `${pct(t + T.textSweep + T.outro)}${text(0, 0)}`
143
+ ]);
144
+ const glimmText = `@keyframes satin-tetris-glimm-text{0%${text(100, 0)}${textSweeps.join("")}100%${text(0, 0)}}`;
145
+ return [...pieces, glimm, glimmText].join("\n");
146
+ }
147
+ var { falls, clears, loop } = playRounds();
148
+ var css;
149
+ function useTetrisKeyframes() {
150
+ React.useInsertionEffect(() => {
151
+ if (document.querySelector("style[data-satin-tetris]")) return;
152
+ const style = document.createElement("style");
153
+ style.dataset.satinTetris = "";
154
+ style.textContent = css ??= keyframes(falls, clears, loop);
155
+ document.head.append(style);
156
+ }, []);
157
+ }
158
+ var GRID = Array.from({ length: COLS * ROWS }, (_, i) => i);
159
+ var TetrisMark = React.memo(function TetrisMark2({ glimmer = "color", className }) {
160
+ useTetrisKeyframes();
161
+ let d = 0;
162
+ return /* @__PURE__ */ jsx(
163
+ "span",
164
+ {
165
+ "aria-hidden": true,
166
+ "data-glimmer": glimmer,
167
+ className: cn("satin-tetris", className),
168
+ style: { "--satin-tetris-loop": `${loop}ms` },
169
+ children: /* @__PURE__ */ jsxs("span", { className: "satin-tetris__board", children: [
170
+ GRID.map((i) => /* @__PURE__ */ jsx("i", {}, i)),
171
+ falls.map((fall, f) => /* @__PURE__ */ jsx("span", { className: "satin-tetris__fall", style: { animationName: `satin-tetris-f${f}` }, children: fall.dots.map((dot) => /* @__PURE__ */ jsx("b", { style: { "--c": dot.col, "--r": dot.row, animationName: `satin-tetris-d${d++}` } }, d)) }, f)),
172
+ /* @__PURE__ */ jsx("span", { className: "satin-tetris__glimm", children: /* @__PURE__ */ jsx("span", {}) })
173
+ ] })
174
+ }
175
+ );
176
+ });
177
+ function TetrisStill({ className }) {
178
+ return /* @__PURE__ */ jsx("span", { "aria-hidden": true, "data-still": true, className: cn("satin-tetris", className), children: /* @__PURE__ */ jsx("span", { className: "satin-tetris__board", children: GRID.map((i) => /* @__PURE__ */ jsx("i", {}, i)) }) });
179
+ }
180
+ function TetrisLabel({ children, glimmer = "color", className }) {
181
+ useTetrisKeyframes();
182
+ return /* @__PURE__ */ jsxs(
183
+ "span",
184
+ {
185
+ "data-glimmer": glimmer,
186
+ className: cn("satin-tetris-label", className),
187
+ style: { "--satin-tetris-loop": `${loop}ms` },
188
+ children: [
189
+ /* @__PURE__ */ jsx("span", { className: "satin-shimmer", children }),
190
+ /* @__PURE__ */ jsx("span", { className: "satin-tetris-label__glimm", "aria-hidden": true, children: /* @__PURE__ */ jsx("span", { children }) })
191
+ ]
192
+ }
193
+ );
194
+ }
195
+ function ShimmerLabel({ children, className }) {
196
+ return /* @__PURE__ */ jsx("span", { className: cn("satin-shimmer", className), children });
197
+ }
198
+ var FLAPPY = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAAMCAYAAACEJVa/AAAAa0lEQVR42mNgwA3+48BEAbDi/6+tUTBYDAiIMQhDM7IBMIDLIBQn4zMEl4v+owN0g8gyhJBB2A0BKUQGME1YvIUvtjBtxRYu4UoYGMMgeMAeZEfBMANISTcYhiAbhM8V2BMcFoPIScFEaQAAya9DoeSCRfcAAAAASUVORK5CYII=";
199
+ var FLAPPY_PARTS = [
200
+ { role: "body", rect: [0, 17, 3, 0] },
201
+ { role: "body", rect: [3, 17, 8, 6] },
202
+ { role: "body", rect: [8, 17, 12, 0] },
203
+ { role: "wing", rect: [3, 6, 8, 0] }
204
+ ];
205
+ function FlappySprite({ size = 16, run = true, className }) {
206
+ const cols = 17;
207
+ const rows = 12;
208
+ return /* @__PURE__ */ jsx(
209
+ "span",
210
+ {
211
+ "aria-hidden": true,
212
+ "data-run": run || void 0,
213
+ className: cn("satin-sprite", className),
214
+ style: { width: size, height: size, "--rows": rows, "--cols": cols },
215
+ children: /* @__PURE__ */ jsx("span", { className: "satin-sprite__frame", style: { width: size, height: size * rows / cols }, children: FLAPPY_PARTS.map(({ role, rect: [t, r, b, l] }, i) => /* @__PURE__ */ jsx(
216
+ "img",
217
+ {
218
+ src: FLAPPY,
219
+ alt: "",
220
+ draggable: false,
221
+ "data-role": role,
222
+ className: "satin-sprite__part",
223
+ style: { clipPath: `inset(${t / rows * 100}% ${(1 - r / cols) * 100}% ${(1 - b / rows) * 100}% ${l / cols * 100}%)` }
224
+ },
225
+ i
226
+ )) })
227
+ }
228
+ );
229
+ }
230
+ function AgentLoading({ label = "Thinking\u2026", time, indicator = "tetris", glimmer = "color", className, ...props }) {
231
+ return /* @__PURE__ */ jsxs("div", { "data-slot": "agent-loading", role: "status", "aria-live": "polite", className: cn("flex items-center gap-3 p-1", className), ...props, children: [
232
+ indicator === "flappy" ? /* @__PURE__ */ jsx(FlappySprite, { size: 16 }) : /* @__PURE__ */ jsx(TetrisMark, { glimmer }),
233
+ indicator === "tetris" ? /* @__PURE__ */ jsx(TetrisLabel, { glimmer, className: "text-sm whitespace-nowrap", children: label }) : /* @__PURE__ */ jsx(ShimmerLabel, { className: "text-sm whitespace-nowrap", children: label }),
234
+ time != null && /* @__PURE__ */ jsx("span", { className: "text-sm whitespace-nowrap text-text-subtle tabular-nums", children: time })
235
+ ] });
236
+ }
237
+
238
+ export { AgentLoading, FlappySprite, ShimmerLabel, TetrisLabel, TetrisMark, TetrisStill };
239
+ //# sourceMappingURL=chunk-KNUWZNFV.js.map
240
+ //# sourceMappingURL=chunk-KNUWZNFV.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/agent-loading.tsx"],"names":["falls","clears","loop","TetrisMark"],"mappings":"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","file":"chunk-KNUWZNFV.js","sourcesContent":["'use client'\n\nimport * as React from 'react'\nimport { cn } from '../lib/utils'\n\n/**\n * Loading: what shows while an agent works, before there is anything to read. The mark playing,\n * the label shimmering and the elapsed time in text/subtle, 12px apart, 4px padding (28 high),\n * announced politely to screen readers. Once the agent is done, Thinking takes its place.\n *\n * The agent's loading marks, from the uiKit: the tetris mark (uiKit \"tetris\", 3215:81593, beside\n * its label in thinkingHeader, 3215:81620) and the flappy bird (flappyAgent, 1215:69019).\n *\n * Tetris: the kit's grid of 5 × 4 dots on a 16px mark, tetrominoes falling into it a row at a\n * time. A full row fades back to the grid and what stood on it comes down; five rounds of five\n * pieces, each ending on an empty board. As a row clears a band of light crosses the dots (and\n * only the dots) and runs on through the label: `glimmer=\"color\"` is glimm.dev's prism,\n * `neutral` a band of text/default. Plain CSS animations on transform and opacity; the\n * keyframes are written from the script below into one <style>, put back on mount if a\n * router dropped it, the static rules are in\n * styles/components/agent-loading.css.\n *\n * Flappy: the 17 × 12 pixel bird running in place. It has one frame, so it is stacked as clipped\n * copies of itself — the body and the wing — and the wing and the whole bird step one sprite\n * pixel on a four-beat cycle.\n */\nexport type Glimmer = 'color' | 'neutral'\n\nconst COLS = 5\nconst ROWS = 4\n\n/** Every shape the game has, in every turn used here, as [row, col] from its top left. */\nconst PIECES = {\n I: [[0, 0], [0, 1], [0, 2], [0, 3]],\n 'I|': [[0, 0], [1, 0], [2, 0], [3, 0]],\n O: [[0, 0], [0, 1], [1, 0], [1, 1]],\n T: [[0, 1], [1, 0], [1, 1], [1, 2]],\n 'T↓': [[0, 0], [0, 1], [0, 2], [1, 1]],\n 'T>': [[0, 0], [1, 0], [1, 1], [2, 0]],\n 'T<': [[0, 1], [1, 0], [1, 1], [2, 1]],\n S: [[0, 1], [0, 2], [1, 0], [1, 1]],\n 'S|': [[0, 0], [1, 0], [1, 1], [2, 1]],\n Z: [[0, 0], [0, 1], [1, 1], [1, 2]],\n 'Z|': [[0, 1], [1, 0], [1, 1], [2, 0]],\n L: [[0, 2], [1, 0], [1, 1], [1, 2]],\n 'L|': [[0, 0], [1, 0], [2, 0], [2, 1]],\n 'L↓': [[0, 0], [0, 1], [0, 2], [1, 0]],\n 'L|↓': [[0, 0], [0, 1], [1, 1], [2, 1]],\n J: [[0, 0], [1, 0], [1, 1], [1, 2]],\n 'J|': [[0, 1], [1, 1], [2, 0], [2, 1]],\n 'J↓': [[0, 0], [0, 1], [0, 2], [1, 2]],\n 'J|↓': [[0, 0], [0, 1], [1, 0], [2, 0]]\n} as const satisfies Record<string, readonly (readonly [number, number])[]>\n\ntype Piece = keyof typeof PIECES\n/** A piece and the column its left edge comes down. */\ntype Drop = [Piece, number]\n\n/**\n * Five rounds of five pieces each. Twenty dots fill the board exactly, so\n * every round ends empty — the last piece clears what is left — and the next\n * one starts on the bare grid. Between them the rounds use all seven shapes.\n */\nconst ROUNDS: Drop[][] = [\n // after Figma's frames: two Js build a tower in the middle, then the sides fill in and it goes in two clears\n [['J|', 1], ['J|↓', 1], ['O', 3], ['I|', 0], ['O', 3]],\n // a square and an L lay the floor; four clears, nearly a row a piece\n [['O', 0], ['L', 2], ['S', 2], ['J', 0], ['I', 1]],\n // a T to start, a J standing in the corner, a bar laid over both\n [['T', 0], ['J|', 3], ['I', 0], ['S', 2], ['L↓', 0]],\n // a pillar on the left, an L and a J lean on it, a Z and a T close it\n [['I|', 0], ['L|', 1], ['J↓', 2], ['Z|', 3], ['T↓', 1]],\n // a bar off the left edge, a Z tucked under the gap, a square in the corner\n [['I', 1], ['Z|', 0], ['J', 2], ['O', 3], ['L↓', 0]]\n]\n\n/** Milliseconds. One row of fall, the glide inside it, and everything between. */\nconst T = {\n step: 90,\n glide: 60,\n /** after a piece rests, before a clear or the next piece */\n lock: 220,\n /** a cleared row fading back to the board */\n clear: 260,\n /** what was above it coming down */\n settle: 200,\n /** between the settle and the next piece */\n next: 80,\n /** the empty board between rounds */\n rest: 150,\n /** the glimm band crossing the mark at a clear, and its fade once across */\n sweep: 520,\n outro: 120,\n /** how long before the fade the band sets off */\n lead: 120,\n /** the same band in the label: it picks up after the mark, a little slower across the longer text */\n textDelay: 220,\n textSweep: 600\n}\n\n/** One row down, in px — the Figma grid's 2 px dot and 1 px gap. */\nconst PITCH = 3\n\n/** A moment on a timeline: an offset down in px (and, for a dot, how visible); `glide` eases into the next key. */\ntype Key = { t: number; y: number; on?: number; glide?: boolean }\n/** A dot rests at its place on the board; its keys only move it down after a clear and fade it out. */\ntype Dot = { col: number; row: number; keys: Key[] }\n/** A piece falls as one: its keys bring its dots from above the board to where they rest. */\ntype Fall = { keys: Key[]; dots: Dot[] }\n\n/** Every piece of every round with its timeline over the whole loop, and the loop's length. */\nfunction playRounds(rounds: Drop[][] = ROUNDS): { falls: Fall[]; clears: number[]; loop: number } {\n const falls: Fall[] = []\n /** when each clear starts, for the glimm band */\n const clears: number[] = []\n let t = 0\n\n for (const [n, round] of rounds.entries()) {\n // where each resting dot is now, for collisions and clears\n const board: { dot: Dot; row: number }[] = []\n\n for (const [name, x] of round) {\n const shape = PIECES[name]\n const height = Math.max(...shape.map(([r]) => r)) + 1\n const taken = (r: number, c: number) => r >= ROWS || board.some((b) => b.row === r && b.dot.col === c)\n // the lowest top row it can rest at, falling straight down from above the board\n let top = -height\n while (!shape.some(([r, c]) => taken(top + 1 + r, x + c))) top++\n if (shape.some(([r]) => top + r < 0)) throw new Error(`tetris round ${n + 1}: ${name} at ${x} sticks out of the top`)\n if (shape.some(([, c]) => x + c >= COLS)) throw new Error(`tetris round ${n + 1}: ${name} at ${x} sticks out of the side`)\n\n // from just above the board (clipped), a row a step: a quick glide, then a beat at rest\n const rows = top + height\n const fall: Fall = { keys: [{ t: 0, y: -rows * PITCH }], dots: [] }\n for (let s = 0; s < rows; s++) {\n fall.keys.push({ t, y: (s - rows) * PITCH, glide: true }, { t: t + T.glide, y: (s + 1 - rows) * PITCH })\n t += T.step\n }\n for (const [r, c] of shape) {\n const dot: Dot = { col: x + c, row: top + r, keys: [{ t: 0, y: 0, on: 1 }] }\n fall.dots.push(dot)\n board.push({ dot, row: dot.row })\n }\n falls.push(fall)\n t += T.lock\n\n const full = [...new Set(board.map((b) => b.row))].filter((r) => board.filter((b) => b.row === r).length === COLS)\n if (!full.length) continue\n clears.push(t)\n\n // the full rows fade back to the board; what stood above them comes down\n for (const { dot, row } of board.filter((b) => full.includes(b.row))) {\n const y = (row - dot.row) * PITCH\n dot.keys.push({ t, y, on: 1, glide: true }, { t: t + T.clear, y, on: 0 })\n }\n t += T.clear\n const kept = board.filter((b) => !full.includes(b.row))\n for (const b of kept) {\n const down = full.filter((r) => r > b.row).length\n if (!down) continue\n b.dot.keys.push({ t, y: (b.row - b.dot.row) * PITCH, on: 1, glide: true }, { t: t + T.settle, y: (b.row + down - b.dot.row) * PITCH, on: 1 })\n b.row += down\n }\n board.splice(0, board.length, ...kept)\n t += T.settle + T.next\n }\n\n if (board.length) throw new Error(`tetris round ${n + 1} leaves ${board.length} dots on the board`)\n t += T.rest\n }\n\n return { falls, clears, loop: t }\n}\n\n// movement on the kit's ease-out, a quick start that settles; a fade on plain ease, so the full row is seen going\nconst GLIDE = 'animation-timing-function:cubic-bezier(0.22,1,0.36,1)'\nconst FADE = 'animation-timing-function:ease'\n\n/**\n * The CSS: `satin-tetris-f<i>` for each piece's fall, `satin-tetris-d<i>` for each of its\n * dots, `satin-tetris-glimm` for the band on the mark and `satin-tetris-glimm-text` for\n * it in the label, over a loop of `loop` ms.\n *\n * The band (glimm.dev's sweep) crosses the mark left to right as a row\n * clears, on glimm's default `ease`, then fades out past the right edge; it\n * carries on into the label as the mark's sweep runs out. Between sweeps it\n * goes back invisible.\n */\nfunction keyframes(falls: Fall[], clears: number[], loop: number): string {\n const pct = (t: number) => `${+((t / loop) * 100).toFixed(3)}%`\n const block = (name: string, keys: Key[]) => {\n const all = [...keys, { ...keys[keys.length - 1], t: loop, glide: false }]\n const frames = all.map((k) => {\n const props = [`transform:translateY(${k.y}px)`]\n if (k.on !== undefined) props.push(`opacity:${k.on}`)\n if (k.glide) props.push(k.on === 1 && keys[keys.indexOf(k) + 1]?.on === 0 ? FADE : GLIDE)\n return `${pct(k.t)}{${props.join(';')}}`\n })\n return `@keyframes ${name}{${frames.join('')}}`\n }\n let d = 0\n const pieces = falls.flatMap((f, i) => [block(`satin-tetris-f${i}`, f.keys), ...f.dots.map((dot) => block(`satin-tetris-d${d++}`, dot.keys))])\n const band = (x: number, on: number, ease = '') => `{transform:translateX(${x}%);opacity:${on}${ease}}`\n // it sets off a beat before the fade, so the light runs over the full row as it goes\n const sweeps = clears.map((c) => c - T.lead).flatMap((t) => [\n `${pct(t - 1)}${band(-100, 0)}`,\n `${pct(t)}${band(-100, 1, ';animation-timing-function:ease')}`,\n `${pct(t + T.sweep)}${band(100, 1, ';animation-timing-function:ease-out')}`,\n `${pct(t + T.sweep + T.outro)}${band(100, 0)}`\n ])\n const glimm = `@keyframes satin-tetris-glimm{0%${band(-100, 0)}${sweeps.join('')}100%${band(100, 0)}}`\n // in the label the band is a text-clipped background three widths wide: 100% is off the left, 0% off the right\n const text = (x: number, on: number, ease = '') => `{background-position:${x}% 0;opacity:${on}${ease}}`\n const textSweeps = clears.map((c) => c - T.lead + T.textDelay).flatMap((t) => [\n `${pct(t - 1)}${text(100, 0)}`,\n `${pct(t)}${text(100, 1, ';animation-timing-function:ease')}`,\n `${pct(t + T.textSweep)}${text(0, 1, ';animation-timing-function:ease-out')}`,\n `${pct(t + T.textSweep + T.outro)}${text(0, 0)}`\n ])\n const glimmText = `@keyframes satin-tetris-glimm-text{0%${text(100, 0)}${textSweeps.join('')}100%${text(0, 0)}}`\n return [...pieces, glimm, glimmText].join('\\n')\n}\n\n\nconst { falls, clears, loop } = playRounds()\n\nlet css: string | undefined\n\n/**\n * Writes the keyframes into <head> if they aren't there. Called on every mount, not once per\n * module: routers that swap <head> on navigation (Astro's ClientRouter, for one) drop the\n * style, and the module never runs again, so a mark on the next page would stand still.\n */\nfunction useTetrisKeyframes() {\n React.useInsertionEffect(() => {\n if (document.querySelector('style[data-satin-tetris]')) return\n const style = document.createElement('style')\n style.dataset.satinTetris = ''\n style.textContent = css ??= keyframes(falls, clears, loop)\n document.head.append(style)\n }, [])\n}\n\nconst GRID = Array.from({ length: COLS * ROWS }, (_, i) => i)\n\n/** The tetris mark. Memoised: its 125 animated elements never re-render with their parent. */\nexport const TetrisMark = React.memo(function TetrisMark({ glimmer = 'color', className }: { glimmer?: Glimmer; className?: string }) {\n useTetrisKeyframes()\n let d = 0\n return (\n <span\n aria-hidden\n data-glimmer={glimmer}\n className={cn('satin-tetris', className)}\n style={{ '--satin-tetris-loop': `${loop}ms` } as React.CSSProperties}\n >\n <span className=\"satin-tetris__board\">\n {GRID.map((i) => (\n <i key={i} />\n ))}\n {falls.map((fall, f) => (\n <span key={f} className=\"satin-tetris__fall\" style={{ animationName: `satin-tetris-f${f}` }}>\n {fall.dots.map((dot) => (\n <b key={d} style={{ '--c': dot.col, '--r': dot.row, animationName: `satin-tetris-d${d++}` } as React.CSSProperties} />\n ))}\n </span>\n ))}\n <span className=\"satin-tetris__glimm\">\n <span />\n </span>\n </span>\n </span>\n )\n})\n\n/** The tetris mark at rest, every dot filled: the finished header's mark. */\nexport function TetrisStill({ className }: { className?: string }) {\n return (\n <span aria-hidden data-still className={cn('satin-tetris', className)}>\n <span className=\"satin-tetris__board\">\n {GRID.map((i) => (\n <i key={i} />\n ))}\n </span>\n </span>\n )\n}\n\n/**\n * The label beside the mark: a shimmer, and on top a copy of the text the band crosses after the\n * mark, on the same loop, so the light runs from the icon on through the word.\n */\nexport function TetrisLabel({ children, glimmer = 'color', className }: { children: React.ReactNode; glimmer?: Glimmer; className?: string }) {\n useTetrisKeyframes()\n return (\n <span\n data-glimmer={glimmer}\n className={cn('satin-tetris-label', className)}\n style={{ '--satin-tetris-loop': `${loop}ms` } as React.CSSProperties}\n >\n <span className=\"satin-shimmer\">{children}</span>\n <span className=\"satin-tetris-label__glimm\" aria-hidden>\n <span>{children}</span>\n </span>\n </span>\n )\n}\n\n/** The label shimmer alone (a band of ink passing through muted text), for the flappy header. */\nexport function ShimmerLabel({ children, className }: { children: React.ReactNode; className?: string }) {\n return <span className={cn('satin-shimmer', className)}>{children}</span>\n}\n\nconst FLAPPY = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAAMCAYAAACEJVa/AAAAa0lEQVR42mNgwA3+48BEAbDi/6+tUTBYDAiIMQhDM7IBMIDLIBQn4zMEl4v+owN0g8gyhJBB2A0BKUQGME1YvIUvtjBtxRYu4UoYGMMgeMAeZEfBMANISTcYhiAbhM8V2BMcFoPIScFEaQAAya9DoeSCRfcAAAAASUVORK5CYII='\n// [top, right, bottom, left) in sprite pixels: the body in three bands around the wing, and the wing.\nconst FLAPPY_PARTS: { role: 'body' | 'wing'; rect: [number, number, number, number] }[] = [\n { role: 'body', rect: [0, 17, 3, 0] },\n { role: 'body', rect: [3, 17, 8, 6] },\n { role: 'body', rect: [8, 17, 12, 0] },\n { role: 'wing', rect: [3, 6, 8, 0] },\n]\n\n/** The flappy bird fitted into a `size` square; `run` plays the run in place. */\nexport function FlappySprite({ size = 16, run = true, className }: { size?: number; run?: boolean; className?: string }) {\n const cols = 17\n const rows = 12\n return (\n <span\n aria-hidden\n data-run={run || undefined}\n className={cn('satin-sprite', className)}\n style={{ width: size, height: size, '--rows': rows, '--cols': cols } as React.CSSProperties}\n >\n <span className=\"satin-sprite__frame\" style={{ width: size, height: (size * rows) / cols }}>\n {FLAPPY_PARTS.map(({ role, rect: [t, r, b, l] }, i) => (\n <img\n key={i}\n src={FLAPPY}\n alt=\"\"\n draggable={false}\n data-role={role}\n className=\"satin-sprite__part\"\n style={{ clipPath: `inset(${(t / rows) * 100}% ${(1 - r / cols) * 100}% ${(1 - b / rows) * 100}% ${(l / cols) * 100}%)` }}\n />\n ))}\n </span>\n </span>\n )\n}\n\n/** What runs beside the label: the tetris mark, or the flappy bird. */\nexport type AgentLoadingIndicator = 'tetris' | 'flappy'\n\nexport interface AgentLoadingProps extends React.ComponentProps<'div'> {\n /** The live label. */\n label?: React.ReactNode\n /** Elapsed time after the label, e.g. \"1.2s\". */\n time?: React.ReactNode\n /** The mark beside the label. */\n indicator?: AgentLoadingIndicator\n /** The light that crosses the tetris mark and its label as rows clear: glimm's prism, or neutral. */\n glimmer?: Glimmer\n}\n\nexport function AgentLoading({ label = 'Thinking…', time, indicator = 'tetris', glimmer = 'color', className, ...props }: AgentLoadingProps) {\n return (\n <div data-slot=\"agent-loading\" role=\"status\" aria-live=\"polite\" className={cn('flex items-center gap-3 p-1', className)} {...props}>\n {indicator === 'flappy' ? <FlappySprite size={16} /> : <TetrisMark glimmer={glimmer} />}\n {indicator === 'tetris' ? (\n <TetrisLabel glimmer={glimmer} className=\"text-sm whitespace-nowrap\">\n {label}\n </TetrisLabel>\n ) : (\n <ShimmerLabel className=\"text-sm whitespace-nowrap\">{label}</ShimmerLabel>\n )}\n {time != null && <span className=\"text-sm whitespace-nowrap text-text-subtle tabular-nums\">{time}</span>}\n </div>\n )\n}\n"]}