@leavepulse/ui 0.70.1 → 0.71.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (321) hide show
  1. package/README.md +91 -0
  2. package/dist/assets/{LpCommandPalette-u-zOxKWa.css → LpCommandPalette-DgPZX0us.css} +24 -17
  3. package/dist/assets/{LpPaletteFacts-B9FdVmIO.css → LpPaletteFacts-CVNh5ICn.css} +16 -16
  4. package/dist/assets/{LpPaletteStats-ClE-37MK.css → LpPaletteStats-ZiZmR6vz.css} +5 -5
  5. package/dist/assets/{LpTable-DHxEQ_-3.css → LpTable-DHvorDiU.css} +19 -19
  6. package/dist/assets/LpTabs-CO3EcsNV.css +18 -0
  7. package/dist/chunks/{ContextMenuItems-6kxEivBI.js → ContextMenuItems-eS7qx9gD.js} +1 -1
  8. package/dist/chunks/{DropzoneLinkField-Bw8Flkhk.js → DropzoneLinkField-DkFhdfEJ.js} +2 -2
  9. package/dist/chunks/{FieldWrapper-BiennROB.js → FieldWrapper-YzbvvC3M.js} +1 -1
  10. package/dist/chunks/{LayoutCanvas-BsJIzhdd.js → LayoutCanvas-Ba4A9QC5.js} +1 -1
  11. package/dist/chunks/{LayoutNode-DJVhkSGk.js → LayoutNode-CTTTLMDM.js} +1 -1
  12. package/dist/chunks/{LpActionBar-CzOFar5t.js → LpActionBar-B7anGlkI.js} +2 -2
  13. package/dist/chunks/{LpAddressInput-Chv_Xf8q.js → LpAddressInput-CD0bzrt-.js} +7 -7
  14. package/dist/chunks/{LpAlert-C5igj802.js → LpAlert-BHi7ARqu.js} +1 -1
  15. package/dist/chunks/{LpAppShell-BiWByLmt.js → LpAppShell-1RmBp6xQ.js} +3 -3
  16. package/dist/chunks/{LpAutocomplete-D92niDHE.js → LpAutocomplete-DDqe2M5g.js} +5 -2
  17. package/dist/chunks/{LpAvatar-CLPl-qOF.js → LpAvatar-DqfKCIdr.js} +1 -1
  18. package/dist/chunks/{LpBadge-BBq1Er9T.js → LpBadge-C8fKEmTF.js} +1 -1
  19. package/dist/chunks/{LpBreadcrumbs-JKEi_yFF.js → LpBreadcrumbs-DhcoStFa.js} +1 -1
  20. package/dist/chunks/{LpButton-CVxBfGjd.js → LpButton-BQDAFXBd.js} +3 -0
  21. package/dist/chunks/{LpCalendar-pVL91-Pl.js → LpCalendar-NmrA47J9.js} +1 -1
  22. package/dist/chunks/{LpCheckbox-DHc2bHDe.js → LpCheckbox-BoOuLnGB.js} +5 -2
  23. package/dist/chunks/{LpCodeBlock-BGsQhxw8.js → LpCodeBlock-Czu-fq1M.js} +2 -2
  24. package/dist/chunks/{LpCodeDisplay-CbJMs-IK.js → LpCodeDisplay-BK2FHbtM.js} +5 -5
  25. package/dist/chunks/{LpColorPicker-B7HSIFmn.js → LpColorPicker-Ee1DZiRd.js} +2 -2
  26. package/dist/chunks/LpCommandScope-ubtWDMa0.js +53 -0
  27. package/dist/chunks/{LpConfirmDialog-CfGCux8m.js → LpConfirmDialog-i15pulMT.js} +3 -3
  28. package/dist/chunks/{LpConfirmHost-exB7JmcO.js → LpConfirmHost-D0oGavtd.js} +1 -1
  29. package/dist/chunks/{LpContextMenu-Cj96ENDC.js → LpContextMenu-DXpFJjlO.js} +13 -3
  30. package/dist/chunks/{LpCopyButton-DzHjnFs0.js → LpCopyButton-Dmq7DmFw.js} +3 -3
  31. package/dist/chunks/{LpCountdown-BQnmB4gq.js → LpCountdown-DTdMF8DL.js} +1 -1
  32. package/dist/chunks/{LpDataList-CHNYQw7h.js → LpDataList-CFPtSsBB.js} +3 -3
  33. package/dist/chunks/{LpDatePicker-ac1_6o5P.js → LpDatePicker-cC9K-Pk8.js} +4 -4
  34. package/dist/chunks/{LpDateRangePicker-B15gYrZJ.js → LpDateRangePicker-BVQKPbvo.js} +2 -2
  35. package/dist/chunks/{LpDisclosure-DOarzlAG.js → LpDisclosure-hRnX3FWL.js} +4 -1
  36. package/dist/chunks/{LpDrawer-DGNKRsGq.js → LpDrawer-u4rxnuR-.js} +1 -1
  37. package/dist/chunks/{LpDropdownMenu-R4aYVtVV.js → LpDropdownMenu-BgZ0Z_Im.js} +13 -3
  38. package/dist/chunks/{LpEditable-Ws3DNQHn.js → LpEditable-CjxuOVh_.js} +2 -2
  39. package/dist/chunks/{LpEmptyState-KTKipws2.js → LpEmptyState-CKFTtQgJ.js} +1 -1
  40. package/dist/chunks/{LpFieldNotice-CurbNuXI.js → LpFieldNotice-CPI8EwtU.js} +1 -1
  41. package/dist/chunks/{LpFileTree-DDNg34tJ.js → LpFileTree-BH0gNlSJ.js} +2 -2
  42. package/dist/chunks/{LpFileTreeNode-DmPuYNZ_.js → LpFileTreeNode-gPVB1oXM.js} +4 -4
  43. package/dist/chunks/{LpFormField-BWwgm1E4.js → LpFormField-gRASfyj2.js} +4 -1
  44. package/dist/chunks/{LpGauge-C14Z9503.js → LpGauge-BdjPGnqw.js} +1 -1
  45. package/dist/chunks/{LpHintLayer-HvwUA5Ec.js → LpHintLayer-C2asBIfs.js} +2 -2
  46. package/dist/chunks/{LpHints-DxbPKOzQ.js → LpHints-CNZQ76JV.js} +3 -3
  47. package/dist/chunks/{LpIcon-HJP5xMz4.js → LpIcon-DkP-9mzK.js} +1 -1
  48. package/dist/chunks/{LpInfraNode-D64hywJI.js → LpInfraNode-yTh4-5qa.js} +2 -2
  49. package/dist/chunks/{LpInput-CDuwODJG.js → LpInput-2H-5ILOB.js} +5 -2
  50. package/dist/chunks/{LpLightbox-XJ0LpTcZ.js → LpLightbox-lAt5uIfx.js} +2 -2
  51. package/dist/chunks/{LpLogViewer-DKK4ySZl.js → LpLogViewer-x78YfhJf.js} +5 -5
  52. package/dist/chunks/{LpMapPicker-BtIl7NOM.js → LpMapPicker-Dp5RSCi9.js} +1 -1
  53. package/dist/chunks/{LpMediaGallery-rmI1DjKN.js → LpMediaGallery-D92oJfY_.js} +3 -3
  54. package/dist/chunks/{LpModal-eBMt2Fqn.js → LpModal-CpH0bNo7.js} +1 -1
  55. package/dist/chunks/{LpModalNav-DiyFCHiZ.js → LpModalNav-BH4ANQGJ.js} +4 -4
  56. package/dist/chunks/{LpNotificationBell-D-WhE5m2.js → LpNotificationBell-BdUQqZhs.js} +6 -6
  57. package/dist/chunks/{LpNumberField-B9k-h2HE.js → LpNumberField-DZY3Z670.js} +4 -1
  58. package/dist/chunks/{LpNumberFlow-Cfyh033Q.js → LpNumberFlow-5khYxsfX.js} +6 -3
  59. package/dist/chunks/{LpPagination-VNCX6pFC.js → LpPagination-DbeACavM.js} +1 -1
  60. package/dist/chunks/LpPaletteTheme-CqWNRwHE.js +104 -0
  61. package/dist/chunks/{LpPasswordInput-GL2KOFK8.js → LpPasswordInput-BzAZsf5V.js} +4 -2
  62. package/dist/chunks/{LpPhoneInput-C60tx1tC.js → LpPhoneInput-BMpd-0QR.js} +2 -2
  63. package/dist/chunks/{LpPie-BgXZ3xmm.js → LpPie-DdFSTbGE.js} +4 -4
  64. package/dist/chunks/{LpPipeline-B31h41Mu.js → LpPipeline-zjEM1Lwc.js} +3 -3
  65. package/dist/chunks/{LpProgress-CE-m5OpD.js → LpProgress-BX33EKkN.js} +1 -1
  66. package/dist/chunks/{LpQuota-DaL8TgNe.js → LpQuota-DqNv1yjs.js} +2 -2
  67. package/dist/chunks/{LpRadioGroup-CGBr5nIp.js → LpRadioGroup-CsXeV9Ct.js} +4 -1
  68. package/dist/chunks/{LpRangeCalendar-c5qj7YcC.js → LpRangeCalendar-l-0nSFmY.js} +1 -1
  69. package/dist/chunks/{LpRangeSlider-Dv6lNvHY.js → LpRangeSlider-CM-0ZITZ.js} +3 -0
  70. package/dist/chunks/{LpRelativeTime-yin9uQP7.js → LpRelativeTime-UM8EkfrN.js} +1 -1
  71. package/dist/chunks/{LpRepeater-D0S7jjwr.js → LpRepeater-OvzByE5L.js} +2 -2
  72. package/dist/chunks/{LpSecretField-CfWyHv9m.js → LpSecretField-DwQiPOko.js} +9 -7
  73. package/dist/chunks/{LpSection-BW4xJ-Li.js → LpSection-DcQsieHG.js} +1 -1
  74. package/dist/chunks/{LpSegmented-BO99Sr4m.js → LpSegmented-D2VST0o3.js} +4 -1
  75. package/dist/chunks/{LpSelect-C_8cJtj6.js → LpSelect-DYX2-u5A.js} +36 -6
  76. package/dist/chunks/{LpSidebar-CZRXtvem.js → LpSidebar-D44peKq2.js} +3 -3
  77. package/dist/chunks/{LpSidebarNav-DBg1U1oW.js → LpSidebarNav--VwkXO_a.js} +3 -3
  78. package/dist/chunks/{LpSlider-DuoRpOrH.js → LpSlider-DxYoCJJO.js} +3 -0
  79. package/dist/chunks/{LpStat-DFKlPJgy.js → LpStat-DIsQfbeD.js} +3 -3
  80. package/dist/chunks/{LpStatus-BsGLrpor.js → LpStatus-BQU1TUsv.js} +1 -1
  81. package/dist/chunks/{LpStepMark-TrzOioa9.js → LpStepMark-CXa7DJy_.js} +1 -1
  82. package/dist/chunks/{LpStepper-BbnS5jUr.js → LpStepper-C9RM_yTG.js} +1 -1
  83. package/dist/chunks/{LpSwitch-DrzKrZZV.js → LpSwitch-bGZxUDSx.js} +3 -0
  84. package/dist/chunks/{LpTableFilter-DyvHGbOk.js → LpTableFilter-CTZMqFDU.js} +2 -2
  85. package/dist/chunks/{LpTableOfContents-rYvo8z5T.js → LpTableOfContents-CraicEs4.js} +2 -2
  86. package/dist/chunks/{LpTagsInput-DZg80W-B.js → LpTagsInput-DrjmrOge.js} +8 -5
  87. package/dist/chunks/{LpTextarea-DiWjeVoJ.js → LpTextarea-BbYqmG1S.js} +4 -1
  88. package/dist/chunks/{LpThemeStudio-DxVZMtzN.js → LpThemeStudio-B2nPaG-h.js} +18 -14
  89. package/dist/chunks/{LpThemeSwitcher-CadVBTBn.js → LpThemeSwitcher-C0A5sg_E.js} +5 -3
  90. package/dist/chunks/{LpTimeField-BFQVf_PW.js → LpTimeField-DQ8WvPr2.js} +4 -1
  91. package/dist/chunks/{LpToaster-Wv9ksj4q.js → LpToaster-BTTl_wuy.js} +2 -2
  92. package/dist/chunks/{LpTooltip-Cz8SQwRt.js → LpTooltip-BcBvIx4z.js} +23 -5
  93. package/dist/chunks/{LpTopologyCanvas-C17A1XCb.js → LpTopologyCanvas-B0wEvAoq.js} +1 -1
  94. package/dist/chunks/{LpTour-CY2bFXge.js → LpTour-BgCfNlaW.js} +1 -1
  95. package/dist/chunks/{LpTruncate-D_5Pfv5B.js → LpTruncate-TcqfkyJM.js} +2 -2
  96. package/dist/chunks/{LpUptimeBar-Tew1Ykx4.js → LpUptimeBar-DRadGq4Y.js} +136 -47
  97. package/dist/chunks/{MenuTitle-BjQZX8tQ.js → MenuTitle-Dab3EGOp.js} +1 -1
  98. package/dist/chunks/{OptionContent-CmYUUeWj.js → OptionContent-DPzNsybd.js} +1 -1
  99. package/dist/chunks/PalettePreviewBoundary-B0Hhu3Yw.js +20 -0
  100. package/dist/chunks/{PickerField-DoHv0WMH.js → PickerField-DwLw7j0L.js} +1 -1
  101. package/dist/chunks/{RevealToggle-C-KzaQGJ.js → RevealToggle-BVFM_FAU.js} +3 -3
  102. package/dist/chunks/{TourCard-BJ8i7dk0.js → TourCard--2IfgnUU.js} +2 -2
  103. package/dist/chunks/icons-CLu1iR1j.js +43 -0
  104. package/dist/chunks/palette-BiY7yQZL.js +29 -0
  105. package/dist/component-names.d.ts +1 -1
  106. package/dist/components/ContextMenuItems.vue.js +1 -1
  107. package/dist/components/DropzoneLinkField.vue.js +1 -1
  108. package/dist/components/FieldWrapper.vue.js +1 -1
  109. package/dist/components/LayoutCanvas.vue.js +1 -1
  110. package/dist/components/LayoutNode.vue.js +1 -1
  111. package/dist/components/LpActionBar.vue.js +1 -1
  112. package/dist/components/LpAddressInput.vue.d.ts +1 -1
  113. package/dist/components/LpAddressInput.vue.js +1 -1
  114. package/dist/components/LpAlert.vue.d.ts +3 -3
  115. package/dist/components/LpAlert.vue.js +1 -1
  116. package/dist/components/LpAppShell.vue.js +1 -1
  117. package/dist/components/LpAutocomplete.vue.d.ts +7 -1
  118. package/dist/components/LpAutocomplete.vue.js +1 -1
  119. package/dist/components/LpAvatar.vue.js +1 -1
  120. package/dist/components/LpBadge.vue.d.ts +2 -2
  121. package/dist/components/LpBadge.vue.js +1 -1
  122. package/dist/components/LpBreadcrumbs.vue.js +1 -1
  123. package/dist/components/LpButton.vue.d.ts +4 -0
  124. package/dist/components/LpButton.vue.js +1 -1
  125. package/dist/components/LpCalendar.vue.js +1 -1
  126. package/dist/components/LpChart.vue.js +2 -2
  127. package/dist/components/LpCheckbox.vue.d.ts +8 -1
  128. package/dist/components/LpCheckbox.vue.js +1 -1
  129. package/dist/components/LpCodeBlock.vue.js +1 -1
  130. package/dist/components/LpCodeDisplay.vue.js +1 -1
  131. package/dist/components/LpColorPicker.vue.d.ts +2 -2
  132. package/dist/components/LpColorPicker.vue.js +1 -1
  133. package/dist/components/LpCommandPalette.vue.d.ts +43 -7
  134. package/dist/components/LpCommandPalette.vue.js +451 -126
  135. package/dist/components/LpCommandScope.vue.d.ts +28 -0
  136. package/dist/components/LpCommandScope.vue.js +4 -0
  137. package/dist/components/LpConfirmDialog.vue.js +1 -1
  138. package/dist/components/LpConfirmHost.vue.js +1 -1
  139. package/dist/components/LpContextMenu.vue.d.ts +5 -1
  140. package/dist/components/LpContextMenu.vue.js +1 -1
  141. package/dist/components/LpCopyButton.vue.d.ts +1 -1
  142. package/dist/components/LpCopyButton.vue.js +1 -1
  143. package/dist/components/LpCountdown.vue.js +1 -1
  144. package/dist/components/LpDataList.vue.js +1 -1
  145. package/dist/components/LpDatePicker.vue.js +1 -1
  146. package/dist/components/LpDateRangePicker.vue.js +1 -1
  147. package/dist/components/LpDisclosure.vue.d.ts +4 -0
  148. package/dist/components/LpDisclosure.vue.js +1 -1
  149. package/dist/components/LpDock.vue.js +2 -2
  150. package/dist/components/LpDrawer.vue.js +1 -1
  151. package/dist/components/LpDropdownMenu.vue.d.ts +5 -1
  152. package/dist/components/LpDropdownMenu.vue.js +1 -1
  153. package/dist/components/LpDropzone.vue.js +2 -2
  154. package/dist/components/LpEditable.vue.js +1 -1
  155. package/dist/components/LpEmptyState.vue.js +1 -1
  156. package/dist/components/LpFieldNotice.vue.js +1 -1
  157. package/dist/components/LpFileTree.vue.d.ts +1 -1
  158. package/dist/components/LpFileTree.vue.js +1 -1
  159. package/dist/components/LpFileTreeNode.vue.js +1 -1
  160. package/dist/components/LpFormField.vue.js +1 -1
  161. package/dist/components/LpGauge.vue.js +1 -1
  162. package/dist/components/LpHintLayer.vue.js +1 -1
  163. package/dist/components/LpHints.vue.js +1 -1
  164. package/dist/components/LpIcon.vue.js +1 -1
  165. package/dist/components/LpImageEditor.vue.js +3 -2
  166. package/dist/components/LpInfraNode.vue.js +1 -1
  167. package/dist/components/LpInput.vue.d.ts +6 -0
  168. package/dist/components/LpInput.vue.js +1 -1
  169. package/dist/components/LpLightbox.vue.js +1 -1
  170. package/dist/components/LpLoader.vue.d.ts +1 -1
  171. package/dist/components/LpLogViewer.vue.js +1 -1
  172. package/dist/components/LpMapPicker.vue.js +1 -1
  173. package/dist/components/LpMediaGallery.vue.d.ts +2 -2
  174. package/dist/components/LpMediaGallery.vue.js +1 -1
  175. package/dist/components/LpModal.vue.js +1 -1
  176. package/dist/components/LpModalNav.vue.js +1 -1
  177. package/dist/components/LpNotificationBell.vue.js +1 -1
  178. package/dist/components/LpNumberField.vue.d.ts +6 -0
  179. package/dist/components/LpNumberField.vue.js +1 -1
  180. package/dist/components/LpNumberFlow.vue.js +1 -1
  181. package/dist/components/LpPagination.vue.js +1 -1
  182. package/dist/components/LpPaletteFacts.vue.d.ts +1 -0
  183. package/dist/components/LpPaletteFacts.vue.js +40 -26
  184. package/dist/components/LpPaletteHeader.vue.js +2 -2
  185. package/dist/components/LpPaletteStats.vue.d.ts +1 -0
  186. package/dist/components/LpPaletteStats.vue.js +45 -21
  187. package/dist/components/LpPaletteTheme.vue.d.ts +7 -0
  188. package/dist/components/LpPaletteTheme.vue.js +4 -0
  189. package/dist/components/LpPasswordInput.vue.js +1 -1
  190. package/dist/components/LpPhoneInput.vue.js +1 -1
  191. package/dist/components/LpPie.vue.js +1 -1
  192. package/dist/components/LpPipeline.vue.js +1 -1
  193. package/dist/components/LpProgress.vue.d.ts +1 -1
  194. package/dist/components/LpProgress.vue.js +1 -1
  195. package/dist/components/LpQuota.vue.js +1 -1
  196. package/dist/components/LpRadioGroup.vue.d.ts +6 -1
  197. package/dist/components/LpRadioGroup.vue.js +1 -1
  198. package/dist/components/LpRangeCalendar.vue.js +1 -1
  199. package/dist/components/LpRangeSlider.vue.d.ts +6 -0
  200. package/dist/components/LpRangeSlider.vue.js +1 -1
  201. package/dist/components/LpRelativeTime.vue.js +1 -1
  202. package/dist/components/LpRepeater.vue.js +1 -1
  203. package/dist/components/LpSecretField.vue.js +1 -1
  204. package/dist/components/LpSection.vue.js +1 -1
  205. package/dist/components/LpSegmented.vue.d.ts +6 -0
  206. package/dist/components/LpSegmented.vue.js +1 -1
  207. package/dist/components/LpSelect.vue.d.ts +3 -0
  208. package/dist/components/LpSelect.vue.js +1 -1
  209. package/dist/components/LpShinyText.vue.d.ts +1 -1
  210. package/dist/components/LpShortcutsDialog.vue.js +1 -1
  211. package/dist/components/LpSidebar.vue.js +1 -1
  212. package/dist/components/LpSidebarNav.vue.js +1 -1
  213. package/dist/components/LpSlider.vue.d.ts +6 -0
  214. package/dist/components/LpSlider.vue.js +1 -1
  215. package/dist/components/LpStat.vue.js +1 -1
  216. package/dist/components/LpStatus.vue.js +1 -1
  217. package/dist/components/LpStepMark.vue.js +1 -1
  218. package/dist/components/LpStepper.vue.js +1 -1
  219. package/dist/components/LpSwitch.vue.d.ts +6 -0
  220. package/dist/components/LpSwitch.vue.js +1 -1
  221. package/dist/components/LpTable.vue.d.ts +6 -0
  222. package/dist/components/LpTable.vue.js +74 -35
  223. package/dist/components/LpTableFilter.vue.js +1 -1
  224. package/dist/components/LpTableOfContents.vue.js +1 -1
  225. package/dist/components/LpTabs.vue.d.ts +1 -1
  226. package/dist/components/LpTabs.vue.js +14 -5
  227. package/dist/components/LpTagsInput.vue.d.ts +6 -0
  228. package/dist/components/LpTagsInput.vue.js +1 -1
  229. package/dist/components/LpTextarea.vue.d.ts +8 -1
  230. package/dist/components/LpTextarea.vue.js +1 -1
  231. package/dist/components/LpThemeStudio.vue.js +1 -1
  232. package/dist/components/LpThemeSwitcher.vue.js +1 -1
  233. package/dist/components/LpTimeField.vue.d.ts +6 -0
  234. package/dist/components/LpTimeField.vue.js +1 -1
  235. package/dist/components/LpToaster.vue.js +1 -1
  236. package/dist/components/LpTooltip.vue.js +1 -1
  237. package/dist/components/LpTopologyCanvas.vue.js +1 -1
  238. package/dist/components/LpTour.vue.js +1 -1
  239. package/dist/components/LpTruncate.vue.js +1 -1
  240. package/dist/components/LpUptimeBar.vue.d.ts +18 -9
  241. package/dist/components/LpUptimeBar.vue.js +1 -1
  242. package/dist/components/MenuTitle.vue.js +1 -1
  243. package/dist/components/OptionContent.vue.js +1 -1
  244. package/dist/components/PalettePreviewBoundary.vue.d.ts +13 -0
  245. package/dist/components/PalettePreviewBoundary.vue.js +4 -0
  246. package/dist/components/PickerField.vue.js +1 -1
  247. package/dist/components/RevealToggle.vue.js +1 -1
  248. package/dist/components/TourCard.vue.js +1 -1
  249. package/dist/components/commandPalette.d.ts +33 -0
  250. package/dist/components/commandPalette.js +21 -1
  251. package/dist/components/commandSearch.d.ts +5 -0
  252. package/dist/components/commandSearch.js +22 -2
  253. package/dist/components/format.d.ts +7 -0
  254. package/dist/components/format.js +4 -0
  255. package/dist/components/menuWhen.js +1 -1
  256. package/dist/components/uptimeBar.d.ts +45 -0
  257. package/dist/components/uptimeBar.js +51 -0
  258. package/dist/composables/scroll.d.ts +5 -0
  259. package/dist/composables/scroll.js +7 -0
  260. package/dist/composables/useField.d.ts +4 -0
  261. package/dist/composables/useField.js +3 -0
  262. package/dist/composables/usePaletteCommands.d.ts +61 -0
  263. package/dist/composables/usePaletteCommands.js +226 -0
  264. package/dist/composables/useReveal.js +21 -0
  265. package/dist/composables/useView.js +5 -1
  266. package/dist/icons-all.js +1 -1
  267. package/dist/index.d.ts +14 -1
  268. package/dist/index.js +101 -85
  269. package/dist/theme/palette.d.ts +9 -0
  270. package/dist/theme/studio.d.ts +12 -1
  271. package/package.json +1 -1
  272. package/src/component-names.ts +2 -0
  273. package/src/components/LpAutocomplete.vue +6 -1
  274. package/src/components/LpButton.vue +5 -1
  275. package/src/components/LpCheckbox.vue +7 -2
  276. package/src/components/LpCommandPalette.vue +263 -29
  277. package/src/components/LpCommandScope.vue +35 -0
  278. package/src/components/LpContextMenu.vue +13 -2
  279. package/src/components/LpDisclosure.vue +5 -1
  280. package/src/components/LpDropdownMenu.vue +13 -2
  281. package/src/components/LpFormField.vue +3 -0
  282. package/src/components/LpInput.vue +6 -1
  283. package/src/components/LpNumberField.vue +6 -1
  284. package/src/components/LpNumberFlow.vue +12 -2
  285. package/src/components/LpPaletteFacts.vue +11 -6
  286. package/src/components/LpPaletteStats.vue +12 -5
  287. package/src/components/LpPaletteTheme.vue +48 -0
  288. package/src/components/LpPasswordInput.vue +3 -0
  289. package/src/components/LpRadioGroup.vue +4 -1
  290. package/src/components/LpRangeSlider.vue +6 -1
  291. package/src/components/LpSecretField.vue +3 -0
  292. package/src/components/LpSegmented.vue +6 -1
  293. package/src/components/LpSelect.vue +41 -2
  294. package/src/components/LpSlider.vue +6 -1
  295. package/src/components/LpSwitch.vue +6 -1
  296. package/src/components/LpTable.vue +35 -2
  297. package/src/components/LpTableOfContents.vue +2 -4
  298. package/src/components/LpTabs.vue +7 -0
  299. package/src/components/LpTagsInput.vue +6 -1
  300. package/src/components/LpTextarea.vue +7 -2
  301. package/src/components/LpThemeStudio.vue +7 -5
  302. package/src/components/LpThemeSwitcher.vue +3 -0
  303. package/src/components/LpTimeField.vue +6 -1
  304. package/src/components/LpTooltip.vue +23 -1
  305. package/src/components/LpUptimeBar.vue +69 -41
  306. package/src/components/PalettePreviewBoundary.vue +5 -0
  307. package/src/components/commandPalette.ts +22 -1
  308. package/src/components/commandSearch.ts +23 -1
  309. package/src/components/format.ts +10 -0
  310. package/src/components/uptimeBar.ts +98 -0
  311. package/src/composables/scroll.ts +11 -0
  312. package/src/composables/useField.ts +7 -0
  313. package/src/composables/usePaletteCommands.ts +226 -0
  314. package/src/composables/useReveal.ts +29 -0
  315. package/src/composables/useView.ts +5 -1
  316. package/src/index.ts +18 -1
  317. package/src/kit-icons.ts +1 -1
  318. package/src/theme/palette.ts +26 -0
  319. package/src/theme/studio.ts +15 -8
  320. package/dist/assets/LpTabs-BJ-nrJuI.css +0 -18
  321. package/dist/chunks/icons-4IywzkHk.js +0 -43
