@leavepulse/ui 0.81.0 → 0.83.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 (270) hide show
  1. package/dist/assets/LpVideoTrim-BcGs3Sh0.css +8 -0
  2. package/dist/assets/VideoFlash-VuPkC7QV.css +23 -0
  3. package/dist/chunks/{ContextMenuItems-DLK4q-mm.js → ContextMenuItems-h-apPY2d.js} +1 -1
  4. package/dist/chunks/{DropzoneLinkField-CcJu8De3.js → DropzoneLinkField-_Op8b_ee.js} +1 -1
  5. package/dist/chunks/{FieldLeading-CldGLINn.js → FieldLeading-mOMcwNqi.js} +1 -1
  6. package/dist/chunks/{FieldWrapper-Cj8ZgoKp.js → FieldWrapper-DtT-AIcn.js} +1 -1
  7. package/dist/chunks/{IconCloseButton-CV4iHRpy.js → IconCloseButton-YmisskyZ.js} +1 -1
  8. package/dist/chunks/{LayoutCanvas-BQbq7Ekw.js → LayoutCanvas-BrISZEBr.js} +1 -1
  9. package/dist/chunks/{LayoutNode-BMedRk3g.js → LayoutNode-CwMQ8ACa.js} +1 -1
  10. package/dist/chunks/{LpActionBar-C7g52Cyo.js → LpActionBar-CVVC6YbC.js} +1 -1
  11. package/dist/chunks/{LpAddressInput-1uakhQk5.js → LpAddressInput-tiuJ-bru.js} +7 -7
  12. package/dist/chunks/{LpAlert-DWFeR82L.js → LpAlert-8-x4H_Nk.js} +1 -1
  13. package/dist/chunks/{LpAppShell-BBINfN1v.js → LpAppShell-BW2YMq-A.js} +4 -4
  14. package/dist/chunks/{LpAutocomplete-DY3LvpB9.js → LpAutocomplete-BZODIops.js} +4 -4
  15. package/dist/chunks/{LpAvatar-CEZmMcGh.js → LpAvatar-BZq4nH53.js} +1 -1
  16. package/dist/chunks/{LpBadge-C_VqLHtg.js → LpBadge-Cz0pSSUu.js} +1 -1
  17. package/dist/chunks/{LpBottomNav-4I89sPDZ.js → LpBottomNav-DVdR5Mry.js} +1 -1
  18. package/dist/chunks/{LpBreadcrumbs-1BBzrfxG.js → LpBreadcrumbs-B2jphAel.js} +1 -1
  19. package/dist/chunks/{LpCalendar-D2dcXjUK.js → LpCalendar-C0_N5LVT.js} +1 -1
  20. package/dist/chunks/{LpCheckbox-CN-MEnbX.js → LpCheckbox-CwqLNdw4.js} +2 -2
  21. package/dist/chunks/{LpCodeBlock-Cj7zk4QE.js → LpCodeBlock-Dcss6bnl.js} +2 -2
  22. package/dist/chunks/{LpCodeDisplay-COzosm77.js → LpCodeDisplay-CLms1zDY.js} +4 -4
  23. package/dist/chunks/{LpColorPicker-BezW6DX1.js → LpColorPicker-CbIY450X.js} +3 -3
  24. package/dist/chunks/{LpConfirmDialog-D9GkDMYr.js → LpConfirmDialog-RmP4z6va.js} +2 -2
  25. package/dist/chunks/{LpConfirmHost-BH7kf0dm.js → LpConfirmHost-DheBgRMB.js} +1 -1
  26. package/dist/chunks/{LpContextMenu-bn5bc9t7.js → LpContextMenu-DIYhlap8.js} +2 -2
  27. package/dist/chunks/{LpContextSwitcher-CGr6ZAuq.js → LpContextSwitcher-fDA6ERl-.js} +2 -2
  28. package/dist/chunks/{LpCopyButton-BSr6YaFR.js → LpCopyButton-DSqKFGEb.js} +1 -1
  29. package/dist/chunks/{LpDataList-Cv7ZUlyy.js → LpDataList-f9UjSx9m.js} +2 -2
  30. package/dist/chunks/{LpDatePicker-CR7vsb5M.js → LpDatePicker-3kDQLVbg.js} +5 -5
  31. package/dist/chunks/{LpDateRangePicker-B9fRO3G6.js → LpDateRangePicker-DUPiTt0_.js} +3 -3
  32. package/dist/chunks/{LpDisclosure-CE4mneaE.js → LpDisclosure-C5WZjhbQ.js} +1 -1
  33. package/dist/chunks/{LpDrawer-DU_5MqBz.js → LpDrawer-CkYvIGIa.js} +1 -1
  34. package/dist/chunks/{LpDropdownMenu-_u6GPWhp.js → LpDropdownMenu-zSsK2Xra.js} +6 -3
  35. package/dist/chunks/{LpEditable-DYoMayjt.js → LpEditable-CP7Y_lKg.js} +2 -2
  36. package/dist/chunks/{LpEmptyState-DpdHeSfz.js → LpEmptyState-dsc5x-ya.js} +1 -1
  37. package/dist/chunks/{LpFieldNotice-DONFCWV7.js → LpFieldNotice-TOTbWvgv.js} +1 -1
  38. package/dist/chunks/{LpFileTree-f5ktGqsN.js → LpFileTree-BPjnOTln.js} +4 -4
  39. package/dist/chunks/{LpFileTreeNode-nbp-JwV9.js → LpFileTreeNode-_-MiWmie.js} +4 -4
  40. package/dist/chunks/{LpFormField-Bz50_LZW.js → LpFormField-BJwD1iEF.js} +1 -1
  41. package/dist/chunks/{LpHintLayer-B6bA5Cb_.js → LpHintLayer-CmfGnxYu.js} +1 -1
  42. package/dist/chunks/{LpHints-DyJ5hVcr.js → LpHints-C2Xvc1Fd.js} +3 -3
  43. package/dist/chunks/{LpIcon-BCQLuHSP.js → LpIcon-CoOLz1oJ.js} +1 -1
  44. package/dist/chunks/{LpInfraNode-BS1RXYAs.js → LpInfraNode-CW3qsM-U.js} +2 -2
  45. package/dist/chunks/{LpInput-BjbXdjcW.js → LpInput-BVWhkPuN.js} +3 -3
  46. package/dist/chunks/{LpInspector-BIyJyM6r.js → LpInspector-REmoX_es.js} +4 -4
  47. package/dist/chunks/{LpLightbox-CSAJuUaJ.js → LpLightbox-C3FFTYP5.js} +65 -20
  48. package/dist/chunks/{LpLogInspector-OJEz1vod.js → LpLogInspector-Cs8Z_fM0.js} +2 -2
  49. package/dist/chunks/{LpLogViewer-CRLwQpk0.js → LpLogViewer-DQRGe-ZK.js} +6 -6
  50. package/dist/chunks/{LpMapPicker-DW2EnrCJ.js → LpMapPicker-CQnQPgKm.js} +1 -1
  51. package/dist/chunks/{LpMediaGallery-BNEYctDP.js → LpMediaGallery-DkKqTy9P.js} +20 -8
  52. package/dist/chunks/{LpModal-DdTqdf4x.js → LpModal-DwejR7VP.js} +1 -1
  53. package/dist/chunks/{LpModalNav-BL7Dp0om.js → LpModalNav-D1-dSe6f.js} +3 -3
  54. package/dist/chunks/{LpNotificationBell-d0F6MWxD.js → LpNotificationBell-EKLkN0sp.js} +4 -4
  55. package/dist/chunks/{LpNotificationFeed-BIe5lapb.js → LpNotificationFeed-Dc-yQ4NH.js} +4 -4
  56. package/dist/chunks/{LpNumberField-D5Dx-hzD.js → LpNumberField-DvUjEMp-.js} +3 -3
  57. package/dist/chunks/{LpOtpInput-BVl74Lfl.js → LpOtpInput-xvDO14f4.js} +1 -1
  58. package/dist/chunks/{LpPagination-D0wb-Uru.js → LpPagination-BFZY8dFn.js} +1 -1
  59. package/dist/chunks/{LpPaletteTheme-DdilCBXA.js → LpPaletteTheme-EBJv0Bwz.js} +2 -2
  60. package/dist/chunks/{LpPasswordInput-DUtqWMkA.js → LpPasswordInput-DRJR3fF1.js} +2 -2
  61. package/dist/chunks/{LpPhoneInput-CalRjkqc.js → LpPhoneInput-BGOOtUoW.js} +3 -3
  62. package/dist/chunks/{LpPie-BVAEmVBT.js → LpPie-Deyn8h5J.js} +2 -2
  63. package/dist/chunks/{LpPipeline-Dk7_sRP1.js → LpPipeline-9ChjeNh5.js} +2 -2
  64. package/dist/chunks/{LpQuota-DKx0IZYA.js → LpQuota-CNh5eF_p.js} +1 -1
  65. package/dist/chunks/{LpRadioGroup-E3SP6j9h.js → LpRadioGroup-Br2Nl9Gr.js} +2 -2
  66. package/dist/chunks/{LpRangeCalendar-BFJFaen4.js → LpRangeCalendar-D66Y3U_w.js} +1 -1
  67. package/dist/chunks/{LpRangeSlider-b6ipywRB.js → LpRangeSlider-eX-fIlOw.js} +1 -1
  68. package/dist/chunks/{LpRepeater-BLrw62Bh.js → LpRepeater-0XvnlVd_.js} +2 -2
  69. package/dist/chunks/{LpSecretField-BV8BNjKc.js → LpSecretField-1RCooChq.js} +5 -5
  70. package/dist/chunks/{LpSection-CghNNxIv.js → LpSection-B9gbJJcP.js} +1 -1
  71. package/dist/chunks/{LpSegmented-DjvrHL_G.js → LpSegmented-hIjGzGTo.js} +2 -2
  72. package/dist/chunks/{LpSelect-DYPimzAZ.js → LpSelect-DugubwTc.js} +4 -4
  73. package/dist/chunks/{LpSidebar-D3S8rYFf.js → LpSidebar-DevSoN8W.js} +4 -4
  74. package/dist/chunks/{LpSidebarNav-Dlse8vxT.js → LpSidebarNav-DwQQ3KXe.js} +3 -3
  75. package/dist/chunks/{LpSidebarRow-Cqd_6tGG.js → LpSidebarRow-CAGgtJCS.js} +3 -3
  76. package/dist/chunks/{LpSlider-B9XIC75O.js → LpSlider-1fHpVxpB.js} +1 -1
  77. package/dist/chunks/{LpStat-kyLRmX-m.js → LpStat-Cm9-FaWQ.js} +2 -2
  78. package/dist/chunks/{LpStatus-szB2dWzM.js → LpStatus-CE8FSAT1.js} +1 -1
  79. package/dist/chunks/{LpStepMark-DP3CDSm9.js → LpStepMark-BWSlV69R.js} +1 -1
  80. package/dist/chunks/{LpStepper-BS3iaJIz.js → LpStepper-1D0ZA7Nx.js} +1 -1
  81. package/dist/chunks/{LpSwitch-DRFy8RNr.js → LpSwitch-D_kvE0oQ.js} +1 -1
  82. package/dist/chunks/{LpTabFace-C9-OperL.js → LpTabFace-CpbFKi-N.js} +2 -2
  83. package/dist/chunks/{LpTableFilter-CEIwLupk.js → LpTableFilter-BYHyCu38.js} +2 -2
  84. package/dist/chunks/{LpTagsInput-ClDeF_aU.js → LpTagsInput-Cqm38t5N.js} +5 -5
  85. package/dist/chunks/{LpTextarea-DEY6pUBy.js → LpTextarea-DaSKBkiV.js} +1 -1
  86. package/dist/chunks/{LpThemeStudio-WY72Z1Db.js → LpThemeStudio-BNdfi-Ds.js} +5 -5
  87. package/dist/chunks/{LpThemeSwitcher-9uCH9c-K.js → LpThemeSwitcher-CbC1nqBP.js} +3 -3
  88. package/dist/chunks/{LpTimeField-BmgxCzQO.js → LpTimeField-CCfPCWPT.js} +2 -2
  89. package/dist/chunks/{LpToaster-DLKO-efn.js → LpToaster-BhlI7h-h.js} +2 -2
  90. package/dist/chunks/{LpTopologyCanvas-B-PhPa7k.js → LpTopologyCanvas-DlKbu7E5.js} +4 -4
  91. package/dist/chunks/{LpTour-ODlBcat2.js → LpTour-CSLerA1B.js} +1 -1
  92. package/dist/chunks/{LpTruncate--nhT0iUo.js → LpTruncate-Ct26BSYB.js} +1 -1
  93. package/dist/chunks/{LpVideoBackdrop-Ct4St4ZL.js → LpVideoBackdrop-57i8RY8g.js} +1 -2
  94. package/dist/chunks/LpVideoPlayer-DcWd9DDW.js +813 -0
  95. package/dist/chunks/{LpWindow-DJxE8Zl-.js → LpWindow-YnF_knSj.js} +2 -2
  96. package/dist/chunks/{LpWorkspaceSwitcher-Bpf41Bw5.js → LpWorkspaceSwitcher-B-5UU6o4.js} +3 -3
  97. package/dist/chunks/{MenuTitle-CMjvLsvC.js → MenuTitle-BnZbFSwq.js} +1 -1
  98. package/dist/chunks/{OptionContent-DY8NaG8s.js → OptionContent-D5e_eklA.js} +1 -1
  99. package/dist/chunks/{PickerField-CgfkaBgg.js → PickerField-KpHiowNI.js} +1 -1
  100. package/dist/chunks/{RevealToggle-CPMXxaKv.js → RevealToggle-CE1hAIJB.js} +1 -1
  101. package/dist/chunks/SteadyText-CwH-kduj.js +25 -0
  102. package/dist/chunks/{TourCard-D4b3lMHi.js → TourCard-t2csiTDS.js} +1 -1
  103. package/dist/chunks/VideoThumb-Csf9bUz3.js +67 -0
  104. package/dist/chunks/VideoVolume-B4_DNmNi.js +68 -0
  105. package/dist/chunks/icons-CdSlCrle.js +59 -0
  106. package/dist/component-names.d.ts +1 -1
  107. package/dist/components/ContextMenuItems.vue.js +1 -1
  108. package/dist/components/DropzoneLinkField.vue.js +1 -1
  109. package/dist/components/FieldLeading.vue.js +1 -1
  110. package/dist/components/FieldWrapper.vue.js +1 -1
  111. package/dist/components/IconCloseButton.vue.js +1 -1
  112. package/dist/components/LayoutCanvas.vue.js +1 -1
  113. package/dist/components/LayoutNode.vue.js +1 -1
  114. package/dist/components/LpActionBar.vue.js +1 -1
  115. package/dist/components/LpAddressInput.vue.js +1 -1
  116. package/dist/components/LpAlert.vue.js +1 -1
  117. package/dist/components/LpAppShell.vue.js +1 -1
  118. package/dist/components/LpAutocomplete.vue.js +1 -1
  119. package/dist/components/LpAvatar.vue.js +1 -1
  120. package/dist/components/LpBadge.vue.js +1 -1
  121. package/dist/components/LpBottomNav.vue.js +1 -1
  122. package/dist/components/LpBreadcrumbs.vue.js +1 -1
  123. package/dist/components/LpCalendar.vue.js +1 -1
  124. package/dist/components/LpChart.vue.js +2 -2
  125. package/dist/components/LpCheckbox.vue.js +1 -1
  126. package/dist/components/LpCodeBlock.vue.js +1 -1
  127. package/dist/components/LpCodeDisplay.vue.js +1 -1
  128. package/dist/components/LpColorPicker.vue.js +1 -1
  129. package/dist/components/LpCommandPalette.vue.js +2 -2
  130. package/dist/components/LpConfirmDialog.vue.js +1 -1
  131. package/dist/components/LpConfirmHost.vue.js +1 -1
  132. package/dist/components/LpContextMenu.vue.js +1 -1
  133. package/dist/components/LpContextSwitcher.vue.js +1 -1
  134. package/dist/components/LpCopyButton.vue.js +1 -1
  135. package/dist/components/LpDataList.vue.js +1 -1
  136. package/dist/components/LpDatePicker.vue.js +1 -1
  137. package/dist/components/LpDateRangePicker.vue.js +1 -1
  138. package/dist/components/LpDisclosure.vue.js +1 -1
  139. package/dist/components/LpDock.vue.js +1 -1
  140. package/dist/components/LpDrawer.vue.js +1 -1
  141. package/dist/components/LpDropdownMenu.vue.d.ts +5 -1
  142. package/dist/components/LpDropdownMenu.vue.js +1 -1
  143. package/dist/components/LpDropzone.vue.js +2 -2
  144. package/dist/components/LpEditable.vue.js +1 -1
  145. package/dist/components/LpEmptyState.vue.js +1 -1
  146. package/dist/components/LpFieldNotice.vue.js +1 -1
  147. package/dist/components/LpFileTree.vue.js +1 -1
  148. package/dist/components/LpFileTreeNode.vue.js +1 -1
  149. package/dist/components/LpFormField.vue.js +1 -1
  150. package/dist/components/LpHintLayer.vue.js +1 -1
  151. package/dist/components/LpHints.vue.js +1 -1
  152. package/dist/components/LpIcon.vue.js +1 -1
  153. package/dist/components/LpImageEditor.vue.js +1 -1
  154. package/dist/components/LpInfraNode.vue.js +1 -1
  155. package/dist/components/LpInput.vue.js +1 -1
  156. package/dist/components/LpInspector.vue.js +1 -1
  157. package/dist/components/LpLightbox.vue.js +1 -1
  158. package/dist/components/LpLogInspector.vue.js +1 -1
  159. package/dist/components/LpLogViewer.vue.js +1 -1
  160. package/dist/components/LpMapPicker.vue.js +1 -1
  161. package/dist/components/LpMediaGallery.vue.d.ts +1 -1
  162. package/dist/components/LpMediaGallery.vue.js +1 -1
  163. package/dist/components/LpModal.vue.js +1 -1
  164. package/dist/components/LpModalNav.vue.js +1 -1
  165. package/dist/components/LpNotificationBell.vue.js +1 -1
  166. package/dist/components/LpNotificationFeed.vue.js +1 -1
  167. package/dist/components/LpNumberField.vue.js +1 -1
  168. package/dist/components/LpOtpInput.vue.js +1 -1
  169. package/dist/components/LpPagination.vue.js +1 -1
  170. package/dist/components/LpPaletteFacts.vue.js +1 -1
  171. package/dist/components/LpPaletteHeader.vue.js +2 -2
  172. package/dist/components/LpPaletteTheme.vue.js +1 -1
  173. package/dist/components/LpPasswordInput.vue.js +1 -1
  174. package/dist/components/LpPhoneInput.vue.js +1 -1
  175. package/dist/components/LpPie.vue.js +1 -1
  176. package/dist/components/LpPipeline.vue.js +1 -1
  177. package/dist/components/LpQuota.vue.js +1 -1
  178. package/dist/components/LpRadioGroup.vue.js +1 -1
  179. package/dist/components/LpRangeCalendar.vue.js +1 -1
  180. package/dist/components/LpRangeSlider.vue.js +1 -1
  181. package/dist/components/LpRepeater.vue.js +1 -1
  182. package/dist/components/LpSecretField.vue.js +1 -1
  183. package/dist/components/LpSection.vue.js +1 -1
  184. package/dist/components/LpSegmented.vue.js +1 -1
  185. package/dist/components/LpSelect.vue.js +1 -1
  186. package/dist/components/LpShortcutsDialog.vue.js +1 -1
  187. package/dist/components/LpSidebar.vue.js +1 -1
  188. package/dist/components/LpSidebarNav.vue.js +1 -1
  189. package/dist/components/LpSidebarRow.vue.js +1 -1
  190. package/dist/components/LpSlider.vue.js +1 -1
  191. package/dist/components/LpStat.vue.js +1 -1
  192. package/dist/components/LpStatus.vue.js +1 -1
  193. package/dist/components/LpStepMark.vue.js +1 -1
  194. package/dist/components/LpStepper.vue.js +1 -1
  195. package/dist/components/LpSwitch.vue.js +1 -1
  196. package/dist/components/LpTabFace.vue.js +1 -1
  197. package/dist/components/LpTable.vue.js +9 -9
  198. package/dist/components/LpTableFilter.vue.js +1 -1
  199. package/dist/components/LpTabs.vue.js +2 -2
  200. package/dist/components/LpTagsInput.vue.js +1 -1
  201. package/dist/components/LpTextarea.vue.js +1 -1
  202. package/dist/components/LpThemeStudio.vue.js +1 -1
  203. package/dist/components/LpThemeSwitcher.vue.js +1 -1
  204. package/dist/components/LpTimeField.vue.js +1 -1
  205. package/dist/components/LpToaster.vue.js +1 -1
  206. package/dist/components/LpTopologyCanvas.vue.js +1 -1
  207. package/dist/components/LpTour.vue.js +1 -1
  208. package/dist/components/LpTruncate.vue.js +1 -1
  209. package/dist/components/LpVideoBackdrop.vue.js +1 -1
  210. package/dist/components/LpVideoPlayer.vue.d.ts +111 -0
  211. package/dist/components/LpVideoPlayer.vue.js +4 -0
  212. package/dist/components/LpVideoTrim.vue.d.ts +82 -0
  213. package/dist/components/LpVideoTrim.vue.js +1106 -0
  214. package/dist/components/LpWindow.vue.js +1 -1
  215. package/dist/components/LpWorkspaceSwitcher.vue.js +1 -1
  216. package/dist/components/MenuTitle.vue.js +1 -1
  217. package/dist/components/OptionContent.vue.js +1 -1
  218. package/dist/components/PickerField.vue.js +1 -1
  219. package/dist/components/RevealToggle.vue.js +1 -1
  220. package/dist/components/SteadyText.vue.d.ts +7 -0
  221. package/dist/components/SteadyText.vue.js +4 -0
  222. package/dist/components/TourCard.vue.js +1 -1
  223. package/dist/components/VideoFlash.vue.d.ts +10 -0
  224. package/dist/components/VideoFlash.vue.js +33 -0
  225. package/dist/components/VideoThumb.vue.d.ts +12 -0
  226. package/dist/components/VideoThumb.vue.js +4 -0
  227. package/dist/components/VideoVolume.vue.d.ts +15 -0
  228. package/dist/components/VideoVolume.vue.js +4 -0
  229. package/dist/components/chartWindow.js +4 -8
  230. package/dist/components/lightbox.d.ts +13 -0
  231. package/dist/components/lightbox.js +11 -1
  232. package/dist/components/menuWhen.js +1 -1
  233. package/dist/components/videoBackdrop.d.ts +15 -2
  234. package/dist/components/videoBackdrop.js +8 -2
  235. package/dist/components/videoFrames.d.ts +37 -0
  236. package/dist/components/videoFrames.js +134 -0
  237. package/dist/components/videoMotion.d.ts +28 -0
  238. package/dist/components/videoMotion.js +26 -0
  239. package/dist/components/videoTrim.d.ts +68 -0
  240. package/dist/components/videoTrim.js +121 -0
  241. package/dist/composables/span.d.ts +10 -0
  242. package/dist/composables/span.js +15 -0
  243. package/dist/composables/useVideoMedia.d.ts +74 -0
  244. package/dist/composables/useVideoMedia.js +291 -0
  245. package/dist/icons-all.js +1 -1
  246. package/dist/index.d.ts +7 -1
  247. package/dist/index.js +186 -174
  248. package/package.json +1 -1
  249. package/src/component-names.ts +2 -0
  250. package/src/components/LpDropdownMenu.vue +4 -0
  251. package/src/components/LpLightbox.vue +61 -8
  252. package/src/components/LpMediaGallery.vue +19 -2
  253. package/src/components/LpVideoBackdrop.vue +1 -7
  254. package/src/components/LpVideoPlayer.vue +774 -0
  255. package/src/components/LpVideoTrim.vue +1066 -0
  256. package/src/components/SteadyText.vue +21 -0
  257. package/src/components/VideoFlash.vue +52 -0
  258. package/src/components/VideoThumb.vue +66 -0
  259. package/src/components/VideoVolume.vue +63 -0
  260. package/src/components/chartWindow.ts +4 -8
  261. package/src/components/lightbox.ts +23 -0
  262. package/src/components/videoBackdrop.ts +20 -3
  263. package/src/components/videoFrames.ts +171 -0
  264. package/src/components/videoMotion.ts +38 -0
  265. package/src/components/videoTrim.ts +186 -0
  266. package/src/composables/span.ts +21 -0
  267. package/src/composables/useVideoMedia.ts +411 -0
  268. package/src/index.ts +7 -1
  269. package/src/kit-icons.ts +1 -1
  270. package/dist/chunks/icons-C1vppR4f.js +0 -59
