@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
package/src/layout/tree.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { clamp } from "../composables/clamp"
|
|
1
2
|
import { reactive } from "vue"
|
|
2
3
|
|
|
3
4
|
/*
|
|
@@ -221,11 +222,11 @@ export function moveLeaf(
|
|
|
221
222
|
): void {
|
|
222
223
|
if (movingId === targetId) return
|
|
223
224
|
const moving = findLeaf(root, movingId)
|
|
224
|
-
|
|
225
|
+
// Checked before the cell is detached: with nowhere to land it would be lost.
|
|
226
|
+
const target = findLeaf(root, targetId)
|
|
227
|
+
if (!moving || !target) return
|
|
225
228
|
|
|
226
229
|
if (side === "center") {
|
|
227
|
-
const target = findLeaf(root, targetId)
|
|
228
|
-
if (!target) return
|
|
229
230
|
const merged = moving.blocks
|
|
230
231
|
detach(root, movingId)
|
|
231
232
|
target.active = target.blocks.length
|
|
@@ -275,23 +276,37 @@ export function serializeLayout(root: Split): SerializedNode {
|
|
|
275
276
|
return strip(root) as SerializedSplit
|
|
276
277
|
}
|
|
277
278
|
|
|
279
|
+
const validSize = (size: unknown): number =>
|
|
280
|
+
typeof size === "number" && Number.isFinite(size) && size > 0 ? size : 1
|
|
281
|
+
|
|
282
|
+
/*
|
|
283
|
+
* The data may come from a file or an older version, so nothing about its
|
|
284
|
+
* shape is taken on trust: a bad size or direction falls back to the default
|
|
285
|
+
* and a node that cannot be drawn (no tabs, no children, an unknown kind) is
|
|
286
|
+
* left out, so one damaged cell does not cost the person the whole layout.
|
|
287
|
+
* Only a root that is no layout at all throws. Block ids are the app's, so an
|
|
288
|
+
* id that repeats across cells is kept.
|
|
289
|
+
*/
|
|
278
290
|
export function deserializeLayout(data: SerializedNode): Split {
|
|
279
|
-
const hydrate = (n: SerializedNode): LayoutNode => {
|
|
280
|
-
if (n
|
|
291
|
+
const hydrate = (n: SerializedNode | null | undefined): LayoutNode | null => {
|
|
292
|
+
if (n?.kind === "leaf") {
|
|
281
293
|
// Accept both the tabbed form and the legacy single-`block` form.
|
|
282
|
-
const
|
|
283
|
-
const
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
kind: "
|
|
288
|
-
id: nid(),
|
|
289
|
-
dir: n.dir,
|
|
290
|
-
size: n.size ?? 1,
|
|
291
|
-
children: n.children.map(hydrate),
|
|
294
|
+
const listed = Array.isArray(n.blocks) ? n.blocks : n.block != null ? [n.block] : []
|
|
295
|
+
const blocks = listed.filter((block) => typeof block === "string")
|
|
296
|
+
if (!blocks.length) return null
|
|
297
|
+
const wanted = Number.isInteger(n.active) ? n.active : 0
|
|
298
|
+
const active = clamp(wanted, 0, blocks.length - 1)
|
|
299
|
+
return { kind: "leaf", id: nid(), blocks, active, size: validSize(n.size) }
|
|
292
300
|
}
|
|
301
|
+
if (n?.kind !== "split" || !Array.isArray(n.children)) return null
|
|
302
|
+
const children = n.children.map(hydrate).filter((child) => child !== null)
|
|
303
|
+
if (!children.length) return null
|
|
304
|
+
return { kind: "split", id: nid(), dir: n.dir === "col" ? "col" : "row", size: validSize(n.size), children }
|
|
293
305
|
}
|
|
294
|
-
|
|
306
|
+
if (data?.kind !== "leaf" && data?.kind !== "split") throw new Error("Invalid layout")
|
|
307
|
+
const root = hydrate(data)
|
|
308
|
+
if (root?.kind === "split") return reactive(root)
|
|
309
|
+
return reactive<Split>({ kind: "split", id: nid(), dir: "row", size: 1, children: root ? [root] : [] })
|
|
295
310
|
}
|
|
296
311
|
|
|
297
312
|
// ── history snapshots ────────────────────────────────────────
|
package/src/layout/useLayout.ts
CHANGED
|
@@ -90,6 +90,8 @@ export interface UseLayout {
|
|
|
90
90
|
serialize: () => unknown
|
|
91
91
|
/** Replace the layout from previously serialized data. */
|
|
92
92
|
hydrate: (data: unknown) => void
|
|
93
|
+
/** Forget undo and redo, for a layout that replaces this one rather than edits it. */
|
|
94
|
+
clearHistory: () => void
|
|
93
95
|
}
|
|
94
96
|
|
|
95
97
|
export function useLayout(options: UseLayoutOptions): UseLayout {
|
|
@@ -180,5 +182,9 @@ export function useLayout(options: UseLayoutOptions): UseLayout {
|
|
|
180
182
|
record(() => {
|
|
181
183
|
layout.value = deserializeLayout(data as Parameters<typeof deserializeLayout>[0])
|
|
182
184
|
}),
|
|
185
|
+
clearHistory: () => {
|
|
186
|
+
past.value = []
|
|
187
|
+
future.value = []
|
|
188
|
+
},
|
|
183
189
|
}
|
|
184
190
|
}
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
import { computed, getCurrentScope, onScopeDispose, watch, type ComputedRef } from "vue"
|
|
2
|
+
import { useKitStorage, usePersisted } from "../composables/usePersisted"
|
|
3
|
+
import { deserializeLayout, makeLayout, serializeLayout } from "./tree"
|
|
4
|
+
import { useLayout, type UseLayout } from "./useLayout"
|
|
5
|
+
|
|
6
|
+
/*
|
|
7
|
+
* useWorkspaces — several named layouts over one useLayout.
|
|
8
|
+
*
|
|
9
|
+
* `controller` is the plain useLayout, so a <LayoutCanvas> binds to it as to
|
|
10
|
+
* any other and never learns workspaces exist. This adds the shelf around it:
|
|
11
|
+
* the saved layouts, which one is showing, and keeping both across a restart.
|
|
12
|
+
* Edits land on the active workspace by themselves, debounced, because a
|
|
13
|
+
* divider drag changes the tree on every frame.
|
|
14
|
+
*
|
|
15
|
+
* What comes back from storage or an import goes through `deserializeLayout`,
|
|
16
|
+
* so a broken record costs that workspace its layout, not the page. Undo
|
|
17
|
+
* history belongs to one workspace: a switch clears it, or Undo would walk
|
|
18
|
+
* back into a layout from another one.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
const STORE_VERSION = 1
|
|
22
|
+
const SAVE_DELAY_MS = 300
|
|
23
|
+
const DEFAULT_NAME = "Workspace"
|
|
24
|
+
|
|
25
|
+
export interface WorkspaceSeed {
|
|
26
|
+
id?: string
|
|
27
|
+
name: string
|
|
28
|
+
blocks: string[]
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface WorkspaceEntry {
|
|
32
|
+
id: string
|
|
33
|
+
name: string
|
|
34
|
+
layout: unknown
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface WorkspaceSummary {
|
|
38
|
+
id: string
|
|
39
|
+
name: string
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** One workspace as a file: what export gives and import takes. */
|
|
43
|
+
export interface WorkspaceExport {
|
|
44
|
+
name: string
|
|
45
|
+
layout: unknown
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
interface WorkspaceStore {
|
|
49
|
+
version: number
|
|
50
|
+
active: string
|
|
51
|
+
items: WorkspaceEntry[]
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface UseWorkspacesOptions {
|
|
55
|
+
/** Block ids of a new or unreadable workspace. */
|
|
56
|
+
initial: string[]
|
|
57
|
+
/** The workspaces a first visit starts with. One from `initial` when unset. */
|
|
58
|
+
defaults?: WorkspaceSeed[]
|
|
59
|
+
/** Keep them across visits under this key (see `usePersisted`). In memory without one. */
|
|
60
|
+
persist?: string
|
|
61
|
+
historyLimit?: number
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export interface UseWorkspaces {
|
|
65
|
+
/** The live layout of the active workspace, for <LayoutCanvas v-model>. */
|
|
66
|
+
controller: UseLayout
|
|
67
|
+
workspaces: ComputedRef<WorkspaceSummary[]>
|
|
68
|
+
activeId: ComputedRef<string>
|
|
69
|
+
switchTo: (id: string) => void
|
|
70
|
+
/** Add one and show it. From `blocks`, else from `initial`. Returns its id. */
|
|
71
|
+
create: (name: string, blocks?: string[]) => string
|
|
72
|
+
/** Copy a workspace (the active one by default) and show the copy. */
|
|
73
|
+
duplicate: (id?: string, name?: string) => string | null
|
|
74
|
+
rename: (id: string, name: string) => void
|
|
75
|
+
/** The last workspace stays: there is always one to show. */
|
|
76
|
+
remove: (id: string) => void
|
|
77
|
+
/** Back to `initial`, for the active workspace. */
|
|
78
|
+
resetActive: () => void
|
|
79
|
+
exportWorkspace: (id?: string) => WorkspaceExport | null
|
|
80
|
+
/** Add a workspace from an export. Null, and nothing added, when it does not read as a layout. */
|
|
81
|
+
importWorkspace: (data: unknown) => string | null
|
|
82
|
+
/** Write pending edits now instead of after the debounce. */
|
|
83
|
+
save: () => void
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function isStore(value: unknown): value is WorkspaceStore {
|
|
87
|
+
const store = value as WorkspaceStore | null
|
|
88
|
+
if (!store || store.version !== STORE_VERSION || typeof store.active !== "string") return false
|
|
89
|
+
if (!Array.isArray(store.items) || store.items.length === 0) return false
|
|
90
|
+
const ids = new Set<string>()
|
|
91
|
+
for (const item of store.items) {
|
|
92
|
+
if (!item || typeof item.id !== "string" || typeof item.name !== "string" || ids.has(item.id)) return false
|
|
93
|
+
ids.add(item.id)
|
|
94
|
+
}
|
|
95
|
+
return true
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
let idCount = 0
|
|
99
|
+
const newId = () => `ws-${Date.now().toString(36)}-${(idCount++).toString(36)}`
|
|
100
|
+
|
|
101
|
+
export function useWorkspaces(options: UseWorkspacesOptions): UseWorkspaces {
|
|
102
|
+
const seed = (blocks: string[]) => serializeLayout(makeLayout(blocks))
|
|
103
|
+
const seeds = options.defaults?.length ? options.defaults : [{ name: DEFAULT_NAME, blocks: options.initial }]
|
|
104
|
+
const items = seeds.map((s, i) => ({ id: s.id ?? `ws-default-${i}`, name: s.name, layout: seed(s.blocks) }))
|
|
105
|
+
|
|
106
|
+
const store = usePersisted<WorkspaceStore>(
|
|
107
|
+
() => options.persist && `lp-workspaces:${options.persist}`,
|
|
108
|
+
{ version: STORE_VERSION, active: items[0]!.id, items },
|
|
109
|
+
{ validate: isStore },
|
|
110
|
+
)
|
|
111
|
+
const controller = useLayout({ initial: options.initial, historyLimit: options.historyLimit })
|
|
112
|
+
|
|
113
|
+
const entry = (id: string) => store.value.items.find((item) => item.id === id)
|
|
114
|
+
const activeId = computed(() => (entry(store.value.active) ? store.value.active : store.value.items[0]!.id))
|
|
115
|
+
const workspaces = computed(() => store.value.items.map(({ id, name }) => ({ id, name })))
|
|
116
|
+
|
|
117
|
+
let timer: ReturnType<typeof setTimeout> | undefined
|
|
118
|
+
|
|
119
|
+
function save() {
|
|
120
|
+
clearTimeout(timer)
|
|
121
|
+
timer = undefined
|
|
122
|
+
const current = entry(activeId.value)
|
|
123
|
+
if (!current) return
|
|
124
|
+
const layout = controller.serialize()
|
|
125
|
+
if (JSON.stringify(layout) !== JSON.stringify(current.layout)) current.layout = layout
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function show(id: string) {
|
|
129
|
+
const target = entry(id)
|
|
130
|
+
if (!target) return
|
|
131
|
+
try {
|
|
132
|
+
controller.hydrate(target.layout)
|
|
133
|
+
} catch {
|
|
134
|
+
// Unreadable: this workspace starts over, the others are untouched.
|
|
135
|
+
controller.reset()
|
|
136
|
+
}
|
|
137
|
+
controller.clearHistory()
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function switchTo(id: string) {
|
|
141
|
+
if (!entry(id) || id === activeId.value) return
|
|
142
|
+
save()
|
|
143
|
+
store.value.active = id
|
|
144
|
+
show(id)
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function add(name: string, layout: unknown): string {
|
|
148
|
+
save()
|
|
149
|
+
const id = newId()
|
|
150
|
+
store.value.items.push({ id, name: name.trim() || DEFAULT_NAME, layout })
|
|
151
|
+
store.value.active = id
|
|
152
|
+
show(id)
|
|
153
|
+
return id
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function duplicate(id = activeId.value, name?: string): string | null {
|
|
157
|
+
save()
|
|
158
|
+
const source = entry(id)
|
|
159
|
+
if (!source) return null
|
|
160
|
+
return add(name ?? source.name, JSON.parse(JSON.stringify(source.layout)))
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function rename(id: string, name: string) {
|
|
164
|
+
const target = entry(id)
|
|
165
|
+
const trimmed = name.trim()
|
|
166
|
+
if (target && trimmed) target.name = trimmed
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function remove(id: string) {
|
|
170
|
+
if (store.value.items.length <= 1) return
|
|
171
|
+
const at = store.value.items.findIndex((item) => item.id === id)
|
|
172
|
+
if (at === -1) return
|
|
173
|
+
const wasActive = id === activeId.value
|
|
174
|
+
if (!wasActive) save()
|
|
175
|
+
store.value.items.splice(at, 1)
|
|
176
|
+
if (!wasActive) return
|
|
177
|
+
clearTimeout(timer)
|
|
178
|
+
const next = store.value.items[Math.min(at, store.value.items.length - 1)]!
|
|
179
|
+
store.value.active = next.id
|
|
180
|
+
show(next.id)
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function exportWorkspace(id = activeId.value): WorkspaceExport | null {
|
|
184
|
+
save()
|
|
185
|
+
const source = entry(id)
|
|
186
|
+
return source ? JSON.parse(JSON.stringify({ name: source.name, layout: source.layout })) : null
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function importWorkspace(data: unknown): string | null {
|
|
190
|
+
const candidate = data as WorkspaceExport | null
|
|
191
|
+
if (!candidate || typeof candidate.name !== "string") return null
|
|
192
|
+
try {
|
|
193
|
+
return add(candidate.name, serializeLayout(deserializeLayout(candidate.layout as Parameters<typeof deserializeLayout>[0])))
|
|
194
|
+
} catch {
|
|
195
|
+
return null
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
show(activeId.value)
|
|
200
|
+
|
|
201
|
+
watch(
|
|
202
|
+
() => JSON.stringify(controller.serialize()),
|
|
203
|
+
() => {
|
|
204
|
+
clearTimeout(timer)
|
|
205
|
+
timer = setTimeout(save, SAVE_DELAY_MS)
|
|
206
|
+
},
|
|
207
|
+
)
|
|
208
|
+
// Direct: by now the persisted ref's own watcher has stopped with the scope.
|
|
209
|
+
const storage = useKitStorage()
|
|
210
|
+
if (getCurrentScope()) {
|
|
211
|
+
onScopeDispose(() => {
|
|
212
|
+
save()
|
|
213
|
+
if (options.persist) storage.set(`lp-workspaces:${options.persist}`, JSON.stringify(store.value))
|
|
214
|
+
})
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
return {
|
|
218
|
+
controller,
|
|
219
|
+
workspaces,
|
|
220
|
+
activeId,
|
|
221
|
+
switchTo,
|
|
222
|
+
create: (name, blocks) => add(name, seed(blocks ?? options.initial)),
|
|
223
|
+
duplicate,
|
|
224
|
+
rename,
|
|
225
|
+
remove,
|
|
226
|
+
resetActive: () => {
|
|
227
|
+
controller.reset()
|
|
228
|
+
save()
|
|
229
|
+
},
|
|
230
|
+
exportWorkspace,
|
|
231
|
+
importWorkspace,
|
|
232
|
+
save,
|
|
233
|
+
}
|
|
234
|
+
}
|
package/src/nuxt.ts
CHANGED
|
@@ -23,6 +23,8 @@
|
|
|
23
23
|
* app's source, "all" registers the whole set, "kit" only the
|
|
24
24
|
* kit's own, or a list of names. Anything else still renders,
|
|
25
25
|
* from a lazily loaded chunk of the bundled collection.
|
|
26
|
+
* propCheck false to skip the dev warning for an unknown prop on a kit
|
|
27
|
+
* component (default: on in `nuxt dev`, never in a build)
|
|
26
28
|
*/
|
|
27
29
|
import { readFileSync } from "node:fs"
|
|
28
30
|
import { createRequire } from "node:module"
|
|
@@ -43,6 +45,8 @@ export interface LeavepulseUiOptions {
|
|
|
43
45
|
exclude?: string[]
|
|
44
46
|
/** Lucide icons registered up front (default "scan"). */
|
|
45
47
|
icons?: "scan" | "all" | "kit" | string[]
|
|
48
|
+
/** Warn in `nuxt dev` about an unknown prop on a kit component (default true). */
|
|
49
|
+
propCheck?: boolean
|
|
46
50
|
}
|
|
47
51
|
|
|
48
52
|
function iconsPlugin(mode: LeavepulseUiOptions["icons"], srcDir: string): string {
|
|
@@ -55,7 +59,7 @@ function iconsPlugin(mode: LeavepulseUiOptions["icons"], srcDir: string): string
|
|
|
55
59
|
|
|
56
60
|
export default defineNuxtModule<LeavepulseUiOptions>({
|
|
57
61
|
meta: { name: "@leavepulse/ui", configKey: "leavepulseUi" },
|
|
58
|
-
defaults: { theme: true, link: true, icons: "scan" },
|
|
62
|
+
defaults: { theme: true, link: true, icons: "scan", propCheck: true },
|
|
59
63
|
setup(options, nuxt) {
|
|
60
64
|
const resolver = createResolver(import.meta.url)
|
|
61
65
|
const skip = new Set(options.exclude ?? [])
|
|
@@ -79,6 +83,10 @@ export default defineNuxtModule<LeavepulseUiOptions>({
|
|
|
79
83
|
})
|
|
80
84
|
}
|
|
81
85
|
|
|
86
|
+
if (nuxt.options.dev && options.propCheck !== false) {
|
|
87
|
+
addPlugin({ src: resolver.resolve("./runtime/propCheck.plugin"), mode: "client" })
|
|
88
|
+
}
|
|
89
|
+
|
|
82
90
|
if (options.link !== false) {
|
|
83
91
|
addPlugin({ src: resolver.resolve("./runtime/config.plugin"), mode: "all" })
|
|
84
92
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Nuxt runtime plugin: warns in `nuxt dev` about an unknown prop on a kit
|
|
3
|
+
* component. Registered by the kit's Nuxt module, never in a build.
|
|
4
|
+
*/
|
|
5
|
+
import { lpPropCheck } from "@leavepulse/ui"
|
|
6
|
+
import { defineNuxtPlugin } from "#imports"
|
|
7
|
+
|
|
8
|
+
export default defineNuxtPlugin((nuxtApp) => {
|
|
9
|
+
nuxtApp.vueApp.use(lpPropCheck)
|
|
10
|
+
})
|
package/src/theme/studio.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { computed, ref, shallowRef, watch, watchEffect, type ComputedRef, type Ref } from "vue"
|
|
1
|
+
import { computed, getCurrentScope, onScopeDispose, ref, shallowRef, watch, watchEffect, type ComputedRef, type Ref } from "vue"
|
|
2
2
|
import { usePersisted } from "../composables/usePersisted"
|
|
3
3
|
import { defineTheme, withBrand } from "./define"
|
|
4
4
|
import { presets } from "./presets"
|
|
@@ -110,10 +110,12 @@ function create(key: string, config: Ref<Omit<ThemeStudioOptions, "storageKey">>
|
|
|
110
110
|
const dark = pair && themes.value.find((t) => t.name === pair.dark)
|
|
111
111
|
return light && dark ? { light, dark } : null
|
|
112
112
|
})
|
|
113
|
-
const
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
113
|
+
const scheme = typeof window === "undefined" ? undefined : window.matchMedia?.("(prefers-color-scheme: dark)")
|
|
114
|
+
const prefersDark = ref(!!scheme?.matches)
|
|
115
|
+
const onScheme = (e: MediaQueryListEvent) => (prefersDark.value = e.matches)
|
|
116
|
+
scheme?.addEventListener("change", onScheme)
|
|
117
|
+
// A studio opened and closed again left its listener on the page.
|
|
118
|
+
if (getCurrentScope()) onScopeDispose(() => scheme?.removeEventListener("change", onScheme))
|
|
117
119
|
const engine = useTheme()
|
|
118
120
|
const defaults = (): StudioState => ({
|
|
119
121
|
theme: engine.currentName.value || themes.value[0]?.name || "",
|
package/src/theme/useTheme.ts
CHANGED
|
@@ -36,7 +36,9 @@ type Cached = { theme: TokenSet; system?: undefined } | { system: SystemThemes;
|
|
|
36
36
|
// ── self-managed theme state (singleton) ─────────────────────────────────
|
|
37
37
|
// The kit owns the active theme, like an i18n locale, so every consumer shares
|
|
38
38
|
// one source of truth. Lazily hydrated from the cache on the client; on the
|
|
39
|
-
// server `current` stays null
|
|
39
|
+
// server `current` stays null and `init` writes nothing, because this module
|
|
40
|
+
// outlives a request and one visitor's theme must not reach the next. A server
|
|
41
|
+
// renders from `themeFromCookie` + `themeToCssRule`; the setters are client-only.
|
|
40
42
|
const _current = shallowRef<TokenSet | null>(null)
|
|
41
43
|
const _system = shallowRef<SystemThemes | null>(null)
|
|
42
44
|
let _hydrated = false
|
|
@@ -182,22 +184,31 @@ export function revealTransition(
|
|
|
182
184
|
update: () => void | Promise<void>,
|
|
183
185
|
origin?: RevealOrigin,
|
|
184
186
|
durationMs = 480,
|
|
187
|
+
page: Document = document,
|
|
185
188
|
): void {
|
|
186
|
-
const doc =
|
|
187
|
-
|
|
189
|
+
const doc = page as ViewTransitionDocument
|
|
190
|
+
const win = page.defaultView ?? window
|
|
191
|
+
if (typeof doc.startViewTransition !== "function" || prefersReducedMotion(win)) {
|
|
188
192
|
void update()
|
|
189
193
|
return
|
|
190
194
|
}
|
|
191
195
|
|
|
192
|
-
const x = origin?.x ??
|
|
193
|
-
const y = origin?.y ??
|
|
196
|
+
const x = origin?.x ?? win.innerWidth / 2
|
|
197
|
+
const y = origin?.y ?? win.innerHeight / 2
|
|
194
198
|
// Radius that reaches the farthest viewport corner from the origin.
|
|
195
199
|
const endRadius = Math.hypot(
|
|
196
|
-
Math.max(x,
|
|
197
|
-
Math.max(y,
|
|
200
|
+
Math.max(x, win.innerWidth - x),
|
|
201
|
+
Math.max(y, win.innerHeight - y),
|
|
198
202
|
)
|
|
199
203
|
|
|
200
204
|
const transition = doc.startViewTransition(update)
|
|
205
|
+
/*
|
|
206
|
+
* `ready` rejects when there will be nothing to animate: a newer transition
|
|
207
|
+
* replaced this one (the browser skips it by itself, and its `update` still
|
|
208
|
+
* runs, so quick switches land in order) or the page went hidden. An `update`
|
|
209
|
+
* that throws still reports, through `finished`.
|
|
210
|
+
*/
|
|
211
|
+
const skipped = () => {}
|
|
201
212
|
transition.ready.then(() => {
|
|
202
213
|
// Animate the OLD snapshot shrinking to a point, revealing the (already
|
|
203
214
|
// repainted) live page underneath through the growing hole. Clipping the
|
|
@@ -205,13 +216,13 @@ export function revealTransition(
|
|
|
205
216
|
// theme before the circle starts, which read as the theme "changing twice".
|
|
206
217
|
// The pseudo-elements belong to the document element whatever was updated,
|
|
207
218
|
// so the animation always targets it.
|
|
208
|
-
|
|
219
|
+
page.documentElement.animate(
|
|
209
220
|
{
|
|
210
221
|
clipPath: [`circle(${endRadius}px at ${x}px ${y}px)`, `circle(0px at ${x}px ${y}px)`],
|
|
211
222
|
},
|
|
212
223
|
{
|
|
213
224
|
duration: durationMs,
|
|
214
|
-
easing: easingToken("ease-emphasized"),
|
|
225
|
+
easing: easingToken("ease-emphasized", undefined, page),
|
|
215
226
|
// Hold the fully-shrunk circle after the keyframes end. Without it the
|
|
216
227
|
// old snapshot snaps back to its default (un-clipped) state for the one
|
|
217
228
|
// frame between animation-end and the browser tearing down the
|
|
@@ -221,7 +232,7 @@ export function revealTransition(
|
|
|
221
232
|
pseudoElement: "::view-transition-old(root)",
|
|
222
233
|
},
|
|
223
234
|
)
|
|
224
|
-
})
|
|
235
|
+
}, skipped)
|
|
225
236
|
}
|
|
226
237
|
|
|
227
238
|
/*
|
|
@@ -235,7 +246,7 @@ export function applyThemeWithTransition(
|
|
|
235
246
|
root: HTMLElement = document.documentElement,
|
|
236
247
|
durationMs = 480,
|
|
237
248
|
): void {
|
|
238
|
-
revealTransition(() => applyTheme(theme, root), origin, durationMs)
|
|
249
|
+
revealTransition(() => applyTheme(theme, root), origin, durationMs, root.ownerDocument)
|
|
239
250
|
}
|
|
240
251
|
|
|
241
252
|
export function serializeTheme(theme: TokenSet): string {
|
|
@@ -279,12 +290,72 @@ export function normalizeTheme(raw: Partial<TokenSet> & { name?: string }): Toke
|
|
|
279
290
|
}
|
|
280
291
|
}
|
|
281
292
|
|
|
282
|
-
|
|
293
|
+
const THEME_NAME_MAX = 64
|
|
294
|
+
/* Past the 9999 a pill radius is spelt with: beyond that a size is a typo, not a design. */
|
|
295
|
+
const THEME_SIZE_MAX = 99_999
|
|
296
|
+
/* A value goes into a stylesheet as text (themeToCssRule), so it must not be able to leave its declaration. */
|
|
297
|
+
const CSS_BREAKOUT = /[{}<>]/
|
|
298
|
+
const CSS_URL = /url\([^()]*\)/g
|
|
299
|
+
|
|
300
|
+
function cssValueProblem(value: unknown): string | null {
|
|
301
|
+
if (typeof value !== "string" || !value.trim()) return "must be a non-empty string"
|
|
302
|
+
// A `;` belongs inside a data: url, and nowhere else in a value.
|
|
303
|
+
if (CSS_BREAKOUT.test(value) || value.replace(CSS_URL, "").includes(";")) return "is not a single CSS value"
|
|
304
|
+
return null
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
function sizeProblem(value: unknown): string | null {
|
|
308
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return "must be a finite number"
|
|
309
|
+
if (value < 0 || value > THEME_SIZE_MAX) return `must be between 0 and ${THEME_SIZE_MAX}`
|
|
310
|
+
return null
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
function axisProblem(
|
|
314
|
+
axis: string,
|
|
315
|
+
values: unknown,
|
|
316
|
+
keys: object,
|
|
317
|
+
check: (value: unknown) => string | null,
|
|
318
|
+
): string | null {
|
|
319
|
+
if (values === undefined) return null
|
|
320
|
+
if (!values || typeof values !== "object") return `${axis} must be an object`
|
|
321
|
+
for (const key of Object.keys(keys)) {
|
|
322
|
+
const value = (values as Record<string, unknown>)[key]
|
|
323
|
+
if (value === undefined) continue
|
|
324
|
+
const problem = check(value)
|
|
325
|
+
if (problem) return `${axis}.${key} ${problem}`
|
|
326
|
+
}
|
|
327
|
+
return null
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/*
|
|
331
|
+
* What is wrong with a theme read from storage or a file, or null. Types stop
|
|
332
|
+
* at the JSON boundary: `radiusCard: "huge"` would reach the page as `hugepx`.
|
|
333
|
+
* A token that is simply missing is fine: normalizeTheme fills it, which is how
|
|
334
|
+
* a theme saved before that token existed keeps working.
|
|
335
|
+
*/
|
|
336
|
+
export function themeProblem(raw: Partial<TokenSet>): string | null {
|
|
337
|
+
const { name, mode } = raw as { name?: unknown; mode?: unknown }
|
|
338
|
+
if (name !== undefined && (typeof name !== "string" || !name.trim() || name.length > THEME_NAME_MAX)) {
|
|
339
|
+
return `name must be 1 to ${THEME_NAME_MAX} characters`
|
|
340
|
+
}
|
|
341
|
+
if (mode !== undefined && mode !== "light" && mode !== "dark") return 'mode must be "light" or "dark"'
|
|
342
|
+
return (
|
|
343
|
+
axisProblem("colors", raw.colors, COLOR_VARS, cssValueProblem) ??
|
|
344
|
+
axisProblem("shape", raw.shape, SHAPE_VARS, sizeProblem) ??
|
|
345
|
+
axisProblem("density", raw.density, DENSITY_VARS, sizeProblem) ??
|
|
346
|
+
axisProblem("font", raw.font, FONT_VARS, cssValueProblem) ??
|
|
347
|
+
axisProblem("surface", raw.surface, SURFACE_VARS, cssValueProblem)
|
|
348
|
+
)
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/** Parse + validate a theme JSON; throws on a malformed shape or a bad token value. */
|
|
283
352
|
export function parseTheme(json: string): TokenSet {
|
|
284
353
|
const raw = JSON.parse(json) as Partial<TokenSet>
|
|
285
354
|
if (!raw || typeof raw !== "object" || !raw.colors || !raw.shape || !raw.density) {
|
|
286
355
|
throw new Error("Invalid theme: missing colors/shape/density")
|
|
287
356
|
}
|
|
357
|
+
const problem = themeProblem(raw)
|
|
358
|
+
if (problem) throw new Error(`Invalid theme: ${problem}`)
|
|
288
359
|
return normalizeTheme(raw)
|
|
289
360
|
}
|
|
290
361
|
|
|
@@ -321,7 +392,8 @@ export function cacheSystemThemes(system: SystemThemes): void {
|
|
|
321
392
|
/*
|
|
322
393
|
* SSR handoff. localStorage is invisible to the server, so the theme NAME rides
|
|
323
394
|
* along in a cookie and the server resolves it against the themes it knows
|
|
324
|
-
* (see themeFromCookie). "Follow the OS" is stored as `system:<light>|<dark
|
|
395
|
+
* (see themeFromCookie). "Follow the OS" is stored as `system:<light>|<dark>`,
|
|
396
|
+
* each name encoded on its own so a `|` in a name stays part of that name.
|
|
325
397
|
*/
|
|
326
398
|
export const THEME_COOKIE = "lp-theme"
|
|
327
399
|
const SYSTEM_PREFIX = "system:"
|
|
@@ -329,7 +401,7 @@ const SYSTEM_PREFIX = "system:"
|
|
|
329
401
|
const COOKIE_MAX_AGE = 60 * 60 * 24 * 365
|
|
330
402
|
|
|
331
403
|
function systemCookieValue(system: SystemThemes): string {
|
|
332
|
-
return `${SYSTEM_PREFIX}${system.light.name}|${system.dark.name}`
|
|
404
|
+
return `${SYSTEM_PREFIX}${encodeURIComponent(system.light.name)}|${encodeURIComponent(system.dark.name)}`
|
|
333
405
|
}
|
|
334
406
|
|
|
335
407
|
function writeThemeCookie(value: string): void {
|
|
@@ -368,7 +440,8 @@ export function themeFromCookie(
|
|
|
368
440
|
if (value.startsWith(SYSTEM_PREFIX)) {
|
|
369
441
|
const [lightName = "", darkName = ""] = value.slice(SYSTEM_PREFIX.length).split("|")
|
|
370
442
|
const wanted = options.prefersDark === false ? lightName : darkName
|
|
371
|
-
|
|
443
|
+
// Raw too: a cookie from before the names were encoded.
|
|
444
|
+
const found = find(safeDecode(wanted)) ?? find(wanted)
|
|
372
445
|
if (found) return found
|
|
373
446
|
continue
|
|
374
447
|
}
|
|
@@ -427,6 +500,7 @@ export function useTheme(): UseTheme {
|
|
|
427
500
|
_current.value = theme
|
|
428
501
|
},
|
|
429
502
|
init: (fallback: TokenSet) => {
|
|
503
|
+
if (typeof window === "undefined") return fallback
|
|
430
504
|
_fallback = fallback
|
|
431
505
|
hydrateOnce()
|
|
432
506
|
if (!_current.value) _current.value = bootstrapTheme(fallback)
|
package/src/tokens/tokens.css
CHANGED
|
@@ -173,6 +173,8 @@
|
|
|
173
173
|
for it, and below the toasts one on a toast's own button (the error
|
|
174
174
|
toast's copy) opened underneath the toast stacked above it. */
|
|
175
175
|
@theme {
|
|
176
|
+
/* The app shell's header, rail and bars over a page that scrolls under them. */
|
|
177
|
+
--z-sticky: 40;
|
|
176
178
|
--z-dropdown: 50;
|
|
177
179
|
--z-overlay: 100;
|
|
178
180
|
--z-modal: 110;
|
|
@@ -823,7 +825,12 @@
|
|
|
823
825
|
CSS transitions/animate-pulse used across the kit collapse to a near-instant
|
|
824
826
|
state change — content still mounts/unmounts, it just doesn't travel. This is
|
|
825
827
|
the CSS half; motion-v springs (Tabs/Layout) opt out in JS via
|
|
826
|
-
usePrefersReducedMotion() (usePillTransition).
|
|
828
|
+
usePrefersReducedMotion() (usePillTransition).
|
|
829
|
+
|
|
830
|
+
Global on purpose, and part of this stylesheet's contract: it also covers the
|
|
831
|
+
host app's own elements, so an app that imports the kit's CSS gets reduced
|
|
832
|
+
motion for free. An animation that must keep running (a status spinner) sets
|
|
833
|
+
its own `animation-duration` with `!important` in a later layer. */
|
|
827
834
|
@media (prefers-reduced-motion: reduce) {
|
|
828
835
|
*,
|
|
829
836
|
*::before,
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
.lp-tab-badge-enter-active[data-v-1d673b52],
|
|
3
|
-
.lp-tab-badge-leave-active[data-v-1d673b52] {
|
|
4
|
-
transition:
|
|
5
|
-
opacity var(--duration-fast) var(--ease-emphasized),
|
|
6
|
-
transform var(--duration-fast) var(--ease-emphasized);
|
|
7
|
-
}
|
|
8
|
-
.lp-tab-badge-enter-from[data-v-1d673b52],
|
|
9
|
-
.lp-tab-badge-leave-to[data-v-1d673b52] {
|
|
10
|
-
opacity: 0;
|
|
11
|
-
transform: scale(0.6);
|
|
12
|
-
}
|
|
13
|
-
@media (prefers-reduced-motion: reduce) {
|
|
14
|
-
.lp-tab-badge-enter-active[data-v-1d673b52],
|
|
15
|
-
.lp-tab-badge-leave-active[data-v-1d673b52] {
|
|
16
|
-
transition: none;
|
|
17
|
-
}
|
|
18
|
-
}
|