@@ -5,7 +5,10 @@ export default { inheritAttrs: false }
5
5
  </script>
6
6
 
7
7
  <script setup lang="ts" generic="T extends Record<string, unknown>">
8
- import { computed, nextTick, onBeforeUnmount, onMounted, ref, shallowRef, useAttrs, watch } from "vue"
8
+ import { computed, h, nextTick, onBeforeUnmount, onMounted, ref, shallowRef, useAttrs, useId, watch } from "vue"
9
+ import { suppressPaletteDiscovery, usePaletteCommands, usePaletteScope } from "../composables/usePaletteCommands"
10
+ import { menuPaletteCommands } from "./commandPalette"
11
+ import LpDataList from "./LpDataList.vue"
9
12
  import LpCheckbox from "./LpCheckbox.vue"
10
13
  import LpContextMenu, { type ContextMenuItemDef } from "./LpContextMenu.vue"
11
14
  import LpEmptyState from "./LpEmptyState.vue"
@@ -157,6 +160,8 @@ type RowKey = string | number
157
160
 
158
161
  const props = withDefaults(
159
162
  defineProps<{
163
+ /** Publish loaded rows and existing row actions to the enclosing command scope. */
164
+ palette?: false | { id?: string; label?: string; labelKey?: keyof T }
160
165
  columns: TableColumn<T>[]
161
166
  /** The rows, for a table that holds its data. Ignored when `load` is set. */
162
167
  rows?: T[]
@@ -245,7 +250,7 @@ const props = withDefaults(
245
250
  */
246
251
  allMatching?: boolean
247
252
  }>(),
248
- {
253
+ { palette: undefined,
249
254
  rows: () => [],
250
255
  pageSize: 25,
251
256
  skeletonRows: 5,
@@ -1075,6 +1080,34 @@ onBeforeUnmount(() => {
1075
1080
  const barInsetTop = computed(() =>
1076
1081
  props.stickyHeader && headHeight.value ? `${headHeight.value}px` : undefined,
1077
1082
  )
1083
+
1084
+ const paletteScope = usePaletteScope()
1085
+ const paletteId = useId()
1086
+ suppressPaletteDiscovery()
1087
+ usePaletteCommands(() => {
1088
+ if (props.palette === false) return []
1089
+ const name = props.palette?.label ?? attrs["aria-label"] as string | undefined
1090
+ if (!name) return []
1091
+ const id = `${paletteScope?.id}/${props.palette?.id ?? paletteId}`
1092
+ return [{
1093
+ id, label: name, icon: "lucide:table", children: [
1094
+ ...menuPaletteCommands(tableMenu(), `${id}/table`),
1095
+ ...displayRows.value.map((row, index) => {
1096
+ const menu = resolveRowMenu(props.rowMenu?.(row))
1097
+ const rowId = `${id}/row/${keyFor(row, index)}`
1098
+ const labelKey = props.palette && props.palette.labelKey || props.columns[0]?.key
1099
+ return {
1100
+ id: rowId, label: menu.title ?? String(labelKey ? row[labelKey] ?? keyFor(row, index) : keyFor(row, index)),
1101
+ description: menu.subtitle, icon: menu.icon ?? "lucide:rows-3", image: menu.image,
1102
+ onSelect: () => emit("rowClick", row), actions: menuPaletteCommands(menu.items, rowId),
1103
+ previewComponent: { setup: () => () => h(LpDataList, { items: props.columns.map(column => ({
1104
+ label: column.label, value: typeof row[column.key] === "object" ? null : String(row[column.key] ?? ""),
1105
+ })) }) },
1106
+ }
1107
+ }),
1108
+ ],
1109
+ }]
1110
+ }, { automatic: true })
1078
1111
  </script>
1079
1112
 
1080
1113
  <template>
@@ -12,7 +12,7 @@
12
12
  import { Motion } from "motion-v"
13
13
  import { computed, onBeforeUnmount, onMounted, ref, useId, watch } from "vue"
14
14
  import { useLabels } from "../composables/useLpConfig"
15
- import { scrollParent } from "../composables/scroll"
15
+ import { atScrollEnd, scrollParent } from "../composables/scroll"
16
16
  import { usePillTransition } from "../composables/usePillTransition"
17
17
 
18
18
  export interface TocLink {
@@ -112,9 +112,7 @@ function setup() {
112
112
  const line = Math.max(props.offset, view / 4)
113
113
  for (const el of headings) if (el.getBoundingClientRect().top - top <= line + 1) current = el.id
114
114
  const scrolled = scroller ? scroller.scrollTop : window.scrollY
115
- const end = scroller
116
- ? scroller.scrollTop + scroller.clientHeight >= scroller.scrollHeight - 2
117
- : window.scrollY + window.innerHeight >= document.documentElement.scrollHeight - 2
115
+ const end = atScrollEnd(headings[0])
118
116
  if (scrolled > 0 && end) current = headings[headings.length - 1]!.id
119
117
  activeId.value = current
120
118
  }
@@ -1,4 +1,5 @@
1
1
  <script setup lang="ts">
2
+ import { usePaletteCommands, usePaletteScope } from "../composables/usePaletteCommands"
2
3
  import { Motion } from "motion-v"
3
4
  import { TabsContent, TabsList, TabsRoot, TabsTrigger } from "reka-ui"
4
5
  import { computed, nextTick, ref, useId, watch } from "vue"
@@ -152,6 +153,12 @@ function releaseFocusAfterPointer(event: PointerEvent) {
152
153
  const target = event.currentTarget
153
154
  if (target instanceof HTMLElement) target.blur()
154
155
  }
156
+
157
+ const paletteScope = usePaletteScope()
158
+ usePaletteCommands(() => props.items.map(item => ({
159
+ id: `${paletteScope?.id}/tabs/${pillId}/${item.value}`, label: props.label ? `${props.label}: ${item.label}` : item.label,
160
+ icon: item.icon, disabled: item.disabled, onSelect: () => select(item.value),
161
+ })), { automatic: true })
155
162
  </script>
156
163
 
157
164
  <template>
@@ -13,6 +13,7 @@ export type TagsInputLabels = {
13
13
  </script>
14
14
 
15
15
  <script setup lang="ts">
16
+ import { usePaletteControl, type PaletteControlOptions } from "../composables/usePaletteCommands"
16
17
  /*
17
18
  * Free-form list of short values: emails, labels, hostnames. Typing a
18
19
  * delimiter or Enter turns the text into a tag, a paste is split into many,
@@ -56,6 +57,8 @@ import { useOutsideGuard, usePortalTarget } from "../composables/useView"
56
57
 
57
58
  const props = withDefaults(
58
59
  defineProps<{
60
+ /** Contextual palette metadata; false excludes this control. */
61
+ palette?: PaletteControlOptions
59
62
  modelValue?: string[]
60
63
  placeholder?: string
61
64
  /** Characters (or a RegExp) that end a tag while typing and split a paste. */
@@ -99,7 +102,7 @@ const props = withDefaults(
99
102
  /** Box height and text size, as LpInput. */
100
103
  size?: "sm" | "md" | "lg"
101
104
  }>(),
102
- {
105
+ { palette: undefined,
103
106
  modelValue: () => [],
104
107
  delimiter: () => [",", "\n", "\t"],
105
108
  duplicates: "reject",
@@ -424,6 +427,8 @@ const portalTo = usePortalTarget()
424
427
  // What happens in another document (the page around a frame, or a frame in
425
428
  // the page) is not "outside" for this layer.
426
429
  const guardOutside = useOutsideGuard()
430
+
431
+ usePaletteControl({ label: () => props.label, disabled: () => field.value.disabled, invalid: () => field.value.invalid, editorProps: () => ({ label: undefined, hint: undefined, error: undefined }) })
427
432
  </script>
428
433
 
429
434
  <template>
@@ -3,12 +3,15 @@ export default { inheritAttrs: false }
3
3
  </script>
4
4
 
5
5
  <script setup lang="ts">
6
+ import { usePaletteControl, type PaletteControlOptions } from "../composables/usePaletteCommands"
6
7
  import { computed, useAttrs } from "vue"
7
8
  import { useFieldControl } from "../composables/useField"
8
9
  import { mergeClass } from "../composables/useMergedClass"
9
10
  import FieldWrapper from "./FieldWrapper.vue"
10
11
 
11
- const props = defineProps<{
12
+ const props = withDefaults(defineProps<{
13
+ /** Contextual palette metadata; false excludes this control. */
14
+ palette?: PaletteControlOptions
12
15
  modelValue?: string
13
16
  placeholder?: string
14
17
  rows?: number
@@ -22,7 +25,7 @@ const props = defineProps<{
22
25
  * the <textarea> for native validation.
23
26
  */
24
27
  required?: boolean
25
- }>()
28
+ }>(), { palette: undefined })
26
29
 
27
30
  defineEmits<{ (e: "update:modelValue", value: string): void }>()
28
31
 
@@ -53,6 +56,8 @@ const textareaClass = computed(() =>
53
56
  hasField.value ? "" : (attrs.class as string),
54
57
  ),
55
58
  )
59
+
60
+ usePaletteControl({ label: () => props.label, disabled: () => field.value.disabled, invalid: () => field.value.invalid, editorProps: () => ({ label: undefined, hint: undefined, error: undefined }) })
56
61
  </script>
57
62
 
58
63
  <template>
@@ -1,4 +1,5 @@
1
1
  <script setup lang="ts">
2
+ import { usePaletteTheme } from "../theme/palette"
2
3
  /*
3
4
  * The theme, tuned in place: a base theme, any brand colour, corners, control
4
5
  * size and motion speed, all applied live, and the result ready to take away
@@ -102,8 +103,9 @@ const TILE =
102
103
  "flex min-w-0 flex-col items-center gap-1 rounded-control p-1.5 text-[11px] text-muted transition-colors duration-(--duration-fast) hover:bg-surface-soft hover:text-ink"
103
104
  const SWATCH = "relative size-8 overflow-hidden rounded-full ring-2 transition-[box-shadow] duration-(--duration-fast)"
104
105
 
105
- const tuneTo = <K extends keyof Omit<StudioState, "theme">>(key: K) =>
106
- (value: string) => studio.tune({ [key]: value } as Partial<StudioState>)
106
+ const tuneTo = <K extends keyof Omit<StudioState, "theme">>(key: K, value: string) => studio.tune({ [key]: value } as Partial<StudioState>)
107
+
108
+ usePaletteTheme(() => studio ?? undefined)
107
109
  </script>
108
110
 
109
111
  <template>
@@ -171,7 +173,7 @@ const tuneTo = <K extends keyof Omit<StudioState, "theme">>(key: K) =>
171
173
  :model-value="state.corners"
172
174
  size="sm"
173
175
  :options="[{ value: 'sharp', label: text.sharp }, { value: 'default', label: text.default }, { value: 'round', label: text.round }]"
174
- @update:model-value="tuneTo('corners')"
176
+ @update:model-value="tuneTo('corners', $event)"
175
177
  />
176
178
  </LpThemeStudioSection>
177
179
 
@@ -180,7 +182,7 @@ const tuneTo = <K extends keyof Omit<StudioState, "theme">>(key: K) =>
180
182
  :model-value="state.size"
181
183
  size="sm"
182
184
  :options="[{ value: 'compact', label: text.compact }, { value: 'default', label: text.default }, { value: 'comfortable', label: text.comfortable }]"
183
- @update:model-value="tuneTo('size')"
185
+ @update:model-value="tuneTo('size', $event)"
184
186
  />
185
187
  </LpThemeStudioSection>
186
188
 
@@ -189,7 +191,7 @@ const tuneTo = <K extends keyof Omit<StudioState, "theme">>(key: K) =>
189
191
  :model-value="state.motion"
190
192
  size="sm"
191
193
  :options="[{ value: 'fast', label: text.fast }, { value: 'normal', label: text.normal }, { value: 'slow', label: text.slow }]"
192
- @update:model-value="tuneTo('motion')"
194
+ @update:model-value="tuneTo('motion', $event)"
193
195
  />
194
196
  </LpThemeStudioSection>
195
197
 
@@ -1,4 +1,5 @@
1
1
  <script setup lang="ts">
2
+ import { usePaletteTheme } from "../theme/palette"
2
3
  /*
3
4
  * Theme switcher. The kit convention for theming, so consumers stop hand-rolling
4
5
  * their own. Default behaviour mirrors LeaveHosting: LEFT-click cycles to the
@@ -298,6 +299,8 @@ const labelName = computed(() =>
298
299
  const triggerLabel = computed(
299
300
  () => props["aria-label"] ?? text.value.trigger.replace("{name}", labelName.value),
300
301
  )
302
+
303
+ usePaletteTheme(() => tuner ?? undefined)
301
304
  </script>
302
305
 
303
306
  <template>
@@ -6,6 +6,7 @@ export default { inheritAttrs: false }
6
6
  </script>
7
7
 
8
8
  <script setup lang="ts">
9
+ import { usePaletteControl, type PaletteControlOptions } from "../composables/usePaletteCommands"
9
10
  /*
10
11
  * Time-of-day field: hour and minute segments, typed or stepped with the arrow
11
12
  * keys (reka TimeField). The model is "HH:mm" (v-model), the same shape a
@@ -26,6 +27,8 @@ import LpIcon from "./LpIcon.vue"
26
27
 
27
28
  const props = withDefaults(
28
29
  defineProps<{
30
+ /** Contextual palette metadata; false excludes this control. */
31
+ palette?: PaletteControlOptions
29
32
  /** Selected time "HH:mm" (v-model). */
30
33
  modelValue?: string
31
34
  disabled?: boolean
@@ -38,7 +41,7 @@ const props = withDefaults(
38
41
  /** Show the clock icon at the start of the field. */
39
42
  icon?: boolean
40
43
  }>(),
41
- { size: "md", hourCycle: 24, icon: true },
44
+ { palette: undefined, size: "md", hourCycle: 24, icon: true },
42
45
  )
43
46
 
44
47
  const emit = defineEmits<{
@@ -84,6 +87,8 @@ const { class: rootClass, attrs: rest } = useMergedAttrs(() =>
84
87
  field.value.invalid ? "border-danger focus-within:ring-danger-soft" : "border-line focus-within:border-brand",
85
88
  ].join(" "),
86
89
  )
90
+
91
+ usePaletteControl({ disabled: () => field.value.disabled, invalid: () => field.value.invalid })
87
92
  </script>
88
93
 
89
94
  <template>
@@ -120,6 +120,27 @@ const PassThrough: FunctionalComponent = (_, { slots }) => slots.default?.()
120
120
 
121
121
  const target = { x: 0, y: 0 }
122
122
  const offset = ref({ x: 0, y: 0 })
123
+ const tipBody = ref<HTMLElement | null>(null)
124
+
125
+ /* reka's own collision padding, which the follow offset would otherwise undo. */
126
+ const EDGE = 8
127
+
128
+ /*
129
+ * reka keeps the tip inside the window for the box it was placed against; the
130
+ * follow offset is added after that, so near the right edge a wide tip (an
131
+ * uptime slice with its breakdown) slid half off screen. The offset is held to
132
+ * the room the window has on each side of where reka put the tip.
133
+ */
134
+ function inView(x: number): number {
135
+ const panel = tipBody.value?.parentElement
136
+ if (!panel) return x
137
+ const rect = panel.getBoundingClientRect()
138
+ const placed = rect.left - offset.value.x
139
+ const width = panel.ownerDocument.defaultView?.innerWidth ?? window.innerWidth
140
+ const min = EDGE - placed
141
+ const max = width - EDGE - rect.width - placed
142
+ return max < min ? x : Math.min(max, Math.max(min, x))
143
+ }
123
144
 
124
145
  /*
125
146
  * The spring constants come from the kit's shared "float" feel — the same ones
@@ -172,6 +193,7 @@ const onPointerMove = (e: PointerEvent) => {
172
193
  const half = props.side === "top" || props.side === "bottom" ? rect.height / 2 : 0
173
194
  const dir = props.side === "bottom" ? -1 : 1
174
195
  target.y = e.clientY - (rect.top + rect.height / 2) + half * dir
196
+ target.x = inView(target.x)
175
197
  // Without motion the tip goes straight to the reading instead of trailing.
176
198
  if (prefersReducedMotion()) {
177
199
  offset.value = { x: target.x, y: target.y }
@@ -272,7 +294,7 @@ const guardOutside = useOutsideGuard()
272
294
  ]"
273
295
  :style="followCursor ? { translate: `${offset.x}px ${offset.y}px` } : undefined"
274
296
  >
275
- <slot name="content">{{ content }}</slot>
297
+ <div ref="tipBody"><slot name="content">{{ content }}</slot></div>
276
298
  </TooltipContent>
277
299
  </TooltipPortal>
278
300
  </TooltipRoot>
@@ -3,27 +3,24 @@
3
3
  // appended to it, so opt out of the automatic pass-through that would add it
4
4
  // a second time, unmerged.
5
5
  export default { inheritAttrs: false }
6
+ export type { UptimeEvent, UptimeSegment, UptimeStatus } from "./uptimeBar"
6
7
  </script>
7
8
 
8
9
  <script setup lang="ts">
9
10
  import { computed, ref } from "vue"
10
11
  import LpNumberFlow from "./LpNumberFlow.vue"
11
12
  import LpRollingText from "./LpRollingText.vue"
13
+ import LpShift from "./LpShift.vue"
12
14
  import LpSkeleton from "./LpSkeleton.vue"
13
15
  import LpSwap from "./LpSwap.vue"
14
16
  import LpTooltip from "./LpTooltip.vue"
15
17
  import { TONES } from "./tone"
16
18
  import { useMergedAttrs } from "../composables/useMergedClass"
17
19
  import { useLabels } from "../composables/useLpConfig"
20
+ import { useLocale } from "../composables/useLocale"
21
+ import { DEFAULT_UNIT_LABELS, formatTime, toEpoch, type TimeUnitLabels } from "./timeSpan"
22
+ import { UPTIME_SEVERITY, uptimeFigure, uptimeRange, uptimeShare, type UptimeSegment, type UptimeStatus } from "./uptimeBar"
18
23
 
19
- export type UptimeStatus = "operational" | "degraded" | "down" | "maintenance" | "empty"
20
-
21
- export interface UptimeSegment {
22
- /** Health of this slice. `empty` renders a faded placeholder (no data). */
23
- status: UptimeStatus
24
- /** Optional rich tooltip body; falls back to a humanised status. */
25
- label?: string
26
- }
27
24
 
28
25
  const props = withDefaults(
29
26
  defineProps<{
@@ -35,6 +32,13 @@ const props = withDefaults(
35
32
  endLabel?: string
36
33
  /** Show the computed uptime % above the bar. */
37
34
  showUptime?: boolean
35
+ /**
36
+ * The uptime figure, when the app has a more exact one than the slices
37
+ * (a server that counted every minute). Otherwise it is worked out from
38
+ * the slices: their `durations`, else their spans, else their count.
39
+ */
40
+ uptime?: number | null
41
+ locale?: string
38
42
  /** Override the headline shown next to the uptime %. */
39
43
  title?: string
40
44
  /**
@@ -44,7 +48,7 @@ const props = withDefaults(
44
48
  */
45
49
  colors?: Partial<Record<UptimeStatus, string>>
46
50
  /** Status names, for the tooltip and screen readers. */
47
- labels?: Partial<Record<UptimeStatus | "loading", string>>
51
+ labels?: Partial<Record<UptimeStatus | "loading" | "uptime", string>>
48
52
  /**
49
53
  * The history is on its way. The row of ticks pulses in place of the
50
54
  * slices (as many as `segments` already holds, else `skeletonSlices`) at
@@ -88,26 +92,18 @@ const statusLabel = useLabels(
88
92
  maintenance: "Maintenance",
89
93
  empty: "No data",
90
94
  loading: "Loading",
95
+ uptime: "uptime",
91
96
  },
92
97
  () => props.labels,
93
98
  )
94
99
 
95
100
  const skeletonCount = computed(() => props.segments.length || props.skeletonSlices)
96
101
 
97
- // Uptime counts only slices with data; operational + maintenance are "up".
98
- const uptimePct = computed(() => {
99
- const tracked = props.segments.filter((s) => s.status !== "empty")
100
- if (!tracked.length) return null
101
- const up = tracked.filter((s) => s.status === "operational" || s.status === "maintenance").length
102
- return (up / tracked.length) * 100
103
- })
102
+ const locale = useLocale(() => props.locale)
103
+ const units = useLabels<TimeUnitLabels>("time", DEFAULT_UNIT_LABELS)
104
+
105
+ const uptimePct = computed(() => (props.uptime ?? null) !== null ? props.uptime! : uptimeShare(props.segments))
104
106
 
105
- /*
106
- * Places actually needed, so trailing zeros stay trimmed: 100 rather than
107
- * 100.00, 99.9 rather than 99.90. The counter needs a fixed count per render —
108
- * it cannot "trim" mid-roll without changing the row's shape — so it is derived
109
- * from the value instead of hard-coded at two.
110
- */
111
107
  /*
112
108
  * The gap is drawn INSIDE each slice, not between slices.
113
109
  *
@@ -136,7 +132,24 @@ const tip = computed(() => {
136
132
  if (i == null) return null
137
133
  const seg = props.segments[i]
138
134
  if (!seg) return null
139
- return { status: seg.status, text: seg.label ?? statusLabel.value[seg.status] }
135
+ return {
136
+ status: seg.status,
137
+ text: seg.label ?? statusLabel.value[seg.status],
138
+ range: seg.start !== undefined ? uptimeRange(seg.start, seg.end, locale.value) : "",
139
+ parts: UPTIME_SEVERITY.filter((st) => (seg.durations?.[st] ?? 0) > 0).map((st) => ({
140
+ status: st,
141
+ text: statusLabel.value[st],
142
+ time: formatTime(seg.durations![st]!, "units", units.value, 2),
143
+ })),
144
+ events: (seg.events ?? []).slice(0, 4).map((e) => ({
145
+ status: e.status ?? seg.status,
146
+ text: e.text,
147
+ at: e.at !== undefined && Number.isFinite(toEpoch(e.at))
148
+ ? new Intl.DateTimeFormat(locale.value, { hour: "2-digit", minute: "2-digit" }).format(toEpoch(e.at))
149
+ : "",
150
+ })),
151
+ more: Math.max(0, (seg.events?.length ?? 0) - 4),
152
+ }
140
153
  })
141
154
 
142
155
  /*
@@ -165,12 +178,7 @@ function onSlotKeydown(e: KeyboardEvent, i: number) {
165
178
  slots.value[next]?.focus()
166
179
  }
167
180
 
168
- const uptimeDecimals = computed(() => {
169
- const v = uptimePct.value
170
- if (v == null) return 0
171
- const trimmed = Number(v.toFixed(2))
172
- return Number.isInteger(trimmed) ? 0 : String(trimmed).split(".")[1].length
173
- })
181
+ const figure = computed(() => (uptimePct.value == null ? null : uptimeFigure(uptimePct.value)))
174
182
 
175
183
  // The bar sets its own gap between the strip and its caption, which a compact status row has to close.
176
184
  const { class: rootClass, attrs: rest } = useMergedAttrs(
@@ -210,13 +218,32 @@ const { class: rootClass, attrs: rest } = useMergedAttrs(
210
218
  </LpSkeleton>
211
219
  <LpTooltip v-else :delay="80" follow-cursor side="top" :disabled="hovered === null">
212
220
  <template #content>
213
- <span v-if="tip" class="flex items-center gap-1.5">
214
- <span
215
- class="size-1.5 shrink-0 rounded-full"
216
- :style="{ backgroundColor: colorFor(tip.status) }"
217
- />
218
- <LpRollingText :value="tip.text" :duration="180" align="start" />
219
- </span>
221
+ <LpShift v-if="tip" axis="both" class="flex">
222
+ <span class="flex min-w-36 flex-col gap-1">
223
+ <span class="flex items-center gap-1.5">
224
+ <span
225
+ class="size-1.5 shrink-0 rounded-full transition-colors duration-[var(--duration-fast)]"
226
+ :style="{ backgroundColor: colorFor(tip.status) }"
227
+ />
228
+ <LpRollingText :value="tip.text" :duration="180" align="start" class="font-medium" />
229
+ </span>
230
+ <LpRollingText v-if="tip.range" :value="tip.range" :duration="180" align="start" class="text-[0.7rem] opacity-70 tabular-nums" />
231
+ <span v-if="tip.parts.length > 1 || (tip.parts.length === 1 && tip.parts[0].status !== tip.status)" class="mt-0.5 flex flex-col gap-0.5">
232
+ <span v-for="part in tip.parts" :key="part.status" class="flex items-center gap-1.5 tabular-nums">
233
+ <span class="size-1.5 shrink-0 rounded-full" :style="{ backgroundColor: colorFor(part.status) }" />
234
+ <span class="flex-1">{{ part.text }}</span>
235
+ <LpRollingText :value="part.time" :duration="180" align="end" class="opacity-80" />
236
+ </span>
237
+ </span>
238
+ <span v-if="tip.events.length" class="mt-0.5 flex flex-col gap-0.5 border-t border-current/15 pt-1">
239
+ <span v-for="(event, n) in tip.events" :key="n" class="flex items-baseline gap-1.5">
240
+ <LpRollingText v-if="event.at" :value="event.at" :duration="180" align="start" class="shrink-0 tabular-nums opacity-70" />
241
+ <LpRollingText :value="event.text" :duration="180" align="start" wrap />
242
+ </span>
243
+ <span v-if="tip.more" class="opacity-70">+{{ tip.more }}</span>
244
+ </span>
245
+ </span>
246
+ </LpShift>
220
247
  </template>
221
248
 
222
249
  <!--
@@ -235,7 +262,7 @@ const { class: rootClass, attrs: rest } = useMergedAttrs(
235
262
  type="button"
236
263
  class="lp-uptime__slot min-w-0 flex-1 outline-none focus-visible:ring-2 focus-visible:ring-ring"
237
264
  :tabindex="i === tabStop ? 0 : -1"
238
- :aria-label="seg.label ?? statusLabel[seg.status]"
265
+ :aria-label="[seg.label ?? statusLabel[seg.status], seg.start !== undefined ? uptimeRange(seg.start, seg.end, locale) : ''].filter(Boolean).join(', ')"
239
266
  @pointerenter="hovered = i"
240
267
  @focus="focusIndex = i; hovered = i"
241
268
  @blur="hovered === i && (hovered = null)"
@@ -275,13 +302,14 @@ const { class: rootClass, attrs: rest } = useMergedAttrs(
275
302
  class="inline-block h-[0.9em] w-8 align-middle"
276
303
  />
277
304
  <LpNumberFlow
278
- v-else-if="uptimePct != null"
279
- :value="uptimePct"
280
- :decimals="uptimeDecimals"
305
+ v-else-if="figure"
306
+ :value="figure.value"
307
+ :decimals="figure.decimals"
308
+ grouped
281
309
  suffix="%"
282
310
  />
283
311
  <template v-else>—</template>
284
- <span class="text-muted"> uptime</span>
312
+ <span class="ml-1 text-muted">{{ statusLabel.uptime }}</span>
285
313
  </span>
286
314
  <span class="h-px flex-1 bg-line" />
287
315
  <span class="shrink-0">{{ endLabel }}</span>
@@ -0,0 +1,5 @@
1
+ <script setup lang="ts">
2
+ import { suppressPaletteDiscovery } from "../composables/usePaletteCommands"
3
+ suppressPaletteDiscovery()
4
+ </script>
5
+ <template><div data-lp-command-preview class="contents"><slot /></div></template>
@@ -1,4 +1,15 @@
1
1
  import { applyQueryFilters, parsePaletteQuery, type CommandSearchTerms, type HighlightPart, type SearchableCommand } from "./commandSearch"
2
+ import type { Command } from "./LpCommandPalette.vue"
3
+ import type { ContextMenuItemDef } from "./LpContextMenu.vue"
4
+
5
+ /** The same action definitions serve a row menu and the command palette. */
6
+ export function menuPaletteCommands(items: ContextMenuItemDef[], prefix: string): Command[] {
7
+ return items.map((item, index) => ({
8
+ id: `${prefix}/${index}`, label: item.label, icon: item.icon, disabled: item.disabled,
9
+ onSelect: item.onSelect, keepOpen: item.checked !== undefined,
10
+ children: item.children ? menuPaletteCommands(item.children, `${prefix}/${index}`) : undefined,
11
+ }))
12
+ }
2
13
 
3
14
  /*
4
15
  * The parts of LpCommandPalette that are plain logic, kept out of the SFC so
@@ -7,6 +18,7 @@ import { applyQueryFilters, parsePaletteQuery, type CommandSearchTerms, type Hig
7
18
 
8
19
  /** The fields of a command that decide whether the preview pane opens for it. */
9
20
  export interface PreviewableCommand {
21
+ previewComponent?: unknown
10
22
  preview?: string
11
23
  excerpt?: string
12
24
  noPreview?: boolean
@@ -23,9 +35,18 @@ export function commandHasPreview<T extends PreviewableCommand>(
23
35
  ): boolean {
24
36
  if (!command || command.noPreview) return false
25
37
  if (options.hasPreview) return options.hasPreview(command)
26
- return options.slot || !!(command.preview || command.excerpt)
38
+ return options.slot || !!(command.previewComponent || command.preview || command.excerpt)
27
39
  }
28
40
 
41
+ export const PALETTE_MODES = {
42
+ people: { prefix: "@", label: "People", icon: "lucide:users" },
43
+ channels: { prefix: "#", label: "Channels", icon: "lucide:hash" },
44
+ roles: { prefix: "&", label: "Roles", icon: "lucide:shield" },
45
+ commands: { prefix: ">", label: "Commands", icon: "lucide:terminal" },
46
+ help: { prefix: "?", label: "Help", icon: "lucide:circle-help" },
47
+ } as const
48
+ export type PaletteMode = keyof typeof PALETTE_MODES
49
+
29
50
  /** One line of LpPaletteFacts: what it is, and what it is set to now. */
30
51
  export interface PaletteFact {
31
52
  /** Text, or the preview slot's `highlight(text)` to mark the query. */
@@ -72,7 +72,7 @@ function score(command: SearchableCommand, terms: CommandSearchTerms): number {
72
72
  return total
73
73
  }
74
74
 
75
- export function rankCommandItems<T extends SearchableCommand>(items: T[], terms: CommandSearchTerms): T[] {
75
+ function rankExact<T extends SearchableCommand>(items: T[], terms: CommandSearchTerms): T[] {
76
76
  return items
77
77
  .map((command) => ({ command, score: score(command, terms) }))
78
78
  .filter((entry) => entry.score > 0)
@@ -80,6 +80,28 @@ export function rankCommandItems<T extends SearchableCommand>(items: T[], terms:
80
80
  .map((entry) => entry.command)
81
81
  }
82
82
 
83
+ const LATIN_KEYS = "`qwertyuiop[]asdfghjkl;'zxcvbnm,."
84
+ const CYRILLIC_KEYS = "ёйцукенгшщзхъфывапролджэячсмитьбю"
85
+ const LAYOUT_KEYS = new Map([...LATIN_KEYS].flatMap((key, i) => [[key, CYRILLIC_KEYS[i]!], [CYRILLIC_KEYS[i]!, key]]))
86
+
87
+ export function swapCommandLayout(value: string): string {
88
+ return [...value].map(char => {
89
+ const replacement = LAYOUT_KEYS.get(char.toLowerCase())
90
+ return replacement ? char === char.toUpperCase() ? replacement.toUpperCase() : replacement : char
91
+ }).join("")
92
+ }
93
+
94
+ export function searchCommandItems<T extends SearchableCommand>(items: T[], terms: CommandSearchTerms): { items: T[]; terms: string[] } {
95
+ const exact = rankExact(items, terms)
96
+ if (exact.length || !terms.include.length) return { items: exact, terms: terms.include }
97
+ const alternate = { include: terms.include.map(swapCommandLayout), exclude: terms.exclude, groups: terms.groups }
98
+ return { items: rankExact(items, alternate), terms: alternate.include }
99
+ }
100
+
101
+ export function rankCommandItems<T extends SearchableCommand>(items: T[], terms: CommandSearchTerms): T[] {
102
+ return searchCommandItems(items, terms).items
103
+ }
104
+
83
105
  /**
84
106
  * A query as a fetcher receives it. `text` is what a remote search should be
85
107
  * sent: the positive words and phrases, in the case they were typed, without
@@ -134,3 +134,13 @@ export function compactNumber(value: number, options: CompactNumberOptions = {})
134
134
  }).format(scaled)
135
135
  return `${number}${COMPACT_UNITS[i][1]}`
136
136
  }
137
+
138
+ /**
139
+ * The character a locale writes between the whole and the fractional part:
140
+ * "." in English, "," in Russian and German. A counter that splits a grouped
141
+ * figure into columns has to find the fraction by this mark, not by ".",
142
+ * because in German "." is the thousands separator.
143
+ */
144
+ export function decimalMark(locale?: string): string {
145
+ return numberFormat(locale, {}).formatToParts(1.5).find((p) => p.type === "decimal")?.value ?? "."
146
+ }