@leavepulse/ui 0.69.0 → 0.70.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 (640) hide show
  1. package/README.md +49 -0
  2. package/dist/assets/LoaderMark-DxBV2iDJ.css +43 -0
  3. package/dist/assets/LpActivityLine-B3EwNQEB.css +33 -0
  4. package/dist/assets/{LpCommandPalette-DxyjhYn_.css → LpCommandPalette-u-zOxKWa.css} +21 -31
  5. package/dist/assets/LpDock-Cbh0wQUv.css +17 -0
  6. package/dist/assets/{LpDropzone-Qb8k7cg4.css → LpDropzone-Bb1vBTeU.css} +12 -17
  7. package/dist/assets/{LpImageEditor-CElPDzpF.css → LpImageEditor-XM5bAziW.css} +6 -6
  8. package/dist/assets/LpLogViewer-BDNW6P52.css +38 -0
  9. package/dist/assets/{LpPalettePreview-CJmAKgYW.css → LpPalettePreview-B9VKDrvA.css} +2 -7
  10. package/dist/assets/LpRepeater-C1Cw27hj.css +21 -0
  11. package/dist/assets/{LpRollingText-BGnE6q9j.css → LpRollingText-dyOXedGO.css} +7 -0
  12. package/dist/assets/LpShinyText-D69cBYDR.css +61 -0
  13. package/dist/assets/LpStatus-430OGTZY.css +17 -0
  14. package/dist/assets/LpStepMark-Drvx33Vg.css +10 -0
  15. package/dist/assets/{LpTable-BPERfJIk.css → LpTable-DHxEQ_-3.css} +19 -19
  16. package/dist/assets/LpTabs-BJ-nrJuI.css +18 -0
  17. package/dist/assets/LpTextReveal-DjI1MsDS.css +35 -0
  18. package/dist/assets/LpTypewriter-CXAm9Nca.css +27 -0
  19. package/dist/chunks/AttentionDot-DCBf4aWg.js +25 -0
  20. package/dist/chunks/{ContextMenuItems-B7XFHksm.js → ContextMenuItems-6kxEivBI.js} +11 -5
  21. package/dist/chunks/{DropzoneLinkField-CjkrpDqO.js → DropzoneLinkField-Bw8Flkhk.js} +2 -2
  22. package/dist/chunks/FieldWrapper-BiennROB.js +43 -0
  23. package/dist/chunks/{LayoutCanvas-DF9CovaK.js → LayoutCanvas-BsJIzhdd.js} +1 -1
  24. package/dist/chunks/{LayoutNode-D9bIFzk0.js → LayoutNode-DJVhkSGk.js} +6 -8
  25. package/dist/chunks/LegendSwatch-BhRjrtzv.js +18 -0
  26. package/dist/chunks/LoaderMark-C1dubiKn.js +85 -0
  27. package/dist/chunks/{LpActionBar-MPKMu3pz.js → LpActionBar-CzOFar5t.js} +14 -20
  28. package/dist/chunks/LpActivityLine-CJvzrNpj.js +95 -0
  29. package/dist/chunks/{LpAddressInput-DG0BTqdH.js → LpAddressInput-Chv_Xf8q.js} +7 -7
  30. package/dist/chunks/LpAlert-C5igj802.js +119 -0
  31. package/dist/chunks/{LpAppShell-DPh1756q.js → LpAppShell-BiWByLmt.js} +15 -16
  32. package/dist/chunks/{LpAutocomplete-Dgn6hw3p.js → LpAutocomplete-D92niDHE.js} +15 -32
  33. package/dist/chunks/{LpAvatar-Bs0Fb1lC.js → LpAvatar-CLPl-qOF.js} +18 -6
  34. package/dist/chunks/LpBadge-BBq1Er9T.js +126 -0
  35. package/dist/chunks/LpBreadcrumbs-JKEi_yFF.js +144 -0
  36. package/dist/chunks/{LpButton-DNR-YAq5.js → LpButton-CVxBfGjd.js} +12 -4
  37. package/dist/chunks/{LpCalendar-BLya39Fh.js → LpCalendar-pVL91-Pl.js} +1 -1
  38. package/dist/chunks/{LpCard-Bf71NXZq.js → LpCard-CqKLnEhl.js} +18 -8
  39. package/dist/chunks/{LpCheckbox-Da5oZPHo.js → LpCheckbox-DHc2bHDe.js} +9 -6
  40. package/dist/chunks/{LpCodeBlock-lLbmslfs.js → LpCodeBlock-BGsQhxw8.js} +24 -32
  41. package/dist/chunks/LpCodeDisplay-CbJMs-IK.js +189 -0
  42. package/dist/chunks/LpCollapse-CWS9M_lm.js +115 -0
  43. package/dist/chunks/{LpColorPicker-DtEfBAcK.js → LpColorPicker-B7HSIFmn.js} +11 -6
  44. package/dist/chunks/{LpConfirmDialog-D7s4jakB.js → LpConfirmDialog-CfGCux8m.js} +3 -3
  45. package/dist/chunks/{LpConfirmHost-DrYI5y1K.js → LpConfirmHost-exB7JmcO.js} +1 -1
  46. package/dist/chunks/{LpContextMenu-CRnjoqSt.js → LpContextMenu-Cj96ENDC.js} +16 -7
  47. package/dist/chunks/LpCopyButton-DzHjnFs0.js +133 -0
  48. package/dist/chunks/LpCountdown-BQnmB4gq.js +231 -0
  49. package/dist/chunks/LpDataList-CHNYQw7h.js +121 -0
  50. package/dist/chunks/LpDatePicker-ac1_6o5P.js +169 -0
  51. package/dist/chunks/LpDateRangePicker-B15gYrZJ.js +127 -0
  52. package/dist/chunks/{LpDisclosure-6AplYBub.js → LpDisclosure-DOarzlAG.js} +22 -11
  53. package/dist/chunks/{LpDrawer-Q4SOxM4s.js → LpDrawer-DGNKRsGq.js} +9 -4
  54. package/dist/chunks/{LpDropdownMenu-DWTt0ooM.js → LpDropdownMenu-R4aYVtVV.js} +14 -6
  55. package/dist/chunks/LpEditable-Ws3DNQHn.js +238 -0
  56. package/dist/chunks/{LpEmptyState-om1UnFjV.js → LpEmptyState-KTKipws2.js} +1 -1
  57. package/dist/chunks/{LpFieldNotice-CHuDeLjc.js → LpFieldNotice-CurbNuXI.js} +1 -1
  58. package/dist/chunks/{LpFileTree-CW24YsVX.js → LpFileTree-DDNg34tJ.js} +5 -3
  59. package/dist/chunks/{LpFileTreeNode-BXhTHaKW.js → LpFileTreeNode-DmPuYNZ_.js} +42 -55
  60. package/dist/chunks/{LpFormField-DPb47HEm.js → LpFormField-BWwgm1E4.js} +23 -9
  61. package/dist/chunks/LpFrame-BVQCQLDI.js +155 -0
  62. package/dist/chunks/LpGauge-C14Z9503.js +204 -0
  63. package/dist/chunks/LpGlowBorder-B8V-QzIl.js +40 -0
  64. package/dist/chunks/{LpHintExample-C1eKYdeL.js → LpHintExample-9Py0wc-L.js} +11 -34
  65. package/dist/chunks/{LpHintLayer-7UOdWQG3.js → LpHintLayer-HvwUA5Ec.js} +27 -14
  66. package/dist/chunks/{LpHints-BUtBp9-w.js → LpHints-DxbPKOzQ.js} +3 -3
  67. package/dist/chunks/LpHoverCard-spGxyw7T.js +130 -0
  68. package/dist/chunks/{LpIcon-6tSrZ3Zy.js → LpIcon-HJP5xMz4.js} +1 -1
  69. package/dist/chunks/LpIdenticon-BzdC_qz9.js +63 -0
  70. package/dist/chunks/{LpInfraNode-Ds_8SZgN.js → LpInfraNode-D64hywJI.js} +39 -25
  71. package/dist/chunks/{LpInput-CUyr6PIE.js → LpInput-CDuwODJG.js} +44 -18
  72. package/dist/chunks/{LpLightbox-B9J8rzsj.js → LpLightbox-XJ0LpTcZ.js} +69 -69
  73. package/dist/chunks/LpLoader-DjJEX8hL.js +92 -0
  74. package/dist/chunks/{LpLogViewer-4QFc-sVN.js → LpLogViewer-DKK4ySZl.js} +225 -129
  75. package/dist/chunks/LpMagnetic-CtXKLa2Y.js +62 -0
  76. package/dist/chunks/{LpMapPicker-BTbkFeSG.js → LpMapPicker-BtIl7NOM.js} +34 -31
  77. package/dist/chunks/LpMasonry--k0571qG.js +185 -0
  78. package/dist/chunks/{LpMediaGallery-CuPcscx3.js → LpMediaGallery-rmI1DjKN.js} +3 -3
  79. package/dist/chunks/{LpModal-hk1noDcr.js → LpModal-eBMt2Fqn.js} +8 -3
  80. package/dist/chunks/{LpModalNav-DQd2keZm.js → LpModalNav-DiyFCHiZ.js} +4 -4
  81. package/dist/chunks/{LpNotificationBell-X84PJIxD.js → LpNotificationBell-D-WhE5m2.js} +32 -48
  82. package/dist/chunks/LpNumberField-B9k-h2HE.js +140 -0
  83. package/dist/chunks/{LpNumberFlow-z4WPZcZu.js → LpNumberFlow-Cfyh033Q.js} +8 -28
  84. package/dist/chunks/{LpOtpInput-PbeNJ5In.js → LpOtpInput-CeBPsM7D.js} +2 -1
  85. package/dist/chunks/{LpPagination-Bx-VG4Ag.js → LpPagination-VNCX6pFC.js} +37 -6
  86. package/dist/chunks/{LpPalettePreview-CFav_7co.js → LpPalettePreview-BNB8T_MY.js} +1 -1
  87. package/dist/chunks/{LpPasswordInput-CbnXitAS.js → LpPasswordInput-GL2KOFK8.js} +26 -22
  88. package/dist/chunks/{LpPhoneInput-DP69HONS.js → LpPhoneInput-C60tx1tC.js} +12 -7
  89. package/dist/chunks/{LpPie-yLF9-aDR.js → LpPie-BgXZ3xmm.js} +159 -48
  90. package/dist/chunks/LpPipeline-B31h41Mu.js +329 -0
  91. package/dist/chunks/{LpPopover-DVnkHaOu.js → LpPopover-DmZ3wUtP.js} +13 -7
  92. package/dist/chunks/{LpProgress-CA86wxXu.js → LpProgress-CE-m5OpD.js} +23 -63
  93. package/dist/chunks/LpQuota-DaL8TgNe.js +162 -0
  94. package/dist/chunks/{LpRadioGroup-8S5hf8lE.js → LpRadioGroup-CGBr5nIp.js} +1 -1
  95. package/dist/chunks/{LpRangeCalendar-ByQi-WUz.js → LpRangeCalendar-c5qj7YcC.js} +1 -1
  96. package/dist/chunks/{LpRangeSlider-C1yF9-nP.js → LpRangeSlider-Dv6lNvHY.js} +6 -2
  97. package/dist/chunks/LpRelativeTime-yin9uQP7.js +73 -0
  98. package/dist/chunks/LpRepeater-D0S7jjwr.js +180 -0
  99. package/dist/chunks/LpRevealOnScroll-DcQjexLy.js +76 -0
  100. package/dist/chunks/{LpRollingText-DB7upf-5.js → LpRollingText-DiL717LK.js} +4 -3
  101. package/dist/chunks/LpScramble-TDO5Kdou.js +85 -0
  102. package/dist/chunks/LpSecretField-CfWyHv9m.js +316 -0
  103. package/dist/chunks/{LpSection-BmRXe6ZP.js → LpSection-BW4xJ-Li.js} +1 -1
  104. package/dist/chunks/{LpSegmented-CW3bFHM4.js → LpSegmented-BO99Sr4m.js} +11 -14
  105. package/dist/chunks/{LpSelect-BCyDYoih.js → LpSelect-C_8cJtj6.js} +91 -111
  106. package/dist/chunks/LpShaderBackdrop-Bote0iuy.js +332 -0
  107. package/dist/chunks/LpShinyText-DX2ajtWq.js +48 -0
  108. package/dist/chunks/{LpSidebar-DfIiS-lB.js → LpSidebar-CZRXtvem.js} +5 -3
  109. package/dist/chunks/{LpSidebarNav-C5FDNI0t.js → LpSidebarNav-DBg1U1oW.js} +3 -3
  110. package/dist/chunks/{LpSlider-Cuzmrd7p.js → LpSlider-DuoRpOrH.js} +6 -2
  111. package/dist/chunks/{LpSparkline-DXT4KrSi.js → LpSparkline-CExIRDb3.js} +32 -27
  112. package/dist/chunks/LpSpotlight-4F3NeUMY.js +41 -0
  113. package/dist/chunks/{LpStat-iBMF5QNk.js → LpStat-DFKlPJgy.js} +38 -35
  114. package/dist/chunks/LpStatus-BsGLrpor.js +86 -0
  115. package/dist/chunks/LpStepMark-TrzOioa9.js +101 -0
  116. package/dist/chunks/LpStepper-BbnS5jUr.js +76 -0
  117. package/dist/chunks/{LpSwap-D82jiecV.js → LpSwap-DzE6M3Mg.js} +1 -1
  118. package/dist/chunks/LpSwitch-DrzKrZZV.js +112 -0
  119. package/dist/chunks/{LpTableFilter-CuIbgdW0.js → LpTableFilter-DyvHGbOk.js} +3 -2
  120. package/dist/chunks/{LpTableOfContents-C_4VBktT.js → LpTableOfContents-rYvo8z5T.js} +34 -21
  121. package/dist/chunks/{LpTagsInput-DiXrpYeE.js → LpTagsInput-DZg80W-B.js} +68 -71
  122. package/dist/chunks/LpTextReveal-CtAnOUkp.js +140 -0
  123. package/dist/chunks/{LpTextarea-xoQd5Lnp.js → LpTextarea-DiWjeVoJ.js} +12 -11
  124. package/dist/chunks/{LpThemeProvider-CQ2jC9TA.js → LpThemeProvider-DcbfOxsU.js} +1 -1
  125. package/dist/chunks/LpThemeStudio-DxVZMtzN.js +416 -0
  126. package/dist/chunks/LpThemeStudioSection-BwhqYDLf.js +20 -0
  127. package/dist/chunks/LpThemeSwitcher-CadVBTBn.js +278 -0
  128. package/dist/chunks/{LpTimeField-MFgAsNc5.js → LpTimeField-BFQVf_PW.js} +13 -5
  129. package/dist/chunks/{LpToaster-C4zE9zB7.js → LpToaster-Wv9ksj4q.js} +49 -82
  130. package/dist/chunks/{LpTooltip-suFCM34F.js → LpTooltip-Cz8SQwRt.js} +8 -3
  131. package/dist/chunks/{LpTopologyCanvas-6ozNrQKh.js → LpTopologyCanvas-C17A1XCb.js} +1 -1
  132. package/dist/chunks/{LpTour-BUp3Jt2e.js → LpTour-CY2bFXge.js} +24 -18
  133. package/dist/chunks/LpTruncate-D_5Pfv5B.js +112 -0
  134. package/dist/chunks/LpTypewriter-BCL_a6wE.js +159 -0
  135. package/dist/chunks/{LpUptimeBar-B6kHw4Cj.js → LpUptimeBar-Tew1Ykx4.js} +10 -9
  136. package/dist/chunks/{LpVideoBackdrop-qWRYBOTQ.js → LpVideoBackdrop-DS1wGv-X.js} +7 -8
  137. package/dist/chunks/{MenuTitle-obgek0B7.js → MenuTitle-BjQZX8tQ.js} +1 -1
  138. package/dist/chunks/OptionContent-CmYUUeWj.js +40 -0
  139. package/dist/chunks/PickerField-DoHv0WMH.js +125 -0
  140. package/dist/chunks/RevealToggle-C-KzaQGJ.js +65 -0
  141. package/dist/chunks/StepConnector-37OgEOXA.js +41 -0
  142. package/dist/chunks/TourCard-BJ8i7dk0.js +123 -0
  143. package/dist/chunks/backdrop-Dkf2vM81.js +59 -0
  144. package/dist/chunks/{icons-DVhZ_plB.js → icons-4IywzkHk.js} +1 -1
  145. package/dist/chunks/{useTheme-2qHG8NEU.js → useTheme-BOV_-AiY.js} +9 -65
  146. package/dist/component-names.d.ts +1 -1
  147. package/dist/components/AttentionDot.vue.d.ts +9 -0
  148. package/dist/components/AttentionDot.vue.js +4 -0
  149. package/dist/components/ContextMenuItems.vue.js +1 -1
  150. package/dist/components/DropzoneLinkField.vue.js +1 -1
  151. package/dist/components/FieldWrapper.vue.d.ts +25 -0
  152. package/dist/components/FieldWrapper.vue.js +4 -0
  153. package/dist/components/LayoutCanvas.vue.js +1 -1
  154. package/dist/components/LayoutNode.vue.d.ts +4 -4
  155. package/dist/components/LayoutNode.vue.js +1 -1
  156. package/dist/components/LegendSwatch.vue.d.ts +6 -0
  157. package/dist/components/LegendSwatch.vue.js +4 -0
  158. package/dist/components/LoaderMark.vue.d.ts +12 -0
  159. package/dist/components/LoaderMark.vue.js +4 -0
  160. package/dist/components/LpActionBar.vue.js +1 -1
  161. package/dist/components/LpActivityLine.vue.d.ts +58 -0
  162. package/dist/components/LpActivityLine.vue.js +4 -0
  163. package/dist/components/LpAddressInput.vue.js +1 -1
  164. package/dist/components/LpAlert.vue.d.ts +2 -2
  165. package/dist/components/LpAlert.vue.js +1 -1
  166. package/dist/components/LpAppShell.vue.d.ts +8 -7
  167. package/dist/components/LpAppShell.vue.js +1 -1
  168. package/dist/components/LpAutocomplete.vue.js +1 -1
  169. package/dist/components/LpAvatar.vue.d.ts +16 -0
  170. package/dist/components/LpAvatar.vue.js +1 -1
  171. package/dist/components/LpBadge.vue.d.ts +19 -5
  172. package/dist/components/LpBadge.vue.js +1 -1
  173. package/dist/components/LpBreadcrumbs.vue.js +1 -1
  174. package/dist/components/LpButton.vue.d.ts +2 -2
  175. package/dist/components/LpButton.vue.js +1 -1
  176. package/dist/components/LpCalendar.vue.js +1 -1
  177. package/dist/components/LpCard.vue.d.ts +24 -0
  178. package/dist/components/LpCard.vue.js +1 -1
  179. package/dist/components/LpChart.vue.js +131 -160
  180. package/dist/components/LpCheckbox.vue.d.ts +2 -2
  181. package/dist/components/LpCheckbox.vue.js +1 -1
  182. package/dist/components/LpCodeBlock.vue.js +1 -1
  183. package/dist/components/LpCodeDisplay.vue.d.ts +89 -0
  184. package/dist/components/LpCodeDisplay.vue.js +4 -0
  185. package/dist/components/LpCollapse.vue.d.ts +58 -0
  186. package/dist/components/LpCollapse.vue.js +4 -0
  187. package/dist/components/LpColorPicker.vue.d.ts +2 -2
  188. package/dist/components/LpColorPicker.vue.js +1 -1
  189. package/dist/components/LpCommandPalette.vue.js +34 -23
  190. package/dist/components/LpConfirmDialog.vue.js +1 -1
  191. package/dist/components/LpConfirmHost.vue.js +1 -1
  192. package/dist/components/LpContextMenu.vue.js +1 -1
  193. package/dist/components/LpCopyButton.vue.d.ts +82 -0
  194. package/dist/components/LpCopyButton.vue.js +4 -0
  195. package/dist/components/LpCountdown.vue.d.ts +100 -0
  196. package/dist/components/LpCountdown.vue.js +4 -0
  197. package/dist/components/LpDataList.vue.d.ts +66 -0
  198. package/dist/components/LpDataList.vue.js +4 -0
  199. package/dist/components/LpDatePicker.vue.d.ts +3 -1
  200. package/dist/components/LpDatePicker.vue.js +1 -1
  201. package/dist/components/LpDateRangePicker.vue.d.ts +3 -1
  202. package/dist/components/LpDateRangePicker.vue.js +1 -1
  203. package/dist/components/LpDisclosure.vue.js +1 -1
  204. package/dist/components/LpDock.vue.d.ts +74 -0
  205. package/dist/components/LpDock.vue.js +204 -0
  206. package/dist/components/LpDrawer.vue.js +1 -1
  207. package/dist/components/LpDropdownMenu.vue.js +1 -1
  208. package/dist/components/LpDropzone.vue.d.ts +8 -1
  209. package/dist/components/LpDropzone.vue.js +36 -15
  210. package/dist/components/LpEditable.vue.d.ts +85 -0
  211. package/dist/components/LpEditable.vue.js +4 -0
  212. package/dist/components/LpEmptyState.vue.js +1 -1
  213. package/dist/components/LpFieldNotice.vue.js +1 -1
  214. package/dist/components/LpFileTree.vue.js +1 -1
  215. package/dist/components/LpFileTreeNode.vue.js +1 -1
  216. package/dist/components/LpFormField.vue.js +1 -1
  217. package/dist/components/LpFrame.vue.d.ts +22 -0
  218. package/dist/components/LpFrame.vue.js +4 -0
  219. package/dist/components/LpGauge.vue.d.ts +116 -0
  220. package/dist/components/LpGauge.vue.js +4 -0
  221. package/dist/components/LpGlowBorder.vue.d.ts +47 -0
  222. package/dist/components/LpGlowBorder.vue.js +4 -0
  223. package/dist/components/LpHintExample.vue.d.ts +2 -2
  224. package/dist/components/LpHintExample.vue.js +1 -1
  225. package/dist/components/LpHintLayer.vue.js +1 -1
  226. package/dist/components/LpHints.vue.js +1 -1
  227. package/dist/components/LpHoverCard.vue.d.ts +60 -0
  228. package/dist/components/LpHoverCard.vue.js +4 -0
  229. package/dist/components/LpIcon.vue.js +1 -1
  230. package/dist/components/LpIdenticon.vue.d.ts +37 -0
  231. package/dist/components/LpIdenticon.vue.js +4 -0
  232. package/dist/components/LpImageEditor.vue.js +13 -19
  233. package/dist/components/LpInfraNode.vue.js +1 -1
  234. package/dist/components/LpInput.vue.d.ts +10 -0
  235. package/dist/components/LpInput.vue.js +1 -1
  236. package/dist/components/LpLightbox.vue.js +1 -1
  237. package/dist/components/LpLoader.vue.d.ts +106 -0
  238. package/dist/components/LpLoader.vue.js +4 -0
  239. package/dist/components/LpLogViewer.vue.js +1 -1
  240. package/dist/components/LpMagnetic.vue.d.ts +28 -0
  241. package/dist/components/LpMagnetic.vue.js +4 -0
  242. package/dist/components/LpMapPicker.vue.js +1 -1
  243. package/dist/components/LpMasonry.vue.d.ts +42 -0
  244. package/dist/components/LpMasonry.vue.js +4 -0
  245. package/dist/components/LpMediaGallery.vue.js +1 -1
  246. package/dist/components/LpModal.vue.js +1 -1
  247. package/dist/components/LpModalNav.vue.js +1 -1
  248. package/dist/components/LpNotificationBell.vue.d.ts +15 -6
  249. package/dist/components/LpNotificationBell.vue.js +1 -1
  250. package/dist/components/LpNumberField.vue.d.ts +57 -0
  251. package/dist/components/LpNumberField.vue.js +1 -1
  252. package/dist/components/LpNumberFlow.vue.js +1 -1
  253. package/dist/components/LpOtpInput.vue.d.ts +12 -4
  254. package/dist/components/LpOtpInput.vue.js +1 -1
  255. package/dist/components/LpPagination.vue.js +1 -1
  256. package/dist/components/LpPaletteFacts.vue.js +2 -2
  257. package/dist/components/LpPaletteHeader.vue.js +2 -2
  258. package/dist/components/LpPalettePreview.vue.js +1 -1
  259. package/dist/components/LpPaletteStats.vue.js +2 -2
  260. package/dist/components/LpPasswordInput.vue.d.ts +19 -2
  261. package/dist/components/LpPasswordInput.vue.js +1 -1
  262. package/dist/components/LpPhoneInput.vue.js +1 -1
  263. package/dist/components/LpPie.vue.d.ts +3 -1
  264. package/dist/components/LpPie.vue.js +1 -1
  265. package/dist/components/LpPipeline.vue.d.ts +91 -0
  266. package/dist/components/LpPipeline.vue.js +4 -0
  267. package/dist/components/LpPopover.vue.d.ts +2 -2
  268. package/dist/components/LpPopover.vue.js +1 -1
  269. package/dist/components/LpProgress.vue.d.ts +11 -4
  270. package/dist/components/LpProgress.vue.js +1 -1
  271. package/dist/components/LpQuota.vue.d.ts +68 -0
  272. package/dist/components/LpQuota.vue.js +4 -0
  273. package/dist/components/LpRadioGroup.vue.js +1 -1
  274. package/dist/components/LpRangeCalendar.vue.js +1 -1
  275. package/dist/components/LpRangeSlider.vue.js +1 -1
  276. package/dist/components/LpRelativeTime.vue.d.ts +41 -0
  277. package/dist/components/LpRelativeTime.vue.js +4 -0
  278. package/dist/components/LpRepeater.vue.d.ts +48 -0
  279. package/dist/components/LpRepeater.vue.js +4 -0
  280. package/dist/components/LpRevealOnScroll.vue.d.ts +67 -0
  281. package/dist/components/LpRevealOnScroll.vue.js +4 -0
  282. package/dist/components/LpRollingText.vue.d.ts +3 -1
  283. package/dist/components/LpRollingText.vue.js +1 -1
  284. package/dist/components/LpScramble.vue.d.ts +55 -0
  285. package/dist/components/LpScramble.vue.js +4 -0
  286. package/dist/components/LpScrollArea.vue.d.ts +1 -1
  287. package/dist/components/LpSecretField.vue.d.ts +108 -0
  288. package/dist/components/LpSecretField.vue.js +4 -0
  289. package/dist/components/LpSection.vue.js +1 -1
  290. package/dist/components/LpSegmented.vue.js +1 -1
  291. package/dist/components/LpSelect.vue.d.ts +2 -0
  292. package/dist/components/LpSelect.vue.js +1 -1
  293. package/dist/components/LpShaderBackdrop.vue.d.ts +39 -0
  294. package/dist/components/LpShaderBackdrop.vue.js +4 -0
  295. package/dist/components/LpShinyText.vue.d.ts +39 -0
  296. package/dist/components/LpShinyText.vue.js +4 -0
  297. package/dist/components/LpShortcutsDialog.vue.js +1 -1
  298. package/dist/components/LpSidebar.vue.js +1 -1
  299. package/dist/components/LpSidebarNav.vue.js +1 -1
  300. package/dist/components/LpSlider.vue.js +1 -1
  301. package/dist/components/LpSparkline.vue.d.ts +4 -3
  302. package/dist/components/LpSparkline.vue.js +1 -1
  303. package/dist/components/LpSpotlight.vue.d.ts +37 -0
  304. package/dist/components/LpSpotlight.vue.js +4 -0
  305. package/dist/components/LpStat.vue.js +1 -1
  306. package/dist/components/LpStatus.vue.d.ts +65 -0
  307. package/dist/components/LpStatus.vue.js +4 -0
  308. package/dist/components/LpStepMark.vue.d.ts +31 -0
  309. package/dist/components/LpStepMark.vue.js +4 -0
  310. package/dist/components/LpStepper.vue.js +1 -1
  311. package/dist/components/LpSwap.vue.js +1 -1
  312. package/dist/components/LpSwitch.vue.d.ts +2 -2
  313. package/dist/components/LpSwitch.vue.js +1 -1
  314. package/dist/components/LpTable.vue.js +104 -120
  315. package/dist/components/LpTableFilter.vue.js +1 -1
  316. package/dist/components/LpTableOfContents.vue.d.ts +6 -1
  317. package/dist/components/LpTableOfContents.vue.js +1 -1
  318. package/dist/components/LpTabs.vue.d.ts +5 -5
  319. package/dist/components/LpTabs.vue.js +25 -33
  320. package/dist/components/LpTagsInput.vue.js +1 -1
  321. package/dist/components/LpTextReveal.vue.d.ts +58 -0
  322. package/dist/components/LpTextReveal.vue.js +4 -0
  323. package/dist/components/LpTextarea.vue.js +1 -1
  324. package/dist/components/LpThemeProvider.vue.js +1 -1
  325. package/dist/components/LpThemeStudio.vue.d.ts +54 -0
  326. package/dist/components/LpThemeStudio.vue.js +4 -0
  327. package/dist/components/LpThemeStudioSection.vue.d.ts +16 -0
  328. package/dist/components/LpThemeStudioSection.vue.js +4 -0
  329. package/dist/components/LpThemeSwitcher.vue.d.ts +32 -1
  330. package/dist/components/LpThemeSwitcher.vue.js +1 -1
  331. package/dist/components/LpTimeField.vue.d.ts +2 -0
  332. package/dist/components/LpTimeField.vue.js +1 -1
  333. package/dist/components/LpToaster.vue.js +1 -1
  334. package/dist/components/LpTooltip.vue.d.ts +2 -2
  335. package/dist/components/LpTooltip.vue.js +1 -1
  336. package/dist/components/LpTopologyCanvas.vue.js +1 -1
  337. package/dist/components/LpTour.vue.js +1 -1
  338. package/dist/components/LpTruncate.vue.d.ts +34 -0
  339. package/dist/components/LpTruncate.vue.js +4 -0
  340. package/dist/components/LpTypewriter.vue.d.ts +61 -0
  341. package/dist/components/LpTypewriter.vue.js +4 -0
  342. package/dist/components/LpUptimeBar.vue.js +1 -1
  343. package/dist/components/LpVideoBackdrop.vue.js +1 -1
  344. package/dist/components/MenuTitle.vue.js +1 -1
  345. package/dist/components/OptionContent.vue.d.ts +7 -0
  346. package/dist/components/OptionContent.vue.js +4 -0
  347. package/dist/components/PickerField.vue.d.ts +45 -0
  348. package/dist/components/PickerField.vue.js +4 -0
  349. package/dist/components/RevealToggle.vue.d.ts +21 -0
  350. package/dist/components/RevealToggle.vue.js +4 -0
  351. package/dist/components/StepConnector.vue.d.ts +19 -0
  352. package/dist/components/StepConnector.vue.js +4 -0
  353. package/dist/components/TourCard.vue.js +1 -1
  354. package/dist/components/badge.d.ts +1 -0
  355. package/dist/components/badge.js +4 -0
  356. package/dist/components/chart.d.ts +5 -1
  357. package/dist/components/chart.js +2 -21
  358. package/dist/components/dateRange.d.ts +11 -0
  359. package/dist/components/dateRange.js +12 -0
  360. package/dist/components/dock.d.ts +19 -0
  361. package/dist/components/dock.js +16 -0
  362. package/dist/components/dropdown.d.ts +10 -1
  363. package/dist/components/dropdown.js +7 -2
  364. package/dist/components/fieldText.d.ts +38 -0
  365. package/dist/components/fieldText.js +53 -0
  366. package/dist/components/fileTree.d.ts +7 -3
  367. package/dist/components/fileTree.js +12 -18
  368. package/dist/components/format.d.ts +41 -0
  369. package/dist/components/format.js +67 -0
  370. package/dist/components/identicon.d.ts +38 -0
  371. package/dist/components/identicon.js +85 -0
  372. package/dist/components/masonry.d.ts +32 -0
  373. package/dist/components/masonry.js +40 -0
  374. package/dist/components/menuWhen.js +1 -1
  375. package/dist/components/meter.d.ts +64 -0
  376. package/dist/components/meter.js +53 -0
  377. package/dist/components/otp.d.ts +1 -1
  378. package/dist/components/otp.js +7 -2
  379. package/dist/components/pipeline.d.ts +55 -0
  380. package/dist/components/pipeline.js +51 -0
  381. package/dist/components/quota.d.ts +57 -0
  382. package/dist/components/quota.js +40 -0
  383. package/dist/components/relativeTime.d.ts +24 -0
  384. package/dist/components/relativeTime.js +56 -0
  385. package/dist/components/reveal.d.ts +18 -0
  386. package/dist/components/reveal.js +32 -0
  387. package/dist/components/scramble.d.ts +68 -0
  388. package/dist/components/scramble.js +58 -0
  389. package/dist/components/shaderBackdrop.d.ts +20 -0
  390. package/dist/components/shaderBackdrop.js +156 -0
  391. package/dist/components/slider.d.ts +5 -0
  392. package/dist/components/slider.js +24 -0
  393. package/dist/components/slotText.d.ts +2 -0
  394. package/dist/components/slotText.js +9 -0
  395. package/dist/components/status.d.ts +14 -0
  396. package/dist/components/status.js +22 -0
  397. package/dist/components/tableFacets.d.ts +8 -0
  398. package/dist/components/tableFacets.js +19 -0
  399. package/dist/components/textSplit.d.ts +17 -0
  400. package/dist/components/textSplit.js +24 -0
  401. package/dist/components/timeSpan.d.ts +78 -0
  402. package/dist/components/timeSpan.js +85 -0
  403. package/dist/components/tone.d.ts +43 -0
  404. package/dist/components/tone.js +82 -0
  405. package/dist/components/tour.js +3 -2
  406. package/dist/components/typewriter.d.ts +36 -0
  407. package/dist/components/typewriter.js +53 -0
  408. package/dist/components/videoBackdrop.js +5 -6
  409. package/dist/composables/easing.d.ts +22 -1
  410. package/dist/composables/easing.js +42 -1
  411. package/dist/composables/pseudoLocale.d.ts +15 -0
  412. package/dist/composables/pseudoLocale.js +96 -0
  413. package/dist/composables/scroll.js +5 -3
  414. package/dist/composables/useClipboard.d.ts +4 -1
  415. package/dist/composables/useClipboard.js +12 -2
  416. package/dist/composables/useField.d.ts +13 -0
  417. package/dist/composables/useField.js +26 -1
  418. package/dist/composables/useForm.js +32 -9
  419. package/dist/composables/useInView.d.ts +11 -0
  420. package/dist/composables/useInView.js +39 -0
  421. package/dist/composables/useLoadingGate.d.ts +8 -0
  422. package/dist/composables/useLoadingGate.js +38 -0
  423. package/dist/composables/useLpConfig.d.ts +10 -0
  424. package/dist/composables/useLpConfig.js +11 -1
  425. package/dist/composables/useMediaQuery.js +3 -1
  426. package/dist/composables/useNow.d.ts +16 -0
  427. package/dist/composables/useNow.js +39 -0
  428. package/dist/composables/usePersisted.js +14 -7
  429. package/dist/composables/usePillTransition.d.ts +5 -2
  430. package/dist/composables/usePillTransition.js +4 -3
  431. package/dist/composables/usePointerFrame.d.ts +18 -0
  432. package/dist/composables/usePointerFrame.js +31 -0
  433. package/dist/composables/useReducedMotion.d.ts +2 -0
  434. package/dist/composables/useReducedMotion.js +7 -0
  435. package/dist/composables/useRepeater.d.ts +17 -0
  436. package/dist/composables/useRepeater.js +31 -0
  437. package/dist/composables/useResizeObserver.d.ts +8 -0
  438. package/dist/composables/useResizeObserver.js +33 -0
  439. package/dist/composables/useRevealedValue.d.ts +31 -0
  440. package/dist/composables/useRevealedValue.js +26 -0
  441. package/dist/composables/useScramble.d.ts +42 -0
  442. package/dist/composables/useScramble.js +76 -0
  443. package/dist/composables/useShift.js +2 -10
  444. package/dist/composables/useSpotlight.d.ts +18 -0
  445. package/dist/composables/useSpotlight.js +53 -0
  446. package/dist/composables/useTilt.js +26 -18
  447. package/dist/composables/useTween.d.ts +43 -0
  448. package/dist/composables/useTween.js +69 -0
  449. package/dist/composables/useView.d.ts +18 -0
  450. package/dist/composables/useView.js +64 -0
  451. package/dist/icons-all.js +1 -1
  452. package/dist/index.d.ts +98 -1
  453. package/dist/index.js +263 -105
  454. package/dist/theme/define.d.ts +12 -0
  455. package/dist/theme/studio.d.ts +90 -0
  456. package/package.json +8 -2
  457. package/src/component-names.ts +33 -0
  458. package/src/components/AttentionDot.vue +27 -0
  459. package/src/components/ContextMenuItems.vue +5 -2
  460. package/src/components/FieldWrapper.vue +38 -0
  461. package/src/components/LayoutNode.vue +5 -9
  462. package/src/components/LegendSwatch.vue +15 -0
  463. package/src/components/LoaderMark.vue +90 -0
  464. package/src/components/LpActionBar.vue +12 -20
  465. package/src/components/LpActivityLine.vue +186 -0
  466. package/src/components/LpAlert.vue +54 -24
  467. package/src/components/LpAppShell.vue +7 -11
  468. package/src/components/LpAutocomplete.vue +16 -14
  469. package/src/components/LpAvatar.vue +17 -2
  470. package/src/components/LpBadge.vue +77 -13
  471. package/src/components/LpBreadcrumbs.vue +101 -35
  472. package/src/components/LpButton.vue +15 -3
  473. package/src/components/LpCard.vue +30 -7
  474. package/src/components/LpChart.vue +162 -171
  475. package/src/components/LpCheckbox.vue +10 -4
  476. package/src/components/LpCodeBlock.vue +11 -19
  477. package/src/components/LpCodeDisplay.vue +228 -0
  478. package/src/components/LpCollapse.vue +162 -0
  479. package/src/components/LpColorPicker.vue +10 -1
  480. package/src/components/LpCommandPalette.vue +38 -24
  481. package/src/components/LpContextMenu.vue +13 -3
  482. package/src/components/LpCopyButton.vue +185 -0
  483. package/src/components/LpCountdown.vue +308 -0
  484. package/src/components/LpDataList.vue +154 -0
  485. package/src/components/LpDatePicker.vue +55 -104
  486. package/src/components/LpDateRangePicker.vue +51 -95
  487. package/src/components/LpDisclosure.vue +58 -36
  488. package/src/components/LpDock.vue +274 -0
  489. package/src/components/LpDrawer.vue +10 -1
  490. package/src/components/LpDropdownMenu.vue +11 -2
  491. package/src/components/LpDropzone.vue +34 -13
  492. package/src/components/LpEditable.vue +295 -0
  493. package/src/components/LpFileTree.vue +5 -1
  494. package/src/components/LpFileTreeNode.vue +25 -50
  495. package/src/components/LpFormField.vue +22 -3
  496. package/src/components/LpFrame.vue +200 -0
  497. package/src/components/LpGauge.vue +287 -0
  498. package/src/components/LpGlowBorder.vue +60 -0
  499. package/src/components/LpHintExample.vue +9 -32
  500. package/src/components/LpHintLayer.vue +13 -3
  501. package/src/components/LpHoverCard.vue +175 -0
  502. package/src/components/LpIdenticon.vue +69 -0
  503. package/src/components/LpImageEditor.vue +15 -18
  504. package/src/components/LpInfraNode.vue +27 -16
  505. package/src/components/LpInput.vue +30 -16
  506. package/src/components/LpLightbox.vue +44 -23
  507. package/src/components/LpLoader.vue +145 -0
  508. package/src/components/LpLogViewer.vue +311 -82
  509. package/src/components/LpMagnetic.vue +97 -0
  510. package/src/components/LpMapPicker.vue +26 -28
  511. package/src/components/LpMasonry.vue +247 -0
  512. package/src/components/LpModal.vue +10 -1
  513. package/src/components/LpNotificationBell.vue +33 -49
  514. package/src/components/LpNumberField.vue +79 -14
  515. package/src/components/LpNumberFlow.vue +10 -35
  516. package/src/components/LpOtpInput.vue +7 -2
  517. package/src/components/LpPagination.vue +38 -4
  518. package/src/components/LpPalettePreview.vue +2 -7
  519. package/src/components/LpPasswordInput.vue +25 -18
  520. package/src/components/LpPhoneInput.vue +12 -3
  521. package/src/components/LpPie.vue +197 -36
  522. package/src/components/LpPipeline.vue +366 -0
  523. package/src/components/LpPopover.vue +14 -1
  524. package/src/components/LpProgress.vue +22 -84
  525. package/src/components/LpQuota.vue +197 -0
  526. package/src/components/LpRangeSlider.vue +5 -1
  527. package/src/components/LpRelativeTime.vue +90 -0
  528. package/src/components/LpRepeater.vue +131 -0
  529. package/src/components/LpRevealOnScroll.vue +125 -0
  530. package/src/components/LpRollingText.vue +12 -1
  531. package/src/components/LpScramble.vue +107 -0
  532. package/src/components/LpSecretField.vue +320 -0
  533. package/src/components/LpSegmented.vue +3 -10
  534. package/src/components/LpSelect.vue +74 -65
  535. package/src/components/LpShaderBackdrop.vue +434 -0
  536. package/src/components/LpShinyText.vue +139 -0
  537. package/src/components/LpSidebar.vue +2 -0
  538. package/src/components/LpSlider.vue +5 -1
  539. package/src/components/LpSparkline.vue +55 -37
  540. package/src/components/LpSpotlight.vue +51 -0
  541. package/src/components/LpStat.vue +6 -2
  542. package/src/components/LpStatus.vue +127 -0
  543. package/src/components/LpStepMark.vue +138 -0
  544. package/src/components/LpStepper.vue +37 -43
  545. package/src/components/LpSwap.vue +2 -2
  546. package/src/components/LpSwitch.vue +15 -22
  547. package/src/components/LpTable.vue +47 -63
  548. package/src/components/LpTableFilter.vue +1 -0
  549. package/src/components/LpTableOfContents.vue +53 -29
  550. package/src/components/LpTabs.vue +10 -21
  551. package/src/components/LpTagsInput.vue +53 -37
  552. package/src/components/LpTextReveal.vue +236 -0
  553. package/src/components/LpTextarea.vue +11 -11
  554. package/src/components/LpThemeStudio.vue +216 -0
  555. package/src/components/LpThemeStudioSection.vue +16 -0
  556. package/src/components/LpThemeSwitcher.vue +156 -47
  557. package/src/components/LpTimeField.vue +9 -1
  558. package/src/components/LpToaster.vue +63 -83
  559. package/src/components/LpTooltip.vue +10 -1
  560. package/src/components/LpTour.vue +24 -13
  561. package/src/components/LpTruncate.vue +135 -0
  562. package/src/components/LpTypewriter.vue +236 -0
  563. package/src/components/LpUptimeBar.vue +6 -5
  564. package/src/components/LpVideoBackdrop.vue +15 -13
  565. package/src/components/OptionContent.vue +17 -0
  566. package/src/components/PickerField.vue +59 -0
  567. package/src/components/RevealToggle.vue +51 -0
  568. package/src/components/StepConnector.vue +54 -0
  569. package/src/components/TourCard.vue +29 -16
  570. package/src/components/badge.ts +8 -0
  571. package/src/components/chart.ts +8 -23
  572. package/src/components/dateRange.ts +25 -0
  573. package/src/components/dock.ts +31 -0
  574. package/src/components/dropdown.ts +15 -3
  575. package/src/components/fieldText.ts +95 -0
  576. package/src/components/fileTree.ts +19 -21
  577. package/src/components/format.ts +136 -0
  578. package/src/components/identicon.ts +160 -0
  579. package/src/components/masonry.ts +73 -0
  580. package/src/components/meter.ts +122 -0
  581. package/src/components/otp.ts +14 -3
  582. package/src/components/pipeline.ts +101 -0
  583. package/src/components/quota.ts +111 -0
  584. package/src/components/relativeTime.ts +118 -0
  585. package/src/components/reveal.ts +56 -0
  586. package/src/components/scramble.ts +138 -0
  587. package/src/components/shaderBackdrop.ts +208 -0
  588. package/src/components/slider.ts +30 -0
  589. package/src/components/slotText.ts +13 -0
  590. package/src/components/status.ts +46 -0
  591. package/src/components/tableFacets.ts +25 -0
  592. package/src/components/textSplit.ts +55 -0
  593. package/src/components/timeSpan.ts +183 -0
  594. package/src/components/tone.ts +144 -0
  595. package/src/components/tour.ts +3 -2
  596. package/src/components/typewriter.ts +104 -0
  597. package/src/components/videoBackdrop.ts +7 -6
  598. package/src/composables/easing.ts +66 -1
  599. package/src/composables/pseudoLocale.ts +105 -0
  600. package/src/composables/scroll.ts +6 -3
  601. package/src/composables/useClipboard.ts +25 -3
  602. package/src/composables/useField.ts +38 -1
  603. package/src/composables/useForm.ts +41 -11
  604. package/src/composables/useInView.ts +61 -0
  605. package/src/composables/useLoadingGate.ts +63 -0
  606. package/src/composables/useLpConfig.ts +20 -1
  607. package/src/composables/useMediaQuery.ts +6 -2
  608. package/src/composables/useNow.ts +84 -0
  609. package/src/composables/usePersisted.ts +19 -8
  610. package/src/composables/usePillTransition.ts +21 -13
  611. package/src/composables/usePointerFrame.ts +72 -0
  612. package/src/composables/useReducedMotion.ts +22 -0
  613. package/src/composables/useRepeater.ts +44 -0
  614. package/src/composables/useResizeObserver.ts +59 -0
  615. package/src/composables/useRevealedValue.ts +84 -0
  616. package/src/composables/useScramble.ts +152 -0
  617. package/src/composables/useShift.ts +10 -16
  618. package/src/composables/useSpotlight.ts +82 -0
  619. package/src/composables/useTilt.ts +40 -23
  620. package/src/composables/useTween.ts +149 -0
  621. package/src/composables/useView.ts +96 -0
  622. package/src/index.css +1 -0
  623. package/src/index.ts +143 -1
  624. package/src/kit-icons.ts +1 -1
  625. package/src/theme/define.ts +65 -0
  626. package/src/theme/studio.ts +202 -0
  627. package/src/tokens/effects.css +151 -0
  628. package/src/tokens/tokens.css +131 -39
  629. package/src/tokens/utilities.css +81 -0
  630. package/dist/assets/LpTabs-Bk7v4bZz.css +0 -18
  631. package/dist/chunks/LpAlert-BdvxvcWE.js +0 -85
  632. package/dist/chunks/LpBadge-Fe0D1bYx.js +0 -62
  633. package/dist/chunks/LpBreadcrumbs-rzOKkLiI.js +0 -98
  634. package/dist/chunks/LpDatePicker-BMfM454Y.js +0 -243
  635. package/dist/chunks/LpDateRangePicker-CNb9X3eL.js +0 -195
  636. package/dist/chunks/LpNumberField-DO1ufKvE.js +0 -107
  637. package/dist/chunks/LpStepper-C2slwObu.js +0 -90
  638. package/dist/chunks/LpSwitch-CZy9_IoA.js +0 -132
  639. package/dist/chunks/LpThemeSwitcher-CDoziW0z.js +0 -166
  640. package/dist/chunks/TourCard-BKI7X9-r.js +0 -105
