@leavepulse/ui 0.43.4 → 0.45.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (437) hide show
  1. package/README.md +121 -7
  2. package/dist/assets/{LpCommandPalette-B-0Est4Z.css → LpCommandPalette-nGNnzXqQ.css} +21 -21
  3. package/dist/assets/{LpDropzone-BBoWvi-J.css → LpDropzone-i4BdMi1H.css} +12 -12
  4. package/dist/assets/{LpImageEditor-Ct7Y3aNu.css → LpImageEditor-CbMA82g8.css} +6 -6
  5. package/dist/assets/{LpRollingText-CR1mb0Vt.css → LpRollingText-BGnE6q9j.css} +3 -0
  6. package/dist/assets/{LpShortcutsDialog-C6YoQ8_M.css → LpShortcutsDialog-CqYX3yy6.css} +28 -7
  7. package/dist/assets/{LpSkeleton-D7xU8IPV.css → LpSkeleton-H3Z5dEIk.css} +4 -1
  8. package/dist/assets/{LpTable-BgPLJOXa.css → LpTable-CPwWxTJS.css} +49 -9
  9. package/dist/chunks/ContextMenuItems-BtKU7074.js +124 -0
  10. package/dist/chunks/{LayoutCanvas-Dd20boUG.js → LayoutCanvas-BkaGXTjx.js} +26 -9
  11. package/dist/chunks/{LayoutNode-DGpZA9LX.js → LayoutNode-B3_9E1Xa.js} +28 -12
  12. package/dist/chunks/{LpActionBar-BlvtGB2P.js → LpActionBar-CnfFVIwC.js} +26 -12
  13. package/dist/chunks/{LpAddressInput-B-Zvxsn7.js → LpAddressInput-BwX8uo4_.js} +54 -49
  14. package/dist/chunks/{LpAlert-BAYHDSS1.js → LpAlert-B6ilrM7T.js} +16 -9
  15. package/dist/chunks/{LpAppShell-BGQVIY-3.js → LpAppShell-C53_RaGF.js} +53 -26
  16. package/dist/chunks/{LpAutocomplete-DExlIZe_.js → LpAutocomplete-C8IgSMsi.js} +52 -24
  17. package/dist/chunks/{LpAvatar-B3-xAOH3.js → LpAvatar-C1XStdOo.js} +19 -10
  18. package/dist/chunks/LpBreadcrumbs-CQJd73Gm.js +95 -0
  19. package/dist/chunks/{LpButton-5VXXCjwF.js → LpButton-CKSwC6Mu.js} +45 -15
  20. package/dist/chunks/{LpCalendar-B7Ko5RdR.js → LpCalendar-DrA2McTU.js} +1 -1
  21. package/dist/chunks/{LpCard-BhI1N_vL.js → LpCard-C2IvucmX.js} +1 -1
  22. package/dist/chunks/{LpCheckbox-CVccKYaH.js → LpCheckbox-Bkj13CbW.js} +8 -4
  23. package/dist/chunks/{LpCodeBlock-Bo0B4TFR.js → LpCodeBlock-ca7ANFyW.js} +49 -17
  24. package/dist/chunks/{LpColorPicker-BlXea7LM.js → LpColorPicker-zz4VGkG4.js} +75 -26
  25. package/dist/chunks/{LpConfirmDialog-Q-bxff8z.js → LpConfirmDialog-DCw5jR1t.js} +59 -17
  26. package/dist/chunks/LpConfirmHost-CbywMpMP.js +49 -0
  27. package/dist/chunks/LpContextMenu-BqkAYa9X.js +69 -0
  28. package/dist/chunks/LpDatePicker-DuusgT69.js +195 -0
  29. package/dist/chunks/LpDateRangePicker-DHeuznfC.js +208 -0
  30. package/dist/chunks/{LpDisclosure-ChQYmjlR.js → LpDisclosure-Cc23-JOT.js} +1 -1
  31. package/dist/chunks/{LpDrawer-BZ6k3hvo.js → LpDrawer-Dt4aB4Fr.js} +65 -28
  32. package/dist/chunks/{LpDropdownMenu-DJpoqRCb.js → LpDropdownMenu-DdJGQzPn.js} +2 -1
  33. package/dist/chunks/{LpEmptyState-Dsdqz1Wj.js → LpEmptyState-BJbZXC8D.js} +1 -1
  34. package/dist/chunks/LpFieldNotice-wtieLUTj.js +64 -0
  35. package/dist/chunks/{LpFileTree-_5vFBgaK.js → LpFileTree-CNurOvkV.js} +25 -10
  36. package/dist/chunks/{LpFileTreeNode-BdcIYKIE.js → LpFileTreeNode-D_qRRdsH.js} +3 -3
  37. package/dist/chunks/LpFormField-CtVYj6tO.js +101 -0
  38. package/dist/chunks/{LpIcon-CCnX5_2j.js → LpIcon-DYtaid0K.js} +20 -6
  39. package/dist/chunks/{LpInput-9EXWUM9B.js → LpInput-D-YGQbEv.js} +58 -20
  40. package/dist/chunks/{LpLightbox-2sYVMY-S.js → LpLightbox-DSDahF23.js} +148 -61
  41. package/dist/chunks/{LpLink-DCND-zyu.js → LpLink-Krkjhe_y.js} +15 -6
  42. package/dist/chunks/{LpLogViewer-CaRy5MeS.js → LpLogViewer-KypbgOyo.js} +300 -72
  43. package/dist/chunks/{LpMapPicker-ul4oYno6.js → LpMapPicker-Cc84LaM3.js} +39 -14
  44. package/dist/chunks/{LpMediaGallery-FF-NquIo.js → LpMediaGallery-YKr-cQPN.js} +7 -4
  45. package/dist/chunks/{LpModal-Bt0pQCI_.js → LpModal-DMMDQHAu.js} +52 -29
  46. package/dist/chunks/{LpModalNav-YaifHrXC.js → LpModalNav-BVPDyIup.js} +10 -7
  47. package/dist/chunks/{LpNotificationBell-DyGGsMAM.js → LpNotificationBell-CCwyJP5C.js} +79 -33
  48. package/dist/chunks/{LpNumberField-CWuX08iX.js → LpNumberField-B77O-z28.js} +21 -8
  49. package/dist/chunks/{LpNumberFlow-amWYaeuA.js → LpNumberFlow-z4WPZcZu.js} +22 -12
  50. package/dist/chunks/{LpOtpInput-y2v-pmiN.js → LpOtpInput-CziPF3Oq.js} +37 -8
  51. package/dist/chunks/{LpPagination-5FxjmmB5.js → LpPagination-BcDlo3r_.js} +31 -20
  52. package/dist/chunks/{LpPasswordInput--sjYCkLS.js → LpPasswordInput-eKzQhbQP.js} +11 -8
  53. package/dist/chunks/LpPhoneInput-BCbrrhgE.js +336 -0
  54. package/dist/chunks/{LpPie-D6OnzwlJ.js → LpPie-BEJ2SfI6.js} +84 -28
  55. package/dist/chunks/{LpPopover-8268UmlC.js → LpPopover-DCKeqiVn.js} +5 -2
  56. package/dist/chunks/{LpProgress-Btq5W4Ut.js → LpProgress-CBU1iI69.js} +5 -4
  57. package/dist/chunks/{LpRadioGroup-xPIuVDO_.js → LpRadioGroup-DmEW0l78.js} +9 -2
  58. package/dist/chunks/LpRangeCalendar-DZuaRV0W.js +331 -0
  59. package/dist/chunks/{LpRollingText-CM5gkSeF.js → LpRollingText-DB7upf-5.js} +1 -1
  60. package/dist/chunks/{LpScrollArea-D8xyKXcF.js → LpScrollArea-Bry1fdOM.js} +14 -2
  61. package/dist/chunks/{LpSection-aM6J_u51.js → LpSection-tKEH0jYf.js} +1 -1
  62. package/dist/chunks/{LpSegmented-fV0Sk92p.js → LpSegmented-C09EEam2.js} +8 -4
  63. package/dist/chunks/{LpSelect-ANIVtrpM.js → LpSelect-DNIL6MB9.js} +50 -31
  64. package/dist/chunks/{LpSidebar-ClBUwoEz.js → LpSidebar-CQNB0-yU.js} +30 -22
  65. package/dist/chunks/{LpSidebarNav-llOcs_Fn.js → LpSidebarNav-kx-LdqNU.js} +16 -11
  66. package/dist/chunks/{LpSkeleton-CTI-vBgW.js → LpSkeleton-BB6XIV-w.js} +1 -1
  67. package/dist/chunks/{LpSlider-BwNIBkAK.js → LpSlider-Cuzmrd7p.js} +7 -2
  68. package/dist/chunks/{LpSparkline-C6xQ2dwv.js → LpSparkline-CB80N8SJ.js} +11 -4
  69. package/dist/chunks/{LpStat-CQaW8R--.js → LpStat-B-7udBRP.js} +2 -2
  70. package/dist/chunks/{LpStepper-Bk-XaQYU.js → LpStepper-C7XiVKZj.js} +34 -19
  71. package/dist/chunks/{LpSwitch-Blizvqyt.js → LpSwitch-OrcOJX9m.js} +41 -27
  72. package/dist/chunks/LpTableFilter-BV8PpLM9.js +158 -0
  73. package/dist/chunks/{LpTableOfContents-CjvxPMpU.js → LpTableOfContents-BUcAfGPQ.js} +36 -41
  74. package/dist/chunks/{LpTabs-Cefe_lys.js → LpTabs-29iW1XOx.js} +20 -6
  75. package/dist/chunks/LpTagsInput-D1SuqbhB.js +300 -0
  76. package/dist/chunks/LpTextarea-uuScWo6-.js +77 -0
  77. package/dist/chunks/LpThemeProvider-Be4r2TQX.js +35 -0
  78. package/dist/chunks/LpThemeSwitcher-B4aVPbNC.js +166 -0
  79. package/dist/chunks/{LpTilt-DuyKbpkA.js → LpTilt-B7KHX1zH.js} +2 -2
  80. package/dist/chunks/{LpTimeField-K6nAGboX.js → LpTimeField-Cqe7fXnh.js} +14 -8
  81. package/dist/chunks/{LpToaster-gDB1EaU3.js → LpToaster-DpJvoCOh.js} +94 -31
  82. package/dist/chunks/{LpTooltip-hCLPAqMG.js → LpTooltip-CXVKcOXT.js} +14 -6
  83. package/dist/chunks/LpTooltipProvider-CftjV7Xc.js +25 -0
  84. package/dist/chunks/{LpTopologyCanvas-Cm1L39uL.js → LpTopologyCanvas-6ozNrQKh.js} +8 -2
  85. package/dist/chunks/{LpUptimeBar-DvAMno0S.js → LpUptimeBar-ugcv25xj.js} +44 -15
  86. package/dist/chunks/icons-CkA0njzm.js +43 -0
  87. package/dist/chunks/{LpThemeSwitcher-Cue-myST.js → useTheme-DJm1JSsY.js} +215 -260
  88. package/dist/component-names.d.ts +1 -1
  89. package/dist/components/ContextMenuItems.vue.d.ts +8 -0
  90. package/dist/components/ContextMenuItems.vue.js +4 -0
  91. package/dist/components/LayoutCanvas.vue.js +1 -1
  92. package/dist/components/LayoutNode.vue.d.ts +4 -0
  93. package/dist/components/LayoutNode.vue.js +1 -1
  94. package/dist/components/LpActionBar.vue.d.ts +3 -0
  95. package/dist/components/LpActionBar.vue.js +1 -1
  96. package/dist/components/LpAddressInput.vue.d.ts +1 -1
  97. package/dist/components/LpAddressInput.vue.js +1 -1
  98. package/dist/components/LpAlert.vue.d.ts +9 -0
  99. package/dist/components/LpAlert.vue.js +1 -1
  100. package/dist/components/LpAppShell.vue.d.ts +10 -0
  101. package/dist/components/LpAppShell.vue.js +1 -1
  102. package/dist/components/LpAutocomplete.vue.d.ts +31 -6
  103. package/dist/components/LpAutocomplete.vue.js +1 -1
  104. package/dist/components/LpAvatar.vue.d.ts +3 -3
  105. package/dist/components/LpAvatar.vue.js +1 -1
  106. package/dist/components/LpBadge.vue.d.ts +2 -2
  107. package/dist/components/LpBreadcrumbs.vue.d.ts +13 -9
  108. package/dist/components/LpBreadcrumbs.vue.js +1 -1
  109. package/dist/components/LpButton.vue.d.ts +35 -98
  110. package/dist/components/LpButton.vue.js +1 -1
  111. package/dist/components/LpCalendar.vue.js +1 -1
  112. package/dist/components/LpCard.vue.js +1 -1
  113. package/dist/components/LpChart.vue.d.ts +47 -3
  114. package/dist/components/LpChart.vue.js +377 -97
  115. package/dist/components/LpCheckbox.vue.js +1 -1
  116. package/dist/components/LpCodeBlock.vue.d.ts +2 -0
  117. package/dist/components/LpCodeBlock.vue.js +1 -1
  118. package/dist/components/LpColorPicker.vue.d.ts +2 -2
  119. package/dist/components/LpColorPicker.vue.js +1 -1
  120. package/dist/components/LpCommandPalette.vue.d.ts +14 -3
  121. package/dist/components/LpCommandPalette.vue.js +142 -77
  122. package/dist/components/LpConfirmDialog.vue.d.ts +15 -3
  123. package/dist/components/LpConfirmDialog.vue.js +1 -1
  124. package/dist/components/LpConfirmHost.vue.d.ts +9 -0
  125. package/dist/components/LpConfirmHost.vue.js +4 -0
  126. package/dist/components/LpContextMenu.vue.d.ts +3 -5
  127. package/dist/components/LpContextMenu.vue.js +1 -1
  128. package/dist/components/LpDatePicker.vue.d.ts +24 -6
  129. package/dist/components/LpDatePicker.vue.js +1 -1
  130. package/dist/components/LpDateRangePicker.vue.d.ts +60 -0
  131. package/dist/components/LpDateRangePicker.vue.js +4 -0
  132. package/dist/components/LpDisclosure.vue.js +1 -1
  133. package/dist/components/LpDrawer.vue.d.ts +10 -0
  134. package/dist/components/LpDrawer.vue.js +1 -1
  135. package/dist/components/LpDropdownMenu.vue.js +1 -1
  136. package/dist/components/LpDropzone.vue.js +23 -7
  137. package/dist/components/LpEmptyState.vue.js +1 -1
  138. package/dist/components/LpFieldNotice.vue.d.ts +8 -0
  139. package/dist/components/LpFieldNotice.vue.js +4 -0
  140. package/dist/components/LpFileTree.vue.d.ts +11 -2
  141. package/dist/components/LpFileTree.vue.js +1 -1
  142. package/dist/components/LpFileTreeNode.vue.js +1 -1
  143. package/dist/components/LpFormField.vue.d.ts +13 -1
  144. package/dist/components/LpFormField.vue.js +1 -1
  145. package/dist/components/LpIcon.vue.d.ts +0 -1
  146. package/dist/components/LpIcon.vue.js +1 -1
  147. package/dist/components/LpImageEditor.vue.js +93 -58
  148. package/dist/components/LpInput.vue.d.ts +22 -3
  149. package/dist/components/LpInput.vue.js +1 -1
  150. package/dist/components/LpLightbox.vue.d.ts +17 -0
  151. package/dist/components/LpLightbox.vue.js +1 -1
  152. package/dist/components/LpLink.vue.d.ts +11 -11
  153. package/dist/components/LpLink.vue.js +1 -1
  154. package/dist/components/LpLogViewer.vue.d.ts +72 -3
  155. package/dist/components/LpLogViewer.vue.js +1 -1
  156. package/dist/components/LpMapPicker.vue.d.ts +4 -4
  157. package/dist/components/LpMapPicker.vue.js +1 -1
  158. package/dist/components/LpMediaGallery.vue.js +1 -1
  159. package/dist/components/LpModal.vue.d.ts +16 -6
  160. package/dist/components/LpModal.vue.js +1 -1
  161. package/dist/components/LpModalNav.vue.d.ts +0 -1
  162. package/dist/components/LpModalNav.vue.js +1 -1
  163. package/dist/components/LpNotificationBell.vue.d.ts +11 -2
  164. package/dist/components/LpNotificationBell.vue.js +1 -1
  165. package/dist/components/LpNumberField.vue.d.ts +2 -0
  166. package/dist/components/LpNumberField.vue.js +1 -1
  167. package/dist/components/LpNumberFlow.vue.js +1 -1
  168. package/dist/components/LpOtpInput.vue.js +1 -1
  169. package/dist/components/LpPagination.vue.d.ts +2 -0
  170. package/dist/components/LpPagination.vue.js +1 -1
  171. package/dist/components/LpPasswordInput.vue.js +1 -1
  172. package/dist/components/LpPhoneInput.vue.d.ts +13 -0
  173. package/dist/components/LpPhoneInput.vue.js +1 -1
  174. package/dist/components/LpPie.vue.d.ts +21 -2
  175. package/dist/components/LpPie.vue.js +1 -1
  176. package/dist/components/LpPopover.vue.d.ts +2 -0
  177. package/dist/components/LpPopover.vue.js +1 -1
  178. package/dist/components/LpProgress.vue.d.ts +1 -1
  179. package/dist/components/LpProgress.vue.js +1 -1
  180. package/dist/components/LpRadioGroup.vue.js +1 -1
  181. package/dist/components/LpRangeCalendar.vue.d.ts +51 -0
  182. package/dist/components/LpRangeCalendar.vue.js +4 -0
  183. package/dist/components/LpRollingText.vue.d.ts +1 -1
  184. package/dist/components/LpRollingText.vue.js +1 -1
  185. package/dist/components/LpScrollArea.vue.d.ts +2 -2
  186. package/dist/components/LpScrollArea.vue.js +1 -1
  187. package/dist/components/LpSection.vue.d.ts +1 -1
  188. package/dist/components/LpSection.vue.js +1 -1
  189. package/dist/components/LpSegmented.vue.d.ts +5 -0
  190. package/dist/components/LpSegmented.vue.js +1 -1
  191. package/dist/components/LpSelect.vue.d.ts +1 -0
  192. package/dist/components/LpSelect.vue.js +1 -1
  193. package/dist/components/LpShortcutsDialog.vue.d.ts +7 -1
  194. package/dist/components/LpShortcutsDialog.vue.js +60 -15
  195. package/dist/components/LpSidebar.vue.d.ts +6 -2
  196. package/dist/components/LpSidebar.vue.js +1 -1
  197. package/dist/components/LpSidebarNav.vue.js +1 -1
  198. package/dist/components/LpSkeleton.vue.js +1 -1
  199. package/dist/components/LpSlider.vue.js +1 -1
  200. package/dist/components/LpSparkline.vue.d.ts +1 -0
  201. package/dist/components/LpSparkline.vue.js +1 -1
  202. package/dist/components/LpStat.vue.js +1 -1
  203. package/dist/components/LpStepper.vue.js +1 -1
  204. package/dist/components/LpSwitch.vue.js +1 -1
  205. package/dist/components/LpTable.vue.d.ts +107 -3
  206. package/dist/components/LpTable.vue.js +540 -128
  207. package/dist/components/LpTableFilter.vue.d.ts +31 -0
  208. package/dist/components/LpTableFilter.vue.js +4 -0
  209. package/dist/components/LpTableOfContents.vue.d.ts +4 -2
  210. package/dist/components/LpTableOfContents.vue.js +1 -1
  211. package/dist/components/LpTabs.vue.d.ts +2 -0
  212. package/dist/components/LpTabs.vue.js +1 -1
  213. package/dist/components/LpTagsInput.vue.d.ts +77 -0
  214. package/dist/components/LpTagsInput.vue.js +4 -0
  215. package/dist/components/LpTextarea.vue.d.ts +19 -6
  216. package/dist/components/LpTextarea.vue.js +1 -1
  217. package/dist/components/LpThemeProvider.vue.d.ts +22 -0
  218. package/dist/components/LpThemeProvider.vue.js +4 -0
  219. package/dist/components/LpThemeSwitcher.vue.d.ts +15 -2
  220. package/dist/components/LpThemeSwitcher.vue.js +1 -1
  221. package/dist/components/LpTilt.vue.js +1 -1
  222. package/dist/components/LpTimeField.vue.js +1 -1
  223. package/dist/components/LpToaster.vue.d.ts +6 -1
  224. package/dist/components/LpToaster.vue.js +1 -1
  225. package/dist/components/LpTooltip.vue.d.ts +1 -2
  226. package/dist/components/LpTooltip.vue.js +1 -1
  227. package/dist/components/LpTooltipProvider.vue.d.ts +22 -0
  228. package/dist/components/LpTooltipProvider.vue.js +4 -0
  229. package/dist/components/LpTopologyCanvas.vue.js +1 -1
  230. package/dist/components/LpUptimeBar.vue.d.ts +4 -0
  231. package/dist/components/LpUptimeBar.vue.js +1 -1
  232. package/dist/components/chart.d.ts +32 -1
  233. package/dist/components/chart.js +42 -6
  234. package/dist/components/codeHighlight.d.ts +6 -0
  235. package/dist/components/codeHighlight.js +92 -20
  236. package/dist/components/color.d.ts +1 -1
  237. package/dist/components/color.js +20 -9
  238. package/dist/components/commandSearch.js +3 -0
  239. package/dist/components/confirm.d.ts +25 -0
  240. package/dist/components/confirm.js +26 -0
  241. package/dist/components/countries.d.ts +6 -0
  242. package/dist/components/countries.js +7 -0
  243. package/dist/components/dateRange.d.ts +43 -0
  244. package/dist/components/dateRange.js +76 -0
  245. package/dist/components/exportData.d.ts +58 -0
  246. package/dist/components/exportData.js +165 -0
  247. package/dist/components/fileTree.js +16 -7
  248. package/dist/components/imageCrop.d.ts +42 -0
  249. package/dist/components/imageCrop.js +47 -0
  250. package/dist/components/link.d.ts +12 -0
  251. package/dist/components/link.js +19 -0
  252. package/dist/components/logSearch.d.ts +18 -0
  253. package/dist/components/logSearch.js +36 -0
  254. package/dist/components/modalDepth.d.ts +18 -2
  255. package/dist/components/modalDepth.js +17 -5
  256. package/dist/components/otp.d.ts +5 -0
  257. package/dist/components/otp.js +10 -0
  258. package/dist/components/phone.d.ts +41 -0
  259. package/dist/components/phone.js +80 -0
  260. package/dist/components/sidebar.d.ts +7 -1
  261. package/dist/components/tableFilter.d.ts +36 -0
  262. package/dist/components/tableFilter.js +84 -0
  263. package/dist/components/tableSort.d.ts +8 -0
  264. package/dist/components/tableSort.js +31 -0
  265. package/dist/components/tags.d.ts +20 -0
  266. package/dist/components/tags.js +37 -0
  267. package/dist/composables/useField.d.ts +38 -0
  268. package/dist/composables/useField.js +45 -0
  269. package/dist/composables/useFieldNotice.d.ts +17 -0
  270. package/dist/composables/useFieldNotice.js +28 -0
  271. package/dist/composables/useForm.d.ts +53 -0
  272. package/dist/composables/useForm.js +207 -0
  273. package/dist/composables/useFormDraft.d.ts +10 -3
  274. package/dist/composables/useFormDraft.js +9 -14
  275. package/dist/composables/useHotkeys.d.ts +81 -5
  276. package/dist/composables/useHotkeys.js +223 -25
  277. package/dist/composables/useInputFilter.d.ts +17 -0
  278. package/dist/composables/useInputFilter.js +47 -10
  279. package/dist/composables/useInputMask.d.ts +59 -0
  280. package/dist/composables/useInputMask.js +170 -0
  281. package/dist/composables/useLpConfig.d.ts +24 -0
  282. package/dist/composables/useLpConfig.js +44 -0
  283. package/dist/composables/usePaintSelect.d.ts +22 -0
  284. package/dist/composables/usePaintSelect.js +169 -0
  285. package/dist/composables/useRovingFocus.js +7 -0
  286. package/dist/composables/useShift.js +7 -2
  287. package/dist/composables/useTilt.d.ts +2 -2
  288. package/dist/composables/useTilt.js +5 -6
  289. package/dist/composables/useToast.d.ts +20 -0
  290. package/dist/composables/useToast.js +28 -4
  291. package/dist/composables/useZoomPan.d.ts +3 -3
  292. package/dist/composables/useZoomPan.js +4 -4
  293. package/dist/icons-all.d.ts +1 -0
  294. package/dist/icons-all.js +3 -0
  295. package/dist/icons.d.ts +14 -1
  296. package/dist/index.d.ts +60 -4
  297. package/dist/index.js +245 -141
  298. package/dist/kit-icons.d.ts +2 -0
  299. package/dist/layout/tree.d.ts +2 -0
  300. package/dist/layout/tree.js +16 -1
  301. package/dist/layout/useLayout.d.ts +15 -0
  302. package/dist/layout/useLayout.js +41 -14
  303. package/dist/theme/backdrop.d.ts +12 -0
  304. package/dist/theme/presets.d.ts +0 -2
  305. package/dist/theme/useTheme.d.ts +32 -9
  306. package/package.json +18 -8
  307. package/src/component-names.ts +9 -7
  308. package/src/components/ContextMenuItems.vue +88 -0
  309. package/src/components/LayoutCanvas.vue +26 -6
  310. package/src/components/LayoutNode.vue +30 -4
  311. package/src/components/LpActionBar.vue +31 -8
  312. package/src/components/LpAddressInput.vue +57 -41
  313. package/src/components/LpAlert.vue +16 -3
  314. package/src/components/LpAppShell.vue +45 -12
  315. package/src/components/LpAutocomplete.vue +47 -10
  316. package/src/components/LpAvatar.vue +20 -6
  317. package/src/components/LpBreadcrumbs.vue +65 -41
  318. package/src/components/LpButton.vue +60 -14
  319. package/src/components/LpChart.vue +351 -28
  320. package/src/components/LpCheckbox.vue +7 -3
  321. package/src/components/LpCodeBlock.vue +50 -14
  322. package/src/components/LpColorPicker.vue +73 -10
  323. package/src/components/LpCommandPalette.vue +115 -20
  324. package/src/components/LpConfirmDialog.vue +68 -17
  325. package/src/components/LpConfirmHost.vue +59 -0
  326. package/src/components/LpContextMenu.vue +15 -116
  327. package/src/components/LpDatePicker.vue +104 -68
  328. package/src/components/LpDateRangePicker.vue +224 -0
  329. package/src/components/LpDrawer.vue +56 -19
  330. package/src/components/LpDropdownMenu.vue +1 -0
  331. package/src/components/LpDropzone.vue +21 -4
  332. package/src/components/LpFieldNotice.vue +65 -0
  333. package/src/components/LpFileTree.vue +33 -7
  334. package/src/components/LpFormField.vue +59 -7
  335. package/src/components/LpIcon.vue +22 -4
  336. package/src/components/LpImageEditor.vue +96 -57
  337. package/src/components/LpInput.vue +62 -11
  338. package/src/components/LpLightbox.vue +140 -29
  339. package/src/components/LpLink.vue +24 -13
  340. package/src/components/LpLogViewer.vue +311 -39
  341. package/src/components/LpMapPicker.vue +36 -13
  342. package/src/components/LpMediaGallery.vue +8 -2
  343. package/src/components/LpModal.vue +54 -28
  344. package/src/components/LpModalNav.vue +5 -2
  345. package/src/components/LpNotificationBell.vue +77 -23
  346. package/src/components/LpNumberField.vue +21 -6
  347. package/src/components/LpNumberFlow.vue +26 -10
  348. package/src/components/LpOtpInput.vue +43 -8
  349. package/src/components/LpPagination.vue +23 -7
  350. package/src/components/LpPasswordInput.vue +4 -1
  351. package/src/components/LpPhoneInput.vue +224 -39
  352. package/src/components/LpPie.vue +102 -20
  353. package/src/components/LpPopover.vue +4 -0
  354. package/src/components/LpProgress.vue +5 -3
  355. package/src/components/LpRadioGroup.vue +9 -2
  356. package/src/components/LpRangeCalendar.vue +365 -0
  357. package/src/components/LpRollingText.vue +3 -0
  358. package/src/components/LpScrollArea.vue +22 -2
  359. package/src/components/LpSegmented.vue +12 -2
  360. package/src/components/LpSelect.vue +0 -0
  361. package/src/components/LpShortcutsDialog.vue +83 -9
  362. package/src/components/LpSidebar.vue +14 -9
  363. package/src/components/LpSidebarNav.vue +17 -5
  364. package/src/components/LpSkeleton.vue +4 -1
  365. package/src/components/LpSlider.vue +7 -1
  366. package/src/components/LpSparkline.vue +11 -3
  367. package/src/components/LpStepper.vue +23 -10
  368. package/src/components/LpSwitch.vue +31 -8
  369. package/src/components/LpTable.vue +668 -90
  370. package/src/components/LpTableFilter.vue +175 -0
  371. package/src/components/LpTableOfContents.vue +50 -32
  372. package/src/components/LpTabs.vue +23 -6
  373. package/src/components/LpTagsInput.vue +364 -0
  374. package/src/components/LpTextarea.vue +44 -6
  375. package/src/components/LpThemeProvider.vue +41 -0
  376. package/src/components/LpThemeSwitcher.vue +76 -33
  377. package/src/components/LpTilt.vue +2 -2
  378. package/src/components/LpTimeField.vue +14 -3
  379. package/src/components/LpToaster.vue +115 -27
  380. package/src/components/LpTooltip.vue +29 -5
  381. package/src/components/LpTooltipProvider.vue +24 -0
  382. package/src/components/LpTopologyCanvas.vue +19 -2
  383. package/src/components/LpUptimeBar.vue +46 -8
  384. package/src/components/chart.ts +82 -8
  385. package/src/components/codeHighlight.ts +111 -15
  386. package/src/components/color.ts +30 -9
  387. package/src/components/commandSearch.ts +6 -0
  388. package/src/components/confirm.ts +58 -0
  389. package/src/components/countries.ts +12 -0
  390. package/src/components/dateRange.ts +134 -0
  391. package/src/components/exportData.ts +228 -0
  392. package/src/components/fileTree.ts +19 -7
  393. package/src/components/imageCrop.ts +107 -0
  394. package/src/components/link.ts +34 -0
  395. package/src/components/logSearch.ts +46 -0
  396. package/src/components/modalDepth.ts +48 -6
  397. package/src/components/otp.ts +16 -0
  398. package/src/components/phone.ts +140 -0
  399. package/src/components/sidebar.ts +7 -1
  400. package/src/components/tableFilter.ts +122 -0
  401. package/src/components/tableSort.ts +37 -0
  402. package/src/components/tags.ts +62 -0
  403. package/src/composables/useField.ts +83 -0
  404. package/src/composables/useFieldNotice.ts +59 -0
  405. package/src/composables/useForm.ts +314 -0
  406. package/src/composables/useFormDraft.ts +25 -20
  407. package/src/composables/useHotkeys.ts +355 -28
  408. package/src/composables/useInputFilter.ts +77 -14
  409. package/src/composables/useInputMask.ts +254 -0
  410. package/src/composables/useLpConfig.ts +87 -0
  411. package/src/composables/usePaintSelect.ts +243 -0
  412. package/src/composables/useRovingFocus.ts +11 -0
  413. package/src/composables/useShift.ts +13 -2
  414. package/src/composables/useTilt.ts +5 -6
  415. package/src/composables/useToast.ts +56 -4
  416. package/src/composables/useZoomPan.ts +6 -6
  417. package/src/iconSubset.d.mts +10 -0
  418. package/src/iconSubset.mjs +51 -0
  419. package/src/icons-all.ts +10 -0
  420. package/src/icons.ts +59 -8
  421. package/src/index.ts +73 -4
  422. package/src/kit-icons.ts +5 -0
  423. package/src/layout/tree.ts +30 -0
  424. package/src/layout/useLayout.ts +68 -18
  425. package/src/nuxt.ts +87 -17
  426. package/src/runtime/config.plugin.ts +14 -0
  427. package/src/runtime/theme.plugin.ts +43 -23
  428. package/src/theme/backdrop.ts +69 -0
  429. package/src/theme/presets.ts +6 -64
  430. package/src/theme/useTheme.ts +206 -100
  431. package/src/tokens/tokens.css +46 -0
  432. package/dist/chunks/LpBreadcrumbs-p4jHe_V-.js +0 -62
  433. package/dist/chunks/LpContextMenu-CwW1QXe_.js +0 -217
  434. package/dist/chunks/LpDatePicker-CwOToxGq.js +0 -164
  435. package/dist/chunks/LpFormField-CazBc2-a.js +0 -62
  436. package/dist/chunks/LpPhoneInput-BDSKVl_z.js +0 -209
  437. package/dist/chunks/LpTextarea-t7F6gv8E.js +0 -47
