@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
@@ -11,13 +11,20 @@
11
11
  * and the choice survives a reload (anti-flash bootstrap). The component never
12
12
  * fetches; it just drives the theme engine.
13
13
  */
14
- import { computed, onMounted, ref } from "vue"
14
+ import { PopoverAnchor, PopoverContent, PopoverPortal, PopoverRoot } from "reka-ui"
15
+ import { computed, onMounted, ref, useId } from "vue"
15
16
  import { useLabels } from "../composables/useLpConfig"
17
+ import { useOutsideGuard, usePortalTarget } from "../composables/useView"
16
18
  import { presets } from "../theme/presets"
19
+ import { STUDIO_SYSTEM, useThemeStudio } from "../theme/studio"
17
20
  import type { TokenSet } from "../theme/tokens"
18
21
  import { useTheme } from "../theme/useTheme"
22
+ import { POPOVER_PANEL } from "./dropdown"
19
23
  import LpContextMenu, { type ContextMenuItemDef } from "./LpContextMenu.vue"
20
24
  import LpIcon from "./LpIcon.vue"
25
+ import LpRollingText from "./LpRollingText.vue"
26
+ import LpShift from "./LpShift.vue"
27
+ import LpThemeStudio, { type ThemeStudioPart } from "./LpThemeStudio.vue"
21
28
 
