@leavepulse/ui 0.69.0 → 0.70.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +49 -0
- package/dist/assets/LoaderMark-DxBV2iDJ.css +43 -0
- package/dist/assets/LpActivityLine-B3EwNQEB.css +33 -0
- package/dist/assets/{LpCommandPalette-DxyjhYn_.css → LpCommandPalette-u-zOxKWa.css} +21 -31
- package/dist/assets/LpDock-Cbh0wQUv.css +17 -0
- package/dist/assets/{LpDropzone-Qb8k7cg4.css → LpDropzone-Bb1vBTeU.css} +12 -17
- package/dist/assets/{LpImageEditor-CElPDzpF.css → LpImageEditor-XM5bAziW.css} +6 -6
- package/dist/assets/LpLogViewer-BDNW6P52.css +38 -0
- package/dist/assets/{LpPalettePreview-CJmAKgYW.css → LpPalettePreview-B9VKDrvA.css} +2 -7
- package/dist/assets/LpRepeater-C1Cw27hj.css +21 -0
- package/dist/assets/{LpRollingText-BGnE6q9j.css → LpRollingText-dyOXedGO.css} +7 -0
- package/dist/assets/LpShinyText-D69cBYDR.css +61 -0
- package/dist/assets/LpStatus-430OGTZY.css +17 -0
- package/dist/assets/LpStepMark-Drvx33Vg.css +10 -0
- package/dist/assets/{LpTable-BPERfJIk.css → LpTable-DHxEQ_-3.css} +19 -19
- package/dist/assets/LpTabs-BJ-nrJuI.css +18 -0
- package/dist/assets/LpTextReveal-DjI1MsDS.css +35 -0
- package/dist/assets/LpTypewriter-CXAm9Nca.css +27 -0
- package/dist/chunks/AttentionDot-DCBf4aWg.js +25 -0
- package/dist/chunks/{ContextMenuItems-B7XFHksm.js → ContextMenuItems-6kxEivBI.js} +11 -5
- package/dist/chunks/{DropzoneLinkField-CjkrpDqO.js → DropzoneLinkField-Bw8Flkhk.js} +2 -2
- package/dist/chunks/FieldWrapper-BiennROB.js +43 -0
- package/dist/chunks/{LayoutCanvas-DF9CovaK.js → LayoutCanvas-BsJIzhdd.js} +1 -1
- package/dist/chunks/{LayoutNode-D9bIFzk0.js → LayoutNode-DJVhkSGk.js} +6 -8
- package/dist/chunks/LegendSwatch-BhRjrtzv.js +18 -0
- package/dist/chunks/LoaderMark-C1dubiKn.js +85 -0
- package/dist/chunks/{LpActionBar-MPKMu3pz.js → LpActionBar-CzOFar5t.js} +14 -20
- package/dist/chunks/LpActivityLine-CJvzrNpj.js +95 -0
- package/dist/chunks/{LpAddressInput-DG0BTqdH.js → LpAddressInput-Chv_Xf8q.js} +7 -7
- package/dist/chunks/LpAlert-C5igj802.js +119 -0
- package/dist/chunks/{LpAppShell-DPh1756q.js → LpAppShell-BiWByLmt.js} +15 -16
- package/dist/chunks/{LpAutocomplete-Dgn6hw3p.js → LpAutocomplete-D92niDHE.js} +15 -32
- package/dist/chunks/{LpAvatar-Bs0Fb1lC.js → LpAvatar-CLPl-qOF.js} +18 -6
- package/dist/chunks/LpBadge-BBq1Er9T.js +126 -0
- package/dist/chunks/LpBreadcrumbs-JKEi_yFF.js +144 -0
- package/dist/chunks/{LpButton-DNR-YAq5.js → LpButton-CVxBfGjd.js} +12 -4
- package/dist/chunks/{LpCalendar-BLya39Fh.js → LpCalendar-pVL91-Pl.js} +1 -1
- package/dist/chunks/{LpCard-Bf71NXZq.js → LpCard-CqKLnEhl.js} +18 -8
- package/dist/chunks/{LpCheckbox-Da5oZPHo.js → LpCheckbox-DHc2bHDe.js} +9 -6
- package/dist/chunks/{LpCodeBlock-lLbmslfs.js → LpCodeBlock-BGsQhxw8.js} +24 -32
- package/dist/chunks/LpCodeDisplay-CbJMs-IK.js +189 -0
- package/dist/chunks/LpCollapse-CWS9M_lm.js +115 -0
- package/dist/chunks/{LpColorPicker-DtEfBAcK.js → LpColorPicker-B7HSIFmn.js} +11 -6
- package/dist/chunks/{LpConfirmDialog-D7s4jakB.js → LpConfirmDialog-CfGCux8m.js} +3 -3
- package/dist/chunks/{LpConfirmHost-DrYI5y1K.js → LpConfirmHost-exB7JmcO.js} +1 -1
- package/dist/chunks/{LpContextMenu-CRnjoqSt.js → LpContextMenu-Cj96ENDC.js} +16 -7
- package/dist/chunks/LpCopyButton-DzHjnFs0.js +133 -0
- package/dist/chunks/LpCountdown-BQnmB4gq.js +231 -0
- package/dist/chunks/LpDataList-CHNYQw7h.js +121 -0
- package/dist/chunks/LpDatePicker-ac1_6o5P.js +169 -0
- package/dist/chunks/LpDateRangePicker-B15gYrZJ.js +127 -0
- package/dist/chunks/{LpDisclosure-6AplYBub.js → LpDisclosure-DOarzlAG.js} +22 -11
- package/dist/chunks/{LpDrawer-Q4SOxM4s.js → LpDrawer-DGNKRsGq.js} +9 -4
- package/dist/chunks/{LpDropdownMenu-DWTt0ooM.js → LpDropdownMenu-R4aYVtVV.js} +14 -6
- package/dist/chunks/LpEditable-Ws3DNQHn.js +238 -0
- package/dist/chunks/{LpEmptyState-om1UnFjV.js → LpEmptyState-KTKipws2.js} +1 -1
- package/dist/chunks/{LpFieldNotice-CHuDeLjc.js → LpFieldNotice-CurbNuXI.js} +1 -1
- package/dist/chunks/{LpFileTree-CW24YsVX.js → LpFileTree-DDNg34tJ.js} +5 -3
- package/dist/chunks/{LpFileTreeNode-BXhTHaKW.js → LpFileTreeNode-DmPuYNZ_.js} +42 -55
- package/dist/chunks/{LpFormField-DPb47HEm.js → LpFormField-BWwgm1E4.js} +23 -9
- package/dist/chunks/LpFrame-BVQCQLDI.js +155 -0
- package/dist/chunks/LpGauge-C14Z9503.js +204 -0
- package/dist/chunks/LpGlowBorder-B8V-QzIl.js +40 -0
- package/dist/chunks/{LpHintExample-C1eKYdeL.js → LpHintExample-9Py0wc-L.js} +11 -34
- package/dist/chunks/{LpHintLayer-7UOdWQG3.js → LpHintLayer-HvwUA5Ec.js} +27 -14
- package/dist/chunks/{LpHints-BUtBp9-w.js → LpHints-DxbPKOzQ.js} +3 -3
- package/dist/chunks/LpHoverCard-spGxyw7T.js +130 -0
- package/dist/chunks/{LpIcon-6tSrZ3Zy.js → LpIcon-HJP5xMz4.js} +1 -1
- package/dist/chunks/LpIdenticon-BzdC_qz9.js +63 -0
- package/dist/chunks/{LpInfraNode-Ds_8SZgN.js → LpInfraNode-D64hywJI.js} +39 -25
- package/dist/chunks/{LpInput-CUyr6PIE.js → LpInput-CDuwODJG.js} +44 -18
- package/dist/chunks/{LpLightbox-B9J8rzsj.js → LpLightbox-XJ0LpTcZ.js} +69 -69
- package/dist/chunks/LpLoader-DjJEX8hL.js +92 -0
- package/dist/chunks/{LpLogViewer-4QFc-sVN.js → LpLogViewer-DKK4ySZl.js} +225 -129
- package/dist/chunks/LpMagnetic-CtXKLa2Y.js +62 -0
- package/dist/chunks/{LpMapPicker-BTbkFeSG.js → LpMapPicker-BtIl7NOM.js} +34 -31
- package/dist/chunks/LpMasonry--k0571qG.js +185 -0
- package/dist/chunks/{LpMediaGallery-CuPcscx3.js → LpMediaGallery-rmI1DjKN.js} +3 -3
- package/dist/chunks/{LpModal-hk1noDcr.js → LpModal-eBMt2Fqn.js} +8 -3
- package/dist/chunks/{LpModalNav-DQd2keZm.js → LpModalNav-DiyFCHiZ.js} +4 -4
- package/dist/chunks/{LpNotificationBell-X84PJIxD.js → LpNotificationBell-D-WhE5m2.js} +32 -48
- package/dist/chunks/LpNumberField-B9k-h2HE.js +140 -0
- package/dist/chunks/{LpNumberFlow-z4WPZcZu.js → LpNumberFlow-Cfyh033Q.js} +8 -28
- package/dist/chunks/{LpOtpInput-PbeNJ5In.js → LpOtpInput-CeBPsM7D.js} +2 -1
- package/dist/chunks/{LpPagination-Bx-VG4Ag.js → LpPagination-VNCX6pFC.js} +37 -6
- package/dist/chunks/{LpPalettePreview-CFav_7co.js → LpPalettePreview-BNB8T_MY.js} +1 -1
- package/dist/chunks/{LpPasswordInput-CbnXitAS.js → LpPasswordInput-GL2KOFK8.js} +26 -22
- package/dist/chunks/{LpPhoneInput-DP69HONS.js → LpPhoneInput-C60tx1tC.js} +12 -7
- package/dist/chunks/{LpPie-yLF9-aDR.js → LpPie-BgXZ3xmm.js} +159 -48
- package/dist/chunks/LpPipeline-B31h41Mu.js +329 -0
- package/dist/chunks/{LpPopover-DVnkHaOu.js → LpPopover-DmZ3wUtP.js} +13 -7
- package/dist/chunks/{LpProgress-CA86wxXu.js → LpProgress-CE-m5OpD.js} +23 -63
- package/dist/chunks/LpQuota-DaL8TgNe.js +162 -0
- package/dist/chunks/{LpRadioGroup-8S5hf8lE.js → LpRadioGroup-CGBr5nIp.js} +1 -1
- package/dist/chunks/{LpRangeCalendar-ByQi-WUz.js → LpRangeCalendar-c5qj7YcC.js} +1 -1
- package/dist/chunks/{LpRangeSlider-C1yF9-nP.js → LpRangeSlider-Dv6lNvHY.js} +6 -2
- package/dist/chunks/LpRelativeTime-yin9uQP7.js +73 -0
- package/dist/chunks/LpRepeater-D0S7jjwr.js +180 -0
- package/dist/chunks/LpRevealOnScroll-DcQjexLy.js +76 -0
- package/dist/chunks/{LpRollingText-DB7upf-5.js → LpRollingText-DiL717LK.js} +4 -3
- package/dist/chunks/LpScramble-TDO5Kdou.js +85 -0
- package/dist/chunks/LpSecretField-CfWyHv9m.js +316 -0
- package/dist/chunks/{LpSection-BmRXe6ZP.js → LpSection-BW4xJ-Li.js} +1 -1
- package/dist/chunks/{LpSegmented-CW3bFHM4.js → LpSegmented-BO99Sr4m.js} +11 -14
- package/dist/chunks/{LpSelect-BCyDYoih.js → LpSelect-C_8cJtj6.js} +91 -111
- package/dist/chunks/LpShaderBackdrop-Bote0iuy.js +332 -0
- package/dist/chunks/LpShinyText-DX2ajtWq.js +48 -0
- package/dist/chunks/{LpSidebar-DfIiS-lB.js → LpSidebar-CZRXtvem.js} +5 -3
- package/dist/chunks/{LpSidebarNav-C5FDNI0t.js → LpSidebarNav-DBg1U1oW.js} +3 -3
- package/dist/chunks/{LpSlider-Cuzmrd7p.js → LpSlider-DuoRpOrH.js} +6 -2
- package/dist/chunks/{LpSparkline-DXT4KrSi.js → LpSparkline-CExIRDb3.js} +32 -27
- package/dist/chunks/LpSpotlight-4F3NeUMY.js +41 -0
- package/dist/chunks/{LpStat-iBMF5QNk.js → LpStat-DFKlPJgy.js} +38 -35
- package/dist/chunks/LpStatus-BsGLrpor.js +86 -0
- package/dist/chunks/LpStepMark-TrzOioa9.js +101 -0
- package/dist/chunks/LpStepper-BbnS5jUr.js +76 -0
- package/dist/chunks/{LpSwap-D82jiecV.js → LpSwap-DzE6M3Mg.js} +1 -1
- package/dist/chunks/LpSwitch-DrzKrZZV.js +112 -0
- package/dist/chunks/{LpTableFilter-CuIbgdW0.js → LpTableFilter-DyvHGbOk.js} +3 -2
- package/dist/chunks/{LpTableOfContents-C_4VBktT.js → LpTableOfContents-rYvo8z5T.js} +34 -21
- package/dist/chunks/{LpTagsInput-DiXrpYeE.js → LpTagsInput-DZg80W-B.js} +68 -71
- package/dist/chunks/LpTextReveal-CtAnOUkp.js +140 -0
- package/dist/chunks/{LpTextarea-xoQd5Lnp.js → LpTextarea-DiWjeVoJ.js} +12 -11
- package/dist/chunks/{LpThemeProvider-CQ2jC9TA.js → LpThemeProvider-DcbfOxsU.js} +1 -1
- package/dist/chunks/LpThemeStudio-DxVZMtzN.js +416 -0
- package/dist/chunks/LpThemeStudioSection-BwhqYDLf.js +20 -0
- package/dist/chunks/LpThemeSwitcher-CadVBTBn.js +278 -0
- package/dist/chunks/{LpTimeField-MFgAsNc5.js → LpTimeField-BFQVf_PW.js} +13 -5
- package/dist/chunks/{LpToaster-C4zE9zB7.js → LpToaster-Wv9ksj4q.js} +49 -82
- package/dist/chunks/{LpTooltip-suFCM34F.js → LpTooltip-Cz8SQwRt.js} +8 -3
- package/dist/chunks/{LpTopologyCanvas-6ozNrQKh.js → LpTopologyCanvas-C17A1XCb.js} +1 -1
- package/dist/chunks/{LpTour-BUp3Jt2e.js → LpTour-CY2bFXge.js} +24 -18
- package/dist/chunks/LpTruncate-D_5Pfv5B.js +112 -0
- package/dist/chunks/LpTypewriter-BCL_a6wE.js +159 -0
- package/dist/chunks/{LpUptimeBar-B6kHw4Cj.js → LpUptimeBar-Tew1Ykx4.js} +10 -9
- package/dist/chunks/{LpVideoBackdrop-qWRYBOTQ.js → LpVideoBackdrop-DS1wGv-X.js} +7 -8
- package/dist/chunks/{MenuTitle-obgek0B7.js → MenuTitle-BjQZX8tQ.js} +1 -1
- package/dist/chunks/OptionContent-CmYUUeWj.js +40 -0
- package/dist/chunks/PickerField-DoHv0WMH.js +125 -0
- package/dist/chunks/RevealToggle-C-KzaQGJ.js +65 -0
- package/dist/chunks/StepConnector-37OgEOXA.js +41 -0
- package/dist/chunks/TourCard-BJ8i7dk0.js +123 -0
- package/dist/chunks/backdrop-Dkf2vM81.js +59 -0
- package/dist/chunks/{icons-DVhZ_plB.js → icons-4IywzkHk.js} +1 -1
- package/dist/chunks/{useTheme-2qHG8NEU.js → useTheme-BOV_-AiY.js} +9 -65
- package/dist/component-names.d.ts +1 -1
- package/dist/components/AttentionDot.vue.d.ts +9 -0
- package/dist/components/AttentionDot.vue.js +4 -0
- package/dist/components/ContextMenuItems.vue.js +1 -1
- package/dist/components/DropzoneLinkField.vue.js +1 -1
- package/dist/components/FieldWrapper.vue.d.ts +25 -0
- package/dist/components/FieldWrapper.vue.js +4 -0
- package/dist/components/LayoutCanvas.vue.js +1 -1
- package/dist/components/LayoutNode.vue.d.ts +4 -4
- package/dist/components/LayoutNode.vue.js +1 -1
- package/dist/components/LegendSwatch.vue.d.ts +6 -0
- package/dist/components/LegendSwatch.vue.js +4 -0
- package/dist/components/LoaderMark.vue.d.ts +12 -0
- package/dist/components/LoaderMark.vue.js +4 -0
- package/dist/components/LpActionBar.vue.js +1 -1
- package/dist/components/LpActivityLine.vue.d.ts +58 -0
- package/dist/components/LpActivityLine.vue.js +4 -0
- package/dist/components/LpAddressInput.vue.js +1 -1
- package/dist/components/LpAlert.vue.d.ts +2 -2
- package/dist/components/LpAlert.vue.js +1 -1
- package/dist/components/LpAppShell.vue.d.ts +8 -7
- package/dist/components/LpAppShell.vue.js +1 -1
- package/dist/components/LpAutocomplete.vue.js +1 -1
- package/dist/components/LpAvatar.vue.d.ts +16 -0
- package/dist/components/LpAvatar.vue.js +1 -1
- package/dist/components/LpBadge.vue.d.ts +19 -5
- package/dist/components/LpBadge.vue.js +1 -1
- package/dist/components/LpBreadcrumbs.vue.js +1 -1
- package/dist/components/LpButton.vue.d.ts +2 -2
- package/dist/components/LpButton.vue.js +1 -1
- package/dist/components/LpCalendar.vue.js +1 -1
- package/dist/components/LpCard.vue.d.ts +24 -0
- package/dist/components/LpCard.vue.js +1 -1
- package/dist/components/LpChart.vue.js +131 -160
- package/dist/components/LpCheckbox.vue.d.ts +2 -2
- package/dist/components/LpCheckbox.vue.js +1 -1
- package/dist/components/LpCodeBlock.vue.js +1 -1
- package/dist/components/LpCodeDisplay.vue.d.ts +89 -0
- package/dist/components/LpCodeDisplay.vue.js +4 -0
- package/dist/components/LpCollapse.vue.d.ts +58 -0
- package/dist/components/LpCollapse.vue.js +4 -0
- package/dist/components/LpColorPicker.vue.d.ts +2 -2
- package/dist/components/LpColorPicker.vue.js +1 -1
- package/dist/components/LpCommandPalette.vue.js +34 -23
- 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/LpCopyButton.vue.d.ts +82 -0
- package/dist/components/LpCopyButton.vue.js +4 -0
- package/dist/components/LpCountdown.vue.d.ts +100 -0
- package/dist/components/LpCountdown.vue.js +4 -0
- package/dist/components/LpDataList.vue.d.ts +66 -0
- package/dist/components/LpDataList.vue.js +4 -0
- package/dist/components/LpDatePicker.vue.d.ts +3 -1
- package/dist/components/LpDatePicker.vue.js +1 -1
- package/dist/components/LpDateRangePicker.vue.d.ts +3 -1
- package/dist/components/LpDateRangePicker.vue.js +1 -1
- package/dist/components/LpDisclosure.vue.js +1 -1
- package/dist/components/LpDock.vue.d.ts +74 -0
- package/dist/components/LpDock.vue.js +204 -0
- package/dist/components/LpDrawer.vue.js +1 -1
- package/dist/components/LpDropdownMenu.vue.js +1 -1
- package/dist/components/LpDropzone.vue.d.ts +8 -1
- package/dist/components/LpDropzone.vue.js +36 -15
- package/dist/components/LpEditable.vue.d.ts +85 -0
- package/dist/components/LpEditable.vue.js +4 -0
- package/dist/components/LpEmptyState.vue.js +1 -1
- package/dist/components/LpFieldNotice.vue.js +1 -1
- package/dist/components/LpFileTree.vue.js +1 -1
- package/dist/components/LpFileTreeNode.vue.js +1 -1
- package/dist/components/LpFormField.vue.js +1 -1
- package/dist/components/LpFrame.vue.d.ts +22 -0
- package/dist/components/LpFrame.vue.js +4 -0
- package/dist/components/LpGauge.vue.d.ts +116 -0
- package/dist/components/LpGauge.vue.js +4 -0
- package/dist/components/LpGlowBorder.vue.d.ts +47 -0
- package/dist/components/LpGlowBorder.vue.js +4 -0
- package/dist/components/LpHintExample.vue.d.ts +2 -2
- 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.d.ts +60 -0
- package/dist/components/LpHoverCard.vue.js +4 -0
- package/dist/components/LpIcon.vue.js +1 -1
- package/dist/components/LpIdenticon.vue.d.ts +37 -0
- package/dist/components/LpIdenticon.vue.js +4 -0
- package/dist/components/LpImageEditor.vue.js +13 -19
- package/dist/components/LpInfraNode.vue.js +1 -1
- package/dist/components/LpInput.vue.d.ts +10 -0
- package/dist/components/LpInput.vue.js +1 -1
- package/dist/components/LpLightbox.vue.js +1 -1
- package/dist/components/LpLoader.vue.d.ts +106 -0
- package/dist/components/LpLoader.vue.js +4 -0
- package/dist/components/LpLogViewer.vue.js +1 -1
- package/dist/components/LpMagnetic.vue.d.ts +28 -0
- package/dist/components/LpMagnetic.vue.js +4 -0
- package/dist/components/LpMapPicker.vue.js +1 -1
- package/dist/components/LpMasonry.vue.d.ts +42 -0
- package/dist/components/LpMasonry.vue.js +4 -0
- package/dist/components/LpMediaGallery.vue.js +1 -1
- package/dist/components/LpModal.vue.js +1 -1
- package/dist/components/LpModalNav.vue.js +1 -1
- package/dist/components/LpNotificationBell.vue.d.ts +15 -6
- package/dist/components/LpNotificationBell.vue.js +1 -1
- package/dist/components/LpNumberField.vue.d.ts +57 -0
- package/dist/components/LpNumberField.vue.js +1 -1
- package/dist/components/LpNumberFlow.vue.js +1 -1
- package/dist/components/LpOtpInput.vue.d.ts +12 -4
- package/dist/components/LpOtpInput.vue.js +1 -1
- package/dist/components/LpPagination.vue.js +1 -1
- package/dist/components/LpPaletteFacts.vue.js +2 -2
- package/dist/components/LpPaletteHeader.vue.js +2 -2
- package/dist/components/LpPalettePreview.vue.js +1 -1
- package/dist/components/LpPaletteStats.vue.js +2 -2
- package/dist/components/LpPasswordInput.vue.d.ts +19 -2
- package/dist/components/LpPasswordInput.vue.js +1 -1
- package/dist/components/LpPhoneInput.vue.js +1 -1
- package/dist/components/LpPie.vue.d.ts +3 -1
- package/dist/components/LpPie.vue.js +1 -1
- package/dist/components/LpPipeline.vue.d.ts +91 -0
- package/dist/components/LpPipeline.vue.js +4 -0
- package/dist/components/LpPopover.vue.d.ts +2 -2
- package/dist/components/LpPopover.vue.js +1 -1
- package/dist/components/LpProgress.vue.d.ts +11 -4
- package/dist/components/LpProgress.vue.js +1 -1
- package/dist/components/LpQuota.vue.d.ts +68 -0
- package/dist/components/LpQuota.vue.js +4 -0
- 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.d.ts +41 -0
- package/dist/components/LpRelativeTime.vue.js +4 -0
- package/dist/components/LpRepeater.vue.d.ts +48 -0
- package/dist/components/LpRepeater.vue.js +4 -0
- package/dist/components/LpRevealOnScroll.vue.d.ts +67 -0
- package/dist/components/LpRevealOnScroll.vue.js +4 -0
- package/dist/components/LpRollingText.vue.d.ts +3 -1
- package/dist/components/LpRollingText.vue.js +1 -1
- package/dist/components/LpScramble.vue.d.ts +55 -0
- package/dist/components/LpScramble.vue.js +4 -0
- package/dist/components/LpScrollArea.vue.d.ts +1 -1
- package/dist/components/LpSecretField.vue.d.ts +108 -0
- package/dist/components/LpSecretField.vue.js +4 -0
- package/dist/components/LpSection.vue.js +1 -1
- package/dist/components/LpSegmented.vue.js +1 -1
- package/dist/components/LpSelect.vue.d.ts +2 -0
- package/dist/components/LpSelect.vue.js +1 -1
- package/dist/components/LpShaderBackdrop.vue.d.ts +39 -0
- package/dist/components/LpShaderBackdrop.vue.js +4 -0
- package/dist/components/LpShinyText.vue.d.ts +39 -0
- package/dist/components/LpShinyText.vue.js +4 -0
- package/dist/components/LpShortcutsDialog.vue.js +1 -1
- package/dist/components/LpSidebar.vue.js +1 -1
- package/dist/components/LpSidebarNav.vue.js +1 -1
- package/dist/components/LpSlider.vue.js +1 -1
- package/dist/components/LpSparkline.vue.d.ts +4 -3
- package/dist/components/LpSparkline.vue.js +1 -1
- package/dist/components/LpSpotlight.vue.d.ts +37 -0
- package/dist/components/LpSpotlight.vue.js +4 -0
- package/dist/components/LpStat.vue.js +1 -1
- package/dist/components/LpStatus.vue.d.ts +65 -0
- package/dist/components/LpStatus.vue.js +4 -0
- package/dist/components/LpStepMark.vue.d.ts +31 -0
- package/dist/components/LpStepMark.vue.js +4 -0
- package/dist/components/LpStepper.vue.js +1 -1
- package/dist/components/LpSwap.vue.js +1 -1
- package/dist/components/LpSwitch.vue.d.ts +2 -2
- package/dist/components/LpSwitch.vue.js +1 -1
- package/dist/components/LpTable.vue.js +104 -120
- package/dist/components/LpTableFilter.vue.js +1 -1
- package/dist/components/LpTableOfContents.vue.d.ts +6 -1
- package/dist/components/LpTableOfContents.vue.js +1 -1
- package/dist/components/LpTabs.vue.d.ts +5 -5
- package/dist/components/LpTabs.vue.js +25 -33
- package/dist/components/LpTagsInput.vue.js +1 -1
- package/dist/components/LpTextReveal.vue.d.ts +58 -0
- package/dist/components/LpTextReveal.vue.js +4 -0
- package/dist/components/LpTextarea.vue.js +1 -1
- package/dist/components/LpThemeProvider.vue.js +1 -1
- package/dist/components/LpThemeStudio.vue.d.ts +54 -0
- package/dist/components/LpThemeStudio.vue.js +4 -0
- package/dist/components/LpThemeStudioSection.vue.d.ts +16 -0
- package/dist/components/LpThemeStudioSection.vue.js +4 -0
- package/dist/components/LpThemeSwitcher.vue.d.ts +32 -1
- package/dist/components/LpThemeSwitcher.vue.js +1 -1
- package/dist/components/LpTimeField.vue.d.ts +2 -0
- package/dist/components/LpTimeField.vue.js +1 -1
- package/dist/components/LpToaster.vue.js +1 -1
- package/dist/components/LpTooltip.vue.d.ts +2 -2
- package/dist/components/LpTooltip.vue.js +1 -1
- package/dist/components/LpTopologyCanvas.vue.js +1 -1
- package/dist/components/LpTour.vue.js +1 -1
- package/dist/components/LpTruncate.vue.d.ts +34 -0
- package/dist/components/LpTruncate.vue.js +4 -0
- package/dist/components/LpTypewriter.vue.d.ts +61 -0
- package/dist/components/LpTypewriter.vue.js +4 -0
- package/dist/components/LpUptimeBar.vue.js +1 -1
- package/dist/components/LpVideoBackdrop.vue.js +1 -1
- package/dist/components/MenuTitle.vue.js +1 -1
- package/dist/components/OptionContent.vue.d.ts +7 -0
- package/dist/components/OptionContent.vue.js +4 -0
- package/dist/components/PickerField.vue.d.ts +45 -0
- package/dist/components/PickerField.vue.js +4 -0
- package/dist/components/RevealToggle.vue.d.ts +21 -0
- package/dist/components/RevealToggle.vue.js +4 -0
- package/dist/components/StepConnector.vue.d.ts +19 -0
- package/dist/components/StepConnector.vue.js +4 -0
- package/dist/components/TourCard.vue.js +1 -1
- package/dist/components/badge.d.ts +1 -0
- package/dist/components/badge.js +4 -0
- package/dist/components/chart.d.ts +5 -1
- package/dist/components/chart.js +2 -21
- package/dist/components/dateRange.d.ts +11 -0
- package/dist/components/dateRange.js +12 -0
- package/dist/components/dock.d.ts +19 -0
- package/dist/components/dock.js +16 -0
- package/dist/components/dropdown.d.ts +10 -1
- package/dist/components/dropdown.js +7 -2
- package/dist/components/fieldText.d.ts +38 -0
- package/dist/components/fieldText.js +53 -0
- package/dist/components/fileTree.d.ts +7 -3
- package/dist/components/fileTree.js +12 -18
- package/dist/components/format.d.ts +41 -0
- package/dist/components/format.js +67 -0
- package/dist/components/identicon.d.ts +38 -0
- package/dist/components/identicon.js +85 -0
- package/dist/components/masonry.d.ts +32 -0
- package/dist/components/masonry.js +40 -0
- package/dist/components/menuWhen.js +1 -1
- package/dist/components/meter.d.ts +64 -0
- package/dist/components/meter.js +53 -0
- package/dist/components/otp.d.ts +1 -1
- package/dist/components/otp.js +7 -2
- package/dist/components/pipeline.d.ts +55 -0
- package/dist/components/pipeline.js +51 -0
- package/dist/components/quota.d.ts +57 -0
- package/dist/components/quota.js +40 -0
- package/dist/components/relativeTime.d.ts +24 -0
- package/dist/components/relativeTime.js +56 -0
- package/dist/components/reveal.d.ts +18 -0
- package/dist/components/reveal.js +32 -0
- package/dist/components/scramble.d.ts +68 -0
- package/dist/components/scramble.js +58 -0
- package/dist/components/shaderBackdrop.d.ts +20 -0
- package/dist/components/shaderBackdrop.js +156 -0
- package/dist/components/slider.d.ts +5 -0
- package/dist/components/slider.js +24 -0
- package/dist/components/slotText.d.ts +2 -0
- package/dist/components/slotText.js +9 -0
- package/dist/components/status.d.ts +14 -0
- package/dist/components/status.js +22 -0
- package/dist/components/tableFacets.d.ts +8 -0
- package/dist/components/tableFacets.js +19 -0
- package/dist/components/textSplit.d.ts +17 -0
- package/dist/components/textSplit.js +24 -0
- package/dist/components/timeSpan.d.ts +78 -0
- package/dist/components/timeSpan.js +85 -0
- package/dist/components/tone.d.ts +43 -0
- package/dist/components/tone.js +82 -0
- package/dist/components/tour.js +3 -2
- package/dist/components/typewriter.d.ts +36 -0
- package/dist/components/typewriter.js +53 -0
- package/dist/components/videoBackdrop.js +5 -6
- package/dist/composables/easing.d.ts +22 -1
- package/dist/composables/easing.js +42 -1
- package/dist/composables/pseudoLocale.d.ts +15 -0
- package/dist/composables/pseudoLocale.js +96 -0
- package/dist/composables/scroll.js +5 -3
- package/dist/composables/useClipboard.d.ts +4 -1
- package/dist/composables/useClipboard.js +12 -2
- package/dist/composables/useField.d.ts +13 -0
- package/dist/composables/useField.js +26 -1
- package/dist/composables/useForm.js +32 -9
- package/dist/composables/useInView.d.ts +11 -0
- package/dist/composables/useInView.js +39 -0
- package/dist/composables/useLoadingGate.d.ts +8 -0
- package/dist/composables/useLoadingGate.js +38 -0
- package/dist/composables/useLpConfig.d.ts +10 -0
- package/dist/composables/useLpConfig.js +11 -1
- package/dist/composables/useMediaQuery.js +3 -1
- package/dist/composables/useNow.d.ts +16 -0
- package/dist/composables/useNow.js +39 -0
- package/dist/composables/usePersisted.js +14 -7
- package/dist/composables/usePillTransition.d.ts +5 -2
- package/dist/composables/usePillTransition.js +4 -3
- package/dist/composables/usePointerFrame.d.ts +18 -0
- package/dist/composables/usePointerFrame.js +31 -0
- package/dist/composables/useReducedMotion.d.ts +2 -0
- package/dist/composables/useReducedMotion.js +7 -0
- package/dist/composables/useRepeater.d.ts +17 -0
- package/dist/composables/useRepeater.js +31 -0
- package/dist/composables/useResizeObserver.d.ts +8 -0
- package/dist/composables/useResizeObserver.js +33 -0
- package/dist/composables/useRevealedValue.d.ts +31 -0
- package/dist/composables/useRevealedValue.js +26 -0
- package/dist/composables/useScramble.d.ts +42 -0
- package/dist/composables/useScramble.js +76 -0
- package/dist/composables/useShift.js +2 -10
- package/dist/composables/useSpotlight.d.ts +18 -0
- package/dist/composables/useSpotlight.js +53 -0
- package/dist/composables/useTilt.js +26 -18
- package/dist/composables/useTween.d.ts +43 -0
- package/dist/composables/useTween.js +69 -0
- package/dist/composables/useView.d.ts +18 -0
- package/dist/composables/useView.js +64 -0
- package/dist/icons-all.js +1 -1
- package/dist/index.d.ts +98 -1
- package/dist/index.js +263 -105
- package/dist/theme/define.d.ts +12 -0
- package/dist/theme/studio.d.ts +90 -0
- package/package.json +8 -2
- package/src/component-names.ts +33 -0
- package/src/components/AttentionDot.vue +27 -0
- package/src/components/ContextMenuItems.vue +5 -2
- package/src/components/FieldWrapper.vue +38 -0
- package/src/components/LayoutNode.vue +5 -9
- package/src/components/LegendSwatch.vue +15 -0
- package/src/components/LoaderMark.vue +90 -0
- package/src/components/LpActionBar.vue +12 -20
- package/src/components/LpActivityLine.vue +186 -0
- package/src/components/LpAlert.vue +54 -24
- package/src/components/LpAppShell.vue +7 -11
- package/src/components/LpAutocomplete.vue +16 -14
- package/src/components/LpAvatar.vue +17 -2
- package/src/components/LpBadge.vue +77 -13
- package/src/components/LpBreadcrumbs.vue +101 -35
- package/src/components/LpButton.vue +15 -3
- package/src/components/LpCard.vue +30 -7
- package/src/components/LpChart.vue +162 -171
- package/src/components/LpCheckbox.vue +10 -4
- package/src/components/LpCodeBlock.vue +11 -19
- package/src/components/LpCodeDisplay.vue +228 -0
- package/src/components/LpCollapse.vue +162 -0
- package/src/components/LpColorPicker.vue +10 -1
- package/src/components/LpCommandPalette.vue +38 -24
- package/src/components/LpContextMenu.vue +13 -3
- package/src/components/LpCopyButton.vue +185 -0
- package/src/components/LpCountdown.vue +308 -0
- package/src/components/LpDataList.vue +154 -0
- package/src/components/LpDatePicker.vue +55 -104
- package/src/components/LpDateRangePicker.vue +51 -95
- package/src/components/LpDisclosure.vue +58 -36
- package/src/components/LpDock.vue +274 -0
- package/src/components/LpDrawer.vue +10 -1
- package/src/components/LpDropdownMenu.vue +11 -2
- package/src/components/LpDropzone.vue +34 -13
- package/src/components/LpEditable.vue +295 -0
- package/src/components/LpFileTree.vue +5 -1
- package/src/components/LpFileTreeNode.vue +25 -50
- package/src/components/LpFormField.vue +22 -3
- package/src/components/LpFrame.vue +200 -0
- package/src/components/LpGauge.vue +287 -0
- package/src/components/LpGlowBorder.vue +60 -0
- package/src/components/LpHintExample.vue +9 -32
- package/src/components/LpHintLayer.vue +13 -3
- package/src/components/LpHoverCard.vue +175 -0
- package/src/components/LpIdenticon.vue +69 -0
- package/src/components/LpImageEditor.vue +15 -18
- package/src/components/LpInfraNode.vue +27 -16
- package/src/components/LpInput.vue +30 -16
- package/src/components/LpLightbox.vue +44 -23
- package/src/components/LpLoader.vue +145 -0
- package/src/components/LpLogViewer.vue +311 -82
- package/src/components/LpMagnetic.vue +97 -0
- package/src/components/LpMapPicker.vue +26 -28
- package/src/components/LpMasonry.vue +247 -0
- package/src/components/LpModal.vue +10 -1
- package/src/components/LpNotificationBell.vue +33 -49
- package/src/components/LpNumberField.vue +79 -14
- package/src/components/LpNumberFlow.vue +10 -35
- package/src/components/LpOtpInput.vue +7 -2
- package/src/components/LpPagination.vue +38 -4
- package/src/components/LpPalettePreview.vue +2 -7
- package/src/components/LpPasswordInput.vue +25 -18
- package/src/components/LpPhoneInput.vue +12 -3
- package/src/components/LpPie.vue +197 -36
- package/src/components/LpPipeline.vue +366 -0
- package/src/components/LpPopover.vue +14 -1
- package/src/components/LpProgress.vue +22 -84
- package/src/components/LpQuota.vue +197 -0
- package/src/components/LpRangeSlider.vue +5 -1
- package/src/components/LpRelativeTime.vue +90 -0
- package/src/components/LpRepeater.vue +131 -0
- package/src/components/LpRevealOnScroll.vue +125 -0
- package/src/components/LpRollingText.vue +12 -1
- package/src/components/LpScramble.vue +107 -0
- package/src/components/LpSecretField.vue +320 -0
- package/src/components/LpSegmented.vue +3 -10
- package/src/components/LpSelect.vue +74 -65
- package/src/components/LpShaderBackdrop.vue +434 -0
- package/src/components/LpShinyText.vue +139 -0
- package/src/components/LpSidebar.vue +2 -0
- package/src/components/LpSlider.vue +5 -1
- package/src/components/LpSparkline.vue +55 -37
- package/src/components/LpSpotlight.vue +51 -0
- package/src/components/LpStat.vue +6 -2
- package/src/components/LpStatus.vue +127 -0
- package/src/components/LpStepMark.vue +138 -0
- package/src/components/LpStepper.vue +37 -43
- package/src/components/LpSwap.vue +2 -2
- package/src/components/LpSwitch.vue +15 -22
- package/src/components/LpTable.vue +47 -63
- package/src/components/LpTableFilter.vue +1 -0
- package/src/components/LpTableOfContents.vue +53 -29
- package/src/components/LpTabs.vue +10 -21
- package/src/components/LpTagsInput.vue +53 -37
- package/src/components/LpTextReveal.vue +236 -0
- package/src/components/LpTextarea.vue +11 -11
- package/src/components/LpThemeStudio.vue +216 -0
- package/src/components/LpThemeStudioSection.vue +16 -0
- package/src/components/LpThemeSwitcher.vue +156 -47
- package/src/components/LpTimeField.vue +9 -1
- package/src/components/LpToaster.vue +63 -83
- package/src/components/LpTooltip.vue +10 -1
- package/src/components/LpTour.vue +24 -13
- package/src/components/LpTruncate.vue +135 -0
- package/src/components/LpTypewriter.vue +236 -0
- package/src/components/LpUptimeBar.vue +6 -5
- package/src/components/LpVideoBackdrop.vue +15 -13
- package/src/components/OptionContent.vue +17 -0
- package/src/components/PickerField.vue +59 -0
- package/src/components/RevealToggle.vue +51 -0
- package/src/components/StepConnector.vue +54 -0
- package/src/components/TourCard.vue +29 -16
- package/src/components/badge.ts +8 -0
- package/src/components/chart.ts +8 -23
- package/src/components/dateRange.ts +25 -0
- package/src/components/dock.ts +31 -0
- package/src/components/dropdown.ts +15 -3
- package/src/components/fieldText.ts +95 -0
- package/src/components/fileTree.ts +19 -21
- package/src/components/format.ts +136 -0
- package/src/components/identicon.ts +160 -0
- package/src/components/masonry.ts +73 -0
- package/src/components/meter.ts +122 -0
- package/src/components/otp.ts +14 -3
- package/src/components/pipeline.ts +101 -0
- package/src/components/quota.ts +111 -0
- package/src/components/relativeTime.ts +118 -0
- package/src/components/reveal.ts +56 -0
- package/src/components/scramble.ts +138 -0
- package/src/components/shaderBackdrop.ts +208 -0
- package/src/components/slider.ts +30 -0
- package/src/components/slotText.ts +13 -0
- package/src/components/status.ts +46 -0
- package/src/components/tableFacets.ts +25 -0
- package/src/components/textSplit.ts +55 -0
- package/src/components/timeSpan.ts +183 -0
- package/src/components/tone.ts +144 -0
- package/src/components/tour.ts +3 -2
- package/src/components/typewriter.ts +104 -0
- package/src/components/videoBackdrop.ts +7 -6
- package/src/composables/easing.ts +66 -1
- package/src/composables/pseudoLocale.ts +105 -0
- package/src/composables/scroll.ts +6 -3
- package/src/composables/useClipboard.ts +25 -3
- package/src/composables/useField.ts +38 -1
- package/src/composables/useForm.ts +41 -11
- package/src/composables/useInView.ts +61 -0
- package/src/composables/useLoadingGate.ts +63 -0
- package/src/composables/useLpConfig.ts +20 -1
- package/src/composables/useMediaQuery.ts +6 -2
- package/src/composables/useNow.ts +84 -0
- package/src/composables/usePersisted.ts +19 -8
- package/src/composables/usePillTransition.ts +21 -13
- package/src/composables/usePointerFrame.ts +72 -0
- package/src/composables/useReducedMotion.ts +22 -0
- package/src/composables/useRepeater.ts +44 -0
- package/src/composables/useResizeObserver.ts +59 -0
- package/src/composables/useRevealedValue.ts +84 -0
- package/src/composables/useScramble.ts +152 -0
- package/src/composables/useShift.ts +10 -16
- package/src/composables/useSpotlight.ts +82 -0
- package/src/composables/useTilt.ts +40 -23
- package/src/composables/useTween.ts +149 -0
- package/src/composables/useView.ts +96 -0
- package/src/index.css +1 -0
- package/src/index.ts +143 -1
- package/src/kit-icons.ts +1 -1
- package/src/theme/define.ts +65 -0
- package/src/theme/studio.ts +202 -0
- package/src/tokens/effects.css +151 -0
- package/src/tokens/tokens.css +131 -39
- package/src/tokens/utilities.css +81 -0
- package/dist/assets/LpTabs-Bk7v4bZz.css +0 -18
- package/dist/chunks/LpAlert-BdvxvcWE.js +0 -85
- package/dist/chunks/LpBadge-Fe0D1bYx.js +0 -62
- package/dist/chunks/LpBreadcrumbs-rzOKkLiI.js +0 -98
- package/dist/chunks/LpDatePicker-BMfM454Y.js +0 -243
- package/dist/chunks/LpDateRangePicker-CNb9X3eL.js +0 -195
- package/dist/chunks/LpNumberField-DO1ufKvE.js +0 -107
- package/dist/chunks/LpStepper-C2slwObu.js +0 -90
- package/dist/chunks/LpSwitch-CZy9_IoA.js +0 -132
- package/dist/chunks/LpThemeSwitcher-CDoziW0z.js +0 -166
- package/dist/chunks/TourCard-BKI7X9-r.js +0 -105
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { CHART_PALETTE } from "./chart"
|
|
2
|
+
import { formatBytes } from "./format"
|
|
3
|
+
import { capacityThresholds, toneAt, type MeterTone } from "./meter"
|
|
4
|
+
|
|
5
|
+
/*
|
|
6
|
+
* The arithmetic behind LpQuota, kept free of Vue so it can be tested and so an
|
|
7
|
+
* app that only needs "how much is left, and how worried should I be" can ask
|
|
8
|
+
* it directly.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/** One part of what is used, for a stacked bar ("Images 12 GB, Backups 30 GB"). */
|
|
12
|
+
export interface QuotaPart {
|
|
13
|
+
label: string
|
|
14
|
+
value: number
|
|
15
|
+
/** Any CSS colour. Unset parts take the chart palette in order, like LpChart's series (minus danger). */
|
|
16
|
+
color?: string
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* How amounts are written. `"bytes"` scales to the largest whole unit (1 536 →
|
|
21
|
+
* 1.5 KB) in binary steps, the way storage limits are sold and shown; any
|
|
22
|
+
* other string is a plain suffix ("seats", "req/min"); a function says it its
|
|
23
|
+
* own way.
|
|
24
|
+
*/
|
|
25
|
+
export type QuotaUnit = "bytes" | string | ((value: number) => string)
|
|
26
|
+
|
|
27
|
+
export interface QuotaState {
|
|
28
|
+
/** Share of the limit used, 0–1 and beyond: 1.25 is a quarter over. */
|
|
29
|
+
ratio: number
|
|
30
|
+
/** What is left; zero once over. */
|
|
31
|
+
remaining: number
|
|
32
|
+
/** By how much the limit is exceeded; zero within it. */
|
|
33
|
+
over: number
|
|
34
|
+
/** False when there is no limit to measure against (unset, zero, Infinity). */
|
|
35
|
+
limited: boolean
|
|
36
|
+
tone: MeterTone
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Where a quota stands. Thresholds are shares of the limit, read on the same
|
|
41
|
+
* capacity scale (and with the same defaults) as LpProgress's `auto` tone and
|
|
42
|
+
* LpGauge, so a quota and a gauge of one disk turn red together; anything past
|
|
43
|
+
* the limit is danger whatever they say, because "over" is the one state a
|
|
44
|
+
* quota must never paint as merely amber.
|
|
45
|
+
*/
|
|
46
|
+
export function quotaState(
|
|
47
|
+
used: number,
|
|
48
|
+
limit: number | null | undefined,
|
|
49
|
+
warnAt = 0.8,
|
|
50
|
+
dangerAt = 0.9,
|
|
51
|
+
): QuotaState {
|
|
52
|
+
const safeUsed = Number.isFinite(used) ? Math.max(0, used) : 0
|
|
53
|
+
const limited = limit != null && Number.isFinite(limit) && limit > 0
|
|
54
|
+
if (!limited) return { ratio: 0, remaining: Infinity, over: 0, limited, tone: "ok" }
|
|
55
|
+
const ratio = safeUsed / limit
|
|
56
|
+
const tone = ratio > 1 ? "danger" : toneAt(ratio, capacityThresholds(0, 1, warnAt, dangerAt))
|
|
57
|
+
return {
|
|
58
|
+
ratio,
|
|
59
|
+
remaining: Math.max(0, limit - safeUsed),
|
|
60
|
+
over: Math.max(0, safeUsed - limit),
|
|
61
|
+
limited,
|
|
62
|
+
tone,
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* An amount in its unit, locale-aware. Bytes go through `formatBytes`, whose
|
|
68
|
+
* unit names come from Intl, so a Russian app reads "1,5 ГБ" without a
|
|
69
|
+
* translation table of its own.
|
|
70
|
+
*/
|
|
71
|
+
export function formatAmount(value: number, unit?: QuotaUnit, locale?: string): string {
|
|
72
|
+
if (typeof unit === "function") return unit(value)
|
|
73
|
+
if (unit === "bytes") return formatBytes(Math.max(0, value), locale)
|
|
74
|
+
const number = new Intl.NumberFormat(locale, { maximumFractionDigits: value < 10 ? 1 : 0 }).format(value)
|
|
75
|
+
return unit ? `${number} ${unit}` : number
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/*
|
|
79
|
+
* Parts are categories, not verdicts, so the palette leaves out danger: a red
|
|
80
|
+
* "Backups" slice would read as an alarm about backups.
|
|
81
|
+
*/
|
|
82
|
+
const PART_PALETTE = CHART_PALETTE.filter((c) => !c.includes("danger"))
|
|
83
|
+
|
|
84
|
+
export interface QuotaSegment extends QuotaPart {
|
|
85
|
+
color: string
|
|
86
|
+
/** Share of the bar's width, 0–1. */
|
|
87
|
+
share: number
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* The parts laid out along the bar. Within the limit the bar's width IS the
|
|
92
|
+
* limit, so a part's share is its value over the limit and the rest of the
|
|
93
|
+
* track is what remains. Over it, the bar stretches to hold everything used and
|
|
94
|
+
* the limit becomes a mark partway along (`limitAt`), so the overflow reads as
|
|
95
|
+
* the part of the bar beyond the line rather than as a bar that simply stopped
|
|
96
|
+
* at full.
|
|
97
|
+
*/
|
|
98
|
+
export function quotaSegments(
|
|
99
|
+
parts: readonly QuotaPart[],
|
|
100
|
+
limit: number | null | undefined,
|
|
101
|
+
): { segments: QuotaSegment[]; limitAt: number | null } {
|
|
102
|
+
const total = parts.reduce((sum, p) => sum + Math.max(0, p.value), 0)
|
|
103
|
+
const limited = limit != null && Number.isFinite(limit) && limit > 0
|
|
104
|
+
const scale = limited ? Math.max(limit, total) : total
|
|
105
|
+
const segments = parts.map((p, i) => ({
|
|
106
|
+
...p,
|
|
107
|
+
color: p.color ?? PART_PALETTE[i % PART_PALETTE.length],
|
|
108
|
+
share: scale > 0 ? Math.max(0, p.value) / scale : 0,
|
|
109
|
+
}))
|
|
110
|
+
return { segments, limitAt: limited && total > limit ? limit / total : null }
|
|
111
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { toEpoch } from "./timeSpan"
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
* "3 min ago" / "in 2 h": the wording, and when it next changes.
|
|
5
|
+
*
|
|
6
|
+
* Pure, so it can be tested without a clock and so a non-Vue caller (a toast,
|
|
7
|
+
* a log line) can say a date the same way LpRelativeTime does.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export type RelativeUnit = "second" | "minute" | "hour" | "day" | "week" | "month" | "year"
|
|
11
|
+
|
|
12
|
+
const SECOND = 1000
|
|
13
|
+
const MINUTE = 60 * SECOND
|
|
14
|
+
const HOUR = 60 * MINUTE
|
|
15
|
+
const DAY = 24 * HOUR
|
|
16
|
+
const WEEK = 7 * DAY
|
|
17
|
+
const MONTH = 30.4375 * DAY
|
|
18
|
+
const YEAR = 365.25 * DAY
|
|
19
|
+
|
|
20
|
+
/*
|
|
21
|
+
* Each unit holds until the next one reads better. The cut-offs sit before the
|
|
22
|
+
* full next unit on purpose: "50 minutes ago" is fussier than "1 hour ago",
|
|
23
|
+
* and nobody says "6 days ago" about a date that was a week ago.
|
|
24
|
+
*/
|
|
25
|
+
const SCALE: { unit: RelativeUnit; ms: number; until: number }[] = [
|
|
26
|
+
{ unit: "second", ms: SECOND, until: 45 * SECOND },
|
|
27
|
+
{ unit: "minute", ms: MINUTE, until: 45 * MINUTE },
|
|
28
|
+
{ unit: "hour", ms: HOUR, until: 22 * HOUR },
|
|
29
|
+
{ unit: "day", ms: DAY, until: 6.5 * DAY },
|
|
30
|
+
{ unit: "week", ms: WEEK, until: 26 * DAY },
|
|
31
|
+
{ unit: "month", ms: MONTH, until: 320 * DAY },
|
|
32
|
+
{ unit: "year", ms: YEAR, until: Infinity },
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
export interface RelativeTimeOptions {
|
|
36
|
+
locale?: string
|
|
37
|
+
/** Intl's width: `long` "3 minutes ago", `short` "3 min. ago", `narrow` "3m ago". */
|
|
38
|
+
style?: Intl.RelativeTimeFormatStyle
|
|
39
|
+
/**
|
|
40
|
+
* Under this many ms either side of now, say "now" instead of counting
|
|
41
|
+
* seconds. Counting seconds is churn nobody reads; a countdown that needs
|
|
42
|
+
* them wants a timer, not this.
|
|
43
|
+
*/
|
|
44
|
+
nowWithin?: number
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface RelativeTime {
|
|
48
|
+
text: string
|
|
49
|
+
unit: RelativeUnit
|
|
50
|
+
/** Signed: negative is the past. */
|
|
51
|
+
value: number
|
|
52
|
+
/**
|
|
53
|
+
* Milliseconds until `text` would read differently. A ticker sleeps this
|
|
54
|
+
* long instead of waking every second for a date three weeks away.
|
|
55
|
+
*/
|
|
56
|
+
refreshIn: number
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const formatters = new Map<string, Intl.RelativeTimeFormat>()
|
|
60
|
+
function formatterFor(locale: string | undefined, style: Intl.RelativeTimeFormatStyle) {
|
|
61
|
+
const key = `${locale ?? ""}|${style}`
|
|
62
|
+
let f = formatters.get(key)
|
|
63
|
+
if (!f) {
|
|
64
|
+
f = new Intl.RelativeTimeFormat(locale, { numeric: "auto", style })
|
|
65
|
+
formatters.set(key, f)
|
|
66
|
+
}
|
|
67
|
+
return f
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** Never sleep longer than this: a laptop waking from sleep should not show "2 h ago" for an hour. */
|
|
71
|
+
const MAX_REFRESH = HOUR
|
|
72
|
+
const MIN_REFRESH = SECOND
|
|
73
|
+
|
|
74
|
+
export function formatRelativeTime(
|
|
75
|
+
date: Date | string | number,
|
|
76
|
+
now: number = Date.now(),
|
|
77
|
+
options: RelativeTimeOptions = {},
|
|
78
|
+
): RelativeTime {
|
|
79
|
+
const { locale, style = "long", nowWithin = 45 * SECOND } = options
|
|
80
|
+
const at = toEpoch(date)
|
|
81
|
+
const fmt = formatterFor(locale, style)
|
|
82
|
+
if (!Number.isFinite(at)) return { text: "", unit: "second", value: 0, refreshIn: MAX_REFRESH }
|
|
83
|
+
|
|
84
|
+
const diff = at - now
|
|
85
|
+
const abs = Math.abs(diff)
|
|
86
|
+
|
|
87
|
+
if (abs < nowWithin) {
|
|
88
|
+
// "now" holds until the date is `nowWithin` away on the far side; a future
|
|
89
|
+
// date crossing into the past stays "now" all the way through.
|
|
90
|
+
const refreshIn = diff >= 0 ? diff + nowWithin : nowWithin - abs
|
|
91
|
+
return { text: fmt.format(0, "second"), unit: "second", value: 0, refreshIn: clampRefresh(refreshIn) }
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const index = SCALE.findIndex((s) => abs < s.until)
|
|
95
|
+
const step = SCALE[index]
|
|
96
|
+
const count = Math.max(1, Math.round(abs / step.ms))
|
|
97
|
+
const value = diff < 0 ? -count : count
|
|
98
|
+
|
|
99
|
+
/*
|
|
100
|
+
* The next moment the rounded count changes. In the past the distance only
|
|
101
|
+
* grows, so that is the next half-unit boundary above it; in the future it
|
|
102
|
+
* only shrinks, so the next one below. Crossing into another unit happens on
|
|
103
|
+
* a boundary of the smaller unit, so this also catches "45 min" → "1 hour".
|
|
104
|
+
*/
|
|
105
|
+
const units = abs / step.ms
|
|
106
|
+
const boundary = diff < 0 ? (Math.floor(units + 0.5) + 0.5) * step.ms : (Math.ceil(units - 0.5) - 0.5) * step.ms
|
|
107
|
+
// A unit change can also come before the next rounding step: a past date
|
|
108
|
+
// grows into the next unit at this one's `until`, a future one shrinks into
|
|
109
|
+
// the previous unit at that one's.
|
|
110
|
+
const unitChange = diff < 0 ? step.until - abs : index > 0 ? abs - SCALE[index - 1].until : Infinity
|
|
111
|
+
const refreshIn = Math.min(diff < 0 ? boundary - abs : abs - boundary, unitChange)
|
|
112
|
+
|
|
113
|
+
return { text: fmt.format(value, step.unit), unit: step.unit, value, refreshIn: clampRefresh(refreshIn) }
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function clampRefresh(ms: number): number {
|
|
117
|
+
return Math.min(MAX_REFRESH, Math.max(MIN_REFRESH, Math.ceil(ms)))
|
|
118
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Keyframes and timing for LpRevealOnScroll, kept apart from the component so
|
|
3
|
+
* they can be tested without a DOM.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export type RevealPreset = "fade" | "rise" | "blur" | "scale"
|
|
7
|
+
export type RevealDirection = "up" | "down" | "left" | "right"
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* The hidden state an entrance starts from; the end state is always "as laid
|
|
11
|
+
* out", so the keyframes only describe where the content comes FROM.
|
|
12
|
+
*
|
|
13
|
+
* `direction` is the way the content travels: `up` starts below and rises.
|
|
14
|
+
* Every preset fades — motion without a fade reads as a layout jump.
|
|
15
|
+
*/
|
|
16
|
+
export function revealFrom(
|
|
17
|
+
preset: RevealPreset,
|
|
18
|
+
direction: RevealDirection,
|
|
19
|
+
distance: number,
|
|
20
|
+
): Keyframe {
|
|
21
|
+
const offset = (d: number) =>
|
|
22
|
+
({
|
|
23
|
+
up: `translate3d(0, ${d}px, 0)`,
|
|
24
|
+
down: `translate3d(0, ${-d}px, 0)`,
|
|
25
|
+
left: `translate3d(${d}px, 0, 0)`,
|
|
26
|
+
right: `translate3d(${-d}px, 0, 0)`,
|
|
27
|
+
})[direction]
|
|
28
|
+
switch (preset) {
|
|
29
|
+
case "fade":
|
|
30
|
+
return { opacity: 0 }
|
|
31
|
+
case "rise":
|
|
32
|
+
return { opacity: 0, transform: offset(distance) }
|
|
33
|
+
case "blur":
|
|
34
|
+
// A short travel with the blur: blur alone looks like a focus problem.
|
|
35
|
+
return { opacity: 0, filter: "blur(8px)", transform: offset(distance / 2) }
|
|
36
|
+
case "scale":
|
|
37
|
+
return { opacity: 0, transform: "scale(0.94)" }
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** The resting state matching `revealFrom`'s properties. */
|
|
42
|
+
export function revealTo(from: Keyframe): Keyframe {
|
|
43
|
+
const to: Keyframe = { opacity: 1 }
|
|
44
|
+
if ("transform" in from) to.transform = "none"
|
|
45
|
+
if ("filter" in from) to.filter = "blur(0px)"
|
|
46
|
+
return to
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Delay of the `index`-th child. The steps stop growing after `cap` children:
|
|
51
|
+
* a list of eighty would otherwise take seconds to finish arriving, and the
|
|
52
|
+
* last rows would still be invisible when the user starts reading them.
|
|
53
|
+
*/
|
|
54
|
+
export function staggerDelay(index: number, delay: number, stagger: number, cap = 10): number {
|
|
55
|
+
return delay + Math.min(Math.max(0, index), cap) * Math.max(0, stagger)
|
|
56
|
+
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Frame generation for the "decrypt" effect (LpScramble, useScramble).
|
|
3
|
+
*
|
|
4
|
+
* Kept pure and clock-free so it can be tested frame by frame: a plan says WHEN
|
|
5
|
+
* each position settles, and a frame is the text at a given moment. The caller
|
|
6
|
+
* owns time and randomness — the composable feeds `performance.now()` and
|
|
7
|
+
* `Math.random`, a test feeds fixed numbers and a seeded generator and gets the
|
|
8
|
+
* same frames every run.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/** Random number in [0, 1). Injected so frames are reproducible under test. */
|
|
12
|
+
export type Random = () => number
|
|
13
|
+
|
|
14
|
+
/** Glyphs a position cycles through before it settles. Upper case and digits read as "code". */
|
|
15
|
+
export const SCRAMBLE_GLYPHS = "ABCDEFGHJKLMNPQRSTUVWXYZ0123456789#%&*+=?@$"
|
|
16
|
+
|
|
17
|
+
export interface ScrambleOptions {
|
|
18
|
+
/** Total travel time in ms, from the first glyph to the last position settling. */
|
|
19
|
+
duration: number
|
|
20
|
+
/**
|
|
21
|
+
* `sequential` settles left to right, which reads as decoding; `together`
|
|
22
|
+
* holds every position until near the end and lands them in a short burst,
|
|
23
|
+
* which reads as a lock opening.
|
|
24
|
+
*/
|
|
25
|
+
order?: "sequential" | "together"
|
|
26
|
+
/** Glyph pool. */
|
|
27
|
+
glyphs?: string
|
|
28
|
+
/**
|
|
29
|
+
* Characters that never scramble: a space or a dash in a key keeps the
|
|
30
|
+
* grouping visible while the rest churns, so the reader sees the shape of
|
|
31
|
+
* what is coming.
|
|
32
|
+
*/
|
|
33
|
+
keep?: (char: string) => boolean
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface ScrambleCell {
|
|
37
|
+
/** The character settled into, or "" for a position the new value no longer has. */
|
|
38
|
+
final: string
|
|
39
|
+
/** The character shown before the run started (sizes a position the value is losing). */
|
|
40
|
+
initial: string
|
|
41
|
+
/** Ms from the start of the run when this position shows `final`. */
|
|
42
|
+
settleAt: number
|
|
43
|
+
/** Never churns (see `keep`). */
|
|
44
|
+
fixed: boolean
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface ScrambleFrameCell {
|
|
48
|
+
/** What to draw now. */
|
|
49
|
+
char: string
|
|
50
|
+
/** The character that gives the position its width: the final one, or the old one while it is leaving. */
|
|
51
|
+
sizer: string
|
|
52
|
+
settled: boolean
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const defaultKeep = (char: string) => /\s/.test(char)
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Splits into user-perceived characters, so an emoji or an accented letter is
|
|
59
|
+
* one position rather than two halves that scramble separately.
|
|
60
|
+
*/
|
|
61
|
+
export function graphemes(text: string): string[] {
|
|
62
|
+
const Segmenter = (Intl as { Segmenter?: typeof Intl.Segmenter }).Segmenter
|
|
63
|
+
if (Segmenter) return Array.from(new Segmenter(undefined, { granularity: "grapheme" }).segment(text), (s) => s.segment)
|
|
64
|
+
return Array.from(text)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* When each position stops churning (all of them start at once).
|
|
69
|
+
*
|
|
70
|
+
* `sequential`: positions start together and settle on a ramp across the last
|
|
71
|
+
* 70% of the run, so the left of the value is readable while the right still
|
|
72
|
+
* moves. `together`: every position settles within the last 15%, with a
|
|
73
|
+
* little random spread so they do not land as one flat click.
|
|
74
|
+
*/
|
|
75
|
+
export function planScramble(from: string, to: string, options: ScrambleOptions, random: Random = Math.random): ScrambleCell[] {
|
|
76
|
+
const keep = options.keep ?? defaultKeep
|
|
77
|
+
const a = graphemes(from)
|
|
78
|
+
const b = graphemes(to)
|
|
79
|
+
const length = Math.max(a.length, b.length)
|
|
80
|
+
const duration = Math.max(0, options.duration)
|
|
81
|
+
const cells: ScrambleCell[] = []
|
|
82
|
+
for (let i = 0; i < length; i++) {
|
|
83
|
+
const final = b[i] ?? ""
|
|
84
|
+
const initial = a[i] ?? ""
|
|
85
|
+
let settleAt: number
|
|
86
|
+
if (options.order === "together") settleAt = duration * (0.85 + 0.15 * random())
|
|
87
|
+
else settleAt = length === 1 ? duration : duration * (0.3 + (0.7 * i) / (length - 1))
|
|
88
|
+
// A kept character (a space in a grouped key) stays put when it is there
|
|
89
|
+
// before and after, so the grouping holds while the rest churns.
|
|
90
|
+
const fixed = final !== "" && keep(final) && (initial === final || initial === "")
|
|
91
|
+
cells.push({ final, initial, settleAt: fixed ? 0 : settleAt, fixed })
|
|
92
|
+
}
|
|
93
|
+
return cells
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** A glyph from the pool. */
|
|
97
|
+
export function randomGlyph(random: Random, glyphs: string = SCRAMBLE_GLYPHS): string {
|
|
98
|
+
const pool = graphemes(glyphs)
|
|
99
|
+
return pool[Math.min(pool.length - 1, Math.floor(random() * pool.length))] ?? ""
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* The text at `elapsed` ms into the run. A churning position draws a fresh
|
|
104
|
+
* random glyph each call; the caller decides how often to call (the glyph
|
|
105
|
+
* re-roll rate), not this.
|
|
106
|
+
*/
|
|
107
|
+
export function scrambleFrame(
|
|
108
|
+
cells: ScrambleCell[],
|
|
109
|
+
elapsed: number,
|
|
110
|
+
random: Random = Math.random,
|
|
111
|
+
glyphs: string = SCRAMBLE_GLYPHS,
|
|
112
|
+
): ScrambleFrameCell[] {
|
|
113
|
+
return cells.map((cell) => {
|
|
114
|
+
const sizer = cell.final || cell.initial
|
|
115
|
+
if (cell.fixed || elapsed >= cell.settleAt) return { char: cell.final, sizer: cell.final, settled: true }
|
|
116
|
+
return { char: randomGlyph(random, glyphs), sizer, settled: false }
|
|
117
|
+
})
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/** The cells of a value at rest, for the first paint and for reduced motion. */
|
|
121
|
+
export function settledCells(text: string): ScrambleFrameCell[] {
|
|
122
|
+
return graphemes(text).map((char) => ({ char, sizer: char, settled: true }))
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* A small seeded generator (mulberry32), for tests and for a scramble that
|
|
127
|
+
* should look the same every time it plays (a demo, a screenshot).
|
|
128
|
+
*/
|
|
129
|
+
export function seededRandom(seed: number): Random {
|
|
130
|
+
let state = seed >>> 0
|
|
131
|
+
return () => {
|
|
132
|
+
state = (state + 0x6d2b79f5) >>> 0
|
|
133
|
+
let t = state
|
|
134
|
+
t = Math.imul(t ^ (t >>> 15), t | 1)
|
|
135
|
+
t ^= t + Math.imul(t ^ (t >>> 7), t | 61)
|
|
136
|
+
return ((t ^ (t >>> 14)) >>> 0) / 4294967296
|
|
137
|
+
}
|
|
138
|
+
}
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* The looks LpShaderBackdrop can draw, and the small decisions around them
|
|
3
|
+
* that are worth testing without a GPU.
|
|
4
|
+
*
|
|
5
|
+
* Every look is a fragment shader over one full-screen triangle, fed the same
|
|
6
|
+
* uniforms: size, time, pixel ratio, intensity, and five theme colours read
|
|
7
|
+
* from the kit's tokens (surface, brand, accent, action, ink). A look never
|
|
8
|
+
* names a colour of its own, so it follows any theme, light or dark.
|
|
9
|
+
*
|
|
10
|
+
* Every look also has a CSS fallback built from the same tokens through
|
|
11
|
+
* `var()`, shown where WebGL is missing, too slow, or unwanted (reduced
|
|
12
|
+
* motion). It is a still that resembles the look, so a page reads the same
|
|
13
|
+
* with or without the GPU, and it is also what shows under the canvas while
|
|
14
|
+
* the first frame is being drawn.
|
|
15
|
+
*
|
|
16
|
+
* The shaders are WebGL 1 / GLSL ES 1.00 and deliberately cheap: a handful of
|
|
17
|
+
* sines and exponentials per pixel, no loops over octaves, no textures. The
|
|
18
|
+
* kit runs inside WebKitGTK, where WebGL may be software-rendered; the soft
|
|
19
|
+
* looks are drawn at half resolution for the same reason.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
export type ShaderPreset = "aurora" | "silk" | "grain" | "dots"
|
|
23
|
+
|
|
24
|
+
export interface PresetDef {
|
|
25
|
+
/** Body of the fragment shader, after the shared header. */
|
|
26
|
+
fragment: string
|
|
27
|
+
/** Rendering resolution relative to CSS pixels × pixel ratio. Soft looks lose nothing at half. */
|
|
28
|
+
scale: number
|
|
29
|
+
/** CSS `background` of the still that stands in for the look. */
|
|
30
|
+
fallback: string
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const VERTEX_SHADER = `
|
|
34
|
+
attribute vec2 p;
|
|
35
|
+
void main() { gl_Position = vec4(p, 0.0, 1.0); }
|
|
36
|
+
`
|
|
37
|
+
|
|
38
|
+
/* Uniforms every look receives. u_bg..u_ink are the theme, in 0–1 sRGB. */
|
|
39
|
+
export const FRAGMENT_HEADER = `
|
|
40
|
+
precision mediump float;
|
|
41
|
+
uniform vec2 u_res;
|
|
42
|
+
uniform float u_time;
|
|
43
|
+
uniform float u_px;
|
|
44
|
+
uniform float u_k;
|
|
45
|
+
uniform vec3 u_bg;
|
|
46
|
+
uniform vec3 u_brand;
|
|
47
|
+
uniform vec3 u_accent;
|
|
48
|
+
uniform vec3 u_action;
|
|
49
|
+
uniform vec3 u_ink;
|
|
50
|
+
`
|
|
51
|
+
|
|
52
|
+
const mix = (token: string, share: number) => `color-mix(in oklab, var(--color-${token}) ${share}%, transparent)`
|
|
53
|
+
|
|
54
|
+
/*
|
|
55
|
+
* Film grain for the CSS still: SVG turbulence, rendered once by the engine
|
|
56
|
+
* as an image. Grey, at low opacity, so it textures any surface colour.
|
|
57
|
+
*/
|
|
58
|
+
const NOISE =
|
|
59
|
+
"url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 0.5 0 0 0 0 0.5 0 0 0 0 0.5 0 0 0 0.35 0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E\")"
|
|
60
|
+
|
|
61
|
+
export const PRESETS: Record<ShaderPreset, PresetDef> = {
|
|
62
|
+
/*
|
|
63
|
+
* Curtains of light across the upper part: each band has a sharp upper
|
|
64
|
+
* edge and a long fall-off below it, waves sideways, and is striated by
|
|
65
|
+
* fine vertical rays that drift.
|
|
66
|
+
*/
|
|
67
|
+
aurora: {
|
|
68
|
+
scale: 0.5,
|
|
69
|
+
fragment: `
|
|
70
|
+
float band(vec2 uv, float x, float off, float sp) {
|
|
71
|
+
float y = 0.66 + off + 0.09 * sin(x * 1.9 + u_time * sp + off * 9.0) + 0.04 * sin(x * 4.7 - u_time * sp * 1.7 + off * 4.0);
|
|
72
|
+
float d = uv.y - y;
|
|
73
|
+
float body = exp(-d * d * (d > 0.0 ? 160.0 : 22.0));
|
|
74
|
+
float rays = 0.86 + 0.14 * sin(x * 70.0 + 6.0 * sin(x * 2.3 + u_time * 0.4));
|
|
75
|
+
return body * rays;
|
|
76
|
+
}
|
|
77
|
+
void main() {
|
|
78
|
+
vec2 uv = gl_FragCoord.xy / u_res;
|
|
79
|
+
float x = gl_FragCoord.x / u_res.y;
|
|
80
|
+
vec3 col = u_bg;
|
|
81
|
+
col = mix(col, u_brand, band(uv, x, 0.0, 0.35) * 0.7 * u_k);
|
|
82
|
+
col = mix(col, u_accent, band(uv, x, -0.18, 0.27) * 0.45 * u_k);
|
|
83
|
+
col = mix(col, u_action, band(uv, x, 0.12, 0.43) * 0.45 * u_k);
|
|
84
|
+
gl_FragColor = vec4(col, 1.0);
|
|
85
|
+
}`,
|
|
86
|
+
fallback: [
|
|
87
|
+
`radial-gradient(70% 38% at 30% 28%, ${mix("brand", 42)}, transparent 70%)`,
|
|
88
|
+
`radial-gradient(55% 30% at 72% 44%, ${mix("accent", 30)}, transparent 70%)`,
|
|
89
|
+
`radial-gradient(50% 26% at 55% 18%, ${mix("action", 26)}, transparent 70%)`,
|
|
90
|
+
].join(", "),
|
|
91
|
+
},
|
|
92
|
+
|
|
93
|
+
/*
|
|
94
|
+
* A sheet of fabric: diagonal folds that slowly ripple, the brand colour
|
|
95
|
+
* in the body and a narrow highlight running along each crest.
|
|
96
|
+
*/
|
|
97
|
+
silk: {
|
|
98
|
+
scale: 0.5,
|
|
99
|
+
fragment: `
|
|
100
|
+
void main() {
|
|
101
|
+
vec2 uv = gl_FragCoord.xy / u_res;
|
|
102
|
+
vec2 p = (gl_FragCoord.xy - 0.5 * u_res) / u_res.y * 2.2;
|
|
103
|
+
float t = u_time * 0.6;
|
|
104
|
+
float f = p.x * 0.9 + p.y * 0.5 + 0.6 * sin(p.y * 1.7 + t) + 0.35 * sin(p.x * 2.3 - t * 0.8 + p.y);
|
|
105
|
+
float fold = sin(f * 3.4);
|
|
106
|
+
float sheen = pow(0.5 + 0.5 * fold, 6.0);
|
|
107
|
+
vec3 tint = mix(u_brand, u_accent, clamp(uv.x * 0.7 + 0.15 * sin(t * 0.5), 0.0, 1.0));
|
|
108
|
+
vec3 col = mix(u_bg, tint, (0.22 + 0.14 * fold) * u_k);
|
|
109
|
+
col += sheen * mix(u_brand, vec3(1.0), 0.4) * 0.28 * u_k;
|
|
110
|
+
gl_FragColor = vec4(col, 1.0);
|
|
111
|
+
}`,
|
|
112
|
+
fallback: [
|
|
113
|
+
`linear-gradient(115deg, transparent 12%, ${mix("brand", 30)} 30%, transparent 46%, ${mix("accent", 22)} 64%, transparent 80%)`,
|
|
114
|
+
`linear-gradient(160deg, ${mix("brand", 16)}, transparent 70%)`,
|
|
115
|
+
].join(", "),
|
|
116
|
+
},
|
|
117
|
+
|
|
118
|
+
/*
|
|
119
|
+
* Two soft colour pools drifting over the surface, under moving film
|
|
120
|
+
* grain. The grain is per device pixel and refreshes a dozen-odd times a
|
|
121
|
+
* second, like film, rather than every frame, which would read as static.
|
|
122
|
+
*/
|
|
123
|
+
grain: {
|
|
124
|
+
scale: 1,
|
|
125
|
+
fragment: `
|
|
126
|
+
float hash(vec2 q) { return fract(sin(dot(q, vec2(12.9898, 78.233))) * 43758.5453); }
|
|
127
|
+
void main() {
|
|
128
|
+
vec2 uv = gl_FragCoord.xy / u_res;
|
|
129
|
+
float aspect = u_res.x / u_res.y;
|
|
130
|
+
vec2 p = vec2(uv.x * aspect, uv.y);
|
|
131
|
+
float t = u_time * 0.25;
|
|
132
|
+
vec2 a = vec2((0.3 + 0.2 * sin(t)) * aspect, 0.7 + 0.15 * cos(t * 1.3));
|
|
133
|
+
vec2 b = vec2((0.75 + 0.15 * cos(t * 0.8)) * aspect, 0.3 + 0.2 * sin(t * 1.1));
|
|
134
|
+
float da = exp(-dot(p - a, p - a) * 3.0);
|
|
135
|
+
float db = exp(-dot(p - b, p - b) * 4.0);
|
|
136
|
+
vec3 col = mix(u_bg, u_brand, da * 0.6 * u_k);
|
|
137
|
+
col = mix(col, u_accent, db * 0.5 * u_k);
|
|
138
|
+
col = mix(col, u_action, (1.0 - uv.y) * 0.12 * u_k);
|
|
139
|
+
vec2 cell = floor(gl_FragCoord.xy / max(1.0, u_px));
|
|
140
|
+
float g = hash(cell + vec2(floor(u_time * 14.0) * 17.31, 0.0)) - 0.5;
|
|
141
|
+
col += g * 0.09;
|
|
142
|
+
gl_FragColor = vec4(col, 1.0);
|
|
143
|
+
}`,
|
|
144
|
+
fallback: [
|
|
145
|
+
NOISE,
|
|
146
|
+
`radial-gradient(55% 55% at 28% 30%, ${mix("brand", 40)}, transparent 70%)`,
|
|
147
|
+
`radial-gradient(50% 50% at 76% 72%, ${mix("accent", 32)}, transparent 70%)`,
|
|
148
|
+
`linear-gradient(to top, ${mix("action", 10)}, transparent 60%)`,
|
|
149
|
+
].join(", "),
|
|
150
|
+
},
|
|
151
|
+
|
|
152
|
+
/*
|
|
153
|
+
* A dot grid with a slow ripple travelling out from the middle: dots on
|
|
154
|
+
* the crest swell and take the brand colour, the rest stay small and quiet.
|
|
155
|
+
*/
|
|
156
|
+
dots: {
|
|
157
|
+
scale: 1,
|
|
158
|
+
fragment: `
|
|
159
|
+
void main() {
|
|
160
|
+
float cell = 22.0 * u_px;
|
|
161
|
+
vec2 g = gl_FragCoord.xy / cell;
|
|
162
|
+
vec2 id = floor(g);
|
|
163
|
+
vec2 f = fract(g) - 0.5;
|
|
164
|
+
float dist = length(id + 0.5 - 0.5 * u_res / cell);
|
|
165
|
+
float wave = pow(0.5 + 0.5 * sin(dist * 0.5 - u_time * 1.6), 3.0);
|
|
166
|
+
float r = 0.08 + 0.07 * wave * u_k;
|
|
167
|
+
float aa = 1.0 / cell;
|
|
168
|
+
float dotMask = 1.0 - smoothstep(r - aa, r + aa, length(f));
|
|
169
|
+
vec3 rest = mix(u_bg, u_ink, 0.2);
|
|
170
|
+
vec3 col = mix(u_bg, u_brand, wave * 0.03 * u_k);
|
|
171
|
+
col = mix(col, mix(rest, u_brand, wave * u_k), dotMask);
|
|
172
|
+
gl_FragColor = vec4(col, 1.0);
|
|
173
|
+
}`,
|
|
174
|
+
fallback: [
|
|
175
|
+
`radial-gradient(circle, ${mix("ink", 22)} 1.5px, transparent 2.2px) center / 22px 22px`,
|
|
176
|
+
`radial-gradient(45% 45% at 50% 50%, ${mix("brand", 16)}, transparent 75%)`,
|
|
177
|
+
].join(", "),
|
|
178
|
+
},
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export const PRESET_NAMES = Object.keys(PRESETS) as ShaderPreset[]
|
|
182
|
+
|
|
183
|
+
/** Whether enough time has passed for the next frame at `fps`. Frames land on the display's beat, so allow a little early. */
|
|
184
|
+
export function frameDue(now: number, last: number, fps: number): boolean {
|
|
185
|
+
return now - last >= 1000 / Math.max(1, fps) - 2
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/*
|
|
189
|
+
* Whether the engine is failing to keep up. Judged on the median interval
|
|
190
|
+
* between drawn frames, not the mean: a single hitch (a tab switch the
|
|
191
|
+
* visibility event missed, a GC) must not cost the backdrop its motion.
|
|
192
|
+
* Too slow means the frames come at under half the asked rate and slower
|
|
193
|
+
* than about 12 a second: a choppy aurora is worse than a still one.
|
|
194
|
+
*/
|
|
195
|
+
export function tooSlow(intervals: readonly number[], fps: number): boolean {
|
|
196
|
+
if (intervals.length < 20) return false
|
|
197
|
+
const sorted = [...intervals].sort((a, b) => a - b)
|
|
198
|
+
const median = sorted[Math.floor(sorted.length / 2)]
|
|
199
|
+
return median > Math.max((2 * 1000) / Math.max(1, fps), 80)
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/** A colour in 0–1 sRGB channels, as a shader takes it. */
|
|
203
|
+
export type Vec3 = [number, number, number]
|
|
204
|
+
|
|
205
|
+
/** Move `from` toward `to` by share `k` (0–1): theme changes blend over a few frames instead of snapping. */
|
|
206
|
+
export function approach(from: Vec3, to: Vec3, k: number): Vec3 {
|
|
207
|
+
return [from[0] + (to[0] - from[0]) * k, from[1] + (to[1] - from[1]) * k, from[2] + (to[2] - from[2]) * k]
|
|
208
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { onBeforeUnmount, ref } from "vue"
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
* A slider whose value is changed from outside (a reset, a preset, data
|
|
5
|
+
* arriving) glides its thumb and range to the new place instead of jumping;
|
|
6
|
+
* while the user drags, they follow the pointer with no delay, since an
|
|
7
|
+
* eased thumb trailing the finger reads as lag. reka places both with inline
|
|
8
|
+
* `left`/`right`, so the transition goes on those.
|
|
9
|
+
*/
|
|
10
|
+
export const SLIDER_GLIDE =
|
|
11
|
+
"[&_[style*=left]]:transition-[left,right] [&_[style*=left]]:duration-(--duration-medium) [&_[style*=left]]:ease-(--ease-emphasized) data-[dragging]:[&_[style*=left]]:transition-none motion-reduce:[&_[style*=left]]:transition-none"
|
|
12
|
+
|
|
13
|
+
export function useSliderDrag() {
|
|
14
|
+
const dragging = ref(false)
|
|
15
|
+
let view: Window | null = null
|
|
16
|
+
const end = () => {
|
|
17
|
+
dragging.value = false
|
|
18
|
+
view?.removeEventListener("pointerup", end)
|
|
19
|
+
view?.removeEventListener("pointercancel", end)
|
|
20
|
+
view = null
|
|
21
|
+
}
|
|
22
|
+
function onPointerDown(event: PointerEvent) {
|
|
23
|
+
dragging.value = true
|
|
24
|
+
view = (event.currentTarget as Element).ownerDocument.defaultView
|
|
25
|
+
view?.addEventListener("pointerup", end)
|
|
26
|
+
view?.addEventListener("pointercancel", end)
|
|
27
|
+
}
|
|
28
|
+
onBeforeUnmount(end)
|
|
29
|
+
return { dragging, onPointerDown }
|
|
30
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Text, type Slot, type VNode } from "vue"
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
* A slot's words when it holds nothing but text, else null. Components that
|
|
5
|
+
* animate a change of what they say (a badge rolling to a new status, an alert
|
|
6
|
+
* swapping its message) need to know when the words changed; anything richer
|
|
7
|
+
* (an icon, markup) is shown as given and only its box eases.
|
|
8
|
+
*/
|
|
9
|
+
export function slotText(slot: Slot | undefined): string | null {
|
|
10
|
+
const nodes: VNode[] = slot?.() ?? []
|
|
11
|
+
if (!nodes.length || !nodes.every((node) => node.type === Text)) return null
|
|
12
|
+
return nodes.map((node) => String(node.children ?? "")).join("").trim() || null
|
|
13
|
+
}
|