@leavepulse/ui 0.77.3 → 0.78.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 (597) 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-DXIr0eFz.js} +2 -2
  12. package/dist/chunks/{FieldWrapper-CORPzo9s.js → FieldWrapper-DnuMTrqW.js} +1 -1
  13. package/dist/chunks/IconCloseButton-COZEsa9u.js +29 -0
  14. package/dist/chunks/{LayoutCanvas-CNOAUk7D.js → LayoutCanvas-CZKOltfA.js} +1 -1
  15. package/dist/chunks/{LayoutNode-DF5BMN0J.js → LayoutNode-D2f7v683.js} +45 -45
  16. package/dist/chunks/{LpActionBar-BP1ld7-S.js → LpActionBar-BGMk5KoG.js} +3 -3
  17. package/dist/chunks/{LpActivityLine-CJvzrNpj.js → LpActivityLine-Ea-t5Vza.js} +4 -2
  18. package/dist/chunks/{LpAddressInput-E8KtwU7-.js → LpAddressInput-ZqoZ-N2Y.js} +12 -11
  19. package/dist/chunks/{LpAlert-n5XPAZfv.js → LpAlert-CjvTZIKF.js} +3 -3
  20. package/dist/chunks/LpAppShell-BNAVTQsY.js +582 -0
  21. package/dist/chunks/{LpAutocomplete-C-SpPfBG.js → LpAutocomplete-DkY6CSyD.js} +24 -42
  22. package/dist/chunks/{LpAvatar-Cg6Az3wz.js → LpAvatar-cQVsNk6F.js} +2 -2
  23. package/dist/chunks/{LpBadge-wUYLN4U9.js → LpBadge-D5QQdDQ_.js} +2 -2
  24. package/dist/chunks/{LpBottomNav-CbNQQPuL.js → LpBottomNav-zxLGGbjm.js} +9 -2
  25. package/dist/chunks/{LpBreadcrumbs-DPdbth-c.js → LpBreadcrumbs-CIIBsygI.js} +4 -3
  26. package/dist/chunks/{LpButton-CFb44IjC.js → LpButton-DSHioIfD.js} +5 -4
  27. package/dist/chunks/{LpCalendar-CuoWks-0.js → LpCalendar-D4-LVjfF.js} +1 -1
  28. package/dist/chunks/{LpCard-r13WRqox.js → LpCard-BPtYXwnT.js} +1 -1
  29. package/dist/chunks/{LpCheckbox-Cs4SHaHX.js → LpCheckbox-D6RuDW4X.js} +38 -16
  30. package/dist/chunks/{LpCodeBlock-DRC8VbXW.js → LpCodeBlock-DuzmnIHl.js} +19 -41
  31. package/dist/chunks/{LpCodeDisplay-CE9TxCA4.js → LpCodeDisplay-CO8fngxa.js} +5 -5
  32. package/dist/chunks/{LpCollapse-CWS9M_lm.js → LpCollapse-DrAw5DBZ.js} +5 -4
  33. package/dist/chunks/{LpColorPicker-1y1QJHZR.js → LpColorPicker-BxAHEnR5.js} +11 -12
  34. package/dist/chunks/{LpCommandScope-CtUhJe8T.js → LpCommandScope-DRUvHiX7.js} +1 -1
  35. package/dist/chunks/{LpConfirmDialog-DlD45Bi_.js → LpConfirmDialog-6oCIJTDa.js} +3 -3
  36. package/dist/chunks/{LpConfirmHost-BIWjBLmI.js → LpConfirmHost-DgHvfT6s.js} +1 -1
  37. package/dist/chunks/{LpContextMenu-rx2tCjwk.js → LpContextMenu--eMGIql6.js} +13 -15
  38. package/dist/chunks/{LpContextSwitcher-Dwh6dyID.js → LpContextSwitcher-1C1tExdl.js} +2 -2
  39. package/dist/chunks/{LpCopyButton-xF7wFVoU.js → LpCopyButton-7EOSIuwa.js} +3 -3
  40. package/dist/chunks/{LpCountdown-DgeY3cLG.js → LpCountdown-DVl7vXI1.js} +4 -3
  41. package/dist/chunks/{LpDataList-Jxyl9H5w.js → LpDataList-DgBeM01y.js} +13 -4
  42. package/dist/chunks/{LpDatePicker-CA0QTfBC.js → LpDatePicker-DwSDY0mt.js} +12 -18
  43. package/dist/chunks/{LpDateRangePicker-u3J51xXk.js → LpDateRangePicker-CNQXdTVv.js} +10 -16
  44. package/dist/chunks/{LpDisclosure-Cpa7Hc5T.js → LpDisclosure-DG7JCo-d.js} +2 -2
  45. package/dist/chunks/{LpDrawer-CrG8t_KP.js → LpDrawer-Bt9UUqt_.js} +61 -61
  46. package/dist/chunks/{LpDropdownMenu-Dl7Z4ZhS.js → LpDropdownMenu-CLzxIOgM.js} +9 -17
  47. package/dist/chunks/{LpEditable-Cy6GLbFx.js → LpEditable-DVT0Rh8F.js} +9 -6
  48. package/dist/chunks/{LpEmptyState-EQHiRYhG.js → LpEmptyState-DurPF001.js} +1 -1
  49. package/dist/chunks/{LpFieldNotice-CzQ2_5rQ.js → LpFieldNotice-DJ10cYIo.js} +1 -1
  50. package/dist/chunks/LpFileTree-BSx1kBoV.js +735 -0
  51. package/dist/chunks/{LpFileTreeNode-cT6BIWvz.js → LpFileTreeNode-4dVmFHb3.js} +116 -34
  52. package/dist/chunks/{LpFormField-Ch69Jd-X.js → LpFormField-BKYn9dyP.js} +3 -3
  53. package/dist/chunks/{LpFrame-BVQCQLDI.js → LpFrame-Bv5Fphte.js} +25 -11
  54. package/dist/chunks/{LpHintExample-9Py0wc-L.js → LpHintExample-YzCXiSUp.js} +1 -1
  55. package/dist/chunks/{LpHintLayer-CgObdUsM.js → LpHintLayer-BeKRsRon.js} +2 -2
  56. package/dist/chunks/{LpHints-Bz-BlaSl.js → LpHints-qf3xYsza.js} +3 -3
  57. package/dist/chunks/{LpHoverCard-spGxyw7T.js → LpHoverCard-DanH3Nz1.js} +23 -5
  58. package/dist/chunks/{LpIcon--5WSb3jJ.js → LpIcon-C6JpQ5td.js} +6 -3
  59. package/dist/chunks/{LpIdenticon-BzdC_qz9.js → LpIdenticon-Dbbr4fvt.js} +2 -1
  60. package/dist/chunks/{LpInfraNode-46XOCkfq.js → LpInfraNode-BAU40Km2.js} +2 -2
  61. package/dist/chunks/{LpInput-Tw-yxT4I.js → LpInput-DL6HEJdi.js} +16 -28
  62. package/dist/chunks/LpInspector-CuoMAhiw.js +209 -0
  63. package/dist/chunks/{LpLightbox-B95yu22B.js → LpLightbox-xvpQ64MM.js} +57 -32
  64. package/dist/chunks/{LpLoader-DVwkOmt9.js → LpLoader-Bf1yZwaU.js} +1 -1
  65. package/dist/chunks/LpLogInspector-6cNnXqp4.js +119 -0
  66. package/dist/chunks/{LpLogViewer-ngqkns4m.js → LpLogViewer-Dvb1E9_G.js} +234 -53
  67. package/dist/chunks/{LpMapPicker-DRGgh89B.js → LpMapPicker-D5_YLLBC.js} +4 -3
  68. package/dist/chunks/{LpMasonry--k0571qG.js → LpMasonry-DPYekFxs.js} +8 -5
  69. package/dist/chunks/{LpMediaGallery-Bkram-MO.js → LpMediaGallery-Df-tiWeq.js} +3 -3
  70. package/dist/chunks/{LpModal-CoGhLTyH.js → LpModal-DrtQ_1A2.js} +11 -13
  71. package/dist/chunks/{LpModalNav-BGvs2y5Q.js → LpModalNav-DkmGCnZE.js} +11 -37
  72. package/dist/chunks/{LpNotificationBell-B0034Lk-.js → LpNotificationBell-D_wklO1m.js} +9 -9
  73. package/dist/chunks/{LpNumberField-e0IBkWU1.js → LpNumberField-CNHsmSDZ.js} +7 -16
  74. package/dist/chunks/{LpOtpInput-CeBPsM7D.js → LpOtpInput-BRpLBbOD.js} +2 -2
  75. package/dist/chunks/{LpPagination-BIIiYHXR.js → LpPagination-snQ020xQ.js} +6 -4
  76. package/dist/chunks/{LpPaletteTheme-DiLYKQiZ.js → LpPaletteTheme-dVtxHx-F.js} +4 -4
  77. package/dist/chunks/{LpPasswordInput-BkHKQDhM.js → LpPasswordInput-B-zD6tUS.js} +2 -2
  78. package/dist/chunks/{LpPhoneInput-CETEp5W7.js → LpPhoneInput-BSL7NX4z.js} +15 -26
  79. package/dist/chunks/{LpPie-DrZepi0p.js → LpPie-9t_guO49.js} +22 -21
  80. package/dist/chunks/{LpPipeline-B3aOVOkV.js → LpPipeline-rjXvEDRJ.js} +16 -7
  81. package/dist/chunks/{LpPopover-DmZ3wUtP.js → LpPopover-BQSuxtai.js} +10 -1
  82. package/dist/chunks/{LpProgress-Yr6ONG5t.js → LpProgress-D_MSjowb.js} +2 -1
  83. package/dist/chunks/{LpQuota-Dk2MyI2Z.js → LpQuota-CgWPgdVt.js} +1 -1
  84. package/dist/chunks/{LpRadioGroup-ggaPL92T.js → LpRadioGroup-BrazjLY_.js} +4 -4
  85. package/dist/chunks/{LpRangeCalendar-CYYUYqJ7.js → LpRangeCalendar-BrqlGZzE.js} +1 -1
  86. package/dist/chunks/{LpRangeSlider-CM-0ZITZ.js → LpRangeSlider-fz61tzc-.js} +5 -9
  87. package/dist/chunks/{LpRelativeTime-DwgCb5-8.js → LpRelativeTime-BovUk8MP.js} +1 -1
  88. package/dist/chunks/{LpRepeater-D7ZmnIYd.js → LpRepeater-BkJHSPI1.js} +3 -3
  89. package/dist/chunks/{LpRevealOnScroll-DcQjexLy.js → LpRevealOnScroll-CPwyiQAr.js} +1 -1
  90. package/dist/chunks/{LpScramble-Bn9OoOtC.js → LpScramble-DsE77iVp.js} +1 -1
  91. package/dist/chunks/{LpScrollArea-MyEnVB_5.js → LpScrollArea-ay4Xo_hg.js} +22 -10
  92. package/dist/chunks/{LpSecretField-BbmqM9ye.js → LpSecretField-DBvU6iAw.js} +10 -9
  93. package/dist/chunks/{LpSection-DFK_uoJE.js → LpSection-BfteipxU.js} +1 -1
  94. package/dist/chunks/{LpSegmented-CeACsMMA.js → LpSegmented-DbEJhbv-.js} +8 -10
  95. package/dist/chunks/{LpSelect-Cxq4yBGd.js → LpSelect-Y-iuSymW.js} +47 -30
  96. package/dist/chunks/{LpShaderBackdrop-Bote0iuy.js → LpShaderBackdrop-wQygYb5h.js} +13 -10
  97. package/dist/chunks/{LpShift-CCq7dofl.js → LpShift-DAgS_d2x.js} +18 -15
  98. package/dist/chunks/{LpSidebar-DzE1783C.js → LpSidebar-kSkpPh3-.js} +74 -87
  99. package/dist/chunks/{LpSidebarNav-DE3PhXVP.js → LpSidebarNav-C2mvqrY2.js} +8 -6
  100. package/dist/chunks/{LpSidebarRow-BRYhNDSo.js → LpSidebarRow-eRP4Q31q.js} +6 -5
  101. package/dist/chunks/{LpSlider-DxYoCJJO.js → LpSlider-A4ixovgS.js} +5 -9
  102. package/dist/chunks/{LpSpotlight-4F3NeUMY.js → LpSpotlight-DwxkhFgR.js} +2 -1
  103. package/dist/chunks/{LpStat-f2LfDCJ4.js → LpStat-pMNZgFcq.js} +2 -2
  104. package/dist/chunks/{LpStatus-DglxttQ7.js → LpStatus-DGJFwk5F.js} +2 -2
  105. package/dist/chunks/{LpStepMark-DpzKVyh8.js → LpStepMark-Dw2MRTrt.js} +2 -2
  106. package/dist/chunks/{LpStepper-DpsocBNJ.js → LpStepper-DNnh7lCu.js} +1 -1
  107. package/dist/chunks/{LpSwap-C96c0LWh.js → LpSwap-M7nFeW3C.js} +1 -1
  108. package/dist/chunks/{LpSwitch-BnXd1llc.js → LpSwitch-DnC2-0aV.js} +3 -3
  109. package/dist/chunks/{LpTabFace-DPCwKkH2.js → LpTabFace-DQoV8mQG.js} +2 -2
  110. package/dist/chunks/{LpTableFilter-CiVBpaq0.js → LpTableFilter-DgARTsA1.js} +4 -4
  111. package/dist/chunks/{LpTableOfContents-BN5zvpnx.js → LpTableOfContents-CKkWEK6R.js} +14 -9
  112. package/dist/chunks/{LpTagsInput-C-mDG648.js → LpTagsInput-Dt0mteSj.js} +19 -27
  113. package/dist/chunks/{LpTextarea-TE-yZv2U.js → LpTextarea-Co0b9PVx.js} +8 -16
  114. package/dist/chunks/LpThemeProvider-XK6ZeWAE.js +81 -0
  115. package/dist/chunks/{LpThemeStudio-BpORDvH6.js → LpThemeStudio-DzYfE-5k.js} +12 -195
  116. package/dist/chunks/{LpThemeSwitcher-BnEn_mvf.js → LpThemeSwitcher-CFLkdbBL.js} +8 -6
  117. package/dist/chunks/{LpTimeField-BawSVsmt.js → LpTimeField-DeOcKWJq.js} +6 -9
  118. package/dist/chunks/{LpToaster-IwTXjbbU.js → LpToaster-QFj0P-5m.js} +10 -8
  119. package/dist/chunks/{LpTooltip-BcBvIx4z.js → LpTooltip-BJf3lZMq.js} +51 -19
  120. package/dist/chunks/LpTopologyCanvas-BOHHHZ_6.js +673 -0
  121. package/dist/chunks/{LpTour-BZq-mUMv.js → LpTour-ClKYhx4h.js} +7 -5
  122. package/dist/chunks/{LpTruncate-CflAIwiJ.js → LpTruncate-C6u3gjc6.js} +6 -5
  123. package/dist/chunks/{LpTypewriter-BCL_a6wE.js → LpTypewriter-CZIzpdbe.js} +2 -1
  124. package/dist/chunks/{LpUptimeBar-DqNvvMBK.js → LpUptimeBar-BnYmR2rj.js} +3 -3
  125. package/dist/chunks/{LpVideoBackdrop-DS1wGv-X.js → LpVideoBackdrop-Ct4St4ZL.js} +10 -13
  126. package/dist/chunks/LpWindow-B8YSRVTH.js +297 -0
  127. package/dist/chunks/LpWorkspaceSwitcher-D0e9upAy.js +139 -0
  128. package/dist/chunks/{MenuTitle-BmCNNTKH.js → MenuTitle-Bh5dAdV1.js} +1 -1
  129. package/dist/chunks/{OptionContent-22UT8zLn.js → OptionContent-CGb8QlHA.js} +1 -1
  130. package/dist/chunks/{PickerField-Bfh8id3d.js → PickerField-BM1dyafh.js} +4 -4
  131. package/dist/chunks/{RevealToggle-BEit1n6Z.js → RevealToggle-CCPDE8Mv.js} +3 -3
  132. package/dist/chunks/SliderRail-DhPREa_R.js +18 -0
  133. package/dist/chunks/{TourCard-DNclH3u_.js → TourCard-DAzTYfav.js} +4 -4
  134. package/dist/chunks/{icons-DMITlfw5.js → icons-CYfcpSDS.js} +26 -10
  135. package/dist/component-names.d.ts +1 -1
  136. package/dist/components/AppShellContextNav.vue.d.ts +13 -0
  137. package/dist/components/AppShellContextNav.vue.js +4 -0
  138. package/dist/components/ChartRangeSelector.vue.d.ts +21 -0
  139. package/dist/components/ChartRangeSelector.vue.js +4 -0
  140. package/dist/components/ContextMenuItems.vue.js +1 -1
  141. package/dist/components/DropzoneLinkField.vue.d.ts +2 -2
  142. package/dist/components/DropzoneLinkField.vue.js +1 -1
  143. package/dist/components/FieldWrapper.vue.js +1 -1
  144. package/dist/components/IconCloseButton.vue.d.ts +7 -0
  145. package/dist/components/IconCloseButton.vue.js +4 -0
  146. package/dist/components/LayoutCanvas.vue.js +1 -1
  147. package/dist/components/LayoutNode.vue.js +1 -1
  148. package/dist/components/LpActionBar.vue.js +1 -1
  149. package/dist/components/LpActivityLine.vue.js +1 -1
  150. package/dist/components/LpAddressInput.vue.js +1 -1
  151. package/dist/components/LpAlert.vue.js +1 -1
  152. package/dist/components/LpAppShell.vue.d.ts +110 -2
  153. package/dist/components/LpAppShell.vue.js +1 -1
  154. package/dist/components/LpAutocomplete.vue.js +1 -1
  155. package/dist/components/LpAvatar.vue.js +1 -1
  156. package/dist/components/LpBadge.vue.js +1 -1
  157. package/dist/components/LpBottomNav.vue.d.ts +5 -0
  158. package/dist/components/LpBottomNav.vue.js +1 -1
  159. package/dist/components/LpBreadcrumbs.vue.js +1 -1
  160. package/dist/components/LpButton.vue.d.ts +14 -2
  161. package/dist/components/LpButton.vue.js +1 -1
  162. package/dist/components/LpCalendar.vue.js +1 -1
  163. package/dist/components/LpCard.vue.js +1 -1
  164. package/dist/components/LpChart.vue.d.ts +74 -2
  165. package/dist/components/LpChart.vue.js +326 -85
  166. package/dist/components/LpCheckbox.vue.d.ts +6 -2
  167. package/dist/components/LpCheckbox.vue.js +1 -1
  168. package/dist/components/LpCodeBlock.vue.js +1 -1
  169. package/dist/components/LpCodeDisplay.vue.js +1 -1
  170. package/dist/components/LpCollapse.vue.js +1 -1
  171. package/dist/components/LpColorPicker.vue.js +1 -1
  172. package/dist/components/LpCommandPalette.vue.d.ts +8 -0
  173. package/dist/components/LpCommandPalette.vue.js +107 -56
  174. package/dist/components/LpCommandScope.vue.js +1 -1
  175. package/dist/components/LpConfirmDialog.vue.js +1 -1
  176. package/dist/components/LpConfirmHost.vue.js +1 -1
  177. package/dist/components/LpContextMenu.vue.js +1 -1
  178. package/dist/components/LpContextSwitcher.vue.js +1 -1
  179. package/dist/components/LpCopyButton.vue.js +1 -1
  180. package/dist/components/LpCountdown.vue.js +1 -1
  181. package/dist/components/LpDataList.vue.d.ts +1 -1
  182. package/dist/components/LpDataList.vue.js +1 -1
  183. package/dist/components/LpDatePicker.vue.d.ts +3 -15
  184. package/dist/components/LpDatePicker.vue.js +1 -1
  185. package/dist/components/LpDateRangePicker.vue.d.ts +3 -13
  186. package/dist/components/LpDateRangePicker.vue.js +1 -1
  187. package/dist/components/LpDisclosure.vue.js +1 -1
  188. package/dist/components/LpDock.vue.js +21 -18
  189. package/dist/components/LpDrawer.vue.d.ts +1 -1
  190. package/dist/components/LpDrawer.vue.js +1 -1
  191. package/dist/components/LpDropdownMenu.vue.js +1 -1
  192. package/dist/components/LpDropzone.vue.js +14 -11
  193. package/dist/components/LpEditable.vue.js +1 -1
  194. package/dist/components/LpEmptyState.vue.js +1 -1
  195. package/dist/components/LpFieldNotice.vue.js +1 -1
  196. package/dist/components/LpFileTree.vue.d.ts +96 -6
  197. package/dist/components/LpFileTree.vue.js +1 -1
  198. package/dist/components/LpFileTreeNode.vue.d.ts +48 -0
  199. package/dist/components/LpFileTreeNode.vue.js +1 -1
  200. package/dist/components/LpFormField.vue.js +1 -1
  201. package/dist/components/LpFrame.vue.js +1 -1
  202. package/dist/components/LpGauge.vue.d.ts +1 -1
  203. package/dist/components/LpHintExample.vue.js +1 -1
  204. package/dist/components/LpHintLayer.vue.js +1 -1
  205. package/dist/components/LpHints.vue.js +1 -1
  206. package/dist/components/LpHoverCard.vue.js +1 -1
  207. package/dist/components/LpIcon.vue.d.ts +2 -0
  208. package/dist/components/LpIcon.vue.js +1 -1
  209. package/dist/components/LpIdenticon.vue.js +1 -1
  210. package/dist/components/LpImageEditor.vue.js +7 -6
  211. package/dist/components/LpInfraNode.vue.js +1 -1
  212. package/dist/components/LpInput.vue.d.ts +1 -1
  213. package/dist/components/LpInput.vue.js +1 -1
  214. package/dist/components/LpInspector.vue.d.ts +87 -0
  215. package/dist/components/LpInspector.vue.js +4 -0
  216. package/dist/components/LpLightbox.vue.d.ts +8 -0
  217. package/dist/components/LpLightbox.vue.js +1 -1
  218. package/dist/components/LpLoader.vue.d.ts +1 -1
  219. package/dist/components/LpLoader.vue.js +1 -1
  220. package/dist/components/LpLogInspector.vue.d.ts +18 -0
  221. package/dist/components/LpLogInspector.vue.js +4 -0
  222. package/dist/components/LpLogViewer.vue.d.ts +93 -0
  223. package/dist/components/LpLogViewer.vue.js +1 -1
  224. package/dist/components/LpMapPicker.vue.js +1 -1
  225. package/dist/components/LpMasonry.vue.js +1 -1
  226. package/dist/components/LpMediaGallery.vue.js +1 -1
  227. package/dist/components/LpModal.vue.d.ts +4 -2
  228. package/dist/components/LpModal.vue.js +1 -1
  229. package/dist/components/LpModalNav.vue.d.ts +5 -5
  230. package/dist/components/LpModalNav.vue.js +1 -1
  231. package/dist/components/LpNotificationBell.vue.d.ts +2 -2
  232. package/dist/components/LpNotificationBell.vue.js +1 -1
  233. package/dist/components/LpNumberField.vue.js +1 -1
  234. package/dist/components/LpOtpInput.vue.js +1 -1
  235. package/dist/components/LpPagination.vue.js +1 -1
  236. package/dist/components/LpPaletteFacts.vue.js +1 -1
  237. package/dist/components/LpPaletteHeader.vue.js +2 -2
  238. package/dist/components/LpPaletteTheme.vue.js +1 -1
  239. package/dist/components/LpPasswordInput.vue.js +1 -1
  240. package/dist/components/LpPhoneInput.vue.js +1 -1
  241. package/dist/components/LpPie.vue.d.ts +2 -2
  242. package/dist/components/LpPie.vue.js +1 -1
  243. package/dist/components/LpPipeline.vue.d.ts +4 -0
  244. package/dist/components/LpPipeline.vue.js +1 -1
  245. package/dist/components/LpPopover.vue.d.ts +7 -3
  246. package/dist/components/LpPopover.vue.js +1 -1
  247. package/dist/components/LpProgress.vue.d.ts +1 -1
  248. package/dist/components/LpProgress.vue.js +1 -1
  249. package/dist/components/LpQuota.vue.js +1 -1
  250. package/dist/components/LpRadioGroup.vue.js +1 -1
  251. package/dist/components/LpRangeCalendar.vue.js +1 -1
  252. package/dist/components/LpRangeSlider.vue.js +1 -1
  253. package/dist/components/LpRelativeTime.vue.js +1 -1
  254. package/dist/components/LpRepeater.vue.js +1 -1
  255. package/dist/components/LpRevealOnScroll.vue.js +1 -1
  256. package/dist/components/LpScramble.vue.js +1 -1
  257. package/dist/components/LpScrollArea.vue.js +1 -1
  258. package/dist/components/LpSecretField.vue.js +1 -1
  259. package/dist/components/LpSection.vue.js +1 -1
  260. package/dist/components/LpSegmented.vue.js +1 -1
  261. package/dist/components/LpSelect.vue.js +1 -1
  262. package/dist/components/LpShaderBackdrop.vue.js +1 -1
  263. package/dist/components/LpShift.vue.js +1 -1
  264. package/dist/components/LpShortcutsDialog.vue.js +1 -1
  265. package/dist/components/LpSidebar.vue.d.ts +1 -1
  266. package/dist/components/LpSidebar.vue.js +1 -1
  267. package/dist/components/LpSidebarNav.vue.js +1 -1
  268. package/dist/components/LpSidebarRow.vue.js +1 -1
  269. package/dist/components/LpSlider.vue.js +1 -1
  270. package/dist/components/LpSparkline.vue.d.ts +1 -1
  271. package/dist/components/LpSpotlight.vue.js +1 -1
  272. package/dist/components/LpStat.vue.js +1 -1
  273. package/dist/components/LpStatus.vue.js +1 -1
  274. package/dist/components/LpStepMark.vue.js +1 -1
  275. package/dist/components/LpStepper.vue.js +1 -1
  276. package/dist/components/LpSwap.vue.js +1 -1
  277. package/dist/components/LpSwitch.vue.js +1 -1
  278. package/dist/components/LpTabFace.vue.js +1 -1
  279. package/dist/components/LpTable.vue.d.ts +132 -0
  280. package/dist/components/LpTable.vue.js +547 -135
  281. package/dist/components/LpTableFilter.vue.js +1 -1
  282. package/dist/components/LpTableOfContents.vue.js +1 -1
  283. package/dist/components/LpTabs.vue.js +6 -5
  284. package/dist/components/LpTagsInput.vue.js +1 -1
  285. package/dist/components/LpTextarea.vue.js +1 -1
  286. package/dist/components/LpThemeProvider.vue.d.ts +2 -0
  287. package/dist/components/LpThemeProvider.vue.js +1 -1
  288. package/dist/components/LpThemeStudio.vue.js +1 -1
  289. package/dist/components/LpThemeSwitcher.vue.js +1 -1
  290. package/dist/components/LpTimeField.vue.js +1 -1
  291. package/dist/components/LpToaster.vue.js +1 -1
  292. package/dist/components/LpTooltip.vue.js +1 -1
  293. package/dist/components/LpTopologyCanvas.vue.d.ts +131 -5
  294. package/dist/components/LpTopologyCanvas.vue.js +1 -1
  295. package/dist/components/LpTour.vue.js +1 -1
  296. package/dist/components/LpTruncate.vue.js +1 -1
  297. package/dist/components/LpTypewriter.vue.js +1 -1
  298. package/dist/components/LpUptimeBar.vue.js +1 -1
  299. package/dist/components/LpVideoBackdrop.vue.js +1 -1
  300. package/dist/components/LpWindow.vue.d.ts +99 -0
  301. package/dist/components/LpWindow.vue.js +4 -0
  302. package/dist/components/LpWorkspaceSwitcher.vue.d.ts +18 -0
  303. package/dist/components/LpWorkspaceSwitcher.vue.js +4 -0
  304. package/dist/components/MenuTitle.vue.js +1 -1
  305. package/dist/components/OptionContent.vue.js +1 -1
  306. package/dist/components/PickerField.vue.d.ts +2 -2
  307. package/dist/components/PickerField.vue.js +1 -1
  308. package/dist/components/RevealToggle.vue.js +1 -1
  309. package/dist/components/SliderRail.vue.d.ts +3 -0
  310. package/dist/components/SliderRail.vue.js +4 -0
  311. package/dist/components/TourCard.vue.js +1 -1
  312. package/dist/components/address.js +2 -1
  313. package/dist/components/appShell.d.ts +36 -0
  314. package/dist/components/appShell.js +30 -0
  315. package/dist/components/calendar.d.ts +12 -3
  316. package/dist/components/calendar.js +2 -5
  317. package/dist/components/chart.d.ts +21 -0
  318. package/dist/components/chart.js +4 -3
  319. package/dist/components/chartWindow.d.ts +57 -0
  320. package/dist/components/chartWindow.js +136 -0
  321. package/dist/components/color.js +1 -1
  322. package/dist/components/commandPalette.d.ts +16 -2
  323. package/dist/components/commandPalette.js +39 -4
  324. package/dist/components/controlSize.d.ts +5 -0
  325. package/dist/components/controlSize.js +8 -0
  326. package/dist/components/dropdown.d.ts +5 -0
  327. package/dist/components/dropdown.js +2 -0
  328. package/dist/components/exportData.d.ts +6 -1
  329. package/dist/components/exportData.js +25 -18
  330. package/dist/components/fileTree.d.ts +67 -0
  331. package/dist/components/fileTree.js +141 -1
  332. package/dist/components/identicon.js +3 -2
  333. package/dist/components/logTools.d.ts +93 -0
  334. package/dist/components/logTools.js +210 -0
  335. package/dist/components/menuWhen.js +1 -1
  336. package/dist/components/meter.js +2 -1
  337. package/dist/components/pipeline.d.ts +2 -0
  338. package/dist/components/pipeline.js +5 -0
  339. package/dist/components/relativeTime.js +2 -1
  340. package/dist/components/reveal.js +2 -1
  341. package/dist/components/select.d.ts +5 -0
  342. package/dist/components/select.js +10 -0
  343. package/dist/components/sidebar.d.ts +1 -0
  344. package/dist/components/tableFeatures.d.ts +58 -0
  345. package/dist/components/tableFeatures.js +87 -0
  346. package/dist/components/topologyGraph.d.ts +164 -0
  347. package/dist/components/topologyGraph.js +416 -0
  348. package/dist/components/tour.js +5 -3
  349. package/dist/components/videoBackdrop.js +7 -6
  350. package/dist/composables/clamp.d.ts +2 -0
  351. package/dist/composables/clamp.js +4 -0
  352. package/dist/composables/devWarn.d.ts +3 -0
  353. package/dist/composables/devWarn.js +14 -0
  354. package/dist/composables/easing.d.ts +13 -4
  355. package/dist/composables/easing.js +16 -10
  356. package/dist/composables/overlayHistory.js +14 -1
  357. package/dist/composables/pointerDrag.d.ts +15 -0
  358. package/dist/composables/pointerDrag.js +40 -0
  359. package/dist/composables/propCheck.d.ts +10 -0
  360. package/dist/composables/propCheck.js +78 -0
  361. package/dist/composables/realm.d.ts +6 -0
  362. package/dist/composables/realm.js +23 -0
  363. package/dist/composables/scroll.js +4 -2
  364. package/dist/composables/scrollMemory.d.ts +12 -1
  365. package/dist/composables/scrollMemory.js +36 -12
  366. package/dist/composables/spatialNav.js +2 -1
  367. package/dist/composables/textEntry.d.ts +17 -0
  368. package/dist/composables/textEntry.js +19 -0
  369. package/dist/composables/useAsyncTask.d.ts +6 -0
  370. package/dist/composables/useChartSync.d.ts +5 -0
  371. package/dist/composables/useChartSync.js +33 -0
  372. package/dist/composables/useCloseGuard.d.ts +3 -1
  373. package/dist/composables/useCloseGuard.js +6 -3
  374. package/dist/composables/useCommands.d.ts +50 -0
  375. package/dist/composables/useCommands.js +66 -0
  376. package/dist/composables/useField.d.ts +37 -0
  377. package/dist/composables/useField.js +27 -2
  378. package/dist/composables/useFitScale.js +4 -1
  379. package/dist/composables/useFormDraft.d.ts +13 -2
  380. package/dist/composables/useFormDraft.js +17 -9
  381. package/dist/composables/useHintSystem.js +2 -2
  382. package/dist/composables/useHotkeys.js +11 -32
  383. package/dist/composables/useInView.js +2 -1
  384. package/dist/composables/useInputFilter.js +7 -5
  385. package/dist/composables/useInputMask.js +5 -3
  386. package/dist/composables/useLinkedView.d.ts +97 -0
  387. package/dist/composables/useLinkedView.js +139 -0
  388. package/dist/composables/useOptimisticToggle.d.ts +3 -1
  389. package/dist/composables/useOptimisticToggle.js +2 -1
  390. package/dist/composables/useOverlayHistory.d.ts +7 -0
  391. package/dist/composables/useOverlayHistory.js +16 -2
  392. package/dist/composables/usePaintSelect.js +18 -14
  393. package/dist/composables/usePaletteCommands.d.ts +7 -0
  394. package/dist/composables/usePaletteCommands.js +12 -1
  395. package/dist/composables/usePersisted.d.ts +10 -0
  396. package/dist/composables/usePersisted.js +1 -1
  397. package/dist/composables/usePillTransition.d.ts +2 -2
  398. package/dist/composables/usePillTransition.js +5 -5
  399. package/dist/composables/useResizeObserver.js +2 -1
  400. package/dist/composables/useReveal.js +2 -1
  401. package/dist/composables/useRovingFocus.d.ts +2 -2
  402. package/dist/composables/useRovingFocus.js +9 -11
  403. package/dist/composables/useScramble.js +7 -1
  404. package/dist/composables/useShift.js +8 -6
  405. package/dist/composables/useTilt.js +2 -1
  406. package/dist/composables/useTween.js +9 -5
  407. package/dist/composables/useView.d.ts +1 -0
  408. package/dist/composables/useView.js +14 -2
  409. package/dist/composables/useZoomPan.js +2 -4
  410. package/dist/composables/windowing.d.ts +40 -0
  411. package/dist/composables/windowing.js +118 -0
  412. package/dist/icons-all.js +1 -1
  413. package/dist/icons.d.ts +4 -0
  414. package/dist/index.d.ts +30 -5
  415. package/dist/index.js +310 -264
  416. package/dist/layout/tree.js +19 -15
  417. package/dist/layout/useLayout.d.ts +2 -0
  418. package/dist/layout/useLayout.js +5 -1
  419. package/dist/layout/useWorkspaces.d.ts +52 -0
  420. package/dist/layout/useWorkspaces.js +140 -0
  421. package/dist/{chunks/backdrop-Dkf2vM81.js → theme/backdrop.js} +5 -5
  422. package/dist/theme/config.js +25 -0
  423. package/dist/theme/define.js +61 -0
  424. package/dist/{chunks/palette-Co8tSASo.js → theme/palette.js} +2 -2
  425. package/dist/theme/presets.js +239 -0
  426. package/dist/theme/studio.js +134 -0
  427. package/dist/theme/tokens.js +63 -0
  428. package/dist/theme/useTheme.d.ts +3 -2
  429. package/dist/theme/useTheme.js +325 -0
  430. package/package.json +8 -1
  431. package/src/component-names.ts +5 -1
  432. package/src/components/AppShellContextNav.vue +44 -0
  433. package/src/components/ChartRangeSelector.vue +120 -0
  434. package/src/components/IconCloseButton.vue +21 -0
  435. package/src/components/LayoutCanvas.vue +3 -0
  436. package/src/components/LayoutNode.vue +37 -38
  437. package/src/components/LpActivityLine.vue +4 -2
  438. package/src/components/LpAddressInput.vue +3 -3
  439. package/src/components/LpAppShell.vue +362 -24
  440. package/src/components/LpAutocomplete.vue +13 -30
  441. package/src/components/LpBottomNav.vue +9 -1
  442. package/src/components/LpBreadcrumbs.vue +2 -1
  443. package/src/components/LpButton.vue +11 -4
  444. package/src/components/LpChart.vue +336 -37
  445. package/src/components/LpCheckbox.vue +43 -9
  446. package/src/components/LpCodeBlock.vue +17 -23
  447. package/src/components/LpCollapse.vue +5 -4
  448. package/src/components/LpColorPicker.vue +8 -9
  449. package/src/components/LpCommandPalette.vue +133 -50
  450. package/src/components/LpContextMenu.vue +15 -13
  451. package/src/components/LpCountdown.vue +2 -1
  452. package/src/components/LpDataList.vue +13 -1
  453. package/src/components/LpDatePicker.vue +5 -17
  454. package/src/components/LpDateRangePicker.vue +5 -16
  455. package/src/components/LpDock.vue +25 -16
  456. package/src/components/LpDrawer.vue +70 -61
  457. package/src/components/LpDropdownMenu.vue +6 -10
  458. package/src/components/LpDropzone.vue +12 -7
  459. package/src/components/LpEditable.vue +6 -2
  460. package/src/components/LpFileTree.vue +507 -66
  461. package/src/components/LpFileTreeNode.vue +106 -5
  462. package/src/components/LpFormField.vue +1 -1
  463. package/src/components/LpFrame.vue +33 -13
  464. package/src/components/LpHoverCard.vue +30 -5
  465. package/src/components/LpIcon.vue +6 -2
  466. package/src/components/LpIdenticon.vue +2 -1
  467. package/src/components/LpImageEditor.vue +5 -2
  468. package/src/components/LpInput.vue +15 -23
  469. package/src/components/LpInspector.vue +197 -0
  470. package/src/components/LpLightbox.vue +74 -29
  471. package/src/components/LpLogInspector.vue +85 -0
  472. package/src/components/LpLogViewer.vue +239 -29
  473. package/src/components/LpMapPicker.vue +2 -1
  474. package/src/components/LpMasonry.vue +8 -5
  475. package/src/components/LpModal.vue +12 -12
  476. package/src/components/LpModalNav.vue +2 -21
  477. package/src/components/LpNumberField.vue +6 -15
  478. package/src/components/LpOtpInput.vue +2 -2
  479. package/src/components/LpPagination.vue +5 -3
  480. package/src/components/LpPhoneInput.vue +8 -19
  481. package/src/components/LpPie.vue +25 -23
  482. package/src/components/LpPipeline.vue +14 -1
  483. package/src/components/LpPopover.vue +23 -2
  484. package/src/components/LpProgress.vue +2 -1
  485. package/src/components/LpRadioGroup.vue +2 -2
  486. package/src/components/LpRangeSlider.vue +5 -6
  487. package/src/components/LpRevealOnScroll.vue +1 -1
  488. package/src/components/LpScrollArea.vue +28 -11
  489. package/src/components/LpSecretField.vue +2 -1
  490. package/src/components/LpSegmented.vue +6 -8
  491. package/src/components/LpSelect.vue +54 -24
  492. package/src/components/LpShaderBackdrop.vue +12 -9
  493. package/src/components/LpShift.vue +18 -15
  494. package/src/components/LpSidebar.vue +77 -74
  495. package/src/components/LpSidebarNav.vue +3 -2
  496. package/src/components/LpSidebarRow.vue +2 -1
  497. package/src/components/LpSlider.vue +5 -6
  498. package/src/components/LpSpotlight.vue +2 -1
  499. package/src/components/LpSwitch.vue +5 -3
  500. package/src/components/LpTable.vue +609 -93
  501. package/src/components/LpTableFilter.vue +1 -1
  502. package/src/components/LpTableOfContents.vue +16 -9
  503. package/src/components/LpTabs.vue +2 -1
  504. package/src/components/LpTagsInput.vue +12 -19
  505. package/src/components/LpTextarea.vue +5 -11
  506. package/src/components/LpThemeProvider.vue +29 -13
  507. package/src/components/LpTimeField.vue +4 -7
  508. package/src/components/LpToaster.vue +7 -6
  509. package/src/components/LpTooltip.vue +62 -18
  510. package/src/components/LpTopologyCanvas.vue +567 -42
  511. package/src/components/LpTour.vue +7 -5
  512. package/src/components/LpTruncate.vue +4 -3
  513. package/src/components/LpTypewriter.vue +2 -1
  514. package/src/components/LpVideoBackdrop.vue +11 -13
  515. package/src/components/LpWindow.vue +373 -0
  516. package/src/components/LpWorkspaceSwitcher.vue +116 -0
  517. package/src/components/PickerField.vue +2 -2
  518. package/src/components/SliderRail.vue +10 -0
  519. package/src/components/address.ts +3 -1
  520. package/src/components/appShell.ts +74 -0
  521. package/src/components/calendar.ts +12 -5
  522. package/src/components/chart.ts +27 -3
  523. package/src/components/chartWindow.ts +198 -0
  524. package/src/components/color.ts +2 -1
  525. package/src/components/commandPalette.ts +66 -5
  526. package/src/components/controlSize.ts +6 -0
  527. package/src/components/dropdown.ts +3 -0
  528. package/src/components/exportData.ts +33 -18
  529. package/src/components/fileTree.ts +215 -0
  530. package/src/components/identicon.ts +4 -2
  531. package/src/components/logTools.ts +298 -0
  532. package/src/components/meter.ts +2 -1
  533. package/src/components/pipeline.ts +6 -0
  534. package/src/components/relativeTime.ts +2 -1
  535. package/src/components/reveal.ts +3 -1
  536. package/src/components/select.ts +14 -0
  537. package/src/components/sidebar.ts +1 -0
  538. package/src/components/tableFeatures.ts +149 -0
  539. package/src/components/topologyGraph.ts +655 -0
  540. package/src/components/tour.ts +5 -3
  541. package/src/components/videoBackdrop.ts +7 -6
  542. package/src/composables/clamp.ts +2 -0
  543. package/src/composables/devWarn.ts +19 -0
  544. package/src/composables/easing.ts +37 -12
  545. package/src/composables/overlayHistory.ts +24 -0
  546. package/src/composables/pointerDrag.ts +54 -0
  547. package/src/composables/propCheck.ts +90 -0
  548. package/src/composables/realm.ts +25 -0
  549. package/src/composables/scroll.ts +4 -2
  550. package/src/composables/scrollMemory.ts +56 -14
  551. package/src/composables/spatialNav.ts +2 -1
  552. package/src/composables/textEntry.ts +33 -0
  553. package/src/composables/useAsyncTask.ts +6 -0
  554. package/src/composables/useChartSync.ts +52 -0
  555. package/src/composables/useCloseGuard.ts +22 -10
  556. package/src/composables/useCommands.ts +123 -0
  557. package/src/composables/useField.ts +41 -2
  558. package/src/composables/useFitScale.ts +4 -1
  559. package/src/composables/useFormDraft.ts +38 -14
  560. package/src/composables/useHintSystem.ts +2 -2
  561. package/src/composables/useHotkeys.ts +19 -48
  562. package/src/composables/useInView.ts +2 -1
  563. package/src/composables/useInputFilter.ts +8 -5
  564. package/src/composables/useInputMask.ts +8 -3
  565. package/src/composables/useLinkedView.ts +222 -0
  566. package/src/composables/useOptimisticToggle.ts +7 -1
  567. package/src/composables/useOverlayHistory.ts +20 -0
  568. package/src/composables/usePaintSelect.ts +20 -14
  569. package/src/composables/usePaletteCommands.ts +13 -0
  570. package/src/composables/usePersisted.ts +10 -0
  571. package/src/composables/usePillTransition.ts +9 -6
  572. package/src/composables/useResizeObserver.ts +2 -1
  573. package/src/composables/useReveal.ts +2 -1
  574. package/src/composables/useRovingFocus.ts +11 -14
  575. package/src/composables/useScramble.ts +12 -1
  576. package/src/composables/useShift.ts +8 -6
  577. package/src/composables/useTilt.ts +2 -1
  578. package/src/composables/useTween.ts +10 -5
  579. package/src/composables/useView.ts +17 -3
  580. package/src/composables/useZoomPan.ts +2 -5
  581. package/src/composables/windowing.ts +190 -0
  582. package/src/icons.ts +27 -6
  583. package/src/index.ts +41 -4
  584. package/src/kit-icons.ts +1 -1
  585. package/src/layout/tree.ts +31 -16
  586. package/src/layout/useLayout.ts +6 -0
  587. package/src/layout/useWorkspaces.ts +234 -0
  588. package/src/nuxt.ts +9 -1
  589. package/src/runtime/propCheck.plugin.ts +10 -0
  590. package/src/theme/studio.ts +7 -5
  591. package/src/theme/useTheme.ts +89 -15
  592. package/src/tokens/tokens.css +8 -1
  593. package/dist/assets/LpTabs-CJ0rYQbx.css +0 -18
  594. package/dist/chunks/LpAppShell-Dgejipzz.js +0 -319
  595. package/dist/chunks/LpFileTree-CooHinsC.js +0 -425
  596. package/dist/chunks/LpThemeProvider-8MG6yqU9.js +0 -640
  597. package/dist/chunks/LpTopologyCanvas-B1gIbS0n.js +0 -298
