@leavepulse/ui 0.68.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 (640) 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.d.ts +10 -0
  177. package/dist/components/LpChart.vue.js +122 -142
  178. package/dist/components/LpCheckbox.vue.d.ts +2 -2
  179. package/dist/components/LpCheckbox.vue.js +1 -1
  180. package/dist/components/LpCodeBlock.vue.js +1 -1
  181. package/dist/components/LpCodeDisplay.vue.d.ts +89 -0
  182. package/dist/components/LpCodeDisplay.vue.js +4 -0
  183. package/dist/components/LpCollapse.vue.d.ts +58 -0
  184. package/dist/components/LpCollapse.vue.js +4 -0
  185. package/dist/components/LpColorPicker.vue.d.ts +2 -2
  186. package/dist/components/LpColorPicker.vue.js +1 -1
  187. package/dist/components/LpCommandPalette.vue.js +34 -23
  188. package/dist/components/LpConfirmDialog.vue.js +1 -1
  189. package/dist/components/LpConfirmHost.vue.js +1 -1
  190. package/dist/components/LpContextMenu.vue.js +1 -1
  191. package/dist/components/LpCopyButton.vue.d.ts +82 -0
  192. package/dist/components/LpCopyButton.vue.js +4 -0
  193. package/dist/components/LpCountdown.vue.d.ts +100 -0
  194. package/dist/components/LpCountdown.vue.js +4 -0
  195. package/dist/components/LpDataList.vue.d.ts +66 -0
  196. package/dist/components/LpDataList.vue.js +4 -0
  197. package/dist/components/LpDatePicker.vue.d.ts +1 -1
  198. package/dist/components/LpDatePicker.vue.js +1 -1
  199. package/dist/components/LpDateRangePicker.vue.d.ts +1 -1
  200. package/dist/components/LpDateRangePicker.vue.js +1 -1
  201. package/dist/components/LpDisclosure.vue.js +1 -1
  202. package/dist/components/LpDock.vue.d.ts +74 -0
  203. package/dist/components/LpDock.vue.js +204 -0
  204. package/dist/components/LpDrawer.vue.js +1 -1
  205. package/dist/components/LpDropdownMenu.vue.js +1 -1
  206. package/dist/components/LpDropzone.vue.d.ts +8 -1
  207. package/dist/components/LpDropzone.vue.js +36 -15
  208. package/dist/components/LpEditable.vue.d.ts +85 -0
  209. package/dist/components/LpEditable.vue.js +4 -0
  210. package/dist/components/LpEmptyState.vue.js +1 -1
  211. package/dist/components/LpFieldNotice.vue.js +1 -1
  212. package/dist/components/LpFileTree.vue.js +1 -1
  213. package/dist/components/LpFileTreeNode.vue.js +1 -1
  214. package/dist/components/LpFormField.vue.js +1 -1
  215. package/dist/components/LpFrame.vue.d.ts +22 -0
  216. package/dist/components/LpFrame.vue.js +4 -0
  217. package/dist/components/LpGauge.vue.d.ts +116 -0
  218. package/dist/components/LpGauge.vue.js +4 -0
  219. package/dist/components/LpGlowBorder.vue.d.ts +47 -0
  220. package/dist/components/LpGlowBorder.vue.js +4 -0
  221. package/dist/components/LpHintExample.vue.d.ts +2 -2
  222. package/dist/components/LpHintExample.vue.js +1 -1
  223. package/dist/components/LpHintLayer.vue.js +1 -1
  224. package/dist/components/LpHints.vue.js +1 -1
  225. package/dist/components/LpHoverCard.vue.d.ts +60 -0
  226. package/dist/components/LpHoverCard.vue.js +4 -0
  227. package/dist/components/LpIcon.vue.js +1 -1
  228. package/dist/components/LpIdenticon.vue.d.ts +37 -0
  229. package/dist/components/LpIdenticon.vue.js +4 -0
  230. package/dist/components/LpImageEditor.vue.js +13 -19
  231. package/dist/components/LpInfraNode.vue.js +1 -1
  232. package/dist/components/LpInput.vue.d.ts +10 -0
  233. package/dist/components/LpInput.vue.js +1 -1
  234. package/dist/components/LpLightbox.vue.js +1 -1
  235. package/dist/components/LpLoader.vue.d.ts +106 -0
  236. package/dist/components/LpLoader.vue.js +4 -0
  237. package/dist/components/LpLogViewer.vue.js +1 -1
  238. package/dist/components/LpMagnetic.vue.d.ts +28 -0
  239. package/dist/components/LpMagnetic.vue.js +4 -0
  240. package/dist/components/LpMapPicker.vue.js +1 -1
  241. package/dist/components/LpMasonry.vue.d.ts +42 -0
  242. package/dist/components/LpMasonry.vue.js +4 -0
  243. package/dist/components/LpMediaGallery.vue.js +1 -1
  244. package/dist/components/LpModal.vue.js +1 -1
  245. package/dist/components/LpModalNav.vue.js +1 -1
  246. package/dist/components/LpNotificationBell.vue.d.ts +15 -6
  247. package/dist/components/LpNotificationBell.vue.js +1 -1
  248. package/dist/components/LpNumberField.vue.d.ts +57 -0
  249. package/dist/components/LpNumberField.vue.js +1 -1
  250. package/dist/components/LpNumberFlow.vue.js +1 -1
  251. package/dist/components/LpOtpInput.vue.d.ts +12 -4
  252. package/dist/components/LpOtpInput.vue.js +1 -1
  253. package/dist/components/LpPagination.vue.js +1 -1
  254. package/dist/components/LpPaletteFacts.vue.js +1 -1
  255. package/dist/components/LpPaletteHeader.vue.js +2 -2
  256. package/dist/components/LpPalettePreview.vue.js +1 -1
  257. package/dist/components/LpPaletteStats.vue.js +1 -1
  258. package/dist/components/LpPasswordInput.vue.d.ts +12 -1
  259. package/dist/components/LpPasswordInput.vue.js +1 -1
  260. package/dist/components/LpPhoneInput.vue.js +1 -1
  261. package/dist/components/LpPie.vue.d.ts +3 -1
  262. package/dist/components/LpPie.vue.js +1 -1
  263. package/dist/components/LpPipeline.vue.d.ts +91 -0
  264. package/dist/components/LpPipeline.vue.js +4 -0
  265. package/dist/components/LpPopover.vue.d.ts +2 -2
  266. package/dist/components/LpPopover.vue.js +1 -1
  267. package/dist/components/LpProgress.vue.d.ts +11 -4
  268. package/dist/components/LpProgress.vue.js +1 -1
  269. package/dist/components/LpQuota.vue.d.ts +68 -0
  270. package/dist/components/LpQuota.vue.js +4 -0
  271. package/dist/components/LpRadioGroup.vue.js +1 -1
  272. package/dist/components/LpRangeCalendar.vue.js +1 -1
  273. package/dist/components/LpRangeSlider.vue.js +1 -1
  274. package/dist/components/LpRelativeTime.vue.d.ts +41 -0
  275. package/dist/components/LpRelativeTime.vue.js +4 -0
  276. package/dist/components/LpRepeater.vue.d.ts +48 -0
  277. package/dist/components/LpRepeater.vue.js +4 -0
  278. package/dist/components/LpRevealOnScroll.vue.d.ts +67 -0
  279. package/dist/components/LpRevealOnScroll.vue.js +4 -0
  280. package/dist/components/LpRollingText.vue.d.ts +1 -1
  281. package/dist/components/LpScramble.vue.d.ts +55 -0
  282. package/dist/components/LpScramble.vue.js +4 -0
  283. package/dist/components/LpScrollArea.vue.d.ts +1 -1
  284. package/dist/components/LpSecretField.vue.d.ts +108 -0
  285. package/dist/components/LpSecretField.vue.js +4 -0
  286. package/dist/components/LpSection.vue.js +1 -1
  287. package/dist/components/LpSegmented.vue.js +1 -1
  288. package/dist/components/LpSelect.vue.d.ts +2 -0
  289. package/dist/components/LpSelect.vue.js +1 -1
  290. package/dist/components/LpShaderBackdrop.vue.d.ts +39 -0
  291. package/dist/components/LpShaderBackdrop.vue.js +4 -0
  292. package/dist/components/LpShinyText.vue.d.ts +39 -0
  293. package/dist/components/LpShinyText.vue.js +4 -0
  294. package/dist/components/LpShortcutsDialog.vue.js +1 -1
  295. package/dist/components/LpSidebar.vue.js +1 -1
  296. package/dist/components/LpSidebarNav.vue.js +1 -1
  297. package/dist/components/LpSlider.vue.js +1 -1
  298. package/dist/components/LpSparkline.vue.d.ts +4 -3
  299. package/dist/components/LpSparkline.vue.js +1 -1
  300. package/dist/components/LpSpotlight.vue.d.ts +37 -0
  301. package/dist/components/LpSpotlight.vue.js +4 -0
  302. package/dist/components/LpStat.vue.js +1 -1
  303. package/dist/components/LpStatus.vue.d.ts +65 -0
  304. package/dist/components/LpStatus.vue.js +4 -0
  305. package/dist/components/LpStepMark.vue.d.ts +31 -0
  306. package/dist/components/LpStepMark.vue.js +4 -0
  307. package/dist/components/LpStepper.vue.js +1 -1
  308. package/dist/components/LpSwap.vue.js +1 -1
  309. package/dist/components/LpSwitch.vue.d.ts +2 -2
  310. package/dist/components/LpSwitch.vue.js +1 -1
  311. package/dist/components/LpTable.vue.js +104 -120
  312. package/dist/components/LpTableFilter.vue.js +1 -1
  313. package/dist/components/LpTableOfContents.vue.d.ts +6 -1
  314. package/dist/components/LpTableOfContents.vue.js +1 -1
  315. package/dist/components/LpTabs.vue.d.ts +5 -5
  316. package/dist/components/LpTabs.vue.js +25 -33
  317. package/dist/components/LpTagsInput.vue.js +1 -1
  318. package/dist/components/LpTextReveal.vue.d.ts +58 -0
  319. package/dist/components/LpTextReveal.vue.js +4 -0
  320. package/dist/components/LpTextarea.vue.js +1 -1
  321. package/dist/components/LpThemeProvider.vue.js +1 -1
  322. package/dist/components/LpThemeStudio.vue.d.ts +54 -0
  323. package/dist/components/LpThemeStudio.vue.js +4 -0
  324. package/dist/components/LpThemeStudioSection.vue.d.ts +16 -0
  325. package/dist/components/LpThemeStudioSection.vue.js +4 -0
  326. package/dist/components/LpThemeSwitcher.vue.d.ts +32 -1
  327. package/dist/components/LpThemeSwitcher.vue.js +1 -1
  328. package/dist/components/LpTimeField.vue.d.ts +2 -0
  329. package/dist/components/LpTimeField.vue.js +1 -1
  330. package/dist/components/LpToaster.vue.js +1 -1
  331. package/dist/components/LpTooltip.vue.d.ts +2 -2
  332. package/dist/components/LpTooltip.vue.js +1 -1
  333. package/dist/components/LpTopologyCanvas.vue.js +1 -1
  334. package/dist/components/LpTour.vue.js +1 -1
  335. package/dist/components/LpTruncate.vue.d.ts +34 -0
  336. package/dist/components/LpTruncate.vue.js +4 -0
  337. package/dist/components/LpTypewriter.vue.d.ts +61 -0
  338. package/dist/components/LpTypewriter.vue.js +4 -0
  339. package/dist/components/LpUptimeBar.vue.js +1 -1
  340. package/dist/components/LpVideoBackdrop.vue.js +1 -1
  341. package/dist/components/MenuTitle.vue.js +1 -1
  342. package/dist/components/OptionContent.vue.d.ts +7 -0
  343. package/dist/components/OptionContent.vue.js +4 -0
  344. package/dist/components/PickerField.vue.d.ts +43 -0
  345. package/dist/components/PickerField.vue.js +4 -0
  346. package/dist/components/RevealToggle.vue.d.ts +21 -0
  347. package/dist/components/RevealToggle.vue.js +4 -0
  348. package/dist/components/StepConnector.vue.d.ts +19 -0
  349. package/dist/components/StepConnector.vue.js +4 -0
  350. package/dist/components/TourCard.vue.js +1 -1
  351. package/dist/components/badge.d.ts +1 -0
  352. package/dist/components/badge.js +4 -0
  353. package/dist/components/chart.d.ts +5 -1
  354. package/dist/components/chart.js +2 -21
  355. package/dist/components/dateRange.d.ts +11 -0
  356. package/dist/components/dateRange.js +12 -0
  357. package/dist/components/dock.d.ts +19 -0
  358. package/dist/components/dock.js +16 -0
  359. package/dist/components/dropdown.d.ts +10 -1
  360. package/dist/components/dropdown.js +7 -2
  361. package/dist/components/fieldText.d.ts +38 -0
  362. package/dist/components/fieldText.js +53 -0
  363. package/dist/components/fileTree.d.ts +7 -3
  364. package/dist/components/fileTree.js +12 -18
  365. package/dist/components/format.d.ts +41 -0
  366. package/dist/components/format.js +67 -0
  367. package/dist/components/identicon.d.ts +38 -0
  368. package/dist/components/identicon.js +85 -0
  369. package/dist/components/masonry.d.ts +32 -0
  370. package/dist/components/masonry.js +40 -0
  371. package/dist/components/menuWhen.js +1 -1
  372. package/dist/components/meter.d.ts +64 -0
  373. package/dist/components/meter.js +53 -0
  374. package/dist/components/otp.d.ts +1 -1
  375. package/dist/components/otp.js +7 -2
  376. package/dist/components/pipeline.d.ts +55 -0
  377. package/dist/components/pipeline.js +51 -0
  378. package/dist/components/quota.d.ts +57 -0
  379. package/dist/components/quota.js +40 -0
  380. package/dist/components/relativeTime.d.ts +24 -0
  381. package/dist/components/relativeTime.js +56 -0
  382. package/dist/components/reveal.d.ts +18 -0
  383. package/dist/components/reveal.js +32 -0
  384. package/dist/components/scramble.d.ts +68 -0
  385. package/dist/components/scramble.js +58 -0
  386. package/dist/components/shaderBackdrop.d.ts +20 -0
  387. package/dist/components/shaderBackdrop.js +156 -0
  388. package/dist/components/slider.d.ts +5 -0
  389. package/dist/components/slider.js +24 -0
  390. package/dist/components/slotText.d.ts +2 -0
  391. package/dist/components/slotText.js +9 -0
  392. package/dist/components/status.d.ts +14 -0
  393. package/dist/components/status.js +22 -0
  394. package/dist/components/tableFacets.d.ts +8 -0
  395. package/dist/components/tableFacets.js +19 -0
  396. package/dist/components/textSplit.d.ts +17 -0
  397. package/dist/components/textSplit.js +24 -0
  398. package/dist/components/timeSpan.d.ts +78 -0
  399. package/dist/components/timeSpan.js +85 -0
  400. package/dist/components/tone.d.ts +43 -0
  401. package/dist/components/tone.js +82 -0
  402. package/dist/components/tour.js +3 -2
  403. package/dist/components/typewriter.d.ts +36 -0
  404. package/dist/components/typewriter.js +53 -0
  405. package/dist/components/videoBackdrop.js +5 -6
  406. package/dist/composables/easing.d.ts +22 -1
  407. package/dist/composables/easing.js +42 -1
  408. package/dist/composables/pseudoLocale.d.ts +15 -0
  409. package/dist/composables/pseudoLocale.js +96 -0
  410. package/dist/composables/scroll.js +5 -3
  411. package/dist/composables/useClipboard.d.ts +4 -1
  412. package/dist/composables/useClipboard.js +12 -2
  413. package/dist/composables/useField.d.ts +8 -0
  414. package/dist/composables/useField.js +5 -0
  415. package/dist/composables/useForm.js +19 -5
  416. package/dist/composables/useHintSystem.d.ts +4 -0
  417. package/dist/composables/useHintSystem.js +33 -1
  418. package/dist/composables/useHints.js +9 -3
  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 +61 -0
  451. package/dist/icons-all.js +1 -1
  452. package/dist/index.d.ts +98 -1
  453. package/dist/index.js +262 -104
  454. package/dist/theme/define.d.ts +12 -0
  455. package/dist/theme/studio.d.ts +90 -0
  456. package/package.json +5 -2
  457. package/src/component-names.ts +33 -0
  458. package/src/components/AttentionDot.vue +27 -0
  459. package/src/components/FieldWrapper.vue +38 -0
  460. package/src/components/LayoutNode.vue +5 -9
  461. package/src/components/LegendSwatch.vue +15 -0
  462. package/src/components/LoaderMark.vue +90 -0
  463. package/src/components/LpActionBar.vue +12 -20
  464. package/src/components/LpActivityLine.vue +186 -0
  465. package/src/components/LpAlert.vue +54 -24
  466. package/src/components/LpAppShell.vue +7 -11
  467. package/src/components/LpAutocomplete.vue +16 -14
  468. package/src/components/LpAvatar.vue +17 -2
  469. package/src/components/LpBadge.vue +77 -13
  470. package/src/components/LpBreadcrumbs.vue +101 -35
  471. package/src/components/LpButton.vue +15 -3
  472. package/src/components/LpCard.vue +30 -7
  473. package/src/components/LpChart.vue +152 -156
  474. package/src/components/LpCheckbox.vue +10 -4
  475. package/src/components/LpCodeBlock.vue +11 -19
  476. package/src/components/LpCodeDisplay.vue +228 -0
  477. package/src/components/LpCollapse.vue +162 -0
  478. package/src/components/LpColorPicker.vue +10 -1
  479. package/src/components/LpCommandPalette.vue +38 -24
  480. package/src/components/LpContextMenu.vue +10 -1
  481. package/src/components/LpCopyButton.vue +185 -0
  482. package/src/components/LpCountdown.vue +308 -0
  483. package/src/components/LpDataList.vue +154 -0
  484. package/src/components/LpDatePicker.vue +53 -104
  485. package/src/components/LpDateRangePicker.vue +49 -95
  486. package/src/components/LpDisclosure.vue +58 -36
  487. package/src/components/LpDock.vue +274 -0
  488. package/src/components/LpDrawer.vue +10 -1
  489. package/src/components/LpDropdownMenu.vue +10 -1
  490. package/src/components/LpDropzone.vue +34 -13
  491. package/src/components/LpEditable.vue +293 -0
  492. package/src/components/LpFileTree.vue +5 -1
  493. package/src/components/LpFileTreeNode.vue +25 -50
  494. package/src/components/LpFormField.vue +22 -3
  495. package/src/components/LpFrame.vue +200 -0
  496. package/src/components/LpGauge.vue +287 -0
  497. package/src/components/LpGlowBorder.vue +60 -0
  498. package/src/components/LpHintExample.vue +14 -38
  499. package/src/components/LpHintLayer.vue +13 -3
  500. package/src/components/LpHoverCard.vue +175 -0
  501. package/src/components/LpIdenticon.vue +69 -0
  502. package/src/components/LpImageEditor.vue +15 -18
  503. package/src/components/LpInfraNode.vue +27 -16
  504. package/src/components/LpInput.vue +30 -16
  505. package/src/components/LpLightbox.vue +44 -23
  506. package/src/components/LpLoader.vue +145 -0
  507. package/src/components/LpLogViewer.vue +311 -82
  508. package/src/components/LpMagnetic.vue +97 -0
  509. package/src/components/LpMapPicker.vue +26 -28
  510. package/src/components/LpMasonry.vue +247 -0
  511. package/src/components/LpModal.vue +10 -1
  512. package/src/components/LpNotificationBell.vue +33 -49
  513. package/src/components/LpNumberField.vue +79 -14
  514. package/src/components/LpNumberFlow.vue +10 -35
  515. package/src/components/LpOtpInput.vue +7 -2
  516. package/src/components/LpPagination.vue +38 -4
  517. package/src/components/LpPalettePreview.vue +2 -7
  518. package/src/components/LpPasswordInput.vue +16 -14
  519. package/src/components/LpPhoneInput.vue +12 -3
  520. package/src/components/LpPie.vue +197 -36
  521. package/src/components/LpPipeline.vue +366 -0
  522. package/src/components/LpPopover.vue +10 -1
  523. package/src/components/LpProgress.vue +22 -84
  524. package/src/components/LpQuota.vue +197 -0
  525. package/src/components/LpRangeSlider.vue +5 -1
  526. package/src/components/LpRelativeTime.vue +90 -0
  527. package/src/components/LpRepeater.vue +80 -0
  528. package/src/components/LpRevealOnScroll.vue +125 -0
  529. package/src/components/LpScramble.vue +107 -0
  530. package/src/components/LpSecretField.vue +313 -0
  531. package/src/components/LpSegmented.vue +3 -10
  532. package/src/components/LpSelect.vue +78 -64
  533. package/src/components/LpShaderBackdrop.vue +434 -0
  534. package/src/components/LpShinyText.vue +139 -0
  535. package/src/components/LpSidebar.vue +2 -0
  536. package/src/components/LpSlider.vue +5 -1
  537. package/src/components/LpSparkline.vue +55 -37
  538. package/src/components/LpSpotlight.vue +51 -0
  539. package/src/components/LpStat.vue +6 -2
  540. package/src/components/LpStatus.vue +127 -0
  541. package/src/components/LpStepMark.vue +138 -0
  542. package/src/components/LpStepper.vue +37 -43
  543. package/src/components/LpSwap.vue +2 -2
  544. package/src/components/LpSwitch.vue +15 -22
  545. package/src/components/LpTable.vue +47 -63
  546. package/src/components/LpTableFilter.vue +1 -0
  547. package/src/components/LpTableOfContents.vue +53 -29
  548. package/src/components/LpTabs.vue +10 -21
  549. package/src/components/LpTagsInput.vue +53 -37
  550. package/src/components/LpTextReveal.vue +236 -0
  551. package/src/components/LpTextarea.vue +11 -11
  552. package/src/components/LpThemeStudio.vue +216 -0
  553. package/src/components/LpThemeStudioSection.vue +16 -0
  554. package/src/components/LpThemeSwitcher.vue +156 -47
  555. package/src/components/LpTimeField.vue +9 -1
  556. package/src/components/LpToaster.vue +63 -83
  557. package/src/components/LpTooltip.vue +10 -1
  558. package/src/components/LpTour.vue +24 -13
  559. package/src/components/LpTruncate.vue +135 -0
  560. package/src/components/LpTypewriter.vue +236 -0
  561. package/src/components/LpUptimeBar.vue +6 -5
  562. package/src/components/LpVideoBackdrop.vue +15 -13
  563. package/src/components/OptionContent.vue +17 -0
  564. package/src/components/PickerField.vue +58 -0
  565. package/src/components/RevealToggle.vue +51 -0
  566. package/src/components/StepConnector.vue +54 -0
  567. package/src/components/TourCard.vue +29 -16
  568. package/src/components/badge.ts +8 -0
  569. package/src/components/chart.ts +8 -23
  570. package/src/components/dateRange.ts +25 -0
  571. package/src/components/dock.ts +31 -0
  572. package/src/components/dropdown.ts +15 -3
  573. package/src/components/fieldText.ts +95 -0
  574. package/src/components/fileTree.ts +19 -21
  575. package/src/components/format.ts +136 -0
  576. package/src/components/identicon.ts +160 -0
  577. package/src/components/masonry.ts +73 -0
  578. package/src/components/meter.ts +122 -0
  579. package/src/components/otp.ts +14 -3
  580. package/src/components/pipeline.ts +101 -0
  581. package/src/components/quota.ts +111 -0
  582. package/src/components/relativeTime.ts +118 -0
  583. package/src/components/reveal.ts +56 -0
  584. package/src/components/scramble.ts +138 -0
  585. package/src/components/shaderBackdrop.ts +208 -0
  586. package/src/components/slider.ts +30 -0
  587. package/src/components/slotText.ts +13 -0
  588. package/src/components/status.ts +46 -0
  589. package/src/components/tableFacets.ts +25 -0
  590. package/src/components/textSplit.ts +55 -0
  591. package/src/components/timeSpan.ts +183 -0
  592. package/src/components/tone.ts +144 -0
  593. package/src/components/tour.ts +3 -2
  594. package/src/components/typewriter.ts +104 -0
  595. package/src/components/videoBackdrop.ts +7 -6
  596. package/src/composables/easing.ts +66 -1
  597. package/src/composables/pseudoLocale.ts +105 -0
  598. package/src/composables/scroll.ts +6 -3
  599. package/src/composables/useClipboard.ts +25 -3
  600. package/src/composables/useField.ts +12 -0
  601. package/src/composables/useForm.ts +27 -6
  602. package/src/composables/useHintSystem.ts +40 -0
  603. package/src/composables/useHints.ts +14 -3
  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 +93 -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
