@leavepulse/ui 0.70.0 → 0.71.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 (345) hide show
  1. package/README.md +103 -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/LpRepeater-C1Cw27hj.css +21 -0
  6. package/dist/assets/{LpRollingText-BGnE6q9j.css → LpRollingText-dyOXedGO.css} +7 -0
  7. package/dist/assets/{LpTable-DHxEQ_-3.css → LpTable-DHvorDiU.css} +19 -19
  8. package/dist/assets/LpTabs-CO3EcsNV.css +18 -0
  9. package/dist/chunks/{ContextMenuItems-B5uE8Znw.js → ContextMenuItems-eS7qx9gD.js} +11 -5
  10. package/dist/chunks/{DropzoneLinkField-Bw8Flkhk.js → DropzoneLinkField-DkFhdfEJ.js} +2 -2
  11. package/dist/chunks/{FieldWrapper-BiennROB.js → FieldWrapper-YzbvvC3M.js} +1 -1
  12. package/dist/chunks/{LayoutCanvas-BsJIzhdd.js → LayoutCanvas-Ba4A9QC5.js} +1 -1
  13. package/dist/chunks/{LayoutNode-DJVhkSGk.js → LayoutNode-CTTTLMDM.js} +1 -1
  14. package/dist/chunks/{LpActionBar-CzOFar5t.js → LpActionBar-B7anGlkI.js} +2 -2
  15. package/dist/chunks/{LpAddressInput-CNnLTDZg.js → LpAddressInput-CD0bzrt-.js} +7 -7
  16. package/dist/chunks/{LpAlert-C5igj802.js → LpAlert-BHi7ARqu.js} +1 -1
  17. package/dist/chunks/{LpAppShell-CEUAB8kK.js → LpAppShell-1RmBp6xQ.js} +3 -3
  18. package/dist/chunks/{LpAutocomplete-D92niDHE.js → LpAutocomplete-DDqe2M5g.js} +5 -2
  19. package/dist/chunks/{LpAvatar-CLPl-qOF.js → LpAvatar-DqfKCIdr.js} +1 -1
  20. package/dist/chunks/{LpBadge-HOEPBYmi.js → LpBadge-C8fKEmTF.js} +2 -2
  21. package/dist/chunks/{LpBreadcrumbs-DYoPc3P4.js → LpBreadcrumbs-DhcoStFa.js} +2 -2
  22. package/dist/chunks/{LpButton-CVxBfGjd.js → LpButton-BQDAFXBd.js} +3 -0
  23. package/dist/chunks/{LpCalendar-pVL91-Pl.js → LpCalendar-NmrA47J9.js} +1 -1
  24. package/dist/chunks/{LpCheckbox-DHc2bHDe.js → LpCheckbox-BoOuLnGB.js} +5 -2
  25. package/dist/chunks/{LpCodeBlock-BGsQhxw8.js → LpCodeBlock-Czu-fq1M.js} +2 -2
  26. package/dist/chunks/{LpCodeDisplay-CIKKT2iW.js → LpCodeDisplay-BK2FHbtM.js} +6 -6
  27. package/dist/chunks/{LpColorPicker-DFugXNUi.js → LpColorPicker-Ee1DZiRd.js} +3 -3
  28. package/dist/chunks/LpCommandScope-ubtWDMa0.js +53 -0
  29. package/dist/chunks/{LpConfirmDialog-CfGCux8m.js → LpConfirmDialog-i15pulMT.js} +3 -3
  30. package/dist/chunks/{LpConfirmHost-exB7JmcO.js → LpConfirmHost-D0oGavtd.js} +1 -1
  31. package/dist/chunks/{LpContextMenu-COwOwRgI.js → LpContextMenu-DXpFJjlO.js} +17 -6
  32. package/dist/chunks/{LpCopyButton-DzHjnFs0.js → LpCopyButton-Dmq7DmFw.js} +3 -3
  33. package/dist/chunks/{LpCountdown-Dtq1ihlJ.js → LpCountdown-DTdMF8DL.js} +2 -2
  34. package/dist/chunks/{LpDataList-B-5nHfNw.js → LpDataList-CFPtSsBB.js} +7 -6
  35. package/dist/chunks/{LpDatePicker-BUe1VUC0.js → LpDatePicker-cC9K-Pk8.js} +7 -6
  36. package/dist/chunks/{LpDateRangePicker-C_k65vGR.js → LpDateRangePicker-BVQKPbvo.js} +5 -4
  37. package/dist/chunks/{LpDisclosure-DOarzlAG.js → LpDisclosure-hRnX3FWL.js} +4 -1
  38. package/dist/chunks/{LpDrawer-DGNKRsGq.js → LpDrawer-u4rxnuR-.js} +1 -1
  39. package/dist/chunks/{LpDropdownMenu-CRgrhdAH.js → LpDropdownMenu-BgZ0Z_Im.js} +17 -4
  40. package/dist/chunks/{LpEditable-DTqLt2lT.js → LpEditable-CjxuOVh_.js} +7 -5
  41. package/dist/chunks/{LpEmptyState-KTKipws2.js → LpEmptyState-CKFTtQgJ.js} +1 -1
  42. package/dist/chunks/{LpFieldNotice-CurbNuXI.js → LpFieldNotice-CPI8EwtU.js} +1 -1
  43. package/dist/chunks/{LpFileTree-DMISXVHC.js → LpFileTree-BH0gNlSJ.js} +2 -2
  44. package/dist/chunks/{LpFileTreeNode-CPXB_Yy3.js → LpFileTreeNode-gPVB1oXM.js} +4 -4
  45. package/dist/chunks/{LpFormField-BWwgm1E4.js → LpFormField-gRASfyj2.js} +4 -1
  46. package/dist/chunks/{LpGauge-Bk8TwJj0.js → LpGauge-BdjPGnqw.js} +2 -2
  47. package/dist/chunks/{LpHintLayer-p4hqzdyA.js → LpHintLayer-C2asBIfs.js} +2 -2
  48. package/dist/chunks/{LpHints-CCrhRfy9.js → LpHints-CNZQ76JV.js} +3 -3
  49. package/dist/chunks/{LpIcon-HJP5xMz4.js → LpIcon-DkP-9mzK.js} +1 -1
  50. package/dist/chunks/{LpInfraNode-CUC3t4na.js → LpInfraNode-yTh4-5qa.js} +2 -2
  51. package/dist/chunks/{LpInput-CDuwODJG.js → LpInput-2H-5ILOB.js} +5 -2
  52. package/dist/chunks/{LpLightbox-XJ0LpTcZ.js → LpLightbox-lAt5uIfx.js} +2 -2
  53. package/dist/chunks/{LpLogViewer-elZHbj85.js → LpLogViewer-x78YfhJf.js} +5 -5
  54. package/dist/chunks/{LpMapPicker-BtIl7NOM.js → LpMapPicker-Dp5RSCi9.js} +1 -1
  55. package/dist/chunks/{LpMediaGallery-D5swlmZ_.js → LpMediaGallery-D92oJfY_.js} +3 -3
  56. package/dist/chunks/{LpModal-eBMt2Fqn.js → LpModal-CpH0bNo7.js} +1 -1
  57. package/dist/chunks/{LpModalNav-D4Z0wVHR.js → LpModalNav-BH4ANQGJ.js} +4 -4
  58. package/dist/chunks/{LpNotificationBell-DPZmP5dM.js → LpNotificationBell-BdUQqZhs.js} +8 -8
  59. package/dist/chunks/{LpNumberField-B9k-h2HE.js → LpNumberField-DZY3Z670.js} +4 -1
  60. package/dist/chunks/{LpNumberFlow-Cfyh033Q.js → LpNumberFlow-5khYxsfX.js} +6 -3
  61. package/dist/chunks/{LpPagination-VNCX6pFC.js → LpPagination-DbeACavM.js} +1 -1
  62. package/dist/chunks/LpPaletteTheme-CqWNRwHE.js +104 -0
  63. package/dist/chunks/{LpPasswordInput-DTV18FIh.js → LpPasswordInput-BzAZsf5V.js} +14 -8
  64. package/dist/chunks/{LpPhoneInput-C60tx1tC.js → LpPhoneInput-BMpd-0QR.js} +2 -2
  65. package/dist/chunks/{LpPie-CFuV7pZv.js → LpPie-DdFSTbGE.js} +5 -5
  66. package/dist/chunks/{LpPipeline-D81yh_NX.js → LpPipeline-zjEM1Lwc.js} +4 -4
  67. package/dist/chunks/{LpPopover-BEIcqb9p.js → LpPopover-DmZ3wUtP.js} +6 -5
  68. package/dist/chunks/{LpProgress-CE-m5OpD.js → LpProgress-BX33EKkN.js} +1 -1
  69. package/dist/chunks/{LpQuota-2Sw_gqcJ.js → LpQuota-DqNv1yjs.js} +3 -3
  70. package/dist/chunks/{LpRadioGroup-CGBr5nIp.js → LpRadioGroup-CsXeV9Ct.js} +4 -1
  71. package/dist/chunks/{LpRangeCalendar-c5qj7YcC.js → LpRangeCalendar-l-0nSFmY.js} +1 -1
  72. package/dist/chunks/{LpRangeSlider-Dv6lNvHY.js → LpRangeSlider-CM-0ZITZ.js} +3 -0
  73. package/dist/chunks/{LpRelativeTime-CKzVqAo9.js → LpRelativeTime-UM8EkfrN.js} +2 -2
  74. package/dist/chunks/LpRepeater-OvzByE5L.js +180 -0
  75. package/dist/chunks/{LpRollingText-DB7upf-5.js → LpRollingText-DiL717LK.js} +4 -3
  76. package/dist/chunks/{LpSecretField-DMfdJ1cQ.js → LpSecretField-DwQiPOko.js} +27 -16
  77. package/dist/chunks/{LpSection-BW4xJ-Li.js → LpSection-DcQsieHG.js} +1 -1
  78. package/dist/chunks/{LpSegmented-BO99Sr4m.js → LpSegmented-D2VST0o3.js} +4 -1
  79. package/dist/chunks/{LpSelect-B4f_yRi5.js → LpSelect-DYX2-u5A.js} +43 -18
  80. package/dist/chunks/{LpSidebar-D-M0Y_7U.js → LpSidebar-D44peKq2.js} +3 -3
  81. package/dist/chunks/{LpSidebarNav-DF-TYxOV.js → LpSidebarNav--VwkXO_a.js} +3 -3
  82. package/dist/chunks/{LpSlider-DuoRpOrH.js → LpSlider-DxYoCJJO.js} +3 -0
  83. package/dist/chunks/{LpStat-B2ix1Q7t.js → LpStat-DIsQfbeD.js} +3 -3
  84. package/dist/chunks/{LpStatus-C7kx5PKE.js → LpStatus-BQU1TUsv.js} +2 -2
  85. package/dist/chunks/{LpStepMark-DK-5BKAt.js → LpStepMark-CXa7DJy_.js} +2 -2
  86. package/dist/chunks/{LpStepper-DRFfdJaq.js → LpStepper-C9RM_yTG.js} +1 -1
  87. package/dist/chunks/{LpSwitch-DrzKrZZV.js → LpSwitch-bGZxUDSx.js} +3 -0
  88. package/dist/chunks/{LpTableFilter-DyvHGbOk.js → LpTableFilter-CTZMqFDU.js} +2 -2
  89. package/dist/chunks/{LpTableOfContents-rYvo8z5T.js → LpTableOfContents-CraicEs4.js} +2 -2
  90. package/dist/chunks/{LpTagsInput-DgpBFRV2.js → LpTagsInput-DrjmrOge.js} +8 -5
  91. package/dist/chunks/{LpTextarea-DiWjeVoJ.js → LpTextarea-BbYqmG1S.js} +4 -1
  92. package/dist/chunks/{LpThemeStudio-DguwHdGt.js → LpThemeStudio-B2nPaG-h.js} +18 -14
  93. package/dist/chunks/{LpThemeSwitcher-D5uz0MhK.js → LpThemeSwitcher-C0A5sg_E.js} +6 -4
  94. package/dist/chunks/{LpTimeField-BFQVf_PW.js → LpTimeField-DQ8WvPr2.js} +4 -1
  95. package/dist/chunks/{LpToaster-Wv9ksj4q.js → LpToaster-BTTl_wuy.js} +2 -2
  96. package/dist/chunks/{LpTooltip-Cz8SQwRt.js → LpTooltip-BcBvIx4z.js} +23 -5
  97. package/dist/chunks/{LpTopologyCanvas-ZhKthfgz.js → LpTopologyCanvas-B0wEvAoq.js} +1 -1
  98. package/dist/chunks/{LpTour-NfRJNEIP.js → LpTour-BgCfNlaW.js} +1 -1
  99. package/dist/chunks/{LpTruncate-D_5Pfv5B.js → LpTruncate-TcqfkyJM.js} +2 -2
  100. package/dist/chunks/{LpUptimeBar-DWegV2p6.js → LpUptimeBar-DRadGq4Y.js} +136 -47
  101. package/dist/chunks/{MenuTitle-BjQZX8tQ.js → MenuTitle-Dab3EGOp.js} +1 -1
  102. package/dist/chunks/{OptionContent-CmYUUeWj.js → OptionContent-DPzNsybd.js} +1 -1
  103. package/dist/chunks/PalettePreviewBoundary-B0Hhu3Yw.js +20 -0
  104. package/dist/chunks/{PickerField-CHRS1h7K.js → PickerField-DwLw7j0L.js} +26 -17
  105. package/dist/chunks/{RevealToggle-C-KzaQGJ.js → RevealToggle-BVFM_FAU.js} +3 -3
  106. package/dist/chunks/{TourCard-BXuVHsfn.js → TourCard--2IfgnUU.js} +3 -3
  107. package/dist/chunks/icons-CLu1iR1j.js +43 -0
  108. package/dist/chunks/palette-BiY7yQZL.js +29 -0
  109. package/dist/component-names.d.ts +1 -1
  110. package/dist/components/ContextMenuItems.vue.js +1 -1
  111. package/dist/components/DropzoneLinkField.vue.js +1 -1
  112. package/dist/components/FieldWrapper.vue.js +1 -1
  113. package/dist/components/LayoutCanvas.vue.js +1 -1
  114. package/dist/components/LayoutNode.vue.js +1 -1
  115. package/dist/components/LpActionBar.vue.js +1 -1
  116. package/dist/components/LpAddressInput.vue.d.ts +1 -1
  117. package/dist/components/LpAddressInput.vue.js +1 -1
  118. package/dist/components/LpAlert.vue.d.ts +3 -3
  119. package/dist/components/LpAlert.vue.js +1 -1
  120. package/dist/components/LpAppShell.vue.js +1 -1
  121. package/dist/components/LpAutocomplete.vue.d.ts +7 -1
  122. package/dist/components/LpAutocomplete.vue.js +1 -1
  123. package/dist/components/LpAvatar.vue.js +1 -1
  124. package/dist/components/LpBadge.vue.d.ts +2 -2
  125. package/dist/components/LpBadge.vue.js +1 -1
  126. package/dist/components/LpBreadcrumbs.vue.js +1 -1
  127. package/dist/components/LpButton.vue.d.ts +4 -0
  128. package/dist/components/LpButton.vue.js +1 -1
  129. package/dist/components/LpCalendar.vue.js +1 -1
  130. package/dist/components/LpChart.vue.js +27 -25
  131. package/dist/components/LpCheckbox.vue.d.ts +8 -1
  132. package/dist/components/LpCheckbox.vue.js +1 -1
  133. package/dist/components/LpCodeBlock.vue.js +1 -1
  134. package/dist/components/LpCodeDisplay.vue.js +1 -1
  135. package/dist/components/LpColorPicker.vue.d.ts +2 -2
  136. package/dist/components/LpColorPicker.vue.js +1 -1
  137. package/dist/components/LpCommandPalette.vue.d.ts +43 -7
  138. package/dist/components/LpCommandPalette.vue.js +451 -126
  139. package/dist/components/LpCommandScope.vue.d.ts +28 -0
  140. package/dist/components/LpCommandScope.vue.js +4 -0
  141. package/dist/components/LpConfirmDialog.vue.js +1 -1
  142. package/dist/components/LpConfirmHost.vue.js +1 -1
  143. package/dist/components/LpContextMenu.vue.d.ts +5 -1
  144. package/dist/components/LpContextMenu.vue.js +1 -1
  145. package/dist/components/LpCopyButton.vue.d.ts +1 -1
  146. package/dist/components/LpCopyButton.vue.js +1 -1
  147. package/dist/components/LpCountdown.vue.js +1 -1
  148. package/dist/components/LpDataList.vue.js +1 -1
  149. package/dist/components/LpDatePicker.vue.d.ts +2 -0
  150. package/dist/components/LpDatePicker.vue.js +1 -1
  151. package/dist/components/LpDateRangePicker.vue.d.ts +2 -0
  152. package/dist/components/LpDateRangePicker.vue.js +1 -1
  153. package/dist/components/LpDisclosure.vue.d.ts +4 -0
  154. package/dist/components/LpDisclosure.vue.js +1 -1
  155. package/dist/components/LpDock.vue.js +2 -2
  156. package/dist/components/LpDrawer.vue.js +1 -1
  157. package/dist/components/LpDropdownMenu.vue.d.ts +5 -1
  158. package/dist/components/LpDropdownMenu.vue.js +1 -1
  159. package/dist/components/LpDropzone.vue.js +2 -2
  160. package/dist/components/LpEditable.vue.js +1 -1
  161. package/dist/components/LpEmptyState.vue.js +1 -1
  162. package/dist/components/LpFieldNotice.vue.js +1 -1
  163. package/dist/components/LpFileTree.vue.d.ts +1 -1
  164. package/dist/components/LpFileTree.vue.js +1 -1
  165. package/dist/components/LpFileTreeNode.vue.js +1 -1
  166. package/dist/components/LpFormField.vue.js +1 -1
  167. package/dist/components/LpGauge.vue.js +1 -1
  168. package/dist/components/LpHintLayer.vue.js +1 -1
  169. package/dist/components/LpHints.vue.js +1 -1
  170. package/dist/components/LpIcon.vue.js +1 -1
  171. package/dist/components/LpImageEditor.vue.js +3 -2
  172. package/dist/components/LpInfraNode.vue.js +1 -1
  173. package/dist/components/LpInput.vue.d.ts +6 -0
  174. package/dist/components/LpInput.vue.js +1 -1
  175. package/dist/components/LpLightbox.vue.js +1 -1
  176. package/dist/components/LpLoader.vue.d.ts +1 -1
  177. package/dist/components/LpLogViewer.vue.js +1 -1
  178. package/dist/components/LpMapPicker.vue.js +1 -1
  179. package/dist/components/LpMediaGallery.vue.d.ts +2 -2
  180. package/dist/components/LpMediaGallery.vue.js +1 -1
  181. package/dist/components/LpModal.vue.js +1 -1
  182. package/dist/components/LpModalNav.vue.js +1 -1
  183. package/dist/components/LpNotificationBell.vue.js +1 -1
  184. package/dist/components/LpNumberField.vue.d.ts +6 -0
  185. package/dist/components/LpNumberField.vue.js +1 -1
  186. package/dist/components/LpNumberFlow.vue.js +1 -1
  187. package/dist/components/LpPagination.vue.js +1 -1
  188. package/dist/components/LpPaletteFacts.vue.d.ts +1 -0
  189. package/dist/components/LpPaletteFacts.vue.js +40 -26
  190. package/dist/components/LpPaletteHeader.vue.js +2 -2
  191. package/dist/components/LpPaletteStats.vue.d.ts +1 -0
  192. package/dist/components/LpPaletteStats.vue.js +45 -21
  193. package/dist/components/LpPaletteTheme.vue.d.ts +7 -0
  194. package/dist/components/LpPaletteTheme.vue.js +4 -0
  195. package/dist/components/LpPasswordInput.vue.d.ts +7 -1
  196. package/dist/components/LpPasswordInput.vue.js +1 -1
  197. package/dist/components/LpPhoneInput.vue.js +1 -1
  198. package/dist/components/LpPie.vue.js +1 -1
  199. package/dist/components/LpPipeline.vue.js +1 -1
  200. package/dist/components/LpPopover.vue.js +1 -1
  201. package/dist/components/LpProgress.vue.d.ts +1 -1
  202. package/dist/components/LpProgress.vue.js +1 -1
  203. package/dist/components/LpQuota.vue.js +1 -1
  204. package/dist/components/LpRadioGroup.vue.d.ts +6 -1
  205. package/dist/components/LpRadioGroup.vue.js +1 -1
  206. package/dist/components/LpRangeCalendar.vue.js +1 -1
  207. package/dist/components/LpRangeSlider.vue.d.ts +6 -0
  208. package/dist/components/LpRangeSlider.vue.js +1 -1
  209. package/dist/components/LpRelativeTime.vue.js +1 -1
  210. package/dist/components/LpRepeater.vue.js +1 -1
  211. package/dist/components/LpRollingText.vue.d.ts +2 -0
  212. package/dist/components/LpRollingText.vue.js +1 -1
  213. package/dist/components/LpSecretField.vue.js +1 -1
  214. package/dist/components/LpSection.vue.js +1 -1
  215. package/dist/components/LpSegmented.vue.d.ts +6 -0
  216. package/dist/components/LpSegmented.vue.js +1 -1
  217. package/dist/components/LpSelect.vue.d.ts +3 -0
  218. package/dist/components/LpSelect.vue.js +1 -1
  219. package/dist/components/LpShinyText.vue.d.ts +1 -1
  220. package/dist/components/LpShortcutsDialog.vue.js +1 -1
  221. package/dist/components/LpSidebar.vue.js +1 -1
  222. package/dist/components/LpSidebarNav.vue.js +1 -1
  223. package/dist/components/LpSlider.vue.d.ts +6 -0
  224. package/dist/components/LpSlider.vue.js +1 -1
  225. package/dist/components/LpStat.vue.js +1 -1
  226. package/dist/components/LpStatus.vue.js +1 -1
  227. package/dist/components/LpStepMark.vue.js +1 -1
  228. package/dist/components/LpStepper.vue.js +1 -1
  229. package/dist/components/LpSwitch.vue.d.ts +6 -0
  230. package/dist/components/LpSwitch.vue.js +1 -1
  231. package/dist/components/LpTable.vue.d.ts +6 -0
  232. package/dist/components/LpTable.vue.js +74 -35
  233. package/dist/components/LpTableFilter.vue.js +1 -1
  234. package/dist/components/LpTableOfContents.vue.js +1 -1
  235. package/dist/components/LpTabs.vue.d.ts +1 -1
  236. package/dist/components/LpTabs.vue.js +15 -6
  237. package/dist/components/LpTagsInput.vue.d.ts +6 -0
  238. package/dist/components/LpTagsInput.vue.js +1 -1
  239. package/dist/components/LpTextarea.vue.d.ts +8 -1
  240. package/dist/components/LpTextarea.vue.js +1 -1
  241. package/dist/components/LpThemeStudio.vue.js +1 -1
  242. package/dist/components/LpThemeSwitcher.vue.js +1 -1
  243. package/dist/components/LpTimeField.vue.d.ts +6 -0
  244. package/dist/components/LpTimeField.vue.js +1 -1
  245. package/dist/components/LpToaster.vue.js +1 -1
  246. package/dist/components/LpTooltip.vue.js +1 -1
  247. package/dist/components/LpTopologyCanvas.vue.js +1 -1
  248. package/dist/components/LpTour.vue.js +1 -1
  249. package/dist/components/LpTruncate.vue.js +1 -1
  250. package/dist/components/LpUptimeBar.vue.d.ts +18 -9
  251. package/dist/components/LpUptimeBar.vue.js +1 -1
  252. package/dist/components/MenuTitle.vue.js +1 -1
  253. package/dist/components/OptionContent.vue.js +1 -1
  254. package/dist/components/PalettePreviewBoundary.vue.d.ts +13 -0
  255. package/dist/components/PalettePreviewBoundary.vue.js +4 -0
  256. package/dist/components/PickerField.vue.d.ts +5 -3
  257. package/dist/components/PickerField.vue.js +1 -1
  258. package/dist/components/RevealToggle.vue.js +1 -1
  259. package/dist/components/TourCard.vue.js +1 -1
  260. package/dist/components/commandPalette.d.ts +33 -0
  261. package/dist/components/commandPalette.js +21 -1
  262. package/dist/components/commandSearch.d.ts +5 -0
  263. package/dist/components/commandSearch.js +22 -2
  264. package/dist/components/format.d.ts +7 -0
  265. package/dist/components/format.js +4 -0
  266. package/dist/components/menuWhen.js +1 -1
  267. package/dist/components/uptimeBar.d.ts +45 -0
  268. package/dist/components/uptimeBar.js +51 -0
  269. package/dist/composables/scroll.d.ts +5 -0
  270. package/dist/composables/scroll.js +7 -0
  271. package/dist/composables/useField.d.ts +9 -0
  272. package/dist/composables/useField.js +24 -1
  273. package/dist/composables/useForm.js +13 -4
  274. package/dist/composables/usePaletteCommands.d.ts +61 -0
  275. package/dist/composables/usePaletteCommands.js +220 -0
  276. package/dist/composables/useReveal.js +21 -0
  277. package/dist/composables/useView.js +9 -2
  278. package/dist/icons-all.js +1 -1
  279. package/dist/index.d.ts +14 -1
  280. package/dist/index.js +103 -87
  281. package/dist/theme/palette.d.ts +9 -0
  282. package/dist/theme/studio.d.ts +12 -1
  283. package/package.json +4 -1
  284. package/src/component-names.ts +2 -0
  285. package/src/components/ContextMenuItems.vue +5 -2
  286. package/src/components/LpAutocomplete.vue +6 -1
  287. package/src/components/LpButton.vue +5 -1
  288. package/src/components/LpChart.vue +19 -17
  289. package/src/components/LpCheckbox.vue +7 -2
  290. package/src/components/LpCommandPalette.vue +263 -29
  291. package/src/components/LpCommandScope.vue +35 -0
  292. package/src/components/LpContextMenu.vue +17 -5
  293. package/src/components/LpDataList.vue +2 -2
  294. package/src/components/LpDatePicker.vue +2 -0
  295. package/src/components/LpDateRangePicker.vue +2 -0
  296. package/src/components/LpDisclosure.vue +5 -1
  297. package/src/components/LpDropdownMenu.vue +14 -3
  298. package/src/components/LpEditable.vue +3 -1
  299. package/src/components/LpFormField.vue +3 -0
  300. package/src/components/LpInput.vue +6 -1
  301. package/src/components/LpNumberField.vue +6 -1
  302. package/src/components/LpNumberFlow.vue +12 -2
  303. package/src/components/LpPaletteFacts.vue +11 -6
  304. package/src/components/LpPaletteStats.vue +12 -5
  305. package/src/components/LpPaletteTheme.vue +48 -0
  306. package/src/components/LpPasswordInput.vue +13 -5
  307. package/src/components/LpPopover.vue +4 -0
  308. package/src/components/LpRadioGroup.vue +4 -1
  309. package/src/components/LpRangeSlider.vue +6 -1
  310. package/src/components/LpRepeater.vue +64 -13
  311. package/src/components/LpRollingText.vue +12 -1
  312. package/src/components/LpSecretField.vue +18 -8
  313. package/src/components/LpSegmented.vue +6 -1
  314. package/src/components/LpSelect.vue +44 -10
  315. package/src/components/LpSlider.vue +6 -1
  316. package/src/components/LpSwitch.vue +6 -1
  317. package/src/components/LpTable.vue +35 -2
  318. package/src/components/LpTableOfContents.vue +2 -4
  319. package/src/components/LpTabs.vue +7 -0
  320. package/src/components/LpTagsInput.vue +6 -1
  321. package/src/components/LpTextarea.vue +7 -2
  322. package/src/components/LpThemeStudio.vue +7 -5
  323. package/src/components/LpThemeSwitcher.vue +3 -0
  324. package/src/components/LpTimeField.vue +6 -1
  325. package/src/components/LpTooltip.vue +23 -1
  326. package/src/components/LpUptimeBar.vue +69 -41
  327. package/src/components/PalettePreviewBoundary.vue +5 -0
  328. package/src/components/PickerField.vue +5 -4
  329. package/src/components/commandPalette.ts +22 -1
  330. package/src/components/commandSearch.ts +23 -1
  331. package/src/components/format.ts +10 -0
  332. package/src/components/uptimeBar.ts +98 -0
  333. package/src/composables/scroll.ts +11 -0
  334. package/src/composables/useField.ts +33 -1
  335. package/src/composables/useForm.ts +14 -5
  336. package/src/composables/usePaletteCommands.ts +220 -0
  337. package/src/composables/useReveal.ts +29 -0
  338. package/src/composables/useView.ts +8 -1
  339. package/src/index.ts +18 -1
  340. package/src/kit-icons.ts +1 -1
  341. package/src/theme/palette.ts +26 -0
  342. package/src/theme/studio.ts +15 -8
  343. package/dist/assets/LpTabs-BJ-nrJuI.css +0 -18
  344. package/dist/chunks/LpRepeater-D0VjmD6y.js +0 -140
  345. package/dist/chunks/icons-4IywzkHk.js +0 -43
