@leavepulse/ui 0.77.3 → 0.78.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (621) hide show
  1. package/README.md +68 -0
  2. package/dist/assets/{LpCommandPalette-DOIZHw-g.css → LpCommandPalette-DDJKICKh.css} +19 -19
  3. package/dist/assets/{LpDock-Cbh0wQUv.css → LpDock-Dw82tWVD.css} +3 -3
  4. package/dist/assets/{LpDropzone-B_qRq1-R.css → LpDropzone-DNbh_NFJ.css} +10 -10
  5. package/dist/assets/{LpImageEditor-XM5bAziW.css → LpImageEditor-Bgu20XLs.css} +6 -6
  6. package/dist/assets/{LpTable-Bv-EcQs7.css → LpTable-CCk0ZHo7.css} +19 -19
  7. package/dist/assets/LpTabs-Bh7a-Bdl.css +18 -0
  8. package/dist/chunks/AppShellContextNav-C6Ho2mof.js +49 -0
  9. package/dist/chunks/ChartRangeSelector-HO0sMMz6.js +129 -0
  10. package/dist/chunks/{ContextMenuItems-BXumicbh.js → ContextMenuItems-DmaKSJVv.js} +1 -1
  11. package/dist/chunks/{DropzoneLinkField-C-9BCGJn.js → DropzoneLinkField-CLLhd3WB.js} +2 -2
  12. package/dist/chunks/FieldLeading-mr81aEAP.js +27 -0
  13. package/dist/chunks/{FieldWrapper-CORPzo9s.js → FieldWrapper-DnuMTrqW.js} +1 -1
  14. package/dist/chunks/IconCloseButton-COZEsa9u.js +29 -0
  15. package/dist/chunks/{LayoutCanvas-CNOAUk7D.js → LayoutCanvas-CZKOltfA.js} +1 -1
  16. package/dist/chunks/{LayoutNode-DF5BMN0J.js → LayoutNode-D2f7v683.js} +45 -45
  17. package/dist/chunks/{LpActionBar-BP1ld7-S.js → LpActionBar-BGMk5KoG.js} +3 -3
  18. package/dist/chunks/{LpActivityLine-CJvzrNpj.js → LpActivityLine-Ea-t5Vza.js} +4 -2
  19. package/dist/chunks/{LpAddressInput-E8KtwU7-.js → LpAddressInput-BSS49jpm.js} +12 -11
  20. package/dist/chunks/{LpAlert-n5XPAZfv.js → LpAlert-CjvTZIKF.js} +3 -3
  21. package/dist/chunks/LpAppShell-BKbYCHkx.js +601 -0
  22. package/dist/chunks/{LpAutocomplete-C-SpPfBG.js → LpAutocomplete-DkY6CSyD.js} +24 -42
  23. package/dist/chunks/{LpAvatar-Cg6Az3wz.js → LpAvatar-cQVsNk6F.js} +2 -2
  24. package/dist/chunks/{LpBadge-wUYLN4U9.js → LpBadge-D5QQdDQ_.js} +2 -2
  25. package/dist/chunks/{LpBottomNav-CbNQQPuL.js → LpBottomNav-zxLGGbjm.js} +9 -2
  26. package/dist/chunks/{LpBreadcrumbs-DPdbth-c.js → LpBreadcrumbs-CIIBsygI.js} +4 -3
  27. package/dist/chunks/{LpButton-CFb44IjC.js → LpButton-DSHioIfD.js} +5 -4
  28. package/dist/chunks/{LpCalendar-CuoWks-0.js → LpCalendar-D4-LVjfF.js} +1 -1
  29. package/dist/chunks/{LpCard-r13WRqox.js → LpCard-BPtYXwnT.js} +1 -1
  30. package/dist/chunks/{LpCheckbox-Cs4SHaHX.js → LpCheckbox-D6RuDW4X.js} +38 -16
  31. package/dist/chunks/{LpCodeBlock-DRC8VbXW.js → LpCodeBlock-DBqy4RLd.js} +19 -41
  32. package/dist/chunks/{LpCodeDisplay-CE9TxCA4.js → LpCodeDisplay-D3BUAhV1.js} +5 -5
  33. package/dist/chunks/{LpCollapse-CWS9M_lm.js → LpCollapse-DrAw5DBZ.js} +5 -4
  34. package/dist/chunks/{LpColorPicker-1y1QJHZR.js → LpColorPicker-COHzV68R.js} +11 -12
  35. package/dist/chunks/{LpCommandScope-CtUhJe8T.js → LpCommandScope-DRUvHiX7.js} +1 -1
  36. package/dist/chunks/{LpConfirmDialog-DlD45Bi_.js → LpConfirmDialog-CnrmzkuN.js} +3 -3
  37. package/dist/chunks/{LpConfirmHost-BIWjBLmI.js → LpConfirmHost-CwbObVGH.js} +1 -1
  38. package/dist/chunks/{LpContextMenu-rx2tCjwk.js → LpContextMenu-Bx_IhQDS.js} +17 -19
  39. package/dist/chunks/{LpContextSwitcher-Dwh6dyID.js → LpContextSwitcher-1C1tExdl.js} +2 -2
  40. package/dist/chunks/{LpCopyButton-xF7wFVoU.js → LpCopyButton-sAMvWeLP.js} +3 -3
  41. package/dist/chunks/{LpCountdown-DgeY3cLG.js → LpCountdown-DVl7vXI1.js} +4 -3
  42. package/dist/chunks/{LpDataList-Jxyl9H5w.js → LpDataList-CaVfsozH.js} +13 -4
  43. package/dist/chunks/{LpDatePicker-CA0QTfBC.js → LpDatePicker-DwSDY0mt.js} +12 -18
  44. package/dist/chunks/{LpDateRangePicker-u3J51xXk.js → LpDateRangePicker-CNQXdTVv.js} +10 -16
  45. package/dist/chunks/{LpDisclosure-Cpa7Hc5T.js → LpDisclosure-DG7JCo-d.js} +2 -2
  46. package/dist/chunks/{LpDrawer-CrG8t_KP.js → LpDrawer-DXNQFpAA.js} +61 -61
  47. package/dist/chunks/{LpDropdownMenu-Dl7Z4ZhS.js → LpDropdownMenu-CLzxIOgM.js} +9 -17
  48. package/dist/chunks/{LpEditable-Cy6GLbFx.js → LpEditable-DVT0Rh8F.js} +9 -6
  49. package/dist/chunks/{LpEmptyState-EQHiRYhG.js → LpEmptyState-DurPF001.js} +1 -1
  50. package/dist/chunks/{LpFieldNotice-CzQ2_5rQ.js → LpFieldNotice-DJ10cYIo.js} +1 -1
  51. package/dist/chunks/LpFileTree-C-rJKzEQ.js +735 -0
  52. package/dist/chunks/{LpFileTreeNode-cT6BIWvz.js → LpFileTreeNode-BgYn4U1J.js} +116 -34
  53. package/dist/chunks/{LpFormField-Ch69Jd-X.js → LpFormField-BKYn9dyP.js} +3 -3
  54. package/dist/chunks/{LpFrame-BVQCQLDI.js → LpFrame-Bv5Fphte.js} +25 -11
  55. package/dist/chunks/{LpHintExample-9Py0wc-L.js → LpHintExample-YzCXiSUp.js} +1 -1
  56. package/dist/chunks/{LpHintLayer-CgObdUsM.js → LpHintLayer-BeKRsRon.js} +2 -2
  57. package/dist/chunks/{LpHints-Bz-BlaSl.js → LpHints-qf3xYsza.js} +3 -3
  58. package/dist/chunks/{LpHoverCard-spGxyw7T.js → LpHoverCard-CHE9jAJG.js} +29 -11
  59. package/dist/chunks/{LpIcon--5WSb3jJ.js → LpIcon-C6JpQ5td.js} +6 -3
  60. package/dist/chunks/{LpIdenticon-BzdC_qz9.js → LpIdenticon-Dbbr4fvt.js} +2 -1
  61. package/dist/chunks/{LpInfraNode-46XOCkfq.js → LpInfraNode-Bb5nbuFY.js} +19 -2
  62. package/dist/chunks/{LpInput-Tw-yxT4I.js → LpInput-D0O6dpuA.js} +32 -47
  63. package/dist/chunks/LpInspector-DO3fTCQ1.js +209 -0
  64. package/dist/chunks/{LpLaneNode-HtnQf2FF.js → LpLaneNode-Bqcdm8Rf.js} +19 -1
  65. package/dist/chunks/{LpLightbox-B95yu22B.js → LpLightbox-CtDTQV01.js} +57 -32
  66. package/dist/chunks/{LpLoader-DVwkOmt9.js → LpLoader-Bf1yZwaU.js} +1 -1
  67. package/dist/chunks/LpLogInspector-BdQlX6S-.js +119 -0
  68. package/dist/chunks/{LpLogViewer-ngqkns4m.js → LpLogViewer-umCZBapN.js} +234 -53
  69. package/dist/chunks/{LpMapPicker-DRGgh89B.js → LpMapPicker-D5_YLLBC.js} +4 -3
  70. package/dist/chunks/{LpMasonry--k0571qG.js → LpMasonry-DPYekFxs.js} +8 -5
  71. package/dist/chunks/{LpMediaGallery-Bkram-MO.js → LpMediaGallery-BYEwySoF.js} +3 -3
  72. package/dist/chunks/{LpModal-CoGhLTyH.js → LpModal-DDvrSAnf.js} +11 -13
  73. package/dist/chunks/{LpModalNav-BGvs2y5Q.js → LpModalNav-gOi1Giz1.js} +11 -37
  74. package/dist/chunks/{LpNotificationBell-B0034Lk-.js → LpNotificationBell-DUbVyK6R.js} +9 -9
  75. package/dist/chunks/{LpNumberField-e0IBkWU1.js → LpNumberField-Csg-1Zh_.js} +25 -20
  76. package/dist/chunks/{LpOtpInput-CeBPsM7D.js → LpOtpInput-BRpLBbOD.js} +2 -2
  77. package/dist/chunks/LpPageScrollbar-D2O0xhJw.js +164 -0
  78. package/dist/chunks/{LpPagination-BIIiYHXR.js → LpPagination-snQ020xQ.js} +6 -4
  79. package/dist/chunks/{LpPaletteTheme-DiLYKQiZ.js → LpPaletteTheme-BIXpZLGe.js} +4 -4
  80. package/dist/chunks/{LpPasswordInput-BkHKQDhM.js → LpPasswordInput-D4HPhqfV.js} +2 -2
  81. package/dist/chunks/{LpPhoneInput-CETEp5W7.js → LpPhoneInput-BSL7NX4z.js} +15 -26
  82. package/dist/chunks/{LpPie-DrZepi0p.js → LpPie-oVNHjsKD.js} +22 -21
  83. package/dist/chunks/{LpPipeline-B3aOVOkV.js → LpPipeline-BSXT6oUJ.js} +16 -7
  84. package/dist/chunks/{LpPopover-DmZ3wUtP.js → LpPopover-BQSuxtai.js} +10 -1
  85. package/dist/chunks/{LpProgress-Yr6ONG5t.js → LpProgress-D_MSjowb.js} +2 -1
  86. package/dist/chunks/{LpQuota-Dk2MyI2Z.js → LpQuota-CgWPgdVt.js} +1 -1
  87. package/dist/chunks/{LpRadioGroup-ggaPL92T.js → LpRadioGroup-BrazjLY_.js} +4 -4
  88. package/dist/chunks/{LpRangeCalendar-CYYUYqJ7.js → LpRangeCalendar-BrqlGZzE.js} +1 -1
  89. package/dist/chunks/{LpRangeSlider-CM-0ZITZ.js → LpRangeSlider-fz61tzc-.js} +5 -9
  90. package/dist/chunks/{LpRelativeTime-DwgCb5-8.js → LpRelativeTime-0zI-44ve.js} +1 -1
  91. package/dist/chunks/{LpRepeater-D7ZmnIYd.js → LpRepeater-BkJHSPI1.js} +3 -3
  92. package/dist/chunks/{LpRevealOnScroll-DcQjexLy.js → LpRevealOnScroll-CPwyiQAr.js} +1 -1
  93. package/dist/chunks/{LpScramble-Bn9OoOtC.js → LpScramble-DsE77iVp.js} +1 -1
  94. package/dist/chunks/{LpScrollArea-MyEnVB_5.js → LpScrollArea-CqvwfZs2.js} +31 -22
  95. package/dist/chunks/{LpSecretField-BbmqM9ye.js → LpSecretField-B2roc4Iz.js} +10 -9
  96. package/dist/chunks/{LpSection-DFK_uoJE.js → LpSection-BfteipxU.js} +1 -1
  97. package/dist/chunks/{LpSegmented-CeACsMMA.js → LpSegmented-DbEJhbv-.js} +8 -10
  98. package/dist/chunks/{LpSelect-Cxq4yBGd.js → LpSelect-Y-iuSymW.js} +47 -30
  99. package/dist/chunks/{LpServiceNode-D8uJ8sGk.js → LpServiceNode-Cln2ntdw.js} +17 -0
  100. package/dist/chunks/{LpShaderBackdrop-Bote0iuy.js → LpShaderBackdrop-wQygYb5h.js} +13 -10
  101. package/dist/chunks/{LpShift-CCq7dofl.js → LpShift-DAgS_d2x.js} +18 -15
  102. package/dist/chunks/{LpSidebar-DzE1783C.js → LpSidebar-DDNJzb69.js} +74 -87
  103. package/dist/chunks/{LpSidebarNav-DE3PhXVP.js → LpSidebarNav-CE0jQS8i.js} +8 -6
  104. package/dist/chunks/{LpSidebarRow-BRYhNDSo.js → LpSidebarRow-DOTg1txp.js} +6 -5
  105. package/dist/chunks/{LpSlider-DxYoCJJO.js → LpSlider-A4ixovgS.js} +5 -9
  106. package/dist/chunks/{LpSpotlight-4F3NeUMY.js → LpSpotlight-DwxkhFgR.js} +2 -1
  107. package/dist/chunks/{LpStat-f2LfDCJ4.js → LpStat-DPjc6_fX.js} +19 -6
  108. package/dist/chunks/{LpStatus-DglxttQ7.js → LpStatus-DGJFwk5F.js} +2 -2
  109. package/dist/chunks/{LpStepMark-DpzKVyh8.js → LpStepMark-Dw2MRTrt.js} +2 -2
  110. package/dist/chunks/{LpStepper-DpsocBNJ.js → LpStepper-DNnh7lCu.js} +1 -1
  111. package/dist/chunks/{LpSwap-C96c0LWh.js → LpSwap-M7nFeW3C.js} +1 -1
  112. package/dist/chunks/{LpSwitch-BnXd1llc.js → LpSwitch-DnC2-0aV.js} +3 -3
  113. package/dist/chunks/{LpTabFace-DPCwKkH2.js → LpTabFace-DQoV8mQG.js} +2 -2
  114. package/dist/chunks/{LpTableFilter-CiVBpaq0.js → LpTableFilter-B_XMOeAh.js} +4 -4
  115. package/dist/chunks/{LpTableOfContents-BN5zvpnx.js → LpTableOfContents-CKkWEK6R.js} +14 -9
  116. package/dist/chunks/{LpTagsInput-C-mDG648.js → LpTagsInput-Dt0mteSj.js} +19 -27
  117. package/dist/chunks/{LpTextarea-TE-yZv2U.js → LpTextarea-Co0b9PVx.js} +8 -16
  118. package/dist/chunks/LpThemeProvider-XK6ZeWAE.js +81 -0
  119. package/dist/chunks/{LpThemeStudio-BpORDvH6.js → LpThemeStudio-ChU0_Ga8.js} +12 -195
  120. package/dist/chunks/{LpThemeSwitcher-BnEn_mvf.js → LpThemeSwitcher-DmCZ21P_.js} +8 -6
  121. package/dist/chunks/{LpTimeField-BawSVsmt.js → LpTimeField-DeOcKWJq.js} +6 -9
  122. package/dist/chunks/{LpToaster-IwTXjbbU.js → LpToaster-Ct_ZOhCh.js} +10 -8
  123. package/dist/chunks/{LpTooltip-BcBvIx4z.js → LpTooltip-Bz_rrq5S.js} +56 -24
  124. package/dist/chunks/LpTopologyCanvas-jqz1KCq2.js +678 -0
  125. package/dist/chunks/{LpTour-BZq-mUMv.js → LpTour-ClKYhx4h.js} +7 -5
  126. package/dist/chunks/{LpTruncate-CflAIwiJ.js → LpTruncate-ellq9RYM.js} +6 -5
  127. package/dist/chunks/{LpTypewriter-BCL_a6wE.js → LpTypewriter-CZIzpdbe.js} +2 -1
  128. package/dist/chunks/{LpUptimeBar-DqNvvMBK.js → LpUptimeBar-BoxcCDVY.js} +3 -3
  129. package/dist/chunks/{LpVideoBackdrop-DS1wGv-X.js → LpVideoBackdrop-Ct4St4ZL.js} +10 -13
  130. package/dist/chunks/LpWindow-B8YSRVTH.js +297 -0
  131. package/dist/chunks/LpWorkspaceSwitcher-BeNHCLd5.js +139 -0
  132. package/dist/chunks/{MenuTitle-BmCNNTKH.js → MenuTitle-Bh5dAdV1.js} +1 -1
  133. package/dist/chunks/{OptionContent-22UT8zLn.js → OptionContent-CGb8QlHA.js} +1 -1
  134. package/dist/chunks/{PickerField-Bfh8id3d.js → PickerField-BM1dyafh.js} +4 -4
  135. package/dist/chunks/{RevealToggle-BEit1n6Z.js → RevealToggle-D_iyrB8d.js} +3 -3
  136. package/dist/chunks/SliderRail-DhPREa_R.js +18 -0
  137. package/dist/chunks/{TourCard-DNclH3u_.js → TourCard-DAzTYfav.js} +4 -4
  138. package/dist/chunks/{icons-DMITlfw5.js → icons-CYfcpSDS.js} +26 -10
  139. package/dist/component-names.d.ts +1 -1
  140. package/dist/components/AppShellContextNav.vue.d.ts +13 -0
  141. package/dist/components/AppShellContextNav.vue.js +4 -0
  142. package/dist/components/ChartRangeSelector.vue.d.ts +21 -0
  143. package/dist/components/ChartRangeSelector.vue.js +4 -0
  144. package/dist/components/ContextMenuItems.vue.js +1 -1
  145. package/dist/components/DropzoneLinkField.vue.d.ts +2 -2
  146. package/dist/components/DropzoneLinkField.vue.js +1 -1
  147. package/dist/components/FieldLeading.vue.d.ts +16 -0
  148. package/dist/components/FieldLeading.vue.js +4 -0
  149. package/dist/components/FieldWrapper.vue.js +1 -1
  150. package/dist/components/IconCloseButton.vue.d.ts +7 -0
  151. package/dist/components/IconCloseButton.vue.js +4 -0
  152. package/dist/components/LayoutCanvas.vue.js +1 -1
  153. package/dist/components/LayoutNode.vue.js +1 -1
  154. package/dist/components/LpActionBar.vue.js +1 -1
  155. package/dist/components/LpActivityLine.vue.js +1 -1
  156. package/dist/components/LpAddressInput.vue.js +1 -1
  157. package/dist/components/LpAlert.vue.js +1 -1
  158. package/dist/components/LpAppShell.vue.d.ts +125 -2
  159. package/dist/components/LpAppShell.vue.js +1 -1
  160. package/dist/components/LpAutocomplete.vue.js +1 -1
  161. package/dist/components/LpAvatar.vue.js +1 -1
  162. package/dist/components/LpBadge.vue.js +1 -1
  163. package/dist/components/LpBottomNav.vue.d.ts +5 -0
  164. package/dist/components/LpBottomNav.vue.js +1 -1
  165. package/dist/components/LpBreadcrumbs.vue.js +1 -1
  166. package/dist/components/LpButton.vue.d.ts +14 -2
  167. package/dist/components/LpButton.vue.js +1 -1
  168. package/dist/components/LpCalendar.vue.js +1 -1
  169. package/dist/components/LpCard.vue.js +1 -1
  170. package/dist/components/LpChart.vue.d.ts +74 -2
  171. package/dist/components/LpChart.vue.js +326 -85
  172. package/dist/components/LpCheckbox.vue.d.ts +6 -2
  173. package/dist/components/LpCheckbox.vue.js +1 -1
  174. package/dist/components/LpCodeBlock.vue.js +1 -1
  175. package/dist/components/LpCodeDisplay.vue.js +1 -1
  176. package/dist/components/LpCollapse.vue.js +1 -1
  177. package/dist/components/LpColorPicker.vue.js +1 -1
  178. package/dist/components/LpCommandPalette.vue.d.ts +8 -0
  179. package/dist/components/LpCommandPalette.vue.js +107 -56
  180. package/dist/components/LpCommandScope.vue.js +1 -1
  181. package/dist/components/LpConfirmDialog.vue.js +1 -1
  182. package/dist/components/LpConfirmHost.vue.js +1 -1
  183. package/dist/components/LpContextMenu.vue.js +1 -1
  184. package/dist/components/LpContextSwitcher.vue.js +1 -1
  185. package/dist/components/LpCopyButton.vue.js +1 -1
  186. package/dist/components/LpCountdown.vue.js +1 -1
  187. package/dist/components/LpDataList.vue.d.ts +1 -1
  188. package/dist/components/LpDataList.vue.js +1 -1
  189. package/dist/components/LpDatePicker.vue.d.ts +3 -15
  190. package/dist/components/LpDatePicker.vue.js +1 -1
  191. package/dist/components/LpDateRangePicker.vue.d.ts +3 -13
  192. package/dist/components/LpDateRangePicker.vue.js +1 -1
  193. package/dist/components/LpDisclosure.vue.js +1 -1
  194. package/dist/components/LpDock.vue.js +21 -18
  195. package/dist/components/LpDrawer.vue.d.ts +1 -1
  196. package/dist/components/LpDrawer.vue.js +1 -1
  197. package/dist/components/LpDropdownMenu.vue.js +1 -1
  198. package/dist/components/LpDropzone.vue.js +14 -11
  199. package/dist/components/LpEditable.vue.js +1 -1
  200. package/dist/components/LpEmptyState.vue.js +1 -1
  201. package/dist/components/LpFieldNotice.vue.js +1 -1
  202. package/dist/components/LpFileTree.vue.d.ts +96 -6
  203. package/dist/components/LpFileTree.vue.js +1 -1
  204. package/dist/components/LpFileTreeNode.vue.d.ts +48 -0
  205. package/dist/components/LpFileTreeNode.vue.js +1 -1
  206. package/dist/components/LpFormField.vue.js +1 -1
  207. package/dist/components/LpFrame.vue.js +1 -1
  208. package/dist/components/LpGauge.vue.d.ts +1 -1
  209. package/dist/components/LpHintExample.vue.js +1 -1
  210. package/dist/components/LpHintLayer.vue.js +1 -1
  211. package/dist/components/LpHints.vue.js +1 -1
  212. package/dist/components/LpHoverCard.vue.js +1 -1
  213. package/dist/components/LpIcon.vue.d.ts +2 -0
  214. package/dist/components/LpIcon.vue.js +1 -1
  215. package/dist/components/LpIdenticon.vue.js +1 -1
  216. package/dist/components/LpImageEditor.vue.js +7 -6
  217. package/dist/components/LpInfraNode.vue.d.ts +11 -2
  218. package/dist/components/LpInfraNode.vue.js +1 -1
  219. package/dist/components/LpInput.vue.d.ts +1 -1
  220. package/dist/components/LpInput.vue.js +1 -1
  221. package/dist/components/LpInspector.vue.d.ts +87 -0
  222. package/dist/components/LpInspector.vue.js +4 -0
  223. package/dist/components/LpLaneNode.vue.d.ts +2 -3
  224. package/dist/components/LpLaneNode.vue.js +1 -1
  225. package/dist/components/LpLightbox.vue.d.ts +8 -0
  226. package/dist/components/LpLightbox.vue.js +1 -1
  227. package/dist/components/LpLoader.vue.d.ts +1 -1
  228. package/dist/components/LpLoader.vue.js +1 -1
  229. package/dist/components/LpLogInspector.vue.d.ts +18 -0
  230. package/dist/components/LpLogInspector.vue.js +4 -0
  231. package/dist/components/LpLogViewer.vue.d.ts +93 -0
  232. package/dist/components/LpLogViewer.vue.js +1 -1
  233. package/dist/components/LpMapPicker.vue.js +1 -1
  234. package/dist/components/LpMasonry.vue.js +1 -1
  235. package/dist/components/LpMediaGallery.vue.js +1 -1
  236. package/dist/components/LpModal.vue.d.ts +4 -2
  237. package/dist/components/LpModal.vue.js +1 -1
  238. package/dist/components/LpModalNav.vue.d.ts +5 -5
  239. package/dist/components/LpModalNav.vue.js +1 -1
  240. package/dist/components/LpNotificationBell.vue.d.ts +2 -2
  241. package/dist/components/LpNotificationBell.vue.js +1 -1
  242. package/dist/components/LpNumberField.vue.d.ts +13 -2
  243. package/dist/components/LpNumberField.vue.js +1 -1
  244. package/dist/components/LpOtpInput.vue.js +1 -1
  245. package/dist/components/LpPageScrollbar.vue.d.ts +10 -0
  246. package/dist/components/LpPageScrollbar.vue.js +4 -0
  247. package/dist/components/LpPagination.vue.js +1 -1
  248. package/dist/components/LpPaletteFacts.vue.js +1 -1
  249. package/dist/components/LpPaletteHeader.vue.js +2 -2
  250. package/dist/components/LpPaletteTheme.vue.js +1 -1
  251. package/dist/components/LpPasswordInput.vue.js +1 -1
  252. package/dist/components/LpPhoneInput.vue.js +1 -1
  253. package/dist/components/LpPie.vue.d.ts +2 -2
  254. package/dist/components/LpPie.vue.js +1 -1
  255. package/dist/components/LpPipeline.vue.d.ts +4 -0
  256. package/dist/components/LpPipeline.vue.js +1 -1
  257. package/dist/components/LpPopover.vue.d.ts +7 -3
  258. package/dist/components/LpPopover.vue.js +1 -1
  259. package/dist/components/LpProgress.vue.d.ts +1 -1
  260. package/dist/components/LpProgress.vue.js +1 -1
  261. package/dist/components/LpQuota.vue.js +1 -1
  262. package/dist/components/LpRadioGroup.vue.js +1 -1
  263. package/dist/components/LpRangeCalendar.vue.js +1 -1
  264. package/dist/components/LpRangeSlider.vue.js +1 -1
  265. package/dist/components/LpRelativeTime.vue.js +1 -1
  266. package/dist/components/LpRepeater.vue.js +1 -1
  267. package/dist/components/LpRevealOnScroll.vue.js +1 -1
  268. package/dist/components/LpScramble.vue.js +1 -1
  269. package/dist/components/LpScrollArea.vue.js +1 -1
  270. package/dist/components/LpSecretField.vue.js +1 -1
  271. package/dist/components/LpSection.vue.js +1 -1
  272. package/dist/components/LpSegmented.vue.js +1 -1
  273. package/dist/components/LpSelect.vue.js +1 -1
  274. package/dist/components/LpServiceNode.vue.d.ts +8 -2
  275. package/dist/components/LpServiceNode.vue.js +1 -1
  276. package/dist/components/LpShaderBackdrop.vue.js +1 -1
  277. package/dist/components/LpShift.vue.js +1 -1
  278. package/dist/components/LpShortcutsDialog.vue.js +1 -1
  279. package/dist/components/LpSidebar.vue.d.ts +1 -1
  280. package/dist/components/LpSidebar.vue.js +1 -1
  281. package/dist/components/LpSidebarNav.vue.js +1 -1
  282. package/dist/components/LpSidebarRow.vue.js +1 -1
  283. package/dist/components/LpSlider.vue.js +1 -1
  284. package/dist/components/LpSparkline.vue.d.ts +1 -1
  285. package/dist/components/LpSpotlight.vue.js +1 -1
  286. package/dist/components/LpStat.vue.d.ts +16 -0
  287. package/dist/components/LpStat.vue.js +1 -1
  288. package/dist/components/LpStatus.vue.js +1 -1
  289. package/dist/components/LpStepMark.vue.js +1 -1
  290. package/dist/components/LpStepper.vue.js +1 -1
  291. package/dist/components/LpSwap.vue.js +1 -1
  292. package/dist/components/LpSwitch.vue.js +1 -1
  293. package/dist/components/LpTabFace.vue.js +1 -1
  294. package/dist/components/LpTable.vue.d.ts +132 -0
  295. package/dist/components/LpTable.vue.js +547 -135
  296. package/dist/components/LpTableFilter.vue.js +1 -1
  297. package/dist/components/LpTableOfContents.vue.js +1 -1
  298. package/dist/components/LpTabs.vue.js +6 -5
  299. package/dist/components/LpTagsInput.vue.js +1 -1
  300. package/dist/components/LpTextarea.vue.js +1 -1
  301. package/dist/components/LpThemeProvider.vue.d.ts +2 -0
  302. package/dist/components/LpThemeProvider.vue.js +1 -1
  303. package/dist/components/LpThemeStudio.vue.js +1 -1
  304. package/dist/components/LpThemeSwitcher.vue.js +1 -1
  305. package/dist/components/LpTimeField.vue.js +1 -1
  306. package/dist/components/LpToaster.vue.js +1 -1
  307. package/dist/components/LpTooltip.vue.js +1 -1
  308. package/dist/components/LpTopologyCanvas.vue.d.ts +131 -5
  309. package/dist/components/LpTopologyCanvas.vue.js +1 -1
  310. package/dist/components/LpTour.vue.js +1 -1
  311. package/dist/components/LpTruncate.vue.js +1 -1
  312. package/dist/components/LpTypewriter.vue.js +1 -1
  313. package/dist/components/LpUptimeBar.vue.js +1 -1
  314. package/dist/components/LpVideoBackdrop.vue.js +1 -1
  315. package/dist/components/LpWindow.vue.d.ts +99 -0
  316. package/dist/components/LpWindow.vue.js +4 -0
  317. package/dist/components/LpWorkspaceSwitcher.vue.d.ts +18 -0
  318. package/dist/components/LpWorkspaceSwitcher.vue.js +4 -0
  319. package/dist/components/MenuTitle.vue.js +1 -1
  320. package/dist/components/OptionContent.vue.js +1 -1
  321. package/dist/components/PickerField.vue.d.ts +2 -2
  322. package/dist/components/PickerField.vue.js +1 -1
  323. package/dist/components/RevealToggle.vue.js +1 -1
  324. package/dist/components/SliderRail.vue.d.ts +3 -0
  325. package/dist/components/SliderRail.vue.js +4 -0
  326. package/dist/components/TourCard.vue.js +1 -1
  327. package/dist/components/address.js +2 -1
  328. package/dist/components/appShell.d.ts +36 -0
  329. package/dist/components/appShell.js +30 -0
  330. package/dist/components/calendar.d.ts +12 -3
  331. package/dist/components/calendar.js +2 -5
  332. package/dist/components/chart.d.ts +21 -0
  333. package/dist/components/chart.js +4 -3
  334. package/dist/components/chartWindow.d.ts +57 -0
  335. package/dist/components/chartWindow.js +136 -0
  336. package/dist/components/color.js +1 -1
  337. package/dist/components/commandPalette.d.ts +16 -2
  338. package/dist/components/commandPalette.js +39 -4
  339. package/dist/components/controlSize.d.ts +5 -0
  340. package/dist/components/controlSize.js +8 -0
  341. package/dist/components/dropdown.d.ts +5 -0
  342. package/dist/components/dropdown.js +2 -0
  343. package/dist/components/exportData.d.ts +6 -1
  344. package/dist/components/exportData.js +25 -18
  345. package/dist/components/fileTree.d.ts +67 -0
  346. package/dist/components/fileTree.js +141 -1
  347. package/dist/components/identicon.js +3 -2
  348. package/dist/components/logTools.d.ts +93 -0
  349. package/dist/components/logTools.js +210 -0
  350. package/dist/components/menuWhen.js +1 -1
  351. package/dist/components/meter.js +2 -1
  352. package/dist/components/pipeline.d.ts +2 -0
  353. package/dist/components/pipeline.js +5 -0
  354. package/dist/components/relativeTime.js +2 -1
  355. package/dist/components/reveal.js +2 -1
  356. package/dist/components/scrollbar.d.ts +34 -0
  357. package/dist/components/scrollbar.js +47 -0
  358. package/dist/components/select.d.ts +5 -0
  359. package/dist/components/select.js +10 -0
  360. package/dist/components/sidebar.d.ts +1 -0
  361. package/dist/components/tableFeatures.d.ts +58 -0
  362. package/dist/components/tableFeatures.js +87 -0
  363. package/dist/components/topologyGraph.d.ts +164 -0
  364. package/dist/components/topologyGraph.js +416 -0
  365. package/dist/components/tour.js +5 -3
  366. package/dist/components/videoBackdrop.js +7 -6
  367. package/dist/composables/clamp.d.ts +2 -0
  368. package/dist/composables/clamp.js +4 -0
  369. package/dist/composables/devWarn.d.ts +3 -0
  370. package/dist/composables/devWarn.js +14 -0
  371. package/dist/composables/easing.d.ts +13 -4
  372. package/dist/composables/easing.js +16 -10
  373. package/dist/composables/overlayHistory.js +14 -1
  374. package/dist/composables/pointerDrag.d.ts +15 -0
  375. package/dist/composables/pointerDrag.js +40 -0
  376. package/dist/composables/propCheck.d.ts +10 -0
  377. package/dist/composables/propCheck.js +89 -0
  378. package/dist/composables/realm.d.ts +6 -0
  379. package/dist/composables/realm.js +23 -0
  380. package/dist/composables/scroll.js +4 -2
  381. package/dist/composables/scrollMemory.d.ts +12 -1
  382. package/dist/composables/scrollMemory.js +36 -12
  383. package/dist/composables/spatialNav.js +2 -1
  384. package/dist/composables/textEntry.d.ts +17 -0
  385. package/dist/composables/textEntry.js +19 -0
  386. package/dist/composables/useAsyncTask.d.ts +6 -0
  387. package/dist/composables/useChartSync.d.ts +5 -0
  388. package/dist/composables/useChartSync.js +33 -0
  389. package/dist/composables/useCloseGuard.d.ts +3 -1
  390. package/dist/composables/useCloseGuard.js +6 -3
  391. package/dist/composables/useCommands.d.ts +50 -0
  392. package/dist/composables/useCommands.js +66 -0
  393. package/dist/composables/useField.d.ts +37 -0
  394. package/dist/composables/useField.js +27 -2
  395. package/dist/composables/useFitScale.js +4 -1
  396. package/dist/composables/useFormDraft.d.ts +13 -2
  397. package/dist/composables/useFormDraft.js +17 -9
  398. package/dist/composables/useHintSystem.js +2 -2
  399. package/dist/composables/useHotkeys.js +11 -32
  400. package/dist/composables/useInView.js +2 -1
  401. package/dist/composables/useInputFilter.js +7 -5
  402. package/dist/composables/useInputMask.js +5 -3
  403. package/dist/composables/useLinkedView.d.ts +97 -0
  404. package/dist/composables/useLinkedView.js +139 -0
  405. package/dist/composables/useOptimisticToggle.d.ts +3 -1
  406. package/dist/composables/useOptimisticToggle.js +2 -1
  407. package/dist/composables/useOverlayHistory.d.ts +7 -0
  408. package/dist/composables/useOverlayHistory.js +16 -2
  409. package/dist/composables/usePaintSelect.js +18 -14
  410. package/dist/composables/usePaletteCommands.d.ts +7 -0
  411. package/dist/composables/usePaletteCommands.js +16 -3
  412. package/dist/composables/usePersisted.d.ts +10 -0
  413. package/dist/composables/usePersisted.js +1 -1
  414. package/dist/composables/usePillTransition.d.ts +2 -2
  415. package/dist/composables/usePillTransition.js +5 -5
  416. package/dist/composables/useResizeObserver.js +2 -1
  417. package/dist/composables/useReveal.js +2 -1
  418. package/dist/composables/useRovingFocus.d.ts +2 -2
  419. package/dist/composables/useRovingFocus.js +9 -11
  420. package/dist/composables/useScramble.js +7 -1
  421. package/dist/composables/useShift.js +8 -6
  422. package/dist/composables/useTilt.js +2 -1
  423. package/dist/composables/useTween.js +9 -5
  424. package/dist/composables/useView.d.ts +1 -0
  425. package/dist/composables/useView.js +14 -2
  426. package/dist/composables/useZoomPan.js +2 -4
  427. package/dist/composables/windowing.d.ts +40 -0
  428. package/dist/composables/windowing.js +118 -0
  429. package/dist/icons-all.js +1 -1
  430. package/dist/icons.d.ts +4 -0
  431. package/dist/index.d.ts +31 -5
  432. package/dist/index.js +312 -264
  433. package/dist/layout/tree.js +19 -15
  434. package/dist/layout/useLayout.d.ts +2 -0
  435. package/dist/layout/useLayout.js +5 -1
  436. package/dist/layout/useWorkspaces.d.ts +52 -0
  437. package/dist/layout/useWorkspaces.js +140 -0
  438. package/dist/{chunks/backdrop-Dkf2vM81.js → theme/backdrop.js} +5 -5
  439. package/dist/theme/config.js +25 -0
  440. package/dist/theme/define.js +61 -0
  441. package/dist/{chunks/palette-Co8tSASo.js → theme/palette.js} +2 -2
  442. package/dist/theme/presets.js +239 -0
  443. package/dist/theme/studio.js +134 -0
  444. package/dist/theme/tokens.js +63 -0
  445. package/dist/theme/useTheme.d.ts +3 -2
  446. package/dist/theme/useTheme.js +325 -0
  447. package/package.json +8 -1
  448. package/src/component-names.ts +6 -1
  449. package/src/components/AppShellContextNav.vue +44 -0
  450. package/src/components/ChartRangeSelector.vue +120 -0
  451. package/src/components/FieldLeading.vue +17 -0
  452. package/src/components/IconCloseButton.vue +21 -0
  453. package/src/components/LayoutCanvas.vue +3 -0
  454. package/src/components/LayoutNode.vue +37 -38
  455. package/src/components/LpActivityLine.vue +4 -2
  456. package/src/components/LpAddressInput.vue +3 -3
  457. package/src/components/LpAppShell.vue +385 -24
  458. package/src/components/LpAutocomplete.vue +13 -30
  459. package/src/components/LpBottomNav.vue +9 -1
  460. package/src/components/LpBreadcrumbs.vue +2 -1
  461. package/src/components/LpButton.vue +11 -4
  462. package/src/components/LpChart.vue +336 -37
  463. package/src/components/LpCheckbox.vue +43 -9
  464. package/src/components/LpCodeBlock.vue +17 -23
  465. package/src/components/LpCollapse.vue +5 -4
  466. package/src/components/LpColorPicker.vue +8 -9
  467. package/src/components/LpCommandPalette.vue +133 -50
  468. package/src/components/LpContextMenu.vue +22 -13
  469. package/src/components/LpCountdown.vue +2 -1
  470. package/src/components/LpDataList.vue +13 -1
  471. package/src/components/LpDatePicker.vue +5 -17
  472. package/src/components/LpDateRangePicker.vue +5 -16
  473. package/src/components/LpDock.vue +25 -16
  474. package/src/components/LpDrawer.vue +70 -61
  475. package/src/components/LpDropdownMenu.vue +6 -10
  476. package/src/components/LpDropzone.vue +12 -7
  477. package/src/components/LpEditable.vue +6 -2
  478. package/src/components/LpFileTree.vue +507 -66
  479. package/src/components/LpFileTreeNode.vue +106 -5
  480. package/src/components/LpFormField.vue +1 -1
  481. package/src/components/LpFrame.vue +33 -13
  482. package/src/components/LpHoverCard.vue +37 -5
  483. package/src/components/LpIcon.vue +6 -2
  484. package/src/components/LpIdenticon.vue +2 -1
  485. package/src/components/LpImageEditor.vue +5 -2
  486. package/src/components/LpInfraNode.vue +4 -3
  487. package/src/components/LpInput.vue +19 -26
  488. package/src/components/LpInspector.vue +197 -0
  489. package/src/components/LpLaneNode.vue +5 -1
  490. package/src/components/LpLightbox.vue +74 -29
  491. package/src/components/LpLogInspector.vue +85 -0
  492. package/src/components/LpLogViewer.vue +239 -29
  493. package/src/components/LpMapPicker.vue +2 -1
  494. package/src/components/LpMasonry.vue +8 -5
  495. package/src/components/LpModal.vue +12 -12
  496. package/src/components/LpModalNav.vue +2 -21
  497. package/src/components/LpNumberField.vue +13 -16
  498. package/src/components/LpOtpInput.vue +2 -2
  499. package/src/components/LpPageScrollbar.vue +195 -0
  500. package/src/components/LpPagination.vue +5 -3
  501. package/src/components/LpPhoneInput.vue +8 -19
  502. package/src/components/LpPie.vue +25 -23
  503. package/src/components/LpPipeline.vue +14 -1
  504. package/src/components/LpPopover.vue +23 -2
  505. package/src/components/LpProgress.vue +2 -1
  506. package/src/components/LpRadioGroup.vue +2 -2
  507. package/src/components/LpRangeSlider.vue +5 -6
  508. package/src/components/LpRevealOnScroll.vue +1 -1
  509. package/src/components/LpScrollArea.vue +55 -34
  510. package/src/components/LpSecretField.vue +2 -1
  511. package/src/components/LpSegmented.vue +6 -8
  512. package/src/components/LpSelect.vue +54 -24
  513. package/src/components/LpServiceNode.vue +4 -2
  514. package/src/components/LpShaderBackdrop.vue +12 -9
  515. package/src/components/LpShift.vue +18 -15
  516. package/src/components/LpSidebar.vue +77 -74
  517. package/src/components/LpSidebarNav.vue +3 -2
  518. package/src/components/LpSidebarRow.vue +2 -1
  519. package/src/components/LpSlider.vue +5 -6
  520. package/src/components/LpSpotlight.vue +2 -1
  521. package/src/components/LpStat.vue +25 -5
  522. package/src/components/LpSwitch.vue +5 -3
  523. package/src/components/LpTable.vue +609 -93
  524. package/src/components/LpTableFilter.vue +1 -1
  525. package/src/components/LpTableOfContents.vue +16 -9
  526. package/src/components/LpTabs.vue +2 -1
  527. package/src/components/LpTagsInput.vue +12 -19
  528. package/src/components/LpTextarea.vue +5 -11
  529. package/src/components/LpThemeProvider.vue +29 -13
  530. package/src/components/LpTimeField.vue +4 -7
  531. package/src/components/LpToaster.vue +7 -6
  532. package/src/components/LpTooltip.vue +69 -18
  533. package/src/components/LpTopologyCanvas.vue +569 -42
  534. package/src/components/LpTour.vue +7 -5
  535. package/src/components/LpTruncate.vue +4 -3
  536. package/src/components/LpTypewriter.vue +2 -1
  537. package/src/components/LpVideoBackdrop.vue +11 -13
  538. package/src/components/LpWindow.vue +373 -0
  539. package/src/components/LpWorkspaceSwitcher.vue +116 -0
  540. package/src/components/PickerField.vue +2 -2
  541. package/src/components/SliderRail.vue +10 -0
  542. package/src/components/address.ts +3 -1
  543. package/src/components/appShell.ts +74 -0
  544. package/src/components/calendar.ts +12 -5
  545. package/src/components/chart.ts +27 -3
  546. package/src/components/chartWindow.ts +198 -0
  547. package/src/components/color.ts +2 -1
  548. package/src/components/commandPalette.ts +66 -5
  549. package/src/components/controlSize.ts +6 -0
  550. package/src/components/dropdown.ts +3 -0
  551. package/src/components/exportData.ts +33 -18
  552. package/src/components/fileTree.ts +215 -0
  553. package/src/components/identicon.ts +4 -2
  554. package/src/components/logTools.ts +298 -0
  555. package/src/components/meter.ts +2 -1
  556. package/src/components/pipeline.ts +6 -0
  557. package/src/components/relativeTime.ts +2 -1
  558. package/src/components/reveal.ts +3 -1
  559. package/src/components/scrollbar.ts +71 -0
  560. package/src/components/select.ts +14 -0
  561. package/src/components/sidebar.ts +1 -0
  562. package/src/components/tableFeatures.ts +149 -0
  563. package/src/components/topologyGraph.ts +655 -0
  564. package/src/components/tour.ts +5 -3
  565. package/src/components/videoBackdrop.ts +7 -6
  566. package/src/composables/clamp.ts +2 -0
  567. package/src/composables/devWarn.ts +19 -0
  568. package/src/composables/easing.ts +37 -12
  569. package/src/composables/overlayHistory.ts +24 -0
  570. package/src/composables/pointerDrag.ts +54 -0
  571. package/src/composables/propCheck.ts +105 -0
  572. package/src/composables/realm.ts +25 -0
  573. package/src/composables/scroll.ts +4 -2
  574. package/src/composables/scrollMemory.ts +56 -14
  575. package/src/composables/spatialNav.ts +2 -1
  576. package/src/composables/textEntry.ts +33 -0
  577. package/src/composables/useAsyncTask.ts +6 -0
  578. package/src/composables/useChartSync.ts +52 -0
  579. package/src/composables/useCloseGuard.ts +22 -10
  580. package/src/composables/useCommands.ts +123 -0
  581. package/src/composables/useField.ts +41 -2
  582. package/src/composables/useFitScale.ts +4 -1
  583. package/src/composables/useFormDraft.ts +38 -14
  584. package/src/composables/useHintSystem.ts +2 -2
  585. package/src/composables/useHotkeys.ts +19 -48
  586. package/src/composables/useInView.ts +2 -1
  587. package/src/composables/useInputFilter.ts +8 -5
  588. package/src/composables/useInputMask.ts +8 -3
  589. package/src/composables/useLinkedView.ts +222 -0
  590. package/src/composables/useOptimisticToggle.ts +7 -1
  591. package/src/composables/useOverlayHistory.ts +20 -0
  592. package/src/composables/usePaintSelect.ts +20 -14
  593. package/src/composables/usePaletteCommands.ts +18 -2
  594. package/src/composables/usePersisted.ts +10 -0
  595. package/src/composables/usePillTransition.ts +9 -6
  596. package/src/composables/useResizeObserver.ts +2 -1
  597. package/src/composables/useReveal.ts +2 -1
  598. package/src/composables/useRovingFocus.ts +11 -14
  599. package/src/composables/useScramble.ts +12 -1
  600. package/src/composables/useShift.ts +8 -6
  601. package/src/composables/useTilt.ts +2 -1
  602. package/src/composables/useTween.ts +10 -5
  603. package/src/composables/useView.ts +17 -3
  604. package/src/composables/useZoomPan.ts +2 -5
  605. package/src/composables/windowing.ts +190 -0
  606. package/src/icons.ts +27 -6
  607. package/src/index.ts +42 -4
  608. package/src/kit-icons.ts +1 -1
  609. package/src/layout/tree.ts +31 -16
  610. package/src/layout/useLayout.ts +6 -0
  611. package/src/layout/useWorkspaces.ts +234 -0
  612. package/src/nuxt.ts +9 -1
  613. package/src/runtime/propCheck.plugin.ts +10 -0
  614. package/src/theme/studio.ts +7 -5
  615. package/src/theme/useTheme.ts +89 -15
  616. package/src/tokens/tokens.css +8 -1
  617. package/dist/assets/LpTabs-CJ0rYQbx.css +0 -18
  618. package/dist/chunks/LpAppShell-Dgejipzz.js +0 -319
  619. package/dist/chunks/LpFileTree-CooHinsC.js +0 -425
  620. package/dist/chunks/LpThemeProvider-8MG6yqU9.js +0 -640
  621. package/dist/chunks/LpTopologyCanvas-B1gIbS0n.js +0 -298
