@leavepulse/ui 0.69.0 → 0.70.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 (634) hide show
  1. package/README.md +37 -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/LpShinyText-D69cBYDR.css +61 -0
  11. package/dist/assets/LpStatus-430OGTZY.css +17 -0
  12. package/dist/assets/LpStepMark-Drvx33Vg.css +10 -0
  13. package/dist/assets/{LpTable-BPERfJIk.css → LpTable-DHxEQ_-3.css} +19 -19
  14. package/dist/assets/LpTabs-BJ-nrJuI.css +18 -0
  15. package/dist/assets/LpTextReveal-DjI1MsDS.css +35 -0
  16. package/dist/assets/LpTypewriter-CXAm9Nca.css +27 -0
  17. package/dist/chunks/AttentionDot-DCBf4aWg.js +25 -0
  18. package/dist/chunks/{ContextMenuItems-B7XFHksm.js → ContextMenuItems-B5uE8Znw.js} +1 -1
  19. package/dist/chunks/{DropzoneLinkField-CjkrpDqO.js → DropzoneLinkField-Bw8Flkhk.js} +2 -2
  20. package/dist/chunks/FieldWrapper-BiennROB.js +43 -0
  21. package/dist/chunks/{LayoutCanvas-DF9CovaK.js → LayoutCanvas-BsJIzhdd.js} +1 -1
  22. package/dist/chunks/{LayoutNode-D9bIFzk0.js → LayoutNode-DJVhkSGk.js} +6 -8
  23. package/dist/chunks/LegendSwatch-BhRjrtzv.js +18 -0
  24. package/dist/chunks/LoaderMark-C1dubiKn.js +85 -0
  25. package/dist/chunks/{LpActionBar-MPKMu3pz.js → LpActionBar-CzOFar5t.js} +14 -20
  26. package/dist/chunks/LpActivityLine-CJvzrNpj.js +95 -0
  27. package/dist/chunks/{LpAddressInput-DG0BTqdH.js → LpAddressInput-CNnLTDZg.js} +7 -7
  28. package/dist/chunks/LpAlert-C5igj802.js +119 -0
  29. package/dist/chunks/{LpAppShell-DPh1756q.js → LpAppShell-CEUAB8kK.js} +15 -16
  30. package/dist/chunks/{LpAutocomplete-Dgn6hw3p.js → LpAutocomplete-D92niDHE.js} +15 -32
  31. package/dist/chunks/{LpAvatar-Bs0Fb1lC.js → LpAvatar-CLPl-qOF.js} +18 -6
  32. package/dist/chunks/LpBadge-HOEPBYmi.js +126 -0
  33. package/dist/chunks/LpBreadcrumbs-DYoPc3P4.js +144 -0
  34. package/dist/chunks/{LpButton-DNR-YAq5.js → LpButton-CVxBfGjd.js} +12 -4
  35. package/dist/chunks/{LpCalendar-BLya39Fh.js → LpCalendar-pVL91-Pl.js} +1 -1
  36. package/dist/chunks/{LpCard-Bf71NXZq.js → LpCard-CqKLnEhl.js} +18 -8
  37. package/dist/chunks/{LpCheckbox-Da5oZPHo.js → LpCheckbox-DHc2bHDe.js} +9 -6
  38. package/dist/chunks/{LpCodeBlock-lLbmslfs.js → LpCodeBlock-BGsQhxw8.js} +24 -32
  39. package/dist/chunks/LpCodeDisplay-CIKKT2iW.js +189 -0
  40. package/dist/chunks/LpCollapse-CWS9M_lm.js +115 -0
  41. package/dist/chunks/{LpColorPicker-DtEfBAcK.js → LpColorPicker-DFugXNUi.js} +10 -5
  42. package/dist/chunks/{LpConfirmDialog-D7s4jakB.js → LpConfirmDialog-CfGCux8m.js} +3 -3
  43. package/dist/chunks/{LpConfirmHost-DrYI5y1K.js → LpConfirmHost-exB7JmcO.js} +1 -1
  44. package/dist/chunks/{LpContextMenu-CRnjoqSt.js → LpContextMenu-COwOwRgI.js} +13 -5
  45. package/dist/chunks/LpCopyButton-DzHjnFs0.js +133 -0
  46. package/dist/chunks/LpCountdown-Dtq1ihlJ.js +231 -0
  47. package/dist/chunks/LpDataList-B-5nHfNw.js +120 -0
  48. package/dist/chunks/LpDatePicker-BUe1VUC0.js +168 -0
  49. package/dist/chunks/LpDateRangePicker-C_k65vGR.js +126 -0
  50. package/dist/chunks/{LpDisclosure-6AplYBub.js → LpDisclosure-DOarzlAG.js} +22 -11
  51. package/dist/chunks/{LpDrawer-Q4SOxM4s.js → LpDrawer-DGNKRsGq.js} +9 -4
  52. package/dist/chunks/{LpDropdownMenu-DWTt0ooM.js → LpDropdownMenu-CRgrhdAH.js} +10 -5
  53. package/dist/chunks/LpEditable-DTqLt2lT.js +236 -0
  54. package/dist/chunks/{LpEmptyState-om1UnFjV.js → LpEmptyState-KTKipws2.js} +1 -1
  55. package/dist/chunks/{LpFieldNotice-CHuDeLjc.js → LpFieldNotice-CurbNuXI.js} +1 -1
  56. package/dist/chunks/{LpFileTree-CW24YsVX.js → LpFileTree-DMISXVHC.js} +5 -3
  57. package/dist/chunks/{LpFileTreeNode-BXhTHaKW.js → LpFileTreeNode-CPXB_Yy3.js} +42 -55
  58. package/dist/chunks/{LpFormField-DPb47HEm.js → LpFormField-BWwgm1E4.js} +23 -9
  59. package/dist/chunks/LpFrame-BVQCQLDI.js +155 -0
  60. package/dist/chunks/LpGauge-Bk8TwJj0.js +204 -0
  61. package/dist/chunks/LpGlowBorder-B8V-QzIl.js +40 -0
  62. package/dist/chunks/{LpHintExample-C1eKYdeL.js → LpHintExample-9Py0wc-L.js} +11 -34
  63. package/dist/chunks/{LpHintLayer-7UOdWQG3.js → LpHintLayer-p4hqzdyA.js} +27 -14
  64. package/dist/chunks/{LpHints-BUtBp9-w.js → LpHints-CCrhRfy9.js} +3 -3
  65. package/dist/chunks/LpHoverCard-spGxyw7T.js +130 -0
  66. package/dist/chunks/{LpIcon-6tSrZ3Zy.js → LpIcon-HJP5xMz4.js} +1 -1
  67. package/dist/chunks/LpIdenticon-BzdC_qz9.js +63 -0
  68. package/dist/chunks/{LpInfraNode-Ds_8SZgN.js → LpInfraNode-CUC3t4na.js} +39 -25
  69. package/dist/chunks/{LpInput-CUyr6PIE.js → LpInput-CDuwODJG.js} +44 -18
  70. package/dist/chunks/{LpLightbox-B9J8rzsj.js → LpLightbox-XJ0LpTcZ.js} +69 -69
  71. package/dist/chunks/LpLoader-DjJEX8hL.js +92 -0
  72. package/dist/chunks/{LpLogViewer-4QFc-sVN.js → LpLogViewer-elZHbj85.js} +225 -129
  73. package/dist/chunks/LpMagnetic-CtXKLa2Y.js +62 -0
  74. package/dist/chunks/{LpMapPicker-BTbkFeSG.js → LpMapPicker-BtIl7NOM.js} +34 -31
  75. package/dist/chunks/LpMasonry--k0571qG.js +185 -0
  76. package/dist/chunks/{LpMediaGallery-CuPcscx3.js → LpMediaGallery-D5swlmZ_.js} +3 -3
  77. package/dist/chunks/{LpModal-hk1noDcr.js → LpModal-eBMt2Fqn.js} +8 -3
  78. package/dist/chunks/{LpModalNav-DQd2keZm.js → LpModalNav-D4Z0wVHR.js} +4 -4
  79. package/dist/chunks/{LpNotificationBell-X84PJIxD.js → LpNotificationBell-DPZmP5dM.js} +32 -48
  80. package/dist/chunks/LpNumberField-B9k-h2HE.js +140 -0
  81. package/dist/chunks/{LpNumberFlow-z4WPZcZu.js → LpNumberFlow-Cfyh033Q.js} +8 -28
  82. package/dist/chunks/{LpOtpInput-PbeNJ5In.js → LpOtpInput-CeBPsM7D.js} +2 -1
  83. package/dist/chunks/{LpPagination-Bx-VG4Ag.js → LpPagination-VNCX6pFC.js} +37 -6
  84. package/dist/chunks/{LpPalettePreview-CFav_7co.js → LpPalettePreview-BNB8T_MY.js} +1 -1
  85. package/dist/chunks/{LpPasswordInput-CbnXitAS.js → LpPasswordInput-DTV18FIh.js} +20 -20
  86. package/dist/chunks/{LpPhoneInput-DP69HONS.js → LpPhoneInput-C60tx1tC.js} +12 -7
  87. package/dist/chunks/{LpPie-yLF9-aDR.js → LpPie-CFuV7pZv.js} +158 -47
  88. package/dist/chunks/LpPipeline-D81yh_NX.js +329 -0
  89. package/dist/chunks/{LpPopover-DVnkHaOu.js → LpPopover-BEIcqb9p.js} +8 -3
  90. package/dist/chunks/{LpProgress-CA86wxXu.js → LpProgress-CE-m5OpD.js} +23 -63
  91. package/dist/chunks/LpQuota-2Sw_gqcJ.js +162 -0
  92. package/dist/chunks/{LpRadioGroup-8S5hf8lE.js → LpRadioGroup-CGBr5nIp.js} +1 -1
  93. package/dist/chunks/{LpRangeCalendar-ByQi-WUz.js → LpRangeCalendar-c5qj7YcC.js} +1 -1
  94. package/dist/chunks/{LpRangeSlider-C1yF9-nP.js → LpRangeSlider-Dv6lNvHY.js} +6 -2
  95. package/dist/chunks/LpRelativeTime-CKzVqAo9.js +73 -0
  96. package/dist/chunks/LpRepeater-D0VjmD6y.js +140 -0
  97. package/dist/chunks/LpRevealOnScroll-DcQjexLy.js +76 -0
  98. package/dist/chunks/LpScramble-TDO5Kdou.js +85 -0
  99. package/dist/chunks/LpSecretField-DMfdJ1cQ.js +307 -0
  100. package/dist/chunks/{LpSection-BmRXe6ZP.js → LpSection-BW4xJ-Li.js} +1 -1
  101. package/dist/chunks/{LpSegmented-CW3bFHM4.js → LpSegmented-BO99Sr4m.js} +11 -14
  102. package/dist/chunks/{LpSelect-BCyDYoih.js → LpSelect-B4f_yRi5.js} +93 -108
  103. package/dist/chunks/LpShaderBackdrop-Bote0iuy.js +332 -0
  104. package/dist/chunks/LpShinyText-DX2ajtWq.js +48 -0
  105. package/dist/chunks/{LpSidebar-DfIiS-lB.js → LpSidebar-D-M0Y_7U.js} +5 -3
  106. package/dist/chunks/{LpSidebarNav-C5FDNI0t.js → LpSidebarNav-DF-TYxOV.js} +3 -3
  107. package/dist/chunks/{LpSlider-Cuzmrd7p.js → LpSlider-DuoRpOrH.js} +6 -2
  108. package/dist/chunks/{LpSparkline-DXT4KrSi.js → LpSparkline-CExIRDb3.js} +32 -27
  109. package/dist/chunks/LpSpotlight-4F3NeUMY.js +41 -0
  110. package/dist/chunks/{LpStat-iBMF5QNk.js → LpStat-B2ix1Q7t.js} +38 -35
  111. package/dist/chunks/LpStatus-C7kx5PKE.js +86 -0
  112. package/dist/chunks/LpStepMark-DK-5BKAt.js +101 -0
  113. package/dist/chunks/LpStepper-DRFfdJaq.js +76 -0
  114. package/dist/chunks/{LpSwap-D82jiecV.js → LpSwap-DzE6M3Mg.js} +1 -1
  115. package/dist/chunks/LpSwitch-DrzKrZZV.js +112 -0
  116. package/dist/chunks/{LpTableFilter-CuIbgdW0.js → LpTableFilter-DyvHGbOk.js} +3 -2
  117. package/dist/chunks/{LpTableOfContents-C_4VBktT.js → LpTableOfContents-rYvo8z5T.js} +34 -21
  118. package/dist/chunks/{LpTagsInput-DiXrpYeE.js → LpTagsInput-DgpBFRV2.js} +68 -71
  119. package/dist/chunks/LpTextReveal-CtAnOUkp.js +140 -0
  120. package/dist/chunks/{LpTextarea-xoQd5Lnp.js → LpTextarea-DiWjeVoJ.js} +12 -11
  121. package/dist/chunks/{LpThemeProvider-CQ2jC9TA.js → LpThemeProvider-DcbfOxsU.js} +1 -1
  122. package/dist/chunks/LpThemeStudio-DguwHdGt.js +416 -0
  123. package/dist/chunks/LpThemeStudioSection-BwhqYDLf.js +20 -0
  124. package/dist/chunks/LpThemeSwitcher-D5uz0MhK.js +278 -0
  125. package/dist/chunks/{LpTimeField-MFgAsNc5.js → LpTimeField-BFQVf_PW.js} +13 -5
  126. package/dist/chunks/{LpToaster-C4zE9zB7.js → LpToaster-Wv9ksj4q.js} +49 -82
  127. package/dist/chunks/{LpTooltip-suFCM34F.js → LpTooltip-Cz8SQwRt.js} +8 -3
  128. package/dist/chunks/{LpTopologyCanvas-6ozNrQKh.js → LpTopologyCanvas-ZhKthfgz.js} +1 -1
  129. package/dist/chunks/{LpTour-BUp3Jt2e.js → LpTour-NfRJNEIP.js} +24 -18
  130. package/dist/chunks/LpTruncate-D_5Pfv5B.js +112 -0
  131. package/dist/chunks/LpTypewriter-BCL_a6wE.js +159 -0
  132. package/dist/chunks/{LpUptimeBar-B6kHw4Cj.js → LpUptimeBar-DWegV2p6.js} +9 -8
  133. package/dist/chunks/{LpVideoBackdrop-qWRYBOTQ.js → LpVideoBackdrop-DS1wGv-X.js} +7 -8
  134. package/dist/chunks/{MenuTitle-obgek0B7.js → MenuTitle-BjQZX8tQ.js} +1 -1
  135. package/dist/chunks/OptionContent-CmYUUeWj.js +40 -0
  136. package/dist/chunks/PickerField-CHRS1h7K.js +116 -0
  137. package/dist/chunks/RevealToggle-C-KzaQGJ.js +65 -0
  138. package/dist/chunks/StepConnector-37OgEOXA.js +41 -0
  139. package/dist/chunks/TourCard-BXuVHsfn.js +123 -0
  140. package/dist/chunks/backdrop-Dkf2vM81.js +59 -0
  141. package/dist/chunks/{icons-DVhZ_plB.js → icons-4IywzkHk.js} +1 -1
  142. package/dist/chunks/{useTheme-2qHG8NEU.js → useTheme-BOV_-AiY.js} +9 -65
  143. package/dist/component-names.d.ts +1 -1
  144. package/dist/components/AttentionDot.vue.d.ts +9 -0
  145. package/dist/components/AttentionDot.vue.js +4 -0
  146. package/dist/components/ContextMenuItems.vue.js +1 -1
  147. package/dist/components/DropzoneLinkField.vue.js +1 -1
  148. package/dist/components/FieldWrapper.vue.d.ts +25 -0
  149. package/dist/components/FieldWrapper.vue.js +4 -0
  150. package/dist/components/LayoutCanvas.vue.js +1 -1
  151. package/dist/components/LayoutNode.vue.d.ts +4 -4
  152. package/dist/components/LayoutNode.vue.js +1 -1
  153. package/dist/components/LegendSwatch.vue.d.ts +6 -0
  154. package/dist/components/LegendSwatch.vue.js +4 -0
  155. package/dist/components/LoaderMark.vue.d.ts +12 -0
  156. package/dist/components/LoaderMark.vue.js +4 -0
  157. package/dist/components/LpActionBar.vue.js +1 -1
  158. package/dist/components/LpActivityLine.vue.d.ts +58 -0
  159. package/dist/components/LpActivityLine.vue.js +4 -0
  160. package/dist/components/LpAddressInput.vue.js +1 -1
  161. package/dist/components/LpAlert.vue.d.ts +2 -2
  162. package/dist/components/LpAlert.vue.js +1 -1
  163. package/dist/components/LpAppShell.vue.d.ts +8 -7
  164. package/dist/components/LpAppShell.vue.js +1 -1
  165. package/dist/components/LpAutocomplete.vue.js +1 -1
  166. package/dist/components/LpAvatar.vue.d.ts +16 -0
  167. package/dist/components/LpAvatar.vue.js +1 -1
  168. package/dist/components/LpBadge.vue.d.ts +19 -5
  169. package/dist/components/LpBadge.vue.js +1 -1
  170. package/dist/components/LpBreadcrumbs.vue.js +1 -1
  171. package/dist/components/LpButton.vue.d.ts +2 -2
  172. package/dist/components/LpButton.vue.js +1 -1
  173. package/dist/components/LpCalendar.vue.js +1 -1
  174. package/dist/components/LpCard.vue.d.ts +24 -0
  175. package/dist/components/LpCard.vue.js +1 -1
  176. package/dist/components/LpChart.vue.js +107 -138
  177. package/dist/components/LpCheckbox.vue.d.ts +2 -2
  178. package/dist/components/LpCheckbox.vue.js +1 -1
  179. package/dist/components/LpCodeBlock.vue.js +1 -1
  180. package/dist/components/LpCodeDisplay.vue.d.ts +89 -0
  181. package/dist/components/LpCodeDisplay.vue.js +4 -0
  182. package/dist/components/LpCollapse.vue.d.ts +58 -0
  183. package/dist/components/LpCollapse.vue.js +4 -0
  184. package/dist/components/LpColorPicker.vue.d.ts +2 -2
  185. package/dist/components/LpColorPicker.vue.js +1 -1
  186. package/dist/components/LpCommandPalette.vue.js +34 -23
  187. package/dist/components/LpConfirmDialog.vue.js +1 -1
  188. package/dist/components/LpConfirmHost.vue.js +1 -1
  189. package/dist/components/LpContextMenu.vue.js +1 -1
  190. package/dist/components/LpCopyButton.vue.d.ts +82 -0
  191. package/dist/components/LpCopyButton.vue.js +4 -0
  192. package/dist/components/LpCountdown.vue.d.ts +100 -0
  193. package/dist/components/LpCountdown.vue.js +4 -0
  194. package/dist/components/LpDataList.vue.d.ts +66 -0
  195. package/dist/components/LpDataList.vue.js +4 -0
  196. package/dist/components/LpDatePicker.vue.d.ts +1 -1
  197. package/dist/components/LpDatePicker.vue.js +1 -1
  198. package/dist/components/LpDateRangePicker.vue.d.ts +1 -1
  199. package/dist/components/LpDateRangePicker.vue.js +1 -1
  200. package/dist/components/LpDisclosure.vue.js +1 -1
  201. package/dist/components/LpDock.vue.d.ts +74 -0
  202. package/dist/components/LpDock.vue.js +204 -0
  203. package/dist/components/LpDrawer.vue.js +1 -1
  204. package/dist/components/LpDropdownMenu.vue.js +1 -1
  205. package/dist/components/LpDropzone.vue.d.ts +8 -1
  206. package/dist/components/LpDropzone.vue.js +36 -15
  207. package/dist/components/LpEditable.vue.d.ts +85 -0
  208. package/dist/components/LpEditable.vue.js +4 -0
  209. package/dist/components/LpEmptyState.vue.js +1 -1
  210. package/dist/components/LpFieldNotice.vue.js +1 -1
  211. package/dist/components/LpFileTree.vue.js +1 -1
  212. package/dist/components/LpFileTreeNode.vue.js +1 -1
  213. package/dist/components/LpFormField.vue.js +1 -1
  214. package/dist/components/LpFrame.vue.d.ts +22 -0
  215. package/dist/components/LpFrame.vue.js +4 -0
  216. package/dist/components/LpGauge.vue.d.ts +116 -0
  217. package/dist/components/LpGauge.vue.js +4 -0
  218. package/dist/components/LpGlowBorder.vue.d.ts +47 -0
  219. package/dist/components/LpGlowBorder.vue.js +4 -0
  220. package/dist/components/LpHintExample.vue.d.ts +2 -2
  221. package/dist/components/LpHintExample.vue.js +1 -1
  222. package/dist/components/LpHintLayer.vue.js +1 -1
  223. package/dist/components/LpHints.vue.js +1 -1
  224. package/dist/components/LpHoverCard.vue.d.ts +60 -0
  225. package/dist/components/LpHoverCard.vue.js +4 -0
  226. package/dist/components/LpIcon.vue.js +1 -1
  227. package/dist/components/LpIdenticon.vue.d.ts +37 -0
  228. package/dist/components/LpIdenticon.vue.js +4 -0
  229. package/dist/components/LpImageEditor.vue.js +13 -19
  230. package/dist/components/LpInfraNode.vue.js +1 -1
  231. package/dist/components/LpInput.vue.d.ts +10 -0
  232. package/dist/components/LpInput.vue.js +1 -1
  233. package/dist/components/LpLightbox.vue.js +1 -1
  234. package/dist/components/LpLoader.vue.d.ts +106 -0
  235. package/dist/components/LpLoader.vue.js +4 -0
  236. package/dist/components/LpLogViewer.vue.js +1 -1
  237. package/dist/components/LpMagnetic.vue.d.ts +28 -0
  238. package/dist/components/LpMagnetic.vue.js +4 -0
  239. package/dist/components/LpMapPicker.vue.js +1 -1
  240. package/dist/components/LpMasonry.vue.d.ts +42 -0
  241. package/dist/components/LpMasonry.vue.js +4 -0
  242. package/dist/components/LpMediaGallery.vue.js +1 -1
  243. package/dist/components/LpModal.vue.js +1 -1
  244. package/dist/components/LpModalNav.vue.js +1 -1
  245. package/dist/components/LpNotificationBell.vue.d.ts +15 -6
  246. package/dist/components/LpNotificationBell.vue.js +1 -1
  247. package/dist/components/LpNumberField.vue.d.ts +57 -0
  248. package/dist/components/LpNumberField.vue.js +1 -1
  249. package/dist/components/LpNumberFlow.vue.js +1 -1
  250. package/dist/components/LpOtpInput.vue.d.ts +12 -4
  251. package/dist/components/LpOtpInput.vue.js +1 -1
  252. package/dist/components/LpPagination.vue.js +1 -1
  253. package/dist/components/LpPaletteFacts.vue.js +1 -1
  254. package/dist/components/LpPaletteHeader.vue.js +2 -2
  255. package/dist/components/LpPalettePreview.vue.js +1 -1
  256. package/dist/components/LpPaletteStats.vue.js +1 -1
  257. package/dist/components/LpPasswordInput.vue.d.ts +12 -1
  258. package/dist/components/LpPasswordInput.vue.js +1 -1
  259. package/dist/components/LpPhoneInput.vue.js +1 -1
  260. package/dist/components/LpPie.vue.d.ts +3 -1
  261. package/dist/components/LpPie.vue.js +1 -1
  262. package/dist/components/LpPipeline.vue.d.ts +91 -0
  263. package/dist/components/LpPipeline.vue.js +4 -0
  264. package/dist/components/LpPopover.vue.d.ts +2 -2
  265. package/dist/components/LpPopover.vue.js +1 -1
  266. package/dist/components/LpProgress.vue.d.ts +11 -4
  267. package/dist/components/LpProgress.vue.js +1 -1
  268. package/dist/components/LpQuota.vue.d.ts +68 -0
  269. package/dist/components/LpQuota.vue.js +4 -0
  270. package/dist/components/LpRadioGroup.vue.js +1 -1
  271. package/dist/components/LpRangeCalendar.vue.js +1 -1
  272. package/dist/components/LpRangeSlider.vue.js +1 -1
  273. package/dist/components/LpRelativeTime.vue.d.ts +41 -0
  274. package/dist/components/LpRelativeTime.vue.js +4 -0
  275. package/dist/components/LpRepeater.vue.d.ts +48 -0
  276. package/dist/components/LpRepeater.vue.js +4 -0
  277. package/dist/components/LpRevealOnScroll.vue.d.ts +67 -0
  278. package/dist/components/LpRevealOnScroll.vue.js +4 -0
  279. package/dist/components/LpRollingText.vue.d.ts +1 -1
  280. package/dist/components/LpScramble.vue.d.ts +55 -0
  281. package/dist/components/LpScramble.vue.js +4 -0
  282. package/dist/components/LpScrollArea.vue.d.ts +1 -1
  283. package/dist/components/LpSecretField.vue.d.ts +108 -0
  284. package/dist/components/LpSecretField.vue.js +4 -0
  285. package/dist/components/LpSection.vue.js +1 -1
  286. package/dist/components/LpSegmented.vue.js +1 -1
  287. package/dist/components/LpSelect.vue.d.ts +2 -0
  288. package/dist/components/LpSelect.vue.js +1 -1
  289. package/dist/components/LpShaderBackdrop.vue.d.ts +39 -0
  290. package/dist/components/LpShaderBackdrop.vue.js +4 -0
  291. package/dist/components/LpShinyText.vue.d.ts +39 -0
  292. package/dist/components/LpShinyText.vue.js +4 -0
  293. package/dist/components/LpShortcutsDialog.vue.js +1 -1
  294. package/dist/components/LpSidebar.vue.js +1 -1
  295. package/dist/components/LpSidebarNav.vue.js +1 -1
  296. package/dist/components/LpSlider.vue.js +1 -1
  297. package/dist/components/LpSparkline.vue.d.ts +4 -3
  298. package/dist/components/LpSparkline.vue.js +1 -1
  299. package/dist/components/LpSpotlight.vue.d.ts +37 -0
  300. package/dist/components/LpSpotlight.vue.js +4 -0
  301. package/dist/components/LpStat.vue.js +1 -1
  302. package/dist/components/LpStatus.vue.d.ts +65 -0
  303. package/dist/components/LpStatus.vue.js +4 -0
  304. package/dist/components/LpStepMark.vue.d.ts +31 -0
  305. package/dist/components/LpStepMark.vue.js +4 -0
  306. package/dist/components/LpStepper.vue.js +1 -1
  307. package/dist/components/LpSwap.vue.js +1 -1
  308. package/dist/components/LpSwitch.vue.d.ts +2 -2
  309. package/dist/components/LpSwitch.vue.js +1 -1
  310. package/dist/components/LpTable.vue.js +104 -120
  311. package/dist/components/LpTableFilter.vue.js +1 -1
  312. package/dist/components/LpTableOfContents.vue.d.ts +6 -1
  313. package/dist/components/LpTableOfContents.vue.js +1 -1
  314. package/dist/components/LpTabs.vue.d.ts +5 -5
  315. package/dist/components/LpTabs.vue.js +25 -33
  316. package/dist/components/LpTagsInput.vue.js +1 -1
  317. package/dist/components/LpTextReveal.vue.d.ts +58 -0
  318. package/dist/components/LpTextReveal.vue.js +4 -0
  319. package/dist/components/LpTextarea.vue.js +1 -1
  320. package/dist/components/LpThemeProvider.vue.js +1 -1
  321. package/dist/components/LpThemeStudio.vue.d.ts +54 -0
  322. package/dist/components/LpThemeStudio.vue.js +4 -0
  323. package/dist/components/LpThemeStudioSection.vue.d.ts +16 -0
  324. package/dist/components/LpThemeStudioSection.vue.js +4 -0
  325. package/dist/components/LpThemeSwitcher.vue.d.ts +32 -1
  326. package/dist/components/LpThemeSwitcher.vue.js +1 -1
  327. package/dist/components/LpTimeField.vue.d.ts +2 -0
  328. package/dist/components/LpTimeField.vue.js +1 -1
  329. package/dist/components/LpToaster.vue.js +1 -1
  330. package/dist/components/LpTooltip.vue.d.ts +2 -2
  331. package/dist/components/LpTooltip.vue.js +1 -1
  332. package/dist/components/LpTopologyCanvas.vue.js +1 -1
  333. package/dist/components/LpTour.vue.js +1 -1
  334. package/dist/components/LpTruncate.vue.d.ts +34 -0
  335. package/dist/components/LpTruncate.vue.js +4 -0
  336. package/dist/components/LpTypewriter.vue.d.ts +61 -0
  337. package/dist/components/LpTypewriter.vue.js +4 -0
  338. package/dist/components/LpUptimeBar.vue.js +1 -1
  339. package/dist/components/LpVideoBackdrop.vue.js +1 -1
  340. package/dist/components/MenuTitle.vue.js +1 -1
  341. package/dist/components/OptionContent.vue.d.ts +7 -0
  342. package/dist/components/OptionContent.vue.js +4 -0
  343. package/dist/components/PickerField.vue.d.ts +43 -0
  344. package/dist/components/PickerField.vue.js +4 -0
  345. package/dist/components/RevealToggle.vue.d.ts +21 -0
  346. package/dist/components/RevealToggle.vue.js +4 -0
  347. package/dist/components/StepConnector.vue.d.ts +19 -0
  348. package/dist/components/StepConnector.vue.js +4 -0
  349. package/dist/components/TourCard.vue.js +1 -1
  350. package/dist/components/badge.d.ts +1 -0
  351. package/dist/components/badge.js +4 -0
  352. package/dist/components/chart.d.ts +5 -1
  353. package/dist/components/chart.js +2 -21
  354. package/dist/components/dateRange.d.ts +11 -0
  355. package/dist/components/dateRange.js +12 -0
  356. package/dist/components/dock.d.ts +19 -0
  357. package/dist/components/dock.js +16 -0
  358. package/dist/components/dropdown.d.ts +10 -1
  359. package/dist/components/dropdown.js +7 -2
  360. package/dist/components/fieldText.d.ts +38 -0
  361. package/dist/components/fieldText.js +53 -0
  362. package/dist/components/fileTree.d.ts +7 -3
  363. package/dist/components/fileTree.js +12 -18
  364. package/dist/components/format.d.ts +41 -0
  365. package/dist/components/format.js +67 -0
  366. package/dist/components/identicon.d.ts +38 -0
  367. package/dist/components/identicon.js +85 -0
  368. package/dist/components/masonry.d.ts +32 -0
  369. package/dist/components/masonry.js +40 -0
  370. package/dist/components/menuWhen.js +1 -1
  371. package/dist/components/meter.d.ts +64 -0
  372. package/dist/components/meter.js +53 -0
  373. package/dist/components/otp.d.ts +1 -1
  374. package/dist/components/otp.js +7 -2
  375. package/dist/components/pipeline.d.ts +55 -0
  376. package/dist/components/pipeline.js +51 -0
  377. package/dist/components/quota.d.ts +57 -0
  378. package/dist/components/quota.js +40 -0
  379. package/dist/components/relativeTime.d.ts +24 -0
  380. package/dist/components/relativeTime.js +56 -0
  381. package/dist/components/reveal.d.ts +18 -0
  382. package/dist/components/reveal.js +32 -0
  383. package/dist/components/scramble.d.ts +68 -0
  384. package/dist/components/scramble.js +58 -0
  385. package/dist/components/shaderBackdrop.d.ts +20 -0
  386. package/dist/components/shaderBackdrop.js +156 -0
  387. package/dist/components/slider.d.ts +5 -0
  388. package/dist/components/slider.js +24 -0
  389. package/dist/components/slotText.d.ts +2 -0
  390. package/dist/components/slotText.js +9 -0
  391. package/dist/components/status.d.ts +14 -0
  392. package/dist/components/status.js +22 -0
  393. package/dist/components/tableFacets.d.ts +8 -0
  394. package/dist/components/tableFacets.js +19 -0
  395. package/dist/components/textSplit.d.ts +17 -0
  396. package/dist/components/textSplit.js +24 -0
  397. package/dist/components/timeSpan.d.ts +78 -0
  398. package/dist/components/timeSpan.js +85 -0
  399. package/dist/components/tone.d.ts +43 -0
  400. package/dist/components/tone.js +82 -0
  401. package/dist/components/tour.js +3 -2
  402. package/dist/components/typewriter.d.ts +36 -0
  403. package/dist/components/typewriter.js +53 -0
  404. package/dist/components/videoBackdrop.js +5 -6
  405. package/dist/composables/easing.d.ts +22 -1
  406. package/dist/composables/easing.js +42 -1
  407. package/dist/composables/pseudoLocale.d.ts +15 -0
  408. package/dist/composables/pseudoLocale.js +96 -0
  409. package/dist/composables/scroll.js +5 -3
  410. package/dist/composables/useClipboard.d.ts +4 -1
  411. package/dist/composables/useClipboard.js +12 -2
  412. package/dist/composables/useField.d.ts +8 -0
  413. package/dist/composables/useField.js +5 -0
  414. package/dist/composables/useForm.js +19 -5
  415. package/dist/composables/useInView.d.ts +11 -0
  416. package/dist/composables/useInView.js +39 -0
  417. package/dist/composables/useLoadingGate.d.ts +8 -0
  418. package/dist/composables/useLoadingGate.js +38 -0
  419. package/dist/composables/useLpConfig.d.ts +10 -0
  420. package/dist/composables/useLpConfig.js +11 -1
  421. package/dist/composables/useMediaQuery.js +3 -1
  422. package/dist/composables/useNow.d.ts +16 -0
  423. package/dist/composables/useNow.js +39 -0
  424. package/dist/composables/usePersisted.js +14 -7
  425. package/dist/composables/usePillTransition.d.ts +5 -2
  426. package/dist/composables/usePillTransition.js +4 -3
  427. package/dist/composables/usePointerFrame.d.ts +18 -0
  428. package/dist/composables/usePointerFrame.js +31 -0
  429. package/dist/composables/useReducedMotion.d.ts +2 -0
  430. package/dist/composables/useReducedMotion.js +7 -0
  431. package/dist/composables/useRepeater.d.ts +17 -0
  432. package/dist/composables/useRepeater.js +31 -0
  433. package/dist/composables/useResizeObserver.d.ts +8 -0
  434. package/dist/composables/useResizeObserver.js +33 -0
  435. package/dist/composables/useRevealedValue.d.ts +31 -0
  436. package/dist/composables/useRevealedValue.js +26 -0
  437. package/dist/composables/useScramble.d.ts +42 -0
  438. package/dist/composables/useScramble.js +76 -0
  439. package/dist/composables/useShift.js +2 -10
  440. package/dist/composables/useSpotlight.d.ts +18 -0
  441. package/dist/composables/useSpotlight.js +53 -0
  442. package/dist/composables/useTilt.js +26 -18
  443. package/dist/composables/useTween.d.ts +43 -0
  444. package/dist/composables/useTween.js +69 -0
  445. package/dist/composables/useView.d.ts +18 -0
  446. package/dist/composables/useView.js +61 -0
  447. package/dist/icons-all.js +1 -1
  448. package/dist/index.d.ts +98 -1
  449. package/dist/index.js +262 -104
  450. package/dist/theme/define.d.ts +12 -0
  451. package/dist/theme/studio.d.ts +90 -0
  452. package/package.json +5 -2
  453. package/src/component-names.ts +33 -0
  454. package/src/components/AttentionDot.vue +27 -0
  455. package/src/components/FieldWrapper.vue +38 -0
  456. package/src/components/LayoutNode.vue +5 -9
  457. package/src/components/LegendSwatch.vue +15 -0
  458. package/src/components/LoaderMark.vue +90 -0
  459. package/src/components/LpActionBar.vue +12 -20
  460. package/src/components/LpActivityLine.vue +186 -0
  461. package/src/components/LpAlert.vue +54 -24
  462. package/src/components/LpAppShell.vue +7 -11
  463. package/src/components/LpAutocomplete.vue +16 -14
  464. package/src/components/LpAvatar.vue +17 -2
  465. package/src/components/LpBadge.vue +77 -13
  466. package/src/components/LpBreadcrumbs.vue +101 -35
  467. package/src/components/LpButton.vue +15 -3
  468. package/src/components/LpCard.vue +30 -7
  469. package/src/components/LpChart.vue +143 -154
  470. package/src/components/LpCheckbox.vue +10 -4
  471. package/src/components/LpCodeBlock.vue +11 -19
  472. package/src/components/LpCodeDisplay.vue +228 -0
  473. package/src/components/LpCollapse.vue +162 -0
  474. package/src/components/LpColorPicker.vue +10 -1
  475. package/src/components/LpCommandPalette.vue +38 -24
  476. package/src/components/LpContextMenu.vue +10 -1
  477. package/src/components/LpCopyButton.vue +185 -0
  478. package/src/components/LpCountdown.vue +308 -0
  479. package/src/components/LpDataList.vue +154 -0
  480. package/src/components/LpDatePicker.vue +53 -104
  481. package/src/components/LpDateRangePicker.vue +49 -95
  482. package/src/components/LpDisclosure.vue +58 -36
  483. package/src/components/LpDock.vue +274 -0
  484. package/src/components/LpDrawer.vue +10 -1
  485. package/src/components/LpDropdownMenu.vue +10 -1
  486. package/src/components/LpDropzone.vue +34 -13
  487. package/src/components/LpEditable.vue +293 -0
  488. package/src/components/LpFileTree.vue +5 -1
  489. package/src/components/LpFileTreeNode.vue +25 -50
  490. package/src/components/LpFormField.vue +22 -3
  491. package/src/components/LpFrame.vue +200 -0
  492. package/src/components/LpGauge.vue +287 -0
  493. package/src/components/LpGlowBorder.vue +60 -0
  494. package/src/components/LpHintExample.vue +9 -32
  495. package/src/components/LpHintLayer.vue +13 -3
  496. package/src/components/LpHoverCard.vue +175 -0
  497. package/src/components/LpIdenticon.vue +69 -0
  498. package/src/components/LpImageEditor.vue +15 -18
  499. package/src/components/LpInfraNode.vue +27 -16
  500. package/src/components/LpInput.vue +30 -16
  501. package/src/components/LpLightbox.vue +44 -23
  502. package/src/components/LpLoader.vue +145 -0
  503. package/src/components/LpLogViewer.vue +311 -82
  504. package/src/components/LpMagnetic.vue +97 -0
  505. package/src/components/LpMapPicker.vue +26 -28
  506. package/src/components/LpMasonry.vue +247 -0
  507. package/src/components/LpModal.vue +10 -1
  508. package/src/components/LpNotificationBell.vue +33 -49
  509. package/src/components/LpNumberField.vue +79 -14
  510. package/src/components/LpNumberFlow.vue +10 -35
  511. package/src/components/LpOtpInput.vue +7 -2
  512. package/src/components/LpPagination.vue +38 -4
  513. package/src/components/LpPalettePreview.vue +2 -7
  514. package/src/components/LpPasswordInput.vue +16 -14
  515. package/src/components/LpPhoneInput.vue +12 -3
  516. package/src/components/LpPie.vue +197 -36
  517. package/src/components/LpPipeline.vue +366 -0
  518. package/src/components/LpPopover.vue +10 -1
  519. package/src/components/LpProgress.vue +22 -84
  520. package/src/components/LpQuota.vue +197 -0
  521. package/src/components/LpRangeSlider.vue +5 -1
  522. package/src/components/LpRelativeTime.vue +90 -0
  523. package/src/components/LpRepeater.vue +80 -0
  524. package/src/components/LpRevealOnScroll.vue +125 -0
  525. package/src/components/LpScramble.vue +107 -0
  526. package/src/components/LpSecretField.vue +313 -0
  527. package/src/components/LpSegmented.vue +3 -10
  528. package/src/components/LpSelect.vue +78 -64
  529. package/src/components/LpShaderBackdrop.vue +434 -0
  530. package/src/components/LpShinyText.vue +139 -0
  531. package/src/components/LpSidebar.vue +2 -0
  532. package/src/components/LpSlider.vue +5 -1
  533. package/src/components/LpSparkline.vue +55 -37
  534. package/src/components/LpSpotlight.vue +51 -0
  535. package/src/components/LpStat.vue +6 -2
  536. package/src/components/LpStatus.vue +127 -0
  537. package/src/components/LpStepMark.vue +138 -0
  538. package/src/components/LpStepper.vue +37 -43
  539. package/src/components/LpSwap.vue +2 -2
  540. package/src/components/LpSwitch.vue +15 -22
  541. package/src/components/LpTable.vue +47 -63
  542. package/src/components/LpTableFilter.vue +1 -0
  543. package/src/components/LpTableOfContents.vue +53 -29
  544. package/src/components/LpTabs.vue +10 -21
  545. package/src/components/LpTagsInput.vue +53 -37
  546. package/src/components/LpTextReveal.vue +236 -0
  547. package/src/components/LpTextarea.vue +11 -11
  548. package/src/components/LpThemeStudio.vue +216 -0
  549. package/src/components/LpThemeStudioSection.vue +16 -0
  550. package/src/components/LpThemeSwitcher.vue +156 -47
  551. package/src/components/LpTimeField.vue +9 -1
  552. package/src/components/LpToaster.vue +63 -83
  553. package/src/components/LpTooltip.vue +10 -1
  554. package/src/components/LpTour.vue +24 -13
  555. package/src/components/LpTruncate.vue +135 -0
  556. package/src/components/LpTypewriter.vue +236 -0
  557. package/src/components/LpUptimeBar.vue +6 -5
  558. package/src/components/LpVideoBackdrop.vue +15 -13
  559. package/src/components/OptionContent.vue +17 -0
  560. package/src/components/PickerField.vue +58 -0
  561. package/src/components/RevealToggle.vue +51 -0
  562. package/src/components/StepConnector.vue +54 -0
  563. package/src/components/TourCard.vue +29 -16
  564. package/src/components/badge.ts +8 -0
  565. package/src/components/chart.ts +8 -23
  566. package/src/components/dateRange.ts +25 -0
  567. package/src/components/dock.ts +31 -0
  568. package/src/components/dropdown.ts +15 -3
  569. package/src/components/fieldText.ts +95 -0
  570. package/src/components/fileTree.ts +19 -21
  571. package/src/components/format.ts +136 -0
  572. package/src/components/identicon.ts +160 -0
  573. package/src/components/masonry.ts +73 -0
  574. package/src/components/meter.ts +122 -0
  575. package/src/components/otp.ts +14 -3
  576. package/src/components/pipeline.ts +101 -0
  577. package/src/components/quota.ts +111 -0
  578. package/src/components/relativeTime.ts +118 -0
  579. package/src/components/reveal.ts +56 -0
  580. package/src/components/scramble.ts +138 -0
  581. package/src/components/shaderBackdrop.ts +208 -0
  582. package/src/components/slider.ts +30 -0
  583. package/src/components/slotText.ts +13 -0
  584. package/src/components/status.ts +46 -0
  585. package/src/components/tableFacets.ts +25 -0
  586. package/src/components/textSplit.ts +55 -0
  587. package/src/components/timeSpan.ts +183 -0
  588. package/src/components/tone.ts +144 -0
  589. package/src/components/tour.ts +3 -2
  590. package/src/components/typewriter.ts +104 -0
  591. package/src/components/videoBackdrop.ts +7 -6
  592. package/src/composables/easing.ts +66 -1
  593. package/src/composables/pseudoLocale.ts +105 -0
  594. package/src/composables/scroll.ts +6 -3
  595. package/src/composables/useClipboard.ts +25 -3
  596. package/src/composables/useField.ts +12 -0
  597. package/src/composables/useForm.ts +27 -6
  598. package/src/composables/useInView.ts +61 -0
  599. package/src/composables/useLoadingGate.ts +63 -0
  600. package/src/composables/useLpConfig.ts +20 -1
  601. package/src/composables/useMediaQuery.ts +6 -2
  602. package/src/composables/useNow.ts +84 -0
  603. package/src/composables/usePersisted.ts +19 -8
  604. package/src/composables/usePillTransition.ts +21 -13
  605. package/src/composables/usePointerFrame.ts +72 -0
  606. package/src/composables/useReducedMotion.ts +22 -0
  607. package/src/composables/useRepeater.ts +44 -0
  608. package/src/composables/useResizeObserver.ts +59 -0
  609. package/src/composables/useRevealedValue.ts +84 -0
  610. package/src/composables/useScramble.ts +152 -0
  611. package/src/composables/useShift.ts +10 -16
  612. package/src/composables/useSpotlight.ts +82 -0
  613. package/src/composables/useTilt.ts +40 -23
  614. package/src/composables/useTween.ts +149 -0
  615. package/src/composables/useView.ts +93 -0
  616. package/src/index.css +1 -0
  617. package/src/index.ts +143 -1
  618. package/src/kit-icons.ts +1 -1
  619. package/src/theme/define.ts +65 -0
  620. package/src/theme/studio.ts +202 -0
  621. package/src/tokens/effects.css +151 -0
  622. package/src/tokens/tokens.css +131 -39
  623. package/src/tokens/utilities.css +81 -0
  624. package/dist/assets/LpTabs-Bk7v4bZz.css +0 -18
  625. package/dist/chunks/LpAlert-BdvxvcWE.js +0 -85
  626. package/dist/chunks/LpBadge-Fe0D1bYx.js +0 -62
  627. package/dist/chunks/LpBreadcrumbs-rzOKkLiI.js +0 -98
  628. package/dist/chunks/LpDatePicker-BMfM454Y.js +0 -243
  629. package/dist/chunks/LpDateRangePicker-CNb9X3eL.js +0 -195
  630. package/dist/chunks/LpNumberField-DO1ufKvE.js +0 -107
  631. package/dist/chunks/LpStepper-C2slwObu.js +0 -90
  632. package/dist/chunks/LpSwitch-CZy9_IoA.js +0 -132
  633. package/dist/chunks/LpThemeSwitcher-CDoziW0z.js +0 -166
  634. package/dist/chunks/TourCard-BKI7X9-r.js +0 -105
