@leavepulse/ui 0.70.1 → 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 (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 +220 -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 +220 -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
@@ -6,6 +6,7 @@ export default { inheritAttrs: false }
6
6
  </script>
7
7
 
8
8
  <script setup lang="ts">
9
+ import { usePaletteAction, type PaletteControlOptions } from "../composables/usePaletteCommands"
9
10
  /*
10
11
  * Collapsible "show more" section built on reka Collapsible. The trigger is a
11
12
  * full-width row with a chevron that rotates open; the body folds open and shut
@@ -31,6 +32,7 @@ import { useMergedAttrs } from "../composables/useMergedClass"
31
32
 
32
33
  const props = withDefaults(
33
34
  defineProps<{
35
+ palette?: PaletteControlOptions
34
36
  /** Controlled open state. Omit to let the component own it (with `defaultOpen`). */
35
37
  open?: boolean
36
38
  defaultOpen?: boolean
@@ -45,7 +47,7 @@ const props = withDefaults(
45
47
  */
46
48
  scrollIntoView?: boolean
47
49
  }>(),
48
- { defaultOpen: false, scrollIntoView: false },
50
+ { palette: undefined, defaultOpen: false, scrollIntoView: false },
49
51
  )
50
52
 
51
53
  const emit = defineEmits<{ (e: "update:open", value: boolean): void }>()