@@ -36,7 +36,11 @@ import {
36
36
  DialogRoot,
37
37
  DialogTitle,
38
38
  } from "reka-ui"
39
- import { computed, nextTick, onBeforeUnmount, ref, shallowRef, useId, watch } from "vue"
39
+ import { type Component, computed, h, nextTick, onBeforeUnmount, ref, shallowRef, useId, watch } from "vue"
40
+ import { mergePaletteCommands, suppressPaletteDiscovery, usePaletteScope } from "../composables/usePaletteCommands"
41
+ import { useToast } from "../composables/useToast"
42
+ import LpButton from "./LpButton.vue"
43
+ import PalettePreviewBoundary from "./PalettePreviewBoundary.vue"
40
44
  import LpAvatar from "./LpAvatar.vue"
41
45
  import LpIcon from "./LpIcon.vue"
42
46
  import LpLoader from "./LpLoader.vue"
@@ -57,12 +61,14 @@ import {
57
61
  highlightParts,
58
62
  parseCommandQuery,
59
63
  parsePaletteQuery,
60
- rankCommandItems,
64
+ searchCommandItems,
61
65
  type HighlightPart,
62
66
  type PaletteQuery,
63
67
  } from "./commandSearch"
64
68
  import {
65
69
  commandHasPreview,
70
+ PALETTE_MODES,
71
+ type PaletteMode,
66
72
  fallbackRows,
67
73
  matchModePrefix,
68
74
  modeChipLabel,
@@ -227,7 +233,23 @@ export interface Command {
227
233
  * placeholder. For a whole class of rows, `hasPreview` says it once.
228
234
  */
229
235
  noPreview?: boolean
230
- onSelect?: () => void
236
+ /** Render a live editor or other rich preview without an application-wide slot. */
237
+ previewComponent?: Component
238
+ /** Enter or click focuses the preview editor, including on narrow screens. */
239
+ editInPreview?: boolean
240
+ /** Runs after the dialog releases its focus trap (e.g. focus the original field). */
241
+ afterClose?: () => void
242
+ prompt?: CommandPrompt
243
+ /** Called only when execution succeeded, with its result. */
244
+ undo?: (result: unknown) => unknown | Promise<unknown>
245
+ onSelect?: () => unknown | Promise<unknown>
246
+ }
247
+
248
+ export interface CommandPrompt {
249
+ placeholder?: string
250
+ initial?: string
251
+ validate?: (value: string) => string | undefined | Promise<string | undefined>
252
+ submit: (value: string) => unknown | Promise<unknown>
231
253
  }
232
254
 
233
255
  /**
@@ -257,8 +279,17 @@ export type CommandFetcher = (
257
279
  ) => Command[] | Promise<Command[]>
258
280
 
259
281
  /** Footer hints and the mode chip, spelled out so an app can translate them. */
260
- export type PaletteLabels = {
282
+ export type PaletteLabels = Partial<Record<PaletteMode, string>> & {
261
283
  navigate: string
284
+ pin?: string
285
+ unpin?: string
286
+ undo?: string
287
+ backToResults?: string
288
+ actionFailed?: string
289
+ submit?: string
290
+ running?: string
291
+ search?: string
292
+ pinned?: string
262
293
  select: string
263
294
  close: string
264
295
  /** Hint for leaving a mode, shown only while inside one. */
@@ -292,7 +323,14 @@ export type PaletteLabels = {
292
323
  const props = withDefaults(
293
324
  defineProps<{
294
325
  open?: boolean
295
- commands: Command[]
326
+ commands?: Command[]
327
+ /** Include live commands registered by enclosing LpCommandScope components. */
328
+ context?: boolean
329
+ /** Built-in sigils; people, channels and roles appear when a provider is supplied. */
330
+ modes?: Partial<Record<PaletteMode, Command[] | CommandFetcher>>
331
+ pinned?: RecentItem[]
332
+ pinnedKey?: string
333
+ pinnedLabel?: string
296
334
  placeholder?: string
297
335
  emptyText?: string
298
336
  /** Bind a global ⌘K / Ctrl-K toggle. */
@@ -392,6 +430,8 @@ const props = withDefaults(
392
430
  {
393
431
  placeholder: "Type a command or search…",
394
432
  emptyText: "No results",
433
+ commands: () => [],
434
+ context: true,
395
435
  hotkey: true,
396
436
  debounce: 250,
397
437
  minChars: 1,
@@ -405,12 +445,49 @@ const props = withDefaults(
405
445
  },
406
446
  )
407
447
 
448
+ const paletteScope = usePaletteScope()
449
+ suppressPaletteDiscovery()
450
+ const baseCommands = computed(() => mergePaletteCommands(props.context ? paletteScope?.registry.commands.value ?? [] : [], props.commands))
451
+ const rootCommands = computed<Command[]>(() => mergePaletteCommands([
452
+ ...baseCommands.value,
453
+ ...Object.entries(PALETTE_MODES).flatMap(([key, definition]) => {
454
+ const name = key as PaletteMode
455
+ const children = props.modes?.[name] ?? (name === "commands" ? baseCommands.value.filter(c => !c.href && !c.children && !c.editInPreview)
456
+ : name === "help" ? [{ id: "palette/help/keys", label: "Keyboard navigation", editInPreview: true, previewComponent: { render: () => h("p", { class: "whitespace-pre-line text-sm leading-relaxed" }, "↑ ↓ Navigate results\nEnter Run or edit\nTab Result actions\nAlt+P Pin a result\nEscape Back or close") } }] : undefined)
457
+ return children ? [{ id: `palette/mode/${name}`, ...definition, label: text.value[name], children, group: text.value.search, remember: false }] : []
458
+ }),
459
+ ], []))
460
+ const toast = useToast()
461
+ const editing = shallowRef<Command>()
462
+ let stopEditorFocus: (() => void) | undefined
463
+ const promptCommand = shallowRef<Command>()
464
+ const promptValue = ref("")
465
+ const promptError = ref("")
466
+ const promptValidation = useAsyncTask<string | undefined>()
467
+ const validating = promptValidation.pending
468
+ watch(promptCommand, () => promptValidation.cancel(), { flush: "sync" })
469
+ const executing = ref(false)
470
+ const actionError = ref("")
471
+ let executionEpoch = 0
472
+ let afterClose: (() => void) | undefined
473
+ const inputQuery = computed({ get: () => promptCommand.value ? promptValue.value : query.value, set: value => { if (promptCommand.value) { promptValidation.cancel(); promptValue.value = value; promptError.value = "" } else query.value = value } })
474
+
408
475
  const frosted = useGlass(() => props.glass)
409
476
  const contentRef = ref<{ $el: HTMLElement } | null>(null)
410
477
  const { claim, release, layer } = useModalLayer({ element: () => contentRef.value?.$el })
411
478
 
412
479
  const DEFAULT_LABELS: Required<PaletteLabels> = {
480
+ ...Object.fromEntries(Object.entries(PALETTE_MODES).map(([key, mode]) => [key, mode.label])) as Record<PaletteMode, string>,
481
+ search: "Search",
482
+ pinned: "Pinned",
413
483
  navigate: "navigate",
484
+ pin: "Pin",
485
+ unpin: "Unpin",
486
+ undo: "Undo",
487
+ backToResults: "Back to results",
488
+ actionFailed: "The action could not be completed.",
489
+ submit: "Submit",
490
+ running: "Running…",
414
491
  select: "select",
415
492
  close: "close",
416
493
  back: "back",
@@ -429,11 +506,13 @@ const DEFAULT_LABELS: Required<PaletteLabels> = {
429
506
  tab: "tab",
430
507
  }
431
508
 
432
- const text = useLabels("palette", DEFAULT_LABELS, () => props.labels)
509
+ const text = useLabels<Required<PaletteLabels>>("palette", DEFAULT_LABELS, () => props.labels)
510
+ const pinnedHeading = computed(() => props.pinnedLabel ?? text.value.pinned)
433
511
 
434
512
  const emit = defineEmits<{
435
513
  (e: "update:open", value: boolean): void
436
514
  (e: "select", command: Command): void
515
+ (e: "update:pinned", items: RecentItem[]): void
437
516
  /**
438
517
  * A command ran — id first, because the common use is appending it to a
439
518
  * stored recency list. Fires for leaf commands only: opening a mode is
@@ -519,6 +598,7 @@ const selectedActions = computed(() => actionsOf(flat.value[selectedIndex.value]
519
598
  * actions it keeps that default.
520
599
  */
521
600
  function onTab(event: KeyboardEvent) {
601
+ if (promptCommand.value) return
522
602
  // Without actions Tab stays Tab, so the trail chips and a Retry button are
523
603
  // still reachable from the keyboard.
524
604
  if (event.shiftKey) return
@@ -527,10 +607,16 @@ function onTab(event: KeyboardEvent) {
527
607
  if (!c || !actions.length) return
528
608
  event.preventDefault()
529
609
  event.stopPropagation()
610
+ openActions(c)
611
+ }
612
+ function openActions(c: Command) {
613
+ const actions = actionsOf(c)
614
+ if (!actions.length) return
530
615
  const actionsMode: Command = { id: `${c.id}:actions`, label: c.label, icon: c.icon, children: actions }
531
616
  syntheticModes.add(actionsMode)
532
617
  hints.use(HINT_SIGNALS.paletteActions)
533
618
  enter(actionsMode)
619
+ inputRef.value?.focus()
534
620
  }
535
621
 
536
622
  // A returning user who never pressed Tab gets the footer key lit up, nothing
@@ -551,7 +637,7 @@ const showActionsKey = computed(() => {
551
637
  * `keepOpen` pick inside it shows its own effect.
552
638
  */
553
639
  watch(
554
- () => props.commands,
640
+ () => rootCommands.value,
555
641
  (commands) => {
556
642
  if (!trail.value.length) return
557
643
  let level: Command[] = commands
@@ -671,7 +757,7 @@ function refresh() {
671
757
  * next to "which service?" is how a palette gets someone to restart the wrong
672
758
  * thing.
673
759
  */
674
- const source = computed<Command[]>(() => (mode.value ? modeItems.value : props.commands))
760
+ const source = computed<Command[]>(() => (mode.value ? modeItems.value : rootCommands.value))
675
761
 
676
762
  /*
677
763
  * Keyboard layouts typed through each other use a QWERTY ↔ Cyrillic map.
@@ -692,8 +778,7 @@ const local = computed<{ items: Command[]; terms: string[] }>(() => {
692
778
  // Empty query: show everything EXCEPT hidden commands (they only surface once
693
779
  // typed). With a query, hidden commands rank like any other.
694
780
  if (!terms.include.length && !terms.exclude.length && !terms.groups.length) return { items: filtered, terms: [] }
695
- const items = rankCommandItems(filtered, terms)
696
- return { items, terms: terms.include }
781
+ return searchCommandItems(filtered, terms)
697
782
  })
698
783
 
699
784
  const ranked = computed<Command[]>(() => local.value.items)
@@ -774,7 +859,7 @@ const recentItems = computed<Command[]>(() => {
774
859
  if (mode.value || !recentList.value.length) return []
775
860
  const terms = parseCommandQuery(query.value)
776
861
  if (terms.include.length) return []
777
- const found = resolveRecentItems(recentList.value, props.commands, {
862
+ const found = resolveRecentItems(recentList.value, rootCommands.value, {
778
863
  limit: Number.POSITIVE_INFINITY,
779
864
  resolve: props.resolveRecent,
780
865
  fromEntry,
@@ -789,6 +874,26 @@ const recentItems = computed<Command[]>(() => {
789
874
  return out
790
875
  })
791
876
 
877
+ const storedPinned = usePersisted<unknown>(() => props.pinnedKey ?? (props.recentKey ? `${props.recentKey}:pinned` : undefined), [])
878
+ const pinList = computed(() => props.pinned ?? readRecent(storedPinned.value))
879
+ const pinResolved = computed(() => resolveRecentItems(pinList.value, rootCommands.value, { limit: Infinity, resolve: props.resolveRecent, fromEntry }))
880
+ const pinnedItems = computed(() => {
881
+ if (mode.value || parseCommandQuery(query.value).include.length) return []
882
+ return pinResolved.value.filter(({ command }) => recentVisible(command, parseCommandQuery(query.value), pinnedHeading.value)).map(({ command, path }) => { recentPath.set(command, path); return command })
883
+ })
884
+ function isPinned(command: Command) {
885
+ const path = recentPath.get(command) ?? trail.value.map(step => step.id)
886
+ return pinList.value.some(item => typeof item === "string" ? !path.length && item === command.id : item.id === command.id && item.path.join("\0") === path.join("\0"))
887
+ }
888
+ function togglePin(command: Command | undefined) {
889
+ if (!command || moreOf.has(command) || command.children || trail.value.some(step => syntheticModes.has(step))) return
890
+ const path = recentPath.get(command) ?? trail.value.map(step => step.id)
891
+ const next = isPinned(command) ? pinList.value.filter(item => typeof item === "string" ? item !== command.id : item.id !== command.id || item.path.join("\0") !== path.join("\0"))
892
+ : pushRecent(pinList.value, toRecentItem(command, path), Infinity)
893
+ if (props.pinned === undefined) storedPinned.value = next
894
+ emit("update:pinned", next)
895
+ }
896
+
792
897
  // Group preserving first-seen order; ungrouped commands sit under "".
793
898
  // Recent first (it answers "the thing I always run"), then the matched list,
794
899
  // then whatever the provider found.
@@ -802,11 +907,13 @@ const grouped = computed<{ name: string; items: Command[] }[]>(() => {
802
907
  }
803
908
  map.get(group)!.push(c)
804
909
  }
805
- const recent = recentItems.value
910
+ const pinned = pinnedItems.value
911
+ for (const c of pinned) push(c, pinnedHeading.value)
912
+ const recent = recentItems.value.filter(c => !pinned.some(p => p.id === c.id))
806
913
  for (const c of recent) push(c, props.recentLabel)
807
914
  // A recent entry is shown once: leaving the copy in its own group would put
808
915
  // the same row on screen twice with nothing to tell them apart.
809
- for (const c of ranked.value) if (!recent.includes(c)) push(c, c.group ?? "")
916
+ for (const c of ranked.value) if (!recent.includes(c) && !pinned.some(p => p.id === c.id)) push(c, c.group ?? "")
810
917
  for (const c of fetched.value) push(c, c.group ?? "")
811
918
  // Last, whatever their group: they are the way out when nothing above is it.
812
919
  const tail = new Map<string, Command[]>()
@@ -869,6 +976,7 @@ const active = computed<Command | undefined>(() => {
869
976
  const c = flat.value[selectedIndex.value]
870
977
  return c && !moreOf.has(c) ? c : undefined
871
978
  })
979
+ const canPin = computed(() => active.value && !active.value.children && !moreOf.has(active.value) && !trail.value.some(step => syntheticModes.has(step)))
872
980
  const previewRef = ref<{ viewportEl: HTMLElement | null } | null>(null)
873
981
 
874
982
  /*
@@ -877,7 +985,7 @@ const previewRef = ref<{ viewportEl: HTMLElement | null } | null>(null)
877
985
  * decides what to show, so any highlighted row opens it.
878
986
  */
879
987
  const previewable = computed(() =>
880
- props.previewPane && commandHasPreview(active.value, { slot: !!slots.preview, hasPreview: props.hasPreview }),
988
+ (props.previewPane || !!editing.value) && commandHasPreview(active.value, { slot: !!slots.preview, hasPreview: props.hasPreview }),
881
989
  )
882
990
 
883
991
  /*
@@ -891,7 +999,7 @@ const PANE_LINGER_MS = 160
891
999
  const paneCommand = shallowRef<Command | undefined>()
892
1000
  let lingerTimer: ReturnType<typeof setTimeout> | undefined
893
1001
  watch(
894
- [active, previewable],
1002
+ [() => editing.value ?? active.value, previewable],
895
1003
  ([command, show]) => {
896
1004
  clearTimeout(lingerTimer)
897
1005
  if (show) {
@@ -905,7 +1013,7 @@ watch(
905
1013
  )
906
1014
  onBeforeUnmount(() => clearTimeout(lingerTimer))
907
1015
 
908
- const paneOpen = computed(() => props.previewPane && !!paneCommand.value)
1016
+ const paneOpen = computed(() => !promptCommand.value && (!!editing.value || (props.previewPane && !!paneCommand.value)))
909
1017
 
910
1018
  function paneHighlight(text: string): HighlightPart[] {
911
1019
  return highlight(text, paneBody.value?.highlight)
@@ -1061,7 +1169,12 @@ function movePage(direction: -1 | 1) {
1061
1169
  select(selectedIndex.value + direction * 8, true)
1062
1170
  }
1063
1171
  function onPaletteKeydown(event: KeyboardEvent) {
1064
- if (event.isComposing) return
1172
+ if (event.isComposing || event.target !== inputRef.value || promptCommand.value) return
1173
+ if (event.altKey && event.key.toLowerCase() === "p") {
1174
+ event.preventDefault()
1175
+ togglePin(active.value)
1176
+ return
1177
+ }
1065
1178
  if (!(["ArrowDown", "ArrowUp", "Home", "End", "PageDown", "PageUp"] as string[]).includes(event.key)) return
1066
1179
  // Home and End belong to the caret while there is text to move it through.
1067
1180
  const typing = event.target instanceof HTMLInputElement && event.target.value !== ""
@@ -1077,10 +1190,38 @@ function onPaletteKeydown(event: KeyboardEvent) {
1077
1190
  function onPointerMove() {
1078
1191
  keyboardSelection.value = false
1079
1192
  }
1193
+ function onCloseAutoFocus(event: Event) {
1194
+ if (!afterClose) return
1195
+ event.preventDefault()
1196
+ const focus = afterClose
1197
+ afterClose = undefined
1198
+ requestAnimationFrame(focus)
1199
+ }
1200
+ async function focusEditor() {
1201
+ stopEditorFocus?.()
1202
+ await nextTick()
1203
+ if (!editing.value) return
1204
+ const root = previewRef.value?.viewportEl
1205
+ if (!root) return
1206
+ const focus = () => {
1207
+ const body = root.querySelector("[data-lp-command-preview]")
1208
+ const field = body?.querySelector<HTMLElement>('input:not([type="hidden"]):not([disabled]):not([tabindex="-1"]):not([aria-hidden="true"]),textarea:not([disabled]):not([tabindex="-1"]),select:not([disabled]),[contenteditable="true"]')
1209
+ ?? body?.querySelector<HTMLElement>('button:not([disabled]):not([tabindex="-1"]),[tabindex="0"]')
1210
+ field?.focus()
1211
+ return !!field
1212
+ }
1213
+ if (focus()) return
1214
+ const observer = new MutationObserver(() => { if (focus()) { observer.disconnect(); stopEditorFocus = undefined } })
1215
+ observer.observe(root, { childList: true, subtree: true })
1216
+ stopEditorFocus = () => { observer.disconnect(); stopEditorFocus = undefined }
1217
+ }
1218
+ watch([editing, () => props.open], () => { if (!editing.value || !props.open) stopEditorFocus?.() })
1219
+ onBeforeUnmount(() => stopEditorFocus?.())
1080
1220
  function onPointerEnter(c: Command) {
1081
1221
  if (!keyboardSelection.value) select(flatIndexOf(c))
1082
1222
  }
1083
1223
  function runSelected(event?: KeyboardEvent) {
1224
+ if (promptCommand.value) { void submitPrompt(); return }
1084
1225
  const c = flat.value[selectedIndex.value]
1085
1226
  if (c?.href && !c.disabled && (event?.ctrlKey || event?.metaKey)) {
1086
1227
  openInNewTab(c)
@@ -1108,8 +1249,8 @@ function onRowClick(event: MouseEvent, c: Command) {
1108
1249
  run(c)
1109
1250
  }
1110
1251
 
1111
- function run(row: Command | undefined) {
1112
- if (!row || row.disabled) return
1252
+ async function run(row: Command | undefined) {
1253
+ if (!row || row.disabled || executing.value) return
1113
1254
  const c = fallbackOf.get(row) ?? row
1114
1255
  // The highlight stays on the same index, which is now the first row that
1115
1256
  // was hidden: the arrows carry on from where the fold was.
@@ -1120,8 +1261,20 @@ function run(row: Command | undefined) {
1120
1261
  inputRef.value?.focus()
1121
1262
  return
1122
1263
  }
1123
- emit("select", c)
1124
- c.onSelect?.()
1264
+ if (c.editInPreview && c.previewComponent) {
1265
+ editing.value = c
1266
+ paneCommand.value = c
1267
+ await focusEditor()
1268
+ return
1269
+ }
1270
+ if (c.prompt) {
1271
+ promptCommand.value = c
1272
+ promptValue.value = c.prompt.initial ?? ""
1273
+ promptError.value = ""
1274
+ inputRef.value?.focus()
1275
+ return
1276
+ }
1277
+ if (!await execute(c, () => c.onSelect?.())) return
1125
1278
  if (!c.onSelect && c.href && !c.children) window.location.assign(c.href)
1126
1279
  // A command with children drills in instead of closing, and is navigation
1127
1280
  // rather than a pick — so it stays out of the recency history.
@@ -1145,16 +1298,53 @@ function run(row: Command | undefined) {
1145
1298
  inputRef.value?.focus()
1146
1299
  return
1147
1300
  }
1301
+ afterClose = c.afterClose
1148
1302
  emit("update:open", false)
1149
1303
  }
1150
1304
 
1305
+ async function execute(command: Command, action: () => unknown | Promise<unknown>): Promise<boolean> {
1306
+ if (executing.value) return false
1307
+ executing.value = true
1308
+ actionError.value = ""
1309
+ const epoch = executionEpoch
1310
+ try {
1311
+ emit("select", command)
1312
+ const result = await action()
1313
+ if (command.undo) toast.success(command.label, { duration: 8000, actions: [{ label: text.value.undo, onClick: async () => { await command.undo!(result) } }] })
1314
+ return epoch === executionEpoch
1315
+ } catch (error) {
1316
+ if (epoch === executionEpoch) actionError.value = error instanceof Error ? error.message : text.value.actionFailed
1317
+ return false
1318
+ } finally { executing.value = false }
1319
+ }
1320
+ function submitPrompt() {
1321
+ const command = promptCommand.value
1322
+ if (!command?.prompt || executing.value || validating.value) return
1323
+ const value = promptValue.value
1324
+ promptValidation.run(() => command.prompt!.validate?.(value), {
1325
+ apply: error => { void completePrompt(command, value, error) },
1326
+ fail: error => { promptError.value = error instanceof Error ? error.message : text.value.actionFailed },
1327
+ })
1328
+ }
1329
+ async function completePrompt(command: Command, value: string, error: string | undefined) {
1330
+ if (promptCommand.value !== command || promptValue.value !== value) return
1331
+ promptError.value = error ?? ""
1332
+ if (promptError.value || !await execute(command, () => command.prompt!.submit(value))) return
1333
+ remember(command)
1334
+ emit("run", command.id, command)
1335
+ if (promptCommand.value !== command) return
1336
+ promptCommand.value = undefined
1337
+ afterClose = command.afterClose
1338
+ if (!command.keepOpen) emit("update:open", false)
1339
+ }
1340
+
1151
1341
  /*
1152
1342
  * Backspace on an empty query leaves the mode. Mirrors how the Slack and
1153
1343
  * Linear palettes behave, and means the way out is the key already under the
1154
1344
  * finger — Escape stays the way out of the palette itself.
1155
1345
  */
1156
1346
  function onBackspace(event: KeyboardEvent) {
1157
- if (query.value) return
1347
+ if (promptCommand.value || query.value) return
1158
1348
  if (back()) event.preventDefault()
1159
1349
  }
1160
1350
 
@@ -1165,6 +1355,14 @@ function onBackspace(event: KeyboardEvent) {
1165
1355
  * (stopPropagation) only while there is a mode to pop.
1166
1356
  */
1167
1357
  function onEscape(event: KeyboardEvent) {
1358
+ if (promptCommand.value || editing.value) {
1359
+ event.preventDefault()
1360
+ event.stopPropagation()
1361
+ promptCommand.value = undefined
1362
+ editing.value = undefined
1363
+ inputRef.value?.focus()
1364
+ return
1365
+ }
1168
1366
  if (!trail.value.length) return
1169
1367
  event.preventDefault()
1170
1368
  event.stopPropagation()
@@ -1187,6 +1385,11 @@ watch(
1187
1385
  claim()
1188
1386
  hints.use(HINT_SIGNALS.palette)
1189
1387
  } else release()
1388
+ executionEpoch++
1389
+ editing.value = undefined
1390
+ promptCommand.value = undefined
1391
+ promptError.value = ""
1392
+ actionError.value = ""
1190
1393
  cancel()
1191
1394
  query.value = ""
1192
1395
  select(0)
@@ -1242,12 +1445,14 @@ const guardOutside = useOutsideGuard()
1242
1445
  />
1243
1446
  <DialogContent
1244
1447
  @interact-outside="guardOutside"
1245
- @escape-key-down="guardOutside"
1448
+ @escape-key-down="event => { guardOutside(event); if (!event.defaultPrevented) onEscape(event) }"
1449
+ @close-auto-focus="onCloseAutoFocus"
1246
1450
  ref="contentRef"
1247
1451
  class="lp-cmd-dialog fixed left-1/2 top-[12vh] flex max-h-[76vh] -translate-x-1/2 flex-col overflow-hidden rounded-card border border-line shadow-panel outline-none data-[state=open]:animate-[pop-in_160ms_var(--ease-emphasized)] data-[state=closed]:animate-[pop-out_130ms_ease]"
1248
1452
  :class="[frosted ? (layer.nested ? 'lp-glass-overlay lp-glass-overlay--nested' : 'lp-glass-overlay') : 'bg-surface-raised', previewPane && 'lp-cmd-split']"
1249
1453
  :style="{ zIndex: layer.panel }"
1250
1454
  :data-pane="paneOpen ? 'open' : undefined"
1455
+ :data-editing="editing ? '' : undefined"
1251
1456
  @open-auto-focus.prevent
1252
1457
  @keydown.capture="onPaletteKeydown"
1253
1458
  >
@@ -1259,7 +1464,7 @@ const guardOutside = useOutsideGuard()
1259
1464
  to its left. -->
1260
1465
  <div class="flex shrink-0 items-center gap-2.5 border-b border-line px-4">
1261
1466
  <LpIcon
1262
- v-if="!searching"
1467
+ v-if="!searching && !executing && !validating"
1263
1468
  name="lucide:search"
1264
1469
  :size="18"
1265
1470
  class="shrink-0 text-muted"
@@ -1270,7 +1475,7 @@ const guardOutside = useOutsideGuard()
1270
1475
  v-else
1271
1476
  size="inline"
1272
1477
  :delay="0"
1273
- :labels="{ loading: text.searching }"
1478
+ :labels="{ loading: executing || validating ? text.running : text.searching }"
1274
1479
  class="shrink-0 text-[18px] text-muted"
1275
1480
  />
1276
1481
 
@@ -1289,14 +1494,16 @@ const guardOutside = useOutsideGuard()
1289
1494
 
1290
1495
  <input
1291
1496
  ref="inputRef"
1292
- v-model="query"
1497
+ v-model="inputQuery"
1498
+ :readonly="executing && !!promptCommand"
1499
+ @focus="editing = undefined"
1293
1500
  type="text"
1294
1501
  role="combobox"
1295
1502
  aria-autocomplete="list"
1296
1503
  aria-expanded="true"
1297
1504
  :aria-controls="listId"
1298
1505
  :aria-activedescendant="activeId"
1299
- :placeholder="mode?.childPlaceholder ?? placeholder"
1506
+ :placeholder="promptCommand?.prompt?.placeholder ?? mode?.childPlaceholder ?? placeholder"
1300
1507
  class="h-12 min-w-0 flex-1 bg-transparent text-sm text-ink outline-none placeholder:text-muted"
1301
1508
  @keydown.enter.prevent="runSelected"
1302
1509
  @keydown.backspace="onBackspace"
@@ -1306,9 +1513,19 @@ const guardOutside = useOutsideGuard()
1306
1513
  <kbd class="shrink-0 rounded-small border border-line px-1.5 py-0.5 text-[10px] text-muted">{{ text.esc }}</kbd>
1307
1514
  </div>
1308
1515
 
1516
+ <div v-if="promptCommand" class="flex flex-col gap-3 p-4">
1517
+ <p class="text-sm font-semibold">{{ promptCommand.label }}</p>
1518
+ <p v-if="promptError" role="alert" class="text-sm text-danger">{{ promptError }}</p>
1519
+ <div class="flex gap-2"><LpButton :loading="executing || validating" @click="submitPrompt">{{ text.submit }}</LpButton><LpButton variant="ghost" @click="promptCommand = undefined; inputRef?.focus()">{{ text.backToResults }}</LpButton></div>
1520
+ </div>
1521
+ <p v-if="actionError" role="alert" class="px-4 py-2 text-sm text-danger">{{ actionError }}</p>
1522
+ <div v-if="active && (canPin || selectedActions.length) && !promptCommand" class="flex items-center justify-end gap-2 border-b border-line px-3 py-1">
1523
+ <LpButton v-if="selectedActions.length" size="xs" variant="ghost" @click="openActions(active)">{{ text.actions }} <span class="text-muted">Tab</span></LpButton>
1524
+ <LpButton v-if="canPin" size="xs" variant="ghost" :aria-label="isPinned(active) ? text.unpin : text.pin" :aria-pressed="isPinned(active)" @click="togglePin(active)"><LpIcon name="lucide:star" :size="14" />{{ isPinned(active) ? text.unpin : text.pin }} <span class="text-muted">Alt+P</span></LpButton>
1525
+ </div>
1309
1526
  <!-- Results -->
1310
1527
  <div class="flex min-h-0 flex-1" @pointermove="onPointerMove">
1311
- <LpScrollArea ref="scrollRef" class="lp-cmd-list min-h-0 min-w-0 flex-1" content-class="p-2">
1528
+ <LpScrollArea v-show="!editing && !promptCommand" ref="scrollRef" class="lp-cmd-list min-h-0 min-w-0 flex-1" content-class="p-2">
1312
1529
  <!-- While a request is out there is nothing to say yet: "no results"
1313
1530
  under a spinner reads as an answer, and it is not one. -->
1314
1531
  <p v-if="flat.length === 0 && !failed" class="px-2 py-10 text-center text-sm text-muted">
@@ -1441,13 +1658,18 @@ const guardOutside = useOutsideGuard()
1441
1658
  <Transition name="lp-cmd-pane">
1442
1659
  <aside
1443
1660
  v-if="paneOpen"
1444
- class="lp-cmd-pane hidden min-h-0 shrink-0 flex-col border-l border-line md:flex"
1661
+ class="lp-cmd-pane min-h-0 flex-col"
1662
+ :class="editing ? 'flex min-w-0 flex-1' : 'hidden shrink-0 border-l border-line md:flex'"
1445
1663
  >
1446
1664
  <LpScrollArea ref="previewRef" class="min-h-0 flex-1" content-class="px-5 py-4">
1447
1665
  <!-- Keyed by the row, so moving the highlight replays the
1448
1666
  entrance rather than swapping text in place. -->
1449
1667
  <div v-if="paneBody" :key="paneBody.id">
1668
+ <LpButton v-if="editing" size="sm" variant="ghost" class="mb-3" @click="editing = undefined; inputRef?.focus()">{{ text.backToResults }}</LpButton>
1669
+ <PalettePreviewBoundary>
1450
1670
  <slot name="preview" :command="paneBody" :highlight="paneHighlight" :query="paneQuery">
1671
+ <component v-if="paneBody.previewComponent" :is="paneBody.previewComponent" />
1672
+ <template v-else>
1451
1673
  <div class="lp-cmd-para flex items-center gap-2 text-ink">
1452
1674
  <LpAvatar
1453
1675
  v-if="imageOf(paneBody)"
@@ -1477,7 +1699,12 @@ const guardOutside = useOutsideGuard()
1477
1699
  <template v-else>{{ p.text }}</template>
1478
1700
  </template>
1479
1701
  </p>
1702
+ </template>
1480
1703
  </slot>
1704
+ </PalettePreviewBoundary>
1705
+ <div v-if="actionsOf(paneBody).length" class="mt-4 flex flex-wrap gap-2 border-t border-line pt-3">
1706
+ <LpButton v-for="action in actionsOf(paneBody)" :key="action.id" size="sm" variant="outline" :disabled="action.disabled || executing" @click="run({ ...action, remember: false })">{{ action.label }}</LpButton>
1707
+ </div>
1481
1708
  </div>
1482
1709
  </LpScrollArea>
1483
1710
  </aside>
@@ -1556,6 +1783,13 @@ const guardOutside = useOutsideGuard()
1556
1783
  .lp-cmd-pane {
1557
1784
  width: var(--lp-cmd-pane);
1558
1785
  }
1786
+ .lp-cmd-dialog[data-editing] .lp-cmd-pane {
1787
+ width: 100%;
1788
+ }
1789
+ .lp-cmd-dialog[data-editing] {
1790
+ width: var(--lp-cmd-list);
1791
+ min-height: 0;
1792
+ }
1559
1793
  /*
1560
1794
  * The pane rides in from the right edge while the dialog opens up to it, and
1561
1795
  * leaves the same way, for as long as the dialog takes to close over it.
@@ -0,0 +1,35 @@
1
+ <script setup lang="ts">
2
+ import { bindPaletteScopeElement, createPaletteRegistry, providePaletteScope, usePaletteCommands, usePaletteScope, type PaletteScope } from "../composables/usePaletteCommands"
3
+ import type { ComponentPublicInstance } from "vue"
4
+ import type { Command } from "./LpCommandPalette.vue"
5
+ import { usePaletteTheme } from "../theme/palette"
6
+ import type { ThemeStudio } from "../theme/studio"
7
+
8
+ const props = withDefaults(defineProps<{
9
+ id: string
10
+ label?: string
11
+ enabled?: boolean
12
+ discover?: boolean
13
+ /** Start a separate registry, e.g. an embedded demo inside an application's palette scope. */
14
+ isolated?: boolean
15
+ theme?: ThemeStudio
16
+ commands?: Command[]
17
+ }>(), { enabled: true, discover: true })
18
+ const parent = usePaletteScope()
19
+ const scope: PaletteScope = {
20
+ registry: !props.isolated && parent ? parent.registry : createPaletteRegistry(),
21
+ get id() { return parent && !props.isolated ? `${parent.id}/${props.id}` : props.id },
22
+ get label() { return props.label ?? props.id },
23
+ depth: parent && !props.isolated ? parent.depth + 1 : 0,
24
+ get enabled() { return props.enabled && (props.isolated || !parent || parent.enabled) },
25
+ get discover() { return props.discover },
26
+ }
27
+ providePaletteScope(scope)
28
+ usePaletteTheme(() => props.theme, { scope })
29
+ usePaletteCommands(() => props.commands ?? [], { scope })
30
+ function bind(element: Element | ComponentPublicInstance | null) {
31
+ if (element && "nodeType" in element) bindPaletteScopeElement(element as Element, scope)
32
+ }
33
+ </script>
34
+
35
+ <template><div :ref="bind" data-lp-command-scope class="contents"><slot /></div></template>