@@ -67,11 +67,13 @@ import { hints } from "../composables/useHints"
67
67
  import { useHotkeys } from "../composables/useHotkeys"
68
68
  import { useLabels } from "../composables/useLpConfig"
69
69
  import { useMediaQuery } from "../composables/useMediaQuery"
70
+ import { useView, viewDocument, viewWindow } from "../composables/useView"
70
71
  import { scrollToElement } from "../composables/scroll"
71
72
  import { PANEL_SURFACE } from "./dropdown"
72
73
  import TourCard from "./TourCard.vue"
73
74
  import type { Box } from "../composables/spatialNav"
74
75
  import { holeClip, holeOf, panelsAround } from "./tour"
76
+ import { usePortalTarget } from "../composables/useView"
75
77
 
76
78
  const props = withDefaults(
77
79
  defineProps<{
@@ -111,6 +113,10 @@ const SETTLE_MS = 1000
111
113
  const CARD_ROOM = 200
112
114
 
113
115
  const wide = useMediaQuery("(min-width: 640px)")
116
+ // The window the tour walks: the page's, or an LpFrame's when it runs in one.
117
+ const view = useView()
118
+ const win = () => viewWindow(view.value)
119
+ const doc = () => viewDocument(view.value)
114
120
  const sampled = ref(false)
115
121
  const step = computed<TourStep | undefined>(() => {
116
122
  const raw = props.steps[index.value]
@@ -161,7 +167,7 @@ function resolve(raw: TourStep["target"]): Element | null {
161
167
  try {
162
168
  if (typeof raw === "function") return raw()
163
169
  if (typeof raw !== "string") return raw.isConnected ? raw : null
164
- return hints.element(raw) ?? document.querySelector(raw)
170
+ return hints.element(raw) ?? doc().querySelector(raw)
165
171
  } catch {
166
172
  return null
167
173
  }
@@ -185,9 +191,9 @@ function waitFor(raw: TourStep["target"], mine: number): Promise<Element | null>
185
191
  // or calc(), which parseFloat would misread (0.5rem → 0.5px), so the browser
186
192
  // resolves it on a throwaway element.
187
193
  function controlRadius(): number {
188
- const probe = document.createElement("div")
194
+ const probe = doc().createElement("div")
189
195
  probe.style.cssText = "position:absolute;visibility:hidden;border-top-left-radius:var(--radius-control)"
190
- document.body.append(probe)
196
+ doc().body.append(probe)
191
197
  const px = parseFloat(getComputedStyle(probe).borderTopLeftRadius)
192
198
  probe.remove()
193
199
  return Number.isFinite(px) ? px : 8
@@ -201,7 +207,7 @@ const pause = (ms: number) => new Promise<void>((done) => setTimeout(done, ms))
201
207
  // to land. Endless animations (a pulse, a spinner) never land and are left out.
202
208
  async function settled(el: Element): Promise<void> {
203
209
  await new Promise(requestAnimationFrame)
204
- const moving = document.getAnimations().filter((animation) => {
210
+ const moving = doc().getAnimations().filter((animation) => {
205
211
  const node = animation.effect instanceof KeyframeEffect ? animation.effect.target : null
206
212
  const end = Number(animation.effect?.getComputedTiming().endTime)
207
213
  return !!node && (node.contains(el) || el.contains(node)) && Number.isFinite(end)
@@ -239,7 +245,7 @@ function glideTo(el: Element | null) {
239
245
  }
240
246
 
241
247
  function measure() {
242
- viewport.value = { width: window.innerWidth, height: window.innerHeight }
248
+ viewport.value = { width: win().innerWidth, height: win().innerHeight }
243
249
  const el = target.value
244
250
  if (!el) {
245
251
  box.value = null
@@ -371,13 +377,13 @@ watch(
371
377
  direction = 1
372
378
  box.value = null
373
379
  radius.value = controlRadius()
374
- document.addEventListener("click", onTargetClick, true)
375
- document.addEventListener("contextmenu", onTargetClick, true)
380
+ doc().addEventListener("click", onTargetClick, true)
381
+ doc().addEventListener("contextmenu", onTargetClick, true)
376
382
  frame ||= requestAnimationFrame(track)
377
383
  void show()
378
384
  } else {
379
- document.removeEventListener("click", onTargetClick, true)
380
- document.removeEventListener("contextmenu", onTargetClick, true)
385
+ doc().removeEventListener("click", onTargetClick, true)
386
+ doc().removeEventListener("contextmenu", onTargetClick, true)
381
387
  end()
382
388
  }
383
389
  },
@@ -389,8 +395,8 @@ watch(index, () => {
389
395
  })
390
396
 
391
397
  onBeforeUnmount(() => {
392
- document.removeEventListener("click", onTargetClick, true)
393
- document.removeEventListener("contextmenu", onTargetClick, true)
398
+ doc().removeEventListener("click", onTargetClick, true)
399
+ doc().removeEventListener("contextmenu", onTargetClick, true)
394
400
  if (open.value) end()
395
401
  })
396
402
 
@@ -408,10 +414,13 @@ useHotkeys(
408
414
  // and reka switches off pointer events on every older one, the card included
409
415
  // when it carries over from the step before. The tour sits above all of them.
410
416
  const CARD = `${PANEL_SURFACE} pointer-events-auto! rounded-card w-80 max-w-[calc(100vw-24px)] p-4 text-sm text-ink outline-none`
417
+
418
+ // Overlays open in the window this component renders in (an LpFrame's, inside one).
419
+ const portalTo = usePortalTarget()
411
420
  </script>
412
421
 
413
422
  <template>
414
- <Teleport to="body">
423
+ <Teleport :to="portalTo">
415
424
  <Transition
416
425
  enter-active-class="transition-opacity duration-[var(--duration-medium)] ease-[var(--ease-emphasized)] motion-reduce:transition-none"
417
426
  leave-active-class="lp-tour-gone transition-opacity duration-[var(--duration-medium)] ease-out motion-reduce:transition-none"
@@ -444,7 +453,7 @@ const CARD = `${PANEL_SURFACE} pointer-events-auto! rounded-card w-80 max-w-[cal
444
453
 
445
454
  <PopoverRoot v-if="wide" :open="open && ready">
446
455
  <PopoverAnchor :reference="reference" />
447
- <PopoverPortal>
456
+ <PopoverPortal :to="portalTo">
448
457
  <PopoverContent
449
458
  :side="side"
450
459
  :side-offset="12"
@@ -452,6 +461,7 @@ const CARD = `${PANEL_SURFACE} pointer-events-auto! rounded-card w-80 max-w-[cal
452
461
  update-position-strategy="always"
453
462
  :trap-focus="false"
454
463
  :class="[CARD, 'z-[calc(var(--z-tour)+1)] data-[state=open]:animate-[popover-in_var(--duration-medium)_var(--ease-emphasized)] data-[state=closed]:animate-[popover-out_var(--duration-fast)_ease-out] motion-reduce:animate-none']"
464
+ :data-lp-glide-position="gliding || undefined"
455
465
  role="dialog"
456
466
  :aria-label="step?.title"
457
467
  @pointerdown.stop
@@ -481,3 +491,4 @@ const CARD = `${PANEL_SURFACE} pointer-events-auto! rounded-card w-80 max-w-[cal
481
491
  </Transition>
482
492
  </Teleport>
483
493
  </template>
494
+
@@ -0,0 +1,135 @@
1
+ <script lang="ts">
2
+ export default { inheritAttrs: false }
3
+ </script>
4
+
5
+ <script setup lang="ts">
6
+ /*
7
+ * Text that may not fit: cut with an ellipsis, and the whole of it in a tip —
8
+ * but only when something was actually cut. A tip that repeats the text you
9
+ * can already read in full is noise, and a list of names where every row
10
+ * pops one up teaches people to ignore them.
11
+ *
12
+ * <LpTruncate :text="server.name" />
13
+ * <LpTruncate :text="description" :lines="2" />
14
+ * <LpTruncate :text="commitHash" middle :tail="6" copyable />
15
+ *
16
+ * Whether it was cut is measured, not guessed: a ResizeObserver re-checks when
17
+ * the box resizes (a sidebar collapsing, a column dragged), so the tip appears
18
+ * and disappears with the layout.
19
+ *
20
+ * `middle` cuts out of the middle instead (`/home/u…/report.pdf`), for ids,
21
+ * paths and hashes, whose two ends are what people compare. The browser has
22
+ * no CSS for that, so the longest fitting cut is found by measuring with the
23
+ * element's own font (see `middleTruncate`). Assistive tech always gets the
24
+ * full text: the cut version is only drawn.
25
+ *
26
+ * A truncated value becomes focusable, so the tip is reachable by keyboard too.
27
+ */
28
+ import { computed, nextTick, onMounted, ref, watch } from "vue"
29
+ import { useMergedAttrs } from "../composables/useMergedClass"
30
+ import { useResizeObserver } from "../composables/useResizeObserver"
31
+ import LpCopyButton from "./LpCopyButton.vue"
32
+ import LpTooltip from "./LpTooltip.vue"
33
+ import { middleTruncate } from "./fieldText"
34
+
35
+ const props = withDefaults(
36
+ defineProps<{
37
+ text: string
38
+ /** Lines before the cut. Ignored with `middle`, which is one line by nature. */
39
+ lines?: number
40
+ /** Cut from the middle, keeping both ends. */
41
+ middle?: boolean
42
+ /** With `middle`: how many characters the end keeps. Unset splits evenly. */
43
+ tail?: number
44
+ /** A copy button after the text, for values people paste elsewhere. */
45
+ copyable?: boolean
46
+ /** Where the tip opens. */
47
+ side?: "top" | "right" | "bottom" | "left"
48
+ /** Never show the tip, even when cut (the full text is shown elsewhere). */
49
+ noTooltip?: boolean
50
+ }>(),
51
+ { lines: 1, side: "top" },
52
+ )
53
+
54
+ const box = ref<HTMLElement | null>(null)
55
+ const truncated = ref(false)
56
+ const drawn = ref(props.text)
57
+
58
+ let canvas: CanvasRenderingContext2D | null = null
59
+ function measurer(el: HTMLElement): (s: string) => number {
60
+ canvas ??= document.createElement("canvas").getContext("2d")
61
+ const style = getComputedStyle(el)
62
+ // Built from the longhands: Firefox leaves the `font` shorthand empty in
63
+ // computed styles.
64
+ if (canvas) canvas.font = `${style.fontStyle} ${style.fontWeight} ${style.fontSize} ${style.fontFamily}`
65
+ const spacing = Number.parseFloat(style.letterSpacing) || 0
66
+ return (s) => (canvas ? canvas.measureText(s).width + spacing * Array.from(s).length : 0)
67
+ }
68
+
69
+ function measure() {
70
+ const el = box.value
71
+ if (!el) return
72
+ if (props.middle) {
73
+ const width = el.clientWidth
74
+ if (!width) return
75
+ const widthOf = measurer(el)
76
+ // Half a pixel of slack: canvas and layout round sub-pixels differently.
77
+ const fit = middleTruncate(props.text, (s) => widthOf(s) <= width - 0.5, { tail: props.tail })
78
+ drawn.value = fit
79
+ truncated.value = fit !== props.text
80
+ return
81
+ }
82
+ truncated.value = props.lines > 1 ? el.scrollHeight > el.clientHeight + 1 : el.scrollWidth > el.clientWidth + 0.5
83
+ }
84
+
85
+ useResizeObserver(box, () => measure())
86
+ onMounted(() => {
87
+ measure()
88
+ // Web fonts land after mount and change every width.
89
+ void document.fonts?.ready.then(() => measure())
90
+ })
91
+ watch(
92
+ () => [props.text, props.lines, props.middle, props.tail],
93
+ async () => {
94
+ drawn.value = props.text
95
+ await nextTick()
96
+ measure()
97
+ },
98
+ )
99
+
100
+ const clampStyle = computed(() =>
101
+ !props.middle && props.lines > 1
102
+ ? { display: "-webkit-box", WebkitBoxOrient: "vertical" as const, WebkitLineClamp: props.lines, overflow: "hidden" }
103
+ : undefined,
104
+ )
105
+ const boxClass = computed(() =>
106
+ props.middle ? "block overflow-hidden whitespace-pre" : props.lines > 1 ? "break-words" : "block truncate",
107
+ )
108
+
109
+ const { class: rootClass, attrs: rest } = useMergedAttrs(() => "inline-flex min-w-0 max-w-full items-center gap-1 align-bottom")
110
+ </script>
111
+
112
+ <template>
113
+ <span :class="rootClass" v-bind="rest">
114
+ <LpTooltip :content="text" :side="side" :disabled="noTooltip || !truncated">
115
+ <span
116
+ ref="box"
117
+ class="min-w-0 flex-1 rounded-[2px] outline-none focus-visible:ring-2 focus-visible:ring-ring"
118
+ :class="boxClass"
119
+ :style="clampStyle"
120
+ :tabindex="truncated && !noTooltip ? 0 : undefined"
121
+ >
122
+ <template v-if="middle">
123
+ <span aria-hidden="true">{{ drawn }}</span>
124
+ <!-- The full text, invisible and flat, gives the box its natural
125
+ width; without it the box would shrink to the cut text and
126
+ never grow back when room opens up. -->
127
+ <span aria-hidden="true" class="invisible block h-0">{{ text }}</span>
128
+ <span class="sr-only">{{ text }}</span>
129
+ </template>
130
+ <template v-else>{{ text }}</template>
131
+ </span>
132
+ </LpTooltip>
133
+ <LpCopyButton v-if="copyable" :value="text" size="xs" class="-my-1 size-6 shrink-0 text-muted" />
134
+ </span>
135
+ </template>
@@ -0,0 +1,236 @@
1
+ <script lang="ts">
2
+ // The root class is merged with the consumer's (useMergedAttrs).
3
+ export default { inheritAttrs: false }
4
+ </script>
5
+
6
+ <script setup lang="ts">
7
+ /*
8
+ * Text typed out a character at a time, optionally cycling through several
9
+ * strings: type, hold, erase, type the next.
10
+ *
11
+ * For a line that suggests rather than states: the examples a search box can
12
+ * take ("Search servers", "Search logs", "Search people"), a hero line naming
13
+ * what a product does, a terminal-styled empty state. Not for anything the
14
+ * reader has to read to continue: it makes them wait for it.
15
+ *
16
+ * <LpTypewriter text="Hello, operator." />
17
+ * <LpTypewriter :text="['servers', 'logs', 'people']" loop />
18
+ *
19
+ * Keystrokes are uneven and pause after punctuation, so it reads as typing and
20
+ * not as a progress bar. Erasing stops at what the next string shares with the
21
+ * current one ("Search logs" to "Search people" erases only "logs").
22
+ *
23
+ * The box reserves the size of the longest string, so the line it sits in
24
+ * does not jump as characters come and go. `reserve` off lets it take the
25
+ * width of what is typed, for text that runs on in a sentence.
26
+ *
27
+ * Screen readers hear whole strings, never characters: a polite live region
28
+ * gets each string once it is fully typed. It starts typing the first time it
29
+ * is on screen and pauses while it is off screen or the tab is hidden. Under
30
+ * reduced motion whole strings appear and swap without typing.
31
+ */
32
+ import { computed, onBeforeUnmount, ref, watch } from "vue"
33
+ import { prefersReducedMotion } from "../composables/easing"
34
+ import { useInView } from "../composables/useInView"
35
+ import { useMergedAttrs } from "../composables/useMergedClass"
36
+ import { useReveal } from "../composables/useReveal"
37
+ import { graphemes } from "./scramble"
38
+ import { typewriterNext, typewriterStart, type TypewriterState } from "./typewriter"
39
+
40
+ const props = withDefaults(
41
+ defineProps<{
42
+ /** One string, or several to cycle through. */
43
+ text: string | string[]
44
+ /** Ms per character, before the natural unevenness. */
45
+ speed?: number
46
+ /** Ms per erased character. */
47
+ eraseSpeed?: number
48
+ /** Ms a finished string stays before it is erased. */
49
+ hold?: number
50
+ /** Ms before the first keystroke, once it is on screen. */
51
+ startDelay?: number
52
+ /** After the last string, start over from the first. Without it the last string stays. */
53
+ loop?: boolean
54
+ /** Show the caret. */
55
+ caret?: boolean
56
+ /** Reserve the longest string's size so the surroundings hold still. */
57
+ reserve?: boolean
58
+ /** Hold it until true. Leave unset to start the first time it is on screen. */
59
+ play?: boolean
60
+ /** Element to render. */
61
+ as?: string
62
+ }>(),
63
+ {
64
+ speed: 55,
65
+ eraseSpeed: 28,
66
+ hold: 1800,
67
+ startDelay: 0,
68
+ loop: false,
69
+ caret: true,
70
+ reserve: true,
71
+ play: undefined,
72
+ as: "span",
73
+ },
74
+ )
75
+
76
+ const emit = defineEmits<{
77
+ /** A string has been typed out in full. */
78
+ typed: [index: number, text: string]
79
+ /** The last string is typed and nothing more will happen (never fires with `loop`). */
80
+ finished: []
81
+ }>()
82
+
83
+ const { class: rootClass, attrs: rest } = useMergedAttrs(
84
+ () => "lp-typewriter relative inline-grid whitespace-pre-wrap align-bottom",
85
+ )
86
+
87
+ const strings = computed(() => (Array.isArray(props.text) ? props.text : [props.text]))
88
+ const split = computed(() => strings.value.map((s) => graphemes(s)))
89
+
90
+ const state = ref<TypewriterState>(typewriterStart())
91
+ const visible = computed(() => (split.value[state.value.index] ?? []).slice(0, state.value.length).join(""))
92
+ const announced = ref(strings.value[0] ?? "")
93
+ const idle = computed(() => state.value.phase === "holding" || state.value.phase === "done")
94
+
95
+ const { el: revealEl, revealed } = useReveal({ immediate: () => props.play !== undefined })
96
+ const { el: viewEl, inView } = useInView()
97
+ const setRoot = (node: unknown) => {
98
+ const element = node instanceof Element ? node : null
99
+ revealEl.value = element
100
+ viewEl.value = element
101
+ }
102
+ const running = computed(() => (props.play ?? revealed.value) && inView.value)
103
+
104
+ let timer: ReturnType<typeof setTimeout> | undefined
105
+ // Time left on the step a pause interrupted, so going off screen mid-hold and
106
+ // back does not restart the hold from zero or skip it.
107
+ let pendingWait: number | null = null
108
+ let dueAt = 0
109
+ let started = false
110
+
111
+ function schedule(wait: number) {
112
+ clearTimeout(timer)
113
+ timer = undefined
114
+ if (!Number.isFinite(wait)) return
115
+ pendingWait = wait
116
+ if (!running.value) return
117
+ dueAt = performance.now() + wait
118
+ timer = setTimeout(tick, wait)
119
+ }
120
+
121
+ function tick() {
122
+ timer = undefined
123
+ pendingWait = null
124
+ const options = { speed: props.speed, eraseSpeed: props.eraseSpeed, hold: props.hold, loop: props.loop }
125
+ let step = typewriterNext(state.value, split.value, options)
126
+ if (prefersReducedMotion()) {
127
+ // Whole strings: run the keystrokes of this phase at once and keep only
128
+ // the waits that are holds.
129
+ let guard = 0
130
+ while (step.typed === undefined && Number.isFinite(step.wait) && guard++ < 10_000) {
131
+ step = typewriterNext(step.state, split.value, options)
132
+ }
133
+ }
134
+ state.value = step.state
135
+ if (step.typed !== undefined) {
136
+ const text = strings.value[step.typed] ?? ""
137
+ announced.value = text
138
+ emit("typed", step.typed, text)
139
+ if (step.state.phase === "done") emit("finished")
140
+ }
141
+ if (step.wait === 0) return tick()
142
+ schedule(step.wait)
143
+ }
144
+
145
+ watch(
146
+ running,
147
+ (now) => {
148
+ if (!now) {
149
+ if (timer) pendingWait = Math.max(0, dueAt - performance.now())
150
+ clearTimeout(timer)
151
+ timer = undefined
152
+ return
153
+ }
154
+ if (!started) {
155
+ started = true
156
+ return schedule(prefersReducedMotion() ? 0 : props.startDelay)
157
+ }
158
+ if (pendingWait !== null) schedule(pendingWait)
159
+ },
160
+ { immediate: true },
161
+ )
162
+
163
+ // New strings: erase back to what the new first string shares, then type it.
164
+ watch(
165
+ () => strings.value.join("\u0000"),
166
+ () => {
167
+ if (!started) return
168
+ const index = Math.min(state.value.index, split.value.length - 1)
169
+ state.value = { index: Math.max(0, index), length: Math.min(state.value.length, split.value[index]?.length ?? 0), phase: "erasing", to: 0 }
170
+ schedule(props.eraseSpeed)
171
+ },
172
+ )
173
+
174
+ watch(
175
+ () => props.loop,
176
+ (loop) => {
177
+ if (loop && state.value.phase === "done") {
178
+ state.value = { ...state.value, phase: "holding" }
179
+ schedule(props.hold)
180
+ }
181
+ },
182
+ )
183
+
184
+ onBeforeUnmount(() => clearTimeout(timer))
185
+ </script>
186
+
187
+ <template>
188
+ <component :is="as" :ref="setRoot" :class="rootClass" v-bind="rest">
189
+ <span class="sr-only" aria-live="polite">{{ announced }}</span>
190
+ <!-- The zero-width space keeps a baseline while nothing is typed yet, so
191
+ the empty box does not sink below the text around it. -->
192
+ <span aria-hidden="true" class="[grid-area:1/1]"
193
+ >&#8203;{{ visible
194
+ }}<span
195
+ v-if="caret"
196
+ class="lp-typewriter__caret"
197
+ :class="idle ? 'lp-typewriter__caret--blink' : ''"
198
+ /></span>
199
+ <template v-if="reserve">
200
+ <span v-for="(s, i) in strings" :key="i" aria-hidden="true" class="invisible [grid-area:1/1]"
201
+ >{{ s }}<span v-if="caret" class="lp-typewriter__caret"
202
+ /></span>
203
+ </template>
204
+ </component>
205
+ </template>
206
+
207
+ <style>
208
+ .lp-typewriter__caret {
209
+ display: inline-block;
210
+ width: 0.08em;
211
+ min-width: 2px;
212
+ height: 1.05em;
213
+ margin-inline-start: 0.06em;
214
+ vertical-align: -0.15em;
215
+ border-radius: 1px;
216
+ background: var(--color-brand);
217
+ }
218
+
219
+ /* Solid while keys are moving, blinking while it waits: the same cue a real
220
+ text field gives about whether input is happening. */
221
+ .lp-typewriter__caret--blink {
222
+ animation: lp-typewriter-blink 1.06s step-end infinite;
223
+ }
224
+
225
+ @keyframes lp-typewriter-blink {
226
+ 50% {
227
+ opacity: 0;
228
+ }
229
+ }
230
+
231
+ @media (prefers-reduced-motion: reduce) {
232
+ .lp-typewriter__caret--blink {
233
+ animation: none;
234
+ }
235
+ }
236
+ </style>
@@ -12,6 +12,7 @@ import LpRollingText from "./LpRollingText.vue"
12
12
  import LpSkeleton from "./LpSkeleton.vue"
13
13
  import LpSwap from "./LpSwap.vue"
14
14
  import LpTooltip from "./LpTooltip.vue"
15
+ import { TONES } from "./tone"
15
16
  import { useMergedAttrs } from "../composables/useMergedClass"
16
17
  import { useLabels } from "../composables/useLpConfig"
17
18
 
@@ -64,11 +65,11 @@ const props = withDefaults(
64
65
  * made the whole row look faded rather than calm.)
65
66
  */
66
67
  const defaultColor: Record<UptimeStatus, string> = {
67
- operational: "var(--color-action)",
68
- degraded: "var(--color-accent)",
69
- down: "var(--color-danger)",
70
- maintenance: "var(--color-brand)",
71
- empty: "var(--color-surface-soft)",
68
+ operational: TONES.success.color,
69
+ degraded: TONES.warning.color,
70
+ down: TONES.danger.color,
71
+ maintenance: TONES.brand.color,
72
+ empty: TONES.neutral.soft,
72
73
  }
73
74
 
74
75
  /* Only "no data yet" is held back; a real reading is always full strength. */
@@ -21,12 +21,12 @@ export default { inheritAttrs: false }
21
21
  * paced on its own media clock, so a clip whose last frame does not match
22
22
  * its first still loops without a visible cut.
23
23
  * - Speed (`rate`) and a darkening veil (`dim`) for text laid over it.
24
- * - Pausing where motion is unwanted: `paused`, a hidden tab, and the
25
- * system's reduced-motion setting, which shows the first frame of the
24
+ * - Pausing where motion is unwanted: `paused`, a hidden tab, scrolled off
25
+ * screen, and the system's reduced-motion setting, which shows the first frame of the
26
26
  * stretch instead (`playWithReducedMotion` overrides it).
27
27
  * - Easing in and out of a pause (`easePlayback`): the picture slows to a
28
28
  * standstill and gathers speed again instead of freezing and lurching. A
29
- * hidden tab and reduced motion still stop at once.
29
+ * hidden tab, a backdrop scrolled away and reduced motion still stop at once.
30
30
  *
31
31
  * The second element exists only while there is a seam to blend, so a plain
32
32
  * loop decodes one stream. Both play the same `src`: a URL the media element
@@ -34,6 +34,7 @@ export default { inheritAttrs: false }
34
34
  */
35
35
  import { computed, onBeforeUnmount, onMounted, ref, watch } from "vue"
36
36
  import { easeTravel, prefersReducedMotion } from "../composables/easing"
37
+ import { useInView } from "../composables/useInView"
37
38
  import { useMergedAttrs } from "../composables/useMergedClass"
38
39
  import { isNativeLoop, planLoop, seamProgress, seamReached } from "./videoBackdrop"
39
40
 
@@ -101,7 +102,13 @@ const throttle = ref(1)
101
102
  const SLOWEST_RATE = 1 / 16
102
103
 
103
104
  const reducedMotion = ref(prefersReducedMotion())
104
- const pageHidden = ref(typeof document !== "undefined" && document.hidden)
105
+ /*
106
+ * Unseen: the tab is hidden or the backdrop is off screen. A video decoding
107
+ * frames under the fold spends as much as one in view, and a page of them
108
+ * (a hero, then panels further down) runs them all at once.
109
+ */
110
+ const { el: root, inView } = useInView()
111
+ const unseen = computed(() => !inView.value)
105
112
 
106
113
  const plan = computed(() =>
107
114
  planLoop(duration.value, { start: props.start, end: props.end, crossfade: props.crossfade }),
@@ -109,7 +116,7 @@ const plan = computed(() =>
109
116
  const native = computed(() => isNativeLoop(plan.value, duration.value))
110
117
  const blended = computed(() => plan.value.fade > 0)
111
118
  const still = computed(() => reducedMotion.value && !props.playWithReducedMotion)
112
- const playing = computed(() => ready.value && !props.paused && !pageHidden.value && !still.value)
119
+ const playing = computed(() => ready.value && !props.paused && !unseen.value && !still.value)
113
120
 
114
121
  function element(index: 0 | 1) {
115
122
  return index === 0 ? first.value : second.value
@@ -253,8 +260,8 @@ function sync() {
253
260
  rampTo(1, easing)
254
261
  return
255
262
  }
256
- // Nobody sees a hidden tab, and a still is asked for outright: both stop at once.
257
- if (!running.value || pageHidden.value || still.value || !easing) {
263
+ // Nobody watches a hidden tab or a backdrop off screen, and a still is asked for outright: all stop at once.
264
+ if (!running.value || unseen.value || still.value || !easing) {
258
265
  rampTo(throttle.value, 0)
259
266
  stopElements()
260
267
  return
@@ -301,23 +308,18 @@ watch(
301
308
  function onMotionChange(event: MediaQueryListEvent) {
302
309
  reducedMotion.value = event.matches
303
310
  }
304
- function onVisibility() {
305
- pageHidden.value = document.hidden
306
- }
307
311
  let motionQuery: MediaQueryList | null = null
308
312
 
309
313
  onMounted(() => {
310
314
  applyRate()
311
315
  motionQuery = window.matchMedia("(prefers-reduced-motion: reduce)")
312
316
  motionQuery.addEventListener("change", onMotionChange)
313
- document.addEventListener("visibilitychange", onVisibility)
314
317
  })
315
318
 
316
319
  onBeforeUnmount(() => {
317
320
  if (frame) cancelAnimationFrame(frame)
318
321
  if (ramp) cancelAnimationFrame(ramp)
319
322
  motionQuery?.removeEventListener("change", onMotionChange)
320
- document.removeEventListener("visibilitychange", onVisibility)
321
323
  })
322
324
 
323
325
  function layerStyle(index: 0 | 1) {
@@ -339,7 +341,7 @@ const { class: rootClass, attrs: rest } = useMergedAttrs(
339
341
  </script>
340
342
 
341
343
  <template>
342
- <div :class="rootClass" aria-hidden="true" v-bind="rest">
344
+ <div ref="root" :class="rootClass" aria-hidden="true" v-bind="rest">
343
345
  <!-- Under the video, which fades in over it: removing a still laid on top
344
346
  would flash the page between the two. -->
345
347
  <img v-if="poster" class="absolute inset-0 size-full" :style="{ objectFit: fit }" :src="poster" alt="" />
@@ -0,0 +1,17 @@
1
+ <script setup lang="ts">
2
+ import type { LpOptionBase } from "./options"
3
+ import LpIcon from "./LpIcon.vue"
4
+
5
+ defineProps<{ option: LpOptionBase<string | number> }>()
6
+ </script>
7
+
8
+ <template>
9
+ <span class="flex min-w-0 items-center gap-2.5">
10
+ <img v-if="option.image" :src="option.image" alt="" class="size-6 shrink-0 rounded-full object-cover" />
11
+ <LpIcon v-else-if="option.icon" :name="option.icon" :size="16" class="shrink-0 text-muted" />
12
+ <span class="flex min-w-0 flex-col">
13
+ <span class="truncate">{{ option.label ?? option.value }}</span>
14
+ <span v-if="option.description" class="truncate text-xs text-muted">{{ option.description }}</span>
15
+ </span>
16
+ </span>
17
+ </template>