@@ -0,0 +1,21 @@
1
+ <script setup lang="ts">
2
+ /*
3
+ * Text that changes while it is read (a clock, a length under a dragged
4
+ * handle) in a box as wide as the widest it will get: an invisible twin of
5
+ * that widest text sits in the same grid cell, the way LpEditable sizes its
6
+ * field. Without it each new value resized the box and everything after it
7
+ * on the line shifted, and a row that wraps re-wrapped and took the whole
8
+ * layout under it up and down with it.
9
+ *
10
+ * `widest` is the longest the text can be, its digits as zeros: with
11
+ * tabular figures every digit is as wide as a zero.
12
+ */
13
+ defineProps<{ text: string; widest: string }>()
14
+ </script>
15
+
16
+ <template>
17
+ <span class="inline-grid whitespace-nowrap tabular-nums">
18
+ <span aria-hidden="true" class="invisible col-start-1 row-start-1">{{ widest }}</span>
19
+ <span class="col-start-1 row-start-1">{{ text }}</span>
20
+ </span>
21
+ </template>
@@ -0,0 +1,52 @@
1
+ <script setup lang="ts">
2
+ /*
3
+ * The answer to a play or pause on LpVideoPlayer and LpVideoTrim: a round
4
+ * badge with what just happened, in the middle of the picture, that swells a
5
+ * little and fades. A press on the picture, or Space with the eyes on it,
6
+ * otherwise has only the motion itself to confirm it, and a pause on a still
7
+ * scene shows nothing at all. Each new `flash` replays it; reduced motion has none.
8
+ */
9
+ import LpIcon from "./LpIcon.vue"
10
+
11
+ defineProps<{
12
+ /** What happened, and a count that changes on every press so the same icon twice still plays twice. */
13
+ flash: { icon: string; n: number } | null
14
+ }>()
15
+ </script>
16
+
17
+ <template>
18
+ <div class="pointer-events-none absolute inset-0 flex items-center justify-center motion-reduce:hidden" aria-hidden="true">
19
+ <span
20
+ v-if="flash"
21
+ :key="flash.n"
22
+ class="lp-video-flash flex size-16 items-center justify-center rounded-full bg-black/50 text-white opacity-0"
23
+ >
24
+ <LpIcon :name="flash.icon" :size="28" :class="flash.icon === 'lucide:play' ? 'translate-x-0.5' : ''" />
25
+ </span>
26
+ </div>
27
+ </template>
28
+
29
+ <style scoped>
30
+ /* Rises into view, holds a moment, then swells away: arriving at full strength in one frame read as a blink. */
31
+ .lp-video-flash {
32
+ animation: lp-video-flash 700ms var(--ease-emphasized) both;
33
+ }
34
+ @keyframes lp-video-flash {
35
+ 0% {
36
+ opacity: 0;
37
+ scale: 0.8;
38
+ }
39
+ 25% {
40
+ opacity: 1;
41
+ scale: 1;
42
+ }
43
+ 45% {
44
+ opacity: 1;
45
+ scale: 1;
46
+ }
47
+ 100% {
48
+ opacity: 0;
49
+ scale: 1.15;
50
+ }
51
+ }
52
+ </style>
@@ -0,0 +1,66 @@
1
+ <script setup lang="ts">
2
+ /*
3
+ * A video's face in a grid or a strip (LpMediaGallery's tile, LpLightbox's
4
+ * filmstrip): its poster, or without one its first frame, under a round play
5
+ * badge and, when there is room and it is known, its length.
6
+ *
7
+ * Without a poster the frame comes from the video itself: metadata and the
8
+ * one frame at a tenth of a second (a media fragment), never the whole file.
9
+ * Its length comes along with the metadata when the item did not say it.
10
+ */
11
+ import { computed, ref } from "vue"
12
+ import LpIcon from "./LpIcon.vue"
13
+ import type { LightboxItem } from "./lightbox"
14
+ import { formatClipTime } from "./videoBackdrop"
15
+
16
+ const props = defineProps<{
17
+ item: LightboxItem
18
+ /** Classes for the picture itself (the gallery's blur and dimming ride on it). */
19
+ mediaClass?: string | (string | false | undefined)[]
20
+ /** A small thumbnail: the badge shrinks and the length is left out. */
21
+ compact?: boolean
22
+ alt?: string
23
+ }>()
24
+
25
+ const measured = ref<number | null>(null)
26
+ const length = computed(() => props.item.duration ?? measured.value)
27
+ const still = computed(() => props.item.poster ?? props.item.thumb)
28
+ </script>
29
+
30
+ <template>
31
+ <img
32
+ v-if="still"
33
+ :src="still"
34
+ :alt="alt ?? ''"
35
+ loading="lazy"
36
+ decoding="async"
37
+ class="size-full object-cover"
38
+ :class="mediaClass"
39
+ draggable="false"
40
+ />
41
+ <video
42
+ v-else
43
+ :src="`${item.src}#t=0.1`"
44
+ preload="metadata"
45
+ muted
46
+ playsinline
47
+ tabindex="-1"
48
+ aria-hidden="true"
49
+ class="pointer-events-none size-full object-cover"
50
+ :class="mediaClass"
51
+ @loadedmetadata="(e) => Number.isFinite((e.target as HTMLVideoElement).duration) && (measured = (e.target as HTMLVideoElement).duration)"
52
+ />
53
+ <span class="pointer-events-none absolute inset-0 flex items-center justify-center" aria-hidden="true">
54
+ <span
55
+ class="flex items-center justify-center rounded-full bg-black/50 text-white ring-1 ring-white/25 transition-transform duration-(--duration-fast) ease-(--ease-emphasized) group-hover:scale-110 motion-reduce:transition-none motion-reduce:group-hover:scale-100"
56
+ :class="compact ? 'size-6' : 'size-10'"
57
+ >
58
+ <LpIcon name="lucide:play" :size="compact ? 12 : 18" class="translate-x-px" />
59
+ </span>
60
+ </span>
61
+ <span
62
+ v-if="!compact && length"
63
+ class="pointer-events-none absolute right-1.5 bottom-1.5 rounded-pill bg-black/60 px-1.5 py-0.5 font-mono text-[10px] text-white tabular-nums"
64
+ aria-hidden="true"
65
+ >{{ formatClipTime(length) }}</span>
66
+ </template>
@@ -0,0 +1,63 @@
1
+ <script setup lang="ts">
2
+ /*
3
+ * Sound for LpVideoPlayer and LpVideoTrim: the mute button, and beside it a
4
+ * volume slider that opens while the pointer or the focus is on either, the
5
+ * way most players keep it out of the way until it is reached for. On a touch
6
+ * screen the slider stays closed: a phone's own buttons set the volume, and a
7
+ * thin slider under a thumb only gets in the way.
8
+ */
9
+ import LpButton from "./LpButton.vue"
10
+ import LpIcon from "./LpIcon.vue"
11
+ import LpSlider from "./LpSlider.vue"
12
+ import type { VideoMedia } from "../composables/useVideoMedia"
13
+ import { ICON_SWAP, OVERLAY_BUTTON } from "./videoMotion"
14
+
15
+ const props = defineProps<{
16
+ media: VideoMedia
17
+ labels: { mute: string; unmute: string; volume: string }
18
+ disabled?: boolean
19
+ /** Over the picture (white on the video) rather than on the page. */
20
+ overlay?: boolean
21
+ }>()
22
+
23
+ const icon = () => {
24
+ const { muted, volume } = props.media
25
+ if (muted.value || volume.value === 0) return "lucide:volume-x"
26
+ return volume.value < 0.5 ? "lucide:volume-1" : "lucide:volume-2"
27
+ }
28
+ </script>
29
+
30
+ <template>
31
+ <div class="group/volume flex items-center">
32
+ <LpButton
33
+ size="sm"
34
+ variant="ghost"
35
+ square
36
+ :class="overlay ? OVERLAY_BUTTON : ''"
37
+ :disabled="disabled"
38
+ :aria-label="media.muted.value ? labels.unmute : labels.mute"
39
+ :title="media.muted.value ? labels.unmute : labels.mute"
40
+ :aria-pressed="media.muted.value"
41
+ @click="media.setMuted(!media.muted.value)"
42
+ >
43
+ <!-- Muting, and the volume crossing a half, hand the icon over instead of swapping it in one frame. -->
44
+ <Transition v-bind="ICON_SWAP">
45
+ <LpIcon :key="icon()" :name="icon()" :size="16" />
46
+ </Transition>
47
+ </LpButton>
48
+ <div
49
+ class="w-0 overflow-hidden opacity-0 transition-[width,opacity] duration-(--duration-medium) ease-(--ease-emphasized) group-hover/volume:w-20 group-hover/volume:opacity-100 group-focus-within/volume:w-20 group-focus-within/volume:opacity-100 pointer-coarse:hidden motion-reduce:transition-none"
50
+ >
51
+ <LpSlider
52
+ class="mx-2 w-16"
53
+ :model-value="media.muted.value ? 0 : Math.round(media.volume.value * 100)"
54
+ :min="0"
55
+ :max="100"
56
+ :step="5"
57
+ :disabled="disabled"
58
+ :aria-label="labels.volume"
59
+ @update:model-value="(v: number) => media.setVolume(v / 100)"
60
+ />
61
+ </div>
62
+ </div>
63
+ </template>
@@ -1,4 +1,5 @@
1
1
  import { clamp } from "../composables/clamp"