22
29
  const props = withDefaults(
23
30
  defineProps<{
@@ -58,7 +65,19 @@ const props = withDefaults(
58
65
  */
59
66
  system?: { light: string | TokenSet; dark: string | TokenSet }
60
67
  /** Text overrides. `trigger` may contain `{name}`. */
61
- labels?: Partial<{ trigger: string; system: string }>
68
+ labels?: Partial<{ trigger: string; system: string; studio: string; openStudio: string }>
69
+ /**
70
+ * The right-click menu ends with "Theme studio…", set apart from the
71
+ * themes, which opens LpThemeStudio by the trigger: the theme plus brand
72
+ * colour, corners, control size and motion, remembered under `studioKey`.
73
+ * Picks and left-click cycling go through the studio then, so a tuned
74
+ * brand or corner survives the next theme. Rows of the app's own go in
75
+ * the `#studio-start` / `#studio-end` slots, built-in ones leave with
76
+ * `studioHide`.
77
+ */
78
+ studio?: boolean
79
+ studioKey?: string
80
+ studioHide?: ThemeStudioPart[]
62
81
  }>(),
63
82
  {
64
83
  variant: "swatch",
@@ -73,10 +92,46 @@ const emit = defineEmits<{
73
92
  }>()
74
93
 
75
94
  const theme = useTheme()
95
+ const systemNames = () => {
96
+ const pair = props.system
97
+ const name = (t: string | TokenSet) => (typeof t === "string" ? t : t.name)
98
+ return pair ? { light: name(pair.light), dark: name(pair.dark) } : undefined
99
+ }
100
+ const tuner = props.studio
101
+ ? useThemeStudio({ themes: props.themes, storageKey: props.studioKey, system: systemNames() })
102
+ : null
103
+ const studioOpen = ref(false)
104
+ const portalTo = usePortalTarget()
105
+ const guardOutside = useOutsideGuard()
106
+ // The trigger is not "outside" the studio: the menu hands focus back to it
107
+ // as the studio opens, and a left-click on it while the studio is open is
108
+ // the next theme, watched in the open panel. Found by a mark rather than a
109
+ // template ref: PopoverAnchor's as-child puts its own ref on the element.
110
+ // Neither is the menu that opened it: it is still fading out when the studio
111
+ // appears, and a real pointer moving over it focuses it, which closed the
112
+ // studio the moment it opened (a synthetic click never moves the pointer).
113
+ const anchorId = `lp-theme-trigger-${useId()}`
114
+ function keepForTrigger(event: Event) {
115
+ guardOutside(event)
116
+ const original = (event as CustomEvent<{ originalEvent?: Event }>).detail?.originalEvent
117
+ const target = (original ?? event).target as Element | null
118
+ if (
119
+ target &&
120
+ typeof target.closest === "function" &&
121
+ (target.closest(`[data-lp-theme-trigger="${anchorId}"]`) || target.closest('[data-dismissable-layer][data-state="closed"]'))
122
+ ) {
123
+ event.preventDefault()
124
+ }
125
+ }
76
126
 
77
127
  const text = useLabels(
78
128
  "themeSwitcher",
79
- { trigger: "Theme: {name}. Click to switch, right-click to choose.", system: "System" },
129
+ {
130
+ trigger: "Theme: {name}. Click to switch, right-click to choose.",
131
+ system: "System",
132
+ studio: "Theme studio",
133
+ openStudio: "Theme studio…",
134
+ },
80
135
  () => props.labels,
81
136
  )
82
137
 
@@ -87,7 +142,7 @@ const list = computed<TokenSet[]>(() =>
87
142
  // kit's self-managed `currentName` (so a consumer needs NO ref/bootstrap — the
88
143
  // kit remembers the theme like an i18n locale). Falls back to the first theme.
89
144
  const activeName = computed(
90
- () => props.modelValue ?? (theme.currentName.value || list.value[0]?.name || ""),
145
+ () => props.modelValue ?? tuner?.state.value.theme ?? (theme.currentName.value || list.value[0]?.name || ""),
91
146
  )
92
147
 
93
148
  // Apply the consumer's default on mount when nothing is cached yet. init()
@@ -113,6 +168,12 @@ const wantsLabel = computed(() => props.showLabel || props.variant === "pill")
113
168
  // Apply a theme through the kit's self-managed setter (it persists + updates
114
169
  // the shared `current`), then notify. The reveal animates from the click point.
115
170
  function applyTheme(t: TokenSet, ev?: MouseEvent) {
171
+ if (tuner) {
172
+ tuner.pickTheme(t.name, ev ? { x: ev.clientX, y: ev.clientY } : undefined)
173
+ emit("update:modelValue", t.name)
174
+ emit("change", tuner.theme.value)
175
+ return
176
+ }
116
177
  if (props.transition) {
117
178
  theme.setTheme(t, ev ? { x: ev.clientX, y: ev.clientY } : undefined)
118
179
  } else {
@@ -163,6 +224,12 @@ const systemPair = computed(() => {
163
224
  function followSystem() {
164
225
  const pair = systemPair.value
165
226
  if (!pair) return
227
+ // With the studio, "System" is its base theme, so the tuning stays on top.
228
+ if (tuner) {
229
+ tuner.pickTheme(STUDIO_SYSTEM)
230
+ emit("change", tuner.theme.value)
231
+ return
232
+ }
166
233
  theme.setSystem(pair)
167
234
  const next = theme.current.value as TokenSet | null
168
235
  if (next) {
@@ -172,7 +239,7 @@ function followSystem() {
172
239
  }
173
240
 
174
241
  const menu = computed<ContextMenuItemDef[]>(() => {
175
- const following = theme.system.value
242
+ const following = tuner ? tuner.state.value.theme === STUDIO_SYSTEM : theme.system.value
176
243
  const items: ContextMenuItemDef[] = list.value.map((t) => ({
177
244
  label: t.name,
178
245
  icon:
@@ -190,6 +257,15 @@ const menu = computed<ContextMenuItemDef[]>(() => {
190
257
  onSelect: followSystem,
191
258
  })
192
259
  }
260
+ // Apart from the themes, so it does not read as one more of them.
261
+ if (tuner) {
262
+ items.push({
263
+ label: text.value.openStudio,
264
+ icon: "lucide:sliders-horizontal",
265
+ separatorBefore: true,
266
+ onSelect: () => (studioOpen.value = true),
267
+ })
268
+ }
193
269
  return items
194
270
  })
195
271
 
@@ -212,7 +288,11 @@ onMounted(() => {
212
288
  })
213
289
 
214
290
  const labelName = computed(() =>
215
- hydrated.value ? activeName.value : (list.value[0]?.name ?? ""),
291
+ hydrated.value
292
+ ? activeName.value === STUDIO_SYSTEM
293
+ ? text.value.system
294
+ : activeName.value
295
+ : (list.value[0]?.name ?? ""),
216
296
  )
217
297
 
218
298
  const triggerLabel = computed(
@@ -221,46 +301,75 @@ const triggerLabel = computed(
221
301
  </script>
222
302
 
223
303
  <template>
224
- <LpContextMenu :items="menu">
225
- <button
226
- type="button"
227
- :aria-label="triggerLabel"
228
- :title="triggerLabel"
229
- class="inline-flex shrink-0 items-center justify-center gap-2 outline-none transition-colors duration-[var(--duration-fast)] focus-visible:ring-2 focus-visible:ring-ring"
230
- :class="
231
- wantsLabel
232
- ? [
233
- HEIGHTS[size],
234
- 'rounded-full border border-line bg-surface-raised pl-2 pr-3 text-ink hover:border-line-strong hover:bg-surface-soft',
235
- ]
236
- : [SIZES[size], 'rounded-control text-muted hover:bg-surface-soft hover:text-ink']
237
- "
238
- @click="cycle"
239
- >
240
- <!-- A preview of the applied theme: its surface and brand halves. Read from
241
- the applied variables rather than the theme object, so the server's
242
- render (painted from the cookie) and the client's agree. -->
243
- <span
244
- v-if="variant !== 'icon'"
245
- class="shrink-0 rounded-full shadow-[inset_0_0_0_1px_color-mix(in_srgb,var(--color-ink)_22%,transparent)]"
246
- :class="CHIP[size]"
247
- style="background: linear-gradient(135deg, var(--color-surface) 50%, var(--color-brand) 50%)"
248
- />
249
- <!-- sun/moon by mode, chosen by CSS from the applied theme's mode -->
250
- <span v-else class="grid shrink-0 place-items-center">
251
- <LpIcon
252
- name="lucide:moon"
253
- :size="ICON_SIZE[size]"
254
- class="in-data-[theme-mode=light]:hidden"
255
- />
256
- <LpIcon
257
- name="lucide:sun"
258
- :size="ICON_SIZE[size]"
259
- class="hidden in-data-[theme-mode=light]:block"
260
- />
261
- </span>
304
+ <!-- The studio opens by the trigger, from the last item of its menu. -->
305
+ <PopoverRoot v-model:open="studioOpen">
306
+ <PopoverAnchor as-child>
307
+ <span :data-lp-theme-trigger="anchorId" class="inline-flex shrink-0">
308
+ <LpContextMenu :items="menu">
309
+ <button
310
+ type="button"
311
+ :aria-label="triggerLabel"
312
+ :title="triggerLabel"
313
+ class="inline-flex shrink-0 items-center justify-center gap-2 outline-none transition-colors duration-[var(--duration-fast)] focus-visible:ring-2 focus-visible:ring-ring"
314
+ :class="
315
+ wantsLabel
316
+ ? [
317
+ HEIGHTS[size],
318
+ 'rounded-full border border-line bg-surface-raised pl-2 pr-3 text-ink hover:border-line-strong hover:bg-surface-soft',
319
+ ]
320
+ : [SIZES[size], 'rounded-control text-muted hover:bg-surface-soft hover:text-ink']
321
+ "
322
+ @click="cycle"
323
+ >
324
+ <!-- A preview of the applied theme: its surface and brand halves. Read from
325
+ the applied variables rather than the theme object, so the server's
326
+ render (painted from the cookie) and the client's agree. -->
327
+ <span
328
+ v-if="variant !== 'icon'"
329
+ class="shrink-0 rounded-full shadow-[inset_0_0_0_1px_color-mix(in_srgb,var(--color-ink)_22%,transparent)]"
330
+ :class="CHIP[size]"
331
+ style="background: linear-gradient(135deg, var(--color-surface) 50%, var(--color-brand) 50%)"
332
+ />
333
+ <!-- sun/moon by mode, chosen by CSS from the applied theme's mode -->
334
+ <span v-else class="grid shrink-0 place-items-center">
335
+ <LpIcon
336
+ name="lucide:moon"
337
+ :size="ICON_SIZE[size]"
338
+ class="in-data-[theme-mode=light]:hidden"
339
+ />
340
+ <LpIcon
341
+ name="lucide:sun"
342
+ :size="ICON_SIZE[size]"
343
+ class="hidden in-data-[theme-mode=light]:block"
344
+ />
345
+ </span>
262
346
 
263
- <span v-if="wantsLabel" class="text-sm font-medium">{{ labelName }}</span>
264
- </button>
265
- </LpContextMenu>
347
+ <!-- The name rolls and the pill eases to its width: the studio hangs
348
+ from this button, and a jump here jumped the whole panel. -->
349
+ <LpShift v-if="wantsLabel" axis="width">
350
+ <LpRollingText class="text-sm font-medium" :value="labelName" align="start" />
351
+ </LpShift>
352
+ </button>
353
+ </LpContextMenu>
354
+ </span>
355
+ </PopoverAnchor>
356
+ <PopoverPortal v-if="tuner" :to="portalTo">
357
+ <PopoverContent
358
+ :collision-padding="8"
359
+ side="bottom"
360
+ align="end"
361
+ :side-offset="6"
362
+ :aria-label="text.studio"
363
+ :class="[POPOVER_PANEL, 'z-(--z-popover) w-96 max-w-[calc(100vw-24px)] rounded-card p-3 text-sm text-ink outline-none']"
364
+ data-lp-glide-position
365
+ @interact-outside="keepForTrigger"
366
+ @escape-key-down="guardOutside"
367
+ >
368
+ <LpThemeStudio :studio="tuner" :hide="studioHide">
369
+ <template #start="scope"><slot name="studio-start" v-bind="scope" /></template>
370
+ <template #end="scope"><slot name="studio-end" v-bind="scope" /></template>
371
+ </LpThemeStudio>
372
+ </PopoverContent>
373
+ </PopoverPortal>
374
+ </PopoverRoot>
266
375
  </template>
@@ -20,7 +20,7 @@ export default { inheritAttrs: false }
20
20
  import { Time } from "@internationalized/date"
21
21
  import { TimeFieldInput, TimeFieldRoot } from "reka-ui"
22
22
  import { computed } from "vue"
23
- import { useFieldControl } from "../composables/useField"
23
+ import { focusLeft, useFieldControl } from "../composables/useField"
24
24
  import { useMergedAttrs } from "../composables/useMergedClass"
25
25
  import LpIcon from "./LpIcon.vue"
26
26
 
@@ -43,8 +43,13 @@ const props = withDefaults(
43
43
 
44
44
  const emit = defineEmits<{
45
45
  (e: "update:modelValue", value: string | undefined): void
46
+ (e: "blur", event: FocusEvent): void
46
47
  }>()
47
48
 
49
+ function onFocusOut(event: FocusEvent) {
50
+ if (focusLeft(event)) emit("blur", event)
51
+ }
52
+
48
53
  const value = computed(() => {
49
54
  const match = /^(\d{2}):(\d{2})/.exec(props.modelValue || "")
50
55
  return match ? new Time(Number(match[1]), Number(match[2])) : undefined
@@ -89,11 +94,14 @@ const { class: rootClass, attrs: rest } = useMergedAttrs(() =>
89
94
  :locale="locale"
90
95
  granularity="minute"
91
96
  :disabled="field.disabled"
97
+ :required="field.required"
92
98
  :aria-invalid="field.invalid || undefined"
93
99
  :aria-describedby="field.describedBy"
100
+ :aria-labelledby="field.labelId"
94
101
  :class="rootClass"
95
102
  v-bind="rest"
96
103
  @update:model-value="onUpdate"
104
+ @focusout="onFocusOut"
97
105
  >
98
106
  <LpIcon v-if="icon" name="lucide:clock" :size="15" class="mr-1.5 shrink-0 text-muted" />
99
107
  <!-- Indexed keys: in 12-hour mode the segments hold two "literal" parts
@@ -13,11 +13,13 @@ import {
13
13
  } from "reka-ui"
14
14
  import { nextTick, onBeforeUnmount, onMounted, ref, useId, watch } from "vue"
15
15
  import { prefersReducedMotion } from "../composables/easing"
16
- import { useClipboard } from "../composables/useClipboard"
17
16
  import { useLabels } from "../composables/useLpConfig"
18
17
  import { useToast, type ToastItem } from "../composables/useToast"
19
18
  import { CLOSE_ICON } from "./dropdown"
19
+ import LpCopyButton from "./LpCopyButton.vue"
20
20
  import LpIcon from "./LpIcon.vue"
21
+ import { toneLook } from "./tone"
22
+ import { usePortalTarget } from "../composables/useView"
21
23
 
22
24
  const props = defineProps<{ labels?: Partial<{ dismiss: string; copy: string; copied: string }> }>()
23
25
  const text = useLabels("toast", { dismiss: "Dismiss", copy: "Copy message", copied: "Copied" }, () => props.labels)
@@ -110,78 +112,59 @@ const variantIcon: Record<ToastItem["variant"], string> = {
110
112
  error: "lucide:circle-x",
111
113
  }
112
114
 
113
- // One token color drives icon + progress per variant.
114
- const variantColor: Record<ToastItem["variant"], string> = {
115
- info: "text-brand",
116
- success: "text-action",
117
- warning: "text-accent",
118
- error: "text-danger",
119
- }
120
- const variantBar: Record<ToastItem["variant"], string> = {
121
- info: "bg-brand",
122
- success: "bg-action",
123
- warning: "bg-accent",
124
- error: "bg-danger",
125
- }
126
-
127
- // reka pauses its own auto-dismiss timer while a toast is hovered/focused; mirror
128
- // that on the visual bar by carrying each toast's dismissAt forward over the
129
- // paused span, so the countdown matches when it actually closes.
130
- const pausedAt = new Map<number, number>()
131
115
 
132
- // Ticking clock so the progress bar animates smoothly. Runs only while there's
133
- // at least one auto-dismissing toast on screen — no idle interval otherwise.
134
- const now = ref(Date.now())
135
- let timer: ReturnType<typeof setInterval> | null = null
116
+ /*
117
+ * The countdown bar is the shared drain (.lp-drain): a CSS animation as long as
118
+ * the toast's duration. reka pauses its own auto-dismiss timer while a toast
119
+ * is hovered or focused and resumes it with the time that was left; pausing
120
+ * the animation alongside it does exactly the same, so the bar empties at the
121
+ * moment the toast actually closes, with no clock ticking in the meantime.
122
+ */
123
+ const paused = ref<Set<string>>(new Set())
136
124
 
137
- function startTimer() {
138
- if (timer === null) timer = setInterval(() => (now.value = Date.now()), 80)
139
- }
140
- function stopTimer() {
141
- if (timer !== null) {
142
- clearInterval(timer)
143
- timer = null
125
+ /*
126
+ * How far into its run a toast already was when its bar first rendered (a
127
+ * Toaster mounted after toasts were queued). Remembered per rendered toast,
128
+ * because the style is re-evaluated on every render and a delay that changed
129
+ * under a running animation would jump the bar.
130
+ */
131
+ const startOffsets = new Map<string, number>()
132
+ function drainStyle(t: ToastItem) {
133
+ const at = tag(t)
134
+ let offset = startOffsets.get(at)
135
+ if (offset === undefined) {
136
+ offset = t.dismissAt === null ? 0 : Math.max(0, t.duration - (t.dismissAt - Date.now()))
137
+ startOffsets.set(at, offset)
144
138
  }
139
+ return { animationDuration: `${t.duration}ms`, animationDelay: `${-offset}ms` }
145
140
  }
146
141
 
142
+ // Keyed by every rendered toast, not the count: a toast replaced through its
143
+ // `key` leaves the same length behind.
147
144
  watch(
148
- () => queue.length,
145
+ () => queue.map(tag).join("|"),
149
146
  () => {
150
- // Drop pause state for toasts that have left the queue (e.g. dismissed
151
- // while hovered) so the map can't grow unbounded over a long session.
152
- for (const id of pausedAt.keys()) {
153
- if (!queue.some((t) => t.id === id)) pausedAt.delete(id)
154
- }
155
- // Same for the closing set — a toast evicted by the queue cap or clear()
156
- // leaves without an animationend, so prune dead ids here.
147
+ // Drop per-toast state for toasts that have left the queue (e.g. dismissed
148
+ // while hovered) so it can't grow unbounded over a long session. A toast
149
+ // evicted by the queue cap or clear() leaves without an animationend, so
150
+ // the closing set is pruned here too.
151
+ const live = (at: string) => queue.some((t) => tag(t) === at)
152
+ for (const at of startOffsets.keys()) if (!live(at)) startOffsets.delete(at)
153
+ if ([...paused.value].some((at) => !live(at))) paused.value = new Set([...paused.value].filter(live))
157
154
  if (closing.value.size) {
158
- const live = new Set([...closing.value].filter((at) => queue.some((t) => tag(t) === at)))
159
- if (live.size !== closing.value.size) closing.value = live
155
+ const next = new Set([...closing.value].filter(live))
156
+ if (next.size !== closing.value.size) closing.value = next
160
157
  }
161
- if (queue.some((t) => t.dismissAt !== null)) startTimer()
162
- else stopTimer()
163
158
  },
164
- { immediate: true },
165
159
  )
166
- onBeforeUnmount(stopTimer)
167
160
 
168
161
  function onPause(t: ToastItem) {
169
- pausedAt.set(t.id, now.value)
162
+ paused.value = new Set(paused.value).add(tag(t))
170
163
  }
171
164
  function onResume(t: ToastItem) {
172
- const since = pausedAt.get(t.id)
173
- if (since !== undefined && t.dismissAt !== null) {
174
- t.dismissAt += Date.now() - since
175
- }
176
- pausedAt.delete(t.id)
177
- }
178
-
179
- function progress(t: ToastItem): number {
180
- if (!t.dismissAt || t.duration <= 0) return 0
181
- // While paused, freeze the bar at the moment hover started.
182
- const at = pausedAt.get(t.id) ?? now.value
183
- const remaining = t.dismissAt - at
184
- return Math.max(0, Math.min(100, (remaining / t.duration) * 100))
165
+ const next = new Set(paused.value)
166
+ next.delete(tag(t))
167
+ paused.value = next
185
168
  }
186
169
 
187
170
  /*
@@ -219,18 +202,9 @@ async function settle(t: ToastItem, run: () => void | Promise<void>) {
219
202
  * opts back into selection and keeps its presses from starting a swipe. A
220
203
  * click that ends a selection is not a click on the toast.
221
204
  */
222
- const { copy } = useClipboard()
223
- const copiedId = ref<string | null>(null)
224
- let copiedTimer: ReturnType<typeof setTimeout> | undefined
225
205
  const copyable = (t: ToastItem) => t.variant === "error" || t.variant === "warning"
226
-
227
- async function copyToast(t: ToastItem) {
228
- if (!(await copy([t.title, t.message, t.details].filter(Boolean).join("\n")))) return
229
- copiedId.value = tag(t)
230
- clearTimeout(copiedTimer)
231
- copiedTimer = setTimeout(() => (copiedId.value = null), 1500)
232
- }
233
- onBeforeUnmount(() => clearTimeout(copiedTimer))
206
+ // Read on click rather than bound, so a toast whose text is updated in place copies what it now says.
207
+ const toastText = (t: ToastItem) => () => [t.title, t.message, t.details].filter(Boolean).join("\n")
234
208
 
235
209
  function selecting(): boolean {
236
210
  return Boolean(window.getSelection()?.toString())
@@ -244,6 +218,9 @@ function runClick(t: ToastItem) {
244
218
  function runAction(t: ToastItem, action: NonNullable<ToastItem["actions"]>[number]) {
245
219
  return settle(t, action.onClick)
246
220
  }
221
+
222
+ // Overlays open in the window this component renders in (an LpFrame's, inside one).
223
+ const portalTo = usePortalTarget()
247
224
  </script>
248
225
 
249
226
  <template>
@@ -269,7 +246,7 @@ function runAction(t: ToastItem, action: NonNullable<ToastItem["actions"]>[numbe
269
246
  :name="variantIcon[t.variant]"
270
247
  :size="18"
271
248
  class="mt-0.5 shrink-0"
272
- :class="variantColor[t.variant]"
249
+ :class="toneLook(t.variant).text"
273
250
  />
274
251
  <div class="flex min-w-0 flex-1 flex-col gap-0.5">
275
252
  <ToastTitle v-if="t.title" class="text-sm font-semibold text-ink">
@@ -302,16 +279,18 @@ function runAction(t: ToastItem, action: NonNullable<ToastItem["actions"]>[numbe
302
279
  </div>
303
280
  </div>
304
281
 
305
- <button
282
+ <!-- Sized to the dismiss button beside it rather than to LpButton's
283
+ scale; the click stays off the toast, whose own click would run
284
+ its action or close it. -->
285
+ <LpCopyButton
306
286
  v-if="copyable(t)"
307
- type="button"
308
- class="-mt-1 rounded-inner p-1 text-muted outline-none transition-colors duration-[var(--duration-fast)] hover:text-ink focus-visible:ring-2 focus-visible:ring-ring"
309
- :aria-label="copiedId === tag(t) ? text.copied : text.copy"
310
- :title="copiedId === tag(t) ? text.copied : text.copy"
311
- @click.stop="copyToast(t)"
312
- >
313
- <LpIcon :name="copiedId === tag(t) ? 'lucide:check' : 'lucide:copy'" :size="15" />
314
- </button>
287
+ :value="toastText(t)"
288
+ variant="muted"
289
+ :icon-size="15"
290
+ class="-mt-1 size-auto aspect-auto rounded-inner p-1 hover:bg-transparent"
291
+ :labels="{ copy: text.copy, copied: text.copied }"
292
+ @click.stop
293
+ />
315
294
  <button
316
295
  type="button"
317
296
  class="group -mr-1 -mt-1 rounded-inner p-1 text-muted outline-none transition-colors duration-[var(--duration-fast)] hover:text-ink"
@@ -329,9 +308,10 @@ function runAction(t: ToastItem, action: NonNullable<ToastItem["actions"]>[numbe
329
308
  <!-- Countdown progress bar -->
330
309
  <div
331
310
  v-if="t.dismissAt"
332
- class="absolute inset-x-0 bottom-0 h-0.5 origin-left transition-transform duration-100 ease-linear motion-reduce:transition-none"
333
- :class="variantBar[t.variant]"
334
- :style="{ transform: `scaleX(${progress(t) / 100})` }"
311
+ class="lp-drain absolute inset-x-0 bottom-0 h-0.5"
312
+ :class="toneLook(t.variant).bg"
313
+ :style="drainStyle(t)"
314
+ :data-paused="paused.has(tag(t)) || undefined"
335
315
  />
336
316
  </ToastRoot>
337
317
 
@@ -339,7 +319,7 @@ function runAction(t: ToastItem, action: NonNullable<ToastItem["actions"]>[numbe
339
319
  consumer's app root creates (e.g. an opaque `#app { z-index }` wrapper).
340
320
  Modals portal to body too, so both compare at the same root level and
341
321
  --z-toast (200) > --z-modal (110) wins as intended. -->
342
- <Teleport to="body">
322
+ <Teleport :to="portalTo">
343
323
  <ToastViewport
344
324
  :data-lp-toasts="viewportId"
345
325
  class="fixed bottom-0 right-0 z-(--z-toast) flex w-[min(92vw,22rem)] flex-col gap-2 p-4 outline-none"
@@ -43,6 +43,7 @@ import {
43
43
  } from "../composables/usePillTransition"
44
44
  import { PANEL_SURFACE, TOOLTIP_ANIM } from "./dropdown"
45
45
  import { prefersReducedMotion } from "../composables/easing"
46
+ import { useOutsideGuard, usePortalTarget } from "../composables/useView"
46
47
 
47
48
  const props = withDefaults(
48
49
  defineProps<{
@@ -223,6 +224,12 @@ watch(
223
224
  onBeforeUnmount(() => {
224
225
  if (frame) cancelAnimationFrame(frame)
225
226
  })
227
+
228
+ // Overlays open in the window this component renders in (an LpFrame's, inside one).
229
+ const portalTo = usePortalTarget()
230
+ // What happens in another document (the page around a frame, or a frame in
231
+ // the page) is not "outside" for this layer.
232
+ const guardOutside = useOutsideGuard()
226
233
  </script>
227
234
 
228
235
  <template>
@@ -250,8 +257,10 @@ onBeforeUnmount(() => {
250
257
  >
251
258
  <slot />
252
259
  </TooltipTrigger>
253
- <TooltipPortal>
260
+ <TooltipPortal :to="portalTo">
254
261
  <TooltipContent
262
+ @interact-outside="guardOutside"
263
+ @escape-key-down="guardOutside"
255
264
  :side="side"
256
265
  :side-offset="sideOffset ?? 6"
257
266
  :collision-padding="8"