@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.
- package/README.md +68 -0
- package/dist/assets/{LpCommandPalette-DOIZHw-g.css → LpCommandPalette-DDJKICKh.css} +19 -19
- package/dist/assets/{LpDock-Cbh0wQUv.css → LpDock-Dw82tWVD.css} +3 -3
- package/dist/assets/{LpDropzone-B_qRq1-R.css → LpDropzone-DNbh_NFJ.css} +10 -10
- package/dist/assets/{LpImageEditor-XM5bAziW.css → LpImageEditor-Bgu20XLs.css} +6 -6
- package/dist/assets/{LpTable-Bv-EcQs7.css → LpTable-CCk0ZHo7.css} +19 -19
- package/dist/assets/LpTabs-Bh7a-Bdl.css +18 -0
- package/dist/chunks/AppShellContextNav-C6Ho2mof.js +49 -0
- package/dist/chunks/ChartRangeSelector-HO0sMMz6.js +129 -0
- package/dist/chunks/{ContextMenuItems-BXumicbh.js → ContextMenuItems-DmaKSJVv.js} +1 -1
- package/dist/chunks/{DropzoneLinkField-C-9BCGJn.js → DropzoneLinkField-DXIr0eFz.js} +2 -2
- package/dist/chunks/{FieldWrapper-CORPzo9s.js → FieldWrapper-DnuMTrqW.js} +1 -1
- package/dist/chunks/IconCloseButton-COZEsa9u.js +29 -0
- package/dist/chunks/{LayoutCanvas-CNOAUk7D.js → LayoutCanvas-CZKOltfA.js} +1 -1
- package/dist/chunks/{LayoutNode-DF5BMN0J.js → LayoutNode-D2f7v683.js} +45 -45
- package/dist/chunks/{LpActionBar-BP1ld7-S.js → LpActionBar-BGMk5KoG.js} +3 -3
- package/dist/chunks/{LpActivityLine-CJvzrNpj.js → LpActivityLine-Ea-t5Vza.js} +4 -2
- package/dist/chunks/{LpAddressInput-E8KtwU7-.js → LpAddressInput-ZqoZ-N2Y.js} +12 -11
- package/dist/chunks/{LpAlert-n5XPAZfv.js → LpAlert-CjvTZIKF.js} +3 -3
- package/dist/chunks/LpAppShell-BNAVTQsY.js +582 -0
- package/dist/chunks/{LpAutocomplete-C-SpPfBG.js → LpAutocomplete-DkY6CSyD.js} +24 -42
- package/dist/chunks/{LpAvatar-Cg6Az3wz.js → LpAvatar-cQVsNk6F.js} +2 -2
- package/dist/chunks/{LpBadge-wUYLN4U9.js → LpBadge-D5QQdDQ_.js} +2 -2
- package/dist/chunks/{LpBottomNav-CbNQQPuL.js → LpBottomNav-zxLGGbjm.js} +9 -2
- package/dist/chunks/{LpBreadcrumbs-DPdbth-c.js → LpBreadcrumbs-CIIBsygI.js} +4 -3
- package/dist/chunks/{LpButton-CFb44IjC.js → LpButton-DSHioIfD.js} +5 -4
- package/dist/chunks/{LpCalendar-CuoWks-0.js → LpCalendar-D4-LVjfF.js} +1 -1
- package/dist/chunks/{LpCard-r13WRqox.js → LpCard-BPtYXwnT.js} +1 -1
- package/dist/chunks/{LpCheckbox-Cs4SHaHX.js → LpCheckbox-D6RuDW4X.js} +38 -16
- package/dist/chunks/{LpCodeBlock-DRC8VbXW.js → LpCodeBlock-DuzmnIHl.js} +19 -41
- package/dist/chunks/{LpCodeDisplay-CE9TxCA4.js → LpCodeDisplay-CO8fngxa.js} +5 -5
- package/dist/chunks/{LpCollapse-CWS9M_lm.js → LpCollapse-DrAw5DBZ.js} +5 -4
- package/dist/chunks/{LpColorPicker-1y1QJHZR.js → LpColorPicker-BxAHEnR5.js} +11 -12
- package/dist/chunks/{LpCommandScope-CtUhJe8T.js → LpCommandScope-DRUvHiX7.js} +1 -1
- package/dist/chunks/{LpConfirmDialog-DlD45Bi_.js → LpConfirmDialog-6oCIJTDa.js} +3 -3
- package/dist/chunks/{LpConfirmHost-BIWjBLmI.js → LpConfirmHost-DgHvfT6s.js} +1 -1
- package/dist/chunks/{LpContextMenu-rx2tCjwk.js → LpContextMenu--eMGIql6.js} +13 -15
- package/dist/chunks/{LpContextSwitcher-Dwh6dyID.js → LpContextSwitcher-1C1tExdl.js} +2 -2
- package/dist/chunks/{LpCopyButton-xF7wFVoU.js → LpCopyButton-7EOSIuwa.js} +3 -3
- package/dist/chunks/{LpCountdown-DgeY3cLG.js → LpCountdown-DVl7vXI1.js} +4 -3
- package/dist/chunks/{LpDataList-Jxyl9H5w.js → LpDataList-DgBeM01y.js} +13 -4
- package/dist/chunks/{LpDatePicker-CA0QTfBC.js → LpDatePicker-DwSDY0mt.js} +12 -18
- package/dist/chunks/{LpDateRangePicker-u3J51xXk.js → LpDateRangePicker-CNQXdTVv.js} +10 -16
- package/dist/chunks/{LpDisclosure-Cpa7Hc5T.js → LpDisclosure-DG7JCo-d.js} +2 -2
- package/dist/chunks/{LpDrawer-CrG8t_KP.js → LpDrawer-Bt9UUqt_.js} +61 -61
- package/dist/chunks/{LpDropdownMenu-Dl7Z4ZhS.js → LpDropdownMenu-CLzxIOgM.js} +9 -17
- package/dist/chunks/{LpEditable-Cy6GLbFx.js → LpEditable-DVT0Rh8F.js} +9 -6
- package/dist/chunks/{LpEmptyState-EQHiRYhG.js → LpEmptyState-DurPF001.js} +1 -1
- package/dist/chunks/{LpFieldNotice-CzQ2_5rQ.js → LpFieldNotice-DJ10cYIo.js} +1 -1
- package/dist/chunks/LpFileTree-BSx1kBoV.js +735 -0
- package/dist/chunks/{LpFileTreeNode-cT6BIWvz.js → LpFileTreeNode-4dVmFHb3.js} +116 -34
- package/dist/chunks/{LpFormField-Ch69Jd-X.js → LpFormField-BKYn9dyP.js} +3 -3
- package/dist/chunks/{LpFrame-BVQCQLDI.js → LpFrame-Bv5Fphte.js} +25 -11
- package/dist/chunks/{LpHintExample-9Py0wc-L.js → LpHintExample-YzCXiSUp.js} +1 -1
- package/dist/chunks/{LpHintLayer-CgObdUsM.js → LpHintLayer-BeKRsRon.js} +2 -2
- package/dist/chunks/{LpHints-Bz-BlaSl.js → LpHints-qf3xYsza.js} +3 -3
- package/dist/chunks/{LpHoverCard-spGxyw7T.js → LpHoverCard-DanH3Nz1.js} +23 -5
- package/dist/chunks/{LpIcon--5WSb3jJ.js → LpIcon-C6JpQ5td.js} +6 -3
- package/dist/chunks/{LpIdenticon-BzdC_qz9.js → LpIdenticon-Dbbr4fvt.js} +2 -1
- package/dist/chunks/{LpInfraNode-46XOCkfq.js → LpInfraNode-BAU40Km2.js} +2 -2
- package/dist/chunks/{LpInput-Tw-yxT4I.js → LpInput-DL6HEJdi.js} +16 -28
- package/dist/chunks/LpInspector-CuoMAhiw.js +209 -0
- package/dist/chunks/{LpLightbox-B95yu22B.js → LpLightbox-xvpQ64MM.js} +57 -32
- package/dist/chunks/{LpLoader-DVwkOmt9.js → LpLoader-Bf1yZwaU.js} +1 -1
- package/dist/chunks/LpLogInspector-6cNnXqp4.js +119 -0
- package/dist/chunks/{LpLogViewer-ngqkns4m.js → LpLogViewer-Dvb1E9_G.js} +234 -53
- package/dist/chunks/{LpMapPicker-DRGgh89B.js → LpMapPicker-D5_YLLBC.js} +4 -3
- package/dist/chunks/{LpMasonry--k0571qG.js → LpMasonry-DPYekFxs.js} +8 -5
- package/dist/chunks/{LpMediaGallery-Bkram-MO.js → LpMediaGallery-Df-tiWeq.js} +3 -3
- package/dist/chunks/{LpModal-CoGhLTyH.js → LpModal-DrtQ_1A2.js} +11 -13
- package/dist/chunks/{LpModalNav-BGvs2y5Q.js → LpModalNav-DkmGCnZE.js} +11 -37
- package/dist/chunks/{LpNotificationBell-B0034Lk-.js → LpNotificationBell-D_wklO1m.js} +9 -9
- package/dist/chunks/{LpNumberField-e0IBkWU1.js → LpNumberField-CNHsmSDZ.js} +7 -16
- package/dist/chunks/{LpOtpInput-CeBPsM7D.js → LpOtpInput-BRpLBbOD.js} +2 -2
- package/dist/chunks/{LpPagination-BIIiYHXR.js → LpPagination-snQ020xQ.js} +6 -4
- package/dist/chunks/{LpPaletteTheme-DiLYKQiZ.js → LpPaletteTheme-dVtxHx-F.js} +4 -4
- package/dist/chunks/{LpPasswordInput-BkHKQDhM.js → LpPasswordInput-B-zD6tUS.js} +2 -2
- package/dist/chunks/{LpPhoneInput-CETEp5W7.js → LpPhoneInput-BSL7NX4z.js} +15 -26
- package/dist/chunks/{LpPie-DrZepi0p.js → LpPie-9t_guO49.js} +22 -21
- package/dist/chunks/{LpPipeline-B3aOVOkV.js → LpPipeline-rjXvEDRJ.js} +16 -7
- package/dist/chunks/{LpPopover-DmZ3wUtP.js → LpPopover-BQSuxtai.js} +10 -1
- package/dist/chunks/{LpProgress-Yr6ONG5t.js → LpProgress-D_MSjowb.js} +2 -1
- package/dist/chunks/{LpQuota-Dk2MyI2Z.js → LpQuota-CgWPgdVt.js} +1 -1
- package/dist/chunks/{LpRadioGroup-ggaPL92T.js → LpRadioGroup-BrazjLY_.js} +4 -4
- package/dist/chunks/{LpRangeCalendar-CYYUYqJ7.js → LpRangeCalendar-BrqlGZzE.js} +1 -1
- package/dist/chunks/{LpRangeSlider-CM-0ZITZ.js → LpRangeSlider-fz61tzc-.js} +5 -9
- package/dist/chunks/{LpRelativeTime-DwgCb5-8.js → LpRelativeTime-BovUk8MP.js} +1 -1
- package/dist/chunks/{LpRepeater-D7ZmnIYd.js → LpRepeater-BkJHSPI1.js} +3 -3
- package/dist/chunks/{LpRevealOnScroll-DcQjexLy.js → LpRevealOnScroll-CPwyiQAr.js} +1 -1
- package/dist/chunks/{LpScramble-Bn9OoOtC.js → LpScramble-DsE77iVp.js} +1 -1
- package/dist/chunks/{LpScrollArea-MyEnVB_5.js → LpScrollArea-ay4Xo_hg.js} +22 -10
- package/dist/chunks/{LpSecretField-BbmqM9ye.js → LpSecretField-DBvU6iAw.js} +10 -9
- package/dist/chunks/{LpSection-DFK_uoJE.js → LpSection-BfteipxU.js} +1 -1
- package/dist/chunks/{LpSegmented-CeACsMMA.js → LpSegmented-DbEJhbv-.js} +8 -10
- package/dist/chunks/{LpSelect-Cxq4yBGd.js → LpSelect-Y-iuSymW.js} +47 -30
- package/dist/chunks/{LpShaderBackdrop-Bote0iuy.js → LpShaderBackdrop-wQygYb5h.js} +13 -10
- package/dist/chunks/{LpShift-CCq7dofl.js → LpShift-DAgS_d2x.js} +18 -15
- package/dist/chunks/{LpSidebar-DzE1783C.js → LpSidebar-kSkpPh3-.js} +74 -87
- package/dist/chunks/{LpSidebarNav-DE3PhXVP.js → LpSidebarNav-C2mvqrY2.js} +8 -6
- package/dist/chunks/{LpSidebarRow-BRYhNDSo.js → LpSidebarRow-eRP4Q31q.js} +6 -5
- package/dist/chunks/{LpSlider-DxYoCJJO.js → LpSlider-A4ixovgS.js} +5 -9
- package/dist/chunks/{LpSpotlight-4F3NeUMY.js → LpSpotlight-DwxkhFgR.js} +2 -1
- package/dist/chunks/{LpStat-f2LfDCJ4.js → LpStat-pMNZgFcq.js} +2 -2
- package/dist/chunks/{LpStatus-DglxttQ7.js → LpStatus-DGJFwk5F.js} +2 -2
- package/dist/chunks/{LpStepMark-DpzKVyh8.js → LpStepMark-Dw2MRTrt.js} +2 -2
- package/dist/chunks/{LpStepper-DpsocBNJ.js → LpStepper-DNnh7lCu.js} +1 -1
- package/dist/chunks/{LpSwap-C96c0LWh.js → LpSwap-M7nFeW3C.js} +1 -1
- package/dist/chunks/{LpSwitch-BnXd1llc.js → LpSwitch-DnC2-0aV.js} +3 -3
- package/dist/chunks/{LpTabFace-DPCwKkH2.js → LpTabFace-DQoV8mQG.js} +2 -2
- package/dist/chunks/{LpTableFilter-CiVBpaq0.js → LpTableFilter-DgARTsA1.js} +4 -4
- package/dist/chunks/{LpTableOfContents-BN5zvpnx.js → LpTableOfContents-CKkWEK6R.js} +14 -9
- package/dist/chunks/{LpTagsInput-C-mDG648.js → LpTagsInput-Dt0mteSj.js} +19 -27
- package/dist/chunks/{LpTextarea-TE-yZv2U.js → LpTextarea-Co0b9PVx.js} +8 -16
- package/dist/chunks/LpThemeProvider-XK6ZeWAE.js +81 -0
- package/dist/chunks/{LpThemeStudio-BpORDvH6.js → LpThemeStudio-DzYfE-5k.js} +12 -195
- package/dist/chunks/{LpThemeSwitcher-BnEn_mvf.js → LpThemeSwitcher-CFLkdbBL.js} +8 -6
- package/dist/chunks/{LpTimeField-BawSVsmt.js → LpTimeField-DeOcKWJq.js} +6 -9
- package/dist/chunks/{LpToaster-IwTXjbbU.js → LpToaster-QFj0P-5m.js} +10 -8
- package/dist/chunks/{LpTooltip-BcBvIx4z.js → LpTooltip-BJf3lZMq.js} +51 -19
- package/dist/chunks/LpTopologyCanvas-BOHHHZ_6.js +673 -0
- package/dist/chunks/{LpTour-BZq-mUMv.js → LpTour-ClKYhx4h.js} +7 -5
- package/dist/chunks/{LpTruncate-CflAIwiJ.js → LpTruncate-C6u3gjc6.js} +6 -5
- package/dist/chunks/{LpTypewriter-BCL_a6wE.js → LpTypewriter-CZIzpdbe.js} +2 -1
- package/dist/chunks/{LpUptimeBar-DqNvvMBK.js → LpUptimeBar-BnYmR2rj.js} +3 -3
- package/dist/chunks/{LpVideoBackdrop-DS1wGv-X.js → LpVideoBackdrop-Ct4St4ZL.js} +10 -13
- package/dist/chunks/LpWindow-B8YSRVTH.js +297 -0
- package/dist/chunks/LpWorkspaceSwitcher-D0e9upAy.js +139 -0
- package/dist/chunks/{MenuTitle-BmCNNTKH.js → MenuTitle-Bh5dAdV1.js} +1 -1
- package/dist/chunks/{OptionContent-22UT8zLn.js → OptionContent-CGb8QlHA.js} +1 -1
- package/dist/chunks/{PickerField-Bfh8id3d.js → PickerField-BM1dyafh.js} +4 -4
- package/dist/chunks/{RevealToggle-BEit1n6Z.js → RevealToggle-CCPDE8Mv.js} +3 -3
- package/dist/chunks/SliderRail-DhPREa_R.js +18 -0
- package/dist/chunks/{TourCard-DNclH3u_.js → TourCard-DAzTYfav.js} +4 -4
- package/dist/chunks/{icons-DMITlfw5.js → icons-CYfcpSDS.js} +26 -10
- package/dist/component-names.d.ts +1 -1
- package/dist/components/AppShellContextNav.vue.d.ts +13 -0
- package/dist/components/AppShellContextNav.vue.js +4 -0
- package/dist/components/ChartRangeSelector.vue.d.ts +21 -0
- package/dist/components/ChartRangeSelector.vue.js +4 -0
- package/dist/components/ContextMenuItems.vue.js +1 -1
- package/dist/components/DropzoneLinkField.vue.d.ts +2 -2
- package/dist/components/DropzoneLinkField.vue.js +1 -1
- package/dist/components/FieldWrapper.vue.js +1 -1
- package/dist/components/IconCloseButton.vue.d.ts +7 -0
- package/dist/components/IconCloseButton.vue.js +4 -0
- package/dist/components/LayoutCanvas.vue.js +1 -1
- package/dist/components/LayoutNode.vue.js +1 -1
- package/dist/components/LpActionBar.vue.js +1 -1
- package/dist/components/LpActivityLine.vue.js +1 -1
- package/dist/components/LpAddressInput.vue.js +1 -1
- package/dist/components/LpAlert.vue.js +1 -1
- package/dist/components/LpAppShell.vue.d.ts +110 -2
- package/dist/components/LpAppShell.vue.js +1 -1
- package/dist/components/LpAutocomplete.vue.js +1 -1
- package/dist/components/LpAvatar.vue.js +1 -1
- package/dist/components/LpBadge.vue.js +1 -1
- package/dist/components/LpBottomNav.vue.d.ts +5 -0
- package/dist/components/LpBottomNav.vue.js +1 -1
- package/dist/components/LpBreadcrumbs.vue.js +1 -1
- package/dist/components/LpButton.vue.d.ts +14 -2
- package/dist/components/LpButton.vue.js +1 -1
- package/dist/components/LpCalendar.vue.js +1 -1
- package/dist/components/LpCard.vue.js +1 -1
- package/dist/components/LpChart.vue.d.ts +74 -2
- package/dist/components/LpChart.vue.js +326 -85
- package/dist/components/LpCheckbox.vue.d.ts +6 -2
- package/dist/components/LpCheckbox.vue.js +1 -1
- package/dist/components/LpCodeBlock.vue.js +1 -1
- package/dist/components/LpCodeDisplay.vue.js +1 -1
- package/dist/components/LpCollapse.vue.js +1 -1
- package/dist/components/LpColorPicker.vue.js +1 -1
- package/dist/components/LpCommandPalette.vue.d.ts +8 -0
- package/dist/components/LpCommandPalette.vue.js +107 -56
- package/dist/components/LpCommandScope.vue.js +1 -1
- package/dist/components/LpConfirmDialog.vue.js +1 -1
- package/dist/components/LpConfirmHost.vue.js +1 -1
- package/dist/components/LpContextMenu.vue.js +1 -1
- package/dist/components/LpContextSwitcher.vue.js +1 -1
- package/dist/components/LpCopyButton.vue.js +1 -1
- package/dist/components/LpCountdown.vue.js +1 -1
- package/dist/components/LpDataList.vue.d.ts +1 -1
- package/dist/components/LpDataList.vue.js +1 -1
- package/dist/components/LpDatePicker.vue.d.ts +3 -15
- package/dist/components/LpDatePicker.vue.js +1 -1
- package/dist/components/LpDateRangePicker.vue.d.ts +3 -13
- package/dist/components/LpDateRangePicker.vue.js +1 -1
- package/dist/components/LpDisclosure.vue.js +1 -1
- package/dist/components/LpDock.vue.js +21 -18
- package/dist/components/LpDrawer.vue.d.ts +1 -1
- package/dist/components/LpDrawer.vue.js +1 -1
- package/dist/components/LpDropdownMenu.vue.js +1 -1
- package/dist/components/LpDropzone.vue.js +14 -11
- package/dist/components/LpEditable.vue.js +1 -1
- package/dist/components/LpEmptyState.vue.js +1 -1
- package/dist/components/LpFieldNotice.vue.js +1 -1
- package/dist/components/LpFileTree.vue.d.ts +96 -6
- package/dist/components/LpFileTree.vue.js +1 -1
- package/dist/components/LpFileTreeNode.vue.d.ts +48 -0
- package/dist/components/LpFileTreeNode.vue.js +1 -1
- package/dist/components/LpFormField.vue.js +1 -1
- package/dist/components/LpFrame.vue.js +1 -1
- package/dist/components/LpGauge.vue.d.ts +1 -1
- package/dist/components/LpHintExample.vue.js +1 -1
- package/dist/components/LpHintLayer.vue.js +1 -1
- package/dist/components/LpHints.vue.js +1 -1
- package/dist/components/LpHoverCard.vue.js +1 -1
- package/dist/components/LpIcon.vue.d.ts +2 -0
- package/dist/components/LpIcon.vue.js +1 -1
- package/dist/components/LpIdenticon.vue.js +1 -1
- package/dist/components/LpImageEditor.vue.js +7 -6
- package/dist/components/LpInfraNode.vue.js +1 -1
- package/dist/components/LpInput.vue.d.ts +1 -1
- package/dist/components/LpInput.vue.js +1 -1
- package/dist/components/LpInspector.vue.d.ts +87 -0
- package/dist/components/LpInspector.vue.js +4 -0
- package/dist/components/LpLightbox.vue.d.ts +8 -0
- package/dist/components/LpLightbox.vue.js +1 -1
- package/dist/components/LpLoader.vue.d.ts +1 -1
- package/dist/components/LpLoader.vue.js +1 -1
- package/dist/components/LpLogInspector.vue.d.ts +18 -0
- package/dist/components/LpLogInspector.vue.js +4 -0
- package/dist/components/LpLogViewer.vue.d.ts +93 -0
- package/dist/components/LpLogViewer.vue.js +1 -1
- package/dist/components/LpMapPicker.vue.js +1 -1
- package/dist/components/LpMasonry.vue.js +1 -1
- package/dist/components/LpMediaGallery.vue.js +1 -1
- package/dist/components/LpModal.vue.d.ts +4 -2
- package/dist/components/LpModal.vue.js +1 -1
- package/dist/components/LpModalNav.vue.d.ts +5 -5
- package/dist/components/LpModalNav.vue.js +1 -1
- package/dist/components/LpNotificationBell.vue.d.ts +2 -2
- package/dist/components/LpNotificationBell.vue.js +1 -1
- package/dist/components/LpNumberField.vue.js +1 -1
- package/dist/components/LpOtpInput.vue.js +1 -1
- package/dist/components/LpPagination.vue.js +1 -1
- package/dist/components/LpPaletteFacts.vue.js +1 -1
- package/dist/components/LpPaletteHeader.vue.js +2 -2
- package/dist/components/LpPaletteTheme.vue.js +1 -1
- package/dist/components/LpPasswordInput.vue.js +1 -1
- package/dist/components/LpPhoneInput.vue.js +1 -1
- package/dist/components/LpPie.vue.d.ts +2 -2
- package/dist/components/LpPie.vue.js +1 -1
- package/dist/components/LpPipeline.vue.d.ts +4 -0
- package/dist/components/LpPipeline.vue.js +1 -1
- package/dist/components/LpPopover.vue.d.ts +7 -3
- package/dist/components/LpPopover.vue.js +1 -1
- package/dist/components/LpProgress.vue.d.ts +1 -1
- package/dist/components/LpProgress.vue.js +1 -1
- package/dist/components/LpQuota.vue.js +1 -1
- package/dist/components/LpRadioGroup.vue.js +1 -1
- package/dist/components/LpRangeCalendar.vue.js +1 -1
- package/dist/components/LpRangeSlider.vue.js +1 -1
- package/dist/components/LpRelativeTime.vue.js +1 -1
- package/dist/components/LpRepeater.vue.js +1 -1
- package/dist/components/LpRevealOnScroll.vue.js +1 -1
- package/dist/components/LpScramble.vue.js +1 -1
- package/dist/components/LpScrollArea.vue.js +1 -1
- package/dist/components/LpSecretField.vue.js +1 -1
- package/dist/components/LpSection.vue.js +1 -1
- package/dist/components/LpSegmented.vue.js +1 -1
- package/dist/components/LpSelect.vue.js +1 -1
- package/dist/components/LpShaderBackdrop.vue.js +1 -1
- package/dist/components/LpShift.vue.js +1 -1
- package/dist/components/LpShortcutsDialog.vue.js +1 -1
- package/dist/components/LpSidebar.vue.d.ts +1 -1
- package/dist/components/LpSidebar.vue.js +1 -1
- package/dist/components/LpSidebarNav.vue.js +1 -1
- package/dist/components/LpSidebarRow.vue.js +1 -1
- package/dist/components/LpSlider.vue.js +1 -1
- package/dist/components/LpSparkline.vue.d.ts +1 -1
- package/dist/components/LpSpotlight.vue.js +1 -1
- package/dist/components/LpStat.vue.js +1 -1
- package/dist/components/LpStatus.vue.js +1 -1
- package/dist/components/LpStepMark.vue.js +1 -1
- package/dist/components/LpStepper.vue.js +1 -1
- package/dist/components/LpSwap.vue.js +1 -1
- package/dist/components/LpSwitch.vue.js +1 -1
- package/dist/components/LpTabFace.vue.js +1 -1
- package/dist/components/LpTable.vue.d.ts +132 -0
- package/dist/components/LpTable.vue.js +547 -135
- package/dist/components/LpTableFilter.vue.js +1 -1
- package/dist/components/LpTableOfContents.vue.js +1 -1
- package/dist/components/LpTabs.vue.js +6 -5
- package/dist/components/LpTagsInput.vue.js +1 -1
- package/dist/components/LpTextarea.vue.js +1 -1
- package/dist/components/LpThemeProvider.vue.d.ts +2 -0
- package/dist/components/LpThemeProvider.vue.js +1 -1
- package/dist/components/LpThemeStudio.vue.js +1 -1
- package/dist/components/LpThemeSwitcher.vue.js +1 -1
- package/dist/components/LpTimeField.vue.js +1 -1
- package/dist/components/LpToaster.vue.js +1 -1
- package/dist/components/LpTooltip.vue.js +1 -1
- package/dist/components/LpTopologyCanvas.vue.d.ts +131 -5
- package/dist/components/LpTopologyCanvas.vue.js +1 -1
- package/dist/components/LpTour.vue.js +1 -1
- package/dist/components/LpTruncate.vue.js +1 -1
- package/dist/components/LpTypewriter.vue.js +1 -1
- package/dist/components/LpUptimeBar.vue.js +1 -1
- package/dist/components/LpVideoBackdrop.vue.js +1 -1
- package/dist/components/LpWindow.vue.d.ts +99 -0
- package/dist/components/LpWindow.vue.js +4 -0
- package/dist/components/LpWorkspaceSwitcher.vue.d.ts +18 -0
- package/dist/components/LpWorkspaceSwitcher.vue.js +4 -0
- package/dist/components/MenuTitle.vue.js +1 -1
- package/dist/components/OptionContent.vue.js +1 -1
- package/dist/components/PickerField.vue.d.ts +2 -2
- package/dist/components/PickerField.vue.js +1 -1
- package/dist/components/RevealToggle.vue.js +1 -1
- package/dist/components/SliderRail.vue.d.ts +3 -0
- package/dist/components/SliderRail.vue.js +4 -0
- package/dist/components/TourCard.vue.js +1 -1
- package/dist/components/address.js +2 -1
- package/dist/components/appShell.d.ts +36 -0
- package/dist/components/appShell.js +30 -0
- package/dist/components/calendar.d.ts +12 -3
- package/dist/components/calendar.js +2 -5
- package/dist/components/chart.d.ts +21 -0
- package/dist/components/chart.js +4 -3
- package/dist/components/chartWindow.d.ts +57 -0
- package/dist/components/chartWindow.js +136 -0
- package/dist/components/color.js +1 -1
- package/dist/components/commandPalette.d.ts +16 -2
- package/dist/components/commandPalette.js +39 -4
- package/dist/components/controlSize.d.ts +5 -0
- package/dist/components/controlSize.js +8 -0
- package/dist/components/dropdown.d.ts +5 -0
- package/dist/components/dropdown.js +2 -0
- package/dist/components/exportData.d.ts +6 -1
- package/dist/components/exportData.js +25 -18
- package/dist/components/fileTree.d.ts +67 -0
- package/dist/components/fileTree.js +141 -1
- package/dist/components/identicon.js +3 -2
- package/dist/components/logTools.d.ts +93 -0
- package/dist/components/logTools.js +210 -0
- package/dist/components/menuWhen.js +1 -1
- package/dist/components/meter.js +2 -1
- package/dist/components/pipeline.d.ts +2 -0
- package/dist/components/pipeline.js +5 -0
- package/dist/components/relativeTime.js +2 -1
- package/dist/components/reveal.js +2 -1
- package/dist/components/select.d.ts +5 -0
- package/dist/components/select.js +10 -0
- package/dist/components/sidebar.d.ts +1 -0
- package/dist/components/tableFeatures.d.ts +58 -0
- package/dist/components/tableFeatures.js +87 -0
- package/dist/components/topologyGraph.d.ts +164 -0
- package/dist/components/topologyGraph.js +416 -0
- package/dist/components/tour.js +5 -3
- package/dist/components/videoBackdrop.js +7 -6
- package/dist/composables/clamp.d.ts +2 -0
- package/dist/composables/clamp.js +4 -0
- package/dist/composables/devWarn.d.ts +3 -0
- package/dist/composables/devWarn.js +14 -0
- package/dist/composables/easing.d.ts +13 -4
- package/dist/composables/easing.js +16 -10
- package/dist/composables/overlayHistory.js +14 -1
- package/dist/composables/pointerDrag.d.ts +15 -0
- package/dist/composables/pointerDrag.js +40 -0
- package/dist/composables/propCheck.d.ts +10 -0
- package/dist/composables/propCheck.js +78 -0
- package/dist/composables/realm.d.ts +6 -0
- package/dist/composables/realm.js +23 -0
- package/dist/composables/scroll.js +4 -2
- package/dist/composables/scrollMemory.d.ts +12 -1
- package/dist/composables/scrollMemory.js +36 -12
- package/dist/composables/spatialNav.js +2 -1
- package/dist/composables/textEntry.d.ts +17 -0
- package/dist/composables/textEntry.js +19 -0
- package/dist/composables/useAsyncTask.d.ts +6 -0
- package/dist/composables/useChartSync.d.ts +5 -0
- package/dist/composables/useChartSync.js +33 -0
- package/dist/composables/useCloseGuard.d.ts +3 -1
- package/dist/composables/useCloseGuard.js +6 -3
- package/dist/composables/useCommands.d.ts +50 -0
- package/dist/composables/useCommands.js +66 -0
- package/dist/composables/useField.d.ts +37 -0
- package/dist/composables/useField.js +27 -2
- package/dist/composables/useFitScale.js +4 -1
- package/dist/composables/useFormDraft.d.ts +13 -2
- package/dist/composables/useFormDraft.js +17 -9
- package/dist/composables/useHintSystem.js +2 -2
- package/dist/composables/useHotkeys.js +11 -32
- package/dist/composables/useInView.js +2 -1
- package/dist/composables/useInputFilter.js +7 -5
- package/dist/composables/useInputMask.js +5 -3
- package/dist/composables/useLinkedView.d.ts +97 -0
- package/dist/composables/useLinkedView.js +139 -0
- package/dist/composables/useOptimisticToggle.d.ts +3 -1
- package/dist/composables/useOptimisticToggle.js +2 -1
- package/dist/composables/useOverlayHistory.d.ts +7 -0
- package/dist/composables/useOverlayHistory.js +16 -2
- package/dist/composables/usePaintSelect.js +18 -14
- package/dist/composables/usePaletteCommands.d.ts +7 -0
- package/dist/composables/usePaletteCommands.js +12 -1
- package/dist/composables/usePersisted.d.ts +10 -0
- package/dist/composables/usePersisted.js +1 -1
- package/dist/composables/usePillTransition.d.ts +2 -2
- package/dist/composables/usePillTransition.js +5 -5
- package/dist/composables/useResizeObserver.js +2 -1
- package/dist/composables/useReveal.js +2 -1
- package/dist/composables/useRovingFocus.d.ts +2 -2
- package/dist/composables/useRovingFocus.js +9 -11
- package/dist/composables/useScramble.js +7 -1
- package/dist/composables/useShift.js +8 -6
- package/dist/composables/useTilt.js +2 -1
- package/dist/composables/useTween.js +9 -5
- package/dist/composables/useView.d.ts +1 -0
- package/dist/composables/useView.js +14 -2
- package/dist/composables/useZoomPan.js +2 -4
- package/dist/composables/windowing.d.ts +40 -0
- package/dist/composables/windowing.js +118 -0
- package/dist/icons-all.js +1 -1
- package/dist/icons.d.ts +4 -0
- package/dist/index.d.ts +30 -5
- package/dist/index.js +310 -264
- package/dist/layout/tree.js +19 -15
- package/dist/layout/useLayout.d.ts +2 -0
- package/dist/layout/useLayout.js +5 -1
- package/dist/layout/useWorkspaces.d.ts +52 -0
- package/dist/layout/useWorkspaces.js +140 -0
- package/dist/{chunks/backdrop-Dkf2vM81.js → theme/backdrop.js} +5 -5
- package/dist/theme/config.js +25 -0
- package/dist/theme/define.js +61 -0
- package/dist/{chunks/palette-Co8tSASo.js → theme/palette.js} +2 -2
- package/dist/theme/presets.js +239 -0
- package/dist/theme/studio.js +134 -0
- package/dist/theme/tokens.js +63 -0
- package/dist/theme/useTheme.d.ts +3 -2
- package/dist/theme/useTheme.js +325 -0
- package/package.json +8 -1
- package/src/component-names.ts +5 -1
- package/src/components/AppShellContextNav.vue +44 -0
- package/src/components/ChartRangeSelector.vue +120 -0
- package/src/components/IconCloseButton.vue +21 -0
- package/src/components/LayoutCanvas.vue +3 -0
- package/src/components/LayoutNode.vue +37 -38
- package/src/components/LpActivityLine.vue +4 -2
- package/src/components/LpAddressInput.vue +3 -3
- package/src/components/LpAppShell.vue +362 -24
- package/src/components/LpAutocomplete.vue +13 -30
- package/src/components/LpBottomNav.vue +9 -1
- package/src/components/LpBreadcrumbs.vue +2 -1
- package/src/components/LpButton.vue +11 -4
- package/src/components/LpChart.vue +336 -37
- package/src/components/LpCheckbox.vue +43 -9
- package/src/components/LpCodeBlock.vue +17 -23
- package/src/components/LpCollapse.vue +5 -4
- package/src/components/LpColorPicker.vue +8 -9
- package/src/components/LpCommandPalette.vue +133 -50
- package/src/components/LpContextMenu.vue +15 -13
- package/src/components/LpCountdown.vue +2 -1
- package/src/components/LpDataList.vue +13 -1
- package/src/components/LpDatePicker.vue +5 -17
- package/src/components/LpDateRangePicker.vue +5 -16
- package/src/components/LpDock.vue +25 -16
- package/src/components/LpDrawer.vue +70 -61
- package/src/components/LpDropdownMenu.vue +6 -10
- package/src/components/LpDropzone.vue +12 -7
- package/src/components/LpEditable.vue +6 -2
- package/src/components/LpFileTree.vue +507 -66
- package/src/components/LpFileTreeNode.vue +106 -5
- package/src/components/LpFormField.vue +1 -1
- package/src/components/LpFrame.vue +33 -13
- package/src/components/LpHoverCard.vue +30 -5
- package/src/components/LpIcon.vue +6 -2
- package/src/components/LpIdenticon.vue +2 -1
- package/src/components/LpImageEditor.vue +5 -2
- package/src/components/LpInput.vue +15 -23
- package/src/components/LpInspector.vue +197 -0
- package/src/components/LpLightbox.vue +74 -29
- package/src/components/LpLogInspector.vue +85 -0
- package/src/components/LpLogViewer.vue +239 -29
- package/src/components/LpMapPicker.vue +2 -1
- package/src/components/LpMasonry.vue +8 -5
- package/src/components/LpModal.vue +12 -12
- package/src/components/LpModalNav.vue +2 -21
- package/src/components/LpNumberField.vue +6 -15
- package/src/components/LpOtpInput.vue +2 -2
- package/src/components/LpPagination.vue +5 -3
- package/src/components/LpPhoneInput.vue +8 -19
- package/src/components/LpPie.vue +25 -23
- package/src/components/LpPipeline.vue +14 -1
- package/src/components/LpPopover.vue +23 -2
- package/src/components/LpProgress.vue +2 -1
- package/src/components/LpRadioGroup.vue +2 -2
- package/src/components/LpRangeSlider.vue +5 -6
- package/src/components/LpRevealOnScroll.vue +1 -1
- package/src/components/LpScrollArea.vue +28 -11
- package/src/components/LpSecretField.vue +2 -1
- package/src/components/LpSegmented.vue +6 -8
- package/src/components/LpSelect.vue +54 -24
- package/src/components/LpShaderBackdrop.vue +12 -9
- package/src/components/LpShift.vue +18 -15
- package/src/components/LpSidebar.vue +77 -74
- package/src/components/LpSidebarNav.vue +3 -2
- package/src/components/LpSidebarRow.vue +2 -1
- package/src/components/LpSlider.vue +5 -6
- package/src/components/LpSpotlight.vue +2 -1
- package/src/components/LpSwitch.vue +5 -3
- package/src/components/LpTable.vue +609 -93
- package/src/components/LpTableFilter.vue +1 -1
- package/src/components/LpTableOfContents.vue +16 -9
- package/src/components/LpTabs.vue +2 -1
- package/src/components/LpTagsInput.vue +12 -19
- package/src/components/LpTextarea.vue +5 -11
- package/src/components/LpThemeProvider.vue +29 -13
- package/src/components/LpTimeField.vue +4 -7
- package/src/components/LpToaster.vue +7 -6
- package/src/components/LpTooltip.vue +62 -18
- package/src/components/LpTopologyCanvas.vue +567 -42
- package/src/components/LpTour.vue +7 -5
- package/src/components/LpTruncate.vue +4 -3
- package/src/components/LpTypewriter.vue +2 -1
- package/src/components/LpVideoBackdrop.vue +11 -13
- package/src/components/LpWindow.vue +373 -0
- package/src/components/LpWorkspaceSwitcher.vue +116 -0
- package/src/components/PickerField.vue +2 -2
- package/src/components/SliderRail.vue +10 -0
- package/src/components/address.ts +3 -1
- package/src/components/appShell.ts +74 -0
- package/src/components/calendar.ts +12 -5
- package/src/components/chart.ts +27 -3
- package/src/components/chartWindow.ts +198 -0
- package/src/components/color.ts +2 -1
- package/src/components/commandPalette.ts +66 -5
- package/src/components/controlSize.ts +6 -0
- package/src/components/dropdown.ts +3 -0
- package/src/components/exportData.ts +33 -18
- package/src/components/fileTree.ts +215 -0
- package/src/components/identicon.ts +4 -2
- package/src/components/logTools.ts +298 -0
- package/src/components/meter.ts +2 -1
- package/src/components/pipeline.ts +6 -0
- package/src/components/relativeTime.ts +2 -1
- package/src/components/reveal.ts +3 -1
- package/src/components/select.ts +14 -0
- package/src/components/sidebar.ts +1 -0
- package/src/components/tableFeatures.ts +149 -0
- package/src/components/topologyGraph.ts +655 -0
- package/src/components/tour.ts +5 -3
- package/src/components/videoBackdrop.ts +7 -6
- package/src/composables/clamp.ts +2 -0
- package/src/composables/devWarn.ts +19 -0
- package/src/composables/easing.ts +37 -12
- package/src/composables/overlayHistory.ts +24 -0
- package/src/composables/pointerDrag.ts +54 -0
- package/src/composables/propCheck.ts +90 -0
- package/src/composables/realm.ts +25 -0
- package/src/composables/scroll.ts +4 -2
- package/src/composables/scrollMemory.ts +56 -14
- package/src/composables/spatialNav.ts +2 -1
- package/src/composables/textEntry.ts +33 -0
- package/src/composables/useAsyncTask.ts +6 -0
- package/src/composables/useChartSync.ts +52 -0
- package/src/composables/useCloseGuard.ts +22 -10
- package/src/composables/useCommands.ts +123 -0
- package/src/composables/useField.ts +41 -2
- package/src/composables/useFitScale.ts +4 -1
- package/src/composables/useFormDraft.ts +38 -14
- package/src/composables/useHintSystem.ts +2 -2
- package/src/composables/useHotkeys.ts +19 -48
- package/src/composables/useInView.ts +2 -1
- package/src/composables/useInputFilter.ts +8 -5
- package/src/composables/useInputMask.ts +8 -3
- package/src/composables/useLinkedView.ts +222 -0
- package/src/composables/useOptimisticToggle.ts +7 -1
- package/src/composables/useOverlayHistory.ts +20 -0
- package/src/composables/usePaintSelect.ts +20 -14
- package/src/composables/usePaletteCommands.ts +13 -0
- package/src/composables/usePersisted.ts +10 -0
- package/src/composables/usePillTransition.ts +9 -6
- package/src/composables/useResizeObserver.ts +2 -1
- package/src/composables/useReveal.ts +2 -1
- package/src/composables/useRovingFocus.ts +11 -14
- package/src/composables/useScramble.ts +12 -1
- package/src/composables/useShift.ts +8 -6
- package/src/composables/useTilt.ts +2 -1
- package/src/composables/useTween.ts +10 -5
- package/src/composables/useView.ts +17 -3
- package/src/composables/useZoomPan.ts +2 -5
- package/src/composables/windowing.ts +190 -0
- package/src/icons.ts +27 -6
- package/src/index.ts +41 -4
- package/src/kit-icons.ts +1 -1
- package/src/layout/tree.ts +31 -16
- package/src/layout/useLayout.ts +6 -0
- package/src/layout/useWorkspaces.ts +234 -0
- package/src/nuxt.ts +9 -1
- package/src/runtime/propCheck.plugin.ts +10 -0
- package/src/theme/studio.ts +7 -5
- package/src/theme/useTheme.ts +89 -15
- package/src/tokens/tokens.css +8 -1
- package/dist/assets/LpTabs-CJ0rYQbx.css +0 -18
- package/dist/chunks/LpAppShell-Dgejipzz.js +0 -319
- package/dist/chunks/LpFileTree-CooHinsC.js +0 -425
- package/dist/chunks/LpThemeProvider-8MG6yqU9.js +0 -640
- 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 =
|
|
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 *
|
|
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
|
+
}
|
package/src/components/meter.ts
CHANGED
|
@@ -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
|
|
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.
|
|
118
|
+
return clamp(Math.ceil(ms), MIN_REFRESH, MAX_REFRESH)
|
|
118
119
|
}
|
package/src/components/reveal.ts
CHANGED
|
@@ -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 +
|
|
57
|
+
return delay + clamp(index, 0, cap) * Math.max(0, stagger)
|
|
56
58
|
}
|
package/src/components/select.ts
CHANGED
|
@@ -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
|