@leavepulse/ui 0.68.0 → 0.70.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (640) hide show
  1. package/README.md +37 -0
  2. package/dist/assets/LoaderMark-DxBV2iDJ.css +43 -0
  3. package/dist/assets/LpActivityLine-B3EwNQEB.css +33 -0
  4. package/dist/assets/{LpCommandPalette-DxyjhYn_.css → LpCommandPalette-u-zOxKWa.css} +21 -31
  5. package/dist/assets/LpDock-Cbh0wQUv.css +17 -0
  6. package/dist/assets/{LpDropzone-Qb8k7cg4.css → LpDropzone-Bb1vBTeU.css} +12 -17
  7. package/dist/assets/{LpImageEditor-CElPDzpF.css → LpImageEditor-XM5bAziW.css} +6 -6
  8. package/dist/assets/LpLogViewer-BDNW6P52.css +38 -0
  9. package/dist/assets/{LpPalettePreview-CJmAKgYW.css → LpPalettePreview-B9VKDrvA.css} +2 -7
  10. package/dist/assets/LpShinyText-D69cBYDR.css +61 -0
  11. package/dist/assets/LpStatus-430OGTZY.css +17 -0
  12. package/dist/assets/LpStepMark-Drvx33Vg.css +10 -0
  13. package/dist/assets/{LpTable-BPERfJIk.css → LpTable-DHxEQ_-3.css} +19 -19
  14. package/dist/assets/LpTabs-BJ-nrJuI.css +18 -0
  15. package/dist/assets/LpTextReveal-DjI1MsDS.css +35 -0
  16. package/dist/assets/LpTypewriter-CXAm9Nca.css +27 -0
  17. package/dist/chunks/AttentionDot-DCBf4aWg.js +25 -0
  18. package/dist/chunks/{ContextMenuItems-B7XFHksm.js → ContextMenuItems-B5uE8Znw.js} +1 -1
  19. package/dist/chunks/{DropzoneLinkField-CjkrpDqO.js → DropzoneLinkField-Bw8Flkhk.js} +2 -2
  20. package/dist/chunks/FieldWrapper-BiennROB.js +43 -0
  21. package/dist/chunks/{LayoutCanvas-DF9CovaK.js → LayoutCanvas-BsJIzhdd.js} +1 -1
  22. package/dist/chunks/{LayoutNode-D9bIFzk0.js → LayoutNode-DJVhkSGk.js} +6 -8
  23. package/dist/chunks/LegendSwatch-BhRjrtzv.js +18 -0
  24. package/dist/chunks/LoaderMark-C1dubiKn.js +85 -0
  25. package/dist/chunks/{LpActionBar-MPKMu3pz.js → LpActionBar-CzOFar5t.js} +14 -20
  26. package/dist/chunks/LpActivityLine-CJvzrNpj.js +95 -0
  27. package/dist/chunks/{LpAddressInput-DG0BTqdH.js → LpAddressInput-CNnLTDZg.js} +7 -7
  28. package/dist/chunks/LpAlert-C5igj802.js +119 -0
  29. package/dist/chunks/{LpAppShell-DPh1756q.js → LpAppShell-CEUAB8kK.js} +15 -16
  30. package/dist/chunks/{LpAutocomplete-Dgn6hw3p.js → LpAutocomplete-D92niDHE.js} +15 -32
  31. package/dist/chunks/{LpAvatar-Bs0Fb1lC.js → LpAvatar-CLPl-qOF.js} +18 -6
  32. package/dist/chunks/LpBadge-HOEPBYmi.js +126 -0
  33. package/dist/chunks/LpBreadcrumbs-DYoPc3P4.js +144 -0
  34. package/dist/chunks/{LpButton-DNR-YAq5.js → LpButton-CVxBfGjd.js} +12 -4
  35. package/dist/chunks/{LpCalendar-BLya39Fh.js → LpCalendar-pVL91-Pl.js} +1 -1
  36. package/dist/chunks/{LpCard-Bf71NXZq.js → LpCard-CqKLnEhl.js} +18 -8
  37. package/dist/chunks/{LpCheckbox-Da5oZPHo.js → LpCheckbox-DHc2bHDe.js} +9 -6
  38. package/dist/chunks/{LpCodeBlock-lLbmslfs.js → LpCodeBlock-BGsQhxw8.js} +24 -32
  39. package/dist/chunks/LpCodeDisplay-CIKKT2iW.js +189 -0
  40. package/dist/chunks/LpCollapse-CWS9M_lm.js +115 -0
  41. package/dist/chunks/{LpColorPicker-DtEfBAcK.js → LpColorPicker-DFugXNUi.js} +10 -5
  42. package/dist/chunks/{LpConfirmDialog-D7s4jakB.js → LpConfirmDialog-CfGCux8m.js} +3 -3
  43. package/dist/chunks/{LpConfirmHost-DrYI5y1K.js → LpConfirmHost-exB7JmcO.js} +1 -1
  44. package/dist/chunks/{LpContextMenu-CRnjoqSt.js → LpContextMenu-COwOwRgI.js} +13 -5
  45. package/dist/chunks/LpCopyButton-DzHjnFs0.js +133 -0
  46. package/dist/chunks/LpCountdown-Dtq1ihlJ.js +231 -0
  47. package/dist/chunks/LpDataList-B-5nHfNw.js +120 -0
  48. package/dist/chunks/LpDatePicker-BUe1VUC0.js +168 -0
  49. package/dist/chunks/LpDateRangePicker-C_k65vGR.js +126 -0
  50. package/dist/chunks/{LpDisclosure-6AplYBub.js → LpDisclosure-DOarzlAG.js} +22 -11
  51. package/dist/chunks/{LpDrawer-Q4SOxM4s.js → LpDrawer-DGNKRsGq.js} +9 -4
  52. package/dist/chunks/{LpDropdownMenu-DWTt0ooM.js → LpDropdownMenu-CRgrhdAH.js} +10 -5
  53. package/dist/chunks/LpEditable-DTqLt2lT.js +236 -0
  54. package/dist/chunks/{LpEmptyState-om1UnFjV.js → LpEmptyState-KTKipws2.js} +1 -1
  55. package/dist/chunks/{LpFieldNotice-CHuDeLjc.js → LpFieldNotice-CurbNuXI.js} +1 -1
  56. package/dist/chunks/{LpFileTree-CW24YsVX.js → LpFileTree-DMISXVHC.js} +5 -3
  57. package/dist/chunks/{LpFileTreeNode-BXhTHaKW.js → LpFileTreeNode-CPXB_Yy3.js} +42 -55
  58. package/dist/chunks/{LpFormField-DPb47HEm.js → LpFormField-BWwgm1E4.js} +23 -9
  59. package/dist/chunks/LpFrame-BVQCQLDI.js +155 -0
  60. package/dist/chunks/LpGauge-Bk8TwJj0.js +204 -0
  61. package/dist/chunks/LpGlowBorder-B8V-QzIl.js +40 -0
  62. package/dist/chunks/{LpHintExample-C1eKYdeL.js → LpHintExample-9Py0wc-L.js} +11 -34
  63. package/dist/chunks/{LpHintLayer-7UOdWQG3.js → LpHintLayer-p4hqzdyA.js} +27 -14
  64. package/dist/chunks/{LpHints-BUtBp9-w.js → LpHints-CCrhRfy9.js} +3 -3
  65. package/dist/chunks/LpHoverCard-spGxyw7T.js +130 -0
  66. package/dist/chunks/{LpIcon-6tSrZ3Zy.js → LpIcon-HJP5xMz4.js} +1 -1
  67. package/dist/chunks/LpIdenticon-BzdC_qz9.js +63 -0
  68. package/dist/chunks/{LpInfraNode-Ds_8SZgN.js → LpInfraNode-CUC3t4na.js} +39 -25
  69. package/dist/chunks/{LpInput-CUyr6PIE.js → LpInput-CDuwODJG.js} +44 -18
  70. package/dist/chunks/{LpLightbox-B9J8rzsj.js → LpLightbox-XJ0LpTcZ.js} +69 -69
  71. package/dist/chunks/LpLoader-DjJEX8hL.js +92 -0
  72. package/dist/chunks/{LpLogViewer-4QFc-sVN.js → LpLogViewer-elZHbj85.js} +225 -129
  73. package/dist/chunks/LpMagnetic-CtXKLa2Y.js +62 -0
  74. package/dist/chunks/{LpMapPicker-BTbkFeSG.js → LpMapPicker-BtIl7NOM.js} +34 -31
  75. package/dist/chunks/LpMasonry--k0571qG.js +185 -0
  76. package/dist/chunks/{LpMediaGallery-CuPcscx3.js → LpMediaGallery-D5swlmZ_.js} +3 -3
  77. package/dist/chunks/{LpModal-hk1noDcr.js → LpModal-eBMt2Fqn.js} +8 -3
  78. package/dist/chunks/{LpModalNav-DQd2keZm.js → LpModalNav-D4Z0wVHR.js} +4 -4
  79. package/dist/chunks/{LpNotificationBell-X84PJIxD.js → LpNotificationBell-DPZmP5dM.js} +32 -48
  80. package/dist/chunks/LpNumberField-B9k-h2HE.js +140 -0
  81. package/dist/chunks/{LpNumberFlow-z4WPZcZu.js → LpNumberFlow-Cfyh033Q.js} +8 -28
  82. package/dist/chunks/{LpOtpInput-PbeNJ5In.js → LpOtpInput-CeBPsM7D.js} +2 -1
  83. package/dist/chunks/{LpPagination-Bx-VG4Ag.js → LpPagination-VNCX6pFC.js} +37 -6
  84. package/dist/chunks/{LpPalettePreview-CFav_7co.js → LpPalettePreview-BNB8T_MY.js} +1 -1
  85. package/dist/chunks/{LpPasswordInput-CbnXitAS.js → LpPasswordInput-DTV18FIh.js} +20 -20
  86. package/dist/chunks/{LpPhoneInput-DP69HONS.js → LpPhoneInput-C60tx1tC.js} +12 -7
  87. package/dist/chunks/{LpPie-yLF9-aDR.js → LpPie-CFuV7pZv.js} +158 -47
  88. package/dist/chunks/LpPipeline-D81yh_NX.js +329 -0
  89. package/dist/chunks/{LpPopover-DVnkHaOu.js → LpPopover-BEIcqb9p.js} +8 -3
  90. package/dist/chunks/{LpProgress-CA86wxXu.js → LpProgress-CE-m5OpD.js} +23 -63
  91. package/dist/chunks/LpQuota-2Sw_gqcJ.js +162 -0
  92. package/dist/chunks/{LpRadioGroup-8S5hf8lE.js → LpRadioGroup-CGBr5nIp.js} +1 -1
  93. package/dist/chunks/{LpRangeCalendar-ByQi-WUz.js → LpRangeCalendar-c5qj7YcC.js} +1 -1
  94. package/dist/chunks/{LpRangeSlider-C1yF9-nP.js → LpRangeSlider-Dv6lNvHY.js} +6 -2
  95. package/dist/chunks/LpRelativeTime-CKzVqAo9.js +73 -0
  96. package/dist/chunks/LpRepeater-D0VjmD6y.js +140 -0
  97. package/dist/chunks/LpRevealOnScroll-DcQjexLy.js +76 -0
  98. package/dist/chunks/LpScramble-TDO5Kdou.js +85 -0
  99. package/dist/chunks/LpSecretField-DMfdJ1cQ.js +307 -0
  100. package/dist/chunks/{LpSection-BmRXe6ZP.js → LpSection-BW4xJ-Li.js} +1 -1
  101. package/dist/chunks/{LpSegmented-CW3bFHM4.js → LpSegmented-BO99Sr4m.js} +11 -14
  102. package/dist/chunks/{LpSelect-BCyDYoih.js → LpSelect-B4f_yRi5.js} +93 -108
  103. package/dist/chunks/LpShaderBackdrop-Bote0iuy.js +332 -0
  104. package/dist/chunks/LpShinyText-DX2ajtWq.js +48 -0
  105. package/dist/chunks/{LpSidebar-DfIiS-lB.js → LpSidebar-D-M0Y_7U.js} +5 -3
  106. package/dist/chunks/{LpSidebarNav-C5FDNI0t.js → LpSidebarNav-DF-TYxOV.js} +3 -3
  107. package/dist/chunks/{LpSlider-Cuzmrd7p.js → LpSlider-DuoRpOrH.js} +6 -2
  108. package/dist/chunks/{LpSparkline-DXT4KrSi.js → LpSparkline-CExIRDb3.js} +32 -27
  109. package/dist/chunks/LpSpotlight-4F3NeUMY.js +41 -0
  110. package/dist/chunks/{LpStat-iBMF5QNk.js → LpStat-B2ix1Q7t.js} +38 -35
  111. package/dist/chunks/LpStatus-C7kx5PKE.js +86 -0
  112. package/dist/chunks/LpStepMark-DK-5BKAt.js +101 -0
  113. package/dist/chunks/LpStepper-DRFfdJaq.js +76 -0
  114. package/dist/chunks/{LpSwap-D82jiecV.js → LpSwap-DzE6M3Mg.js} +1 -1
  115. package/dist/chunks/LpSwitch-DrzKrZZV.js +112 -0
  116. package/dist/chunks/{LpTableFilter-CuIbgdW0.js → LpTableFilter-DyvHGbOk.js} +3 -2
  117. package/dist/chunks/{LpTableOfContents-C_4VBktT.js → LpTableOfContents-rYvo8z5T.js} +34 -21
  118. package/dist/chunks/{LpTagsInput-DiXrpYeE.js → LpTagsInput-DgpBFRV2.js} +68 -71
  119. package/dist/chunks/LpTextReveal-CtAnOUkp.js +140 -0
  120. package/dist/chunks/{LpTextarea-xoQd5Lnp.js → LpTextarea-DiWjeVoJ.js} +12 -11
  121. package/dist/chunks/{LpThemeProvider-CQ2jC9TA.js → LpThemeProvider-DcbfOxsU.js} +1 -1
  122. package/dist/chunks/LpThemeStudio-DguwHdGt.js +416 -0
  123. package/dist/chunks/LpThemeStudioSection-BwhqYDLf.js +20 -0
  124. package/dist/chunks/LpThemeSwitcher-D5uz0MhK.js +278 -0
  125. package/dist/chunks/{LpTimeField-MFgAsNc5.js → LpTimeField-BFQVf_PW.js} +13 -5
  126. package/dist/chunks/{LpToaster-C4zE9zB7.js → LpToaster-Wv9ksj4q.js} +49 -82
  127. package/dist/chunks/{LpTooltip-suFCM34F.js → LpTooltip-Cz8SQwRt.js} +8 -3
  128. package/dist/chunks/{LpTopologyCanvas-6ozNrQKh.js → LpTopologyCanvas-ZhKthfgz.js} +1 -1
  129. package/dist/chunks/{LpTour-BUp3Jt2e.js → LpTour-NfRJNEIP.js} +24 -18
  130. package/dist/chunks/LpTruncate-D_5Pfv5B.js +112 -0
  131. package/dist/chunks/LpTypewriter-BCL_a6wE.js +159 -0
  132. package/dist/chunks/{LpUptimeBar-B6kHw4Cj.js → LpUptimeBar-DWegV2p6.js} +9 -8
  133. package/dist/chunks/{LpVideoBackdrop-qWRYBOTQ.js → LpVideoBackdrop-DS1wGv-X.js} +7 -8
  134. package/dist/chunks/{MenuTitle-obgek0B7.js → MenuTitle-BjQZX8tQ.js} +1 -1
  135. package/dist/chunks/OptionContent-CmYUUeWj.js +40 -0
  136. package/dist/chunks/PickerField-CHRS1h7K.js +116 -0
  137. package/dist/chunks/RevealToggle-C-KzaQGJ.js +65 -0
  138. package/dist/chunks/StepConnector-37OgEOXA.js +41 -0
  139. package/dist/chunks/TourCard-BXuVHsfn.js +123 -0
  140. package/dist/chunks/backdrop-Dkf2vM81.js +59 -0
  141. package/dist/chunks/{icons-DVhZ_plB.js → icons-4IywzkHk.js} +1 -1
  142. package/dist/chunks/{useTheme-2qHG8NEU.js → useTheme-BOV_-AiY.js} +9 -65
  143. package/dist/component-names.d.ts +1 -1
  144. package/dist/components/AttentionDot.vue.d.ts +9 -0
  145. package/dist/components/AttentionDot.vue.js +4 -0
  146. package/dist/components/ContextMenuItems.vue.js +1 -1
  147. package/dist/components/DropzoneLinkField.vue.js +1 -1
  148. package/dist/components/FieldWrapper.vue.d.ts +25 -0
  149. package/dist/components/FieldWrapper.vue.js +4 -0
  150. package/dist/components/LayoutCanvas.vue.js +1 -1
  151. package/dist/components/LayoutNode.vue.d.ts +4 -4
  152. package/dist/components/LayoutNode.vue.js +1 -1
  153. package/dist/components/LegendSwatch.vue.d.ts +6 -0
  154. package/dist/components/LegendSwatch.vue.js +4 -0
  155. package/dist/components/LoaderMark.vue.d.ts +12 -0
  156. package/dist/components/LoaderMark.vue.js +4 -0
  157. package/dist/components/LpActionBar.vue.js +1 -1
  158. package/dist/components/LpActivityLine.vue.d.ts +58 -0
  159. package/dist/components/LpActivityLine.vue.js +4 -0
  160. package/dist/components/LpAddressInput.vue.js +1 -1
  161. package/dist/components/LpAlert.vue.d.ts +2 -2
  162. package/dist/components/LpAlert.vue.js +1 -1
  163. package/dist/components/LpAppShell.vue.d.ts +8 -7
  164. package/dist/components/LpAppShell.vue.js +1 -1
  165. package/dist/components/LpAutocomplete.vue.js +1 -1
  166. package/dist/components/LpAvatar.vue.d.ts +16 -0
  167. package/dist/components/LpAvatar.vue.js +1 -1
  168. package/dist/components/LpBadge.vue.d.ts +19 -5
  169. package/dist/components/LpBadge.vue.js +1 -1
  170. package/dist/components/LpBreadcrumbs.vue.js +1 -1
  171. package/dist/components/LpButton.vue.d.ts +2 -2
  172. package/dist/components/LpButton.vue.js +1 -1
  173. package/dist/components/LpCalendar.vue.js +1 -1
  174. package/dist/components/LpCard.vue.d.ts +24 -0
  175. package/dist/components/LpCard.vue.js +1 -1
  176. package/dist/components/LpChart.vue.d.ts +10 -0
  177. package/dist/components/LpChart.vue.js +122 -142
  178. package/dist/components/LpCheckbox.vue.d.ts +2 -2
  179. package/dist/components/LpCheckbox.vue.js +1 -1
  180. package/dist/components/LpCodeBlock.vue.js +1 -1
  181. package/dist/components/LpCodeDisplay.vue.d.ts +89 -0
  182. package/dist/components/LpCodeDisplay.vue.js +4 -0
  183. package/dist/components/LpCollapse.vue.d.ts +58 -0
  184. package/dist/components/LpCollapse.vue.js +4 -0
  185. package/dist/components/LpColorPicker.vue.d.ts +2 -2
  186. package/dist/components/LpColorPicker.vue.js +1 -1
  187. package/dist/components/LpCommandPalette.vue.js +34 -23
  188. package/dist/components/LpConfirmDialog.vue.js +1 -1
  189. package/dist/components/LpConfirmHost.vue.js +1 -1
  190. package/dist/components/LpContextMenu.vue.js +1 -1
  191. package/dist/components/LpCopyButton.vue.d.ts +82 -0
  192. package/dist/components/LpCopyButton.vue.js +4 -0
  193. package/dist/components/LpCountdown.vue.d.ts +100 -0
  194. package/dist/components/LpCountdown.vue.js +4 -0
  195. package/dist/components/LpDataList.vue.d.ts +66 -0
  196. package/dist/components/LpDataList.vue.js +4 -0
  197. package/dist/components/LpDatePicker.vue.d.ts +1 -1
  198. package/dist/components/LpDatePicker.vue.js +1 -1
  199. package/dist/components/LpDateRangePicker.vue.d.ts +1 -1
  200. package/dist/components/LpDateRangePicker.vue.js +1 -1
  201. package/dist/components/LpDisclosure.vue.js +1 -1
  202. package/dist/components/LpDock.vue.d.ts +74 -0
  203. package/dist/components/LpDock.vue.js +204 -0
  204. package/dist/components/LpDrawer.vue.js +1 -1
  205. package/dist/components/LpDropdownMenu.vue.js +1 -1
  206. package/dist/components/LpDropzone.vue.d.ts +8 -1
  207. package/dist/components/LpDropzone.vue.js +36 -15
  208. package/dist/components/LpEditable.vue.d.ts +85 -0
  209. package/dist/components/LpEditable.vue.js +4 -0
  210. package/dist/components/LpEmptyState.vue.js +1 -1
  211. package/dist/components/LpFieldNotice.vue.js +1 -1
  212. package/dist/components/LpFileTree.vue.js +1 -1
  213. package/dist/components/LpFileTreeNode.vue.js +1 -1
  214. package/dist/components/LpFormField.vue.js +1 -1
  215. package/dist/components/LpFrame.vue.d.ts +22 -0
  216. package/dist/components/LpFrame.vue.js +4 -0
  217. package/dist/components/LpGauge.vue.d.ts +116 -0
  218. package/dist/components/LpGauge.vue.js +4 -0
  219. package/dist/components/LpGlowBorder.vue.d.ts +47 -0
  220. package/dist/components/LpGlowBorder.vue.js +4 -0
  221. package/dist/components/LpHintExample.vue.d.ts +2 -2
  222. package/dist/components/LpHintExample.vue.js +1 -1
  223. package/dist/components/LpHintLayer.vue.js +1 -1
  224. package/dist/components/LpHints.vue.js +1 -1
  225. package/dist/components/LpHoverCard.vue.d.ts +60 -0
  226. package/dist/components/LpHoverCard.vue.js +4 -0
  227. package/dist/components/LpIcon.vue.js +1 -1
  228. package/dist/components/LpIdenticon.vue.d.ts +37 -0
  229. package/dist/components/LpIdenticon.vue.js +4 -0
  230. package/dist/components/LpImageEditor.vue.js +13 -19
  231. package/dist/components/LpInfraNode.vue.js +1 -1
  232. package/dist/components/LpInput.vue.d.ts +10 -0
  233. package/dist/components/LpInput.vue.js +1 -1
  234. package/dist/components/LpLightbox.vue.js +1 -1
  235. package/dist/components/LpLoader.vue.d.ts +106 -0
  236. package/dist/components/LpLoader.vue.js +4 -0
  237. package/dist/components/LpLogViewer.vue.js +1 -1
  238. package/dist/components/LpMagnetic.vue.d.ts +28 -0
  239. package/dist/components/LpMagnetic.vue.js +4 -0
  240. package/dist/components/LpMapPicker.vue.js +1 -1
  241. package/dist/components/LpMasonry.vue.d.ts +42 -0
  242. package/dist/components/LpMasonry.vue.js +4 -0
  243. package/dist/components/LpMediaGallery.vue.js +1 -1
  244. package/dist/components/LpModal.vue.js +1 -1
  245. package/dist/components/LpModalNav.vue.js +1 -1
  246. package/dist/components/LpNotificationBell.vue.d.ts +15 -6
  247. package/dist/components/LpNotificationBell.vue.js +1 -1
  248. package/dist/components/LpNumberField.vue.d.ts +57 -0
  249. package/dist/components/LpNumberField.vue.js +1 -1
  250. package/dist/components/LpNumberFlow.vue.js +1 -1
  251. package/dist/components/LpOtpInput.vue.d.ts +12 -4
  252. package/dist/components/LpOtpInput.vue.js +1 -1
  253. package/dist/components/LpPagination.vue.js +1 -1
  254. package/dist/components/LpPaletteFacts.vue.js +1 -1
  255. package/dist/components/LpPaletteHeader.vue.js +2 -2
  256. package/dist/components/LpPalettePreview.vue.js +1 -1
  257. package/dist/components/LpPaletteStats.vue.js +1 -1
  258. package/dist/components/LpPasswordInput.vue.d.ts +12 -1
  259. package/dist/components/LpPasswordInput.vue.js +1 -1
  260. package/dist/components/LpPhoneInput.vue.js +1 -1
  261. package/dist/components/LpPie.vue.d.ts +3 -1
  262. package/dist/components/LpPie.vue.js +1 -1
  263. package/dist/components/LpPipeline.vue.d.ts +91 -0
  264. package/dist/components/LpPipeline.vue.js +4 -0
  265. package/dist/components/LpPopover.vue.d.ts +2 -2
  266. package/dist/components/LpPopover.vue.js +1 -1
  267. package/dist/components/LpProgress.vue.d.ts +11 -4
  268. package/dist/components/LpProgress.vue.js +1 -1
  269. package/dist/components/LpQuota.vue.d.ts +68 -0
  270. package/dist/components/LpQuota.vue.js +4 -0
  271. package/dist/components/LpRadioGroup.vue.js +1 -1
  272. package/dist/components/LpRangeCalendar.vue.js +1 -1
  273. package/dist/components/LpRangeSlider.vue.js +1 -1
  274. package/dist/components/LpRelativeTime.vue.d.ts +41 -0
  275. package/dist/components/LpRelativeTime.vue.js +4 -0
  276. package/dist/components/LpRepeater.vue.d.ts +48 -0
  277. package/dist/components/LpRepeater.vue.js +4 -0
  278. package/dist/components/LpRevealOnScroll.vue.d.ts +67 -0
  279. package/dist/components/LpRevealOnScroll.vue.js +4 -0
  280. package/dist/components/LpRollingText.vue.d.ts +1 -1
  281. package/dist/components/LpScramble.vue.d.ts +55 -0
  282. package/dist/components/LpScramble.vue.js +4 -0
  283. package/dist/components/LpScrollArea.vue.d.ts +1 -1
  284. package/dist/components/LpSecretField.vue.d.ts +108 -0
  285. package/dist/components/LpSecretField.vue.js +4 -0
  286. package/dist/components/LpSection.vue.js +1 -1
  287. package/dist/components/LpSegmented.vue.js +1 -1
  288. package/dist/components/LpSelect.vue.d.ts +2 -0
  289. package/dist/components/LpSelect.vue.js +1 -1
  290. package/dist/components/LpShaderBackdrop.vue.d.ts +39 -0
  291. package/dist/components/LpShaderBackdrop.vue.js +4 -0
  292. package/dist/components/LpShinyText.vue.d.ts +39 -0
  293. package/dist/components/LpShinyText.vue.js +4 -0
  294. package/dist/components/LpShortcutsDialog.vue.js +1 -1
  295. package/dist/components/LpSidebar.vue.js +1 -1
  296. package/dist/components/LpSidebarNav.vue.js +1 -1
  297. package/dist/components/LpSlider.vue.js +1 -1
  298. package/dist/components/LpSparkline.vue.d.ts +4 -3
  299. package/dist/components/LpSparkline.vue.js +1 -1
  300. package/dist/components/LpSpotlight.vue.d.ts +37 -0
  301. package/dist/components/LpSpotlight.vue.js +4 -0
  302. package/dist/components/LpStat.vue.js +1 -1
  303. package/dist/components/LpStatus.vue.d.ts +65 -0
  304. package/dist/components/LpStatus.vue.js +4 -0
  305. package/dist/components/LpStepMark.vue.d.ts +31 -0
  306. package/dist/components/LpStepMark.vue.js +4 -0
  307. package/dist/components/LpStepper.vue.js +1 -1
  308. package/dist/components/LpSwap.vue.js +1 -1
  309. package/dist/components/LpSwitch.vue.d.ts +2 -2
  310. package/dist/components/LpSwitch.vue.js +1 -1
  311. package/dist/components/LpTable.vue.js +104 -120
  312. package/dist/components/LpTableFilter.vue.js +1 -1
  313. package/dist/components/LpTableOfContents.vue.d.ts +6 -1
  314. package/dist/components/LpTableOfContents.vue.js +1 -1
  315. package/dist/components/LpTabs.vue.d.ts +5 -5
  316. package/dist/components/LpTabs.vue.js +25 -33
  317. package/dist/components/LpTagsInput.vue.js +1 -1
  318. package/dist/components/LpTextReveal.vue.d.ts +58 -0
  319. package/dist/components/LpTextReveal.vue.js +4 -0
  320. package/dist/components/LpTextarea.vue.js +1 -1
  321. package/dist/components/LpThemeProvider.vue.js +1 -1
  322. package/dist/components/LpThemeStudio.vue.d.ts +54 -0
  323. package/dist/components/LpThemeStudio.vue.js +4 -0
  324. package/dist/components/LpThemeStudioSection.vue.d.ts +16 -0
  325. package/dist/components/LpThemeStudioSection.vue.js +4 -0
  326. package/dist/components/LpThemeSwitcher.vue.d.ts +32 -1
  327. package/dist/components/LpThemeSwitcher.vue.js +1 -1
  328. package/dist/components/LpTimeField.vue.d.ts +2 -0
  329. package/dist/components/LpTimeField.vue.js +1 -1
  330. package/dist/components/LpToaster.vue.js +1 -1
  331. package/dist/components/LpTooltip.vue.d.ts +2 -2
  332. package/dist/components/LpTooltip.vue.js +1 -1
  333. package/dist/components/LpTopologyCanvas.vue.js +1 -1
  334. package/dist/components/LpTour.vue.js +1 -1
  335. package/dist/components/LpTruncate.vue.d.ts +34 -0
  336. package/dist/components/LpTruncate.vue.js +4 -0
  337. package/dist/components/LpTypewriter.vue.d.ts +61 -0
  338. package/dist/components/LpTypewriter.vue.js +4 -0
  339. package/dist/components/LpUptimeBar.vue.js +1 -1
  340. package/dist/components/LpVideoBackdrop.vue.js +1 -1
  341. package/dist/components/MenuTitle.vue.js +1 -1
  342. package/dist/components/OptionContent.vue.d.ts +7 -0
  343. package/dist/components/OptionContent.vue.js +4 -0
  344. package/dist/components/PickerField.vue.d.ts +43 -0
  345. package/dist/components/PickerField.vue.js +4 -0
  346. package/dist/components/RevealToggle.vue.d.ts +21 -0
  347. package/dist/components/RevealToggle.vue.js +4 -0
  348. package/dist/components/StepConnector.vue.d.ts +19 -0
  349. package/dist/components/StepConnector.vue.js +4 -0
  350. package/dist/components/TourCard.vue.js +1 -1
  351. package/dist/components/badge.d.ts +1 -0
  352. package/dist/components/badge.js +4 -0
  353. package/dist/components/chart.d.ts +5 -1
  354. package/dist/components/chart.js +2 -21
  355. package/dist/components/dateRange.d.ts +11 -0
  356. package/dist/components/dateRange.js +12 -0
  357. package/dist/components/dock.d.ts +19 -0
  358. package/dist/components/dock.js +16 -0
  359. package/dist/components/dropdown.d.ts +10 -1
  360. package/dist/components/dropdown.js +7 -2
  361. package/dist/components/fieldText.d.ts +38 -0
  362. package/dist/components/fieldText.js +53 -0
  363. package/dist/components/fileTree.d.ts +7 -3
  364. package/dist/components/fileTree.js +12 -18
  365. package/dist/components/format.d.ts +41 -0
  366. package/dist/components/format.js +67 -0
  367. package/dist/components/identicon.d.ts +38 -0
  368. package/dist/components/identicon.js +85 -0
  369. package/dist/components/masonry.d.ts +32 -0
  370. package/dist/components/masonry.js +40 -0
  371. package/dist/components/menuWhen.js +1 -1
  372. package/dist/components/meter.d.ts +64 -0
  373. package/dist/components/meter.js +53 -0
  374. package/dist/components/otp.d.ts +1 -1
  375. package/dist/components/otp.js +7 -2
  376. package/dist/components/pipeline.d.ts +55 -0
  377. package/dist/components/pipeline.js +51 -0
  378. package/dist/components/quota.d.ts +57 -0
  379. package/dist/components/quota.js +40 -0
  380. package/dist/components/relativeTime.d.ts +24 -0
  381. package/dist/components/relativeTime.js +56 -0
  382. package/dist/components/reveal.d.ts +18 -0
  383. package/dist/components/reveal.js +32 -0
  384. package/dist/components/scramble.d.ts +68 -0
  385. package/dist/components/scramble.js +58 -0
  386. package/dist/components/shaderBackdrop.d.ts +20 -0
  387. package/dist/components/shaderBackdrop.js +156 -0
  388. package/dist/components/slider.d.ts +5 -0
  389. package/dist/components/slider.js +24 -0
  390. package/dist/components/slotText.d.ts +2 -0
  391. package/dist/components/slotText.js +9 -0
  392. package/dist/components/status.d.ts +14 -0
  393. package/dist/components/status.js +22 -0
  394. package/dist/components/tableFacets.d.ts +8 -0
  395. package/dist/components/tableFacets.js +19 -0
  396. package/dist/components/textSplit.d.ts +17 -0
  397. package/dist/components/textSplit.js +24 -0
  398. package/dist/components/timeSpan.d.ts +78 -0
  399. package/dist/components/timeSpan.js +85 -0
  400. package/dist/components/tone.d.ts +43 -0
  401. package/dist/components/tone.js +82 -0
  402. package/dist/components/tour.js +3 -2
  403. package/dist/components/typewriter.d.ts +36 -0
  404. package/dist/components/typewriter.js +53 -0
  405. package/dist/components/videoBackdrop.js +5 -6
  406. package/dist/composables/easing.d.ts +22 -1
  407. package/dist/composables/easing.js +42 -1
  408. package/dist/composables/pseudoLocale.d.ts +15 -0
  409. package/dist/composables/pseudoLocale.js +96 -0
  410. package/dist/composables/scroll.js +5 -3
  411. package/dist/composables/useClipboard.d.ts +4 -1
  412. package/dist/composables/useClipboard.js +12 -2
  413. package/dist/composables/useField.d.ts +8 -0
  414. package/dist/composables/useField.js +5 -0
  415. package/dist/composables/useForm.js +19 -5
  416. package/dist/composables/useHintSystem.d.ts +4 -0
  417. package/dist/composables/useHintSystem.js +33 -1
  418. package/dist/composables/useHints.js +9 -3
  419. package/dist/composables/useInView.d.ts +11 -0
  420. package/dist/composables/useInView.js +39 -0
  421. package/dist/composables/useLoadingGate.d.ts +8 -0
  422. package/dist/composables/useLoadingGate.js +38 -0
  423. package/dist/composables/useLpConfig.d.ts +10 -0
  424. package/dist/composables/useLpConfig.js +11 -1
  425. package/dist/composables/useMediaQuery.js +3 -1
  426. package/dist/composables/useNow.d.ts +16 -0
  427. package/dist/composables/useNow.js +39 -0
  428. package/dist/composables/usePersisted.js +14 -7
  429. package/dist/composables/usePillTransition.d.ts +5 -2
  430. package/dist/composables/usePillTransition.js +4 -3
  431. package/dist/composables/usePointerFrame.d.ts +18 -0
  432. package/dist/composables/usePointerFrame.js +31 -0
  433. package/dist/composables/useReducedMotion.d.ts +2 -0
  434. package/dist/composables/useReducedMotion.js +7 -0
  435. package/dist/composables/useRepeater.d.ts +17 -0
  436. package/dist/composables/useRepeater.js +31 -0
  437. package/dist/composables/useResizeObserver.d.ts +8 -0
  438. package/dist/composables/useResizeObserver.js +33 -0
  439. package/dist/composables/useRevealedValue.d.ts +31 -0
  440. package/dist/composables/useRevealedValue.js +26 -0
  441. package/dist/composables/useScramble.d.ts +42 -0
  442. package/dist/composables/useScramble.js +76 -0
  443. package/dist/composables/useShift.js +2 -10
  444. package/dist/composables/useSpotlight.d.ts +18 -0
  445. package/dist/composables/useSpotlight.js +53 -0
  446. package/dist/composables/useTilt.js +26 -18
  447. package/dist/composables/useTween.d.ts +43 -0
  448. package/dist/composables/useTween.js +69 -0
  449. package/dist/composables/useView.d.ts +18 -0
  450. package/dist/composables/useView.js +61 -0
  451. package/dist/icons-all.js +1 -1
  452. package/dist/index.d.ts +98 -1
  453. package/dist/index.js +262 -104
  454. package/dist/theme/define.d.ts +12 -0
  455. package/dist/theme/studio.d.ts +90 -0
  456. package/package.json +5 -2
  457. package/src/component-names.ts +33 -0
  458. package/src/components/AttentionDot.vue +27 -0
  459. package/src/components/FieldWrapper.vue +38 -0
  460. package/src/components/LayoutNode.vue +5 -9
  461. package/src/components/LegendSwatch.vue +15 -0
  462. package/src/components/LoaderMark.vue +90 -0
  463. package/src/components/LpActionBar.vue +12 -20
  464. package/src/components/LpActivityLine.vue +186 -0
  465. package/src/components/LpAlert.vue +54 -24
  466. package/src/components/LpAppShell.vue +7 -11
  467. package/src/components/LpAutocomplete.vue +16 -14
  468. package/src/components/LpAvatar.vue +17 -2
  469. package/src/components/LpBadge.vue +77 -13
  470. package/src/components/LpBreadcrumbs.vue +101 -35
  471. package/src/components/LpButton.vue +15 -3
  472. package/src/components/LpCard.vue +30 -7
  473. package/src/components/LpChart.vue +152 -156
  474. package/src/components/LpCheckbox.vue +10 -4
  475. package/src/components/LpCodeBlock.vue +11 -19
  476. package/src/components/LpCodeDisplay.vue +228 -0
  477. package/src/components/LpCollapse.vue +162 -0
  478. package/src/components/LpColorPicker.vue +10 -1
  479. package/src/components/LpCommandPalette.vue +38 -24
  480. package/src/components/LpContextMenu.vue +10 -1
  481. package/src/components/LpCopyButton.vue +185 -0
  482. package/src/components/LpCountdown.vue +308 -0
  483. package/src/components/LpDataList.vue +154 -0
  484. package/src/components/LpDatePicker.vue +53 -104
  485. package/src/components/LpDateRangePicker.vue +49 -95
  486. package/src/components/LpDisclosure.vue +58 -36
  487. package/src/components/LpDock.vue +274 -0
  488. package/src/components/LpDrawer.vue +10 -1
  489. package/src/components/LpDropdownMenu.vue +10 -1
  490. package/src/components/LpDropzone.vue +34 -13
  491. package/src/components/LpEditable.vue +293 -0
  492. package/src/components/LpFileTree.vue +5 -1
  493. package/src/components/LpFileTreeNode.vue +25 -50
  494. package/src/components/LpFormField.vue +22 -3
  495. package/src/components/LpFrame.vue +200 -0
  496. package/src/components/LpGauge.vue +287 -0
  497. package/src/components/LpGlowBorder.vue +60 -0
  498. package/src/components/LpHintExample.vue +14 -38
  499. package/src/components/LpHintLayer.vue +13 -3
  500. package/src/components/LpHoverCard.vue +175 -0
  501. package/src/components/LpIdenticon.vue +69 -0
  502. package/src/components/LpImageEditor.vue +15 -18
  503. package/src/components/LpInfraNode.vue +27 -16
  504. package/src/components/LpInput.vue +30 -16
  505. package/src/components/LpLightbox.vue +44 -23
  506. package/src/components/LpLoader.vue +145 -0
  507. package/src/components/LpLogViewer.vue +311 -82
  508. package/src/components/LpMagnetic.vue +97 -0
  509. package/src/components/LpMapPicker.vue +26 -28
  510. package/src/components/LpMasonry.vue +247 -0
  511. package/src/components/LpModal.vue +10 -1
  512. package/src/components/LpNotificationBell.vue +33 -49
  513. package/src/components/LpNumberField.vue +79 -14
  514. package/src/components/LpNumberFlow.vue +10 -35
  515. package/src/components/LpOtpInput.vue +7 -2
  516. package/src/components/LpPagination.vue +38 -4
  517. package/src/components/LpPalettePreview.vue +2 -7
  518. package/src/components/LpPasswordInput.vue +16 -14
  519. package/src/components/LpPhoneInput.vue +12 -3
  520. package/src/components/LpPie.vue +197 -36
  521. package/src/components/LpPipeline.vue +366 -0
  522. package/src/components/LpPopover.vue +10 -1
  523. package/src/components/LpProgress.vue +22 -84
  524. package/src/components/LpQuota.vue +197 -0
  525. package/src/components/LpRangeSlider.vue +5 -1
  526. package/src/components/LpRelativeTime.vue +90 -0
  527. package/src/components/LpRepeater.vue +80 -0
  528. package/src/components/LpRevealOnScroll.vue +125 -0
  529. package/src/components/LpScramble.vue +107 -0
  530. package/src/components/LpSecretField.vue +313 -0
  531. package/src/components/LpSegmented.vue +3 -10
  532. package/src/components/LpSelect.vue +78 -64
  533. package/src/components/LpShaderBackdrop.vue +434 -0
  534. package/src/components/LpShinyText.vue +139 -0
  535. package/src/components/LpSidebar.vue +2 -0
  536. package/src/components/LpSlider.vue +5 -1
  537. package/src/components/LpSparkline.vue +55 -37
  538. package/src/components/LpSpotlight.vue +51 -0
  539. package/src/components/LpStat.vue +6 -2
  540. package/src/components/LpStatus.vue +127 -0
  541. package/src/components/LpStepMark.vue +138 -0
  542. package/src/components/LpStepper.vue +37 -43
  543. package/src/components/LpSwap.vue +2 -2
  544. package/src/components/LpSwitch.vue +15 -22
  545. package/src/components/LpTable.vue +47 -63
  546. package/src/components/LpTableFilter.vue +1 -0
  547. package/src/components/LpTableOfContents.vue +53 -29
  548. package/src/components/LpTabs.vue +10 -21
  549. package/src/components/LpTagsInput.vue +53 -37
  550. package/src/components/LpTextReveal.vue +236 -0
  551. package/src/components/LpTextarea.vue +11 -11
  552. package/src/components/LpThemeStudio.vue +216 -0
  553. package/src/components/LpThemeStudioSection.vue +16 -0
  554. package/src/components/LpThemeSwitcher.vue +156 -47
  555. package/src/components/LpTimeField.vue +9 -1
  556. package/src/components/LpToaster.vue +63 -83
  557. package/src/components/LpTooltip.vue +10 -1
  558. package/src/components/LpTour.vue +24 -13
  559. package/src/components/LpTruncate.vue +135 -0
  560. package/src/components/LpTypewriter.vue +236 -0
  561. package/src/components/LpUptimeBar.vue +6 -5
  562. package/src/components/LpVideoBackdrop.vue +15 -13
  563. package/src/components/OptionContent.vue +17 -0
  564. package/src/components/PickerField.vue +58 -0
  565. package/src/components/RevealToggle.vue +51 -0
  566. package/src/components/StepConnector.vue +54 -0
  567. package/src/components/TourCard.vue +29 -16
  568. package/src/components/badge.ts +8 -0
  569. package/src/components/chart.ts +8 -23
  570. package/src/components/dateRange.ts +25 -0
  571. package/src/components/dock.ts +31 -0
  572. package/src/components/dropdown.ts +15 -3
  573. package/src/components/fieldText.ts +95 -0
  574. package/src/components/fileTree.ts +19 -21
  575. package/src/components/format.ts +136 -0
  576. package/src/components/identicon.ts +160 -0
  577. package/src/components/masonry.ts +73 -0
  578. package/src/components/meter.ts +122 -0
  579. package/src/components/otp.ts +14 -3
  580. package/src/components/pipeline.ts +101 -0
  581. package/src/components/quota.ts +111 -0
  582. package/src/components/relativeTime.ts +118 -0
  583. package/src/components/reveal.ts +56 -0
  584. package/src/components/scramble.ts +138 -0
  585. package/src/components/shaderBackdrop.ts +208 -0
  586. package/src/components/slider.ts +30 -0
  587. package/src/components/slotText.ts +13 -0
  588. package/src/components/status.ts +46 -0
  589. package/src/components/tableFacets.ts +25 -0
  590. package/src/components/textSplit.ts +55 -0
  591. package/src/components/timeSpan.ts +183 -0
  592. package/src/components/tone.ts +144 -0
  593. package/src/components/tour.ts +3 -2
  594. package/src/components/typewriter.ts +104 -0
  595. package/src/components/videoBackdrop.ts +7 -6
  596. package/src/composables/easing.ts +66 -1
  597. package/src/composables/pseudoLocale.ts +105 -0
  598. package/src/composables/scroll.ts +6 -3
  599. package/src/composables/useClipboard.ts +25 -3
  600. package/src/composables/useField.ts +12 -0
  601. package/src/composables/useForm.ts +27 -6
  602. package/src/composables/useHintSystem.ts +40 -0
  603. package/src/composables/useHints.ts +14 -3
  604. package/src/composables/useInView.ts +61 -0
  605. package/src/composables/useLoadingGate.ts +63 -0
  606. package/src/composables/useLpConfig.ts +20 -1
  607. package/src/composables/useMediaQuery.ts +6 -2
  608. package/src/composables/useNow.ts +84 -0
  609. package/src/composables/usePersisted.ts +19 -8
  610. package/src/composables/usePillTransition.ts +21 -13
  611. package/src/composables/usePointerFrame.ts +72 -0
  612. package/src/composables/useReducedMotion.ts +22 -0
  613. package/src/composables/useRepeater.ts +44 -0
  614. package/src/composables/useResizeObserver.ts +59 -0
  615. package/src/composables/useRevealedValue.ts +84 -0
  616. package/src/composables/useScramble.ts +152 -0
  617. package/src/composables/useShift.ts +10 -16
  618. package/src/composables/useSpotlight.ts +82 -0
  619. package/src/composables/useTilt.ts +40 -23
  620. package/src/composables/useTween.ts +149 -0
  621. package/src/composables/useView.ts +93 -0
  622. package/src/index.css +1 -0
  623. package/src/index.ts +143 -1
  624. package/src/kit-icons.ts +1 -1
  625. package/src/theme/define.ts +65 -0
  626. package/src/theme/studio.ts +202 -0
  627. package/src/tokens/effects.css +151 -0
  628. package/src/tokens/tokens.css +131 -39
  629. package/src/tokens/utilities.css +81 -0
  630. package/dist/assets/LpTabs-Bk7v4bZz.css +0 -18
  631. package/dist/chunks/LpAlert-BdvxvcWE.js +0 -85
  632. package/dist/chunks/LpBadge-Fe0D1bYx.js +0 -62
  633. package/dist/chunks/LpBreadcrumbs-rzOKkLiI.js +0 -98
  634. package/dist/chunks/LpDatePicker-BMfM454Y.js +0 -243
  635. package/dist/chunks/LpDateRangePicker-CNb9X3eL.js +0 -195
  636. package/dist/chunks/LpNumberField-DO1ufKvE.js +0 -107
  637. package/dist/chunks/LpStepper-C2slwObu.js +0 -90
  638. package/dist/chunks/LpSwitch-CZy9_IoA.js +0 -132
  639. package/dist/chunks/LpThemeSwitcher-CDoziW0z.js +0 -166
  640. package/dist/chunks/TourCard-BKI7X9-r.js +0 -105