@@ -3,6 +3,7 @@
3
3
  * so they can be tested without a media element.
4
4
  */
5
5
 
6
+ import { clamp } from "../composables/clamp"
6
7
  import { splitTime } from "./timeSpan"
7
8
 
8
9
  /** The stretch of a clip that loops, and how long two passes overlap at the seam. */
@@ -29,16 +30,16 @@ export function planLoop(
29
30
  options: { start?: number; end?: number; crossfade?: number; minLength?: number } = {},
30
31
  ): LoopPlan {
31
32
  if (!Number.isFinite(duration) || duration <= 0) return { from: 0, to: 0, fade: 0 }
32
- const clamp = (value: number) => Math.min(duration, Math.max(0, value))
33
- let from = clamp(options.start ?? 0)
34
- let to = clamp(options.end ?? duration)
33
+ const within = (value: number) => clamp(value, 0, duration)
34
+ let from = within(options.start ?? 0)
35
+ let to = within(options.end ?? duration)
35
36
  if (to < from) [from, to] = [to, from]
36
- const minLength = Math.min(duration, Math.max(0, options.minLength ?? 0.5))
37
+ const minLength = clamp(options.minLength ?? 0.5, 0, duration)
37
38
  if (to - from < minLength) {
38
39
  to = Math.min(duration, from + minLength)
39
40
  from = Math.max(0, to - minLength)
40
41
  }
41
- const fade = Math.min(Math.max(0, options.crossfade ?? 0), (to - from) / 2)
42
+ const fade = clamp(options.crossfade ?? 0, 0, (to - from) / 2)
42
43
  return { from, to, fade }
43
44
  }