@@ -4,6 +4,7 @@
4
4
  * component-to-component cycle. Re-exported from LpFileTree.vue.
5
5
  */
6
6
 
7
+ import { clamp } from "../composables/clamp"
7
8
  import type { ContextMenuItemDef } from "./LpContextMenu.vue"
8
9
  import { formatBytes } from "./format"
9
10
 
@@ -329,3 +330,217 @@ export function ancestorIds(nodes: FileNode[], id: string): string[] {
329
330
  walk(nodes, [])
330
331
  return path
331
332
  }
333
+
334
+ /** What narrows the tree. Every field is optional; all that are set must hold. */
335
+ export interface FileTreeFilter {
336
+ /** Case-insensitive, against the entry's name and its path from the root. */
337
+ query?: string
338
+ kind?: FileNode["kind"]
339
+ /** File extensions without the dot. A directory never matches one. */
340
+ extensions?: string[]
341
+ /** Bytes, inclusive. An entry with no `size` does not match. */
342
+ minSize?: number
343
+ maxSize?: number
344
+ /** Inclusive. An entry with no readable `modified` does not match. */
345
+ modifiedFrom?: Date | number
346
+ modifiedTo?: Date | number
347
+ }
348
+
349
+ const PATH_SEPARATOR = "/"
350
+
351
+ /** A file's extension, lowercase. A leading dot starts a name, not an extension. */
352
+ export function fileExtension(name: string): string {
353
+ const dot = name.lastIndexOf(".")
354
+ return dot > 0 ? name.slice(dot + 1).toLowerCase() : ""
355
+ }
356
+
357
+ function modifiedTime(value: FileNode["modified"]): number {
358
+ if (value === undefined) return Number.NaN
359
+ return value instanceof Date ? value.getTime() : typeof value === "number" ? value : Date.parse(value)
360
+ }
361
+
362
+ const timeOf = (value: Date | number) => (typeof value === "number" ? value : value.getTime())
363
+
364
+ /** True when no field of the filter would narrow anything. */
365
+ export function isFilterEmpty(filter: FileTreeFilter | undefined): boolean {
366
+ if (!filter) return true
367
+ return (
368
+ !filter.query?.trim() &&
369
+ filter.kind === undefined &&
370
+ !filter.extensions?.length &&
371
+ filter.minSize === undefined &&
372
+ filter.maxSize === undefined &&
373
+ filter.modifiedFrom === undefined &&
374
+ filter.modifiedTo === undefined
375
+ )
376
+ }
377
+
378
+ /** Whether one entry, at this path, passes the filter on its own. */
379
+ export function matchesFilter(node: FileNode, path: string, filter: FileTreeFilter): boolean {
380
+ const needle = filter.query?.trim().toLowerCase()
381
+ if (needle && !node.name.toLowerCase().includes(needle) && !path.toLowerCase().includes(needle)) return false
382
+ if (filter.kind && node.kind !== filter.kind) return false
383
+ if (filter.extensions?.length) {
384
+ if (node.kind !== "file") return false
385
+ const wanted = filter.extensions.map((ext) => ext.replace(/^\./, "").toLowerCase())
386
+ if (!wanted.includes(fileExtension(node.name))) return false
387
+ }
388
+ if (filter.minSize !== undefined || filter.maxSize !== undefined) {
389
+ if (node.size === undefined) return false
390
+ if (filter.minSize !== undefined && node.size < filter.minSize) return false
391
+ if (filter.maxSize !== undefined && node.size > filter.maxSize) return false
392
+ }
393
+ if (filter.modifiedFrom !== undefined || filter.modifiedTo !== undefined) {
394
+ const time = modifiedTime(node.modified)
395
+ if (Number.isNaN(time)) return false
396
+ if (filter.modifiedFrom !== undefined && time < timeOf(filter.modifiedFrom)) return false
397
+ if (filter.modifiedTo !== undefined && time > timeOf(filter.modifiedTo)) return false
398
+ }
399
+ return true
400
+ }
401
+
402
+ export interface FilteredTree {
403
+ nodes: FileNode[]
404
+ /** Directories that hold a match: shown open while the filter is on. */
405
+ openIds: Set<string>
406
+ }
407
+
408
+ /**
409
+ * The tree with only the matches and the folders leading to them. A kept folder
410
+ * is a copy whose `children` are its kept entries, so what is ticked through it
411
+ * is what is shown. A folder that is not loaded keeps `children` undefined: only
412
+ * loaded entries can be searched.
413
+ */
414
+ export function filterTree(nodes: FileNode[], filter: FileTreeFilter | undefined): FilteredTree {
415
+ const openIds = new Set<string>()
416
+ if (!filter || isFilterEmpty(filter)) return { nodes, openIds }
417
+ const active = filter
418
+
419
+ function walk(list: FileNode[], parentPath: string): FileNode[] {
420
+ const kept: FileNode[] = []
421
+ for (const node of list) {
422
+ const path = parentPath ? `${parentPath}${PATH_SEPARATOR}${node.name}` : node.name
423
+ const children = node.children && walk(node.children, path)
424
+ if (children?.length) {
425
+ openIds.add(node.id)
426
+ kept.push({ ...node, children })
427
+ } else if (matchesFilter(node, path, active)) {
428
+ kept.push(children ? { ...node, children } : node)
429
+ }
430
+ }
431
+ return kept
432
+ }
433
+
434
+ return { nodes: walk(nodes, ""), openIds }
435
+ }
436
+
437
+ /** The tree with children fetched by `load` put in place. The app's own `children` win. */
438
+ export function withLoadedChildren(nodes: FileNode[], loaded: ReadonlyMap<string, FileNode[]>): FileNode[] {
439
+ if (!loaded.size) return nodes
440
+ return nodes.map((node) => {
441
+ if (node.kind !== "dir") return node
442
+ const children = node.children ?? loaded.get(node.id)
443
+ if (!children) return node
444
+ return { ...node, children: withLoadedChildren(children, loaded) }
445
+ })
446
+ }
447
+
448
+ /** One row of the flattened tree, with what a flat DOM cannot imply to assistive tech. */
449
+ export interface FlatRow {
450
+ node: FileNode
451
+ depth: number
452
+ /** Siblings at this level, and this row's 1-based place among them. */
453
+ setSize: number
454
+ posInSet: number
455
+ }
456
+
457
+ /** The visible rows in render order: open directories followed by their children. */
458
+ export function flattenVisible(nodes: FileNode[], open: ReadonlySet<string>, sort: boolean): FlatRow[] {
459
+ const rows: FlatRow[] = []
460
+ function walk(list: FileNode[], depth: number) {
461
+ const ordered = sort ? sortNodes(list) : list
462
+ ordered.forEach((node, index) => {
463
+ rows.push({ node, depth, setSize: ordered.length, posInSet: index + 1 })
464
+ if (node.kind === "dir" && open.has(node.id) && node.children?.length) walk(node.children, depth + 1)
465
+ })
466
+ }
467
+ walk(nodes, 0)
468
+ return rows
469
+ }
470
+
471
+ export interface RowWindow {
472
+ /** First row to render, and one past the last. */
473
+ start: number
474
+ end: number
475
+ }
476
+
477
+ /** Which rows a scroll position shows, with `overscan` extra on each side. */
478
+ export function rowWindow(scrollTop: number, viewport: number, rowHeight: number, total: number, overscan: number): RowWindow {
479
+ const first = Math.floor(Math.max(0, scrollTop) / rowHeight)
480
+ const count = Math.ceil(Math.max(0, viewport) / rowHeight)
481
+ return {
482
+ start: clamp(first - overscan, 0, total),
483
+ end: Math.min(total, first + count + overscan),
484
+ }
485
+ }
486
+
487
+ /** Ids between two rows, inclusive, in row order; locked rows are left out. */
488
+ export function rangeIds(rows: readonly FileNode[], anchor: string | undefined, target: string): string[] {
489
+ const to = rows.findIndex((node) => node.id === target)
490
+ if (to < 0) return []
491
+ const found = anchor === undefined ? -1 : rows.findIndex((node) => node.id === anchor)
492
+ const from = found < 0 ? to : found
493
+ return rows
494
+ .slice(Math.min(from, to), Math.max(from, to) + 1)
495
+ .filter((node) => !node.disabled)
496
+ .map((node) => node.id)
497
+ }
498
+
499
+ /** The chain from the root down to `id`, itself included. */
500
+ export function nodeTrail(id: string, byId: ReadonlyMap<string, FileNode>, parentOf: ReadonlyMap<string, string>): FileNode[] {
501
+ const trail: FileNode[] = []
502
+ for (let at: string | undefined = id; at !== undefined; at = parentOf.get(at)) {
503
+ const node = byId.get(at)
504
+ if (node) trail.unshift(node)
505
+ }
506
+ return trail
507
+ }
508
+
509
+ /**
510
+ * Whether `sources` may land in `target`: a directory that is not one of them,
511
+ * not inside one of them, and not where all of them already are.
512
+ */
513
+ export function canDrop(sources: readonly string[], target: FileNode, parentOf: ReadonlyMap<string, string>): boolean {
514
+ if (target.kind !== "dir" || target.disabled || !sources.length) return false
515
+ for (let at: string | undefined = target.id; at !== undefined; at = parentOf.get(at)) {
516
+ if (sources.includes(at)) return false
517
+ }
518
+ return sources.some((id) => parentOf.get(id) !== target.id)
519
+ }
520
+
521
+ /** Those of `ids` no other of them contains: a moved folder carries what is inside it. */
522
+ export function topmostIds(ids: readonly string[], parentOf: ReadonlyMap<string, string>): string[] {
523
+ const all = new Set(ids)
524
+ return ids.filter((id) => {
525
+ for (let at = parentOf.get(id); at !== undefined; at = parentOf.get(at)) {
526
+ if (all.has(at)) return false
527
+ }
528
+ return true
529
+ })
530
+ }
531
+
532
+ export type RenameProblem = "empty" | "separator" | "duplicate"
533
+
534
+ /** Why a new name cannot stand, or null. An unchanged name is the caller's no-op. */
535
+ export function renameProblem(name: string, node: FileNode, siblings: readonly FileNode[]): RenameProblem | null {
536
+ const next = name.trim()
537
+ if (!next) return "empty"
538
+ if (/[/\\\0]/.test(next)) return "separator"
539
+ return siblings.some((other) => other.id !== node.id && other.name === next) ? "duplicate" : null
540
+ }
541
+
542
+ /** How much of a name to select for renaming: the stem, so the extension survives typing. */
543
+ export function renameSelectionEnd(node: FileNode): number {
544
+ const dot = node.name.lastIndexOf(".")
545
+ return node.kind === "file" && dot > 0 ? dot : node.name.length
546
+ }
@@ -1,3 +1,5 @@
1
+ import { clamp } from "../composables/clamp"
2
+
1
3
  /*
2
4
  * Identicons: a picture derived from a string, so something with no avatar
3
5
  * of its own (a new account, a server, an API key, a commit) still gets a
@@ -67,7 +69,7 @@ function generator(seed: number): () => number {
67
69
 
68
70
  /** The grid and colour for a seed. */