@@ -35,16 +35,18 @@ import {
35
35
  ComboboxTrigger,
36
36
  ComboboxViewport,
37
37
  } from "reka-ui"
38
- import { computed, nextTick, onBeforeUnmount, reactive, ref, watch } from "vue"
38
+ import { computed, nextTick, reactive, ref, useAttrs, useId, watch } from "vue"
39
39
  import { useFieldControl } from "../composables/useField"
40
40
  import { useLabels } from "../composables/useLpConfig"
41
41
  import { useMergedAttrs } from "../composables/useMergedClass"
42
- import { OPTION_ITEM, POPOVER_PANEL } from "./dropdown"
42
+ import { OPTION_ITEM, optionStagger, POPOVER_PANEL } from "./dropdown"
43
43
  import LpIcon from "./LpIcon.vue"
44
+ import OptionContent from "./OptionContent.vue"
44
45
  import LpRollingText from "./LpRollingText.vue"
45
- import LpShift from "./LpShift.vue"
46
46
  import type { LpOptionBase, OptionSelection } from "./options"
47
47
  import { fitChips, toggledValues } from "./select"
48
+ import { useOutsideGuard, usePortalTarget } from "../composables/useView"
49
+ import { useResizeObserver } from "../composables/useResizeObserver"
48
50
 