2
+ import { shiftSpan, zoomSpan } from "../composables/span"
2
3
 
3
4
  /*
4
5
  * The part of a long series a chart draws: which points are in the window, and
@@ -54,18 +55,13 @@ export function clampRange(range: ChartRange | null | undefined, count: number):
54
55
  /** The range after a zoom by `factor` (below 1 is in), keeping `anchor` (0–1 across it) in place. */
55
56
  export function zoomRange(range: ChartRange | null, count: number, anchor: number, factor: number): ChartRange | null {
56
57
  const from = range ?? { start: 0, end: count - 1 }
57
- const span = from.end - from.start
58
- const next = Math.max(MIN_RANGE_POINTS - 1, span * factor)
59
- const at = from.start + span * clamp(anchor, 0, 1)
60
- const start = at - (at - from.start) * (next / (span || 1))
61
- return clampRange({ start, end: start + next }, count)
58
+ const at = from.start + (from.end - from.start) * clamp(anchor, 0, 1)
59
+ return clampRange(zoomSpan(from.start, from.end, at, factor, MIN_RANGE_POINTS - 1), count)
62
60
  }
63
61
 
64
62
  /** The range moved by `delta` points, keeping its width at the ends of the data. */
65
63
  export function panRange(range: ChartRange, count: number, delta: number): ChartRange | null {
66
- const span = range.end - range.start
67
- const start = Math.max(0, Math.min(count - 1 - span, range.start + delta))
68
- return clampRange({ start, end: start + span }, count)
64
+ return clampRange(shiftSpan(range.start, range.end, delta, 0, count - 1), count)
69
65
  }