@@ -1,4 +1,4 @@
1
- import { computed, ref, shallowRef, type Ref } from "vue"
1
+ import { computed, ref, shallowRef, watch, type Ref } from "vue"
2
2
  import {
3
3
  addLeaf,
4
4
  addTab,
@@ -10,8 +10,10 @@ import {
10
10
  removeLeaf,
11
11
  reorderTab,
12
12
  resizeAt,
13
+ restoreLayout,
13
14
  setActiveTab,
14
15
  serializeLayout,
16
+ snapshotLayout,
15
17
  type Side,
16
18
  type Split,
17
19
  } from "./tree"
@@ -22,13 +24,34 @@ import {
22
24
  * tree.ts is a pile of pure-ish mutators; this composable is the ergonomic
23
25
  * surface a consumer actually wires to UI: it owns the reactive tree, snapshots
24
26
  * every structural edit for undo/redo, and exposes save/load/reset against a
25
- * serialized form. The canvas calls the same mutators for drag/drop/resize, so
26
- * those go through `record()` too and land in history.
27
+ * serialized form. A <LayoutCanvas v-model> bound to this tree finds the
28
+ * controller through `layoutController`, so drag-move, tab close/reorder and
29
+ * divider resizes land in history with no extra wiring.
27
30
  *
28
- * History holds serialized snapshots (plain JSON), not live trees, so an undo
29
- * can't smuggle stale reactive nodes back in.
31
+ * History holds snapshots (plain JSON with node ids), not live trees, so an
32
+ * undo can't smuggle stale reactive nodes back in, yet blocks keep their
33
+ * identity and are not remounted.
30
34
  */
31
35
 
36
+ /** The edits a canvas performs, routed through history when a controller owns the tree. */
37
+ export interface LayoutController {
38
+ move: (movingId: string, targetId: string, side: Side) => void
39
+ removeBlock: (leafId: string, index: number) => void
40
+ reorderTab: (leafId: string, from: number, to: number) => void
41
+ setActive: (leafId: string, index: number) => void
42
+ resize: (parent: Split, index: number, deltaFraction: number) => void
43
+ /** Bracket a continuous resize gesture so it records one history step. */
44
+ beginResize: () => void
45
+ endResize: () => void
46
+ }
47
+
48
+ const controllers = new WeakMap<Split, LayoutController>()
49
+
50
+ /** The controller managing `tree`, if a useLayout() owns it. */
51
+ export function layoutController(tree: Split): LayoutController | undefined {
52
+ return controllers.get(tree)
53
+ }
54
+
32
55
  export interface UseLayoutOptions {
33
56
  /** Initial block ids, left-to-right, when no saved layout is loaded. */
34
57
  initial: string[]
@@ -57,6 +80,8 @@ export interface UseLayout {
57
80
  remove: (leafId: string) => void
58
81
  move: (movingId: string, targetId: string, side: Side) => void
59
82
  resize: (parent: Split, index: number, deltaFraction: number) => void
83
+ beginResize: () => void
84
+ endResize: () => void
60
85
  undo: () => void
61
86
  redo: () => void
62
87
  /** Replace the whole layout from a flat block list. */
@@ -77,19 +102,43 @@ export function useLayout(options: UseLayoutOptions): UseLayout {
77
102
  const canUndo = computed(() => past.value.length > 0)
78
103
  const canRedo = computed(() => future.value.length > 0)
79
104
 
80
- const snapshot = () => JSON.stringify(serializeLayout(layout.value))
105
+ const snapshot = () => snapshotLayout(layout.value)
106
+
107
+ function push(before: string) {
108
+ if (before === snapshot()) return
109
+ past.value.push(before)
110
+ if (past.value.length > limit) past.value.shift()
111
+ future.value = []
112
+ }
81
113
 
82
114
  // Capture the pre-edit state, run the structural change, drop any redo branch.
83
115
  function record(mutate: () => void) {
84
116
  const before = snapshot()
85
117
  mutate()
86
- past.value.push(before)
87
- if (past.value.length > limit) past.value.shift()
88
- future.value = []
118
+ push(before)
119
+ }
120
+
121
+ let resizeStart: string | null = null
122
+
123
+ const controller: LayoutController = {
124
+ move: (movingId, targetId, side) => record(() => moveLeaf(layout.value, movingId, targetId, side)),
125
+ removeBlock: (leafId, index) => record(() => removeBlock(layout.value, leafId, index)),
126
+ reorderTab: (leafId, from, to) => record(() => reorderTab(layout.value, leafId, from, to)),
127
+ setActive: (leafId, index) => setActiveTab(layout.value, leafId, index),
128
+ resize: (parent, index, delta) => resizeAt(parent, index, delta),
129
+ beginResize: () => {
130
+ resizeStart = snapshot()
131
+ },
132
+ endResize: () => {
133
+ if (resizeStart !== null) push(resizeStart)
134
+ resizeStart = null
135
+ },
89
136
  }
90
137
 
138
+ watch(layout, (tree) => controllers.set(tree, controller), { immediate: true, flush: "sync" })
139
+
91
140
  function restore(serialized: string) {
92
- layout.value = deserializeLayout(JSON.parse(serialized))
141
+ layout.value = restoreLayout(serialized)
93
142
  }
94
143
 
95
144
  return {
@@ -99,16 +148,17 @@ export function useLayout(options: UseLayoutOptions): UseLayout {
99
148
  canRedo,
100
149
  add: (block, side) => record(() => addLeaf(layout.value, block, side)),
101
150
  addTab: (leafId, block) => record(() => addTab(layout.value, leafId, block)),
102
- setActive: (leafId, index) => setActiveTab(layout.value, leafId, index),
103
- reorderTab: (leafId, from, to) => record(() => reorderTab(layout.value, leafId, from, to)),
104
- removeBlock: (leafId, index) => record(() => removeBlock(layout.value, leafId, index)),
151
+ setActive: controller.setActive,
152
+ reorderTab: controller.reorderTab,
153
+ removeBlock: controller.removeBlock,
105
154
  remove: (leafId) => record(() => removeLeaf(layout.value, leafId)),
106
- move: (movingId, targetId, side) =>
107
- record(() => moveLeaf(layout.value, movingId, targetId, side)),
155
+ move: controller.move,
108
156
  // Resize fires continuously during a divider drag; recording every frame
109
- // would flood history, so it mutates without a snapshot. (A consumer that
110
- // wants resize in history can wrap a single record() around the gesture.)
111
- resize: (parent, index, delta) => resizeAt(parent, index, delta),
157
+ // would flood history. The canvas brackets its gestures with
158
+ // beginResize/endResize, which records the whole drag as one step.
159
+ resize: controller.resize,
160
+ beginResize: controller.beginResize,
161
+ endResize: controller.endResize,
112
162
  undo: () => {
113
163
  const prev = past.value.pop()
114
164
  if (prev === undefined) return
package/src/nuxt.ts CHANGED
@@ -11,28 +11,71 @@
11
11
  *
12
12
  * Options (nuxt.config `leavepulseUi`):
13
13
  * theme false to skip the theme plugin entirely (default: true)
14
- * defaultTheme preset name used when the visitor has no saved theme
14
+ * defaultTheme theme name used when the visitor has no saved theme
15
+ * themes path of a module whose default export is the app's own
16
+ * TokenSet[] (e.g. "~/themes"), so SSR can paint and fall back
17
+ * to custom themes, not only the built-in presets
18
+ * link false to skip providing NuxtLink as the kit's link component
19
+ * exclude component names not to auto-register (e.g. ["LayoutCanvas"]
20
+ * when the app has its own component of that name)
21
+ * icons which lucide icons render in the server pass and ship in the
22
+ * main bundle: "scan" (default) finds "lucide:*" literals in the
23
+ * app's source, "all" registers the whole set, "kit" only the
24
+ * kit's own, or a list of names. Anything else still renders,
25
+ * from a lazily loaded chunk of the bundled collection.
15
26
  */
16
- import { addComponent, addPlugin, createResolver, defineNuxtModule } from "@nuxt/kit"
27
+ import { readFileSync } from "node:fs"
28
+ import { createRequire } from "node:module"
29
+ import { addComponent, addPlugin, addPluginTemplate, addTemplate, createResolver, defineNuxtModule } from "@nuxt/kit"
17
30
  import { COMPONENT_NAMES } from "./component-names"
31
+ import { namesInDirs, subset } from "./iconSubset.mjs"
18
32
 
19
33
  export interface LeavepulseUiOptions {
20
34
  /** Register the SSR theme plugin. Disable to wire theming yourself. */
21
35
  theme?: boolean
22
- /** Preset name to fall back to when nothing is saved (default "LeavePulse"). */
36
+ /** Theme name to fall back to when nothing is saved (default "Dark"). */
23
37
  defaultTheme?: string
38
+ /** Module path whose default export is the app's TokenSet[]. */
39
+ themes?: string
40
+ /** Provide NuxtLink as the link component for kit navigation (default true). */
41
+ link?: boolean
42
+ /** Component names to leave unregistered. */
43
+ exclude?: string[]
44
+ /** Lucide icons registered up front (default "scan"). */
45
+ icons?: "scan" | "all" | "kit" | string[]
46
+ }
47
+
48
+ function iconsPlugin(mode: LeavepulseUiOptions["icons"], srcDir: string): string {
49
+ if (mode === "kit") return ""
50
+ if (mode === "all") return 'import "@leavepulse/ui/icons-all"\n'
51
+ const lucide = JSON.parse(readFileSync(createRequire(import.meta.url).resolve("@iconify-json/lucide/icons.json"), "utf8"))
52
+ const names = Array.isArray(mode) ? mode.map((n) => n.replace(/^lucide:/, "")) : namesInDirs([srcDir])
53
+ return `import { registerIconCollection } from "@leavepulse/ui"\nregisterIconCollection(${JSON.stringify(subset(lucide, names))})\n`
24
54
  }
25
55
 
26
56
  export default defineNuxtModule<LeavepulseUiOptions>({
27
57
  meta: { name: "@leavepulse/ui", configKey: "leavepulseUi" },
28
- defaults: { theme: true },
29
- setup(options: LeavepulseUiOptions, nuxt: { options: { runtimeConfig: { public: Record<string, unknown> } } }) {
58
+ defaults: { theme: true, link: true, icons: "scan" },
59
+ setup(
60
+ options: LeavepulseUiOptions,
61
+ nuxt: {
62
+ options: {
63
+ runtimeConfig: { public: Record<string, unknown> }
64
+ alias: Record<string, string>
65
+ srcDir: string
66
+ }
67
+ hook: (
68
+ name: "prepare:types",
69
+ fn: (options: { references: { path: string }[] }) => void,
70
+ ) => void
71
+ },
72
+ ) {
73
+ const resolver = createResolver(import.meta.url)
74
+ const skip = new Set(options.exclude ?? [])
30
75
  for (const name of COMPONENT_NAMES) {
31
- // Point each component at its own dist chunk rather than the barrel
32
- // ("@leavepulse/ui"). Importing through the barrel makes the bundler pull
33
- // the whole kit; a per-component path lets it ship only the components an
34
- // app actually uses (plus their real deps — e.g. @vue-flow only when a
35
- // canvas component is used).
76
+ if (skip.has(name)) continue
77
+ // Each component's own dist chunk rather than the barrel, so the app
78
+ // ships only the components it uses (and their real deps).
36
79
  addComponent({
37
80
  name,
38
81
  export: "default",
@@ -40,18 +83,45 @@ export default defineNuxtModule<LeavepulseUiOptions>({
40
83
  })
41
84
  }
42
85
 
86
+ const icons = iconsPlugin(options.icons, nuxt.options.srcDir)
87
+ if (icons) {
88
+ addPluginTemplate({
89
+ filename: "leavepulse-ui-icons.mjs",
90
+ mode: "all",
91
+ getContents: () => `${icons}import { defineNuxtPlugin } from "#app"\nexport default defineNuxtPlugin(() => {})\n`,
92
+ })
93
+ }
94
+
95
+ if (options.link !== false) {
96
+ addPlugin({ src: resolver.resolve("./runtime/config.plugin"), mode: "all" })
97
+ }
98
+
43
99
  if (options.theme === false) return
44
100
 
45
- // The plugin reads its options from runtimeConfig rather than closing over
46
- // them, so they survive the build into the server bundle.
101
+ const themes = addTemplate({
102
+ filename: "leavepulse-ui-themes.mjs",
103
+ getContents: () =>
104
+ options.themes
105
+ ? `export { default } from ${JSON.stringify(options.themes)}\n`
106
+ : "export default []\n",
107
+ })
108
+ nuxt.options.alias["#lp-ui-themes"] = themes.dst
109
+ const themeTypes = addTemplate({
110
+ filename: "types/leavepulse-ui-themes.d.ts",
111
+ getContents: () =>
112
+ 'declare module "#lp-ui-themes" {\n const themes: import("@leavepulse/ui").TokenSet[]\n export default themes\n}\n',
113
+ })
114
+ nuxt.hook("prepare:types", ({ references }) => {
115
+ references.push({ path: themeTypes.dst })
116
+ })
117
+
118
+ // Read from runtimeConfig rather than closed over, so the options survive
119
+ // the build into the server bundle.
47
120
  nuxt.options.runtimeConfig.public.leavepulseUi = {
48
- ...(nuxt.options.runtimeConfig.public.leavepulseUi ?? {}),
121
+ ...((nuxt.options.runtimeConfig.public.leavepulseUi as Record<string, unknown> | undefined) ?? {}),
49
122
  defaultTheme: options.defaultTheme,
50
123
  }
51
124
 
52
- addPlugin({
53
- src: createResolver(import.meta.url).resolve("./runtime/theme.plugin"),
54
- mode: "all",
55
- })
125
+ addPlugin({ src: resolver.resolve("./runtime/theme.plugin"), mode: "all" })
56
126
  },
57
127
  })
@@ -0,0 +1,14 @@
1
+ /*
2
+ * Nuxt runtime plugin: provides the kit configuration app-wide, with NuxtLink
3
+ * as the link component, so navigation pieces (LpLink, LpButton with `to`,
4
+ * LpBreadcrumbs, LpSidebar items) navigate client-side without an `as` on
5
+ * every call site. Registered by the kit's Nuxt module.
6
+ */
7
+ import { LP_CONFIG, type LpConfig } from "@leavepulse/ui"
8
+ import { NuxtLink } from "#components"
9
+ import { defineNuxtPlugin } from "#imports"
10
+
11
+ export default defineNuxtPlugin((nuxtApp) => {
12
+ const config: LpConfig = { linkComponent: NuxtLink }
13
+ nuxtApp.vueApp.provide(LP_CONFIG, config)
14
+ })
@@ -1,54 +1,74 @@
1
1
  /*
2
- * Nuxt runtime plugin — paints the user's theme during SSR, so a reload shows
2
+ * Nuxt runtime plugin: paints the user's theme during SSR, so a reload shows
3
3
  * the right colours in the first frame instead of flashing the default.
4
4
  *
5
- * Registered automatically by the kit's Nuxt module; a consuming app needs no
6
- * plugin of its own. Opt out (or point it at custom themes) via nuxt.config:
5
+ * Registered automatically by the kit's Nuxt module. Options (nuxt.config):
7
6
  *
8
7
  * leavepulseUi: { theme: false } // no theme handling at all
9
- * leavepulseUi: { defaultTheme: "Nord" } // different fallback preset
8
+ * leavepulseUi: { defaultTheme: "Nord" } // different fallback theme
9
+ * leavepulseUi: { themes: "~/themes" } // module exporting the app's own themes
10
10
  *
11
- * How it avoids the flash: the kit mirrors the chosen theme's NAME into the
12
- * `lp-theme` cookie (see cacheTheme), which unlike localStorage travels with the
13
- * request. Here the server reads it, resolves it against the built-in presets,
14
- * and inlines that theme's CSS variables into <head>. On the client
15
- * bootstrapTheme() still runs before mount, so a localStorage choice — which
16
- * holds the full token values, including themes the server cannot name — keeps
17
- * winning.
11
+ * The kit mirrors the chosen theme's NAME into the `lp-theme` cookie, which
12
+ * unlike localStorage travels with the request. The server resolves it against
13
+ * the built-in presets plus the app's themes and inlines that theme's variables
14
+ * into <head>. On the client useTheme().init() still restores localStorage,
15
+ * which holds the full token values and keeps winning.
18
16
  */
19
17
  // Through the barrel, not "../theme/…": this file is compiled by the consuming
20
18
  // app, where the barrel resolves to the same dist instance the components use.
21
- // A relative path would load a second copy of the theme singleton.
22
19
  import {
23
- bootstrapTheme,
24
- leavepulse,
20
+ dark,
25
21
  presets,
26
22
  THEME_COOKIE,
27
23
  themeFromCookie,
28
24
  themeToCssRule,
25
+ useTheme,
29
26
  type TokenSet,
30
27
  } from "@leavepulse/ui"
31
- import { defineNuxtPlugin, useCookie, useHead, useRuntimeConfig } from "#imports"
28
+ import appThemes from "#lp-ui-themes"
29
+ import {
30
+ defineNuxtPlugin,
31
+ useCookie,
32
+ useHead,
33
+ useRequestEvent,
34
+ useRequestHeaders,
35
+ useRuntimeConfig,
36
+ } from "#imports"
37
+
38
+ const HINT = "sec-ch-prefers-color-scheme"
32
39
 
33
40
  export default defineNuxtPlugin(() => {
34
41
  const options = (useRuntimeConfig().public.leavepulseUi ?? {}) as {
35
42
  defaultTheme?: string
36
43
  }
37
44
 
38
- const themes = Object.values(presets) as TokenSet[]
39
- const fallback =
40
- themes.find((theme) => theme.name === options.defaultTheme) ?? leavepulse
45
+ const themes = [...(appThemes as TokenSet[]), ...(Object.values(presets) as TokenSet[])]
46
+ const fallback = themes.find((theme) => theme.name === options.defaultTheme) ?? dark
41
47
 
42
- const cookie = useCookie<string | null>(THEME_COOKIE, { readonly: true })
43
- const initial = themeFromCookie(cookie.value, themes) ?? fallback
48
+ // Raw value: themeFromCookie decodes safely itself, a throwing decode here
49
+ // would turn a malformed cookie into a 500 on every request.
50
+ const cookie = useCookie<string | null>(THEME_COOKIE, {
51
+ readonly: true,
52
+ decode: (value: string) => value,
53
+ })
54
+ let prefersDark: boolean | undefined
55
+ if (import.meta.server) {
56
+ const hint = useRequestHeaders([HINT])[HINT]
57
+ if (hint) prefersDark = hint.includes("dark")
58
+ // Ask the browser for the OS colour scheme from the next request on, so a
59
+ // visitor following the OS gets the right theme server-side as well.
60
+ useRequestEvent()?.node.res.setHeader("Accept-CH", "Sec-CH-Prefers-Color-Scheme")
61
+ }
62
+ const initial = themeFromCookie(cookie.value, themes, { prefersDark }) ?? fallback
44
63
 
45
64
  useHead({
65
+ htmlAttrs: { "data-theme-mode": initial.mode },
46
66
  style: [{ id: "lp-ui-theme", innerHTML: themeToCssRule(initial) }],
47
67
  })
48
68
 
49
- // `initial` as the fallback (not the preset default) so a visitor who only
50
- // has the cookie keeps the theme the server just painted.
69
+ // `initial` as the fallback, so a visitor who only has the cookie keeps the
70
+ // theme the server just painted and the shared state starts from it.
51
71
  if (import.meta.client) {
52
- bootstrapTheme(initial)
72
+ useTheme().init(initial)
53
73
  }
54
74
  })
@@ -49,3 +49,72 @@ export function detectBackdropFilter(
49
49
  root.toggleAttribute(NO_BACKDROP_ATTR, !usable)
50
50
  return usable
51
51
  }
52
+
53
+ /*
54
+ * Glass without backdrop-filter. Where the engine cannot blur what lies under
55
+ * a panel (the flag above), it can still blur an ordinary element, so a panel
56
+ * can be frosted by painting a blurred copy of what is under it, as long as
57
+ * that is known. In an app that paints its own wallpaper (a launcher, a shell)
58
+ * it is: the consumer hands the wallpaper over as a CSS `background` value,
59
+ * and every `.lp-glass-overlay` draws it, blurred and aligned to the viewport,
60
+ * beneath its tint. Live content under the panel stays sharp; the wallpaper,
61
+ * which is most of what glass shows, reads as frosted.
62
+ *
63
+ * The copy is placed by the panel's position on screen, which overlays animate
64
+ * and drawers drag, so while any glass panel is mounted its offset is kept in
65
+ * CSS variables (`--lp-gx`, `--lp-gy`) once a frame. Nothing runs where the
66
+ * real blur works or no backdrop was given.
67
+ */
68
+ export const GLASS_BACKDROP_ATTR = "data-lp-glass-backdrop"
69
+
70
+ let tracking = 0
71
+ let observer: MutationObserver | undefined
72
+
73
+ function trackPanels() {
74
+ const panels = document.querySelectorAll<HTMLElement>(".lp-glass-overlay")
75
+ for (const panel of panels) {
76
+ const rect = panel.getBoundingClientRect()
77
+ const x = `${Math.round(rect.left)}px`
78
+ const y = `${Math.round(rect.top)}px`
79
+ if (panel.style.getPropertyValue("--lp-gx") !== x) panel.style.setProperty("--lp-gx", x)
80
+ if (panel.style.getPropertyValue("--lp-gy") !== y) panel.style.setProperty("--lp-gy", y)
81
+ }
82
+ tracking = panels.length ? requestAnimationFrame(trackPanels) : 0
83
+ }
84
+
85
+ function startTracking() {
86
+ if (!tracking && document.querySelector(".lp-glass-overlay")) tracking = requestAnimationFrame(trackPanels)
87
+ }
88
+
89
+ /**
90
+ * Give glass panels a wallpaper to frost where backdrop-filter does not work:
91
+ * `setGlassBackdrop("url(/bg.jpg) center / cover no-repeat, #0b0f14")`, the
92
+ * same value the app paints behind itself. `filter` replaces the default blur,
93
+ * e.g. `"none"` for a wallpaper that was blurred ahead of time, which is
94
+ * cheaper on an engine that blurs on the CPU. `null` turns it off.
95
+ */
96
+ export function setGlassBackdrop(
97
+ background: string | null,
98
+ options: { filter?: string; root?: HTMLElement } = {},
99
+ ): void {
100
+ if (typeof document === "undefined") return
101
+ const root = options.root ?? document.documentElement
102
+ if (!background) {
103
+ root.removeAttribute(GLASS_BACKDROP_ATTR)
104
+ root.style.removeProperty("--lp-glass-backdrop")
105
+ root.style.removeProperty("--lp-glass-backdrop-filter")
106
+ observer?.disconnect()
107
+ observer = undefined
108
+ return
109
+ }
110
+ root.setAttribute(GLASS_BACKDROP_ATTR, "")
111
+ root.style.setProperty("--lp-glass-backdrop", background)
112
+ if (options.filter) root.style.setProperty("--lp-glass-backdrop-filter", options.filter)
113
+ else root.style.removeProperty("--lp-glass-backdrop-filter")
114
+ if (!root.hasAttribute(NO_BACKDROP_ATTR) || typeof MutationObserver === "undefined") return
115
+ if (!observer) {
116
+ observer = new MutationObserver(startTracking)
117
+ observer.observe(document.body, { childList: true, subtree: true })
118
+ }
119
+ startTracking()
120
+ }
@@ -1,4 +1,4 @@
1
- import type { SurfaceTokens, TokenSet } from "./tokens"
1
+ import type { TokenSet } from "./tokens"
2
2
 
3
3
  /*
4
4
  * Built-in theme presets — plain TokenSet objects (JSON). These ARE the themes;
@@ -14,13 +14,12 @@ const interFont = {
14
14
  }
15
15
 
16
16
  /*
17
- * NOTE on naming: the exported CONST names are frozen for compatibility (apps
18
- * import `dark` / `leavepulse` directly), but the display `name` changed —
19
- * `leavepulse` is now the one called "Dark" (it is the product's dark theme),
20
- * and this near-black neutral became "Onyx" to free that label up.
17
+ * The kit's dark theme, a near-black neutral. It was displayed as "Onyx" while
18
+ * the LeavePulse site theme held the "Dark" label; with that theme gone the
19
+ * label is back here.
21
20
  */
22
21
  export const dark: TokenSet = {
23
- name: "Onyx",
22
+ name: "Dark",
24
23
  mode: "dark",
25
24
  colors: {
26
25
  surface: "#080b0d",
@@ -120,63 +119,6 @@ export const lime: TokenSet = {
120
119
  },
121
120
  }
122
121
 
123
- /*
124
- * Glass skin — frosted panels with backdrop blur, a faint top-down highlight
125
- * overlay, a layered drop shadow and an inset highlight line. Opt a theme into
126
- * it by setting its `surface` axis to this (the LeavePulse site uses it).
127
- */
128
- const glassSurface: SurfaceTokens = {
129
- borderWidth: "1px",
130
- panelBlur: "10px",
131
- panelSaturate: "120%",
132
- panelOverlay:
133
- "linear-gradient(180deg, rgba(255, 255, 255, 0.06), rgba(255, 255, 255, 0.01))",
134
- panelShadow: "0 20px 60px rgba(0, 0, 0, 0.35)",
135
- panelInset: "inset 0 1px 0 rgba(255, 255, 255, 0.08)",
136
- }
137
-
138
- /*
139
- * LeavePulse site theme — the colours the website shipped before the kit
140
- * migration (deep navy surfaces, cyan brand, teal accent), with the glass skin.
141
- * Distinct from `dark` (which is a near-black neutral) so the site keeps its
142
- * look when it switches its CSS-var dictionary over to the kit.
143
- */
144
- export const leavepulse: TokenSet = {
145
- name: "Dark",
146
- mode: "dark",
147
- colors: {
148
- surface: "#020618",
149
- surfaceRaised: "#0b1222",
150
- surfaceSoft: "rgba(11, 18, 34, 0.7)",
151
- ink: "#f3f6ff",
152
- inkInverse: "#0b1222",
153
- muted: "#94a3bf",
154
- mutedStrong: "#cad5e2",
155
- brand: "#00bcff",
156
- brandHover: "#19c3ff",
157
- brandSoft: "rgba(0, 188, 255, 0.2)",
158
- action: "#44c2ad",
159
- actionHover: "#56d3be",
160
- actionSoft: "rgba(68, 194, 173, 0.14)",
161
- accent: "#d36a2c",
162
- accentHover: "#da824e",
163
- accentSoft: "rgba(211, 106, 44, 0.14)",
164
- danger: "#f87171",
165
- dangerHover: "#fca5a5",
166
- dangerSoft: "rgba(248, 113, 113, 0.14)",
167
- line: "#1c2740",
168
- lineStrong: "rgba(255, 255, 255, 0.15)",
169
- ring: "rgba(0, 188, 255, 0.35)",
170
- },
171
- shape: { ...sharedShape, radiusControl: 12 },
172
- density: { ...sharedDensity },
173
- font: {
174
- sans: '"Inter", "IBM Plex Sans", "Segoe UI", sans-serif',
175
- mono: '"JetBrains Mono", "SF Mono", ui-monospace, monospace',
176
- },
177
- surface: glassSurface,
178
- }
179
-
180
122
  export const rose: TokenSet = {
181
123
  name: "Rose",
182
124
  mode: "dark",
@@ -305,5 +247,5 @@ export const nord: TokenSet = {
305
247
  font: { ...interFont },
306
248
  }
307
249
 
308
- export const presets = { dark, light, lime, leavepulse, rose, amber, violet, nord } as const
250
+ export const presets = { dark, light, lime, rose, amber, violet, nord } as const
309
251
  export type PresetName = keyof typeof presets