@leavepulse/ui 0.69.0 → 0.70.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (634) hide show
  1. package/README.md +37 -0
  2. package/dist/assets/LoaderMark-DxBV2iDJ.css +43 -0
  3. package/dist/assets/LpActivityLine-B3EwNQEB.css +33 -0
  4. package/dist/assets/{LpCommandPalette-DxyjhYn_.css → LpCommandPalette-u-zOxKWa.css} +21 -31
  5. package/dist/assets/LpDock-Cbh0wQUv.css +17 -0
  6. package/dist/assets/{LpDropzone-Qb8k7cg4.css → LpDropzone-Bb1vBTeU.css} +12 -17
  7. package/dist/assets/{LpImageEditor-CElPDzpF.css → LpImageEditor-XM5bAziW.css} +6 -6
  8. package/dist/assets/LpLogViewer-BDNW6P52.css +38 -0
  9. package/dist/assets/{LpPalettePreview-CJmAKgYW.css → LpPalettePreview-B9VKDrvA.css} +2 -7
  10. package/dist/assets/LpShinyText-D69cBYDR.css +61 -0
  11. package/dist/assets/LpStatus-430OGTZY.css +17 -0
  12. package/dist/assets/LpStepMark-Drvx33Vg.css +10 -0
  13. package/dist/assets/{LpTable-BPERfJIk.css → LpTable-DHxEQ_-3.css} +19 -19
  14. package/dist/assets/LpTabs-BJ-nrJuI.css +18 -0
  15. package/dist/assets/LpTextReveal-DjI1MsDS.css +35 -0
  16. package/dist/assets/LpTypewriter-CXAm9Nca.css +27 -0
  17. package/dist/chunks/AttentionDot-DCBf4aWg.js +25 -0
  18. package/dist/chunks/{ContextMenuItems-B7XFHksm.js → ContextMenuItems-B5uE8Znw.js} +1 -1
  19. package/dist/chunks/{DropzoneLinkField-CjkrpDqO.js → DropzoneLinkField-Bw8Flkhk.js} +2 -2
  20. package/dist/chunks/FieldWrapper-BiennROB.js +43 -0
  21. package/dist/chunks/{LayoutCanvas-DF9CovaK.js → LayoutCanvas-BsJIzhdd.js} +1 -1
  22. package/dist/chunks/{LayoutNode-D9bIFzk0.js → LayoutNode-DJVhkSGk.js} +6 -8
  23. package/dist/chunks/LegendSwatch-BhRjrtzv.js +18 -0
  24. package/dist/chunks/LoaderMark-C1dubiKn.js +85 -0
  25. package/dist/chunks/{LpActionBar-MPKMu3pz.js → LpActionBar-CzOFar5t.js} +14 -20
  26. package/dist/chunks/LpActivityLine-CJvzrNpj.js +95 -0
  27. package/dist/chunks/{LpAddressInput-DG0BTqdH.js → LpAddressInput-CNnLTDZg.js} +7 -7
  28. package/dist/chunks/LpAlert-C5igj802.js +119 -0
  29. package/dist/chunks/{LpAppShell-DPh1756q.js → LpAppShell-CEUAB8kK.js} +15 -16
  30. package/dist/chunks/{LpAutocomplete-Dgn6hw3p.js → LpAutocomplete-D92niDHE.js} +15 -32
  31. package/dist/chunks/{LpAvatar-Bs0Fb1lC.js → LpAvatar-CLPl-qOF.js} +18 -6
  32. package/dist/chunks/LpBadge-HOEPBYmi.js +126 -0
  33. package/dist/chunks/LpBreadcrumbs-DYoPc3P4.js +144 -0
  34. package/dist/chunks/{LpButton-DNR-YAq5.js → LpButton-CVxBfGjd.js} +12 -4
  35. package/dist/chunks/{LpCalendar-BLya39Fh.js → LpCalendar-pVL91-Pl.js} +1 -1
  36. package/dist/chunks/{LpCard-Bf71NXZq.js → LpCard-CqKLnEhl.js} +18 -8
  37. package/dist/chunks/{LpCheckbox-Da5oZPHo.js → LpCheckbox-DHc2bHDe.js} +9 -6
  38. package/dist/chunks/{LpCodeBlock-lLbmslfs.js → LpCodeBlock-BGsQhxw8.js} +24 -32
  39. package/dist/chunks/LpCodeDisplay-CIKKT2iW.js +189 -0
  40. package/dist/chunks/LpCollapse-CWS9M_lm.js +115 -0
  41. package/dist/chunks/{LpColorPicker-DtEfBAcK.js → LpColorPicker-DFugXNUi.js} +10 -5
  42. package/dist/chunks/{LpConfirmDialog-D7s4jakB.js → LpConfirmDialog-CfGCux8m.js} +3 -3
  43. package/dist/chunks/{LpConfirmHost-DrYI5y1K.js → LpConfirmHost-exB7JmcO.js} +1 -1
  44. package/dist/chunks/{LpContextMenu-CRnjoqSt.js → LpContextMenu-COwOwRgI.js} +13 -5
  45. package/dist/chunks/LpCopyButton-DzHjnFs0.js +133 -0
  46. package/dist/chunks/LpCountdown-Dtq1ihlJ.js +231 -0
  47. package/dist/chunks/LpDataList-B-5nHfNw.js +120 -0
  48. package/dist/chunks/LpDatePicker-BUe1VUC0.js +168 -0
  49. package/dist/chunks/LpDateRangePicker-C_k65vGR.js +126 -0
  50. package/dist/chunks/{LpDisclosure-6AplYBub.js → LpDisclosure-DOarzlAG.js} +22 -11
  51. package/dist/chunks/{LpDrawer-Q4SOxM4s.js → LpDrawer-DGNKRsGq.js} +9 -4
  52. package/dist/chunks/{LpDropdownMenu-DWTt0ooM.js → LpDropdownMenu-CRgrhdAH.js} +10 -5
  53. package/dist/chunks/LpEditable-DTqLt2lT.js +236 -0
  54. package/dist/chunks/{LpEmptyState-om1UnFjV.js → LpEmptyState-KTKipws2.js} +1 -1
  55. package/dist/chunks/{LpFieldNotice-CHuDeLjc.js → LpFieldNotice-CurbNuXI.js} +1 -1
  56. package/dist/chunks/{LpFileTree-CW24YsVX.js → LpFileTree-DMISXVHC.js} +5 -3
  57. package/dist/chunks/{LpFileTreeNode-BXhTHaKW.js → LpFileTreeNode-CPXB_Yy3.js} +42 -55
  58. package/dist/chunks/{LpFormField-DPb47HEm.js → LpFormField-BWwgm1E4.js} +23 -9
  59. package/dist/chunks/LpFrame-BVQCQLDI.js +155 -0
  60. package/dist/chunks/LpGauge-Bk8TwJj0.js +204 -0
  61. package/dist/chunks/LpGlowBorder-B8V-QzIl.js +40 -0
  62. package/dist/chunks/{LpHintExample-C1eKYdeL.js → LpHintExample-9Py0wc-L.js} +11 -34
  63. package/dist/chunks/{LpHintLayer-7UOdWQG3.js → LpHintLayer-p4hqzdyA.js} +27 -14
  64. package/dist/chunks/{LpHints-BUtBp9-w.js → LpHints-CCrhRfy9.js} +3 -3
  65. package/dist/chunks/LpHoverCard-spGxyw7T.js +130 -0
  66. package/dist/chunks/{LpIcon-6tSrZ3Zy.js → LpIcon-HJP5xMz4.js} +1 -1
  67. package/dist/chunks/LpIdenticon-BzdC_qz9.js +63 -0
  68. package/dist/chunks/{LpInfraNode-Ds_8SZgN.js → LpInfraNode-CUC3t4na.js} +39 -25
  69. package/dist/chunks/{LpInput-CUyr6PIE.js → LpInput-CDuwODJG.js} +44 -18
  70. package/dist/chunks/{LpLightbox-B9J8rzsj.js → LpLightbox-XJ0LpTcZ.js} +69 -69
  71. package/dist/chunks/LpLoader-DjJEX8hL.js +92 -0
  72. package/dist/chunks/{LpLogViewer-4QFc-sVN.js → LpLogViewer-elZHbj85.js} +225 -129
  73. package/dist/chunks/LpMagnetic-CtXKLa2Y.js +62 -0
  74. package/dist/chunks/{LpMapPicker-BTbkFeSG.js → LpMapPicker-BtIl7NOM.js} +34 -31
  75. package/dist/chunks/LpMasonry--k0571qG.js +185 -0
  76. package/dist/chunks/{LpMediaGallery-CuPcscx3.js → LpMediaGallery-D5swlmZ_.js} +3 -3
  77. package/dist/chunks/{LpModal-hk1noDcr.js → LpModal-eBMt2Fqn.js} +8 -3
  78. package/dist/chunks/{LpModalNav-DQd2keZm.js → LpModalNav-D4Z0wVHR.js} +4 -4
  79. package/dist/chunks/{LpNotificationBell-X84PJIxD.js → LpNotificationBell-DPZmP5dM.js} +32 -48
  80. package/dist/chunks/LpNumberField-B9k-h2HE.js +140 -0
  81. package/dist/chunks/{LpNumberFlow-z4WPZcZu.js → LpNumberFlow-Cfyh033Q.js} +8 -28
  82. package/dist/chunks/{LpOtpInput-PbeNJ5In.js → LpOtpInput-CeBPsM7D.js} +2 -1
  83. package/dist/chunks/{LpPagination-Bx-VG4Ag.js → LpPagination-VNCX6pFC.js} +37 -6
  84. package/dist/chunks/{LpPalettePreview-CFav_7co.js → LpPalettePreview-BNB8T_MY.js} +1 -1
  85. package/dist/chunks/{LpPasswordInput-CbnXitAS.js → LpPasswordInput-DTV18FIh.js} +20 -20
  86. package/dist/chunks/{LpPhoneInput-DP69HONS.js → LpPhoneInput-C60tx1tC.js} +12 -7
  87. package/dist/chunks/{LpPie-yLF9-aDR.js → LpPie-CFuV7pZv.js} +158 -47
  88. package/dist/chunks/LpPipeline-D81yh_NX.js +329 -0
  89. package/dist/chunks/{LpPopover-DVnkHaOu.js → LpPopover-BEIcqb9p.js} +8 -3
  90. package/dist/chunks/{LpProgress-CA86wxXu.js → LpProgress-CE-m5OpD.js} +23 -63
  91. package/dist/chunks/LpQuota-2Sw_gqcJ.js +162 -0
  92. package/dist/chunks/{LpRadioGroup-8S5hf8lE.js → LpRadioGroup-CGBr5nIp.js} +1 -1
  93. package/dist/chunks/{LpRangeCalendar-ByQi-WUz.js → LpRangeCalendar-c5qj7YcC.js} +1 -1
  94. package/dist/chunks/{LpRangeSlider-C1yF9-nP.js → LpRangeSlider-Dv6lNvHY.js} +6 -2
  95. package/dist/chunks/LpRelativeTime-CKzVqAo9.js +73 -0
  96. package/dist/chunks/LpRepeater-D0VjmD6y.js +140 -0
  97. package/dist/chunks/LpRevealOnScroll-DcQjexLy.js +76 -0
  98. package/dist/chunks/LpScramble-TDO5Kdou.js +85 -0
  99. package/dist/chunks/LpSecretField-DMfdJ1cQ.js +307 -0
  100. package/dist/chunks/{LpSection-BmRXe6ZP.js → LpSection-BW4xJ-Li.js} +1 -1
  101. package/dist/chunks/{LpSegmented-CW3bFHM4.js → LpSegmented-BO99Sr4m.js} +11 -14
  102. package/dist/chunks/{LpSelect-BCyDYoih.js → LpSelect-B4f_yRi5.js} +93 -108
  103. package/dist/chunks/LpShaderBackdrop-Bote0iuy.js +332 -0
  104. package/dist/chunks/LpShinyText-DX2ajtWq.js +48 -0
  105. package/dist/chunks/{LpSidebar-DfIiS-lB.js → LpSidebar-D-M0Y_7U.js} +5 -3
  106. package/dist/chunks/{LpSidebarNav-C5FDNI0t.js → LpSidebarNav-DF-TYxOV.js} +3 -3
  107. package/dist/chunks/{LpSlider-Cuzmrd7p.js → LpSlider-DuoRpOrH.js} +6 -2
  108. package/dist/chunks/{LpSparkline-DXT4KrSi.js → LpSparkline-CExIRDb3.js} +32 -27
  109. package/dist/chunks/LpSpotlight-4F3NeUMY.js +41 -0
  110. package/dist/chunks/{LpStat-iBMF5QNk.js → LpStat-B2ix1Q7t.js} +38 -35
  111. package/dist/chunks/LpStatus-C7kx5PKE.js +86 -0
  112. package/dist/chunks/LpStepMark-DK-5BKAt.js +101 -0
  113. package/dist/chunks/LpStepper-DRFfdJaq.js +76 -0
  114. package/dist/chunks/{LpSwap-D82jiecV.js → LpSwap-DzE6M3Mg.js} +1 -1
  115. package/dist/chunks/LpSwitch-DrzKrZZV.js +112 -0
  116. package/dist/chunks/{LpTableFilter-CuIbgdW0.js → LpTableFilter-DyvHGbOk.js} +3 -2
  117. package/dist/chunks/{LpTableOfContents-C_4VBktT.js → LpTableOfContents-rYvo8z5T.js} +34 -21
  118. package/dist/chunks/{LpTagsInput-DiXrpYeE.js → LpTagsInput-DgpBFRV2.js} +68 -71
  119. package/dist/chunks/LpTextReveal-CtAnOUkp.js +140 -0
  120. package/dist/chunks/{LpTextarea-xoQd5Lnp.js → LpTextarea-DiWjeVoJ.js} +12 -11
  121. package/dist/chunks/{LpThemeProvider-CQ2jC9TA.js → LpThemeProvider-DcbfOxsU.js} +1 -1
  122. package/dist/chunks/LpThemeStudio-DguwHdGt.js +416 -0
  123. package/dist/chunks/LpThemeStudioSection-BwhqYDLf.js +20 -0
  124. package/dist/chunks/LpThemeSwitcher-D5uz0MhK.js +278 -0
  125. package/dist/chunks/{LpTimeField-MFgAsNc5.js → LpTimeField-BFQVf_PW.js} +13 -5
  126. package/dist/chunks/{LpToaster-C4zE9zB7.js → LpToaster-Wv9ksj4q.js} +49 -82
  127. package/dist/chunks/{LpTooltip-suFCM34F.js → LpTooltip-Cz8SQwRt.js} +8 -3
  128. package/dist/chunks/{LpTopologyCanvas-6ozNrQKh.js → LpTopologyCanvas-ZhKthfgz.js} +1 -1
  129. package/dist/chunks/{LpTour-BUp3Jt2e.js → LpTour-NfRJNEIP.js} +24 -18
  130. package/dist/chunks/LpTruncate-D_5Pfv5B.js +112 -0
  131. package/dist/chunks/LpTypewriter-BCL_a6wE.js +159 -0
  132. package/dist/chunks/{LpUptimeBar-B6kHw4Cj.js → LpUptimeBar-DWegV2p6.js} +9 -8
  133. package/dist/chunks/{LpVideoBackdrop-qWRYBOTQ.js → LpVideoBackdrop-DS1wGv-X.js} +7 -8
  134. package/dist/chunks/{MenuTitle-obgek0B7.js → MenuTitle-BjQZX8tQ.js} +1 -1
  135. package/dist/chunks/OptionContent-CmYUUeWj.js +40 -0
  136. package/dist/chunks/PickerField-CHRS1h7K.js +116 -0
  137. package/dist/chunks/RevealToggle-C-KzaQGJ.js +65 -0
  138. package/dist/chunks/StepConnector-37OgEOXA.js +41 -0
  139. package/dist/chunks/TourCard-BXuVHsfn.js +123 -0
  140. package/dist/chunks/backdrop-Dkf2vM81.js +59 -0
  141. package/dist/chunks/{icons-DVhZ_plB.js → icons-4IywzkHk.js} +1 -1
  142. package/dist/chunks/{useTheme-2qHG8NEU.js → useTheme-BOV_-AiY.js} +9 -65
  143. package/dist/component-names.d.ts +1 -1
  144. package/dist/components/AttentionDot.vue.d.ts +9 -0
  145. package/dist/components/AttentionDot.vue.js +4 -0
  146. package/dist/components/ContextMenuItems.vue.js +1 -1
  147. package/dist/components/DropzoneLinkField.vue.js +1 -1
  148. package/dist/components/FieldWrapper.vue.d.ts +25 -0
  149. package/dist/components/FieldWrapper.vue.js +4 -0
  150. package/dist/components/LayoutCanvas.vue.js +1 -1
  151. package/dist/components/LayoutNode.vue.d.ts +4 -4
  152. package/dist/components/LayoutNode.vue.js +1 -1
  153. package/dist/components/LegendSwatch.vue.d.ts +6 -0
  154. package/dist/components/LegendSwatch.vue.js +4 -0
  155. package/dist/components/LoaderMark.vue.d.ts +12 -0
  156. package/dist/components/LoaderMark.vue.js +4 -0
  157. package/dist/components/LpActionBar.vue.js +1 -1
  158. package/dist/components/LpActivityLine.vue.d.ts +58 -0
  159. package/dist/components/LpActivityLine.vue.js +4 -0
  160. package/dist/components/LpAddressInput.vue.js +1 -1
  161. package/dist/components/LpAlert.vue.d.ts +2 -2
  162. package/dist/components/LpAlert.vue.js +1 -1
  163. package/dist/components/LpAppShell.vue.d.ts +8 -7
  164. package/dist/components/LpAppShell.vue.js +1 -1
  165. package/dist/components/LpAutocomplete.vue.js +1 -1
  166. package/dist/components/LpAvatar.vue.d.ts +16 -0
  167. package/dist/components/LpAvatar.vue.js +1 -1
  168. package/dist/components/LpBadge.vue.d.ts +19 -5
  169. package/dist/components/LpBadge.vue.js +1 -1
  170. package/dist/components/LpBreadcrumbs.vue.js +1 -1
  171. package/dist/components/LpButton.vue.d.ts +2 -2
  172. package/dist/components/LpButton.vue.js +1 -1
  173. package/dist/components/LpCalendar.vue.js +1 -1
  174. package/dist/components/LpCard.vue.d.ts +24 -0
  175. package/dist/components/LpCard.vue.js +1 -1
  176. package/dist/components/LpChart.vue.js +107 -138
  177. package/dist/components/LpCheckbox.vue.d.ts +2 -2
  178. package/dist/components/LpCheckbox.vue.js +1 -1
  179. package/dist/components/LpCodeBlock.vue.js +1 -1
  180. package/dist/components/LpCodeDisplay.vue.d.ts +89 -0
  181. package/dist/components/LpCodeDisplay.vue.js +4 -0
  182. package/dist/components/LpCollapse.vue.d.ts +58 -0
  183. package/dist/components/LpCollapse.vue.js +4 -0
  184. package/dist/components/LpColorPicker.vue.d.ts +2 -2
  185. package/dist/components/LpColorPicker.vue.js +1 -1
  186. package/dist/components/LpCommandPalette.vue.js +34 -23
  187. package/dist/components/LpConfirmDialog.vue.js +1 -1
  188. package/dist/components/LpConfirmHost.vue.js +1 -1
  189. package/dist/components/LpContextMenu.vue.js +1 -1
  190. package/dist/components/LpCopyButton.vue.d.ts +82 -0
  191. package/dist/components/LpCopyButton.vue.js +4 -0
  192. package/dist/components/LpCountdown.vue.d.ts +100 -0
  193. package/dist/components/LpCountdown.vue.js +4 -0
  194. package/dist/components/LpDataList.vue.d.ts +66 -0
  195. package/dist/components/LpDataList.vue.js +4 -0
  196. package/dist/components/LpDatePicker.vue.d.ts +1 -1
  197. package/dist/components/LpDatePicker.vue.js +1 -1
  198. package/dist/components/LpDateRangePicker.vue.d.ts +1 -1
  199. package/dist/components/LpDateRangePicker.vue.js +1 -1
  200. package/dist/components/LpDisclosure.vue.js +1 -1
  201. package/dist/components/LpDock.vue.d.ts +74 -0
  202. package/dist/components/LpDock.vue.js +204 -0
  203. package/dist/components/LpDrawer.vue.js +1 -1
  204. package/dist/components/LpDropdownMenu.vue.js +1 -1
  205. package/dist/components/LpDropzone.vue.d.ts +8 -1
  206. package/dist/components/LpDropzone.vue.js +36 -15
  207. package/dist/components/LpEditable.vue.d.ts +85 -0
  208. package/dist/components/LpEditable.vue.js +4 -0
  209. package/dist/components/LpEmptyState.vue.js +1 -1
  210. package/dist/components/LpFieldNotice.vue.js +1 -1
  211. package/dist/components/LpFileTree.vue.js +1 -1
  212. package/dist/components/LpFileTreeNode.vue.js +1 -1
  213. package/dist/components/LpFormField.vue.js +1 -1
  214. package/dist/components/LpFrame.vue.d.ts +22 -0
  215. package/dist/components/LpFrame.vue.js +4 -0
  216. package/dist/components/LpGauge.vue.d.ts +116 -0
  217. package/dist/components/LpGauge.vue.js +4 -0
  218. package/dist/components/LpGlowBorder.vue.d.ts +47 -0
  219. package/dist/components/LpGlowBorder.vue.js +4 -0
  220. package/dist/components/LpHintExample.vue.d.ts +2 -2
  221. package/dist/components/LpHintExample.vue.js +1 -1
  222. package/dist/components/LpHintLayer.vue.js +1 -1
  223. package/dist/components/LpHints.vue.js +1 -1
  224. package/dist/components/LpHoverCard.vue.d.ts +60 -0
  225. package/dist/components/LpHoverCard.vue.js +4 -0
  226. package/dist/components/LpIcon.vue.js +1 -1
  227. package/dist/components/LpIdenticon.vue.d.ts +37 -0
  228. package/dist/components/LpIdenticon.vue.js +4 -0
  229. package/dist/components/LpImageEditor.vue.js +13 -19
  230. package/dist/components/LpInfraNode.vue.js +1 -1
  231. package/dist/components/LpInput.vue.d.ts +10 -0
  232. package/dist/components/LpInput.vue.js +1 -1
  233. package/dist/components/LpLightbox.vue.js +1 -1
  234. package/dist/components/LpLoader.vue.d.ts +106 -0
  235. package/dist/components/LpLoader.vue.js +4 -0
  236. package/dist/components/LpLogViewer.vue.js +1 -1
  237. package/dist/components/LpMagnetic.vue.d.ts +28 -0
  238. package/dist/components/LpMagnetic.vue.js +4 -0
  239. package/dist/components/LpMapPicker.vue.js +1 -1
  240. package/dist/components/LpMasonry.vue.d.ts +42 -0
  241. package/dist/components/LpMasonry.vue.js +4 -0
  242. package/dist/components/LpMediaGallery.vue.js +1 -1
  243. package/dist/components/LpModal.vue.js +1 -1
  244. package/dist/components/LpModalNav.vue.js +1 -1
  245. package/dist/components/LpNotificationBell.vue.d.ts +15 -6
  246. package/dist/components/LpNotificationBell.vue.js +1 -1
  247. package/dist/components/LpNumberField.vue.d.ts +57 -0
  248. package/dist/components/LpNumberField.vue.js +1 -1
  249. package/dist/components/LpNumberFlow.vue.js +1 -1
  250. package/dist/components/LpOtpInput.vue.d.ts +12 -4
  251. package/dist/components/LpOtpInput.vue.js +1 -1
  252. package/dist/components/LpPagination.vue.js +1 -1
  253. package/dist/components/LpPaletteFacts.vue.js +1 -1
  254. package/dist/components/LpPaletteHeader.vue.js +2 -2
  255. package/dist/components/LpPalettePreview.vue.js +1 -1
  256. package/dist/components/LpPaletteStats.vue.js +1 -1
  257. package/dist/components/LpPasswordInput.vue.d.ts +12 -1
  258. package/dist/components/LpPasswordInput.vue.js +1 -1
  259. package/dist/components/LpPhoneInput.vue.js +1 -1
  260. package/dist/components/LpPie.vue.d.ts +3 -1
  261. package/dist/components/LpPie.vue.js +1 -1
  262. package/dist/components/LpPipeline.vue.d.ts +91 -0
  263. package/dist/components/LpPipeline.vue.js +4 -0
  264. package/dist/components/LpPopover.vue.d.ts +2 -2
  265. package/dist/components/LpPopover.vue.js +1 -1
  266. package/dist/components/LpProgress.vue.d.ts +11 -4
  267. package/dist/components/LpProgress.vue.js +1 -1
  268. package/dist/components/LpQuota.vue.d.ts +68 -0
  269. package/dist/components/LpQuota.vue.js +4 -0
  270. package/dist/components/LpRadioGroup.vue.js +1 -1
  271. package/dist/components/LpRangeCalendar.vue.js +1 -1
  272. package/dist/components/LpRangeSlider.vue.js +1 -1
  273. package/dist/components/LpRelativeTime.vue.d.ts +41 -0
  274. package/dist/components/LpRelativeTime.vue.js +4 -0
  275. package/dist/components/LpRepeater.vue.d.ts +48 -0
  276. package/dist/components/LpRepeater.vue.js +4 -0
  277. package/dist/components/LpRevealOnScroll.vue.d.ts +67 -0
  278. package/dist/components/LpRevealOnScroll.vue.js +4 -0
  279. package/dist/components/LpRollingText.vue.d.ts +1 -1
  280. package/dist/components/LpScramble.vue.d.ts +55 -0
  281. package/dist/components/LpScramble.vue.js +4 -0
  282. package/dist/components/LpScrollArea.vue.d.ts +1 -1
  283. package/dist/components/LpSecretField.vue.d.ts +108 -0
  284. package/dist/components/LpSecretField.vue.js +4 -0
  285. package/dist/components/LpSection.vue.js +1 -1
  286. package/dist/components/LpSegmented.vue.js +1 -1
  287. package/dist/components/LpSelect.vue.d.ts +2 -0
  288. package/dist/components/LpSelect.vue.js +1 -1
  289. package/dist/components/LpShaderBackdrop.vue.d.ts +39 -0
  290. package/dist/components/LpShaderBackdrop.vue.js +4 -0
  291. package/dist/components/LpShinyText.vue.d.ts +39 -0
  292. package/dist/components/LpShinyText.vue.js +4 -0
  293. package/dist/components/LpShortcutsDialog.vue.js +1 -1
  294. package/dist/components/LpSidebar.vue.js +1 -1
  295. package/dist/components/LpSidebarNav.vue.js +1 -1
  296. package/dist/components/LpSlider.vue.js +1 -1
  297. package/dist/components/LpSparkline.vue.d.ts +4 -3
  298. package/dist/components/LpSparkline.vue.js +1 -1
  299. package/dist/components/LpSpotlight.vue.d.ts +37 -0
  300. package/dist/components/LpSpotlight.vue.js +4 -0
  301. package/dist/components/LpStat.vue.js +1 -1
  302. package/dist/components/LpStatus.vue.d.ts +65 -0
  303. package/dist/components/LpStatus.vue.js +4 -0
  304. package/dist/components/LpStepMark.vue.d.ts +31 -0
  305. package/dist/components/LpStepMark.vue.js +4 -0
  306. package/dist/components/LpStepper.vue.js +1 -1
  307. package/dist/components/LpSwap.vue.js +1 -1
  308. package/dist/components/LpSwitch.vue.d.ts +2 -2
  309. package/dist/components/LpSwitch.vue.js +1 -1
  310. package/dist/components/LpTable.vue.js +104 -120
  311. package/dist/components/LpTableFilter.vue.js +1 -1
  312. package/dist/components/LpTableOfContents.vue.d.ts +6 -1
  313. package/dist/components/LpTableOfContents.vue.js +1 -1
  314. package/dist/components/LpTabs.vue.d.ts +5 -5
  315. package/dist/components/LpTabs.vue.js +25 -33
  316. package/dist/components/LpTagsInput.vue.js +1 -1
  317. package/dist/components/LpTextReveal.vue.d.ts +58 -0
  318. package/dist/components/LpTextReveal.vue.js +4 -0
  319. package/dist/components/LpTextarea.vue.js +1 -1
  320. package/dist/components/LpThemeProvider.vue.js +1 -1
  321. package/dist/components/LpThemeStudio.vue.d.ts +54 -0
  322. package/dist/components/LpThemeStudio.vue.js +4 -0
  323. package/dist/components/LpThemeStudioSection.vue.d.ts +16 -0
  324. package/dist/components/LpThemeStudioSection.vue.js +4 -0
  325. package/dist/components/LpThemeSwitcher.vue.d.ts +32 -1
  326. package/dist/components/LpThemeSwitcher.vue.js +1 -1
  327. package/dist/components/LpTimeField.vue.d.ts +2 -0
  328. package/dist/components/LpTimeField.vue.js +1 -1
  329. package/dist/components/LpToaster.vue.js +1 -1
  330. package/dist/components/LpTooltip.vue.d.ts +2 -2
  331. package/dist/components/LpTooltip.vue.js +1 -1
  332. package/dist/components/LpTopologyCanvas.vue.js +1 -1
  333. package/dist/components/LpTour.vue.js +1 -1
  334. package/dist/components/LpTruncate.vue.d.ts +34 -0
  335. package/dist/components/LpTruncate.vue.js +4 -0
  336. package/dist/components/LpTypewriter.vue.d.ts +61 -0
  337. package/dist/components/LpTypewriter.vue.js +4 -0
  338. package/dist/components/LpUptimeBar.vue.js +1 -1
  339. package/dist/components/LpVideoBackdrop.vue.js +1 -1
  340. package/dist/components/MenuTitle.vue.js +1 -1
  341. package/dist/components/OptionContent.vue.d.ts +7 -0
  342. package/dist/components/OptionContent.vue.js +4 -0
  343. package/dist/components/PickerField.vue.d.ts +43 -0
  344. package/dist/components/PickerField.vue.js +4 -0
  345. package/dist/components/RevealToggle.vue.d.ts +21 -0
  346. package/dist/components/RevealToggle.vue.js +4 -0
  347. package/dist/components/StepConnector.vue.d.ts +19 -0
  348. package/dist/components/StepConnector.vue.js +4 -0
  349. package/dist/components/TourCard.vue.js +1 -1
  350. package/dist/components/badge.d.ts +1 -0
  351. package/dist/components/badge.js +4 -0
  352. package/dist/components/chart.d.ts +5 -1
  353. package/dist/components/chart.js +2 -21
  354. package/dist/components/dateRange.d.ts +11 -0
  355. package/dist/components/dateRange.js +12 -0
  356. package/dist/components/dock.d.ts +19 -0
  357. package/dist/components/dock.js +16 -0
  358. package/dist/components/dropdown.d.ts +10 -1
  359. package/dist/components/dropdown.js +7 -2
  360. package/dist/components/fieldText.d.ts +38 -0
  361. package/dist/components/fieldText.js +53 -0
  362. package/dist/components/fileTree.d.ts +7 -3
  363. package/dist/components/fileTree.js +12 -18
  364. package/dist/components/format.d.ts +41 -0
  365. package/dist/components/format.js +67 -0
  366. package/dist/components/identicon.d.ts +38 -0
  367. package/dist/components/identicon.js +85 -0
  368. package/dist/components/masonry.d.ts +32 -0
  369. package/dist/components/masonry.js +40 -0
  370. package/dist/components/menuWhen.js +1 -1
  371. package/dist/components/meter.d.ts +64 -0
  372. package/dist/components/meter.js +53 -0
  373. package/dist/components/otp.d.ts +1 -1
  374. package/dist/components/otp.js +7 -2
  375. package/dist/components/pipeline.d.ts +55 -0
  376. package/dist/components/pipeline.js +51 -0
  377. package/dist/components/quota.d.ts +57 -0
  378. package/dist/components/quota.js +40 -0
  379. package/dist/components/relativeTime.d.ts +24 -0
  380. package/dist/components/relativeTime.js +56 -0
  381. package/dist/components/reveal.d.ts +18 -0
  382. package/dist/components/reveal.js +32 -0
  383. package/dist/components/scramble.d.ts +68 -0
  384. package/dist/components/scramble.js +58 -0
  385. package/dist/components/shaderBackdrop.d.ts +20 -0
  386. package/dist/components/shaderBackdrop.js +156 -0
  387. package/dist/components/slider.d.ts +5 -0
  388. package/dist/components/slider.js +24 -0
  389. package/dist/components/slotText.d.ts +2 -0
  390. package/dist/components/slotText.js +9 -0
  391. package/dist/components/status.d.ts +14 -0
  392. package/dist/components/status.js +22 -0
  393. package/dist/components/tableFacets.d.ts +8 -0
  394. package/dist/components/tableFacets.js +19 -0
  395. package/dist/components/textSplit.d.ts +17 -0
  396. package/dist/components/textSplit.js +24 -0
  397. package/dist/components/timeSpan.d.ts +78 -0
  398. package/dist/components/timeSpan.js +85 -0
  399. package/dist/components/tone.d.ts +43 -0
  400. package/dist/components/tone.js +82 -0
  401. package/dist/components/tour.js +3 -2
  402. package/dist/components/typewriter.d.ts +36 -0
  403. package/dist/components/typewriter.js +53 -0
  404. package/dist/components/videoBackdrop.js +5 -6
  405. package/dist/composables/easing.d.ts +22 -1
  406. package/dist/composables/easing.js +42 -1
  407. package/dist/composables/pseudoLocale.d.ts +15 -0
  408. package/dist/composables/pseudoLocale.js +96 -0
  409. package/dist/composables/scroll.js +5 -3
  410. package/dist/composables/useClipboard.d.ts +4 -1
  411. package/dist/composables/useClipboard.js +12 -2
  412. package/dist/composables/useField.d.ts +8 -0
  413. package/dist/composables/useField.js +5 -0
  414. package/dist/composables/useForm.js +19 -5
  415. package/dist/composables/useInView.d.ts +11 -0
  416. package/dist/composables/useInView.js +39 -0
  417. package/dist/composables/useLoadingGate.d.ts +8 -0
  418. package/dist/composables/useLoadingGate.js +38 -0
  419. package/dist/composables/useLpConfig.d.ts +10 -0
  420. package/dist/composables/useLpConfig.js +11 -1
  421. package/dist/composables/useMediaQuery.js +3 -1
  422. package/dist/composables/useNow.d.ts +16 -0
  423. package/dist/composables/useNow.js +39 -0
  424. package/dist/composables/usePersisted.js +14 -7
  425. package/dist/composables/usePillTransition.d.ts +5 -2
  426. package/dist/composables/usePillTransition.js +4 -3
  427. package/dist/composables/usePointerFrame.d.ts +18 -0
  428. package/dist/composables/usePointerFrame.js +31 -0
  429. package/dist/composables/useReducedMotion.d.ts +2 -0
  430. package/dist/composables/useReducedMotion.js +7 -0
  431. package/dist/composables/useRepeater.d.ts +17 -0
  432. package/dist/composables/useRepeater.js +31 -0
  433. package/dist/composables/useResizeObserver.d.ts +8 -0
  434. package/dist/composables/useResizeObserver.js +33 -0
  435. package/dist/composables/useRevealedValue.d.ts +31 -0
  436. package/dist/composables/useRevealedValue.js +26 -0
  437. package/dist/composables/useScramble.d.ts +42 -0
  438. package/dist/composables/useScramble.js +76 -0
  439. package/dist/composables/useShift.js +2 -10
  440. package/dist/composables/useSpotlight.d.ts +18 -0
  441. package/dist/composables/useSpotlight.js +53 -0
  442. package/dist/composables/useTilt.js +26 -18
  443. package/dist/composables/useTween.d.ts +43 -0
  444. package/dist/composables/useTween.js +69 -0
  445. package/dist/composables/useView.d.ts +18 -0
  446. package/dist/composables/useView.js +61 -0
  447. package/dist/icons-all.js +1 -1
  448. package/dist/index.d.ts +98 -1
  449. package/dist/index.js +262 -104
  450. package/dist/theme/define.d.ts +12 -0
  451. package/dist/theme/studio.d.ts +90 -0
  452. package/package.json +5 -2
  453. package/src/component-names.ts +33 -0
  454. package/src/components/AttentionDot.vue +27 -0
  455. package/src/components/FieldWrapper.vue +38 -0
  456. package/src/components/LayoutNode.vue +5 -9
  457. package/src/components/LegendSwatch.vue +15 -0
  458. package/src/components/LoaderMark.vue +90 -0
  459. package/src/components/LpActionBar.vue +12 -20
  460. package/src/components/LpActivityLine.vue +186 -0
  461. package/src/components/LpAlert.vue +54 -24
  462. package/src/components/LpAppShell.vue +7 -11
  463. package/src/components/LpAutocomplete.vue +16 -14
  464. package/src/components/LpAvatar.vue +17 -2
  465. package/src/components/LpBadge.vue +77 -13
  466. package/src/components/LpBreadcrumbs.vue +101 -35
  467. package/src/components/LpButton.vue +15 -3
  468. package/src/components/LpCard.vue +30 -7
  469. package/src/components/LpChart.vue +143 -154
  470. package/src/components/LpCheckbox.vue +10 -4
  471. package/src/components/LpCodeBlock.vue +11 -19
  472. package/src/components/LpCodeDisplay.vue +228 -0
  473. package/src/components/LpCollapse.vue +162 -0
  474. package/src/components/LpColorPicker.vue +10 -1
  475. package/src/components/LpCommandPalette.vue +38 -24
  476. package/src/components/LpContextMenu.vue +10 -1
  477. package/src/components/LpCopyButton.vue +185 -0
  478. package/src/components/LpCountdown.vue +308 -0
  479. package/src/components/LpDataList.vue +154 -0
  480. package/src/components/LpDatePicker.vue +53 -104
  481. package/src/components/LpDateRangePicker.vue +49 -95
  482. package/src/components/LpDisclosure.vue +58 -36
  483. package/src/components/LpDock.vue +274 -0
  484. package/src/components/LpDrawer.vue +10 -1
  485. package/src/components/LpDropdownMenu.vue +10 -1
  486. package/src/components/LpDropzone.vue +34 -13
  487. package/src/components/LpEditable.vue +293 -0
  488. package/src/components/LpFileTree.vue +5 -1
  489. package/src/components/LpFileTreeNode.vue +25 -50
  490. package/src/components/LpFormField.vue +22 -3
  491. package/src/components/LpFrame.vue +200 -0
  492. package/src/components/LpGauge.vue +287 -0
  493. package/src/components/LpGlowBorder.vue +60 -0
  494. package/src/components/LpHintExample.vue +9 -32
  495. package/src/components/LpHintLayer.vue +13 -3
  496. package/src/components/LpHoverCard.vue +175 -0
  497. package/src/components/LpIdenticon.vue +69 -0
  498. package/src/components/LpImageEditor.vue +15 -18
  499. package/src/components/LpInfraNode.vue +27 -16
  500. package/src/components/LpInput.vue +30 -16
  501. package/src/components/LpLightbox.vue +44 -23
  502. package/src/components/LpLoader.vue +145 -0
  503. package/src/components/LpLogViewer.vue +311 -82
  504. package/src/components/LpMagnetic.vue +97 -0
  505. package/src/components/LpMapPicker.vue +26 -28
  506. package/src/components/LpMasonry.vue +247 -0
  507. package/src/components/LpModal.vue +10 -1
  508. package/src/components/LpNotificationBell.vue +33 -49
  509. package/src/components/LpNumberField.vue +79 -14
  510. package/src/components/LpNumberFlow.vue +10 -35
  511. package/src/components/LpOtpInput.vue +7 -2
  512. package/src/components/LpPagination.vue +38 -4
  513. package/src/components/LpPalettePreview.vue +2 -7
  514. package/src/components/LpPasswordInput.vue +16 -14
  515. package/src/components/LpPhoneInput.vue +12 -3
  516. package/src/components/LpPie.vue +197 -36
  517. package/src/components/LpPipeline.vue +366 -0
  518. package/src/components/LpPopover.vue +10 -1
  519. package/src/components/LpProgress.vue +22 -84
  520. package/src/components/LpQuota.vue +197 -0
  521. package/src/components/LpRangeSlider.vue +5 -1
  522. package/src/components/LpRelativeTime.vue +90 -0
  523. package/src/components/LpRepeater.vue +80 -0
  524. package/src/components/LpRevealOnScroll.vue +125 -0
  525. package/src/components/LpScramble.vue +107 -0
  526. package/src/components/LpSecretField.vue +313 -0
  527. package/src/components/LpSegmented.vue +3 -10
  528. package/src/components/LpSelect.vue +78 -64
  529. package/src/components/LpShaderBackdrop.vue +434 -0
  530. package/src/components/LpShinyText.vue +139 -0
  531. package/src/components/LpSidebar.vue +2 -0
  532. package/src/components/LpSlider.vue +5 -1
  533. package/src/components/LpSparkline.vue +55 -37
  534. package/src/components/LpSpotlight.vue +51 -0
  535. package/src/components/LpStat.vue +6 -2
  536. package/src/components/LpStatus.vue +127 -0
  537. package/src/components/LpStepMark.vue +138 -0
  538. package/src/components/LpStepper.vue +37 -43
  539. package/src/components/LpSwap.vue +2 -2
  540. package/src/components/LpSwitch.vue +15 -22
  541. package/src/components/LpTable.vue +47 -63
  542. package/src/components/LpTableFilter.vue +1 -0
  543. package/src/components/LpTableOfContents.vue +53 -29
  544. package/src/components/LpTabs.vue +10 -21
  545. package/src/components/LpTagsInput.vue +53 -37
  546. package/src/components/LpTextReveal.vue +236 -0
  547. package/src/components/LpTextarea.vue +11 -11
  548. package/src/components/LpThemeStudio.vue +216 -0
  549. package/src/components/LpThemeStudioSection.vue +16 -0
  550. package/src/components/LpThemeSwitcher.vue +156 -47
  551. package/src/components/LpTimeField.vue +9 -1
  552. package/src/components/LpToaster.vue +63 -83
  553. package/src/components/LpTooltip.vue +10 -1
  554. package/src/components/LpTour.vue +24 -13
  555. package/src/components/LpTruncate.vue +135 -0
  556. package/src/components/LpTypewriter.vue +236 -0
  557. package/src/components/LpUptimeBar.vue +6 -5
  558. package/src/components/LpVideoBackdrop.vue +15 -13
  559. package/src/components/OptionContent.vue +17 -0
  560. package/src/components/PickerField.vue +58 -0
  561. package/src/components/RevealToggle.vue +51 -0
  562. package/src/components/StepConnector.vue +54 -0
  563. package/src/components/TourCard.vue +29 -16
  564. package/src/components/badge.ts +8 -0
  565. package/src/components/chart.ts +8 -23
  566. package/src/components/dateRange.ts +25 -0
  567. package/src/components/dock.ts +31 -0
  568. package/src/components/dropdown.ts +15 -3
  569. package/src/components/fieldText.ts +95 -0
  570. package/src/components/fileTree.ts +19 -21
  571. package/src/components/format.ts +136 -0
  572. package/src/components/identicon.ts +160 -0
  573. package/src/components/masonry.ts +73 -0
  574. package/src/components/meter.ts +122 -0
  575. package/src/components/otp.ts +14 -3
  576. package/src/components/pipeline.ts +101 -0
  577. package/src/components/quota.ts +111 -0
  578. package/src/components/relativeTime.ts +118 -0
  579. package/src/components/reveal.ts +56 -0
  580. package/src/components/scramble.ts +138 -0
  581. package/src/components/shaderBackdrop.ts +208 -0
  582. package/src/components/slider.ts +30 -0
  583. package/src/components/slotText.ts +13 -0
  584. package/src/components/status.ts +46 -0
  585. package/src/components/tableFacets.ts +25 -0
  586. package/src/components/textSplit.ts +55 -0
  587. package/src/components/timeSpan.ts +183 -0
  588. package/src/components/tone.ts +144 -0
  589. package/src/components/tour.ts +3 -2
  590. package/src/components/typewriter.ts +104 -0
  591. package/src/components/videoBackdrop.ts +7 -6
  592. package/src/composables/easing.ts +66 -1
  593. package/src/composables/pseudoLocale.ts +105 -0
  594. package/src/composables/scroll.ts +6 -3
  595. package/src/composables/useClipboard.ts +25 -3
  596. package/src/composables/useField.ts +12 -0
  597. package/src/composables/useForm.ts +27 -6
  598. package/src/composables/useInView.ts +61 -0
  599. package/src/composables/useLoadingGate.ts +63 -0
  600. package/src/composables/useLpConfig.ts +20 -1
  601. package/src/composables/useMediaQuery.ts +6 -2
  602. package/src/composables/useNow.ts +84 -0
  603. package/src/composables/usePersisted.ts +19 -8
  604. package/src/composables/usePillTransition.ts +21 -13
  605. package/src/composables/usePointerFrame.ts +72 -0
  606. package/src/composables/useReducedMotion.ts +22 -0
  607. package/src/composables/useRepeater.ts +44 -0
  608. package/src/composables/useResizeObserver.ts +59 -0
  609. package/src/composables/useRevealedValue.ts +84 -0
  610. package/src/composables/useScramble.ts +152 -0
  611. package/src/composables/useShift.ts +10 -16
  612. package/src/composables/useSpotlight.ts +82 -0
  613. package/src/composables/useTilt.ts +40 -23
  614. package/src/composables/useTween.ts +149 -0
  615. package/src/composables/useView.ts +93 -0
  616. package/src/index.css +1 -0
  617. package/src/index.ts +143 -1
  618. package/src/kit-icons.ts +1 -1
  619. package/src/theme/define.ts +65 -0
  620. package/src/theme/studio.ts +202 -0
  621. package/src/tokens/effects.css +151 -0
  622. package/src/tokens/tokens.css +131 -39
  623. package/src/tokens/utilities.css +81 -0
  624. package/dist/assets/LpTabs-Bk7v4bZz.css +0 -18
  625. package/dist/chunks/LpAlert-BdvxvcWE.js +0 -85
  626. package/dist/chunks/LpBadge-Fe0D1bYx.js +0 -62
  627. package/dist/chunks/LpBreadcrumbs-rzOKkLiI.js +0 -98
  628. package/dist/chunks/LpDatePicker-BMfM454Y.js +0 -243
  629. package/dist/chunks/LpDateRangePicker-CNb9X3eL.js +0 -195
  630. package/dist/chunks/LpNumberField-DO1ufKvE.js +0 -107
  631. package/dist/chunks/LpStepper-C2slwObu.js +0 -90
  632. package/dist/chunks/LpSwitch-CZy9_IoA.js +0 -132
  633. package/dist/chunks/LpThemeSwitcher-CDoziW0z.js +0 -166
  634. package/dist/chunks/TourCard-BKI7X9-r.js +0 -105