70
66
 
71
67
  /**
@@ -24,6 +24,29 @@ export interface LightboxItem {
24
24
  * a marked picture sitting beside plain ones.
25
25
  */
26
26
  spoiler?: boolean
27
+ /**
28
+ * A video rather than a picture: shown in LpVideoPlayer, with a play badge
29
+ * on its tile and its strip thumbnail. Left out, an address ending in a
30
+ * video extension (.mp4, .webm, .mov…) counts as one; set it for an address
31
+ * that does not say (an API route, a blob: URL).
32
+ */
33
+ kind?: "image" | "video"
34
+ /** A still for a video's tile and thumbnail; the video's first frame when absent. */
35
+ poster?: string
36
+ /** A video's length in seconds, for the badge on its tile, when known up front. */
37
+ duration?: number
38
+ }
39
+
40
+ const VIDEO_EXTENSION = /\.(mp4|m4v|webm|mov|ogv|ogg|mkv)$/i
41
+
42
+ /** Whether the item is a video: said outright by `kind`, else told by its address's extension. */
43
+ export function isVideoItem(item: Pick<LightboxItem, "src" | "kind">): boolean {
44
+ if (item.kind) return item.kind === "video"
45
+ try {
46
+ return VIDEO_EXTENSION.test(new URL(item.src, "http://x").pathname)
47
+ } catch {
48
+ return false
49
+ }
27
50
  }