@@ -0,0 +1,97 @@
1
+ <script lang="ts">
2
+ export default { inheritAttrs: false }
3
+ </script>
4
+
5
+ <script setup lang="ts">
6
+ /*
7
+ * LpMagnetic — leans what it wraps toward a nearby pointer and lets it spring
8
+ * back when the pointer leaves.
9
+ *
10
+ * <LpMagnetic><LpButton size="lg">Play</LpButton></LpMagnetic>
11
+ *
12
+ * For the one or two primary actions of a screen (the launcher's Play): the
13
+ * button meets the cursor a little way, which makes it feel like the obvious
14
+ * target. On every button it turns a toolbar into jelly, so keep it rare.
15
+ *
16
+ * The field reaches `reach` px past the element's edge. The position is read
17
+ * once per animation frame from a single listener on its window
18
+ * (usePointerFrame) and written to the `translate` property directly, so
19
+ * nothing re-renders. Mouse only; reduced
20
+ * motion leaves the element still. Layout is untouched: the wrapper keeps its
21
+ * place and only its paint moves.
22
+ */
23
+ import { onBeforeUnmount, onMounted, ref } from "vue"
24
+ import { useMergedAttrs } from "../composables/useMergedClass"
25
+ import { usePointerFrame } from "../composables/usePointerFrame"
26
+ import { magneticOffset } from "./dock"
27
+
28
+ const props = withDefaults(
29
+ defineProps<{
30
+ /** How far past the element's edge, in px, the pull is felt. */
31
+ reach?: number
32
+ /** Fraction of the pointer's distance the element travels, 0–1. Small is better. */
33
+ strength?: number
34
+ /** Keep the wrapper, turn the pull off. */
35
+ disabled?: boolean
36
+ }>(),
37
+ { reach: 60, strength: 0.25, disabled: false },
38
+ )
39
+
40
+ const { class: classes, attrs: rest } = useMergedAttrs(() => "inline-block")
41
+
42
+ const el = ref<HTMLElement | null>(null)
43
+ let held = false
44
+
45
+ function paint(lastX: number, lastY: number) {
46
+ const node = el.value
47
+ if (!node) return
48
+ const rect = node.getBoundingClientRect()
49
+ const dx = lastX - (rect.left + rect.width / 2)
50
+ const dy = lastY - (rect.top + rect.height / 2)
51
+ // The field is measured from the edge, so a wide button pulls from as far
52
+ // away along its length as a square one does.
53
+ const reach = props.reach + Math.max(rect.width, rect.height) / 2
54
+ const { x, y } = props.disabled ? { x: 0, y: 0 } : magneticOffset(dx, dy, reach, props.strength)
55
+ const pulling = x !== 0 || y !== 0
56
+ if (!pulling && !held) return
57
+ held = pulling
58
+ // Following: short and direct. Letting go: the settle curve, whose slight
59
+ // overshoot is the spring back.
60
+ node.style.transition = pulling
61
+ ? "translate 120ms ease-out"
62
+ : "translate var(--duration-slow) var(--ease-settle)"
63
+ node.style.translate = pulling ? `${x.toFixed(1)}px ${y.toFixed(1)}px` : ""
64
+ }
65
+
66
+ // `disabled` is not a dropped move here: the paint turns the pull off, so an
67
+ // element held when it is disabled still springs back.
68
+ const pointer = usePointerFrame({ paint })
69
+ const onMove = (event: PointerEvent) => void pointer.move(event)
70
+
71
+ // The pointer leaving the window sends no further moves, so the element would
72
+ // stay leaning toward a cursor that is gone.
73
+ function onOut(event: PointerEvent) {
74
+ if (event.relatedTarget || !held) return
75
+ pointer.moveTo(Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY)
76
+ }
77
+
78
+ // The window the element actually lives in: inside LpFrame that is the
79
+ // frame's, and the pointer events over it never reach the page's own window.
80
+ let host: Window | null = null
81
+
82
+ onMounted(() => {
83
+ host = el.value?.ownerDocument.defaultView ?? window
84
+ host.addEventListener("pointermove", onMove, { passive: true })
85
+ host.addEventListener("pointerout", onOut, { passive: true })
86
+ })
87
+ onBeforeUnmount(() => {
88
+ host?.removeEventListener("pointermove", onMove)
89
+ host?.removeEventListener("pointerout", onOut)
90
+ })
91
+ </script>
92
+
93
+ <template>
94
+ <div ref="el" :class="classes" v-bind="rest">
95
+ <slot />
96
+ </div>
97
+ </template>
@@ -20,12 +20,14 @@ export default { inheritAttrs: false }
20
20
  * layer first and swaps the tile pyramid only once the animation lands, which