@@ -0,0 +1,111 @@
1
+ import { CHART_PALETTE } from "./chart"
2
+ import { formatBytes } from "./format"
3
+ import { capacityThresholds, toneAt, type MeterTone } from "./meter"
4
+
5
+ /*
6
+ * The arithmetic behind LpQuota, kept free of Vue so it can be tested and so an
7
+ * app that only needs "how much is left, and how worried should I be" can ask
8
+ * it directly.
9
+ */
10
+
11
+ /** One part of what is used, for a stacked bar ("Images 12 GB, Backups 30 GB"). */
12
+ export interface QuotaPart {
13
+ label: string
14
+ value: number
15
+ /** Any CSS colour. Unset parts take the chart palette in order, like LpChart's series (minus danger). */
16
+ color?: string
17
+ }
18
+
19
+ /**
20
+ * How amounts are written. `"bytes"` scales to the largest whole unit (1 536 →
21
+ * 1.5 KB) in binary steps, the way storage limits are sold and shown; any
22
+ * other string is a plain suffix ("seats", "req/min"); a function says it its
23
+ * own way.
24
+ */
25
+ export type QuotaUnit = "bytes" | string | ((value: number) => string)
26
+
27
+ export interface QuotaState {
28
+ /** Share of the limit used, 0–1 and beyond: 1.25 is a quarter over. */
29
+ ratio: number
30
+ /** What is left; zero once over. */
31
+ remaining: number
32
+ /** By how much the limit is exceeded; zero within it. */
33
+ over: number
34
+ /** False when there is no limit to measure against (unset, zero, Infinity). */
35
+ limited: boolean
36
+ tone: MeterTone
37
+ }
38
+
39
+ /**
40
+ * Where a quota stands. Thresholds are shares of the limit, read on the same
41
+ * capacity scale (and with the same defaults) as LpProgress's `auto` tone and
42
+ * LpGauge, so a quota and a gauge of one disk turn red together; anything past
43
+ * the limit is danger whatever they say, because "over" is the one state a
44
+ * quota must never paint as merely amber.
45
+ */
46
+ export function quotaState(
47
+ used: number,
48
+ limit: number | null | undefined,
49
+ warnAt = 0.8,
50
+ dangerAt = 0.9,
51
+ ): QuotaState {
52
+ const safeUsed = Number.isFinite(used) ? Math.max(0, used) : 0
53
+ const limited = limit != null && Number.isFinite(limit) && limit > 0
54
+ if (!limited) return { ratio: 0, remaining: Infinity, over: 0, limited, tone: "ok" }
55
+ const ratio = safeUsed / limit
56
+ const tone = ratio > 1 ? "danger" : toneAt(ratio, capacityThresholds(0, 1, warnAt, dangerAt))
57
+ return {
58
+ ratio,
59
+ remaining: Math.max(0, limit - safeUsed),
60
+ over: Math.max(0, safeUsed - limit),
61
+ limited,
62
+ tone,
63
+ }
64
+ }
65
+
66
+ /**
67
+ * An amount in its unit, locale-aware. Bytes go through `formatBytes`, whose
68
+ * unit names come from Intl, so a Russian app reads "1,5 ГБ" without a
69
+ * translation table of its own.
70
+ */
71
+ export function formatAmount(value: number, unit?: QuotaUnit, locale?: string): string {
72
+ if (typeof unit === "function") return unit(value)
73
+ if (unit === "bytes") return formatBytes(Math.max(0, value), locale)
74
+ const number = new Intl.NumberFormat(locale, { maximumFractionDigits: value < 10 ? 1 : 0 }).format(value)
75
+ return unit ? `${number} ${unit}` : number
76
+ }
77
+
78
+ /*
79
+ * Parts are categories, not verdicts, so the palette leaves out danger: a red
80
+ * "Backups" slice would read as an alarm about backups.
81
+ */
82
+ const PART_PALETTE = CHART_PALETTE.filter((c) => !c.includes("danger"))
83
+
84
+ export interface QuotaSegment extends QuotaPart {
85
+ color: string
86
+ /** Share of the bar's width, 0–1. */
87
+ share: number
88
+ }
89
+
90
+ /**
91
+ * The parts laid out along the bar. Within the limit the bar's width IS the
92
+ * limit, so a part's share is its value over the limit and the rest of the
93
+ * track is what remains. Over it, the bar stretches to hold everything used and
94
+ * the limit becomes a mark partway along (`limitAt`), so the overflow reads as
95
+ * the part of the bar beyond the line rather than as a bar that simply stopped
96
+ * at full.
97
+ */
98
+ export function quotaSegments(
99
+ parts: readonly QuotaPart[],
100
+ limit: number | null | undefined,
101
+ ): { segments: QuotaSegment[]; limitAt: number | null } {
102
+ const total = parts.reduce((sum, p) => sum + Math.max(0, p.value), 0)
103
+ const limited = limit != null && Number.isFinite(limit) && limit > 0
104
+ const scale = limited ? Math.max(limit, total) : total
105
+ const segments = parts.map((p, i) => ({
106
+ ...p,
107
+ color: p.color ?? PART_PALETTE[i % PART_PALETTE.length],
108
+ share: scale > 0 ? Math.max(0, p.value) / scale : 0,
109
+ }))
110
+ return { segments, limitAt: limited && total > limit ? limit / total : null }
111
+ }
@@ -0,0 +1,118 @@
1
+ import { toEpoch } from "./timeSpan"
2
+
3
+ /*
4
+ * "3 min ago" / "in 2 h": the wording, and when it next changes.
5
+ *
6
+ * Pure, so it can be tested without a clock and so a non-Vue caller (a toast,
7
+ * a log line) can say a date the same way LpRelativeTime does.
8
+ */
9
+
10
+ export type RelativeUnit = "second" | "minute" | "hour" | "day" | "week" | "month" | "year"
11
+
12
+ const SECOND = 1000
13
+ const MINUTE = 60 * SECOND
14
+ const HOUR = 60 * MINUTE
15
+ const DAY = 24 * HOUR
16
+ const WEEK = 7 * DAY
17
+ const MONTH = 30.4375 * DAY
18
+ const YEAR = 365.25 * DAY
19
+
20
+ /*
21
+ * Each unit holds until the next one reads better. The cut-offs sit before the
22
+ * full next unit on purpose: "50 minutes ago" is fussier than "1 hour ago",
23
+ * and nobody says "6 days ago" about a date that was a week ago.
24
+ */
25
+ const SCALE: { unit: RelativeUnit; ms: number; until: number }[] = [
26
+ { unit: "second", ms: SECOND, until: 45 * SECOND },
27
+ { unit: "minute", ms: MINUTE, until: 45 * MINUTE },
28
+ { unit: "hour", ms: HOUR, until: 22 * HOUR },
29
+ { unit: "day", ms: DAY, until: 6.5 * DAY },
30
+ { unit: "week", ms: WEEK, until: 26 * DAY },
31
+ { unit: "month", ms: MONTH, until: 320 * DAY },
32
+ { unit: "year", ms: YEAR, until: Infinity },
33
+ ]
34
+
35
+ export interface RelativeTimeOptions {
36
+ locale?: string
37
+ /** Intl's width: `long` "3 minutes ago", `short` "3 min. ago", `narrow` "3m ago". */
38
+ style?: Intl.RelativeTimeFormatStyle
39
+ /**
40
+ * Under this many ms either side of now, say "now" instead of counting
41
+ * seconds. Counting seconds is churn nobody reads; a countdown that needs
42
+ * them wants a timer, not this.
43
+ */
44
+ nowWithin?: number
45
+ }
46
+
47
+ export interface RelativeTime {
48
+ text: string
49
+ unit: RelativeUnit
50
+ /** Signed: negative is the past. */
51
+ value: number
52
+ /**
53
+ * Milliseconds until `text` would read differently. A ticker sleeps this
54
+ * long instead of waking every second for a date three weeks away.
55
+ */
56
+ refreshIn: number
57
+ }
58
+
59
+ const formatters = new Map<string, Intl.RelativeTimeFormat>()
60
+ function formatterFor(locale: string | undefined, style: Intl.RelativeTimeFormatStyle) {
61
+ const key = `${locale ?? ""}|${style}`
62
+ let f = formatters.get(key)
63
+ if (!f) {
64
+ f = new Intl.RelativeTimeFormat(locale, { numeric: "auto", style })
65
+ formatters.set(key, f)
66
+ }
67
+ return f
68
+ }
69
+
70
+ /** Never sleep longer than this: a laptop waking from sleep should not show "2 h ago" for an hour. */
71
+ const MAX_REFRESH = HOUR
72
+ const MIN_REFRESH = SECOND
73
+
74
+ export function formatRelativeTime(
75
+ date: Date | string | number,
76
+ now: number = Date.now(),
77
+ options: RelativeTimeOptions = {},
78
+ ): RelativeTime {
79
+ const { locale, style = "long", nowWithin = 45 * SECOND } = options
80
+ const at = toEpoch(date)
81
+ const fmt = formatterFor(locale, style)
82
+ if (!Number.isFinite(at)) return { text: "", unit: "second", value: 0, refreshIn: MAX_REFRESH }
83
+
84
+ const diff = at - now
85
+ const abs = Math.abs(diff)
86
+
87
+ if (abs < nowWithin) {
88
+ // "now" holds until the date is `nowWithin` away on the far side; a future
89
+ // date crossing into the past stays "now" all the way through.
90
+ const refreshIn = diff >= 0 ? diff + nowWithin : nowWithin - abs
91
+ return { text: fmt.format(0, "second"), unit: "second", value: 0, refreshIn: clampRefresh(refreshIn) }
92
+ }
93
+
94
+ const index = SCALE.findIndex((s) => abs < s.until)
95
+ const step = SCALE[index]
96
+ const count = Math.max(1, Math.round(abs / step.ms))
97
+ const value = diff < 0 ? -count : count
98
+
99
+ /*
100
+ * The next moment the rounded count changes. In the past the distance only
101
+ * grows, so that is the next half-unit boundary above it; in the future it
102
+ * only shrinks, so the next one below. Crossing into another unit happens on
103
+ * a boundary of the smaller unit, so this also catches "45 min" → "1 hour".
104
+ */
105
+ const units = abs / step.ms
106
+ const boundary = diff < 0 ? (Math.floor(units + 0.5) + 0.5) * step.ms : (Math.ceil(units - 0.5) - 0.5) * step.ms
107
+ // A unit change can also come before the next rounding step: a past date
108
+ // grows into the next unit at this one's `until`, a future one shrinks into
109
+ // the previous unit at that one's.
110
+ const unitChange = diff < 0 ? step.until - abs : index > 0 ? abs - SCALE[index - 1].until : Infinity
111
+ const refreshIn = Math.min(diff < 0 ? boundary - abs : abs - boundary, unitChange)
112
+
113
+ return { text: fmt.format(value, step.unit), unit: step.unit, value, refreshIn: clampRefresh(refreshIn) }
114
+ }
115
+
116
+ function clampRefresh(ms: number): number {
117
+ return Math.min(MAX_REFRESH, Math.max(MIN_REFRESH, Math.ceil(ms)))
118
+ }
@@ -0,0 +1,56 @@
1
+ /*
2
+ * Keyframes and timing for LpRevealOnScroll, kept apart from the component so
3
+ * they can be tested without a DOM.
4
+ */
5
+
6
+ export type RevealPreset = "fade" | "rise" | "blur" | "scale"
7
+ export type RevealDirection = "up" | "down" | "left" | "right"
8
+
9
+ /**
10
+ * The hidden state an entrance starts from; the end state is always "as laid
11
+ * out", so the keyframes only describe where the content comes FROM.
12
+ *
13
+ * `direction` is the way the content travels: `up` starts below and rises.
14
+ * Every preset fades — motion without a fade reads as a layout jump.
15
+ */
16
+ export function revealFrom(
17
+ preset: RevealPreset,
18
+ direction: RevealDirection,
19
+ distance: number,
20
+ ): Keyframe {
21
+ const offset = (d: number) =>
22
+ ({
23
+ up: `translate3d(0, ${d}px, 0)`,
24
+ down: `translate3d(0, ${-d}px, 0)`,
25
+ left: `translate3d(${d}px, 0, 0)`,
26
+ right: `translate3d(${-d}px, 0, 0)`,
27
+ })[direction]
28
+ switch (preset) {
29
+ case "fade":
30
+ return { opacity: 0 }
31
+ case "rise":
32
+ return { opacity: 0, transform: offset(distance) }
33
+ case "blur":
34
+ // A short travel with the blur: blur alone looks like a focus problem.
35
+ return { opacity: 0, filter: "blur(8px)", transform: offset(distance / 2) }
36
+ case "scale":
37
+ return { opacity: 0, transform: "scale(0.94)" }
38
+ }
39
+ }
40
+
41
+ /** The resting state matching `revealFrom`'s properties. */
42
+ export function revealTo(from: Keyframe): Keyframe {
43
+ const to: Keyframe = { opacity: 1 }
44
+ if ("transform" in from) to.transform = "none"
45
+ if ("filter" in from) to.filter = "blur(0px)"
46
+ return to
47
+ }
48
+
49
+ /**
50
+ * Delay of the `index`-th child. The steps stop growing after `cap` children:
51
+ * a list of eighty would otherwise take seconds to finish arriving, and the
52
+ * last rows would still be invisible when the user starts reading them.
53
+ */
54
+ export function staggerDelay(index: number, delay: number, stagger: number, cap = 10): number {
55
+ return delay + Math.min(Math.max(0, index), cap) * Math.max(0, stagger)
56
+ }
@@ -0,0 +1,138 @@
1
+ /*
2
+ * Frame generation for the "decrypt" effect (LpScramble, useScramble).
3
+ *
4
+ * Kept pure and clock-free so it can be tested frame by frame: a plan says WHEN
5
+ * each position settles, and a frame is the text at a given moment. The caller
6
+ * owns time and randomness — the composable feeds `performance.now()` and
7
+ * `Math.random`, a test feeds fixed numbers and a seeded generator and gets the
8
+ * same frames every run.
9
+ */
10
+
11
+ /** Random number in [0, 1). Injected so frames are reproducible under test. */
12
+ export type Random = () => number
13
+
14
+ /** Glyphs a position cycles through before it settles. Upper case and digits read as "code". */
15
+ export const SCRAMBLE_GLYPHS = "ABCDEFGHJKLMNPQRSTUVWXYZ0123456789#%&*+=?@$"
16
+
17
+ export interface ScrambleOptions {
18
+ /** Total travel time in ms, from the first glyph to the last position settling. */
19
+ duration: number
20
+ /**
21
+ * `sequential` settles left to right, which reads as decoding; `together`
22
+ * holds every position until near the end and lands them in a short burst,
23
+ * which reads as a lock opening.
24
+ */
25
+ order?: "sequential" | "together"
26
+ /** Glyph pool. */
27
+ glyphs?: string
28
+ /**
29
+ * Characters that never scramble: a space or a dash in a key keeps the
30
+ * grouping visible while the rest churns, so the reader sees the shape of
31
+ * what is coming.
32
+ */
33
+ keep?: (char: string) => boolean
34
+ }
35
+
36
+ export interface ScrambleCell {
37
+ /** The character settled into, or "" for a position the new value no longer has. */
38
+ final: string
39
+ /** The character shown before the run started (sizes a position the value is losing). */
40
+ initial: string
41
+ /** Ms from the start of the run when this position shows `final`. */
42
+ settleAt: number
43
+ /** Never churns (see `keep`). */
44
+ fixed: boolean
45
+ }
46
+
47
+ export interface ScrambleFrameCell {
48
+ /** What to draw now. */
49
+ char: string
50
+ /** The character that gives the position its width: the final one, or the old one while it is leaving. */
51
+ sizer: string
52
+ settled: boolean
53
+ }
54
+
55
+ const defaultKeep = (char: string) => /\s/.test(char)
56
+
57
+ /**
58
+ * Splits into user-perceived characters, so an emoji or an accented letter is
59
+ * one position rather than two halves that scramble separately.
60
+ */
61
+ export function graphemes(text: string): string[] {
62
+ const Segmenter = (Intl as { Segmenter?: typeof Intl.Segmenter }).Segmenter
63
+ if (Segmenter) return Array.from(new Segmenter(undefined, { granularity: "grapheme" }).segment(text), (s) => s.segment)
64
+ return Array.from(text)
65
+ }
66
+
67
+ /**
68
+ * When each position stops churning (all of them start at once).
69
+ *
70
+ * `sequential`: positions start together and settle on a ramp across the last
71
+ * 70% of the run, so the left of the value is readable while the right still
72
+ * moves. `together`: every position settles within the last 15%, with a
73
+ * little random spread so they do not land as one flat click.
74
+ */
75
+ export function planScramble(from: string, to: string, options: ScrambleOptions, random: Random = Math.random): ScrambleCell[] {
76
+ const keep = options.keep ?? defaultKeep
77
+ const a = graphemes(from)
78
+ const b = graphemes(to)
79
+ const length = Math.max(a.length, b.length)
80
+ const duration = Math.max(0, options.duration)
81
+ const cells: ScrambleCell[] = []
82
+ for (let i = 0; i < length; i++) {
83
+ const final = b[i] ?? ""
84
+ const initial = a[i] ?? ""
85
+ let settleAt: number
86
+ if (options.order === "together") settleAt = duration * (0.85 + 0.15 * random())
87
+ else settleAt = length === 1 ? duration : duration * (0.3 + (0.7 * i) / (length - 1))
88
+ // A kept character (a space in a grouped key) stays put when it is there
89
+ // before and after, so the grouping holds while the rest churns.
90
+ const fixed = final !== "" && keep(final) && (initial === final || initial === "")
91
+ cells.push({ final, initial, settleAt: fixed ? 0 : settleAt, fixed })
92
+ }
93
+ return cells
94
+ }
95
+
96
+ /** A glyph from the pool. */
97
+ export function randomGlyph(random: Random, glyphs: string = SCRAMBLE_GLYPHS): string {
98
+ const pool = graphemes(glyphs)
99
+ return pool[Math.min(pool.length - 1, Math.floor(random() * pool.length))] ?? ""
100
+ }
101
+
102
+ /**
103
+ * The text at `elapsed` ms into the run. A churning position draws a fresh
104
+ * random glyph each call; the caller decides how often to call (the glyph
105
+ * re-roll rate), not this.
106
+ */
107
+ export function scrambleFrame(
108
+ cells: ScrambleCell[],
109
+ elapsed: number,
110
+ random: Random = Math.random,
111
+ glyphs: string = SCRAMBLE_GLYPHS,
112
+ ): ScrambleFrameCell[] {
113
+ return cells.map((cell) => {
114
+ const sizer = cell.final || cell.initial
115
+ if (cell.fixed || elapsed >= cell.settleAt) return { char: cell.final, sizer: cell.final, settled: true }
116
+ return { char: randomGlyph(random, glyphs), sizer, settled: false }
117
+ })
118
+ }
119
+
120
+ /** The cells of a value at rest, for the first paint and for reduced motion. */
121
+ export function settledCells(text: string): ScrambleFrameCell[] {
122
+ return graphemes(text).map((char) => ({ char, sizer: char, settled: true }))
123
+ }
124
+
125
+ /**
126
+ * A small seeded generator (mulberry32), for tests and for a scramble that
127
+ * should look the same every time it plays (a demo, a screenshot).
128
+ */
129
+ export function seededRandom(seed: number): Random {
130
+ let state = seed >>> 0
131
+ return () => {
132
+ state = (state + 0x6d2b79f5) >>> 0
133
+ let t = state
134
+ t = Math.imul(t ^ (t >>> 15), t | 1)
135
+ t ^= t + Math.imul(t ^ (t >>> 7), t | 61)
136
+ return ((t ^ (t >>> 14)) >>> 0) / 4294967296
137
+ }
138
+ }
@@ -0,0 +1,208 @@
1
+ /*
2
+ * The looks LpShaderBackdrop can draw, and the small decisions around them
3
+ * that are worth testing without a GPU.
4
+ *
5
+ * Every look is a fragment shader over one full-screen triangle, fed the same
6
+ * uniforms: size, time, pixel ratio, intensity, and five theme colours read
7
+ * from the kit's tokens (surface, brand, accent, action, ink). A look never
8
+ * names a colour of its own, so it follows any theme, light or dark.
9
+ *
10
+ * Every look also has a CSS fallback built from the same tokens through
11
+ * `var()`, shown where WebGL is missing, too slow, or unwanted (reduced
12
+ * motion). It is a still that resembles the look, so a page reads the same
13
+ * with or without the GPU, and it is also what shows under the canvas while
14
+ * the first frame is being drawn.
15
+ *
16
+ * The shaders are WebGL 1 / GLSL ES 1.00 and deliberately cheap: a handful of
17
+ * sines and exponentials per pixel, no loops over octaves, no textures. The
18
+ * kit runs inside WebKitGTK, where WebGL may be software-rendered; the soft
19
+ * looks are drawn at half resolution for the same reason.
20
+ */
21
+
22
+ export type ShaderPreset = "aurora" | "silk" | "grain" | "dots"
23
+
24
+ export interface PresetDef {
25
+ /** Body of the fragment shader, after the shared header. */
26
+ fragment: string
27
+ /** Rendering resolution relative to CSS pixels × pixel ratio. Soft looks lose nothing at half. */
28
+ scale: number
29
+ /** CSS `background` of the still that stands in for the look. */
30
+ fallback: string
31
+ }
32
+
33
+ export const VERTEX_SHADER = `
34
+ attribute vec2 p;
35
+ void main() { gl_Position = vec4(p, 0.0, 1.0); }
36
+ `
37
+
38
+ /* Uniforms every look receives. u_bg..u_ink are the theme, in 0–1 sRGB. */
39
+ export const FRAGMENT_HEADER = `
40
+ precision mediump float;
41
+ uniform vec2 u_res;
42
+ uniform float u_time;
43
+ uniform float u_px;
44
+ uniform float u_k;
45
+ uniform vec3 u_bg;
46
+ uniform vec3 u_brand;
47
+ uniform vec3 u_accent;
48
+ uniform vec3 u_action;
49
+ uniform vec3 u_ink;
50
+ `
51
+
52
+ const mix = (token: string, share: number) => `color-mix(in oklab, var(--color-${token}) ${share}%, transparent)`
53
+
54
+ /*
55
+ * Film grain for the CSS still: SVG turbulence, rendered once by the engine
56
+ * as an image. Grey, at low opacity, so it textures any surface colour.
57
+ */
58
+ const NOISE =
59
+ "url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 0.5 0 0 0 0 0.5 0 0 0 0 0.5 0 0 0 0.35 0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E\")"
60
+
61
+ export const PRESETS: Record<ShaderPreset, PresetDef> = {
62
+ /*
63
+ * Curtains of light across the upper part: each band has a sharp upper
64
+ * edge and a long fall-off below it, waves sideways, and is striated by
65
+ * fine vertical rays that drift.
66
+ */
67
+ aurora: {
68
+ scale: 0.5,
69
+ fragment: `
70
+ float band(vec2 uv, float x, float off, float sp) {
71
+ float y = 0.66 + off + 0.09 * sin(x * 1.9 + u_time * sp + off * 9.0) + 0.04 * sin(x * 4.7 - u_time * sp * 1.7 + off * 4.0);
72
+ float d = uv.y - y;
73
+ float body = exp(-d * d * (d > 0.0 ? 160.0 : 22.0));
74
+ float rays = 0.86 + 0.14 * sin(x * 70.0 + 6.0 * sin(x * 2.3 + u_time * 0.4));
75
+ return body * rays;
76
+ }
77
+ void main() {
78
+ vec2 uv = gl_FragCoord.xy / u_res;
79
+ float x = gl_FragCoord.x / u_res.y;
80
+ vec3 col = u_bg;
81
+ col = mix(col, u_brand, band(uv, x, 0.0, 0.35) * 0.7 * u_k);
82
+ col = mix(col, u_accent, band(uv, x, -0.18, 0.27) * 0.45 * u_k);
83
+ col = mix(col, u_action, band(uv, x, 0.12, 0.43) * 0.45 * u_k);
84
+ gl_FragColor = vec4(col, 1.0);
85
+ }`,
86
+ fallback: [
87
+ `radial-gradient(70% 38% at 30% 28%, ${mix("brand", 42)}, transparent 70%)`,
88
+ `radial-gradient(55% 30% at 72% 44%, ${mix("accent", 30)}, transparent 70%)`,
89
+ `radial-gradient(50% 26% at 55% 18%, ${mix("action", 26)}, transparent 70%)`,
90
+ ].join(", "),
91
+ },
92
+
93
+ /*
94
+ * A sheet of fabric: diagonal folds that slowly ripple, the brand colour
95
+ * in the body and a narrow highlight running along each crest.
96
+ */
97
+ silk: {
98
+ scale: 0.5,
99
+ fragment: `
100
+ void main() {
101
+ vec2 uv = gl_FragCoord.xy / u_res;
102
+ vec2 p = (gl_FragCoord.xy - 0.5 * u_res) / u_res.y * 2.2;
103
+ float t = u_time * 0.6;
104
+ float f = p.x * 0.9 + p.y * 0.5 + 0.6 * sin(p.y * 1.7 + t) + 0.35 * sin(p.x * 2.3 - t * 0.8 + p.y);
105
+ float fold = sin(f * 3.4);
106
+ float sheen = pow(0.5 + 0.5 * fold, 6.0);
107
+ vec3 tint = mix(u_brand, u_accent, clamp(uv.x * 0.7 + 0.15 * sin(t * 0.5), 0.0, 1.0));
108
+ vec3 col = mix(u_bg, tint, (0.22 + 0.14 * fold) * u_k);
109
+ col += sheen * mix(u_brand, vec3(1.0), 0.4) * 0.28 * u_k;
110
+ gl_FragColor = vec4(col, 1.0);
111
+ }`,
112
+ fallback: [
113
+ `linear-gradient(115deg, transparent 12%, ${mix("brand", 30)} 30%, transparent 46%, ${mix("accent", 22)} 64%, transparent 80%)`,
114
+ `linear-gradient(160deg, ${mix("brand", 16)}, transparent 70%)`,
115
+ ].join(", "),
116
+ },
117
+
118
+ /*
119
+ * Two soft colour pools drifting over the surface, under moving film
120
+ * grain. The grain is per device pixel and refreshes a dozen-odd times a
121
+ * second, like film, rather than every frame, which would read as static.
122
+ */
123
+ grain: {
124
+ scale: 1,
125
+ fragment: `
126
+ float hash(vec2 q) { return fract(sin(dot(q, vec2(12.9898, 78.233))) * 43758.5453); }
127
+ void main() {
128
+ vec2 uv = gl_FragCoord.xy / u_res;
129
+ float aspect = u_res.x / u_res.y;
130
+ vec2 p = vec2(uv.x * aspect, uv.y);
131
+ float t = u_time * 0.25;
132
+ vec2 a = vec2((0.3 + 0.2 * sin(t)) * aspect, 0.7 + 0.15 * cos(t * 1.3));
133
+ vec2 b = vec2((0.75 + 0.15 * cos(t * 0.8)) * aspect, 0.3 + 0.2 * sin(t * 1.1));
134
+ float da = exp(-dot(p - a, p - a) * 3.0);
135
+ float db = exp(-dot(p - b, p - b) * 4.0);
136
+ vec3 col = mix(u_bg, u_brand, da * 0.6 * u_k);
137
+ col = mix(col, u_accent, db * 0.5 * u_k);
138
+ col = mix(col, u_action, (1.0 - uv.y) * 0.12 * u_k);
139
+ vec2 cell = floor(gl_FragCoord.xy / max(1.0, u_px));
140
+ float g = hash(cell + vec2(floor(u_time * 14.0) * 17.31, 0.0)) - 0.5;
141
+ col += g * 0.09;
142
+ gl_FragColor = vec4(col, 1.0);
143
+ }`,
144
+ fallback: [
145
+ NOISE,
146
+ `radial-gradient(55% 55% at 28% 30%, ${mix("brand", 40)}, transparent 70%)`,
147
+ `radial-gradient(50% 50% at 76% 72%, ${mix("accent", 32)}, transparent 70%)`,
148
+ `linear-gradient(to top, ${mix("action", 10)}, transparent 60%)`,
149
+ ].join(", "),
150
+ },
151
+
152
+ /*
153
+ * A dot grid with a slow ripple travelling out from the middle: dots on
154
+ * the crest swell and take the brand colour, the rest stay small and quiet.
155
+ */
156
+ dots: {
157
+ scale: 1,
158
+ fragment: `
159
+ void main() {
160
+ float cell = 22.0 * u_px;
161
+ vec2 g = gl_FragCoord.xy / cell;
162
+ vec2 id = floor(g);
163
+ vec2 f = fract(g) - 0.5;
164
+ float dist = length(id + 0.5 - 0.5 * u_res / cell);
165
+ float wave = pow(0.5 + 0.5 * sin(dist * 0.5 - u_time * 1.6), 3.0);
166
+ float r = 0.08 + 0.07 * wave * u_k;
167
+ float aa = 1.0 / cell;
168
+ float dotMask = 1.0 - smoothstep(r - aa, r + aa, length(f));
169
+ vec3 rest = mix(u_bg, u_ink, 0.2);
170
+ vec3 col = mix(u_bg, u_brand, wave * 0.03 * u_k);
171
+ col = mix(col, mix(rest, u_brand, wave * u_k), dotMask);
172
+ gl_FragColor = vec4(col, 1.0);
173
+ }`,
174
+ fallback: [
175
+ `radial-gradient(circle, ${mix("ink", 22)} 1.5px, transparent 2.2px) center / 22px 22px`,
176
+ `radial-gradient(45% 45% at 50% 50%, ${mix("brand", 16)}, transparent 75%)`,
177
+ ].join(", "),
178
+ },
179
+ }
180
+
181
+ export const PRESET_NAMES = Object.keys(PRESETS) as ShaderPreset[]
182
+
183
+ /** Whether enough time has passed for the next frame at `fps`. Frames land on the display's beat, so allow a little early. */
184
+ export function frameDue(now: number, last: number, fps: number): boolean {
185
+ return now - last >= 1000 / Math.max(1, fps) - 2
186
+ }
187
+
188
+ /*
189
+ * Whether the engine is failing to keep up. Judged on the median interval
190
+ * between drawn frames, not the mean: a single hitch (a tab switch the
191
+ * visibility event missed, a GC) must not cost the backdrop its motion.
192
+ * Too slow means the frames come at under half the asked rate and slower
193
+ * than about 12 a second: a choppy aurora is worse than a still one.
194
+ */
195
+ export function tooSlow(intervals: readonly number[], fps: number): boolean {
196
+ if (intervals.length < 20) return false
197
+ const sorted = [...intervals].sort((a, b) => a - b)
198
+ const median = sorted[Math.floor(sorted.length / 2)]
199
+ return median > Math.max((2 * 1000) / Math.max(1, fps), 80)
200
+ }
201
+
202
+ /** A colour in 0–1 sRGB channels, as a shader takes it. */
203
+ export type Vec3 = [number, number, number]
204
+
205
+ /** Move `from` toward `to` by share `k` (0–1): theme changes blend over a few frames instead of snapping. */
206
+ export function approach(from: Vec3, to: Vec3, k: number): Vec3 {
207
+ return [from[0] + (to[0] - from[0]) * k, from[1] + (to[1] - from[1]) * k, from[2] + (to[2] - from[2]) * k]
208
+ }
@@ -0,0 +1,30 @@
1
+ import { onBeforeUnmount, ref } from "vue"
2
+
3
+ /*
4
+ * A slider whose value is changed from outside (a reset, a preset, data
5
+ * arriving) glides its thumb and range to the new place instead of jumping;
6
+ * while the user drags, they follow the pointer with no delay, since an
7
+ * eased thumb trailing the finger reads as lag. reka places both with inline
8
+ * `left`/`right`, so the transition goes on those.
9
+ */
10
+ export const SLIDER_GLIDE =
11
+ "[&_[style*=left]]:transition-[left,right] [&_[style*=left]]:duration-(--duration-medium) [&_[style*=left]]:ease-(--ease-emphasized) data-[dragging]:[&_[style*=left]]:transition-none motion-reduce:[&_[style*=left]]:transition-none"
12
+
13
+ export function useSliderDrag() {
14
+ const dragging = ref(false)
15
+ let view: Window | null = null
16
+ const end = () => {
17
+ dragging.value = false
18
+ view?.removeEventListener("pointerup", end)
19
+ view?.removeEventListener("pointercancel", end)
20
+ view = null
21
+ }
22
+ function onPointerDown(event: PointerEvent) {
23
+ dragging.value = true
24
+ view = (event.currentTarget as Element).ownerDocument.defaultView
25
+ view?.addEventListener("pointerup", end)
26
+ view?.addEventListener("pointercancel", end)
27
+ }
28
+ onBeforeUnmount(end)
29
+ return { dragging, onPointerDown }
30
+ }
@@ -0,0 +1,13 @@
1
+ import { Text, type Slot, type VNode } from "vue"
2
+
3
+ /*
4
+ * A slot's words when it holds nothing but text, else null. Components that
5
+ * animate a change of what they say (a badge rolling to a new status, an alert
6
+ * swapping its message) need to know when the words changed; anything richer
7
+ * (an icon, markup) is shown as given and only its box eases.
8
+ */
9
+ export function slotText(slot: Slot | undefined): string | null {
10
+ const nodes: VNode[] = slot?.() ?? []
11
+ if (!nodes.length || !nodes.every((node) => node.type === Text)) return null
12
+ return nodes.map((node) => String(node.children ?? "")).join("").trim() || null
13
+ }