28
51
 
29
52
  /** Last path segment of a URL, or a fallback — used to name a download. */
@@ -64,12 +64,29 @@ export function isNativeLoop(plan: LoopPlan, duration: number): boolean {
64
64
  return plan.fade === 0 && plan.from === 0 && plan.to >= duration
65
65
  }
66
66
 
67
- /** "1:05", "0:07.5": a position on a clip as a range slider labels it. */
68
- export function formatClipTime(seconds: number, precise = false): string {
67
+ /**
68
+ * "1:05", "0:07.5": a position on a clip as a range slider labels it.
69
+ * `precise` adds tenths when there are any; "fixed" always writes them, for a
70
+ * time that changes while it is read, so it keeps one length ("0:05.0", not
71
+ * "0:05" between "0:04.9" and "0:05.1").
72
+ */
73
+ export function formatClipTime(seconds: number, precise: boolean | "fixed" = false): string {
69
74
  const t = splitTime(Number.isFinite(seconds) ? seconds * 1000 : 0)
70
75
  // Hours fold into minutes: a slider over a clip reads "75:00", not "1:15:00".
71
76
  const minutes = (t.days * 24 + t.hours) * 60 + t.minutes
72
77
  const secs = String(t.seconds).padStart(2, "0")
73
78
  const tenth = Math.floor(t.ms / 100)
74
- return precise && tenth ? `${minutes}:${secs}.${tenth}` : `${minutes}:${secs}`
79
+ return precise === "fixed" || (precise && tenth) ? `${minutes}:${secs}.${tenth}` : `${minutes}:${secs}`
80
+ }
81
+
82
+ /** The widest a clip time up to `seconds` gets, its digits as zeros: what SteadyText reserves. */
83
+ export function widestClipTime(seconds: number): string {
84
+ return formatClipTime(seconds, "fixed").replace(/\d/g, "0")
75
85
  }
86
+
87
+ /**
88
+ * The slowest rate a pause eases down to before the element is paused, and a
89
+ * start eases up from. Chromium refuses a rate below a sixteenth; past that
90
+ * point the picture is all but still anyway.
91
+ */
92
+ export const SLOWEST_RATE = 1 / 16
@@ -0,0 +1,171 @@
1
+ /*
2
+ * Stills for LpVideoTrim's timeline, taken from a second, hidden <video> of the
3
+ * same address: seek, wait for the frame, draw it on a small canvas.
4
+ *
5
+ * - One seek at a time. A seek started while another runs is dropped by some
6
+ * engines and freezes WebKitGTK's GStreamer pipeline (WebKit bug 194499);
7
+ * a seek that never answers is given up after a while, so one bad spot does
8
+ * not stop the strip.
9
+ * - A seek lands on a key frame where the engine can (`fastSeek`, WebKit and
10
+ * Firefox): a thumbnail needs a nearby frame, not the exact one, and a key
11
+ * frame needs no decoding forward from the previous one.
12
+ * - Asking again replaces what is still queued: zooming or panning should not
13
+ * wait for stills of a stretch already scrolled away.
14
+ * - The canvas is only ever drawn, never read back, so a video from another
15
+ * origin without CORS still gives stills (a tainted canvas still shows).
16
+ */
17
+
18
+ const SEEK_TIMEOUT_MS = 4000
19
+ const FRAME_TIMEOUT_MS = 250
20
+ const CACHE_LIMIT = 240
21
+
22
+ export interface FrameRequest {
23
+ key: string
24
+ time: number
25
+ }
26
+
27
+ type VideoWithFrames = HTMLVideoElement & {
28
+ requestVideoFrameCallback?: (cb: () => void) => number
29
+ fastSeek?: (time: number) => void
30
+ }
31
+
32
+ export class FrameSampler {
33
+ private queue: FrameRequest[] = []
34
+ private running = false
35
+ private disposed = false
36
+ private cache = new Map<string, HTMLCanvasElement>()
37
+
38
+ constructor(
39
+ private readonly video: VideoWithFrames,
40
+ private readonly size: () => { width: number; height: number },
41
+ private readonly onFrame: (key: string, canvas: HTMLCanvasElement) => void,
42
+ ) {}
43
+
44
+ /** A frame taken earlier, if it is still kept. */
45
+ cached(key: string): HTMLCanvasElement | undefined {
46
+ const hit = this.cache.get(key)
47
+ if (hit) {
48
+ // Least recently used goes first: refresh this one's place.
49
+ this.cache.delete(key)
50
+ this.cache.set(key, hit)
51
+ }
52
+ return hit
53
+ }
54
+
55
+ /** The frames wanted now, most wanted first; whatever was queued before is dropped. */
56
+ want(requests: FrameRequest[]): void {
57
+ this.queue = requests.filter((r) => !this.cache.has(r.key))
58
+ void this.pump()
59
+ }
60
+
61
+ dispose(): void {
62
+ this.disposed = true
63
+ this.queue = []
64
+ this.cache.clear()
65
+ }
66
+
67
+ private async pump() {
68
+ if (this.running) return
69
+ this.running = true
70
+ try {
71
+ while (!this.disposed && this.queue.length) {
72
+ const next = this.queue.shift()!
73
+ if (this.cache.has(next.key)) continue
74
+ const canvas = await this.take(next.time)
75
+ if (this.disposed) return
76
+ if (!canvas) continue
77
+ this.cache.set(next.key, canvas)
78
+ if (this.cache.size > CACHE_LIMIT) this.cache.delete(this.cache.keys().next().value as string)
79
+ this.onFrame(next.key, canvas)
80
+ }
81
+ } finally {
82
+ this.running = false
83
+ }
84
+ }
85
+
86
+ private async take(time: number): Promise<HTMLCanvasElement | null> {
87
+ const video = this.video
88
+ if (video.readyState < 1) {
89
+ const ready = await this.once("loadedmetadata", SEEK_TIMEOUT_MS)
90
+ if (!ready) return null
91
+ }
92
+ const target = Math.min(Math.max(0, time), Number.isFinite(video.duration) ? Math.max(0, video.duration - 0.05) : time)
93
+ const seeked = this.once("seeked", SEEK_TIMEOUT_MS)
94
+ if (video.fastSeek) video.fastSeek(target)
95
+ else video.currentTime = target
96
+ if (!(await seeked)) return null
97
+ await this.framePresented()
98
+ if (this.disposed || !video.videoWidth) return null
99
+ const { width, height } = this.size()
100
+ const ratio = Math.min(2, video.ownerDocument.defaultView?.devicePixelRatio ?? 1)
101
+ const canvas = video.ownerDocument.createElement("canvas")
102
+ canvas.width = Math.max(1, Math.round(width * ratio))
103
+ canvas.height = Math.max(1, Math.round(height * ratio))
104
+ const ctx = canvas.getContext("2d")
105
+ if (!ctx) return null
106
+ // Cover the tile, as object-fit: cover would: a strip of letterboxes reads as broken.
107
+ const scale = Math.max(canvas.width / video.videoWidth, canvas.height / video.videoHeight)
108
+ const w = video.videoWidth * scale
109
+ const h = video.videoHeight * scale
110
+ try {
111
+ ctx.drawImage(video, (canvas.width - w) / 2, (canvas.height - h) / 2, w, h)
112
+ } catch {
113
+ return null
114
+ }
115
+ return canvas
116
+ }
117
+
118
+ /*
119
+ * `seeked` says the position moved, not that its picture is ready to draw:
120
+ * Chromium can still hand the previous frame for a moment. Wait for the
121
+ * frame where the engine reports it, and a couple of paints where not.
122
+ */
123
+ private framePresented(): Promise<void> {
124
+ const video = this.video
125
+ const win = video.ownerDocument.defaultView
126
+ return new Promise((resolve) => {
127
+ let done = false
128
+ const finish = () => {
129
+ if (done) return
130
+ done = true
131
+ resolve()
132
+ }
133
+ if (video.requestVideoFrameCallback) {
134
+ video.requestVideoFrameCallback(finish)
135
+ win?.setTimeout(finish, FRAME_TIMEOUT_MS)
136
+ } else if (win) {
137
+ win.requestAnimationFrame(() => win.requestAnimationFrame(finish))
138
+ } else finish()
139
+ })
140
+ }
141
+
142
+ private once(event: string, timeout: number): Promise<boolean> {
143
+ const video = this.video
144
+ const win = video.ownerDocument.defaultView
145
+ return new Promise((resolve) => {
146
+ const finish = (ok: boolean) => {
147
+ video.removeEventListener(event, onEvent)
148
+ video.removeEventListener("error", onError)
149
+ if (timer) win?.clearTimeout(timer)
150
+ resolve(ok)
151
+ }
152
+ const onEvent = () => finish(true)
153
+ const onError = () => finish(false)
154
+ video.addEventListener(event, onEvent)
155
+ video.addEventListener("error", onError)
156
+ const timer = win?.setTimeout(() => finish(false), timeout)
157
+ })
158
+ }
159
+ }
160
+
161
+ /**
162
+ * Seconds one still stands for at this zoom: the shortest power of two that
163
+ * keeps a still at least `minPx` wide. Stills sit at fixed points in time, so
164
+ * panning reuses the ones already taken and a zoom step of two keeps half.
165
+ */
166
+ export function stillSpan(secondsPerPx: number, minPx: number): number {
167
+ const wanted = secondsPerPx * minPx
168
+ let span = 1 / 8
169
+ while (span < wanted && span < 8192) span *= 2
170
+ return span
171
+ }
@@ -0,0 +1,38 @@
1
+ /*
2
+ * How things come and go in LpVideoPlayer and LpVideoTrim, bound on a
3
+ * <Transition> with v-bind so every control, overlay and hint in both appears
4
+ * and leaves the same way, never in a single frame. Entrances take the
5
+ * medium duration and the emphasized curve; exits are quicker, since what is
6
+ * leaving no longer needs reading. Reduced motion swaps instantly.
7
+ */
8
+ export const FADE = {
9
+ enterActiveClass: "transition-opacity duration-(--duration-medium) ease-(--ease-emphasized) motion-reduce:transition-none",
10
+ leaveActiveClass: "transition-opacity duration-(--duration-fast) ease-(--ease-emphasized) motion-reduce:transition-none",
11
+ enterFromClass: "opacity-0",
12
+ leaveToClass: "opacity-0",
13
+ } as const
14
+
15
+ /** A fade with a small settle in scale: for a button or a box, which reads as arriving rather than blinking on. */
16
+ export const FADE_SCALE = {
17
+ enterActiveClass: "transition-[opacity,scale] duration-(--duration-medium) ease-(--ease-emphasized) motion-reduce:transition-none",
18
+ leaveActiveClass: "transition-[opacity,scale] duration-(--duration-fast) ease-(--ease-emphasized) motion-reduce:transition-none",
19
+ enterFromClass: "scale-90 opacity-0",
20
+ leaveToClass: "scale-90 opacity-0",
21
+ } as const
22
+
23
+ /** One icon handing over to another in the same spot (play and pause, sound on and off). */
24
+ export const ICON_SWAP = {
25
+ mode: "out-in",
26
+ enterActiveClass: "transition-[opacity,scale] duration-(--duration-fast) ease-(--ease-emphasized) motion-reduce:transition-none",
27
+ leaveActiveClass: "transition-[opacity,scale] duration-(--duration-fast) ease-(--ease-emphasized) motion-reduce:transition-none",
28
+ enterFromClass: "scale-75 opacity-0",
29
+ leaveToClass: "scale-75 opacity-0",
30
+ } as const
31
+
32
+ /**
33
+ * A ghost button over the picture: white on the video in every theme, as the
34
+ * lightbox's are. The players' own buttons use it, and their `#actions` and
35
+ * `#controls` slots hand it out as `buttonClass`, so an app's button there
36
+ * (a bin that takes the video back out of a dropzone) looks like theirs.
37
+ */
38
+ export const OVERLAY_BUTTON = "text-white/85 hover:bg-white/15 hover:text-white"