21
21
  * is how the zoom reads as continuous instead of as a hard cut between levels.
22
22
  */
23
- import { computed, onBeforeUnmount, onMounted, ref, watch } from "vue"
23
+ import { computed, onBeforeUnmount, ref, watch } from "vue"
24
24
  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
+ import LpLoader from "./LpLoader.vue"
27
28
  import { useMergedAttrs } from "../composables/useMergedClass"
28
29
  import { useLabels } from "../composables/useLpConfig"
30
+ import { useResizeObserver } from "../composables/useResizeObserver"
29
31
 
30
32
  const props = withDefaults(
31
33
  defineProps<{
@@ -538,26 +540,19 @@ watch(
538
540
  },
539
541
  )
540
542
 
541
- let observer: ResizeObserver | undefined
542
-
543
- onMounted(() => {
544
- const el = viewport.value
545
- if (!el) return
546
- observer = new ResizeObserver(([entry]) => {
547
- viewWidth.value = entry.contentRect.width
548
- viewHeight.value = entry.contentRect.height
549
- if (!layers.value.length) {
550
- pushLayer()
551
- return
552
- }
553
- reanchor()
554
- refreshTiles()
555
- })
556
- observer.observe(el)
543
+ useResizeObserver(viewport, (entry) => {
544
+ if (!entry) return
545
+ viewWidth.value = entry.contentRect.width
546
+ viewHeight.value = entry.contentRect.height
547
+ if (!layers.value.length) {
548
+ pushLayer()
549
+ return
550
+ }
551
+ reanchor()
552
+ refreshTiles()
557
553
  })
558
554
 
559
555
  onBeforeUnmount(() => {
560
- observer?.disconnect()
561
556
  clearTimeout(zoomTimer)
562
557
  clearTimeout(pruneTimer)
563
558
  stopGlide()
@@ -672,21 +667,24 @@ const { class: rootClass, attrs: rest } = useMergedAttrs(
672
667
  :disabled="disabled || locating"
673
668
  :title="text.locate"
674
669
  :aria-label="text.locate"
670
+ :aria-busy="locating || undefined"
675
671
  @click="locate"
676
672
  >
677
- <LpIcon
678
- :name="locating ? 'lucide:loader-circle' : 'lucide:crosshair'"
679
- :size="16"
680
- :class="locating ? 'animate-spin' : ''"
681
- />
673
+ <!-- At once, since it takes the crosshair's place; silent, since the
674
+ button's aria-busy already says it. -->
675
+ <LpLoader v-if="locating" size="inline" :delay="0" aria-hidden="true" class="text-[16px]" />
676
+ <LpIcon v-else name="lucide:crosshair" :size="16" />
682
677
  </button>
683
678
  </div>
684
679
 
685
- <LpIcon
686
- v-if="loadingTiles"
687
- name="lucide:loader-circle"
688
- :size="16"
689
- class="pointer-events-none absolute left-2 top-2 animate-spin text-white/70 drop-shadow-[0_1px_2px_rgba(0,0,0,0.6)]"
680
+ <!-- Driven by `active` so cached tiles show nothing and a pan does not
681
+ blink it. Silent: tiles load on every pan and zoom, and announcing
682
+ each batch would only be noise. -->
683
+ <LpLoader
684
+ :active="loadingTiles"
685
+ size="inline"
686
+ aria-hidden="true"
687
+ class="pointer-events-none absolute left-2 top-2 text-[16px] text-white/70 drop-shadow-[0_1px_2px_rgba(0,0,0,0.6)]"
690
688
  />
691
689
 
692
690
  <p
@@ -0,0 +1,247 @@
1
+ <script lang="ts">
2
+ export default { inheritAttrs: false }
3
+ </script>
4
+
5
+ <script setup lang="ts" generic="T">
6
+ /*
7
+ * LpMasonry — items of different heights packed into columns without the
8
+ * ragged gaps a grid leaves: memo cards, screenshots, mod tiles.
9
+ *
10
+ * <LpMasonry :items="memos" item-key="id" :min-column-width="240">
11
+ * <template #default="{ item }"><LpCard>{{ item.text }}</LpCard></template>
12
+ * </LpMasonry>
13
+ *
14
+ * Why positioned, not CSS columns: `column-count` fills column by column, so
15
+ * the first items run down the left edge and adding one item reflows
16
+ * everything. Here each item goes, in order, into the shortest column, so the
17
+ * newest content stays at the top and an appended item leaves the rest alone.
18
+ *
19
+ * Every item is measured (one ResizeObserver for all of them) and placed with
20
+ * the `translate` property, so a change of column count, an item growing, or one being
21
+ * added or removed makes the others glide to their new places rather than
22
+ * jump; the container's height eases along. Items arrive with a short
23
+ * staggered entrance the first time the block is seen (useReveal), and later
24
+ * additions fade in where they land. Reduced motion places everything at once.
25
+ *
26
+ * DOM order is the items' order, which is also the reading order for
27
+ * keyboard and screen readers.
28
+ */
29
+ import { computed, onBeforeUnmount, onMounted, reactive, ref, watch } from "vue"
30
+ import { easingToken, prefersReducedMotion } from "../composables/easing"
31
+ import { useMergedAttrs } from "../composables/useMergedClass"
32
+ import { useReveal } from "../composables/useReveal"
33
+ import { masonryColumnCount, placeMasonry } from "./masonry"
34
+ import { staggerDelay } from "./reveal"
35
+
36
+ const props = withDefaults(
37
+ defineProps<{
38
+ items: T[]
39
+ /** Field (or function) giving each item a stable identity, so it keeps its element across changes. */
40
+ itemKey?: keyof T | ((item: T, index: number) => string | number)
41
+ /**
42
+ * Fixed column count, or a table of minimum container widths to counts
43
+ * (`{ 0: 1, 560: 2, 900: 3 }`). Omit to fit as many `minColumnWidth`
44
+ * columns as the container allows — the right default, because a masonry
45
+ * usually lives in a panel whose width the viewport does not decide.
46
+ */
47
+ columns?: number | Record<number, number>
48
+ /** Narrowest a column may get when the count is derived from the width. */
49
+ minColumnWidth?: number
50
+ /** Upper bound for the derived count. */
51
+ maxColumns?: number
52
+ /** Space between items, in px, both ways. */
53
+ gap?: number
54
+ /** Play the entrance when the block is first seen. Off shows it as is. */
55
+ entrance?: boolean
56
+ }>(),
57
+ { minColumnWidth: 240, gap: 16, entrance: true },
58
+ )
59
+
60
+ defineSlots<{
61
+ default?(props: { item: T; index: number }): unknown
62
+ }>()
63
+
64
+ const { class: classes, attrs: rest } = useMergedAttrs(
65
+ () => "relative w-full transition-[height] duration-(--duration-slow) ease-(--ease-emphasized)",
66
+ )
67
+
68
+ function keyOf(item: T, index: number): string | number {
69
+ const key = props.itemKey
70
+ if (typeof key === "function") return key(item, index)
71
+ if (key != null) return item[key] as string | number
72
+ return index
73
+ }
74
+
75
+ const keys = computed(() => props.items.map((item, index) => keyOf(item, index)))
76
+
77
+ // ── measuring ─────────────────────────────────────────────
78
+
79
+ const root = ref<HTMLElement | null>(null)
80
+ const width = ref(0)
81
+ const heights = reactive(new Map<string | number, number>())
82
+ const elements = new Map<string | number, HTMLElement>()
83
+ const keyByElement = new WeakMap<Element, string | number>()
84
+
85
+ let observer: ResizeObserver | null = null
86
+
87
+ function heightOf(entry: ResizeObserverEntry): number {
88
+ const box = entry.borderBoxSize?.[0]
89
+ return box ? box.blockSize : (entry.target as HTMLElement).offsetHeight
90
+ }
91
+
92
+ function setItem(key: string | number, el: unknown) {
93
+ const previous = elements.get(key)
94
+ if (el instanceof HTMLElement) {
95
+ if (previous === el) return
96
+ if (previous) observer?.unobserve(previous)
97
+ elements.set(key, el)
98
+ keyByElement.set(el, key)
99
+ heights.set(key, el.offsetHeight)
100
+ observer?.observe(el)
101
+ } else if (previous && !keys.value.includes(key)) {
102
+ // Gone from the items (its element may still be fading out): stop
103
+ // measuring it. A re-added key gets a fresh element and a fresh entry.
104
+ observer?.unobserve(previous)
105
+ elements.delete(key)
106
+ heights.delete(key)
107
+ }
108
+ }
109
+
110
+ onMounted(() => {
111
+ if (typeof ResizeObserver === "undefined") {
112
+ width.value = root.value?.clientWidth ?? 0
113
+ return
114
+ }
115
+ observer = new ResizeObserver((entries) => {
116
+ for (const entry of entries) {
117
+ if (entry.target === root.value) {
118
+ width.value = entry.contentRect.width
119
+ continue
120
+ }
121
+ const key = keyByElement.get(entry.target)
122
+ if (key !== undefined) heights.set(key, heightOf(entry))
123
+ }
124
+ })
125
+ if (root.value) {
126
+ width.value = root.value.clientWidth
127
+ observer.observe(root.value)
128
+ }
129
+ for (const el of elements.values()) observer.observe(el)
130
+ })
131
+
132
+ onBeforeUnmount(() => observer?.disconnect())
133
+
134
+ // ── layout ────────────────────────────────────────────────
135
+
136
+ const count = computed(() =>
137
+ masonryColumnCount(width.value, {
138
+ columns: props.columns,
139
+ minColumnWidth: props.minColumnWidth,
140
+ maxColumns: props.maxColumns,
141
+ gap: props.gap,
142
+ }),
143
+ )
144
+
145
+ const layout = computed(() =>
146
+ placeMasonry(
147
+ keys.value.map((key) => heights.get(key) ?? 0),
148
+ count.value,
149
+ width.value,
150
+ props.gap,
151
+ ),
152
+ )
153
+
154
+ const position = computed(() => {
155
+ const map = new Map<string | number, { x: number; y: number }>()
156
+ keys.value.forEach((key, index) => map.set(key, { x: layout.value.x[index]!, y: layout.value.y[index]! }))
157
+ return map
158
+ })
159
+
160
+ // An item glides only once it has been placed: a new one must appear where it
161
+ // belongs, not fly in from the top-left corner where it was first measured.
162
+ const placed = reactive(new Set<string | number>())
163
+ const ready = computed(() => width.value > 0)
164
+
165
+ watch(
166
+ [layout, ready],
167
+ () => {
168
+ if (!ready.value) return
169
+ requestAnimationFrame(() => {
170
+ for (const key of keys.value) placed.add(key)
171
+ for (const key of placed) if (!keys.value.includes(key)) placed.delete(key)
172
+ })
173
+ },
174
+ { flush: "post" },
175
+ )
176
+
177
+ function itemStyle(key: string | number) {
178
+ const at = position.value.get(key)
179
+ return {
180
+ width: `${layout.value.columnWidth}px`,
181
+ // `translate`, not `transform`: TransitionGroup runs its own FLIP on any
182
+ // child whose transition names `transform`, and that FLIP clears the inline
183
+ // transform when it finishes — every item landed in the top-left corner.
184
+ translate: at ? `${at.x}px ${at.y}px` : undefined,
185
+ }
186
+ }
187
+
188
+ // ── entrance ──────────────────────────────────────────────
189
+
190
+ const { el: revealEl, revealed } = useReveal({ immediate: () => !props.entrance, threshold: 0.05 })
191
+ watch(root, (node) => (revealEl.value = node))
192
+
193
+ const hidden = computed(() => !ready.value || !revealed.value)
194
+
195
+ watch(
196
+ () => ready.value && revealed.value,
197
+ (go) => {
198
+ if (!go || !props.entrance || prefersReducedMotion()) return
199
+ const easing = easingToken("ease-emphasized")
200
+ // Stagger by the order items are placed in, which reads top to bottom.
201
+ keys.value.forEach((key, index) => {
202
+ const el = elements.get(key)
203
+ el?.firstElementChild?.animate?.(
204
+ [
205
+ { opacity: 0, transform: "translate3d(0, 12px, 0)" },
206
+ { opacity: 1, transform: "none" },
207
+ ],
208
+ { duration: 460, delay: staggerDelay(index, 0, 40, 12), easing, fill: "backwards" },
209
+ )
210
+ })
211
+ },
212
+ { flush: "post" },
213
+ )
214
+ </script>
215
+
216
+ <template>
217
+ <div
218
+ ref="root"
219
+ :class="classes"
220
+ role="list"
221
+ :style="{ height: `${layout.height}px`, opacity: hidden ? 0 : undefined }"
222
+ v-bind="rest"
223
+ >
224
+ <TransitionGroup
225
+ enter-active-class="transition-[opacity,scale] duration-(--duration-slow) ease-(--ease-emphasized)"
226
+ enter-from-class="opacity-0 scale-95"
227
+ leave-active-class="transition-[opacity,scale] duration-(--duration-medium) ease-(--ease-emphasized)"
228
+ leave-to-class="opacity-0 scale-95"
229
+ >
230
+ <div
231
+ v-for="(item, index) in items"
232
+ :key="keys[index]"
233
+ :ref="(el) => setItem(keys[index]!, el)"
234
+ role="listitem"
235
+ class="absolute top-0 left-0"
236
+ :class="placed.has(keys[index]!) && 'transition-[translate] duration-(--duration-slow) ease-(--ease-emphasized)'"
237
+ :style="itemStyle(keys[index]!)"
238
+ >
239
+ <!-- The inner box takes the entrance, so its transform never fights
240
+ the placement transform on the outer one. -->
241
+ <div>
242
+ <slot :item="item" :index="index" />
243
+ </div>
244
+ </div>
245
+ </TransitionGroup>
246
+ </div>
247
+ </template>
@@ -17,6 +17,7 @@ import { useModalLayer } from "./modalDepth"
17
17
  import { CLOSE_ICON } from "./dropdown"
18
18
  import LpIcon from "./LpIcon.vue"
19
19
  import LpScrollArea from "./LpScrollArea.vue"
20
+ import { useOutsideGuard, usePortalTarget } from "../composables/useView"
20
21
 
21
22
  /*
22
23
  * Stacking depth, so a dialog opened FROM a dialog sits above the one that
@@ -122,6 +123,7 @@ function close() {
122
123
  defineExpose({ close })
123
124
 
124
125
  function onEscape(event: KeyboardEvent) {
126
+ guardOutside(event)
125
127
  if (!props.dismissible) event.preventDefault()
126
128
  }
127
129
 
@@ -136,6 +138,7 @@ function onEscape(event: KeyboardEvent) {
136
138
  * attribute and every click behaves exactly as before.
137
139
  */
138
140
  const onInteractOutside = (event: Event) => {
141
+ guardOutside(event)
139
142
  if (!props.dismissible) return event.preventDefault()
140
143
  const target = event.target
141
144
  if (target instanceof Element && target.closest("[data-lp-modal-aside]")) {
@@ -285,11 +288,17 @@ watch(
285
288
  },
286
289
  { flush: "post" },
287
290
  )
291
+
292
+ // Overlays open in the window this component renders in (an LpFrame's, inside one).
293
+ const portalTo = usePortalTarget()
294
+ // What happens in another document (the page around a frame, or a frame in
295
+ // the page) is not "outside" for this layer.
296
+ const guardOutside = useOutsideGuard()
288
297
  </script>
289
298
 
290
299
  <template>
291
300
  <DialogRoot :open="open" @update:open="onOpenChange">
292
- <DialogPortal>
301
+ <DialogPortal :to="portalTo">
293
302
  <DialogOverlay
294
303
  :class="{ 'lp-scrim--light': frosted, 'lp-scrim--nested': layer.nested, 'lp-scrim--covered': layer.covered }"
295
304
  class="lp-scrim fixed inset-0 data-[state=open]:animate-[fade-in_var(--duration-medium)_var(--ease-emphasized)] data-[state=closed]:animate-[fade-out_120ms_ease]"
@@ -8,18 +8,21 @@
8
8
  * `select`. Mirrors the kit convention (cf. LpSidebar): data in, events out,
9
9
  * no data fetching inside.
10
10
  */
11
- import { computed, getCurrentInstance, onBeforeUnmount, onMounted, ref } from "vue"
11
+ import { computed, getCurrentInstance } from "vue"
12
12
  import { useLabels } from "../composables/useLpConfig"
13
+ import { useNow } from "../composables/useNow"
13
14
  import LpBadge from "./LpBadge.vue"
14
15
  import LpButton from "./LpButton.vue"
15
16
  import LpContextMenu, { type ContextMenuItemDef } from "./LpContextMenu.vue"
16
17
  import LpEmptyState from "./LpEmptyState.vue"
17
18
  import LpIcon from "./LpIcon.vue"
18
19
  import LpPopover from "./LpPopover.vue"
20
+ import LpRelativeTime from "./LpRelativeTime.vue"
19
21
  import LpRollingText from "./LpRollingText.vue"
20
22
  import LpScrollArea from "./LpScrollArea.vue"
21
23
  import LpSkeleton from "./LpSkeleton.vue"
22
24
  import LpShift from "./LpShift.vue"
25
+ import { toneLook, type ToneName } from "./tone"
23
26
 
24
27
  export interface NotificationItem {
25
28
  id: string
@@ -35,7 +38,7 @@ export interface NotificationItem {
35
38
  * consumers were mapping their own types onto raw palette classes, which is
36
39
  * the one place a theme cannot follow them.
37
40
  */
38
- tone?: "brand" | "action" | "danger" | "accent" | "muted"
41
+ tone?: ToneName
39
42
  /** Optional deep link; the row becomes clickable when set. */
40
43
  link?: string
41
44
  /** ISO-8601 creation time; rendered as a relative label. */
@@ -63,10 +66,17 @@ export interface NotificationLabels {
63
66
  unread?: string
64
67
  /** Screen-reader marker on an unread row. */
65
68
  unreadItem?: string
66
- /** Relative times; `{n}` is filled in. */
69
+ /**
70
+ * No longer read: times are worded by Intl in the app's locale. Still
71
+ * accepted so existing label objects keep type-checking.
72
+ * @deprecated
73
+ */
67
74
  justNow?: string
75
+ /** @deprecated See `justNow`. */
68
76
  minutes?: string
77
+ /** @deprecated See `justNow`. */
69
78
  hours?: string
79
+ /** @deprecated See `justNow`. */
70
80
  days?: string
71
81
  }
72
82
 
@@ -85,8 +95,9 @@ const props = withDefaults(
85
95
  /** Translations for the strings the bell renders itself. */
86
96
  labels?: NotificationLabels
87
97
  /**
88
- * Formats `item.createdAt` for display. Defaults to a compact English
89
- * relative time ("just now", "5m", "3h"); pass your own to localise it.
98
+ * Formats `item.createdAt` for display. Defaults to a narrow relative time
99
+ * in the app's locale ("now", "5m ago", "3h ago"); pass your own to word
100
+ * it differently.
90
101
  */
91
102
  formatTime?: (iso: string) => string
92
103
  }>(),
@@ -109,10 +120,6 @@ const text = useLabels(
109
120
  ariaLabel: "",
110
121
  unread: "{count} unread",
111
122
  unreadItem: "Unread",
112
- justNow: "just now",
113
- minutes: "{n}m",
114
- hours: "{n}h",
115
- days: "{n}d",
116
123
  },
117
124
  () => props.labels,
118
125
  )
@@ -152,14 +159,6 @@ const buttonLabel = computed(() =>
152
159
  hasUnread.value ? `${l.value.ariaLabel}, ${l.value.unread.replace("{count}", String(unread.value))}` : l.value.ariaLabel,
153
160
  )
154
161
 
155
- /** Icon tints for an unread row, keyed by the item's category tone. */
156
- const TONE_CLASS: Record<string, string> = {
157
- brand: "bg-brand-soft text-brand",
158
- action: "bg-action-soft text-action",
159
- danger: "bg-danger-soft text-danger",
160
- accent: "bg-accent-soft text-accent",
161
- muted: "bg-surface-soft text-muted-strong",
162
- }
163
162
 
164
163
  /*
165
164
  * Which optional actions the consumer actually handles. A button for an event
@@ -199,8 +198,11 @@ const contextMenu = computed<ContextMenuItemDef[]>(() => {
199
198
  return items
200
199
  })
201
200
 
201
+ // Picking an item goes somewhere (a page, a dialog, a tour step), so the feed
202
+ // steps out of the way: left open it would cover what the item just opened.
202
203
  function onSelect(item: NotificationItem) {
203
204
  if (!item.read) emit("markRead", item.id)
205
+ open.value = false
204
206
  emit("select", item)
205
207
  }
206
208
 
@@ -226,7 +228,7 @@ function rowMenu(item: NotificationItem): ContextMenuItemDef[] {
226
228
  actions.push({
227
229
  label: l.value.open,
228
230
  icon: "lucide:external-link",
229
- onSelect: () => emit("select", item),
231
+ onSelect: () => onSelect(item),
230
232
  })
231
233
  }
232
234
  actions.push({
@@ -239,35 +241,14 @@ function rowMenu(item: NotificationItem): ContextMenuItemDef[] {
239
241
  return actions
240
242
  }
241
243
 
242
- // Compact relative time ("just now", "5m", "3h", "2d"); falls back to a date.
243
- // A `formatTime` prop takes over entirely — that's the localisation seam.
244
- //
245
- // The clock is only read after mount and then ticks, so a server render and the
246
- // hydrating client agree (both print nothing) and "just now" does not stay
247
- // "just now" for the life of the page.
248
- const now = ref<number | null>(null)
249
- let clock: ReturnType<typeof setInterval> | undefined
250
- onMounted(() => {
251
- now.value = Date.now()
252
- clock = setInterval(() => (now.value = Date.now()), 30_000)
253
- })
254
- onBeforeUnmount(() => clearInterval(clock))
255
-
256
- function timeAgo(iso?: string): string {
257
- if (!iso || now.value === null) return ""
258
- if (props.formatTime) return props.formatTime(iso)
259
- const then = new Date(iso).getTime()
260
- if (Number.isNaN(then)) return ""
261
- const secs = Math.max(0, Math.round((now.value - then) / 1000))
262
- const n = (template: string, value: number) => template.replace("{n}", String(value))
263
- if (secs < 45) return l.value.justNow
264
- const mins = Math.round(secs / 60)
265
- if (mins < 60) return n(l.value.minutes, mins)
266
- const hrs = Math.round(mins / 60)
267
- if (hrs < 24) return n(l.value.hours, hrs)
268
- const days = Math.round(hrs / 24)
269
- if (days < 7) return n(l.value.days, days)
270
- return new Date(then).toLocaleDateString()
244
+ // A `formatTime` prop takes over the row's time entirely; it is re-asked twice
245
+ // a minute so its "5m" keeps moving. Without one, the row renders
246
+ // LpRelativeTime, which words the time through Intl in the app's locale and
247
+ // wakes only when its own text would change.
248
+ const customNow = useNow(() => !!props.formatTime, { nextIn: () => 30_000 })
249
+ function customTime(iso: string): string {
250
+ void customNow.value
251
+ return props.formatTime ? props.formatTime(iso) : ""
271
252
  }
272
253
  </script>
273
254
 
@@ -371,7 +352,7 @@ function timeAgo(iso?: string): string {
371
352
  it is still unread. -->
372
353
  <span
373
354
  class="grid size-7 shrink-0 place-items-center rounded-control"
374
- :class="item.read ? 'bg-surface-soft text-muted' : TONE_CLASS[item.tone ?? 'brand']"
355
+ :class="item.read ? 'bg-surface-soft text-muted' : toneLook(item.tone, 'brand').chip"
375
356
  >
376
357
  <LpIcon :name="item.icon || 'lucide:bell'" :size="15" />
377
358
  </span>
@@ -387,7 +368,10 @@ function timeAgo(iso?: string): string {
387
368
  {{ item.body }}
388
369
  </span>
389
370
  <span v-if="item.createdAt" class="mt-0.5 block text-[11px] text-muted/70">
390
- {{ timeAgo(item.createdAt) }}
371
+ <template v-if="formatTime">{{ customTime(item.createdAt) }}</template>
372
+ <!-- The row is itself a button or link, so the exact date goes
373
+ to the native title rather than a focusable tooltip. -->
374
+ <LpRelativeTime v-else :date="item.createdAt" format="narrow" :tooltip="false" />
391
375
  </span>
392
376
  </span>
393
377
  </component>