@@ -23,7 +23,9 @@ export default { inheritAttrs: false }
23
23
  import { computed, onBeforeUnmount, ref, watch } from "vue"
24
24
  import { easeTravel, prefersReducedMotion } from "../composables/easing"
25
25
  import { useReveal } from "../composables/useReveal"
26
+ import { lerp, mixColor, useTween } from "../composables/useTween"
26
27
  import { arcPath, calloutLabels, resolveSlices, formatTick, type PieSlice } from "./chart"
28
+ import LegendSwatch from "./LegendSwatch.vue"
27
29
  import LpContextMenu, { type ContextMenuItemDef } from "./LpContextMenu.vue"
28
30
  import LpEmptyState from "./LpEmptyState.vue"
29
31
  import LpNumberFlow from "./LpNumberFlow.vue"
@@ -278,9 +280,25 @@ const inner = computed(() =>
278
280
  * from. The pad is taken off each slice's own sweep and only when there is more
279
281
  * than one slice — a lone slice with a gap would be a circle with a nick in it.
280
282
  */
283
+ /*
284
+ * A slice is the same slice across data changes when it keeps its label. The
285
+ * index is no identity: a category inserted at the front would hand every
286
+ * other slice its neighbour's place, and the arcs would trade colours as they
287
+ * moved. The occurrence count only separates two slices that share a label.
288
+ */
289
+ const sliceKeys = computed(() => {
290
+ const seen = new Map<string, number>()
291
+ return resolved.value.slices.map((s) => {
292
+ const n = seen.get(s.label) ?? 0
293
+ seen.set(s.label, n + 1)
294
+ return `${s.isOther ? "other" : "slice"}:${s.label}#${n}`
295
+ })
296
+ })
297
+
281
298
  const arcs = computed(() =>
282
- arcAngles.value.map(({ slice, start, end, mid }) => ({
299
+ arcAngles.value.map(({ slice, start, end, mid, index }) => ({
283
300
  ...slice,
301
+ key: sliceKeys.value[index],
284
302
  d: arcPath(cx.value, cy.value, outer.value, inner.value, start, end, props.cornerRadius),
285
303
  /*
286
304
  * Push along the slice's own bisector, so it moves straight out of the
@@ -367,6 +385,143 @@ const sweepPath = computed(() => {
367
385
  return arcPath(cx.value, cy.value, radius, 0, start, start + Math.PI * 2 * sweep.value)
368
386
  })
369
387
 
388
+ /*
389
+ * What is DRAWN, as opposed to what is: each slice's span as fractions of the
390
+ * circle, travelling from the last data to the new.
391
+ *
392
+ * Interpolated as cumulative edges, not as sizes. Two neighbours share an edge
393
+ * at both ends of the move, so they share it at every frame between — a slice
394
+ * that grows pushes the next one round rather than overlapping it, and the
395
+ * circle stays closed throughout. A new slice opens from a zero-width sliver at
396
+ * the edge where it will sit; a removed one closes onto the edge its
397
+ * neighbours meet at, and is dropped once it has.
398
+ *
399
+ * Only the drawing moves. Hit areas, callouts, the legend and every event keep
400
+ * reading `arcs`, the data as it now is: a click mid-move lands on the slice
401
+ * the reader is aiming at, which is where it is going, and callouts laid out
402
+ * per frame would reshuffle their stacked labels as the slices slid.
403
+ */
404
+ interface Wedge {
405
+ key: string
406
+ from: number
407
+ to: number
408
+ color: string
409
+ }
410
+
411
+ const wedges = computed<Wedge[]>(() => {
412
+ let at = 0
413
+ return resolved.value.slices.map((s, i) => {
414
+ const w = { key: sliceKeys.value[i], from: at, to: at + s.fraction, color: s.color }
415
+ at = w.to
416
+ return w
417
+ })
418
+ })
419
+
420
+ const mixWedges = (from: Wedge[], to: Wedge[], t: number): Wedge[] => {
421
+ const before = new Map(from.map((w) => [w.key, w]))
422
+ const after = new Map(to.map((w) => [w.key, w]))
423
+ const out: Wedge[] = []
424
+
425
+ // Entering slices open at the far edge of the nearest earlier slice that was
426
+ // already drawn — exactly where the slice after them used to start.
427
+ let edge = 0
428
+ for (const w of to) {
429
+ const was = before.get(w.key)
430
+ const a = was ?? { from: edge, to: edge, color: w.color }
431
+ if (was) edge = was.to
432
+ out.push({
433
+ key: w.key,
434
+ from: lerp(a.from, w.from, t),
435
+ to: lerp(a.to, w.to, t),
436
+ color: mixColor(a.color, w.color, t),
437
+ })
438
+ }
439
+
440
+ // Leaving slices close onto the edge their surviving predecessor will end
441
+ // at, and keep their place in the order so the drawing stays in sequence.
442
+ edge = 0
443
+ let slot = 0
444
+ for (const w of from) {
445
+ const next = after.get(w.key)
446
+ if (next) {
447
+ edge = next.to
448
+ slot = out.findIndex((o) => o.key === w.key) + 1
449
+ continue
450
+ }
451
+ out.splice(slot++, 0, {
452
+ key: w.key,
453
+ from: lerp(w.from, edge, t),
454
+ to: lerp(w.to, edge, t),
455
+ color: w.color,
456
+ })
457
+ }
458
+ return out
459
+ }
460
+
461
+ /*
462
+ * The sweep and the tween never overlap: while the circle is still to open
463
+ * (not yet scrolled to, or closed again for a load) a change simply lands, and
464
+ * the data that follows sweeps in like the first.
465
+ */
466
+ const shownWedges = useTween(() => wedges.value, mixWedges, {
467
+ enabled: () => props.animate && sweep.value >= 1,
468
+ })
469
+
470
+ /*
471
+ * The legend's figures count to their new values, and a figure's text changes
472
+ * width as it goes ("8.13k" is wider than "12k"): the legend column, and the
473
+ * circle and everything beside it, shook on every frame. Each figure keeps the
474
+ * width of the widest text it passes through between the old value and the new
475
+ * one, measured in characters of its monospace font.
476
+ */
477
+ const figureChars = ref(0)
478
+ watch(
479
+ () => arcs.value.map((a) => [a.key, props.showPercent ? a.fraction : a.value] as const),
480
+ (next, prev) => {
481
+ const text = props.showPercent ? pct : fmt
482
+ const before = new Map(prev ?? [])
483
+ let widest = 0
484
+ for (const [key, to] of next) {
485
+ const from = before.get(key) ?? to
486
+ for (let k = 0; k <= 8; k++) widest = Math.max(widest, text(lerp(from, to, k / 8)).length)
487
+ }
488
+ figureChars.value = widest
489
+ },
490
+ { immediate: true },
491
+ )
492
+
493
+ const drawn = computed(() => {
494
+ const indexOf = new Map(arcs.value.map((a, i) => [a.key, i]))
495
+ const pad = arcs.value.length > 1 ? (props.padAngle * Math.PI) / 180 : 0
496
+ const TURN = Math.PI * 2
497
+ return shownWedges.value
498
+ .filter((w) => w.to - w.from > 1e-5)
499
+ .map((w) => {
500
+ const sweepAngle = (w.to - w.from) * TURN
501
+ const start = -Math.PI / 2 + w.from * TURN
502
+ const gap = Math.min(pad, sweepAngle * 0.5)
503
+ const mid = start + sweepAngle / 2
504
+ return {
505
+ key: w.key,
506
+ /* -1 for a slice on its way out: no longer data, so never lifted. */
507
+ index: indexOf.get(w.key) ?? -1,
508
+ color: w.color,
509
+ d: arcPath(
510
+ cx.value,
511
+ cy.value,
512
+ outer.value,
513
+ inner.value,
514
+ start + gap / 2,
515
+ start + sweepAngle - gap / 2,
516
+ props.cornerRadius,
517
+ ),
518
+ lift: `translate(${(Math.cos(mid) * props.hoverOffset).toFixed(2)} ${(
519
+ Math.sin(mid) * props.hoverOffset
520
+ ).toFixed(2)})`,
521
+ }
522
+ })
523
+ })
524
+
370
525
  const callouts = computed(() =>
371
526
  props.labels
372
527
  ? calloutLabels(
@@ -808,20 +963,20 @@ const { class: rootClass, attrs: rest } = useMergedAttrs(
808
963
  nothing. -->
809
964
  <g :clip-path="animate ? `url(#${sweepId}-sweep)` : undefined">
810
965
  <path
811
- v-for="(a, i) in arcs"
812
- :key="`${i}-${a.label}`"
813
- :d="a.d"
814
- :fill="a.color"
966
+ v-for="w in drawn"
967
+ :key="w.key"
968
+ :d="w.d"
969
+ :fill="w.color"
815
970
  class="pointer-events-none transition-[transform,opacity] duration-medium ease-[var(--ease-emphasized)] motion-reduce:transition-none"
816
- :transform="active === i ? a.lift : undefined"
817
- :opacity="dimmed(i) ? 0.28 : 1"
971
+ :transform="w.index >= 0 && active === w.index ? w.lift : undefined"
972
+ :opacity="dimmed(w.index) ? 0.28 : 1"
818
973
  />
819
974
  </g>
820
975
 
821
976
  <!-- Invisible, unmoving, and on top: the actual hit area. -->
822
977
  <path
823
978
  v-for="(a, i) in arcs"
824
- :key="`hit-${i}-${a.label}`"
979
+ :key="`hit-${a.key}`"
825
980
  :d="a.d"
826
981
  fill="transparent"
827
982
  class="cursor-pointer"
@@ -899,34 +1054,40 @@ const { class: rootClass, attrs: rest } = useMergedAttrs(
899
1054
 
900
1055
  <!-- The legend is the same control surface as the circle: identical
901
1056
  modifiers, and slivers too small to hit are reachable here. -->
902
- <div v-if="legendShown" class="flex min-w-0 flex-col gap-1.5" @pointerup="endDrag">
903
- <button
904
- v-for="(a, i) in arcs"
905
- :key="`${i}-${a.label}`"
906
- type="button"
907
- class="group flex cursor-pointer items-center gap-2 rounded-inner px-1 py-0.5 text-xs outline-none transition-[opacity,background-color] duration-fast focus-visible:ring-2 focus-visible:ring-ring"
908
- :class="[dimmed(i) ? 'opacity-45' : '', isSelected(i) ? 'bg-surface-soft' : '']"
909
- :aria-pressed="isSelected(i)"
910
- @pointerenter="onSlicePointerEnter(i)"
911
- @pointerleave="onSlicePointerLeave(i)"
912
- @pointerdown="onSlicePointerDown(i, $event)"
913
- @click="onLegendClick(i, $event)"
914
- @focus="active = i"
915
- @blur="active === i && (active = -1)"
916
- @contextmenu="onSliceContextMenu(i)"
917
- >
918
- <span
919
- class="size-2 shrink-0 rounded-full transition-transform duration-fast group-hover:scale-125"
920
- :style="{ backgroundColor: a.color }"
921
- />
922
- <span class="truncate text-muted-strong group-hover:text-ink">{{ a.label }}</span>
923
- <!-- What the bucket stands for, so a fold is never a silent loss. -->
924
- <span v-if="a.isOther" class="shrink-0 text-muted">({{ a.count }})</span>
925
- <span class="ml-auto shrink-0 pl-2 font-mono tabular-nums text-muted">
926
- {{ showPercent ? pct(a.fraction) : fmt(a.value) }}
927
- </span>
928
- </button>
929
- </div>
1057
+ <!-- Eases when the figures need more room or less. -->
1058
+ <LpShift v-if="legendShown" axis="width" class="min-w-0">
1059
+ <div class="flex min-w-0 flex-col gap-1.5" @pointerup="endDrag">
1060
+ <button
1061
+ v-for="(a, i) in arcs"
1062
+ :key="a.key"
1063
+ type="button"
1064
+ class="group flex cursor-pointer items-center gap-2 rounded-inner px-1 py-0.5 text-xs outline-none transition-[opacity,background-color] duration-fast focus-visible:ring-2 focus-visible:ring-ring"
1065
+ :class="[dimmed(i) ? 'opacity-45' : '', isSelected(i) ? 'bg-surface-soft' : '']"
1066
+ :aria-pressed="isSelected(i)"
1067
+ @pointerenter="onSlicePointerEnter(i)"
1068
+ @pointerleave="onSlicePointerLeave(i)"
1069
+ @pointerdown="onSlicePointerDown(i, $event)"
1070
+ @click="onLegendClick(i, $event)"
1071
+ @focus="active = i"
1072
+ @blur="active === i && (active = -1)"
1073
+ @contextmenu="onSliceContextMenu(i)"
1074
+ >
1075
+ <LegendSwatch :color="a.color" />
1076
+ <span class="truncate text-muted-strong group-hover:text-ink">{{ a.label }}</span>
1077
+ <!-- What the bucket stands for, so a fold is never a silent loss. -->
1078
+ <span v-if="a.isOther" class="shrink-0 text-muted">({{ a.count }})</span>
1079
+ <!-- Counts to the new figure, on the same beat as its slice resizing. -->
1080
+ <LpNumberFlow
1081
+ class="ml-auto shrink-0 pl-2 text-right font-mono tabular-nums text-muted"
1082
+ :style="{ minWidth: `calc(${figureChars}ch + 0.5rem)` }"
1083
+ :value="showPercent ? a.fraction : a.value"
1084
+ :duration="360"
1085
+ :format="showPercent ? pct : fmt"
1086
+ :animate-on-mount="false"
1087
+ />
1088
+ </button>
1089
+ </div>
1090
+ </LpShift>
930
1091
  </div>
931
1092
  </LpContextMenu>
932
1093
  </LpSwap>
@@ -0,0 +1,366 @@
1
+ <script lang="ts">
2
+ // The consumer's `class` is merged into the root's own (below) rather than
3
+ // appended to it, so opt out of the automatic pass-through that would add it
4
+ // a second time, unmerged.
5
+ export default { inheritAttrs: false }
6
+ </script>
7
+
8
+ <script setup lang="ts">
9
+ /*
10
+ * A run of jobs and the state each one is in: a deploy, a CI workflow, a
11
+ * server being provisioned.
12
+ *
13
+ * Not a wizard. LpStepper answers "where am I in a form", with one current
14
+ * step and everything before it done; here every step has its own state —
15
+ * three can pass, one fail and the rest never run — and the viewer is
16
+ * watching rather than driving. So the pieces are the ones a process monitor
17
+ * needs: a glyph per state (LpStepMark), how long each job took (ticking
18
+ * while it runs, from `startedAt`, without the app re-sending it), the line
19
+ * between steps filled up to where the run got and broken where it stopped,
20
+ * and a step's inside (its sub-jobs, its log) folded under it.
21
+ *
22
+ * <LpPipeline :steps="run.jobs" follow />
23
+ * <LpPipeline :steps="stages" orientation="horizontal" compact />
24
+ *
25
+ * Each change of state is announced politely ("Deploy: failed"), so a screen
26
+ * reader user learns about the run the way a sighted one glances at it.
27
+ */
28
+ import { computed, nextTick, ref, useId, useSlots, watch } from "vue"
29
+ import { prefersReducedMotion } from "../composables/easing"
30
+ import { useLabels } from "../composables/useLpConfig"
31
+ import { useMergedAttrs } from "../composables/useMergedClass"
32
+ import { useNow } from "../composables/useNow"
33
+ import LpCollapse from "./LpCollapse.vue"
34
+ import LpIcon from "./LpIcon.vue"
35
+ import LpRollingText from "./LpRollingText.vue"
36
+ import LpShift from "./LpShift.vue"
37
+ import LpStepMark from "./LpStepMark.vue"
38
+ import LpTooltip from "./LpTooltip.vue"
39
+ import StepConnector from "./StepConnector.vue"
40
+ import {
41
+ childProgress,
42
+ connectorFilled,
43
+ currentStepIndex,
44
+ stepElapsed,
45
+ stepKey,
46
+ type PipelineStep,
47
+ type StepStatus,
48
+ } from "./pipeline"
49
+ import { DEFAULT_UNIT_LABELS, formatElapsed, toEpoch, type TimeUnitLabels } from "./timeSpan"
50
+ import { TONES } from "./tone"
51
+
52
+ export type PipelineLabels = Record<StepStatus, string> & {
53
+ showDetails: string
54
+ hideDetails: string
55
+ }
56
+
57
+ const props = withDefaults(
58
+ defineProps<{
59
+ steps: PipelineStep[]
60
+ /**
61
+ * `vertical` (default) is the job list: room for descriptions, durations
62
+ * and folded sub-steps. `horizontal` is the strip across a card or a
63
+ * header, where the run is read at a glance.
64
+ */
65
+ orientation?: "horizontal" | "vertical"
66
+ /**
67
+ * Smaller marks, no descriptions. Horizontal and compact is marks and
68
+ * lines only, each step named in a tooltip — a run in a table cell.
69
+ */
70
+ compact?: boolean
71
+ /** Number the pending marks, for a run whose order is part of its meaning. */
72
+ numbered?: boolean
73
+ /**
74
+ * Keep the current step (see `currentStepIndex`) scrolled into view as the
75
+ * run advances: a long job list, or a horizontal strip wider than its box.
76
+ */
77
+ follow?: boolean
78
+ /**
79
+ * Open a step on its own when it starts running or fails, if it has
80
+ * something inside, and fold it again when it passes. That is the step the
81
+ * viewer would open next anyway; a step the viewer opened stays open.
82
+ */
83
+ autoExpand?: boolean
84
+ /** Announce status changes to screen readers. A nested pipeline turns it off so its parent speaks once. */
85
+ announce?: boolean
86
+ labels?: Partial<PipelineLabels>
87
+ }>(),
88
+ { orientation: "vertical", autoExpand: true, announce: true },
89
+ )
90
+
91
+ /** Keys of the open steps. Works uncontrolled; bind it to keep it in the app. */
92
+ const expanded = defineModel<string[]>("expanded", { default: () => [] })
93
+
94
+ defineSlots<{
95
+ /** Inside an expanded step, under its sub-steps: a log tail, a link to the run, an error. */
96
+ detail?: (props: { step: PipelineStep; index: number }) => unknown
97
+ }>()
98
+
99
+ const text = useLabels<PipelineLabels>(
100
+ "pipeline",
101
+ {
102
+ pending: "Pending",
103
+ running: "Running",
104
+ success: "Passed",
105
+ failed: "Failed",
106
+ skipped: "Skipped",
107
+ cancelled: "Cancelled",
108
+ showDetails: "Show details",
109
+ hideDetails: "Hide details",
110
+ },
111
+ () => props.labels,
112
+ )
113
+ const units = useLabels<TimeUnitLabels>("time", DEFAULT_UNIT_LABELS)
114
+
115
+ const slots = useSlots()
116
+ const vertical = computed(() => props.orientation === "vertical")
117
+ const markSize = computed(() => (props.compact ? "sm" : "md"))
118
+
119
+ /* The clock only runs while a step is running on its own timestamps. */
120
+ const ticking = computed(() =>
121
+ props.steps.some((s) => s.status === "running" && s.duration === undefined && !Number.isNaN(toEpoch(s.startedAt))),
122
+ )
123
+ const now = useNow(ticking)
124
+
125
+ const elapsed = (step: PipelineStep) => {
126
+ const ms = stepElapsed(step, now.value)
127
+ return ms === null ? "" : formatElapsed(ms, units.value)
128
+ }
129
+
130
+ const progressText = (step: PipelineStep) => {
131
+ const p = childProgress(step)
132
+ return p ? `${p.done}/${p.total}` : ""
133
+ }
134
+
135
+ /* A job that has not started has no log yet; its sub-steps are still worth a look. */
136
+ const expandable = (step: PipelineStep) =>
137
+ vertical.value && (!!step.steps?.length || (!!slots.detail && step.status !== "pending"))
138
+ const isOpen = (step: PipelineStep, i: number) => expanded.value.includes(stepKey(step, i))
139
+
140
+ function toggle(step: PipelineStep, i: number) {
141
+ const key = stepKey(step, i)
142
+ // Touched by the viewer: theirs now, not the pipeline's to fold away.
143
+ autoOpened.delete(key)
144
+ expanded.value = isOpen(step, i) ? expanded.value.filter((k) => k !== key) : [...expanded.value, key]
145
+ }
146
+
147
+ /*
148
+ * The fill of the line leaving a step: travelled in the colour of what
149
+ * happened (passed, skipped), flowing while the step runs, empty where the
150
+ * run stopped or has not reached.
151
+ */
152
+ const FILL: Partial<Record<StepStatus, string>> = {
153
+ success: TONES.success.bg,
154
+ skipped: "bg-line-strong",
155
+ }
156
+
157
+ const statusText = (step: PipelineStep) => {
158
+ const time = elapsed(step)
159
+ return time ? `${text.value[step.status]}, ${time}` : text.value[step.status]
160
+ }
161
+
162
+ /* ── Announcements and auto-expansion: both react to a step CHANGING state. */
163
+ const announcement = ref("")
164
+ let seen = new Map<string, StepStatus>()
165
+ /* Steps the pipeline opened itself — the only ones it may close again. */
166
+ const autoOpened = new Set<string>()
167
+
168
+ watch(
169
+ () => props.steps.map((s, i) => [stepKey(s, i), s.status] as const),
170
+ (pairs, old) => {
171
+ const next = new Map(pairs)
172
+ if (old) {
173
+ const opened: string[] = []
174
+ const closed: string[] = []
175
+ const said: string[] = []
176
+ props.steps.forEach((step, i) => {
177
+ const key = stepKey(step, i)
178
+ const before = seen.get(key)
179
+ if (before === undefined || before === step.status) return
180
+ said.push(`${step.label}: ${text.value[step.status]}`)
181
+ if (!props.autoExpand) return
182
+ if ((step.status === "running" || step.status === "failed") && expandable(step)) opened.push(key)
183
+ else if (connectorFilled(step.status) && autoOpened.has(key)) closed.push(key)
184
+ })
185
+ if (said.length && props.announce) announcement.value = said.join(". ")
186
+ const fresh = opened.filter((k) => !expanded.value.includes(k))
187
+ fresh.forEach((k) => autoOpened.add(k))
188
+ closed.forEach((k) => autoOpened.delete(k))
189
+ if (fresh.length || closed.length) {
190
+ expanded.value = [...expanded.value.filter((k) => !closed.includes(k)), ...fresh]
191
+ }
192
+ }
193
+ seen = next
194
+ },
195
+ { immediate: true },
196
+ )
197
+
198
+ /* ── Following the current step. */
199
+ const uid = useId()
200
+ const items: Array<Element | null> = []
201
+ const bindItem = (i: number) => (el: unknown) => { items[i] = el instanceof Element ? el : null }
202
+ const current = computed(() => currentStepIndex(props.steps))
203
+
204
+ watch(current, async (i) => {
205
+ if (!props.follow || i < 0) return
206
+ await nextTick()
207
+ items[i]?.scrollIntoView({
208
+ block: "nearest",
209
+ inline: "center",
210
+ behavior: prefersReducedMotion() ? "auto" : "smooth",
211
+ })
212
+ })
213
+
214
+ const { class: rootClass, attrs: rest } = useMergedAttrs(() =>
215
+ vertical.value
216
+ ? "flex w-full min-w-0 flex-col"
217
+ : `flex w-full min-w-0 items-start overflow-x-auto ${props.compact ? "gap-1.5 py-1" : "gap-3 pb-1"}`,
218
+ )
219
+ </script>
220
+
221
+ <template>
222
+ <div class="contents">
223
+ <ol :class="rootClass" v-bind="rest">
224
+ <li
225
+ v-for="(step, i) in steps"
226
+ :key="stepKey(step, i)"
227
+ :ref="bindItem(i)"
228
+ :aria-current="i === current ? 'step' : undefined"
229
+ :class="vertical
230
+ ? 'grid grid-cols-[auto_minmax(0,1fr)] gap-x-3'
231
+ : 'flex min-w-0 flex-1 items-start last:flex-none ' + (compact ? 'min-w-6 gap-1.5' : 'min-w-28 gap-3')"
232
+ >
233
+ <!-- ── Vertical: mark and line in a rail, the job beside it. -->
234
+ <template v-if="vertical">
235
+ <div class="flex flex-col items-center">
236
+ <LpStepMark
237
+ :status="step.status"
238
+ :size="markSize"
239
+ :number="numbered ? i + 1 : undefined"
240
+ :icon="step.icon"
241
+ :class="compact ? 'mt-0.5' : 'mt-px'"
242
+ />
243
+ <StepConnector
244
+ v-if="i < steps.length - 1"
245
+ orientation="vertical"
246
+ :filled="connectorFilled(step.status)"
247
+ :fill="FILL[step.status] ?? TONES.success.bg"
248
+ :flowing="step.status === 'running'"
249
+ class="my-1 min-h-3"
250
+ />
251
+ </div>
252
+
253
+ <div class="min-w-0" :class="i < steps.length - 1 ? (compact ? 'pb-2' : 'pb-4') : ''">
254
+ <component
255
+ :is="expandable(step) ? 'button' : 'div'"
256
+ :type="expandable(step) ? 'button' : undefined"
257
+ :aria-expanded="expandable(step) ? isOpen(step, i) : undefined"
258
+ :aria-controls="expandable(step) ? `${uid}-${i}` : undefined"
259
+ class="group/row flex w-full min-w-0 items-start gap-2 rounded-control text-left"
260
+ :class="expandable(step) && '-mx-1.5 -my-0.5 w-[calc(100%+0.75rem)] px-1.5 py-0.5 transition-colors duration-(--duration-fast) hover:bg-surface-soft focus-visible:ring-2 focus-visible:ring-ring focus-visible:outline-none'"
261
+ @click="expandable(step) && toggle(step, i)"
262
+ >
263
+ <span class="flex min-w-0 flex-1 flex-col">
264
+ <span
265
+ class="truncate transition-colors duration-(--duration-medium)"
266
+ :class="[compact ? 'text-xs' : 'text-sm', step.status === 'pending' || step.status === 'skipped' ? 'text-muted' : 'text-ink', step.status === 'running' && 'font-medium']"
267
+ >
268
+ {{ step.label }}
269
+ <span class="sr-only">({{ statusText(step) }})</span>
270
+ </span>
271
+ <span v-if="step.description && !compact" class="truncate text-xs text-muted">{{ step.description }}</span>
272
+ </span>
273
+ <span
274
+ class="flex shrink-0 items-center gap-2 font-mono text-xs tabular-nums"
275
+ :class="step.status === 'failed' ? TONES.danger.text : 'text-muted'"
276
+ aria-hidden="true"
277
+ >
278
+ <span v-if="progressText(step)" class="text-muted">{{ progressText(step) }}</span>
279
+ <LpShift axis="width">
280
+ <LpRollingText v-if="elapsed(step)" :value="elapsed(step)" align="end" :duration="200" />
281
+ </LpShift>
282
+ <LpIcon
283
+ v-if="expandable(step)"
284
+ name="lucide:chevron-down"
285
+ :size="14"
286
+ class="text-muted transition-transform duration-(--duration-medium) ease-(--ease-emphasized) motion-reduce:transition-none"
287
+ :class="isOpen(step, i) && 'rotate-180'"
288
+ />
289
+ </span>
290
+ </component>
291
+
292
+ <LpCollapse :open="expandable(step) && isOpen(step, i)">
293
+ <div :id="`${uid}-${i}`" class="flex flex-col gap-2 pt-2">
294
+ <LpPipeline
295
+ v-if="step.steps?.length"
296
+ :steps="step.steps"
297
+ compact
298
+ :announce="false"
299
+ :auto-expand="false"
300
+ :labels="labels"
301
+ class="rounded-card border border-line bg-surface-soft/40 px-3 py-2"
302
+ />
303
+ <slot name="detail" :step="step" :index="i" />
304
+ </div>
305
+ </LpCollapse>
306
+ </div>
307
+ </template>
308
+
309
+ <!-- ── Horizontal: mark, words, then the line to the next step. -->
310
+ <template v-else>
311
+ <LpTooltip v-if="compact" :content="`${step.label}: ${statusText(step)}`">
312
+ <span
313
+ tabindex="0"
314
+ class="inline-flex rounded-pill focus-visible:ring-2 focus-visible:ring-ring focus-visible:outline-none"
315
+ >
316
+ <LpStepMark
317
+ :status="step.status"
318
+ size="sm"
319
+ :number="numbered ? i + 1 : undefined"
320
+ :icon="step.icon"
321
+ />
322
+ <span class="sr-only">{{ step.label }} ({{ statusText(step) }})</span>
323
+ </span>
324
+ </LpTooltip>
325
+ <template v-else>
326
+ <LpStepMark
327
+ :status="step.status"
328
+ :size="markSize"
329
+ :number="numbered ? i + 1 : undefined"
330
+ :icon="step.icon"
331
+ />
332
+ <span class="flex min-w-0 shrink flex-col">
333
+ <span
334
+ class="truncate text-sm transition-colors duration-(--duration-medium)"
335
+ :class="[step.status === 'pending' || step.status === 'skipped' ? 'text-muted' : 'text-ink', step.status === 'running' && 'font-medium']"
336
+ >
337
+ {{ step.label }}
338
+ <span class="sr-only">({{ statusText(step) }})</span>
339
+ </span>
340
+ <span
341
+ class="flex h-4 items-center gap-1.5 font-mono text-[11px] tabular-nums"
342
+ :class="step.status === 'failed' ? TONES.danger.text : 'text-muted'"
343
+ aria-hidden="true"
344
+ >
345
+ <LpShift axis="width">
346
+ <LpRollingText :value="elapsed(step) || text[step.status]" align="start" :duration="200" />
347
+ </LpShift>
348
+ <span v-if="progressText(step)">· {{ progressText(step) }}</span>
349
+ </span>
350
+ </span>
351
+ </template>
352
+ <StepConnector
353
+ v-if="i < steps.length - 1"
354
+ :filled="connectorFilled(step.status)"
355
+ :fill="FILL[step.status] ?? TONES.success.bg"
356
+ :flowing="step.status === 'running'"
357
+ class="min-w-4 self-start"
358
+ :class="compact ? 'mt-[7px]' : 'mt-[11px]'"
359
+ />
360
+ </template>
361
+ </li>
362
+ </ol>
363
+ <span v-if="announce" class="sr-only" aria-live="polite" aria-atomic="true">{{ announcement }}</span>
364
+ </div>
365
+ </template>
366
+
@@ -8,6 +8,7 @@ import {
8
8
  import { computed } from "vue"
9
9
  import { mergeClass } from "../composables/useMergedClass"
10
10
  import { POPOVER_PANEL } from "./dropdown"
11
+ import { useOutsideGuard, usePortalTarget } from "../composables/useView"
11
12
 
12
13
  const props = withDefaults(
13
14
  defineProps<{
@@ -31,6 +32,12 @@ const open = defineModel<boolean>("open")
31
32
  const panel = computed(() =>
32
33
  mergeClass(POPOVER_PANEL, "z-(--z-popover) w-64 rounded-card p-3 text-sm text-ink outline-none", props.panelClass),
33
34
  )
35
+
36
+ // Overlays open in the window this component renders in (an LpFrame's, inside one).
37
+ const portalTo = usePortalTarget()
38
+ // What happens in another document (the page around a frame, or a frame in
39
+ // the page) is not "outside" for this layer.
40
+ const guardOutside = useOutsideGuard()
34
41
  </script>
35
42
 
36
43
  <template>
@@ -38,8 +45,10 @@ const panel = computed(() =>
38
45
  <PopoverTrigger as-child>
39
46
  <slot name="trigger" />
40
47
  </PopoverTrigger>
41
- <PopoverPortal>
48
+ <PopoverPortal :to="portalTo">
42
49
  <PopoverContent
50
+ @interact-outside="guardOutside"
51
+ @escape-key-down="guardOutside"
43
52
  :collision-padding="8"
44
53
  :side="side"
45
54
  :align="align"