49
51
  export type SelectValue = string | number
50
52
 
@@ -143,6 +145,8 @@ const emit = defineEmits<{
143
145
  (e: "change", value: V): void
144
146
  // Search input text (for `v-model:searchTerm` / server-side search).
145
147
  (e: "update:searchTerm", value: string): void
148
+ /** Focus left the whole field (not into its own list or clear button). */
149
+ (e: "blur", event: FocusEvent): void
146
150
  }>()
147
151
 
148
152
  // Server-side search is active when the parent binds searchTerm.
@@ -164,10 +168,38 @@ const query = computed<string>({
164
168
  // This is not enough on its own: reka seeds the box on *select*, not on open,
165
169
  // so the next opening filters before this runs. `reset-search-term-on-select`
166
170
  // below is what stops the seeding; this keeps a stale term from surviving.
167
- function onOpenChange(_open: boolean) {
171
+ const isOpen = ref(false)
172
+ function onOpenChange(open: boolean) {
173
+ isOpen.value = open
168
174
  query.value = ""
169
175
  }
170
176
 
177
+ // Leaving the field, not moving between its parts: the trigger and the list
178
+ // are marked with one id, because the list is portalled away from the trigger
179
+ // and a check on the trigger alone either missed the search box or took a
180
+ // step into it for leaving.
181
+ const partOf = `lp-select-${useId()}`
182
+ function onFocusOut(event: FocusEvent) {
183
+ const next = event.relatedTarget as Element | null
184
+ const inside = !!next && typeof next.closest === "function" && !!next.closest(`[data-lp-select-part="${partOf}"]`)
185
+ if (!inside) emit("blur", event)
186
+ }
187
+
188
+ /*
189
+ * The trigger's name. reka hard-codes aria-label="Show popup" on it, which hid
190
+ * the field's label and the chosen value; aria-labelledby outranks aria-label,
191
+ * so inside a labelled field the name is the label plus the value. On its own,
192
+ * the consumer's aria-label, else the value or placeholder.
193
+ */
194
+ const attrs = useAttrs()
195
+ const valueId = `lp-select-${useId()}-value`
196
+ const valueText = computed(() => (chips.value.length ? chipNames.value : displayLabel.value || placeholderText.value))
197
+ const triggerName = computed(() =>
198
+ field.value.labelId
199
+ ? { "aria-labelledby": `${field.value.labelId} ${valueId}`, "aria-label": undefined }
200
+ : { "aria-label": (attrs["aria-label"] as string | undefined) ?? valueText.value },
201
+ )
202
+
171
203
  // reka's Combobox forbids an empty-string item value (it reserves "" for
172
204
  // "cleared"). An empty-string option is legitimate for us (e.g. "Any edition"),
173
205
  // so we encode "" to a sentinel for reka and decode it back at the boundary —
@@ -271,6 +303,12 @@ const displayLabel = computed(() => {
271
303
  return current.value?.label ?? ""
272
304
  })
273
305
 
306
+ // What makes the shown pick "another one": its name, hint and picture together.
307
+ const rollKey = computed(() => {
308
+ const c = current.value
309
+ return c ? [c.label, c.hint ?? "", c.image ?? c.icon ?? ""].join("\u0000") : `\u0000${placeholderText.value}`
310
+ })
311
+
274
312
  /*
275
313
  * Chips collapse to "+N" when the row runs out of room. Every chip is drawn a
276
314
  * second time in an invisible probe row at its natural width; the visible row
@@ -296,16 +334,8 @@ function measure() {
296
334
  )
297
335
  }
298
336
 
299
- let rowObserver: ResizeObserver | null = null
300
- watch(chipRow, (row) => {
301
- rowObserver?.disconnect()
302
- rowObserver = null
303
- if (!row || typeof ResizeObserver === "undefined") return
304
- rowObserver = new ResizeObserver(() => measure())
305
- rowObserver.observe(row)
306
- })
337
+ useResizeObserver(chipRow, () => measure())
307
338
  watch(chips, () => void nextTick(measure), { flush: "post", deep: true })
308
- onBeforeUnmount(() => rowObserver?.disconnect())
309
339
 
310
340
  const shownChips = computed(() => chips.value.slice(0, fit.value))
311
341
  const hiddenCount = computed(() => modelList.value.length - shownChips.value.length)
@@ -334,8 +364,15 @@ function onUpdate(value: unknown) {
334
364
  }
335
365
 
336
366
  function clear() {
367
+ if (field.value.disabled) return
337
368
  emit("update:modelValue", (props.multiple ? [] : undefined) as Model)
338
369
  }
370
+
371
+ // Overlays open in the window this component renders in (an LpFrame's, inside one).
372
+ const portalTo = usePortalTarget()
373
+ // What happens in another document (the page around a frame, or a frame in
374
+ // the page) is not "outside" for this layer.
375
+ const guardOutside = useOutsideGuard()
339
376
  </script>
340
377
 
341
378
  <template>
@@ -349,6 +386,7 @@ function clear() {
349
386
  :model-value="comboModel"
350
387
  :multiple="multiple"
351
388
  :disabled="field.disabled"
389
+ :required="field.required"
352
390
  :ignore-filter="true"
353
391
  :reset-search-term-on-select="false"
354
392
  :reset-search-term-on-blur="false"
@@ -358,9 +396,12 @@ function clear() {
358
396
  @update:open="onOpenChange"
359
397
  >
360
398
  <ComboboxAnchor
399
+ :data-lp-select-part="partOf"
400
+ @focusout="onFocusOut"
361
401
  :class="[
362
402
  anchorHeight,
363
- 'group flex w-full items-center gap-2 rounded-control border bg-surface-soft px-3 text-sm text-ink transition-colors duration-[var(--duration-fast)] focus-within:ring-2 data-[disabled]:cursor-not-allowed data-[disabled]:opacity-55',
403
+ 'group flex w-full items-center gap-2 rounded-control border bg-surface-soft px-3 text-sm text-ink transition-colors duration-[var(--duration-fast)] focus-within:ring-2',
404
+ field.disabled && 'cursor-not-allowed opacity-55',
364
405
  field.invalid
365
406
  ? 'border-danger focus-within:border-danger focus-within:ring-danger-soft'
366
407
  : 'border-line focus-within:border-brand focus-within:ring-ring',
@@ -370,8 +411,10 @@ function clear() {
370
411
  will not go narrower than its content without it, so a long
371
412
  placeholder stretched the whole field past the viewport instead of
372
413
  being cut short. -->
414
+ <span :id="valueId" hidden>{{ valueText }}</span>
373
415
  <ComboboxTrigger
374
416
  :id="field.id"
417
+ v-bind="triggerName"
375
418
  :aria-invalid="field.invalid || undefined"
376
419
  :aria-describedby="field.describedBy"
377
420
  class="flex min-w-0 flex-1 items-center gap-2 outline-none"
@@ -429,52 +472,28 @@ function clear() {
429
472
  </span>
430
473
  </span>
431
474
  <template v-else>
432
- <!-- The pick's picture or icon eases in beside the name; the box it
433
- sits in grows rather than shoving the name over in one frame. -->
434
- <LpShift axis="width" class="shrink-0">
435
- <Transition
436
- mode="out-in"
437
- enter-active-class="transition-[opacity,scale] duration-[var(--duration-fast)] motion-reduce:transition-none"
438
- leave-active-class="transition-[opacity,scale] duration-[var(--duration-fast)] motion-reduce:transition-none"
439
- enter-from-class="scale-75 opacity-0"
440
- leave-to-class="scale-75 opacity-0"
441
- >
475
+ <!-- The pick turns over as one piece: its picture, name and hint
476
+ leave together and the next ones arrive together. Animated
477
+ apart (a fading picture, a rolling name, a cross-faded hint)
478
+ they ran at three speeds, and for a moment the old hint stood
479
+ beside the new one and shoved the row. -->
480
+ <LpRollingText :value="rollKey" align="start" class="min-w-0 flex-1">
481
+ <span class="flex min-w-0 items-center gap-2">
442
482
  <img
443
483
  v-if="current?.image"
444
- :key="current.image"
445
484
  :src="current.image"
446
485
  alt=""
447
- class="block size-5 rounded-full object-cover"
448
- />
449
- <LpIcon
450
- v-else-if="current?.icon"
451
- :key="current.icon"
452
- :name="current.icon"
453
- :size="16"
454
- class="block text-muted"
486
+ class="block size-5 shrink-0 rounded-full object-cover"
455
487
  />
456
- </Transition>
457
- </LpShift>
458
- <!-- The choice rolls in rather than swapping, as the kit's other
459
- changing labels do. -->
460
- <LpRollingText
461
- :value="displayLabel || placeholderText"
462
- align="start"
463
- class="min-w-0"
464
- :class="displayLabel ? '' : 'text-muted'"
465
- />
466
- <Transition
467
- enter-active-class="transition-opacity duration-[var(--duration-fast)] motion-reduce:transition-none"
468
- leave-active-class="transition-opacity duration-[var(--duration-fast)] motion-reduce:transition-none"
469
- enter-from-class="opacity-0"
470
- leave-to-class="opacity-0"
471
- >
472
- <span v-if="current?.hint" :key="current.hint" class="min-w-0 truncate text-muted">{{ current.hint }}</span>
473
- </Transition>
488
+ <LpIcon v-else-if="current?.icon" :name="current.icon" :size="16" class="block shrink-0 text-muted" />
489
+ <span class="min-w-0 truncate" :class="displayLabel ? '' : 'text-muted'">{{ displayLabel || placeholderText }}</span>
490
+ <span v-if="current?.hint" class="min-w-0 truncate text-muted">{{ current.hint }}</span>
491
+ </span>
492
+ </LpRollingText>
474
493
  </template>
475
494
  </ComboboxTrigger>
476
495
  <button
477
- v-if="clearable && hasValue"
496
+ v-if="clearable && hasValue && !field.disabled"
478
497
  type="button"
479
498
  class="group flex shrink-0 items-center text-muted outline-none transition-colors duration-[var(--duration-fast)] hover:text-ink"
480
499
  :aria-label="labels.clear"
@@ -507,8 +526,12 @@ function clear() {
507
526
  </ComboboxTrigger>
508
527
  </ComboboxAnchor>
509
528
 
510
- <ComboboxPortal>
529
+ <ComboboxPortal :to="portalTo">
511
530
  <ComboboxContent
531
+ :data-lp-select-part="partOf"
532
+ @focusout="onFocusOut"
533
+ @interact-outside="guardOutside"
534
+ @escape-key-down="guardOutside"
512
535
  position="popper"
513
536
  :side-offset="6"
514
537
  :class="[POPOVER_PANEL, 'z-(--z-popover) max-h-72 w-(--reka-combobox-trigger-width) overflow-hidden']"
@@ -535,19 +558,10 @@ function clear() {
535
558
  :key="String(opt.value)"
536
559
  :value="encode(opt.value)"
537
560
  :disabled="opt.disabled"
538
- :style="{ animationDelay: `${Math.min(i, 12) * 22}ms` }"
561
+ :style="optionStagger(i)"
539
562
  :class="[OPTION_ITEM, 'justify-between data-[disabled]:cursor-not-allowed data-[disabled]:opacity-50']"
540
563
  >
541
- <span class="flex min-w-0 items-center gap-2.5">
542
- <img v-if="opt.image" :src="opt.image" alt="" class="size-6 shrink-0 rounded-full object-cover" />
543
- <LpIcon v-else-if="opt.icon" :name="opt.icon" :size="16" class="shrink-0 text-muted" />
544
- <span class="flex min-w-0 flex-col">
545
- <span class="truncate">{{ opt.label }}</span>
546
- <span v-if="opt.description" class="truncate text-xs text-muted">
547
- {{ opt.description }}
548
- </span>
549
- </span>
550
- </span>
564
+ <OptionContent :option="opt" />
551
565
  <ComboboxItemIndicator class="flex shrink-0 items-center text-brand">
552
566
  <LpIcon name="lucide:check" :size="16" />
553
567
  </ComboboxItemIndicator>
@@ -0,0 +1,434 @@
1
+ <script lang="ts">
2
+ // The consumer's `class` is merged into the root's own (below) rather than
3
+ // appended to it, so opt out of the automatic pass-through that would add it
4
+ // a second time, unmerged.
5
+ export default { inheritAttrs: false }
6
+ </script>
7
+
8
+ <script setup lang="ts">
9
+ /*
10
+ * A slowly moving decorative background, drawn on the GPU: behind a sign-in
11
+ * screen, a hero, an empty state, a launcher's home. It fills the box it is
12
+ * put in (`absolute inset-0`, so position the parent) and takes no pointer
13
+ * events.
14
+ *
15
+ * <section class="relative">
16
+ * <LpShaderBackdrop preset="aurora" />
17
+ * …content…
18
+ * </section>
19
+ *
20
+ * Looks (`preset`, see shaderBackdrop.ts): `aurora` (flowing bands of light),
21
+ * `silk` (folded sheen), `grain` (drifting colour under film grain), `dots`
22
+ * (a dot grid with a travelling ripple). All are coloured from the theme's
23
+ * tokens and follow a theme change, blending to the new colours.
24
+ *
25
+ * What makes it safe to drop in anywhere:
26
+ * - A CSS still of the same look is always underneath. It is what shows
27
+ * while the first frame is drawn, and instead of the canvas where WebGL is
28
+ * missing, software-only, loses its context, or cannot keep up (judged on
29
+ * the first second of frames) — the cases that occur on WebKitGTK, where the
30
+ * kit's desktop shells run. The page looks the same; it only stops moving.
31
+ * - Reduced motion gets the still, without creating a GL context at all.
32
+ * - It draws only while someone can see it: off-screen (IntersectionObserver),
33
+ * in a hidden tab, or `paused`, the loop stops. Frames are capped (`fps`),
34
+ * since a background gains nothing from 120 a second.
35
+ * - Unmounting releases the GL context, so a page that mounts and unmounts
36
+ * backdrops does not run into the browser's limit on live contexts.
37
+ */
38
+ import { computed, onBeforeUnmount, onMounted, ref, watch } from "vue"
39
+ import { parseColor } from "./color"
40
+ import { approach, frameDue, FRAGMENT_HEADER, PRESETS, tooSlow, VERTEX_SHADER, type ShaderPreset, type Vec3 } from "./shaderBackdrop"
41
+ import { isBrokenBackdropEngine } from "../theme/backdrop"
42
+ import { durationToken } from "../composables/easing"
43
+ import { useInView } from "../composables/useInView"
44
+ import { useMergedAttrs } from "../composables/useMergedClass"
45
+ import { usePrefersReducedMotion } from "../composables/useReducedMotion"
46
+ import { useResizeObserver } from "../composables/useResizeObserver"
47
+
48
+ const props = withDefaults(
49
+ defineProps<{
50
+ preset?: ShaderPreset
51
+ /** How strongly the theme colours show over the surface, 0–1. Lower for a backdrop under body text. */
52
+ intensity?: number
53
+ /** Motion speed; 1 is a calm drift, 0.5 half as fast. */
54
+ speed?: number
55
+ /** Frame cap. 30 is smooth for slow motion and half the work of 60. */
56
+ fps?: number
57
+ /** Hold the current frame. */
58
+ paused?: boolean
59
+ /** Always show the CSS still, never start WebGL: for a low-power mode or a screenshot. */
60
+ still?: boolean
61
+ }>(),
62
+ { preset: "aurora", intensity: 0.7, speed: 1, fps: 30, paused: false, still: false },
63
+ )
64
+
65
+ const emit = defineEmits<{
66
+ /** WebGL could not be used (missing, too slow, context lost); the still is showing. Useful to log, not to act on. */
67
+ (e: "fallback", reason: "unavailable" | "slow" | "lost" | "compile"): void
68
+ }>()
69
+
70
+ /*
71
+ * Whether anyone can see it (on screen, in a visible tab) and whether the OS
72
+ * asks for less motion are the kit's own questions (useInView,
73
+ * usePrefersReducedMotion); the root element is the one watched.
74
+ */
75
+ const { el: root, inView } = useInView()
76
+ const reduced = usePrefersReducedMotion()
77
+ const canvas = ref<HTMLCanvasElement | null>(null)
78
+ /** Bumped whenever a context is released: a canvas whose context was lost cannot get a new one. */
79
+ const canvasKey = ref(0)
80
+
81
+ const failed = ref(false)
82
+ /** The canvas holds a finished frame of the current look and is faded in over the still. */
83
+ const shown = ref(false)
84
+
85
+ const wantGl = computed(() => !props.still && !reduced.value && !failed.value)
86
+ const animating = computed(() => !props.paused && inView.value)
87
+
88
+ /*
89
+ * WebKitGTK composites on the CPU more often than not. There the canvas is
90
+ * drawn at one device pixel per CSS pixel and at most 24 frames a second,
91
+ * which keeps the look and halves the cost again.
92
+ */
93
+ const weakEngine = typeof navigator !== "undefined" && isBrokenBackdropEngine()
94
+
95
+ const fallbackStyle = computed(() => ({ background: PRESETS[props.preset].fallback, opacity: Math.min(1, clamp01(props.intensity) * 1.15) }))
96
+
97
+ function clamp01(n: number) {
98
+ return Math.max(0, Math.min(1, n))
99
+ }
100
+
101
+ // ── Theme colours ───────────────────────────────────────────────────────────
102
+
103
+ const TOKENS = ["surface", "brand", "accent", "action", "ink"] as const
104
+ type Palette = Record<(typeof TOKENS)[number], Vec3>
105
+
106
+ let probe: HTMLSpanElement | null = null
107
+ let paint: CanvasRenderingContext2D | null = null
108
+
109
+ /*
110
+ * A token's colour as numbers. The computed `color` of a probe styled with
111
+ * the variable resolves any scoped override and any colour syntax a theme
112
+ * uses; rgb() is parsed directly, and anything newer (color(), oklch()) is
113
+ * handed to a 2D canvas, which converts whatever the engine itself accepts.
114
+ */
115
+ function tokenColor(name: string): Vec3 {
116
+ if (!probe || !root.value) return [0, 0, 0]
117
+ probe.style.color = `var(--color-${name})`
118
+ const css = getComputedStyle(probe).color
119
+ const rgb = parseColor(css)
120
+ if (rgb) return [rgb.r / 255, rgb.g / 255, rgb.b / 255]
121
+ paint ??= document.createElement("canvas").getContext("2d", { willReadFrequently: true })
122
+ if (!paint) return [0, 0, 0]
123
+ paint.clearRect(0, 0, 1, 1)
124
+ paint.fillStyle = "#000"
125
+ paint.fillStyle = css
126
+ paint.fillRect(0, 0, 1, 1)
127
+ const [r, g, b] = paint.getImageData(0, 0, 1, 1).data
128
+ return [r / 255, g / 255, b / 255]
129
+ }
130
+
131
+ function readPalette(): Palette {
132
+ const out = {} as Palette
133
+ for (const name of TOKENS) out[name] = tokenColor(name)
134
+ return out
135
+ }
136
+
137
+ let target: Palette | null = null
138
+ let current: Palette | null = null
139
+
140
+ function refreshPalette() {
141
+ target = readPalette()
142
+ // Mid-animation the loop blends toward it; otherwise there is no next frame to blend in, so it lands now.
143
+ if (!current || !running) {
144
+ current = { ...target }
145
+ drawFrame()
146
+ }
147
+ }
148
+
149
+ // ── GL ──────────────────────────────────────────────────────────────────────
150
+
151
+ let gl: WebGLRenderingContext | null = null
152
+ let program: WebGLProgram | null = null
153
+ let buffer: WebGLBuffer | null = null
154
+ let uniforms: Record<string, WebGLUniformLocation | null> = {}
155
+
156
+ function compile(type: number, source: string): WebGLShader | null {
157
+ if (!gl) return null
158
+ const shader = gl.createShader(type)
159
+ if (!shader) return null
160
+ gl.shaderSource(shader, source)
161
+ gl.compileShader(shader)
162
+ if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
163
+ gl.deleteShader(shader)
164
+ return null
165
+ }
166
+ return shader
167
+ }
168
+
169
+ function useProgram(preset: ShaderPreset): boolean {
170
+ if (!gl) return false
171
+ if (program) gl.deleteProgram(program)
172
+ program = null
173
+ const vs = compile(gl.VERTEX_SHADER, VERTEX_SHADER)
174
+ const fs = compile(gl.FRAGMENT_SHADER, FRAGMENT_HEADER + PRESETS[preset].fragment)
175
+ if (!vs || !fs) return false
176
+ const next = gl.createProgram()
177
+ if (!next) return false
178
+ gl.attachShader(next, vs)
179
+ gl.attachShader(next, fs)
180
+ gl.linkProgram(next)
181
+ // Linked programs keep what they need; the shader objects can go.
182
+ gl.deleteShader(vs)
183
+ gl.deleteShader(fs)
184
+ if (!gl.getProgramParameter(next, gl.LINK_STATUS)) {
185
+ gl.deleteProgram(next)
186
+ return false
187
+ }
188
+ program = next
189
+ gl.useProgram(program)
190
+ const at = gl.getAttribLocation(program, "p")
191
+ gl.bindBuffer(gl.ARRAY_BUFFER, buffer)
192
+ gl.enableVertexAttribArray(at)
193
+ gl.vertexAttribPointer(at, 2, gl.FLOAT, false, 0, 0)
194
+ uniforms = {}
195
+ for (const name of ["u_res", "u_time", "u_px", "u_k", "u_bg", "u_brand", "u_accent", "u_action", "u_ink"]) {
196
+ uniforms[name] = gl.getUniformLocation(program, name)
197
+ }
198
+ sizeCanvas()
199
+ return true
200
+ }
201
+
202
+ let pixelRatio = 1
203
+
204
+ function sizeCanvas() {
205
+ const el = canvas.value
206
+ if (!el) return
207
+ const dpr = Math.min(weakEngine ? 1 : 2, window.devicePixelRatio || 1)
208
+ pixelRatio = dpr * PRESETS[props.preset].scale
209
+ const w = Math.max(1, Math.round(el.clientWidth * pixelRatio))
210
+ const h = Math.max(1, Math.round(el.clientHeight * pixelRatio))
211
+ if (el.width !== w || el.height !== h) {
212
+ el.width = w
213
+ el.height = h
214
+ }
215
+ gl?.viewport(0, 0, w, h)
216
+ }
217
+
218
+ let time = 0
219
+
220
+ function drawFrame() {
221
+ if (!gl || !program || !current || !canvas.value) return
222
+ const c = current
223
+ gl.uniform2f(uniforms.u_res, canvas.value.width, canvas.value.height)
224
+ gl.uniform1f(uniforms.u_time, time)
225
+ gl.uniform1f(uniforms.u_px, pixelRatio)
226
+ gl.uniform1f(uniforms.u_k, clamp01(props.intensity))
227
+ gl.uniform3fv(uniforms.u_bg, c.surface)
228
+ gl.uniform3fv(uniforms.u_brand, c.brand)
229
+ gl.uniform3fv(uniforms.u_accent, c.accent)
230
+ gl.uniform3fv(uniforms.u_action, c.action)
231
+ gl.uniform3fv(uniforms.u_ink, c.ink)
232
+ gl.drawArrays(gl.TRIANGLES, 0, 3)
233
+ }
234
+
235
+ function giveUp(reason: "unavailable" | "slow" | "lost" | "compile") {
236
+ failed.value = true
237
+ emit("fallback", reason)
238
+ }
239
+
240
+ function onContextLost() {
241
+ release()
242
+ giveUp("lost")
243
+ }
244
+
245
+ function start() {
246
+ const el = canvas.value
247
+ if (!el || gl) return
248
+ /*
249
+ * `failIfMajorPerformanceCaveat` refuses a software rasteriser outright:
250
+ * a CPU-drawn full-screen shader is exactly the load a backdrop must not
251
+ * put on a machine that has no GPU to spare.
252
+ */
253
+ const context = el.getContext("webgl", {
254
+ alpha: false,
255
+ antialias: false,
256
+ depth: false,
257
+ stencil: false,
258
+ preserveDrawingBuffer: false,
259
+ powerPreference: "low-power",
260
+ failIfMajorPerformanceCaveat: true,
261
+ })
262
+ if (!context) return giveUp("unavailable")
263
+ gl = context
264
+ el.addEventListener("webglcontextlost", onContextLost)
265
+ buffer = gl.createBuffer()
266
+ gl.bindBuffer(gl.ARRAY_BUFFER, buffer)
267
+ // One triangle that covers the whole viewport: cheaper than a quad, no seam.
268
+ gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1, -1, 3, -1, -1, 3]), gl.STATIC_DRAW)
269
+ if (!useProgram(props.preset)) {
270
+ release()
271
+ return giveUp("compile")
272
+ }
273
+ intervals = []
274
+ refreshPalette()
275
+ drawFrame()
276
+ shown.value = true
277
+ sync()
278
+ }
279
+
280
+ /** Free the context and everything on it; the next start gets a fresh canvas. */
281
+ function release() {
282
+ stop()
283
+ shown.value = false
284
+ const el = canvas.value
285
+ el?.removeEventListener("webglcontextlost", onContextLost)
286
+ if (gl) {
287
+ if (program) gl.deleteProgram(program)
288
+ if (buffer) gl.deleteBuffer(buffer)
289
+ gl.getExtension("WEBGL_lose_context")?.loseContext()
290
+ canvasKey.value++
291
+ }
292
+ gl = null
293
+ program = null
294
+ buffer = null
295
+ current = null
296
+ }
297
+
298
+ // ── Loop ────────────────────────────────────────────────────────────────────
299
+
300
+ let frame = 0
301
+ let running = false
302
+ let last = 0
303
+ let intervals: number[] = []
304
+
305
+ function tick(now: number) {
306
+ frame = requestAnimationFrame(tick)
307
+ const cap = weakEngine ? Math.min(props.fps, 24) : props.fps
308
+ if (!frameDue(now, last, cap)) return
309
+ const dt = last ? now - last : 0
310
+ last = now
311
+ // A long gap (a stalled tab) is skipped over, not caught up on.
312
+ time += (Math.min(dt, 100) / 1000) * props.speed
313
+ if (current && target) {
314
+ const k = Math.min(1, dt / 160)
315
+ for (const name of TOKENS) current[name] = approach(current[name], target[name], k)
316
+ }
317
+ drawFrame()
318
+ // The first second of drawing decides whether the engine can carry it.
319
+ if (dt > 0 && dt < 500 && intervals.length < 30) {
320
+ intervals.push(dt)
321
+ if (tooSlow(intervals, cap)) {
322
+ release()
323
+ giveUp("slow")
324
+ }
325
+ }
326
+ }
327
+
328
+ function stop() {
329
+ running = false
330
+ if (frame) cancelAnimationFrame(frame)
331
+ frame = 0
332
+ }
333
+
334
+ function sync() {
335
+ if (gl && animating.value) {
336
+ if (running) return
337
+ running = true
338
+ last = 0
339
+ frame = requestAnimationFrame(tick)
340
+ } else stop()
341
+ }
342
+
343
+ // ── Observers ───────────────────────────────────────────────────────────────
344
+
345
+ let themeObserver: MutationObserver | undefined
346
+ let schemeQuery: MediaQueryList | undefined
347
+
348
+ useResizeObserver(root, () => {
349
+ sizeCanvas()
350
+ drawFrame()
351
+ })
352
+
353
+ onMounted(() => {
354
+ probe = document.createElement("span")
355
+ probe.style.display = "none"
356
+ root.value?.appendChild(probe)
357
+
358
+ // Themes are applied as variables and attributes on <html>; a scheme switch can also come from the OS.
359
+ themeObserver = new MutationObserver(() => {
360
+ if (gl) refreshPalette()
361
+ })
362
+ themeObserver.observe(document.documentElement, { attributes: true, attributeFilter: ["style", "class", "data-theme", "data-theme-mode"] })
363
+ schemeQuery = window.matchMedia?.("(prefers-color-scheme: dark)")
364
+ schemeQuery?.addEventListener("change", refreshPalette)
365
+
366
+ if (wantGl.value) start()
367
+ })
368
+
369
+ watch(wantGl, (on) => {
370
+ if (on) requestAnimationFrame(start)
371
+ else release()
372
+ })
373
+ watch(animating, sync)
374
+
375
+ /*
376
+ * A new look fades the canvas out to the still of the new look (which has
377
+ * crossfaded in underneath), swaps the shader there, and fades back in.
378
+ */
379
+ let swapTimer = 0
380
+ watch(
381
+ () => props.preset,
382
+ (preset) => {
383
+ if (!gl) return
384
+ shown.value = false
385
+ window.clearTimeout(swapTimer)
386
+ swapTimer = window.setTimeout(() => {
387
+ if (!gl) return
388
+ if (!useProgram(preset)) {
389
+ release()
390
+ return giveUp("compile")
391
+ }
392
+ drawFrame()
393
+ shown.value = true
394
+ }, durationToken("duration-medium"))
395
+ },
396
+ )
397
+ watch(
398
+ () => props.intensity,
399
+ () => {
400
+ if (!running) drawFrame()
401
+ },
402
+ )
403
+
404
+ onBeforeUnmount(() => {
405
+ window.clearTimeout(swapTimer)
406
+ release()
407
+ themeObserver?.disconnect()
408
+ schemeQuery?.removeEventListener("change", refreshPalette)
409
+ probe?.remove()
410
+ })
411
+
412
+ const { class: rootClass, attrs: rest } = useMergedAttrs("pointer-events-none absolute inset-0 overflow-hidden bg-surface")
413
+ </script>
414
+
415
+ <template>
416
+ <div ref="root" :class="rootClass" v-bind="rest" aria-hidden="true" :data-mode="shown ? 'gl' : 'css'">
417
+ <Transition
418
+ enter-active-class="transition-opacity duration-medium ease-out motion-reduce:transition-none"
419
+ leave-active-class="transition-opacity duration-medium ease-out motion-reduce:transition-none"
420
+ enter-from-class="opacity-0"
421
+ leave-to-class="opacity-0"
422
+ >
423
+ <div :key="preset" class="absolute inset-0">
424
+ <div class="absolute inset-0 transition-opacity duration-medium" :style="fallbackStyle" />
425
+ </div>
426
+ </Transition>
427
+ <canvas
428
+ :key="canvasKey"
429
+ ref="canvas"
430
+ class="absolute inset-0 size-full transition-opacity duration-medium ease-out"
431
+ :class="shown ? 'opacity-100' : 'opacity-0'"
432
+ />
433
+ </div>
434
+ </template>