69
71
  export function identicon(seed: string, options: IdenticonOptions = {}): Identicon {
70
- const size = Math.max(3, Math.min(12, Math.round(options.grid ?? 5)))
72
+ const size = clamp(Math.round(options.grid ?? 5), 3, 12)
71
73
  const [sMin, sMax] = options.saturation ?? [55, 72]
72
74
  const [lMin, lMax] = options.lightness ?? [48, 60]
73
75
  const next = generator(hashString(seed))
@@ -146,7 +148,7 @@ export interface IdenticonSvgOptions extends IdenticonOptions {
146
148
  export function identiconSvg(seed: string, options: IdenticonSvgOptions = {}): string {
147
149
  const { cells, color } = identicon(seed, options)
148
150
  const n = cells.length
149
- const pad = n * Math.max(0, Math.min(0.4, options.padding ?? 0.12))
151
+ const pad = n * clamp(options.padding ?? 0.12, 0, 0.4)
150
152
  const view = n + pad * 2
151
153
  const px = options.size ?? 64
152
154
  const bg = options.background ? `<rect width="${view}" height="${view}" fill="${options.background}"/>` : ""
@@ -0,0 +1,298 @@
1
+ /*
2
+ * The log viewer's reading tools, free of the component so their rules can be
3
+ * tested alone: pattern search, time range, repeats, correlation ids, structured
4
+ * payloads and what a selection exports as.
5
+ */
6
+
7
+ /*
8
+ * A pattern is typed by a person but runs over every line of a live stream, and
9
+ * JavaScript cannot interrupt a running match. So the guard is in what may reach
10
+ * the engine: a bounded pattern, none of the nested quantifiers that backtrack
11
+ * catastrophically, a bounded length of text per line, and a time budget per
12
+ * pass that stops starting new lines. A single match on one capped line is the
13
+ * longest it can block.
14
+ */
15
+ export const MAX_PATTERN_LENGTH = 200
16
+ export const MAX_MATCH_INPUT = 2000
17
+ export const MATCH_BUDGET_MS = 50
18
+ const NESTED_QUANTIFIER = /\((?:[^()\\]|\\.)*[+*](?:[^()\\]|\\.)*\)\s*(?:[+*]|\{\d+,\d*\})/
19
+
20
+ export type PatternError = "too-long" | "unsafe" | "invalid"
21
+
22
+ export interface LogPattern {
23
+ test(text: string): boolean
24
+ ranges(text: string): [number, number][]
25
+ }
26
+
27
+ export function compilePattern(source: string): { pattern: LogPattern | null; error: PatternError | null } {
28
+ if (source.length > MAX_PATTERN_LENGTH) return { pattern: null, error: "too-long" }
29
+ if (NESTED_QUANTIFIER.test(source)) return { pattern: null, error: "unsafe" }
30
+ let regex: RegExp
31
+ try {
32
+ regex = new RegExp(source, "giu")
33
+ } catch {
34
+ return { pattern: null, error: "invalid" }
35
+ }
36
+ const capped = (text: string) => (text.length > MAX_MATCH_INPUT ? text.slice(0, MAX_MATCH_INPUT) : text)
37
+ return {
38
+ error: null,
39
+ pattern: {
40
+ test(text) {
41
+ regex.lastIndex = 0
42
+ return regex.test(capped(text))
43
+ },
44
+ ranges(text) {
45
+ const out: [number, number][] = []
46
+ // matchAll starts from wherever the last `test` left the shared regex.
47
+ regex.lastIndex = 0
48
+ for (const match of capped(text).matchAll(regex)) {
49
+ // A zero-width match highlights nothing.
50
+ if (match[0]) out.push([match.index, match.index + match[0].length])
51
+ }
52
+ return out
53
+ },
54
+ },
55
+ }
56
+ }
57
+
58
+ /** Positions of the rows the pattern finds, giving up once the budget is spent. */
59
+ export function patternPositions(
60
+ rows: readonly { message: string; source?: string }[],
61
+ pattern: LogPattern,
62
+ now: () => number = () => performance.now(),
63
+ ): number[] {
64
+ const out: number[] = []
65
+ const deadline = now() + MATCH_BUDGET_MS
66
+ for (let i = 0; i < rows.length; i++) {
67
+ if (now() > deadline) break
68
+ const row = rows[i]
69
+ if (pattern.test(row.message) || (row.source !== undefined && pattern.test(row.source))) out.push(i)
70
+ }
71
+ return out
72
+ }
73
+
74
+ /** Every occurrence of a plain term, case-insensitive. */
75
+ export function termRanges(text: string, term: string): [number, number][] {
76
+ const needle = term.toLowerCase()
77
+ if (!needle) return []
78
+ const lower = text.toLowerCase()
79
+ const out: [number, number][] = []
80
+ for (let at = lower.indexOf(needle); at !== -1; at = lower.indexOf(needle, at + needle.length)) {
81
+ out.push([at, at + needle.length])
82
+ }
83
+ return out
84
+ }
85
+
86
+ /** A line's time as epoch ms; a pre-formatted label ("12:03:05") has none. */
87
+ export function timeMs(time: number | string | undefined): number | null {
88
+ if (time == null) return null
89
+ if (typeof time === "number") return Number.isFinite(time) ? time : null
90
+ if (/^\d+$/.test(time)) return Number(time)
91
+ const parsed = Date.parse(time)
92
+ return Number.isNaN(parsed) ? null : parsed
93
+ }
94
+
95
+ export interface LogTimeRange {
96
+ from?: number | string
97
+ to?: number | string
98
+ }
99
+
100
+ /**
101
+ * Inclusive at both ends. A line with no readable time stays: most are the
102
+ * continuation of the entry above (a stack frame), and hiding them would cut
103
+ * the entries the range keeps in half.
104
+ */
105
+ export function inTimeRange(time: number | string | undefined, range: LogTimeRange | undefined): boolean {
106
+ if (!range) return true
107
+ const at = timeMs(time)
108
+ if (at === null) return true
109
+ const from = timeMs(range.from)
110
+ const to = timeMs(range.to)
111
+ return (from === null || at >= from) && (to === null || at <= to)
112
+ }
113
+
114
+ /** The position of the row closest in time, -1 when none has a readable time. */
115
+ export function nearestByTime(rows: readonly { time?: number | string }[], target: number | string): number {
116
+ const wanted = timeMs(target)
117
+ if (wanted === null) return -1
118
+ let best = -1
119
+ let distance = Infinity
120
+ rows.forEach((row, i) => {
121
+ const at = timeMs(row.time)
122
+ if (at === null) return
123
+ const gap = Math.abs(at - wanted)
124
+ if (gap < distance) {
125
+ best = i
126
+ distance = gap
127
+ }
128
+ })
129
+ return best
130
+ }
131
+
132
+ const VOLATILE = /\b[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\b|\b0x[0-9a-f]+\b|\b[0-9a-f]{16,}\b|\d+(?:\.\d+)*/gi
133
+
134
+ /** A message with its volatile parts (numbers, ids, hashes) blanked, so repeats compare equal. */
135
+ export function messageShape(message: string): string {
136
+ return message.replace(VOLATILE, "#")
137
+ }
138
+
139
+ export interface RepeatRow<L> {
140
+ line: L
141
+ n: number
142
+ count: number
143
+ }
144
+
145
+ /** Folds rows of the same level, source and shape into the first, wherever they sit. */
146
+ export function groupRepeats<L extends { message: string; level?: string; source?: string }>(
147
+ rows: readonly { line: L; n: number }[],
148
+ ): RepeatRow<L>[] {
149
+ const groups = new Map<string, RepeatRow<L>>()
150
+ for (const { line, n } of rows) {
151
+ const key = [line.level ?? "", line.source ?? "", messageShape(line.message)].join("\0")
152
+ const group = groups.get(key)
153
+ if (group) group.count++
154
+ else groups.set(key, { line, n, count: 1 })
155
+ }
156
+ return [...groups.values()]
157
+ }
158
+
159
+ export type LogIdKind = "trace" | "request" | "span" | "correlation"
160
+
161
+ export interface LogId {
162
+ kind: LogIdKind
163
+ value: string
164
+ start: number
165
+ end: number
166
+ }
167
+
168
+ const ID_PATTERN = /\b(trace|request|req|span|correlation|corr)[_-]?id["']?\s*[=:]\s*["']?([\w-]{6,})/gi
169
+ const ID_KINDS: Record<string, LogIdKind> = {
170
+ trace: "trace",
171
+ request: "request",
172
+ req: "request",
173
+ span: "span",
174
+ correlation: "correlation",
175
+ corr: "correlation",
176
+ }
177
+
178
+ /** The correlation ids a message carries, as `trace_id=abc` or `"requestId": "abc"`. */
179
+ export function findIds(message: string): LogId[] {
180
+ const out: LogId[] = []
181
+ for (const match of message.matchAll(ID_PATTERN)) {
182
+ const value = match[2]
183
+ const start = match.index + match[0].length - value.length
184
+ out.push({ kind: ID_KINDS[match[1].toLowerCase()], value, start, end: start + value.length })
185
+ }
186
+ return out
187
+ }
188
+
189
+ export interface MessagePart {
190
+ text: string
191
+ hit: boolean
192
+ id?: LogId
193
+ }
194
+
195
+ /** A message cut around its search hits and ids. An id wins where the two overlap. */
196
+ export function messageParts(message: string, hits: readonly [number, number][], ids: readonly LogId[]): MessagePart[] {
197
+ const parts: MessagePart[] = []
198
+ const plain = (from: number, to: number) => {
199
+ let at = from
200
+ for (const [start, end] of hits) {
201
+ if (end <= at || start >= to) continue
202
+ if (start > at) parts.push({ text: message.slice(at, start), hit: false })
203
+ const stop = Math.min(end, to)
204
+ parts.push({ text: message.slice(Math.max(start, at), stop), hit: true })
205
+ at = stop
206
+ }
207
+ if (at < to) parts.push({ text: message.slice(at, to), hit: false })
208
+ }
209
+ let at = 0
210
+ for (const id of ids) {
211
+ plain(at, id.start)
212
+ parts.push({ text: message.slice(id.start, id.end), hit: false, id })
213
+ at = id.end
214
+ }
215
+ plain(at, message.length)
216
+ return parts.length ? parts : [{ text: message, hit: false }]
217
+ }
218
+
219
+ /** The object or array a message holds, whole or after a text prefix; null when it holds none. */
220
+ export function structuredPayload(message: string): object | null {
221
+ const start = message.search(/[{[]/)
222
+ if (start < 0) return null
223
+ try {
224
+ const value: unknown = JSON.parse(message.slice(start))
225
+ return typeof value === "object" && value !== null ? value : null
226
+ } catch {
227
+ return null
228
+ }
229
+ }
230
+
231
+ export interface JsonRow {
232
+ path: string
233
+ depth: number
234
+ label: string
235
+ /** Rendered value; for an object or array its size, as `{3}` or `[3]`. */
236
+ value: string
237
+ kind: "object" | "array" | "string" | "number" | "boolean" | "null"
238
+ expandable: boolean
239
+ }
240
+
241
+ function kindOf(value: unknown): JsonRow["kind"] {
242
+ if (value === null) return "null"
243
+ if (Array.isArray(value)) return "array"
244
+ const type = typeof value
245
+ return type === "string" || type === "number" || type === "boolean" ? type : "object"
246
+ }
247
+
248
+ function holds(value: unknown, needle: string, label: string): boolean {
249
+ if (label.toLowerCase().includes(needle)) return true
250
+ if (value !== null && typeof value === "object") {
251
+ return Object.entries(value).some(([key, child]) => holds(child, needle, key))
252
+ }
253
+ return String(value).toLowerCase().includes(needle)
254
+ }
255
+
256
+ /**
257
+ * A payload as flat rows, skipping what is collapsed. With `filter`, only the
258
+ * fields whose name or value hold it, plus the parents leading to them.
259
+ */
260
+ export function jsonRows(root: object, collapsed: ReadonlySet<string>, filter = ""): JsonRow[] {
261
+ const needle = filter.trim().toLowerCase()
262
+ const rows: JsonRow[] = []
263
+ const walk = (value: object, parent: string, depth: number) => {
264
+ for (const [label, child] of Object.entries(value)) {
265
+ if (needle && !holds(child, needle, label)) continue
266
+ const path = parent ? `${parent}.${label}` : label
267
+ const kind = kindOf(child)
268
+ const nested = kind === "object" || kind === "array"
269
+ const size = nested ? Object.keys(child as object).length : 0
270
+ const shown = kind === "array" ? `[${size}]` : kind === "object" ? `{${size}}` : kind === "string" ? (child as string) : String(child)
271
+ rows.push({ path, depth, label, value: shown, kind, expandable: nested && size > 0 })
272
+ if (nested && !collapsed.has(path)) walk(child as object, path, depth + 1)
273
+ }
274
+ }
275
+ walk(root, "", 0)
276
+ return rows
277
+ }
278
+
279
+ const STACK_FRAME = /^\s*(?:at\s+\S|File ".+", line \d+|#\d+\s|\S+\.\w+:\d+)/
280
+
281
+ /** Text split into lines, with a stack's frames marked; null when it holds no stack. */
282
+ export function stackLines(text: string): { text: string; frame: boolean }[] | null {
283
+ const lines = text.split(/\r?\n/).map((line) => ({ text: line, frame: STACK_FRAME.test(line) }))
284
+ return lines.length > 1 && lines.some((line) => line.frame) ? lines : null
285
+ }
286
+
287
+ export type LogExportFormat = "text" | "ndjson"
288
+
289
+ /** Lines as a file: readable text, or one JSON object per line. */
290
+ export function exportLines<L>(lines: readonly L[], format: LogExportFormat, asText: (line: L) => string): string {
291
+ const rows = format === "ndjson" ? lines.map((line) => JSON.stringify(line)) : lines.map(asText)
292
+ return rows.join("\n")
293
+ }
294
+
295
+ /** The inclusive run between two row numbers, whichever order they came in. */
296
+ export function rowSpan(from: number, to: number): [number, number] {
297
+ return from <= to ? [from, to] : [to, from]
298
+ }
@@ -5,6 +5,7 @@
5
5
  * disk agree on when it turns amber, instead of each picking its own breakpoints.
6
6
  * The colours themselves are tone.ts's: a meter tone is one of its aliases.
7
7
  */
8
+ import { clamp } from "../composables/clamp"
8
9
  import { TONE_ALIASES, TONES, type ToneLook } from "./tone"
9
10
 
10
11
  /**
@@ -38,7 +39,7 @@ export const TONE_SOFT: Record<MeterTone, string> = byMeterTone((look) => look.s
38
39
  export function meterFraction(value: number, min: number, max: number): number {
39
40
  const span = max - min
40
41
  if (!(span > 0) || !Number.isFinite(value)) return 0
41
- return Math.max(0, Math.min(1, (value - min) / span))
42
+ return clamp((value - min) / span, 0, 1)
42
43
  }
43
44
 
44
45
  /**
@@ -99,3 +99,9 @@ export function childProgress(step: PipelineStep): { done: number; total: number
99
99
  export function stepKey(step: Pick<PipelineStep, "key">, index: number): string {
100
100
  return step.key ?? String(index)
101
101
  }
102
+
103
+ /** Keys used by more than one step, which would then share their open state. */
104
+ export function duplicateStepKeys(steps: readonly Pick<PipelineStep, "key">[]): string[] {
105
+ const keys = steps.map(stepKey)
106
+ return [...new Set(keys.filter((key, i) => keys.indexOf(key) !== i))]
107
+ }
@@ -1,3 +1,4 @@
1
+ import { clamp } from "../composables/clamp"
1
2
  import { toEpoch } from "./timeSpan"
2
3
 
3
4
  /*
@@ -114,5 +115,5 @@ export function formatRelativeTime(
114
115
  }
115
116
 
116
117
  function clampRefresh(ms: number): number {
117
- return Math.min(MAX_REFRESH, Math.max(MIN_REFRESH, Math.ceil(ms)))
118
+ return clamp(Math.ceil(ms), MIN_REFRESH, MAX_REFRESH)
118
119
  }
@@ -1,3 +1,5 @@
1
+ import { clamp } from "../composables/clamp"
2
+
1
3
  /*
2
4
  * Keyframes and timing for LpRevealOnScroll, kept apart from the component so
3
5
  * they can be tested without a DOM.
@@ -52,5 +54,5 @@ export function revealTo(from: Keyframe): Keyframe {
52
54
  * last rows would still be invisible when the user starts reading them.
53
55
  */
54
56
  export function staggerDelay(index: number, delay: number, stagger: number, cap = 10): number {
55
- return delay + Math.min(Math.max(0, index), cap) * Math.max(0, stagger)
57
+ return delay + clamp(index, 0, cap) * Math.max(0, stagger)
56
58
  }
@@ -3,6 +3,20 @@
3
3
  * testable without a DOM.
4
4
  */
5
5
 
6
+ /**
7
+ * Drop the oldest entries past `limit`, never one of `keep`. For a cache that
8
+ * must outlive the list it was read from, in a long session over a big catalog.
9
+ */
10
+ export function evictOldest<K, V>(cache: Map<K, V>, keep: ReadonlySet<K>, limit: number): void {
11
+ let excess = cache.size - limit
12
+ for (const key of cache.keys()) {
13
+ if (excess <= 0) return
14
+ if (keep.has(key)) continue
15
+ cache.delete(key)
16
+ excess--
17
+ }
18
+ }
19
+
6
20
  /**
7
21
  * How many chips of a multiple pick fit in one row before the rest collapse
8
22
  * into a "+N" chip.
@@ -48,6 +48,7 @@ export interface SidebarItem {
48
48
  * away without opening the section first. In a compact sidebar they open in
49
49
  * a card beside the icon.
50
50
  */
51
+ /** One level: search, the active row and the rail stop at these pages, nothing under them is drawn. */
51
52
  children?: SidebarItem[]
52
53
  /** A dot instead of a count: something new here that has no number to it. */
53
54
  dot?: boolean