@leavepulse/ui 0.43.4 → 0.45.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 +121 -7
- package/dist/assets/{LpCommandPalette-B-0Est4Z.css → LpCommandPalette-nGNnzXqQ.css} +21 -21
- package/dist/assets/{LpDropzone-BBoWvi-J.css → LpDropzone-i4BdMi1H.css} +12 -12
- package/dist/assets/{LpImageEditor-Ct7Y3aNu.css → LpImageEditor-CbMA82g8.css} +6 -6
- package/dist/assets/{LpRollingText-CR1mb0Vt.css → LpRollingText-BGnE6q9j.css} +3 -0
- package/dist/assets/{LpShortcutsDialog-C6YoQ8_M.css → LpShortcutsDialog-CqYX3yy6.css} +28 -7
- package/dist/assets/{LpSkeleton-D7xU8IPV.css → LpSkeleton-H3Z5dEIk.css} +4 -1
- package/dist/assets/{LpTable-BgPLJOXa.css → LpTable-CPwWxTJS.css} +49 -9
- package/dist/chunks/ContextMenuItems-BtKU7074.js +124 -0
- package/dist/chunks/{LayoutCanvas-Dd20boUG.js → LayoutCanvas-BkaGXTjx.js} +26 -9
- package/dist/chunks/{LayoutNode-DGpZA9LX.js → LayoutNode-B3_9E1Xa.js} +28 -12
- package/dist/chunks/{LpActionBar-BlvtGB2P.js → LpActionBar-CnfFVIwC.js} +26 -12
- package/dist/chunks/{LpAddressInput-B-Zvxsn7.js → LpAddressInput-BwX8uo4_.js} +54 -49
- package/dist/chunks/{LpAlert-BAYHDSS1.js → LpAlert-B6ilrM7T.js} +16 -9
- package/dist/chunks/{LpAppShell-BGQVIY-3.js → LpAppShell-C53_RaGF.js} +53 -26
- package/dist/chunks/{LpAutocomplete-DExlIZe_.js → LpAutocomplete-C8IgSMsi.js} +52 -24
- package/dist/chunks/{LpAvatar-B3-xAOH3.js → LpAvatar-C1XStdOo.js} +19 -10
- package/dist/chunks/LpBreadcrumbs-CQJd73Gm.js +95 -0
- package/dist/chunks/{LpButton-5VXXCjwF.js → LpButton-CKSwC6Mu.js} +45 -15
- package/dist/chunks/{LpCalendar-B7Ko5RdR.js → LpCalendar-DrA2McTU.js} +1 -1
- package/dist/chunks/{LpCard-BhI1N_vL.js → LpCard-C2IvucmX.js} +1 -1
- package/dist/chunks/{LpCheckbox-CVccKYaH.js → LpCheckbox-Bkj13CbW.js} +8 -4
- package/dist/chunks/{LpCodeBlock-Bo0B4TFR.js → LpCodeBlock-ca7ANFyW.js} +49 -17
- package/dist/chunks/{LpColorPicker-BlXea7LM.js → LpColorPicker-zz4VGkG4.js} +75 -26
- package/dist/chunks/{LpConfirmDialog-Q-bxff8z.js → LpConfirmDialog-DCw5jR1t.js} +59 -17
- package/dist/chunks/LpConfirmHost-CbywMpMP.js +49 -0
- package/dist/chunks/LpContextMenu-BqkAYa9X.js +69 -0
- package/dist/chunks/LpDatePicker-DuusgT69.js +195 -0
- package/dist/chunks/LpDateRangePicker-DHeuznfC.js +208 -0
- package/dist/chunks/{LpDisclosure-ChQYmjlR.js → LpDisclosure-Cc23-JOT.js} +1 -1
- package/dist/chunks/{LpDrawer-BZ6k3hvo.js → LpDrawer-Dt4aB4Fr.js} +65 -28
- package/dist/chunks/{LpDropdownMenu-DJpoqRCb.js → LpDropdownMenu-DdJGQzPn.js} +2 -1
- package/dist/chunks/{LpEmptyState-Dsdqz1Wj.js → LpEmptyState-BJbZXC8D.js} +1 -1
- package/dist/chunks/LpFieldNotice-wtieLUTj.js +64 -0
- package/dist/chunks/{LpFileTree-_5vFBgaK.js → LpFileTree-CNurOvkV.js} +25 -10
- package/dist/chunks/{LpFileTreeNode-BdcIYKIE.js → LpFileTreeNode-D_qRRdsH.js} +3 -3
- package/dist/chunks/LpFormField-CtVYj6tO.js +101 -0
- package/dist/chunks/{LpIcon-CCnX5_2j.js → LpIcon-DYtaid0K.js} +20 -6
- package/dist/chunks/{LpInput-9EXWUM9B.js → LpInput-D-YGQbEv.js} +58 -20
- package/dist/chunks/{LpLightbox-2sYVMY-S.js → LpLightbox-DSDahF23.js} +148 -61
- package/dist/chunks/{LpLink-DCND-zyu.js → LpLink-Krkjhe_y.js} +15 -6
- package/dist/chunks/{LpLogViewer-CaRy5MeS.js → LpLogViewer-KypbgOyo.js} +300 -72
- package/dist/chunks/{LpMapPicker-ul4oYno6.js → LpMapPicker-Cc84LaM3.js} +39 -14
- package/dist/chunks/{LpMediaGallery-FF-NquIo.js → LpMediaGallery-YKr-cQPN.js} +7 -4
- package/dist/chunks/{LpModal-Bt0pQCI_.js → LpModal-DMMDQHAu.js} +52 -29
- package/dist/chunks/{LpModalNav-YaifHrXC.js → LpModalNav-BVPDyIup.js} +10 -7
- package/dist/chunks/{LpNotificationBell-DyGGsMAM.js → LpNotificationBell-CCwyJP5C.js} +79 -33
- package/dist/chunks/{LpNumberField-CWuX08iX.js → LpNumberField-B77O-z28.js} +21 -8
- package/dist/chunks/{LpNumberFlow-amWYaeuA.js → LpNumberFlow-z4WPZcZu.js} +22 -12
- package/dist/chunks/{LpOtpInput-y2v-pmiN.js → LpOtpInput-CziPF3Oq.js} +37 -8
- package/dist/chunks/{LpPagination-5FxjmmB5.js → LpPagination-BcDlo3r_.js} +31 -20
- package/dist/chunks/{LpPasswordInput--sjYCkLS.js → LpPasswordInput-eKzQhbQP.js} +11 -8
- package/dist/chunks/LpPhoneInput-BCbrrhgE.js +336 -0
- package/dist/chunks/{LpPie-D6OnzwlJ.js → LpPie-BEJ2SfI6.js} +84 -28
- package/dist/chunks/{LpPopover-8268UmlC.js → LpPopover-DCKeqiVn.js} +5 -2
- package/dist/chunks/{LpProgress-Btq5W4Ut.js → LpProgress-CBU1iI69.js} +5 -4
- package/dist/chunks/{LpRadioGroup-xPIuVDO_.js → LpRadioGroup-DmEW0l78.js} +9 -2
- package/dist/chunks/LpRangeCalendar-DZuaRV0W.js +331 -0
- package/dist/chunks/{LpRollingText-CM5gkSeF.js → LpRollingText-DB7upf-5.js} +1 -1
- package/dist/chunks/{LpScrollArea-D8xyKXcF.js → LpScrollArea-Bry1fdOM.js} +14 -2
- package/dist/chunks/{LpSection-aM6J_u51.js → LpSection-tKEH0jYf.js} +1 -1
- package/dist/chunks/{LpSegmented-fV0Sk92p.js → LpSegmented-C09EEam2.js} +8 -4
- package/dist/chunks/{LpSelect-ANIVtrpM.js → LpSelect-DNIL6MB9.js} +50 -31
- package/dist/chunks/{LpSidebar-ClBUwoEz.js → LpSidebar-CQNB0-yU.js} +30 -22
- package/dist/chunks/{LpSidebarNav-llOcs_Fn.js → LpSidebarNav-kx-LdqNU.js} +16 -11
- package/dist/chunks/{LpSkeleton-CTI-vBgW.js → LpSkeleton-BB6XIV-w.js} +1 -1
- package/dist/chunks/{LpSlider-BwNIBkAK.js → LpSlider-Cuzmrd7p.js} +7 -2
- package/dist/chunks/{LpSparkline-C6xQ2dwv.js → LpSparkline-CB80N8SJ.js} +11 -4
- package/dist/chunks/{LpStat-CQaW8R--.js → LpStat-B-7udBRP.js} +2 -2
- package/dist/chunks/{LpStepper-Bk-XaQYU.js → LpStepper-C7XiVKZj.js} +34 -19
- package/dist/chunks/{LpSwitch-Blizvqyt.js → LpSwitch-OrcOJX9m.js} +41 -27
- package/dist/chunks/LpTableFilter-BV8PpLM9.js +158 -0
- package/dist/chunks/{LpTableOfContents-CjvxPMpU.js → LpTableOfContents-BUcAfGPQ.js} +36 -41
- package/dist/chunks/{LpTabs-Cefe_lys.js → LpTabs-29iW1XOx.js} +20 -6
- package/dist/chunks/LpTagsInput-D1SuqbhB.js +300 -0
- package/dist/chunks/LpTextarea-uuScWo6-.js +77 -0
- package/dist/chunks/LpThemeProvider-Be4r2TQX.js +35 -0
- package/dist/chunks/LpThemeSwitcher-B4aVPbNC.js +166 -0
- package/dist/chunks/{LpTilt-DuyKbpkA.js → LpTilt-B7KHX1zH.js} +2 -2
- package/dist/chunks/{LpTimeField-K6nAGboX.js → LpTimeField-Cqe7fXnh.js} +14 -8
- package/dist/chunks/{LpToaster-gDB1EaU3.js → LpToaster-DpJvoCOh.js} +94 -31
- package/dist/chunks/{LpTooltip-hCLPAqMG.js → LpTooltip-CXVKcOXT.js} +14 -6
- package/dist/chunks/LpTooltipProvider-CftjV7Xc.js +25 -0
- package/dist/chunks/{LpTopologyCanvas-Cm1L39uL.js → LpTopologyCanvas-6ozNrQKh.js} +8 -2
- package/dist/chunks/{LpUptimeBar-DvAMno0S.js → LpUptimeBar-ugcv25xj.js} +44 -15
- package/dist/chunks/icons-CkA0njzm.js +43 -0
- package/dist/chunks/{LpThemeSwitcher-Cue-myST.js → useTheme-DJm1JSsY.js} +215 -260
- package/dist/component-names.d.ts +1 -1
- package/dist/components/ContextMenuItems.vue.d.ts +8 -0
- package/dist/components/ContextMenuItems.vue.js +4 -0
- package/dist/components/LayoutCanvas.vue.js +1 -1
- package/dist/components/LayoutNode.vue.d.ts +4 -0
- package/dist/components/LayoutNode.vue.js +1 -1
- package/dist/components/LpActionBar.vue.d.ts +3 -0
- package/dist/components/LpActionBar.vue.js +1 -1
- package/dist/components/LpAddressInput.vue.d.ts +1 -1
- package/dist/components/LpAddressInput.vue.js +1 -1
- package/dist/components/LpAlert.vue.d.ts +9 -0
- package/dist/components/LpAlert.vue.js +1 -1
- package/dist/components/LpAppShell.vue.d.ts +10 -0
- package/dist/components/LpAppShell.vue.js +1 -1
- package/dist/components/LpAutocomplete.vue.d.ts +31 -6
- package/dist/components/LpAutocomplete.vue.js +1 -1
- package/dist/components/LpAvatar.vue.d.ts +3 -3
- package/dist/components/LpAvatar.vue.js +1 -1
- package/dist/components/LpBadge.vue.d.ts +2 -2
- package/dist/components/LpBreadcrumbs.vue.d.ts +13 -9
- package/dist/components/LpBreadcrumbs.vue.js +1 -1
- package/dist/components/LpButton.vue.d.ts +35 -98
- 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 +47 -3
- package/dist/components/LpChart.vue.js +377 -97
- package/dist/components/LpCheckbox.vue.js +1 -1
- package/dist/components/LpCodeBlock.vue.d.ts +2 -0
- package/dist/components/LpCodeBlock.vue.js +1 -1
- package/dist/components/LpColorPicker.vue.d.ts +2 -2
- package/dist/components/LpColorPicker.vue.js +1 -1
- package/dist/components/LpCommandPalette.vue.d.ts +14 -3
- package/dist/components/LpCommandPalette.vue.js +142 -77
- package/dist/components/LpConfirmDialog.vue.d.ts +15 -3
- package/dist/components/LpConfirmDialog.vue.js +1 -1
- package/dist/components/LpConfirmHost.vue.d.ts +9 -0
- package/dist/components/LpConfirmHost.vue.js +4 -0
- package/dist/components/LpContextMenu.vue.d.ts +3 -5
- package/dist/components/LpContextMenu.vue.js +1 -1
- package/dist/components/LpDatePicker.vue.d.ts +24 -6
- package/dist/components/LpDatePicker.vue.js +1 -1
- package/dist/components/LpDateRangePicker.vue.d.ts +60 -0
- package/dist/components/LpDateRangePicker.vue.js +4 -0
- package/dist/components/LpDisclosure.vue.js +1 -1
- package/dist/components/LpDrawer.vue.d.ts +10 -0
- package/dist/components/LpDrawer.vue.js +1 -1
- package/dist/components/LpDropdownMenu.vue.js +1 -1
- package/dist/components/LpDropzone.vue.js +23 -7
- package/dist/components/LpEmptyState.vue.js +1 -1
- package/dist/components/LpFieldNotice.vue.d.ts +8 -0
- package/dist/components/LpFieldNotice.vue.js +4 -0
- package/dist/components/LpFileTree.vue.d.ts +11 -2
- package/dist/components/LpFileTree.vue.js +1 -1
- package/dist/components/LpFileTreeNode.vue.js +1 -1
- package/dist/components/LpFormField.vue.d.ts +13 -1
- package/dist/components/LpFormField.vue.js +1 -1
- package/dist/components/LpIcon.vue.d.ts +0 -1
- package/dist/components/LpIcon.vue.js +1 -1
- package/dist/components/LpImageEditor.vue.js +93 -58
- package/dist/components/LpInput.vue.d.ts +22 -3
- package/dist/components/LpInput.vue.js +1 -1
- package/dist/components/LpLightbox.vue.d.ts +17 -0
- package/dist/components/LpLightbox.vue.js +1 -1
- package/dist/components/LpLink.vue.d.ts +11 -11
- package/dist/components/LpLink.vue.js +1 -1
- package/dist/components/LpLogViewer.vue.d.ts +72 -3
- package/dist/components/LpLogViewer.vue.js +1 -1
- package/dist/components/LpMapPicker.vue.d.ts +4 -4
- package/dist/components/LpMapPicker.vue.js +1 -1
- package/dist/components/LpMediaGallery.vue.js +1 -1
- package/dist/components/LpModal.vue.d.ts +16 -6
- package/dist/components/LpModal.vue.js +1 -1
- package/dist/components/LpModalNav.vue.d.ts +0 -1
- package/dist/components/LpModalNav.vue.js +1 -1
- package/dist/components/LpNotificationBell.vue.d.ts +11 -2
- package/dist/components/LpNotificationBell.vue.js +1 -1
- package/dist/components/LpNumberField.vue.d.ts +2 -0
- package/dist/components/LpNumberField.vue.js +1 -1
- package/dist/components/LpNumberFlow.vue.js +1 -1
- package/dist/components/LpOtpInput.vue.js +1 -1
- package/dist/components/LpPagination.vue.d.ts +2 -0
- package/dist/components/LpPagination.vue.js +1 -1
- package/dist/components/LpPasswordInput.vue.js +1 -1
- package/dist/components/LpPhoneInput.vue.d.ts +13 -0
- package/dist/components/LpPhoneInput.vue.js +1 -1
- package/dist/components/LpPie.vue.d.ts +21 -2
- package/dist/components/LpPie.vue.js +1 -1
- package/dist/components/LpPopover.vue.d.ts +2 -0
- 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/LpRadioGroup.vue.js +1 -1
- package/dist/components/LpRangeCalendar.vue.d.ts +51 -0
- package/dist/components/LpRangeCalendar.vue.js +4 -0
- package/dist/components/LpRollingText.vue.d.ts +1 -1
- package/dist/components/LpRollingText.vue.js +1 -1
- package/dist/components/LpScrollArea.vue.d.ts +2 -2
- package/dist/components/LpScrollArea.vue.js +1 -1
- package/dist/components/LpSection.vue.d.ts +1 -1
- package/dist/components/LpSection.vue.js +1 -1
- package/dist/components/LpSegmented.vue.d.ts +5 -0
- package/dist/components/LpSegmented.vue.js +1 -1
- package/dist/components/LpSelect.vue.d.ts +1 -0
- package/dist/components/LpSelect.vue.js +1 -1
- package/dist/components/LpShortcutsDialog.vue.d.ts +7 -1
- package/dist/components/LpShortcutsDialog.vue.js +60 -15
- package/dist/components/LpSidebar.vue.d.ts +6 -2
- package/dist/components/LpSidebar.vue.js +1 -1
- package/dist/components/LpSidebarNav.vue.js +1 -1
- package/dist/components/LpSkeleton.vue.js +1 -1
- package/dist/components/LpSlider.vue.js +1 -1
- package/dist/components/LpSparkline.vue.d.ts +1 -0
- package/dist/components/LpSparkline.vue.js +1 -1
- package/dist/components/LpStat.vue.js +1 -1
- package/dist/components/LpStepper.vue.js +1 -1
- package/dist/components/LpSwitch.vue.js +1 -1
- package/dist/components/LpTable.vue.d.ts +107 -3
- package/dist/components/LpTable.vue.js +540 -128
- package/dist/components/LpTableFilter.vue.d.ts +31 -0
- package/dist/components/LpTableFilter.vue.js +4 -0
- package/dist/components/LpTableOfContents.vue.d.ts +4 -2
- package/dist/components/LpTableOfContents.vue.js +1 -1
- package/dist/components/LpTabs.vue.d.ts +2 -0
- package/dist/components/LpTabs.vue.js +1 -1
- package/dist/components/LpTagsInput.vue.d.ts +77 -0
- package/dist/components/LpTagsInput.vue.js +4 -0
- package/dist/components/LpTextarea.vue.d.ts +19 -6
- package/dist/components/LpTextarea.vue.js +1 -1
- package/dist/components/LpThemeProvider.vue.d.ts +22 -0
- package/dist/components/LpThemeProvider.vue.js +4 -0
- package/dist/components/LpThemeSwitcher.vue.d.ts +15 -2
- package/dist/components/LpThemeSwitcher.vue.js +1 -1
- package/dist/components/LpTilt.vue.js +1 -1
- package/dist/components/LpTimeField.vue.js +1 -1
- package/dist/components/LpToaster.vue.d.ts +6 -1
- package/dist/components/LpToaster.vue.js +1 -1
- package/dist/components/LpTooltip.vue.d.ts +1 -2
- package/dist/components/LpTooltip.vue.js +1 -1
- package/dist/components/LpTooltipProvider.vue.d.ts +22 -0
- package/dist/components/LpTooltipProvider.vue.js +4 -0
- package/dist/components/LpTopologyCanvas.vue.js +1 -1
- package/dist/components/LpUptimeBar.vue.d.ts +4 -0
- package/dist/components/LpUptimeBar.vue.js +1 -1
- package/dist/components/chart.d.ts +32 -1
- package/dist/components/chart.js +42 -6
- package/dist/components/codeHighlight.d.ts +6 -0
- package/dist/components/codeHighlight.js +92 -20
- package/dist/components/color.d.ts +1 -1
- package/dist/components/color.js +20 -9
- package/dist/components/commandSearch.js +3 -0
- package/dist/components/confirm.d.ts +25 -0
- package/dist/components/confirm.js +26 -0
- package/dist/components/countries.d.ts +6 -0
- package/dist/components/countries.js +7 -0
- package/dist/components/dateRange.d.ts +43 -0
- package/dist/components/dateRange.js +76 -0
- package/dist/components/exportData.d.ts +58 -0
- package/dist/components/exportData.js +165 -0
- package/dist/components/fileTree.js +16 -7
- package/dist/components/imageCrop.d.ts +42 -0
- package/dist/components/imageCrop.js +47 -0
- package/dist/components/link.d.ts +12 -0
- package/dist/components/link.js +19 -0
- package/dist/components/logSearch.d.ts +18 -0
- package/dist/components/logSearch.js +36 -0
- package/dist/components/modalDepth.d.ts +18 -2
- package/dist/components/modalDepth.js +17 -5
- package/dist/components/otp.d.ts +5 -0
- package/dist/components/otp.js +10 -0
- package/dist/components/phone.d.ts +41 -0
- package/dist/components/phone.js +80 -0
- package/dist/components/sidebar.d.ts +7 -1
- package/dist/components/tableFilter.d.ts +36 -0
- package/dist/components/tableFilter.js +84 -0
- package/dist/components/tableSort.d.ts +8 -0
- package/dist/components/tableSort.js +31 -0
- package/dist/components/tags.d.ts +20 -0
- package/dist/components/tags.js +37 -0
- package/dist/composables/useField.d.ts +38 -0
- package/dist/composables/useField.js +45 -0
- package/dist/composables/useFieldNotice.d.ts +17 -0
- package/dist/composables/useFieldNotice.js +28 -0
- package/dist/composables/useForm.d.ts +53 -0
- package/dist/composables/useForm.js +207 -0
- package/dist/composables/useFormDraft.d.ts +10 -3
- package/dist/composables/useFormDraft.js +9 -14
- package/dist/composables/useHotkeys.d.ts +81 -5
- package/dist/composables/useHotkeys.js +223 -25
- package/dist/composables/useInputFilter.d.ts +17 -0
- package/dist/composables/useInputFilter.js +47 -10
- package/dist/composables/useInputMask.d.ts +59 -0
- package/dist/composables/useInputMask.js +170 -0
- package/dist/composables/useLpConfig.d.ts +24 -0
- package/dist/composables/useLpConfig.js +44 -0
- package/dist/composables/usePaintSelect.d.ts +22 -0
- package/dist/composables/usePaintSelect.js +169 -0
- package/dist/composables/useRovingFocus.js +7 -0
- package/dist/composables/useShift.js +7 -2
- package/dist/composables/useTilt.d.ts +2 -2
- package/dist/composables/useTilt.js +5 -6
- package/dist/composables/useToast.d.ts +20 -0
- package/dist/composables/useToast.js +28 -4
- package/dist/composables/useZoomPan.d.ts +3 -3
- package/dist/composables/useZoomPan.js +4 -4
- package/dist/icons-all.d.ts +1 -0
- package/dist/icons-all.js +3 -0
- package/dist/icons.d.ts +14 -1
- package/dist/index.d.ts +60 -4
- package/dist/index.js +245 -141
- package/dist/kit-icons.d.ts +2 -0
- package/dist/layout/tree.d.ts +2 -0
- package/dist/layout/tree.js +16 -1
- package/dist/layout/useLayout.d.ts +15 -0
- package/dist/layout/useLayout.js +41 -14
- package/dist/theme/backdrop.d.ts +12 -0
- package/dist/theme/presets.d.ts +0 -2
- package/dist/theme/useTheme.d.ts +32 -9
- package/package.json +18 -8
- package/src/component-names.ts +9 -7
- package/src/components/ContextMenuItems.vue +88 -0
- package/src/components/LayoutCanvas.vue +26 -6
- package/src/components/LayoutNode.vue +30 -4
- package/src/components/LpActionBar.vue +31 -8
- package/src/components/LpAddressInput.vue +57 -41
- package/src/components/LpAlert.vue +16 -3
- package/src/components/LpAppShell.vue +45 -12
- package/src/components/LpAutocomplete.vue +47 -10
- package/src/components/LpAvatar.vue +20 -6
- package/src/components/LpBreadcrumbs.vue +65 -41
- package/src/components/LpButton.vue +60 -14
- package/src/components/LpChart.vue +351 -28
- package/src/components/LpCheckbox.vue +7 -3
- package/src/components/LpCodeBlock.vue +50 -14
- package/src/components/LpColorPicker.vue +73 -10
- package/src/components/LpCommandPalette.vue +115 -20
- package/src/components/LpConfirmDialog.vue +68 -17
- package/src/components/LpConfirmHost.vue +59 -0
- package/src/components/LpContextMenu.vue +15 -116
- package/src/components/LpDatePicker.vue +104 -68
- package/src/components/LpDateRangePicker.vue +224 -0
- package/src/components/LpDrawer.vue +56 -19
- package/src/components/LpDropdownMenu.vue +1 -0
- package/src/components/LpDropzone.vue +21 -4
- package/src/components/LpFieldNotice.vue +65 -0
- package/src/components/LpFileTree.vue +33 -7
- package/src/components/LpFormField.vue +59 -7
- package/src/components/LpIcon.vue +22 -4
- package/src/components/LpImageEditor.vue +96 -57
- package/src/components/LpInput.vue +62 -11
- package/src/components/LpLightbox.vue +140 -29
- package/src/components/LpLink.vue +24 -13
- package/src/components/LpLogViewer.vue +311 -39
- package/src/components/LpMapPicker.vue +36 -13
- package/src/components/LpMediaGallery.vue +8 -2
- package/src/components/LpModal.vue +54 -28
- package/src/components/LpModalNav.vue +5 -2
- package/src/components/LpNotificationBell.vue +77 -23
- package/src/components/LpNumberField.vue +21 -6
- package/src/components/LpNumberFlow.vue +26 -10
- package/src/components/LpOtpInput.vue +43 -8
- package/src/components/LpPagination.vue +23 -7
- package/src/components/LpPasswordInput.vue +4 -1
- package/src/components/LpPhoneInput.vue +224 -39
- package/src/components/LpPie.vue +102 -20
- package/src/components/LpPopover.vue +4 -0
- package/src/components/LpProgress.vue +5 -3
- package/src/components/LpRadioGroup.vue +9 -2
- package/src/components/LpRangeCalendar.vue +365 -0
- package/src/components/LpRollingText.vue +3 -0
- package/src/components/LpScrollArea.vue +22 -2
- package/src/components/LpSegmented.vue +12 -2
- package/src/components/LpSelect.vue +0 -0
- package/src/components/LpShortcutsDialog.vue +83 -9
- package/src/components/LpSidebar.vue +14 -9
- package/src/components/LpSidebarNav.vue +17 -5
- package/src/components/LpSkeleton.vue +4 -1
- package/src/components/LpSlider.vue +7 -1
- package/src/components/LpSparkline.vue +11 -3
- package/src/components/LpStepper.vue +23 -10
- package/src/components/LpSwitch.vue +31 -8
- package/src/components/LpTable.vue +668 -90
- package/src/components/LpTableFilter.vue +175 -0
- package/src/components/LpTableOfContents.vue +50 -32
- package/src/components/LpTabs.vue +23 -6
- package/src/components/LpTagsInput.vue +364 -0
- package/src/components/LpTextarea.vue +44 -6
- package/src/components/LpThemeProvider.vue +41 -0
- package/src/components/LpThemeSwitcher.vue +76 -33
- package/src/components/LpTilt.vue +2 -2
- package/src/components/LpTimeField.vue +14 -3
- package/src/components/LpToaster.vue +115 -27
- package/src/components/LpTooltip.vue +29 -5
- package/src/components/LpTooltipProvider.vue +24 -0
- package/src/components/LpTopologyCanvas.vue +19 -2
- package/src/components/LpUptimeBar.vue +46 -8
- package/src/components/chart.ts +82 -8
- package/src/components/codeHighlight.ts +111 -15
- package/src/components/color.ts +30 -9
- package/src/components/commandSearch.ts +6 -0
- package/src/components/confirm.ts +58 -0
- package/src/components/countries.ts +12 -0
- package/src/components/dateRange.ts +134 -0
- package/src/components/exportData.ts +228 -0
- package/src/components/fileTree.ts +19 -7
- package/src/components/imageCrop.ts +107 -0
- package/src/components/link.ts +34 -0
- package/src/components/logSearch.ts +46 -0
- package/src/components/modalDepth.ts +48 -6
- package/src/components/otp.ts +16 -0
- package/src/components/phone.ts +140 -0
- package/src/components/sidebar.ts +7 -1
- package/src/components/tableFilter.ts +122 -0
- package/src/components/tableSort.ts +37 -0
- package/src/components/tags.ts +62 -0
- package/src/composables/useField.ts +83 -0
- package/src/composables/useFieldNotice.ts +59 -0
- package/src/composables/useForm.ts +314 -0
- package/src/composables/useFormDraft.ts +25 -20
- package/src/composables/useHotkeys.ts +355 -28
- package/src/composables/useInputFilter.ts +77 -14
- package/src/composables/useInputMask.ts +254 -0
- package/src/composables/useLpConfig.ts +87 -0
- package/src/composables/usePaintSelect.ts +243 -0
- package/src/composables/useRovingFocus.ts +11 -0
- package/src/composables/useShift.ts +13 -2
- package/src/composables/useTilt.ts +5 -6
- package/src/composables/useToast.ts +56 -4
- package/src/composables/useZoomPan.ts +6 -6
- package/src/iconSubset.d.mts +10 -0
- package/src/iconSubset.mjs +51 -0
- package/src/icons-all.ts +10 -0
- package/src/icons.ts +59 -8
- package/src/index.ts +73 -4
- package/src/kit-icons.ts +5 -0
- package/src/layout/tree.ts +30 -0
- package/src/layout/useLayout.ts +68 -18
- package/src/nuxt.ts +87 -17
- package/src/runtime/config.plugin.ts +14 -0
- package/src/runtime/theme.plugin.ts +43 -23
- package/src/theme/backdrop.ts +69 -0
- package/src/theme/presets.ts +6 -64
- package/src/theme/useTheme.ts +206 -100
- package/src/tokens/tokens.css +46 -0
- package/dist/chunks/LpBreadcrumbs-p4jHe_V-.js +0 -62
- package/dist/chunks/LpContextMenu-CwW1QXe_.js +0 -217
- package/dist/chunks/LpDatePicker-CwOToxGq.js +0 -164
- package/dist/chunks/LpFormField-CazBc2-a.js +0 -62
- package/dist/chunks/LpPhoneInput-BDSKVl_z.js +0 -209
- package/dist/chunks/LpTextarea-t7F6gv8E.js +0 -47
|
@@ -31,6 +31,35 @@ import LpContextMenu, { type ContextMenuItemDef } from "./LpContextMenu.vue"
|
|
|
31
31
|
import LpIcon from "./LpIcon.vue"
|
|
32
32
|
import LpScrollArea from "./LpScrollArea.vue"
|
|
33
33
|
import { useMergedAttrs } from "../composables/useMergedClass"
|
|
34
|
+
import { useLabels } from "../composables/useLpConfig"
|
|
35
|
+
import { countLevels, matchPositions, ringWindow, stepMatch } from "./logSearch"
|
|
36
|
+
|
|
37
|
+
export interface LogViewerLabels {
|
|
38
|
+
[key: string]: string
|
|
39
|
+
empty: string
|
|
40
|
+
frozen: string
|
|
41
|
+
noMatch: string
|
|
42
|
+
loadingOlder: string
|
|
43
|
+
copyMessage: string
|
|
44
|
+
copyLine: string
|
|
45
|
+
copyTimestamp: string
|
|
46
|
+
copied: string
|
|
47
|
+
/** `{source}` is replaced by the line's source. */
|
|
48
|
+
filterSource: string
|
|
49
|
+
/** `{level}` is replaced by the line's level. */
|
|
50
|
+
filterLevel: string
|
|
51
|
+
/** `{n}` is replaced by the count. */
|
|
52
|
+
identical: string
|
|
53
|
+
jump: string
|
|
54
|
+
search: string
|
|
55
|
+
clearSearch: string
|
|
56
|
+
previousMatch: string
|
|
57
|
+
nextMatch: string
|
|
58
|
+
/** `{current}` and `{total}` are replaced. */
|
|
59
|
+
matchCount: string
|
|
60
|
+
noMatches: string
|
|
61
|
+
levels: string
|
|
62
|
+
}
|
|
34
63
|
|
|
35
64
|
export type LogLevel = "trace" | "debug" | "info" | "warn" | "error" | "fatal" | "success"
|
|
36
65
|
|
|
@@ -103,6 +132,19 @@ const props = withDefaults(
|
|
|
103
132
|
* so a duplicate level word / timestamp doesn't repeat in the message body.
|
|
104
133
|
*/
|
|
105
134
|
parse?: boolean
|
|
135
|
+
/**
|
|
136
|
+
* Keep only the newest N lines on screen. The consumer can keep pushing
|
|
137
|
+
* into the same array (or hand over a longer one) and the viewer shows the
|
|
138
|
+
* tail; trimming the source itself is still the cheaper way to cap memory.
|
|
139
|
+
*/
|
|
140
|
+
maxLines?: number
|
|
141
|
+
/**
|
|
142
|
+
* A bar above the stream with a search box (Enter / Shift+Enter or F3 step
|
|
143
|
+
* through matches) and level chips. The term binds via `v-model:search`,
|
|
144
|
+
* the chosen levels via `v-model:levels`.
|
|
145
|
+
*/
|
|
146
|
+
toolbar?: boolean
|
|
147
|
+
labels?: Partial<LogViewerLabels>
|
|
106
148
|
}>(),
|
|
107
149
|
{
|
|
108
150
|
showTime: true,
|
|
@@ -111,8 +153,6 @@ const props = withDefaults(
|
|
|
111
153
|
wrap: false,
|
|
112
154
|
tail: true,
|
|
113
155
|
height: "20rem",
|
|
114
|
-
emptyLabel: "No logs yet",
|
|
115
|
-
frozenLabel: "Held — release Ctrl to resume",
|
|
116
156
|
rowMenu: true,
|
|
117
157
|
parse: false,
|
|
118
158
|
},
|
|
@@ -129,6 +169,38 @@ const emit = defineEmits<{
|
|
|
129
169
|
(e: "reach-top"): void
|
|
130
170
|
}>()
|
|
131
171
|
|
|
172
|
+
/** The toolbar's search term. Without the toolbar, `highlight` is the term. */
|
|
173
|
+
const query = defineModel<string>("search", { default: "" })
|
|
174
|
+
/** Levels to show. Empty shows every level. */
|
|
175
|
+
const levels = defineModel<LogLevel[]>("levels", { default: () => [] })
|
|
176
|
+
const term = computed(() => (props.toolbar && query.value ? query.value : (props.highlight ?? "")).trim())
|
|
177
|
+
|
|
178
|
+
const text = useLabels<LogViewerLabels>(
|
|
179
|
+
"logViewer",
|
|
180
|
+
{
|
|
181
|
+
empty: "No logs yet",
|
|
182
|
+
frozen: "Held — release Ctrl to resume",
|
|
183
|
+
noMatch: "No lines match",
|
|
184
|
+
loadingOlder: "loading older…",
|
|
185
|
+
copyMessage: "Copy message",
|
|
186
|
+
copyLine: "Copy line",
|
|
187
|
+
copyTimestamp: "Copy timestamp",
|
|
188
|
+
copied: "Copied to clipboard",
|
|
189
|
+
filterSource: "Filter by [{source}]",
|
|
190
|
+
filterLevel: "Filter by {level}",
|
|
191
|
+
identical: "{n} identical lines",
|
|
192
|
+
jump: "Jump to latest",
|
|
193
|
+
search: "Search logs",
|
|
194
|
+
clearSearch: "Clear search",
|
|
195
|
+
previousMatch: "Previous match",
|
|
196
|
+
nextMatch: "Next match",
|
|
197
|
+
matchCount: "{current} / {total}",
|
|
198
|
+
noMatches: "No matches",
|
|
199
|
+
levels: "Levels",
|
|
200
|
+
},
|
|
201
|
+
() => props.labels,
|
|
202
|
+
)
|
|
203
|
+
|
|
132
204
|
// Level → gutter rail + text colour. Reads only semantic tokens so re-skinning
|
|
133
205
|
// just works. trace/debug stay muted; success is green; warn amber; error/fatal
|
|
134
206
|
// red (fatal gets a filled chip to stand apart from plain error).
|
|
@@ -242,7 +314,10 @@ const root = ref<HTMLElement | null>(null)
|
|
|
242
314
|
const frozen = ref(false)
|
|
243
315
|
const frozenLines = shallowRef<LogLine[] | null>(null)
|
|
244
316
|
|
|
245
|
-
const
|
|
317
|
+
const windowed = computed(() => ringWindow(frozenLines.value ?? props.lines, props.maxLines))
|
|
318
|
+
const sourceLines = computed<LogLine[]>(() => windowed.value.lines as LogLine[])
|
|
319
|
+
// Line numbers keep counting from the start of the stream after the cap trims it.
|
|
320
|
+
const lineOffset = computed(() => windowed.value.offset)
|
|
246
321
|
|
|
247
322
|
function freeze(): boolean {
|
|
248
323
|
// Declining unless the stream is being looked at: `false` hands Ctrl back
|
|
@@ -253,7 +328,9 @@ function freeze(): boolean {
|
|
|
253
328
|
// Snapshotted by reference, not copied: the array the consumer hands over is
|
|
254
329
|
// replaced wholesale on every update, so holding the old one is enough — and
|
|
255
330
|
// a copy of ten thousand lines on every keypress is not.
|
|
256
|
-
|
|
331
|
+
// A capped stream is usually one array pushed into in place, which a
|
|
332
|
+
// reference would not hold still; its window is small enough to copy.
|
|
333
|
+
frozenLines.value = props.maxLines ? props.lines.slice(-props.maxLines) : props.lines
|
|
257
334
|
frozen.value = true
|
|
258
335
|
return true
|
|
259
336
|
}
|
|
@@ -279,9 +356,13 @@ function focusWithin() {
|
|
|
279
356
|
return !!el && el.contains(document.activeElement)
|
|
280
357
|
}
|
|
281
358
|
|
|
359
|
+
// `when` rather than a bare decline: several viewers on one page all bind these
|
|
360
|
+
// keys, and only the one being looked at should claim them.
|
|
361
|
+
const lookedAt = () => hovered.value || focusWithin()
|
|
362
|
+
|
|
282
363
|
useHotkeys(() => [
|
|
283
|
-
{ key: "control", allowInInput: true, handler: freeze },
|
|
284
|
-
{ key: "meta", allowInInput: true, handler: freeze },
|
|
364
|
+
{ key: "control", allowInInput: true, when: lookedAt, handler: freeze },
|
|
365
|
+
{ key: "meta", allowInInput: true, when: lookedAt, handler: freeze },
|
|
285
366
|
])
|
|
286
367
|
|
|
287
368
|
// Released on keyup anywhere, and on blur: a window that loses focus mid-hold
|
|
@@ -309,10 +390,12 @@ const parsedLines = computed<LogLine[]>(() =>
|
|
|
309
390
|
// With filterMatches + a search term, keep only lines whose message contains it
|
|
310
391
|
// (case-insensitive); otherwise pass everything.
|
|
311
392
|
const visibleLines = computed<{ line: LogLine; n: number; count: number }[]>(() => {
|
|
312
|
-
const
|
|
393
|
+
const needle = props.filterMatches ? term.value.toLowerCase() : ""
|
|
394
|
+
const shown = levels.value.length ? new Set<LogLevel>(levels.value) : null
|
|
313
395
|
const filtered = parsedLines.value
|
|
314
396
|
.map((line, n) => ({ line, n }))
|
|
315
|
-
.filter(({ line }) => !
|
|
397
|
+
.filter(({ line }) => !shown || shown.has(line.level ?? "info"))
|
|
398
|
+
.filter(({ line }) => !needle || matchPositions([line], needle).length > 0)
|
|
316
399
|
|
|
317
400
|
if (!props.compact) return filtered.map((row) => ({ ...row, count: 1 }))
|
|
318
401
|
|
|
@@ -336,6 +419,106 @@ const visibleLines = computed<{ line: LogLine; n: number; count: number }[]>(()
|
|
|
336
419
|
return folded
|
|
337
420
|
})
|
|
338
421
|
|
|
422
|
+
/*
|
|
423
|
+
* Rows are keyed by the identity of the line object the consumer passed, not by
|
|
424
|
+
* position: prepending older history shifts every index, and index keys made
|
|
425
|
+
* the bottom rows play the enter animation instead of the new top ones.
|
|
426
|
+
*/
|
|
427
|
+
const lineIds = new WeakMap<LogLine, number>()
|
|
428
|
+
let nextLineId = 0
|
|
429
|
+
function lineKey(n: number): number {
|
|
430
|
+
const line = sourceLines.value[n]
|
|
431
|
+
if (!line) return -1 - n
|
|
432
|
+
let id = lineIds.get(line)
|
|
433
|
+
if (id === undefined) {
|
|
434
|
+
id = nextLineId++
|
|
435
|
+
lineIds.set(line, id)
|
|
436
|
+
}
|
|
437
|
+
return id
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
// ── toolbar: level chips and match navigation ───────────────────────
|
|
441
|
+
const LEVEL_ORDER: LogLevel[] = ["trace", "debug", "info", "success", "warn", "error", "fatal"]
|
|
442
|
+
const levelCounts = computed(() => countLevels<LogLevel>(parsedLines.value, "info"))
|
|
443
|
+
const presentLevels = computed(() => LEVEL_ORDER.filter((l) => levelCounts.value.has(l)))
|
|
444
|
+
|
|
445
|
+
function toggleLevel(level: LogLevel) {
|
|
446
|
+
levels.value = levels.value.includes(level)
|
|
447
|
+
? levels.value.filter((l) => l !== level)
|
|
448
|
+
: [...levels.value, level]
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
const matches = computed(() => matchPositions(visibleLines.value.map((r) => r.line), term.value))
|
|
452
|
+
const current = ref(-1)
|
|
453
|
+
const currentRow = computed(() => {
|
|
454
|
+
const pos = matches.value[current.value]
|
|
455
|
+
return pos === undefined ? -1 : (visibleLines.value[pos]?.n ?? -1)
|
|
456
|
+
})
|
|
457
|
+
watch(term, () => {
|
|
458
|
+
current.value = -1
|
|
459
|
+
})
|
|
460
|
+
watch(matches, (list) => {
|
|
461
|
+
if (current.value >= list.length) current.value = list.length - 1
|
|
462
|
+
})
|
|
463
|
+
|
|
464
|
+
const matchLabel = computed(() => {
|
|
465
|
+
if (!term.value) return ""
|
|
466
|
+
const total = matches.value.length
|
|
467
|
+
if (!total) return text.value.noMatches
|
|
468
|
+
return text.value.matchCount
|
|
469
|
+
.replace("{current}", current.value >= 0 ? String(current.value + 1) : "–")
|
|
470
|
+
.replace("{total}", String(total))
|
|
471
|
+
})
|
|
472
|
+
|
|
473
|
+
function step(direction: 1 | -1) {
|
|
474
|
+
const next = stepMatch(current.value, matches.value.length, direction)
|
|
475
|
+
if (next < 0) return
|
|
476
|
+
current.value = next
|
|
477
|
+
pinned.value = false
|
|
478
|
+
nextTick(() => revealMatch(matches.value[next]))
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
// Brings the match to the middle of the viewport. A virtualised row may not be
|
|
482
|
+
// in the DOM yet, so the offset is computed from the fixed row height instead.
|
|
483
|
+
function revealMatch(position: number | undefined) {
|
|
484
|
+
const el = viewport()
|
|
485
|
+
if (!el || position === undefined) return
|
|
486
|
+
const behavior: ScrollBehavior = prefersReducedMotion() ? "auto" : "smooth"
|
|
487
|
+
if (virtualise.value) {
|
|
488
|
+
el.scrollTo({ top: Math.max(0, position * ROW_HEIGHT - el.clientHeight / 2), behavior })
|
|
489
|
+
return
|
|
490
|
+
}
|
|
491
|
+
const n = visibleLines.value[position]?.n
|
|
492
|
+
el.querySelector<HTMLElement>(`[data-log-row="${n}"]`)?.scrollIntoView({ block: "center", behavior })
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
const searchEl = ref<HTMLInputElement | null>(null)
|
|
496
|
+
|
|
497
|
+
function onSearchKeydown(event: KeyboardEvent) {
|
|
498
|
+
if (event.isComposing) return
|
|
499
|
+
if (event.key === "Enter") {
|
|
500
|
+
event.preventDefault()
|
|
501
|
+
step(event.shiftKey ? -1 : 1)
|
|
502
|
+
} else if (event.key === "Escape" && query.value) {
|
|
503
|
+
event.preventDefault()
|
|
504
|
+
event.stopPropagation()
|
|
505
|
+
query.value = ""
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
const searching = () => !!term.value && lookedAt()
|
|
510
|
+
|
|
511
|
+
useHotkeys(() => [
|
|
512
|
+
{ key: "f3", allowInInput: true, when: searching, handler: () => step(1) },
|
|
513
|
+
{ key: "f3", shift: true, allowInInput: true, when: searching, handler: () => step(-1) },
|
|
514
|
+
])
|
|
515
|
+
|
|
516
|
+
defineExpose({
|
|
517
|
+
next: () => step(1),
|
|
518
|
+
previous: () => step(-1),
|
|
519
|
+
focusSearch: () => searchEl.value?.focus(),
|
|
520
|
+
})
|
|
521
|
+
|
|
339
522
|
// True when filtering has emptied an otherwise non-empty stream.
|
|
340
523
|
const filteredEmpty = computed(
|
|
341
524
|
() => props.lines.length > 0 && visibleLines.value.length === 0,
|
|
@@ -356,7 +539,7 @@ function fmtTime(time?: number | string): string {
|
|
|
356
539
|
// A menu item closes the menu as it runs, leaving nothing on screen to say the
|
|
357
540
|
// copy happened — so unlike LpCodeBlock's button, this one announces itself.
|
|
358
541
|
const { copy: writeClipboard } = useClipboard()
|
|
359
|
-
const copy = (
|
|
542
|
+
const copy = (value: string) => writeClipboard(value, { toast: text.value.copied })
|
|
360
543
|
|
|
361
544
|
// The whole line as one copyable string: "time LEVEL [source] message".
|
|
362
545
|
function lineText(line: LogLine): string {
|
|
@@ -398,15 +581,15 @@ function onRowPointerDown(e: PointerEvent) {
|
|
|
398
581
|
function menuFor(line: LogLine, index: number): ContextMenuItemDef[] {
|
|
399
582
|
if (!props.rowMenu) return []
|
|
400
583
|
const items: ContextMenuItemDef[] = [
|
|
401
|
-
{ label:
|
|
402
|
-
{ label:
|
|
584
|
+
{ label: text.value.copyMessage, icon: "lucide:copy", onSelect: () => copy(line.message) },
|
|
585
|
+
{ label: text.value.copyLine, icon: "lucide:clipboard-list", onSelect: () => copy(lineText(line)) },
|
|
403
586
|
]
|
|
404
587
|
if (line.time != null) {
|
|
405
|
-
items.push({ label:
|
|
588
|
+
items.push({ label: text.value.copyTimestamp, icon: "lucide:clock", onSelect: () => copy(fmtTime(line.time)) })
|
|
406
589
|
}
|
|
407
590
|
if (line.source) {
|
|
408
591
|
items.push({
|
|
409
|
-
label:
|
|
592
|
+
label: text.value.filterSource.replace("{source}", line.source),
|
|
410
593
|
icon: "lucide:filter",
|
|
411
594
|
separatorBefore: true,
|
|
412
595
|
onSelect: () => emit("filter", { source: line.source }),
|
|
@@ -414,10 +597,13 @@ function menuFor(line: LogLine, index: number): ContextMenuItemDef[] {
|
|
|
414
597
|
}
|
|
415
598
|
if (line.level) {
|
|
416
599
|
items.push({
|
|
417
|
-
label:
|
|
600
|
+
label: text.value.filterLevel.replace("{level}", line.level),
|
|
418
601
|
icon: "lucide:filter",
|
|
419
602
|
separatorBefore: !line.source,
|
|
420
|
-
onSelect: () =>
|
|
603
|
+
onSelect: () => {
|
|
604
|
+
if (props.toolbar && line.level) levels.value = [line.level]
|
|
605
|
+
emit("filter", { level: line.level })
|
|
606
|
+
},
|
|
421
607
|
})
|
|
422
608
|
}
|
|
423
609
|
const extra = props.extraRowItems?.(line, index) ?? []
|
|
@@ -432,11 +618,10 @@ interface Part {
|
|
|
432
618
|
hit: boolean
|
|
433
619
|
}
|
|
434
620
|
function splitHighlight(message: string): Part[] {
|
|
435
|
-
|
|
436
|
-
if (!term) return [{ text: message, hit: false }]
|
|
621
|
+
if (!term.value) return [{ text: message, hit: false }]
|
|
437
622
|
const parts: Part[] = []
|
|
438
623
|
const lower = message.toLowerCase()
|
|
439
|
-
const needle = term.toLowerCase()
|
|
624
|
+
const needle = term.value.toLowerCase()
|
|
440
625
|
let i = 0
|
|
441
626
|
while (i < message.length) {
|
|
442
627
|
const at = lower.indexOf(needle, i)
|
|
@@ -569,6 +754,7 @@ function rideToBottom(smooth = true, force = false) {
|
|
|
569
754
|
}
|
|
570
755
|
|
|
571
756
|
function scrollToBottom() {
|
|
757
|
+
current.value = -1
|
|
572
758
|
rideToBottom(true, true)
|
|
573
759
|
pinned.value = true
|
|
574
760
|
}
|
|
@@ -576,8 +762,15 @@ function scrollToBottom() {
|
|
|
576
762
|
// Snapshot the scroll anchor BEFORE the DOM updates for a line change (flush:pre)
|
|
577
763
|
// so a prepend can be compensated post-update. Only matters when NOT pinned to
|
|
578
764
|
// the bottom (i.e. the user is reading history up the stream).
|
|
765
|
+
/*
|
|
766
|
+
* A capped buffer (shift + push, or slice(-N)) keeps the length constant once
|
|
767
|
+
* full, so the length alone stops seeing new lines. The array, its length and
|
|
768
|
+
* its last line together change on every append, prepend or replacement.
|
|
769
|
+
*/
|
|
770
|
+
const linesSignal = () => [props.lines, props.lines.length, props.lines[props.lines.length - 1]]
|
|
771
|
+
|
|
579
772
|
watch(
|
|
580
|
-
|
|
773
|
+
linesSignal,
|
|
581
774
|
() => {
|
|
582
775
|
if (!pinned.value) captureScrollAnchor()
|
|
583
776
|
},
|
|
@@ -587,10 +780,10 @@ watch(
|
|
|
587
780
|
// After the DOM updates: tail to the bottom if pinned, else keep the view glued
|
|
588
781
|
// to where it was so a top prepend (older history) doesn't yank it.
|
|
589
782
|
watch(
|
|
590
|
-
|
|
783
|
+
linesSignal,
|
|
591
784
|
async () => {
|
|
592
785
|
await nextTick()
|
|
593
|
-
if (props.tail && pinned.value) {
|
|
786
|
+
if (props.tail && pinned.value && current.value < 0) {
|
|
594
787
|
requestAnimationFrame(() => rideToBottom(true))
|
|
595
788
|
} else {
|
|
596
789
|
restoreScrollAnchor()
|
|
@@ -632,13 +825,13 @@ onBeforeUnmount(() => {
|
|
|
632
825
|
// (a component :is can't carry TransitionGroup-only props inertly otherwise).
|
|
633
826
|
const transitionProps = {
|
|
634
827
|
tag: "ol",
|
|
635
|
-
"enter-active-class": "transition duration-
|
|
828
|
+
"enter-active-class": "transition duration-[var(--duration-medium)] ease-[var(--ease-emphasized)]",
|
|
636
829
|
"enter-from-class": "-translate-x-1 opacity-0",
|
|
637
830
|
"leave-active-class":
|
|
638
|
-
"overflow-hidden transition-all duration-
|
|
831
|
+
"overflow-hidden transition-all duration-[var(--duration-medium)] ease-[var(--ease-emphasized)]",
|
|
639
832
|
"leave-from-class": "max-h-8 opacity-100",
|
|
640
833
|
"leave-to-class": "max-h-0 -translate-y-1 opacity-0",
|
|
641
|
-
"move-class": "transition-transform duration-
|
|
834
|
+
"move-class": "transition-transform duration-[var(--duration-medium)] ease-[var(--ease-emphasized)]",
|
|
642
835
|
}
|
|
643
836
|
|
|
644
837
|
// The viewer draws its own card frame and hides its overflow; a consumer
|
|
@@ -673,10 +866,82 @@ const { class: rootClass, attrs: rest } = useMergedAttrs(() =>
|
|
|
673
866
|
v-if="frozen"
|
|
674
867
|
class="pointer-events-none absolute top-2 left-1/2 z-10 -translate-x-1/2 rounded-pill bg-brand px-2.5 py-1 text-[11px] font-medium text-ink-inverse"
|
|
675
868
|
>
|
|
676
|
-
{{ frozenLabel }}
|
|
869
|
+
{{ frozenLabel ?? text.frozen }}
|
|
677
870
|
</div>
|
|
678
871
|
</Transition>
|
|
679
872
|
|
|
873
|
+
<div
|
|
874
|
+
v-if="toolbar"
|
|
875
|
+
class="flex flex-wrap items-center gap-2 border-b border-line px-2 py-1.5 font-sans"
|
|
876
|
+
>
|
|
877
|
+
<div class="flex min-w-48 flex-1 items-center gap-1.5 rounded-control bg-surface-soft px-2 focus-within:ring-2 focus-within:ring-ring">
|
|
878
|
+
<LpIcon name="lucide:search" :size="14" class="shrink-0 text-muted" />
|
|
879
|
+
<input
|
|
880
|
+
ref="searchEl"
|
|
881
|
+
v-model="query"
|
|
882
|
+
type="search"
|
|
883
|
+
:aria-label="text.search"
|
|
884
|
+
:placeholder="text.search"
|
|
885
|
+
class="h-7 min-w-0 flex-1 bg-transparent text-xs text-ink outline-none placeholder:text-muted [&::-webkit-search-cancel-button]:hidden"
|
|
886
|
+
@keydown="onSearchKeydown"
|
|
887
|
+
/>
|
|
888
|
+
<span
|
|
889
|
+
v-if="term"
|
|
890
|
+
class="shrink-0 text-[11px] tabular-nums text-muted"
|
|
891
|
+
aria-live="polite"
|
|
892
|
+
>{{ matchLabel }}</span>
|
|
893
|
+
<button
|
|
894
|
+
v-if="query"
|
|
895
|
+
type="button"
|
|
896
|
+
class="rounded-xs p-0.5 text-muted outline-none hover:text-ink focus-visible:ring-2 focus-visible:ring-ring"
|
|
897
|
+
:aria-label="text.clearSearch"
|
|
898
|
+
@click="query = ''; searchEl?.focus()"
|
|
899
|
+
>
|
|
900
|
+
<LpIcon name="lucide:x" :size="12" />
|
|
901
|
+
</button>
|
|
902
|
+
</div>
|
|
903
|
+
<div class="flex items-center">
|
|
904
|
+
<button
|
|
905
|
+
type="button"
|
|
906
|
+
class="rounded-xs p-1 text-muted outline-none hover:text-ink focus-visible:ring-2 focus-visible:ring-ring disabled:opacity-40"
|
|
907
|
+
:aria-label="text.previousMatch"
|
|
908
|
+
:disabled="!matches.length"
|
|
909
|
+
@click="step(-1)"
|
|
910
|
+
>
|
|
911
|
+
<LpIcon name="lucide:chevron-up" :size="14" />
|
|
912
|
+
</button>
|
|
913
|
+
<button
|
|
914
|
+
type="button"
|
|
915
|
+
class="rounded-xs p-1 text-muted outline-none hover:text-ink focus-visible:ring-2 focus-visible:ring-ring disabled:opacity-40"
|
|
916
|
+
:aria-label="text.nextMatch"
|
|
917
|
+
:disabled="!matches.length"
|
|
918
|
+
@click="step(1)"
|
|
919
|
+
>
|
|
920
|
+
<LpIcon name="lucide:chevron-down" :size="14" />
|
|
921
|
+
</button>
|
|
922
|
+
</div>
|
|
923
|
+
<div
|
|
924
|
+
v-if="presentLevels.length > 1"
|
|
925
|
+
role="group"
|
|
926
|
+
:aria-label="text.levels"
|
|
927
|
+
class="flex flex-wrap items-center gap-1"
|
|
928
|
+
>
|
|
929
|
+
<button
|
|
930
|
+
v-for="level in presentLevels"
|
|
931
|
+
:key="level"
|
|
932
|
+
type="button"
|
|
933
|
+
class="flex items-center gap-1 rounded-pill border px-2 py-0.5 text-[10px] font-semibold uppercase tracking-[0.06em] outline-none transition-colors focus-visible:ring-2 focus-visible:ring-ring"
|
|
934
|
+
:class="levels.includes(level) ? 'border-line-strong bg-surface-soft text-ink' : 'border-line text-muted hover:text-ink'"
|
|
935
|
+
:aria-pressed="levels.includes(level)"
|
|
936
|
+
@click="toggleLevel(level)"
|
|
937
|
+
>
|
|
938
|
+
<span class="size-1.5 rounded-full" :class="metaFor(level).rail" aria-hidden="true" />
|
|
939
|
+
{{ level }}
|
|
940
|
+
<span class="font-normal tabular-nums text-muted">{{ levelCounts.get(level) }}</span>
|
|
941
|
+
</button>
|
|
942
|
+
</div>
|
|
943
|
+
</div>
|
|
944
|
+
|
|
680
945
|
<!-- `instant`: this viewer decides for itself when a jump animates (see
|
|
681
946
|
rideToBottom) and restores scrollTop directly after prepending older
|
|
682
947
|
lines. A CSS smooth scroll would override both. -->
|
|
@@ -691,8 +956,8 @@ const { class: rootClass, attrs: rest } = useMergedAttrs(() =>
|
|
|
691
956
|
v-if="lines.length === 0 || filteredEmpty"
|
|
692
957
|
class="flex h-full flex-col items-center justify-center gap-1 px-4 py-10 text-center"
|
|
693
958
|
>
|
|
694
|
-
<span class="text-muted">{{ filteredEmpty ?
|
|
695
|
-
<span v-if="filteredEmpty" class="text-muted/70">“{{
|
|
959
|
+
<span class="text-muted">{{ filteredEmpty ? text.noMatch : (emptyLabel ?? text.empty) }}</span>
|
|
960
|
+
<span v-if="filteredEmpty && term" class="text-muted/70">“{{ term }}”</span>
|
|
696
961
|
</div>
|
|
697
962
|
|
|
698
963
|
<template v-else>
|
|
@@ -704,7 +969,7 @@ const { class: rootClass, attrs: rest } = useMergedAttrs(() =>
|
|
|
704
969
|
class="flex items-center justify-center gap-2 py-2 text-muted"
|
|
705
970
|
>
|
|
706
971
|
<LpIcon name="lucide:loader-circle" :size="14" class="animate-spin" />
|
|
707
|
-
<span class="text-[11px]">
|
|
972
|
+
<span class="text-[11px]">{{ text.loadingOlder }}</span>
|
|
708
973
|
</div>
|
|
709
974
|
|
|
710
975
|
<!-- ONE context menu wraps the whole list (not each row): @contextmenu
|
|
@@ -738,10 +1003,11 @@ const { class: rootClass, attrs: rest } = useMergedAttrs(() =>
|
|
|
738
1003
|
/>
|
|
739
1004
|
<li
|
|
740
1005
|
v-for="{ line, n, count } in renderedLines"
|
|
741
|
-
:key="n"
|
|
1006
|
+
:key="lineKey(n)"
|
|
742
1007
|
:data-log-row="n"
|
|
743
1008
|
class="group relative flex items-start gap-0 px-0 leading-5 transition-colors hover:bg-surface-soft/60"
|
|
744
|
-
:class="metaFor(line.level).sel"
|
|
1009
|
+
:class="[metaFor(line.level).sel, n === currentRow ? 'bg-accent/15' : '']"
|
|
1010
|
+
:aria-current="n === currentRow ? 'true' : undefined"
|
|
745
1011
|
>
|
|
746
1012
|
<!-- level rail -->
|
|
747
1013
|
<!-- Level rail: absolutely positioned so it spans the FULL row height
|
|
@@ -759,7 +1025,7 @@ const { class: rootClass, attrs: rest } = useMergedAttrs(() =>
|
|
|
759
1025
|
<span
|
|
760
1026
|
v-if="lineNumbers"
|
|
761
1027
|
class="mr-3 w-10 shrink-0 select-none text-right tabular-nums text-muted/70"
|
|
762
|
-
>{{ n + 1 }}</span>
|
|
1028
|
+
>{{ n + lineOffset + 1 }}</span>
|
|
763
1029
|
|
|
764
1030
|
<!-- timestamp -->
|
|
765
1031
|
<span
|
|
@@ -785,7 +1051,13 @@ const { class: rootClass, attrs: rest } = useMergedAttrs(() =>
|
|
|
785
1051
|
class="min-w-0 pr-3"
|
|
786
1052
|
:class="[metaFor(line.level).text, wrap ? 'whitespace-pre-wrap break-words' : 'whitespace-pre']"
|
|
787
1053
|
>
|
|
788
|
-
|
|
1054
|
+
<!-- Virtualised rows must stay one row tall, so a multi-line
|
|
1055
|
+
message (a stack trace) is shown on one line with ↵ marks;
|
|
1056
|
+
copying still yields the original text. -->
|
|
1057
|
+
<template
|
|
1058
|
+
v-for="(part, p) in splitHighlight(virtualise ? line.message.replace(/\r?\n/g, ' ↵ ') : line.message)"
|
|
1059
|
+
:key="p"
|
|
1060
|
+
>
|
|
789
1061
|
<mark
|
|
790
1062
|
v-if="part.hit"
|
|
791
1063
|
class="rounded-xs bg-accent/30 text-ink"
|
|
@@ -796,14 +1068,14 @@ const { class: rootClass, attrs: rest } = useMergedAttrs(() =>
|
|
|
796
1068
|
Keyed on count so each new fold re-mounts the chip and pops it,
|
|
797
1069
|
cueing where the duplicate just merged. -->
|
|
798
1070
|
<Transition
|
|
799
|
-
enter-active-class="
|
|
1071
|
+
enter-active-class="transition duration-[var(--duration-medium)] ease-[var(--ease-emphasized)]"
|
|
800
1072
|
enter-from-class="scale-50 opacity-0"
|
|
801
1073
|
>
|
|
802
1074
|
<span
|
|
803
1075
|
v-if="count > 1"
|
|
804
1076
|
:key="count"
|
|
805
|
-
class="ml-2 select-none rounded-pill bg-surface-soft px-1.5 align-[1px] text-[10px] font-semibold tabular-nums text-muted-strong"
|
|
806
|
-
:title="
|
|
1077
|
+
class="ml-2 inline-block select-none rounded-pill bg-surface-soft px-1.5 align-[1px] text-[10px] font-semibold tabular-nums text-muted-strong"
|
|
1078
|
+
:title="text.identical.replace('{n}', String(count))"
|
|
807
1079
|
>×{{ count }}</span>
|
|
808
1080
|
</Transition>
|
|
809
1081
|
</span>
|
|
@@ -823,9 +1095,9 @@ const { class: rootClass, attrs: rest } = useMergedAttrs(() =>
|
|
|
823
1095
|
Transition rides an outer wrapper so its transform/opacity don't fight
|
|
824
1096
|
the button's own -translate-x-1/2 centring and hover colour transition. -->
|
|
825
1097
|
<Transition
|
|
826
|
-
enter-active-class="transition duration-
|
|
1098
|
+
enter-active-class="transition duration-[var(--duration-medium)] ease-[var(--ease-emphasized)]"
|
|
827
1099
|
enter-from-class="translate-y-3 scale-90 opacity-0"
|
|
828
|
-
leave-active-class="transition duration-
|
|
1100
|
+
leave-active-class="transition duration-[var(--duration-fast)] ease-in"
|
|
829
1101
|
leave-to-class="translate-y-3 scale-90 opacity-0"
|
|
830
1102
|
>
|
|
831
1103
|
<div v-if="showJump" class="absolute inset-x-0 bottom-3 flex justify-center">
|
|
@@ -837,9 +1109,9 @@ const { class: rootClass, attrs: rest } = useMergedAttrs(() =>
|
|
|
837
1109
|
<LpIcon
|
|
838
1110
|
name="lucide:arrow-down"
|
|
839
1111
|
:size="14"
|
|
840
|
-
class="transition-transform duration-
|
|
1112
|
+
class="transition-transform duration-[var(--duration-medium)] ease-[var(--ease-emphasized)] group-hover:translate-y-0.5"
|
|
841
1113
|
/>
|
|
842
|
-
|
|
1114
|
+
{{ text.jump }}
|
|
843
1115
|
</button>
|
|
844
1116
|
</div>
|
|
845
1117
|
</Transition>
|
|
@@ -25,6 +25,7 @@ import { prefersReducedMotion } from "../composables/easing"
|
|
|
25
25
|
import { type LatLon, OSM_TILES, type TileSource, project, tileUrl, unproject } from "./address"
|
|
26
26
|
import LpIcon from "./LpIcon.vue"
|
|
27
27
|
import { useMergedAttrs } from "../composables/useMergedClass"
|
|
28
|
+
import { useLabels } from "../composables/useLpConfig"
|
|
28
29
|
|
|
29
30
|
const props = withDefaults(
|
|
30
31
|
defineProps<{
|
|
@@ -45,6 +46,8 @@ const props = withDefaults(
|
|
|
45
46
|
zoomOutLabel?: string
|
|
46
47
|
locateLabel?: string
|
|
47
48
|
recentreLabel?: string
|
|
49
|
+
/** Accessible name of the map itself. */
|
|
50
|
+
mapLabel?: string
|
|
48
51
|
}>(),
|
|
49
52
|
{
|
|
50
53
|
tiles: () => OSM_TILES,
|
|
@@ -52,15 +55,26 @@ const props = withDefaults(
|
|
|
52
55
|
center: () => ({ lat: 50.11, lon: 14.44 }),
|
|
53
56
|
height: "20rem",
|
|
54
57
|
locatable: true,
|
|
55
|
-
zoomInLabel: "Zoom in",
|
|
56
|
-
zoomOutLabel: "Zoom out",
|
|
57
|
-
locateLabel: "Use my location",
|
|
58
|
-
recentreLabel: "Back to the pin",
|
|
59
58
|
},
|
|
60
59
|
)
|
|
61
60
|
|
|
62
61
|
const emit = defineEmits<{ (e: "update:modelValue", value: LatLon): void }>()
|
|
63
62
|
|
|
63
|
+
const labelSet = useLabels("mapPicker", {
|
|
64
|
+
zoomIn: "Zoom in",
|
|
65
|
+
zoomOut: "Zoom out",
|
|
66
|
+
locate: "Use my location",
|
|
67
|
+
recentre: "Back to the pin",
|
|
68
|
+
map: "Map — arrows pan, +/− zoom, Enter drops the pin in the centre",
|
|
69
|
+
})
|
|
70
|
+
const text = computed(() => ({
|
|
71
|
+
zoomIn: props.zoomInLabel ?? labelSet.value.zoomIn,
|
|
72
|
+
zoomOut: props.zoomOutLabel ?? labelSet.value.zoomOut,
|
|
73
|
+
locate: props.locateLabel ?? labelSet.value.locate,
|
|
74
|
+
recentre: props.recentreLabel ?? labelSet.value.recentre,
|
|
75
|
+
map: props.mapLabel ?? labelSet.value.map,
|
|
76
|
+
}))
|
|
77
|
+
|
|
64
78
|
const PAN_STEP = 80
|
|
65
79
|
const ZOOM_MS = 220
|
|
66
80
|
|
|
@@ -452,6 +466,15 @@ function onKeydown(e: KeyboardEvent) {
|
|
|
452
466
|
e.preventDefault()
|
|
453
467
|
recentre()
|
|
454
468
|
break
|
|
469
|
+
// Without this a keyboard could pan and zoom but never actually pick.
|
|
470
|
+
case "Enter":
|
|
471
|
+
case " ": {
|
|
472
|
+
const el = viewport.value
|
|
473
|
+
if (!el) return
|
|
474
|
+
e.preventDefault()
|
|
475
|
+
emit("update:modelValue", toLatLon({ x: el.clientWidth / 2, y: el.clientHeight / 2 }))
|
|
476
|
+
break
|
|
477
|
+
}
|
|
455
478
|
}
|
|
456
479
|
}
|
|
457
480
|
|
|
@@ -563,7 +586,7 @@ const { class: rootClass, attrs: rest } = useMergedAttrs(
|
|
|
563
586
|
:class="disabled ? 'cursor-not-allowed opacity-55' : panning ? 'cursor-grabbing' : 'cursor-grab'"
|
|
564
587
|
:tabindex="disabled ? -1 : 0"
|
|
565
588
|
role="application"
|
|
566
|
-
:aria-label="
|
|
589
|
+
:aria-label="text.map"
|
|
567
590
|
@pointerdown="onPointerDown"
|
|
568
591
|
@pointermove="onPointerMove"
|
|
569
592
|
@pointerup="onPointerUp"
|
|
@@ -616,8 +639,8 @@ const { class: rootClass, attrs: rest } = useMergedAttrs(
|
|
|
616
639
|
:class="CONTROL"
|
|
617
640
|
type="button"
|
|
618
641
|
:disabled="disabled || level >= maxZoom"
|
|
619
|
-
:title="
|
|
620
|
-
:aria-label="
|
|
642
|
+
:title="text.zoomIn"
|
|
643
|
+
:aria-label="text.zoomIn"
|
|
621
644
|
@click="zoomBy(1)"
|
|
622
645
|
>
|
|
623
646
|
<LpIcon name="lucide:plus" :size="16" />
|
|
@@ -626,8 +649,8 @@ const { class: rootClass, attrs: rest } = useMergedAttrs(
|
|
|
626
649
|
:class="CONTROL"
|
|
627
650
|
type="button"
|
|
628
651
|
:disabled="disabled || level <= minZoom"
|
|
629
|
-
:title="
|
|
630
|
-
:aria-label="
|
|
652
|
+
:title="text.zoomOut"
|
|
653
|
+
:aria-label="text.zoomOut"
|
|
631
654
|
@click="zoomBy(-1)"
|
|
632
655
|
>
|
|
633
656
|
<LpIcon name="lucide:minus" :size="16" />
|
|
@@ -636,8 +659,8 @@ const { class: rootClass, attrs: rest } = useMergedAttrs(
|
|
|
636
659
|
v-if="canRecentre"
|
|
637
660
|
:class="CONTROL"
|
|
638
661
|
type="button"
|
|
639
|
-
:title="
|
|
640
|
-
:aria-label="
|
|
662
|
+
:title="text.recentre"
|
|
663
|
+
:aria-label="text.recentre"
|
|
641
664
|
@click="recentre"
|
|
642
665
|
>
|
|
643
666
|
<LpIcon name="lucide:locate-fixed" :size="16" />
|
|
@@ -647,8 +670,8 @@ const { class: rootClass, attrs: rest } = useMergedAttrs(
|
|
|
647
670
|
:class="CONTROL"
|
|
648
671
|
type="button"
|
|
649
672
|
:disabled="disabled || locating"
|
|
650
|
-
:title="
|
|
651
|
-
:aria-label="
|
|
673
|
+
:title="text.locate"
|
|
674
|
+
:aria-label="text.locate"
|
|
652
675
|
@click="locate"
|
|
653
676
|
>
|
|
654
677
|
<LpIcon
|