@@ -113,6 +115,8 @@ function onToggle(value: boolean) {
113
115
  const { class: rootClass, attrs: rest } = useMergedAttrs(
114
116
  "shrink-0 overflow-hidden rounded-control border border-line bg-surface-soft",
115
117
  )
118
+
119
+ usePaletteAction({ label: () => props.label, element: () => (root.value?.$el as HTMLElement | undefined)?.querySelector<HTMLElement>("button"), disabled: () => false })
116
120
  </script>
117
121
 
118
122
  <template>
@@ -1,4 +1,7 @@
1
1
  <script setup lang="ts">
2
+ import { useId } from "vue"
3
+ import { menuPaletteCommands } from "./commandPalette"
4
+ import { usePaletteCommands, usePaletteScope, type PaletteControlOptions } from "../composables/usePaletteCommands"
2
5
  /*
3
6
  * A menu opened from a button (usually a row's "…"). Its items are the same
4
7
  * shape as LpContextMenu's and are drawn by the same renderer, so one array
@@ -17,7 +20,8 @@ import { useOutsideGuard, usePortalTarget } from "../composables/useView"
17
20
  /** A dropdown item: the context-menu item shape, kept under its old name. */
18
21
  export type MenuItem = ContextMenuItemDef
19
22
 
20
- const props = defineProps<{
23
+ const props = withDefaults(defineProps<{
24
+ palette?: PaletteControlOptions
21
25
  items: MenuItem[]
22
26
  /** A heading naming what the menu acts on; see LpContextMenu's `title`. */
23
27
  title?: string
@@ -29,7 +33,7 @@ const props = defineProps<{
29
33
  * menu). Opening it then counts toward the hint system's right-click tip.
30
34
  */
31
35
  rightClickToo?: boolean
32
- }>()
36
+ }>(), { palette: undefined })
33
37
 
34
38
  const PANEL = `${POPOVER_PANEL} z-(--z-popover) min-w-44 p-1 outline-none`
35
39
 
@@ -42,6 +46,13 @@ const portalTo = usePortalTarget()
42
46
  // What happens in another document (the page around a frame, or a frame in
43
47
  // the page) is not "outside" for this layer.
44
48
  const guardOutside = useOutsideGuard()
49
+
50
+ const paletteScope = usePaletteScope()
51
+ const paletteId = useId()
52
+ usePaletteCommands(() => props.palette === false || !props.title || !props.items.length ? [] : [{
53
+ id: `${paletteScope?.id}/${props.palette?.id ?? paletteId}`, label: props.palette?.label ?? props.title,
54
+ children: menuPaletteCommands(props.items, `${paletteScope?.id}/${props.palette?.id ?? paletteId}`),
55
+ }], { automatic: true })
45
56
  </script>
46
57
 
47
58
  <template>
@@ -49,6 +49,9 @@ const noticeId = computed(() => `${id.value}-notice`)
49
49
  provideField(
50
50
  computed(() => ({
51
51
  id: id.value,
52
+ label: props.label,
53
+ hint: props.hint,
54
+ error: props.error,
52
55
  labelId: props.label ? `${id.value}-label` : undefined,
53
56
  describedBy:
54
57
  [hasMessage.value ? messageId.value : "", notice.message.value ? noticeId.value : ""]
@@ -3,6 +3,7 @@ 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 { tv, type VariantProps } from "tailwind-variants"
7
8
  import { computed, ref, useAttrs, useSlots } from "vue"
8
9
  import { useFieldControl } from "../composables/useField"
@@ -46,6 +47,8 @@ type InputVariants = VariantProps<typeof shell>
46
47
 
47
48
  const props = withDefaults(
48
49
  defineProps<{
50
+ /** Contextual palette metadata; false excludes this control. */
51
+ palette?: PaletteControlOptions
49
52
  modelValue?: string
50
53
  size?: InputVariants["size"]
51
54
  invalid?: boolean
@@ -87,7 +90,7 @@ const props = withDefaults(
87
90
  /** Extra or replacement mask tokens, one-character RegExps by key. */
88
91
  maskTokens?: MaskTokens
89
92
  }>(),
90
- { type: "text", size: "md" },
93
+ { palette: undefined, type: "text", size: "md" },
91
94
  )
92
95
 
93
96
  const emit = defineEmits<{
@@ -174,6 +177,8 @@ defineExpose({
174
177
  select: () => inputEl.value?.select(),
175
178
  input: inputEl,
176
179
  })
180
+
181
+ usePaletteControl({ label: () => props.label, disabled: () => field.value.disabled, invalid: () => field.value.invalid, available: () => !['password', 'hidden'].includes(props.type), editorProps: () => ({ label: undefined, hint: undefined, error: undefined }) })
177
182
  </script>
178
183
 
179
184
  <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
  // Numeric input with stepper buttons (reka NumberField). Themed via tokens.
10
11
  import {
11
12
  NumberFieldDecrement,
@@ -25,6 +26,8 @@ export interface NumberFieldLabels extends Record<string, string> {
25
26
  }
26
27
 
27
28
  const props = withDefaults(defineProps<{
29
+ /** Contextual palette metadata; false excludes this control. */
30
+ palette?: PaletteControlOptions
28
31
  // null = empty field (reka's native state); consumers that need a number
29
32
  // should default it themselves, the field doesn't invent a value.
30
33
  modelValue?: number | null
@@ -67,7 +70,7 @@ const props = withDefaults(defineProps<{
67
70
  /** Control height, matching LpInput. */
68
71
  size?: "sm" | "md" | "lg"
69
72
  labels?: Partial<NumberFieldLabels>
70
- }>(), { size: "md", stepSnapping: true, steppers: true, focusOnChange: true })
73
+ }>(), { palette: undefined, size: "md", stepSnapping: true, steppers: true, focusOnChange: true })
71
74
 
72
75
  // The value is committed when the field loses focus or Enter is pressed, and on
73
76
  // every step, not on each keystroke: "1." or "-" is not a number yet, and a
@@ -131,6 +134,8 @@ const rootClass = useMergedClass(() =>
131
134
  : "border-line focus-within:border-brand focus-within:ring-ring",
132
135
  ].join(" "),
133
136
  )
137
+
138
+ usePaletteControl({ disabled: () => field.value.disabled, invalid: () => field.value.invalid })
134
139
  </script>
135
140
 
136
141
  <template>
@@ -28,6 +28,8 @@
28
28
  */
29
29
  import { computed, onBeforeUnmount, ref, watch } from "vue"
30
30
  import { easeOut, prefersReducedMotion } from "../composables/easing"
31
+ import { useLocale } from "../composables/useLocale"
32
+ import { decimalMark } from "./format"
31
33
  import { useRevealedValue } from "../composables/useRevealedValue"
32
34
  import { compactNumber } from "./format"
33
35
 
@@ -222,6 +224,14 @@ const { el: revealAnchor, target } = useRevealedValue(() => props.value, {
222
224
  watch(target, travel)
223
225
  onBeforeUnmount(stop)
224
226
 
227
+ /*
228
+ * The app's locale (reka's ConfigProvider) when the counter has none of its
229
+ * own, the same rule the dates follow: a Russian app read "4,594" otherwise.
230
+ */
231
+ const locale = useLocale(() => props.locale)
232
+ // Only the grouped form is written in the locale; the others always use ".".
233
+ const fractionMark = computed(() => (props.grouped && !props.format && !props.compact ? decimalMark(locale.value) : "."))
234
+
225
235
  const formatted = computed(() => formatAt(round(displayed.value), !settled.value))
226
236
 
227
237
  // Rounded first by the caller: every branch should render the shape the
@@ -246,7 +256,7 @@ function formatAt(v: number, padded: boolean): string {
246
256
  }
247
257
  const pad = padded ? Math.max(1, padTo.value) : 1
248
258
  if (props.grouped)
249
- return v.toLocaleString(props.locale, {
259
+ return v.toLocaleString(locale.value, {
250
260
  minimumFractionDigits: props.decimals,
251
261
  maximumFractionDigits: props.decimals,
252
262
  minimumIntegerDigits: pad,
@@ -294,7 +304,7 @@ interface Cell {
294
304
  */
295
305
  const cells = computed<Cell[]>(() => {
296
306
  const chars = [...formatted.value]
297
- const dot = chars.indexOf(".")
307
+ const dot = chars.indexOf(fractionMark.value)
298
308
  const intEnd = dot === -1 ? chars.length : dot
299
309
 
300
310
  const isDigit = (c: string) => {
@@ -20,11 +20,14 @@
20
20
  import HighlightText from "./HighlightText.vue"
21
21
  import LpIcon from "./LpIcon.vue"
22
22
  import LpRollingText from "./LpRollingText.vue"
23
+ import LpSkeleton from "./LpSkeleton.vue"
23
24
  import type { PaletteFact } from "./commandPalette"
25
+ import { computed } from "vue"
24
26
 
25
- withDefaults(
27
+ const props = withDefaults(
26
28
  defineProps<{
27
29
  items: PaletteFact[]
30
+ loading?: boolean
28
31
  /** Small heading over the list. */
29
32
  title?: string
30
33
  /** Plain rows instead of soft tiles, for a list read as text. */
@@ -32,6 +35,7 @@ withDefaults(
32
35
  }>(),
33
36
  { plain: false },
34
37
  )
38
+ const shown = computed(() => props.loading && !props.items.length ? Array.from({ length: 3 }, () => ({ label: "", value: "—" } as PaletteFact)) : props.items)
35
39
 
36
40
  defineSlots<{
37
41
  value?: (props: { item: PaletteFact; index: number }) => unknown
@@ -39,11 +43,11 @@ defineSlots<{
39
43
  </script>
40
44
 
41
45
  <template>
42
- <section class="lp-palette-facts" :class="{ 'lp-palette-facts--plain': plain }">
46
+ <section class="lp-palette-facts" :aria-busy="loading || undefined" :class="{ 'lp-palette-facts--plain': plain }">
43
47
  <p v-if="title" class="lp-palette-facts__title">{{ title }}</p>
44
48
  <ul class="lp-palette-facts__list">
45
49
  <li
46
- v-for="(item, index) in items"
50
+ v-for="(item, index) in shown"
47
51
  :key="index"
48
52
  class="lp-palette-facts__row"
49
53
  :class="{ 'is-current': item.current }"
@@ -52,15 +56,16 @@ defineSlots<{
52
56
  <span v-if="item.color" class="lp-palette-facts__dot" :style="{ background: item.color }" />
53
57
  <LpIcon v-else-if="item.icon" :name="item.icon" :size="15" class="lp-palette-facts__icon" />
54
58
  <span class="lp-palette-facts__label">
55
- <span class="lp-palette-facts__name"><HighlightText :text="item.label" /></span>
59
+ <span class="lp-palette-facts__name"><LpSkeleton v-if="loading && !item.label" class="h-3 w-24" /><HighlightText v-else :text="item.label" /></span>
56
60
  <span v-if="item.hint?.length" class="lp-palette-facts__hint"><HighlightText :text="item.hint" /></span>
57
61
  </span>
58
62
  <span
59
- v-if="item.value !== undefined && item.value !== '' || $slots.value"
63
+ v-if="loading || item.value !== undefined && item.value !== '' || $slots.value"
60
64
  class="lp-palette-facts__value"
61
65
  :class="{ 'lp-palette-facts__value--mono': item.mono }"
62
66
  >
63
- <slot name="value" :item="item" :index="index">
67
+ <LpSkeleton v-if="loading" class="h-3 w-16 max-w-full" />
68
+ <slot v-else name="value" :item="item" :index="index">
64
69
  <LpRollingText v-if="item.value !== undefined" :value="item.value" align="end" />
65
70
  </slot>
66
71
  </span>
@@ -13,23 +13,29 @@
13
13
  */
14
14
  import LpNumberFlow from "./LpNumberFlow.vue"
15
15
  import LpRollingText from "./LpRollingText.vue"
16
+ import LpSkeleton from "./LpSkeleton.vue"
16
17
  import type { PaletteStat } from "./commandPalette"
18
+ import { computed } from "vue"
17
19
 
18
- withDefaults(
20
+ const props = withDefaults(
19
21
  defineProps<{
20
22
  items: PaletteStat[]
23
+ loading?: boolean
21
24
  /** Tiles per row. */
22
25
  columns?: 2 | 3 | 4
23
26
  }>(),
24
27
  { columns: 2 },
25
28
  )
29
+ const shown = computed(() => props.loading && !props.items.length ? Array.from({ length: props.columns }, () => ({ label: "", value: "—" } as PaletteStat)) : props.items)
26
30
  </script>
27
31
 
28
32
  <template>
29
- <dl class="lp-palette-stats" :style="{ gridTemplateColumns: `repeat(${columns}, minmax(0, 1fr))` }">
30
- <div v-for="(item, index) in items" :key="`${item.label}-${index}`" class="lp-palette-stats__tile">
31
- <dt>{{ item.label }}</dt>
32
- <dd>
33
+ <dl class="lp-palette-stats" :aria-busy="loading || undefined" :style="{ gridTemplateColumns: `repeat(${columns}, minmax(0, 1fr))` }">
34
+ <div v-for="(item, index) in shown" :key="`${item.label}-${index}`" class="lp-palette-stats__tile">
35
+ <dt><LpSkeleton v-if="loading && !item.label" class="my-1 h-2 w-20" /><template v-else>{{ item.label }}</template></dt>
36
+ <dd class="relative">
37
+ <LpSkeleton v-if="loading" class="absolute top-1/2 h-[1em] w-16 max-w-full -translate-y-1/2" />
38
+ <span :class="loading && 'invisible'" :aria-hidden="loading || undefined">
33
39
  <LpNumberFlow
34
40
  v-if="typeof item.value === 'number'"
35
41
  :value="item.value"
@@ -40,6 +46,7 @@ withDefaults(
40
46
  grouped
41
47
  />
42
48
  <LpRollingText v-else :value="item.value" align="start" />
49
+ </span>
43
50
  </dd>
44
51
  </div>
45
52
  </dl>
@@ -0,0 +1,48 @@
1
+ <script setup lang="ts">
2
+ import { computed, ref } from "vue"
3
+ import type { StudioState, PreviewableThemeStudio } from "../theme/studio"
4
+ import { useLabels } from "../composables/useLpConfig"
5
+ import { suppressPaletteDiscovery } from "../composables/usePaletteCommands"
6
+ import LpThemeStudio from "./LpThemeStudio.vue"
7
+ import LpThemeProvider from "./LpThemeProvider.vue"
8
+ import LpButton from "./LpButton.vue"
9
+ import LpInput from "./LpInput.vue"
10
+
11
+ const props = defineProps<{ studio: PreviewableThemeStudio }>()
12
+ suppressPaletteDiscovery()
13
+ const text = useLabels("paletteTheme", { title: "Theme", apply: "Apply theme", revert: "Revert draft", sample: "Preview", action: "Continue", placeholder: "A field in this theme" })
14
+ const snapshot = ref({ ...props.studio.state.value })
15
+ const state = ref<StudioState>({ ...snapshot.value })
16
+ const preview = computed(() => props.studio.preview(state.value))
17
+ const changed = computed(() => JSON.stringify(state.value) !== JSON.stringify(snapshot.value))
18
+ const draft: PreviewableThemeStudio = {
19
+ state, themes: props.studio.themes, system: props.studio.system,
20
+ theme: computed(() => preview.value.theme), exported: computed(() => preview.value.exported), changed,
21
+ brand: computed({ get: () => preview.value.theme.colors.brand, set: brand => { state.value = { ...state.value, brand } } }),
22
+ pickTheme: theme => { state.value = { ...state.value, theme } },
23
+ tune: patch => { state.value = { ...state.value, ...patch } },
24
+ reset: () => { state.value = { ...snapshot.value } },
25
+ preview: props.studio.preview,
26
+ }
27
+ function apply() {
28
+ const { theme, ...tuning } = state.value
29
+ props.studio.pickTheme(theme)
30
+ props.studio.tune(tuning)
31
+ snapshot.value = { ...state.value }
32
+ }
33
+ </script>
34
+
35
+ <template>
36
+ <div class="flex min-w-0 flex-col gap-4">
37
+ <div class="sticky top-0 z-10 flex gap-2 border-b border-line bg-surface-overlay pb-3">
38
+ <LpButton :disabled="!changed" @click="apply">{{ text.apply }}</LpButton>
39
+ <LpButton variant="ghost" :disabled="!changed" @click="draft.reset()">{{ text.revert }}</LpButton>
40
+ </div>
41
+ <LpThemeProvider :theme="preview.theme" class="flex flex-col gap-3 rounded-card border border-line p-4">
42
+ <span class="text-sm font-semibold">{{ text.sample }}</span>
43
+ <LpInput :placeholder="text.placeholder" readonly :tabindex="-1" />
44
+ <LpButton :tabindex="-1">{{ text.action }}</LpButton>
45
+ </LpThemeProvider>
46
+ <LpThemeStudio :studio="draft" :labels="{ reset: text.revert }" />
47
+ </div>
48
+ </template>
@@ -8,6 +8,7 @@ export interface PasswordInputLabels extends Record<string, string> {
8
8
  </script>
9
9
 
10
10
  <script setup lang="ts">
11
+ import { suppressPaletteDiscovery } from "../composables/usePaletteCommands"
11
12
  // Password field = LpInput + a show/hide toggle in its trailing slot.
12
13
  // No duplicated field styling — the shell lives in LpInput.
13
14
  // LpSecretField's input form is this same field: it drives `visible` (to hide
@@ -51,6 +52,8 @@ defineExpose({
51
52
  blur: () => inner.value?.blur(),
52
53
  select: () => inner.value?.select(),
53
54
  })
55
+
56
+ suppressPaletteDiscovery()
54
57
  </script>
55
58
 
56
59
  <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
  import { RadioGroupIndicator, RadioGroupItem, RadioGroupRoot } from "reka-ui"
10
11
  import { useId } from "vue"
11
12
  import { useFieldControl } from "../composables/useField"
@@ -21,7 +22,7 @@ export interface RadioOption extends LpOptionBase<string> {
21
22
  // a disabled option stays visible but cannot be picked. For anything richer
22
23
  // (badges, custom layout) pass <LpRadio> children in the default slot instead —
23
24
  // they share the same RadioGroupRoot, so selection still flows through v-model.
24
- const props = defineProps<{ modelValue?: string; options?: RadioOption[]; disabled?: boolean }>()
25
+ const props = withDefaults(defineProps<{ palette?: PaletteControlOptions; modelValue?: string; options?: RadioOption[]; disabled?: boolean }>(), { palette: undefined })
25
26
  defineEmits<{ (e: "update:modelValue", value: string): void }>()
26
27
 
27
28
  const field = useFieldControl(() => ({ wrapped: false, disabled: props.disabled }))
@@ -31,6 +32,8 @@ const uid = useId()
31
32
  const { class: rootClass, attrs: rest } = useMergedAttrs(
32
33
  "flex flex-col gap-2",
33
34
  )
35
+
36
+ usePaletteControl({ disabled: () => field.value.disabled })
34
37
  </script>
35
38
 
36
39
  <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
  * Two thumbs on one track: a range rather than a value. A price "from – to",
11
12
  * a stretch of a clip to loop, a span of years. The model is `[low, high]`;
@@ -20,6 +21,8 @@ import { SLIDER_GLIDE, useSliderDrag } from "./slider"
20
21
 
21
22
  const props = withDefaults(
22
23
  defineProps<{
24
+ /** Contextual palette metadata; false excludes this control. */
25
+ palette?: PaletteControlOptions
23
26
  modelValue?: [number, number]
24
27
  min?: number
25
28
  max?: number
@@ -32,7 +35,7 @@ const props = withDefaults(
32
35
  /** Label over each thumb while it is in use: `(value) => "1:05"`. */
33
36
  format?: (value: number) => string
34
37
  }>(),
35
- { modelValue: () => [0, 100], min: 0, max: 100, step: 1, minGap: 0 },
38
+ { palette: undefined, modelValue: () => [0, 100], min: 0, max: 100, step: 1, minGap: 0 },
36
39
  )
37
40
 
38
41
  const emit = defineEmits<{
@@ -65,6 +68,8 @@ const thumbClass =
65
68
  "group/thumb relative block size-4 rounded-pill border-2 border-brand bg-surface shadow outline-none transition-transform duration-[var(--duration-fast)] ease-[var(--ease-emphasized)] hover:scale-110 active:scale-95 focus-visible:ring-2 focus-visible:ring-ring motion-reduce:hover:scale-100 motion-reduce:active:scale-100"
66
69
  const labelClass =
67
70
  "pointer-events-none absolute bottom-full left-1/2 mb-1.5 -translate-x-1/2 translate-y-0.5 rounded-control bg-surface-raised px-1.5 py-0.5 text-[11px] whitespace-nowrap text-ink tabular-nums opacity-0 shadow-sm transition-[opacity,translate] duration-[var(--duration-fast)] ease-[var(--ease-emphasized)] group-hover/thumb:translate-y-0 group-hover/thumb:opacity-100 group-focus-visible/thumb:translate-y-0 group-focus-visible/thumb:opacity-100 group-active/thumb:translate-y-0 group-active/thumb:opacity-100 motion-reduce:transition-none"
71
+
72
+ usePaletteControl({ disabled: () => field.value.disabled })
68
73
  </script>
69
74
 
70
75
  <template>
@@ -12,6 +12,7 @@ export interface SecretFieldLabels extends Record<string, string> {
12
12
  </script>
13
13
 
14
14
  <script setup lang="ts">
15
+ import { suppressPaletteDiscovery } from "../composables/usePaletteCommands"
15
16
  /*
16
17
  * A value someone must be able to read and copy but should not have on screen
17
18
  * by default: an API key, a bot token, a webhook secret, a recovery code.
@@ -178,6 +179,8 @@ const shell = computed(() =>
178
179
  { sm: "min-h-(--size-control-sm) pl-2.5 pr-1 text-xs", md: "min-h-(--size-control-md) pl-3 pr-1.5 text-sm", lg: "min-h-(--size-control-lg) pl-3.5 pr-1.5 text-sm" }[props.size],
179
180
  ),
180
181
  )
182
+
183
+ suppressPaletteDiscovery()
181
184
  </script>
182
185
 
183
186
  <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
  * Segmented control — a compact bar of mutually-exclusive options with a sliding
11
12
  * pill marking the selection (iOS-style). Built on reka ToggleGroup for the
@@ -36,6 +37,8 @@ export interface SegmentedOption extends LpOptionBase<string> {
36
37
 
37
38
  const props = withDefaults(
38
39
  defineProps<{
40
+ /** Contextual palette metadata; false excludes this control. */
41
+ palette?: PaletteControlOptions
39
42
  modelValue?: string
40
43
  options: SegmentedOption[]
41
44
  size?: "sm" | "md" | "lg"
@@ -49,7 +52,7 @@ const props = withDefaults(
49
52
  */
50
53
  hoverFollow?: boolean
51
54
  }>(),
52
- { size: "md", block: false, hoverFollow: true },
55
+ { palette: undefined, size: "md", block: false, hoverFollow: true },
53
56
  )
54
57
 
55
58
  const emit = defineEmits<{
@@ -106,6 +109,8 @@ function releaseFocusAfterPointer(event: PointerEvent) {
106
109
  const target = event.currentTarget
107
110
  if (target instanceof HTMLElement) target.blur()
108
111
  }
112
+
113
+ usePaletteControl({ disabled: () => field.value.disabled })
109
114
  </script>
110
115
 
111
116
  <template>
@@ -16,6 +16,7 @@ export type SelectLabels = {
16
16
  </script>
17
17
 
18
18
  <script setup lang="ts" generic="V extends SelectValue = string">
19
+ import { usePaletteControl, type PaletteControlOptions } from "../composables/usePaletteCommands"
19
20
  /*
20
21
  * One select to rule them all — built on reka-ui Combobox.
21
22
  * `searchable` opts into the filter input; without it, it's a plain select.
@@ -61,6 +62,8 @@ type Model = V | V[] | undefined
61
62
 
62
63
  const props = withDefaults(
63
64
  defineProps<{
65
+ /** Contextual palette metadata; false excludes this control. */
66
+ palette?: PaletteControlOptions
64
67
  modelValue?: Model
65
68
  options: SelectOption<V>[]
66
69
  placeholder?: string
@@ -90,7 +93,7 @@ const props = withDefaults(
90
93
  selected?: SelectSelection | (SelectSelection | null | undefined)[] | null
91
94
  labels?: Partial<SelectLabels>
92
95
  }>(),
93
- {
96
+ { palette: undefined,
94
97
  searchable: false,
95
98
  multiple: false,
96
99
  clearable: false,
@@ -173,12 +176,43 @@ function onOpenChange(open: boolean) {
173
176
  isOpen.value = open
174
177
  query.value = ""
175
178
  }
179
+ async function openFromKeyboard(event: KeyboardEvent) {
180
+ if (field.value.disabled || isOpen.value || !["ArrowDown", "ArrowUp"].includes(event.key)) return
181
+ event.preventDefault()
182
+ event.stopPropagation()
183
+ const trigger = event.currentTarget as HTMLElement
184
+ trigger.click()
185
+ await nextTick()
186
+ const panel = trigger.ownerDocument.getElementById(trigger.getAttribute("aria-controls") ?? "")
187
+ const search = panel?.querySelector<HTMLInputElement>('input:not([disabled])')
188
+ const items = panel?.querySelectorAll<HTMLElement>('[role="option"]:not([data-disabled])')
189
+ const selected = panel?.querySelector<HTMLElement>('[role="option"][aria-selected="true"]:not([data-disabled])')
190
+ const target = search ?? selected ?? (event.key === "ArrowUp" ? items?.[items.length - 1] : items?.[0])
191
+ target?.focus()
192
+ }
176
193
 
177
194
  // Leaving the field, not moving between its parts: the trigger and the list
178
195
  // are marked with one id, because the list is portalled away from the trigger
179
196
  // and a check on the trigger alone either missed the search box or took a
180
197
  // step into it for leaving.
181
198
  const { partId, onFocusOut } = useFieldBlur((event) => emit("blur", event))
199
+ const triggerRef = ref<{ $el: HTMLElement } | null>(null)
200
+
201
+ /*
202
+ * A pick closes the list while the chosen option still holds focus, then the
203
+ * option is removed and focus falls to the body: the field looked left, and
204
+ * the next arrow key went to the page (inside the palette's editor it went
205
+ * nowhere). Focus dropped by a closed list goes back to the trigger instead,
206
+ * and that is not leaving the field, so no blur is reported for it.
207
+ */
208
+ function onListFocusOut(event: FocusEvent) {
209
+ const trigger = triggerRef.value?.$el
210
+ if (isOpen.value || event.relatedTarget || !trigger?.isConnected) {
211
+ onFocusOut(event)
212
+ return
213
+ }
214
+ trigger.focus({ preventScroll: true })
215
+ }
182
216
 
183
217
  /*
184
218
  * The trigger's name. reka hard-codes aria-label="Show popup" on it, which hid
@@ -368,6 +402,8 @@ const portalTo = usePortalTarget()
368
402
  // What happens in another document (the page around a frame, or a frame in
369
403
  // the page) is not "outside" for this layer.
370
404
  const guardOutside = useOutsideGuard()
405
+
406
+ usePaletteControl({ disabled: () => field.value.disabled, invalid: () => field.value.invalid, editorProps: () => ({ selected: props.multiple ? modelList.value.map((value, index) => shown(value, index)) : current.value }) })
371
407
  </script>
372
408
 
373
409
  <template>
@@ -408,7 +444,10 @@ const guardOutside = useOutsideGuard()
408
444
  being cut short. -->
409
445
  <span :id="valueId" hidden>{{ valueText }}</span>
410
446
  <ComboboxTrigger
447
+ ref="triggerRef"
411
448
  :id="field.id"
449
+ :tabindex="field.disabled ? -1 : 0"
450
+ @keydown="openFromKeyboard"
412
451
  v-bind="triggerName"
413
452
  :aria-invalid="field.invalid || undefined"
414
453
  :aria-describedby="field.describedBy"
@@ -524,7 +563,7 @@ const guardOutside = useOutsideGuard()
524
563
  <ComboboxPortal :to="portalTo">
525
564
  <ComboboxContent
526
565
  :data-lp-field-part="partId"
527
- @focusout="onFocusOut"
566
+ @focusout="onListFocusOut"
528
567
  @interact-outside="guardOutside"
529
568
  @escape-key-down="guardOutside"
530
569
  position="popper"
@@ -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
  // Single-value slider (reka Slider). reka models value as an array; we expose
10
11
  // a plain number for the common single-thumb case. Themed via tokens.
11
12
  import { SliderRange, SliderRoot, SliderThumb, SliderTrack } from "reka-ui"
@@ -15,6 +16,8 @@ import { SLIDER_GLIDE, useSliderDrag } from "./slider"
15
16
 
16
17
  const props = withDefaults(
17
18
  defineProps<{
19
+ /** Contextual palette metadata; false excludes this control. */
20
+ palette?: PaletteControlOptions
18
21
  modelValue?: number
19
22
  min?: number
20
23
  max?: number
@@ -23,7 +26,7 @@ const props = withDefaults(
23
26
  /** Accessible name for the thumb when there's no visible label. */
24
27
  ariaLabel?: string
25
28
  }>(),
26
- { modelValue: 0, min: 0, max: 100, step: 1 },
29
+ { palette: undefined, modelValue: 0, min: 0, max: 100, step: 1 },
27
30
  )
28
31
 
29
32
  const emit = defineEmits<{
@@ -47,6 +50,8 @@ const { class: rootClass, attrs: rest } = useMergedAttrs(
47
50
  `relative flex h-5 w-full touch-none select-none items-center data-[disabled]:opacity-55 ${SLIDER_GLIDE}`,
48
51
  )
49
52
  const { dragging, onPointerDown } = useSliderDrag()
53
+
54
+ usePaletteControl({ disabled: () => field.value.disabled })
50
55
  </script>
51
56
 
52
57
  <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
  import { computed, useId, useSlots } from "vue"
10
11
  import { SwitchRoot, SwitchThumb } from "reka-ui"
11
12
  import { useFieldControl } from "../composables/useField"
@@ -15,6 +16,8 @@ import { useMergedAttrs } from "../composables/useMergedClass"
15
16
 
16
17
  const props = withDefaults(
17
18
  defineProps<{
19
+ /** Contextual palette metadata; false excludes this control. */
20
+ palette?: PaletteControlOptions
18
21
  modelValue?: boolean
19
22
  disabled?: boolean
20
23
  label?: string
@@ -34,7 +37,7 @@ const props = withDefaults(
34
37
  */
35
38
  action?: (value: boolean) => unknown
36
39
  }>(),
37
- { labelPosition: "end" },
40
+ { palette: undefined, labelPosition: "end" },
38
41
  )
39
42
  const emit = defineEmits<{
40
43
  (e: "update:modelValue", value: boolean): void
@@ -79,6 +82,8 @@ const describedBy = computed(
79
82
  function onUpdate(value: boolean) {
80
83
  void set(value)
81
84
  }
85
+
86
+ usePaletteControl({ label: () => props.label, disabled: () => field.value.disabled || pending.value, editorProps: () => ({ action: undefined, 'onUpdate:modelValue': onUpdate }) })
82
87
  </script>
83
88
 
84
89
  <template>