44
45
 
@@ -55,7 +56,7 @@ export function seamReached(plan: LoopPlan, time: number): boolean {
55
56
  */
56
57
  export function seamProgress(plan: LoopPlan, incomingTime: number): number {
57
58
  if (plan.fade <= 0) return 1
58
- return Math.min(1, Math.max(0, (incomingTime - plan.from) / plan.fade))
59
+ return clamp((incomingTime - plan.from) / plan.fade, 0, 1)
59
60
  }
60
61
 
61
62
  /** Whether the whole clip loops with nothing cut and no overlap, which the media element does natively. */
@@ -0,0 +1,2 @@
1
+ /** `value` held between `min` and `max`. When they cross, `max` wins. */
2
+ export const clamp = (value: number, min: number, max: number): number => Math.min(max, Math.max(min, value))
@@ -0,0 +1,19 @@
1
+ declare const process: { env: { NODE_ENV?: string } }
2
+
3
+ /*
4
+ * Written as the literal `process.env.NODE_ENV` so the consumer's bundler
5
+ * replaces it (Vite and webpack both do for dependencies); where nothing
6
+ * replaces it and no `process` exists, warnings simply stay off.
7
+ */
8
+ export function isDev(): boolean {
9
+ try {
10
+ return process.env.NODE_ENV !== "production"
11
+ } catch {
12
+ return false
13
+ }
14
+ }
15
+
16
+ /** A warning for the developer only. */
17
+ export function devWarn(message: string): void {
18
+ if (isDev()) console.warn(`[@leavepulse/ui] ${message}`)
19
+ }
@@ -28,10 +28,33 @@
28
28
  * A plain function, not a reactive composable: it is read at the moment an
29
29
  * animation starts, and a component that also needs to RE-RENDER when the
30
30
  * setting changes mid-session should use `usePrefersReducedMotion`.
31
+ *
32
+ * Which one to use: a one-off animation reads this as it starts; anything that
33
+ * keeps running (a loop, a spring, a video) follows the reactive one or the
34
+ * media query's own change event, so it stops without a new action; a CSS
35
+ * transition uses `prefers-reduced-motion` in CSS.
36
+ *
37
+ * `win` is the window to ask, for a caller inside an LpFrame.
31
38
  */
32
- export const prefersReducedMotion = (): boolean =>
33
- typeof window !== "undefined" &&
34
- (window.matchMedia?.("(prefers-reduced-motion: reduce)").matches ?? false)
39
+ export const prefersReducedMotion = (win?: Window): boolean => {
40
+ const target = win ?? (typeof window === "undefined" ? undefined : window)
41
+ return target?.matchMedia?.("(prefers-reduced-motion: reduce)").matches ?? false
42
+ }
43
+
44
+ /*
45
+ * Where a motion token is read from. A scoped theme (LpThemeProvider) or an
46
+ * LpFrame declares its tokens on its own element or document, so a caller with
47
+ * an element at hand passes it; without one it is the page's root.
48
+ */
49
+ type TokenScope = Element | Document
50
+
51
+ function readToken(name: string, scope?: TokenScope): string | null {
52
+ if (typeof window === "undefined") return null
53
+ const el = scope ? ("documentElement" in scope ? scope.documentElement : scope) : document.documentElement
54
+ const view = el.ownerDocument?.defaultView ?? window
55
+ if (!view.getComputedStyle) return null
56
+ return view.getComputedStyle(el).getPropertyValue(`--${name}`).trim()
57
+ }
35
58
 
36
59
  /**
37
60
  * A motion token's value, for an API that cannot take a `var()`.
@@ -47,20 +70,21 @@ export const prefersReducedMotion = (): boolean =>
47
70
  export const easingToken = (
48
71
  name: "ease-emphasized" | "ease-travel" | "ease-settle",
49
72
  fallback = "cubic-bezier(0.2, 0, 0, 1)",
50
- ): string => {
51
- if (typeof window === "undefined" || !window.getComputedStyle) return fallback
52
- const value = getComputedStyle(document.documentElement).getPropertyValue(`--${name}`).trim()
53
- return value || fallback
54
- }
73
+ scope?: TokenScope,
74
+ ): string => readToken(name, scope) || fallback
55
75
 
56
76
  /**
57
77
  * A duration token in milliseconds, for an animation or a timer driven in
58
78
  * script. Same reasoning as `easingToken`: a typed-out 220 would not follow a
59
79
  * theme that retunes the token.
60
80
  */
61
- export const durationToken = (name: "duration-fast" | "duration-medium", fallback = 220): number => {
62
- if (typeof window === "undefined" || !window.getComputedStyle) return fallback
63
- const raw = getComputedStyle(document.documentElement).getPropertyValue(`--${name}`).trim()
81
+ export const durationToken = (
82
+ name: "duration-fast" | "duration-medium",
83
+ fallback = 220,
84
+ scope?: TokenScope,
85
+ ): number => {
86
+ const raw = readToken(name, scope)
87
+ if (raw === null) return fallback
64
88
  const ms = Number.parseFloat(raw)
65
89
  return Number.isFinite(ms) ? (raw.endsWith("ms") || !raw.endsWith("s") ? ms : ms * 1000) : fallback
66
90
  }
@@ -156,8 +180,9 @@ export const cubicBezier = (x1: number, y1: number, x2: number, y2: number) => {
156
180
  */
157
181
  export const tokenEase = (
158
182
  name: "ease-emphasized" | "ease-travel" | "ease-settle",
183
+ scope?: TokenScope,
159
184
  ): ((t: number) => number) => {
160
- const match = /cubic-bezier\(([^)]+)\)/.exec(easingToken(name))
185
+ const match = /cubic-bezier\(([^)]+)\)/.exec(easingToken(name, undefined, scope))
161
186
  const n = match?.[1].split(",").map((p) => Number.parseFloat(p))
162
187
  return n && n.length === 4 && n.every(Number.isFinite)
163
188
  ? cubicBezier(n[0], n[1], n[2], n[3])
@@ -40,6 +40,8 @@
40
40
 
41
41
  const KEY = "__lpOverlay"
42
42
  const SETTLE_MS = 300
43
+ /* Far longer than a traversal takes: past it, the step the kit asked for is not coming. */
44
+ const STEP_TIMEOUT_MS = 1500
43
45
 
44
46
  export interface HistoryEntry {
45
47
  /** Back left this entry: undo what it recorded (close the overlay, restore the tab). */
@@ -91,6 +93,8 @@ interface EngineState {
91
93
  serial: number
92
94
  internal: boolean
93
95
  settle: number
96
+ /** Timer that gives up on `silent` pops the browser never delivers. */
97
+ stalled: number
94
98
  }
95
99
 
96
100
  const STATE_KEY = Symbol.for("@leavepulse/ui:overlay-history")
@@ -111,6 +115,7 @@ const fresh: EngineState = {
111
115
  serial: 0,
112
116
  internal: false,
113
117
  settle: 0,
118
+ stalled: 0,
114
119
  }
115
120
  const s = (g[STATE_KEY] ??= fresh)
116
121
 
@@ -142,10 +147,25 @@ function own<T>(run: () => T): T {
142
147
  function silentGo(delta: number) {
143
148
  if (delta === 0) return
144
149
  s.silent++
150
+ window.clearTimeout(s.stalled)
151
+ s.stalled = window.setTimeout(abandonSteps, STEP_TIMEOUT_MS)
145
152
  mute()
146
153
  own(() => history.go(delta))
147
154
  }
148
155
 
156
+ /*
157
+ * `go` reports nothing: with no entry to go to, or a traversal the browser
158
+ * dropped, no pop ever comes. Left waiting, the engine would swallow the user's
159
+ * next Back, hold every push in `waiting` and keep the router muted for good.
160
+ */
161
+ function abandonSteps() {
162
+ s.silent = 0
163
+ s.keep = null
164
+ s.href = location.href
165
+ flushWaiting()
166
+ mute()
167
+ }
168
+
149
169
  /* Pushes wait for a pop the kit started: a push between `go` and its arrival would land on the wrong entry. */
150
170
  function whenSettled(run: () => void) {
151
171
  if (s.silent > 0) s.waiting.push(run)
@@ -182,6 +202,7 @@ function handlePop(event: PopStateEvent) {
182
202
  s.href = location.href
183
203
  if (s.silent > 0) {
184
204
  s.silent--
205
+ if (s.silent === 0) window.clearTimeout(s.stalled)
185
206
  const keep = s.silent === 0 ? s.keep : null
186
207
  if (s.silent === 0) s.keep = null
187
208
  if (keep && keep.href !== location.href) restore(keep, event)
@@ -322,6 +343,9 @@ export function setHistoryRouter(router: HistoryRouter | null): void {
322
343
  if (s.router && s.router !== router) s.router.listening = true
323
344
  s.router = router
324
345
  if (typeof window === "undefined") return
346
+ // Now, not at the first overlay: a page reloaded on a kit entry mutes the
347
+ // router below, and only the engine's listeners ever switch it back on.
348
+ install()
325
349
  // After the router's own listener, which it added when it was created.
326
350
  window.removeEventListener("popstate", afterPop)
327
351
  window.addEventListener("popstate", afterPop)
@@ -0,0 +1,54 @@
1
+ import { nodeWindow } from "./useView"
2
+
3
+ export interface PointerDragHandlers {
4
+ move: (event: PointerEvent) => void
5
+ /** `completed` is false for a cancelled or lost pointer: nothing should be committed. */
6
+ end: (completed: boolean) => void
7
+ /** Hold this cursor over the whole page for the drag, the way a resize grip wants. */
8
+ cursor?: string
9
+ }
10
+
11
+ /**
12
+ * Follow a pointer from the handle it pressed until it lets go, however that
13
+ * ends: release, a cancelled touch, a lost capture or the returned stop. The
14
+ * end runs once. Everything happens on the handle, which holds the capture, so
15
+ * it works inside an LpFrame, and the body's text selection is off meanwhile,
16
+ * in the handle's own window, and put back as it was.
17
+ */
18
+ export function startPointerDrag(event: PointerEvent, handlers: PointerDragHandlers): () => void {
19
+ const handle = event.currentTarget as HTMLElement
20
+ const body = nodeWindow(handle).document.body.style
21
+ const { userSelect, cursor } = body
22
+ let active = true
23
+
24
+ const finish = (completed: boolean) => {
25
+ if (!active) return
26
+ active = false
27
+ handle.removeEventListener("pointermove", onMove)
28
+ handle.removeEventListener("pointerup", onUp)
29
+ handle.removeEventListener("pointercancel", onCancel)
30
+ handle.removeEventListener("lostpointercapture", onCancel)
31
+ body.userSelect = userSelect
32
+ body.cursor = cursor
33
+ handlers.end(completed)
34
+ }
35
+ function onMove(ev: PointerEvent) {
36
+ if (ev.pointerId === event.pointerId) handlers.move(ev)
37
+ }
38
+ function onUp(ev: PointerEvent) {
39
+ if (ev.pointerId === event.pointerId) finish(true)
40
+ }
41
+ function onCancel(ev: PointerEvent) {
42
+ if (ev.pointerId === event.pointerId) finish(false)
43
+ }
44
+
45
+ event.preventDefault()
46
+ body.userSelect = "none"
47
+ if (handlers.cursor) body.cursor = handlers.cursor
48
+ handle.setPointerCapture(event.pointerId)
49
+ handle.addEventListener("pointermove", onMove)
50
+ handle.addEventListener("pointerup", onUp)
51
+ handle.addEventListener("pointercancel", onCancel)
52
+ handle.addEventListener("lostpointercapture", onCancel)
53
+ return () => finish(false)
54
+ }
@@ -0,0 +1,105 @@
1
+ import { Teleport, type ComponentPublicInstance, type Plugin, type VNode } from "vue"
2
+
3
+ /*
4
+ * Development check for a misspelt prop on a kit component.
5
+ *
6
+ * A prop a component does not declare falls through as an attribute with no
7
+ * error, and vue-tsc's own `checkUnknownProps` cannot tell that apart from the
8
+ * `id`, `title` or `aria-*` an app rightly passes through. Here the attribute
9
+ * is checked against the element it would land on, so `<LpTooltip text>` is
10
+ * reported and `<LpInput autocomplete>` is not.
11
+ *
12
+ * Install it in development only:
13
+ *
14
+ * if (import.meta.env.DEV) app.use(lpPropCheck)
15
+ *
16
+ * The Nuxt module does that on its own. Attributes of a third-party library
17
+ * that nothing here can know go in `ignore`.
18
+ */
19
+ export interface PropCheckOptions {
20
+ /** Attributes that are not a mistake: exact names or patterns. */
21
+ ignore?: (string | RegExp)[]
22
+ /** Where a report goes. Defaults to console.warn. */
23
+ warn?: (message: string) => void
24
+ }
25
+
26
+ const KIT_COMPONENT = /^Lp[A-Z]/
27
+ const ALWAYS_ALLOWED = /^(data-|aria-|on[A-Z])/
28
+ const GLOBAL_ATTRIBUTES = new Set([
29
+ "accesskey", "class", "contenteditable", "dir", "draggable", "enterkeyhint", "for", "hidden",
30
+ "id", "inert", "inputmode", "is", "key", "lang", "nonce", "popover", "ref", "role", "slot",
31
+ "spellcheck", "style", "tabindex", "title", "translate",
32
+ ])
33
+ /* Where a component with `inheritAttrs: false` hands its attributes on to. */
34
+ const FORWARD_TARGETS = "input, textarea, select, button, a, img, form, label"
35
+
36
+ const propertiesByTag = new Map<string, Set<string>>()
37
+
38
+ function propertiesOf(el: Element): Set<string> {
39
+ let known = propertiesByTag.get(el.tagName)
40
+ if (!known) {
41
+ known = new Set<string>()
42
+ for (const name in el) known.add(name.toLowerCase())
43
+ propertiesByTag.set(el.tagName, known)
44
+ }
45
+ return known
46
+ }
47
+
48
+ const normalized = (name: string) => name.replaceAll("-", "").toLowerCase()
49
+
50
+ /** The attributes no element under this component could take. */
51
+ export function unknownAttributes(
52
+ attributes: string[],
53
+ elements: Element[],
54
+ ignore: PropCheckOptions["ignore"] = [],
55
+ ): string[] {
56
+ return attributes.filter((name) => {
57
+ if (ALWAYS_ALLOWED.test(name) || GLOBAL_ATTRIBUTES.has(name.toLowerCase())) return false
58
+ if (ignore.some((rule) => (typeof rule === "string" ? rule === name : rule.test(name)))) return false
59
+ const key = normalized(name)
60
+ return !elements.some((el) => propertiesOf(el).has(key))
61
+ })
62
+ }
63
+
64
+ /*
65
+ * The elements a component rendered at its top. Not `$el`: with several roots
66
+ * (a trigger and its portal) that is the fragment's empty text anchor, which
67
+ * made every attribute look unknown. A teleport's content is left out, since
68
+ * nothing a caller passes lands there.
69
+ */
70
+ function rootElements(vnode: VNode | null | undefined, out: Element[] = []): Element[] {
71
+ if (!vnode || vnode.type === Teleport) return out
72
+ if (vnode.component) return rootElements(vnode.component.subTree, out)
73
+ const el = vnode.el as Node | null
74
+ if (el?.nodeType === 1) {
75
+ out.push(el as Element)
76
+ return out
77
+ }
78
+ if (Array.isArray(vnode.children)) for (const child of vnode.children) if (child && typeof child === "object") rootElements(child as VNode, out)
79
+ return out
80
+ }
81
+
82
+ function renderedElements(instance: ComponentPublicInstance): Element[] {
83
+ return rootElements(instance.$.subTree).flatMap((el) => [el, ...el.querySelectorAll(FORWARD_TARGETS)])
84
+ }
85
+
86
+ export const lpPropCheck: Plugin<[PropCheckOptions?]> = {
87
+ install(app, options = {}) {
88
+ const warn = options.warn ?? ((message: string) => console.warn(message))
89
+ const reported = new Set<string>()
90
+ app.mixin({
91
+ mounted(this: ComponentPublicInstance) {
92
+ const name = this.$options.__name
93
+ if (!name || !KIT_COMPONENT.test(name)) return
94
+ const attributes = Object.keys(this.$attrs)
95
+ if (!attributes.length) return
96
+ for (const attribute of unknownAttributes(attributes, renderedElements(this), options.ignore)) {
97
+ const id = `${name}:${attribute}`
98
+ if (reported.has(id)) continue
99
+ reported.add(id)
100
+ warn(`[@leavepulse/ui] <${name}> has no prop "${attribute}". Props: ${Object.keys(this.$props).join(", ")}`)
101
+ }
102
+ },
103
+ })
104
+ },
105
+ }
@@ -0,0 +1,25 @@
1
+ /*
2
+ * `instanceof Element` is false for a node of an LpFrame: each window has its
3
+ * own classes. These go by what the node is, and what it belongs to.
4
+ */
5
+ export function isNode(value: unknown): value is Node {
6
+ return typeof (value as Node | null | undefined)?.nodeType === "number"
7
+ }
8
+
9
+ export function isElement(value: unknown): value is Element {
10
+ return (value as Node | null | undefined)?.nodeType === 1
11
+ }
12
+
13
+ export function isInputElement(value: unknown): value is HTMLInputElement {
14
+ return isElement(value) && value.tagName === "INPUT"
15
+ }
16
+
17
+ export function isHTMLElement(value: unknown): value is HTMLElement {
18
+ return isElement(value) && "style" in value
19
+ }
20
+
21
+ /** The window a node lives in, with that realm's constructors (`MouseEvent`, `ResizeObserver`). */
22
+ export function nodeWindow(node: Node): Window & typeof globalThis {
23
+ const doc = node.nodeType === 9 ? (node as Document) : node.ownerDocument
24
+ return (doc?.defaultView ?? globalThis) as Window & typeof globalThis
25
+ }
@@ -1,3 +1,5 @@
1
+ import { nodeWindow } from "./useView"
2
+
1
3
  /*
2
4
  * The element that actually scrolls, and scrolling it to show a child. Inside an
3
5
  * app shell that is a scroll region, not the window, and `scrollIntoView` moves
@@ -18,7 +20,7 @@ export interface ScrollParentOptions {
18
20
  export function scrollParent(el: Element | null | undefined, { axis = "y", self = false }: ScrollParentOptions = {}): HTMLElement | null {
19
21
  for (let node = self ? (el as HTMLElement | null | undefined) : el?.parentElement; node; node = node.parentElement) {
20
22
  if (node === node.ownerDocument.body || node === node.ownerDocument.documentElement) return null
21
- const style = getComputedStyle(node)
23
+ const style = nodeWindow(node).getComputedStyle(node)
22
24
  const y = axis !== "x" && SCROLLS.test(style.overflowY) && node.scrollHeight > node.clientHeight
23
25
  const x = axis !== "y" && SCROLLS.test(style.overflowX) && node.scrollWidth > node.clientWidth
24
26
  if (y || x) return node
@@ -48,7 +50,7 @@ export interface ScrollToElementOptions {
48
50
  /** Scroll the nearest scroller, and only it, so that `el` is in view. */
49
51
  /** The element's own `scroll-margin-top`, the gap it asks to keep above it when scrolled to. */
50
52
  export function scrollMarginTop(el: Element): number {
51
- return Number.parseFloat(getComputedStyle(el).scrollMarginTop) || 0
53
+ return Number.parseFloat(nodeWindow(el).getComputedStyle(el).scrollMarginTop) || 0
52
54
  }
53
55
 
54
56
  export function scrollToElement(
@@ -1,10 +1,14 @@
1
+ import { nodeWindow } from "./realm"
1
2
  import { nextTick, onBeforeUnmount, onMounted, watch } from "vue"
2
3
  import { scrollMarginTop } from "./scroll"
3
4
 
4
5
  /*
5
- * Where a page lands when the shell's own scroll region changes page. The
6
- * region is not the window, so a router's scrollBehavior never reaches it, and
7
- * the shell has to answer the questions a browser answers for a plain site:
6
+ * Where a page lands when the shell changes page. In its own scroll region a
7
+ * router's scrollBehavior never reaches the page, and the shell has to answer
8
+ * the questions a browser answers for a plain site. When the document scrolls
9
+ * (the viewport is the document's scrollingElement) it answers them the same
10
+ * way, so a router without a scrollBehavior behaves as one with it, and one
11
+ * with it lands in the same place:
8
12
  *
9
13
  * - back and forward return to where the page was left;
10
14
  * - an address with a hash lands on that anchor, and stays on it while the
@@ -22,20 +26,56 @@ import { scrollMarginTop } from "./scroll"
22
26
  const SETTLE_MS = 1500
23
27
  const DIALOG_WAIT_MS = 2000
24
28
  const SETTLE_STOP = ["wheel", "touchstart", "keydown", "pointerdown"] as const
29
+ /* Addresses remembered; a long session over /devices/:id must not grow forever. */
30
+ const MAX_POSITIONS = 200
25
31
 
26
32
  const positions = new Map<string, number>()
27
33
 
28
- function addressKey(): string {
29
- return `${location.pathname}${location.search}`
34
+ /** Records a position, dropping the address visited longest ago past `limit`. */
35
+ export function rememberPosition(store: Map<string, number>, key: string, top: number, limit = MAX_POSITIONS): void {
36
+ store.delete(key)
37
+ store.set(key, top)
38
+ if (store.size > limit) store.delete(store.keys().next().value as string)
39
+ }
40
+
41
+ /** The element id a hash names. A malformed escape ("#%E0%A4%A") is taken as written. */
42
+ /* The document's own scroller: its box moves with the scroll, the window's does not. */
43
+ function isDocument(view: Element): boolean {
44
+ return view === view.ownerDocument.scrollingElement
45
+ }
46
+
47
+ function frameTop(view: Element): number {
48
+ return isDocument(view) ? 0 : view.getBoundingClientRect().top
49
+ }
50
+
51
+ export function hashTargetId(value: string): string {
52
+ const id = value.replace(/^#/, "")
53
+ try {
54
+ return decodeURIComponent(id)
55
+ } catch {
56
+ return id
57
+ }
58
+ }
59
+
60
+ function addressKey(scope: string): string {
61
+ return `${scope}${location.pathname}${location.search}`
30
62
  }
31
63
 
32
64
  export function useScrollMemory(
33
65
  viewport: () => HTMLElement | null | undefined,
34
66
  path: () => string | undefined,
35
67
  hash: () => string | undefined,
68
+ /** Sets a second region's positions apart from the first's on the same address. */
69
+ scope = "",
70
+ /**
71
+ * What covers the top of the viewport (a sticky header), in px: an anchor
72
+ * lands below it. Not scroll-padding-top on the document, which also makes
73
+ * the browser scroll the page whenever a control in that header takes focus.
74
+ */
75
+ covered: () => number = () => 0,
36
76
  ): void {
37
77
  if (typeof window === "undefined") return
38
- let current = addressKey()
78
+ let current = addressKey(scope)
39
79
  let shown = path()
40
80
  let travelled = false
41
81
  let stopHold: (() => void) | undefined
@@ -48,15 +88,14 @@ export function useScrollMemory(
48
88
 
49
89
  function anchor(value: string | undefined): HTMLElement | null {
50
90
  if (!value || value === "#") return null
51
- const id = decodeURIComponent(value.replace(/^#/, ""))
52
- return document.getElementById(id)
91
+ return document.getElementById(hashTargetId(value))
53
92
  }
54
93
 
55
94
  function reach(value: string | undefined, behavior: ScrollBehavior): boolean {
56
95
  const view = viewport()
57
96
  const target = anchor(value)
58
97
  if (!view || !target || !view.contains(target)) return false
59
- const top = target.getBoundingClientRect().top - view.getBoundingClientRect().top + view.scrollTop - scrollMarginTop(target)
98
+ const top = target.getBoundingClientRect().top - frameTop(view) + view.scrollTop - scrollMarginTop(target) - covered()
60
99
  view.scrollTo({ top: Math.max(0, top), behavior })
61
100
  return true
62
101
  }
@@ -65,10 +104,10 @@ export function useScrollMemory(
65
104
  function hold(land: () => void) {
66
105
  stopHold?.()
67
106
  const view = viewport()
68
- const content = view?.firstElementChild
107
+ const content = view && (isDocument(view) ? view.ownerDocument.body : view.firstElementChild)
69
108
  land()
70
109
  if (!content) return
71
- const watcher = new ResizeObserver(land)
110
+ const watcher = new (nodeWindow(content).ResizeObserver)(land)
72
111
  const timer = window.setTimeout(() => stop(), SETTLE_MS)
73
112
  const stop = () => {
74
113
  watcher.disconnect()
@@ -84,7 +123,7 @@ export function useScrollMemory(
84
123
  function arrive() {
85
124
  const back = travelled
86
125
  travelled = false
87
- current = addressKey()
126
+ current = addressKey(scope)
88
127
  shown = path()
89
128
  const view = viewport()
90
129
  if (!view) return
@@ -108,7 +147,9 @@ export function useScrollMemory(
108
147
  const mine = ++pending
109
148
  const tick = () => {
110
149
  if (mine !== pending) return
111
- const locked = document.querySelector('[role="dialog"]') || getComputedStyle(document.body).overflow === "hidden"
150
+ // The document the shell scrolls in: a dialog of the page is not one of a frame's.
151
+ const doc = viewport()?.ownerDocument ?? document
152
+ const locked = doc.querySelector('[role="dialog"]') || nodeWindow(doc).getComputedStyle(doc.body).overflow === "hidden"
112
153
  if (locked && performance.now() - since < DIALOG_WAIT_MS) requestAnimationFrame(tick)
113
154
  else run()
114
155
  }
@@ -119,7 +160,7 @@ export function useScrollMemory(
119
160
  path,
120
161
  () => {
121
162
  const view = viewport()
122
- if (view) positions.set(current, view.scrollTop)
163
+ if (view) rememberPosition(positions, current, view.scrollTop)
123
164
  void nextTick(() => requestAnimationFrame(arrive))
124
165
  },
125
166
  { flush: "pre" },
@@ -140,6 +181,7 @@ export function useScrollMemory(
140
181
 
141
182
  onBeforeUnmount(() => {
142
183
  stopHold?.()
184
+ pending += 1
143
185
  window.removeEventListener("popstate", onPop)
144
186
  })
145
187
  }
@@ -1,3 +1,4 @@
1
+ import { nodeWindow } from "./realm"
1
2
  /*
2
3
  * Moving focus by direction rather than by document order: what a D-pad, a
3
4
  * stick or a TV remote needs, where "next" means "the thing to the right", not
@@ -113,7 +114,7 @@ export function focusableIn(root: ParentNode): HTMLElement[] {
113
114
  if (element.getAttribute("tabindex") === "-1" && !element.matches(ROVING_ITEMS)) return false
114
115
  const rect = element.getBoundingClientRect()
115
116
  if (rect.width === 0 || rect.height === 0) return false
116
- const style = getComputedStyle(element)
117
+ const style = nodeWindow(element).getComputedStyle(element)
117
118
  return style.visibility !== "hidden" && style.display !== "none"
118
119
  })
119
120
  }
@@ -0,0 +1,33 @@
1
+ import { isElement, isHTMLElement } from "./realm"
2
+
3
+ const TYPED_INPUTS = new Set(["text", "search", "email", "number", "password", "tel", "url"])
4
+
5
+ /** Where text can be typed, for a target that may sit inside one. */
6
+ export const TEXT_FIELD_SELECTOR = "input, textarea, [contenteditable]"
7
+
8
+ export interface TextEntryOptions {
9
+ /** Which `<input>` types count. Defaults to the ones that take typed text. */
10
+ inputType?: (type: string) => boolean
11
+ /** A `<select>` counts too: it keeps letters and arrows for itself. */
12
+ select?: boolean
13
+ }
14
+
15
+ /**
16
+ * Whether the event lands in something the user is typing into. Checked off the
17
+ * event target rather than `document.activeElement` so it stays right inside a
18
+ * dialog's focus trap, and by tag rather than class so a field of an LpFrame
19
+ * counts too.
20
+ */
21
+ export function isTextEntry(target: EventTarget | null, options: TextEntryOptions = {}): boolean {
22
+ if (!isHTMLElement(target)) return false
23
+ if (target.isContentEditable || target.tagName === "TEXTAREA") return true
24
+ if (options.select && target.tagName === "SELECT") return true
25
+ if (target.tagName !== "INPUT") return false
26
+ const { type } = target as HTMLInputElement
27
+ return options.inputType ? options.inputType(type) : TYPED_INPUTS.has(type)
28
+ }
29
+
30
+ /** Whether the target is, or sits inside, what the selector names. */
31
+ export function inTextField(target: EventTarget | null, selector = TEXT_FIELD_SELECTOR): boolean {
32
+ return isElement(target) && !!target.closest(selector)
33
+ }
@@ -8,6 +8,12 @@ import { getCurrentScope, onScopeDispose, ref } from "vue"
8
8
  * fresher one, and a response landing after the control moved on. The token
9
9
  * guards both — the abort signal only asks the consumer to stop, it cannot
10
10
  * promise they did.
11
+ *
12
+ * What is guarded is `apply` and `fail`, not the task: whatever it does past
13
+ * the point it honours the signal (a cache write after the fetch) still runs,
14
+ * and an error from a superseded run is dropped unseen. It is also one slot:
15
+ * a new `run` replaces the last, and `pending` speaks for the latest only, so
16
+ * independent requests each need their own `useAsyncTask`.
11
17
  */
12
18
  export function useAsyncTask<T>() {
13
19
  const pending = ref(false)