@leavepulse/ui 0.82.0 → 0.83.1

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 (263) 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-kRVTuSZd.js → ContextMenuItems-h-apPY2d.js} +1 -1
  4. package/dist/chunks/{DropzoneLinkField-CvMUu8HQ.js → DropzoneLinkField-_Op8b_ee.js} +1 -1
  5. package/dist/chunks/{FieldLeading-CNN_5Dp1.js → FieldLeading-mOMcwNqi.js} +1 -1
  6. package/dist/chunks/{FieldWrapper-BBJPEVHr.js → FieldWrapper-DtT-AIcn.js} +1 -1
  7. package/dist/chunks/{IconCloseButton-C59_etp2.js → IconCloseButton-YmisskyZ.js} +1 -1
  8. package/dist/chunks/{LayoutCanvas-BIY74SUe.js → LayoutCanvas-BrISZEBr.js} +1 -1
  9. package/dist/chunks/{LayoutNode-CTlKlhb-.js → LayoutNode-CwMQ8ACa.js} +1 -1
  10. package/dist/chunks/{LpActionBar-Cowb-28D.js → LpActionBar-CVVC6YbC.js} +1 -1
  11. package/dist/chunks/{LpAddressInput-nTJlUQ6K.js → LpAddressInput-tiuJ-bru.js} +7 -7
  12. package/dist/chunks/{LpAlert-DhZdjekN.js → LpAlert-8-x4H_Nk.js} +1 -1
  13. package/dist/chunks/{LpAppShell-CK9hTgQ1.js → LpAppShell-BW2YMq-A.js} +4 -4
  14. package/dist/chunks/{LpAutocomplete--PaZkQoU.js → LpAutocomplete-BZODIops.js} +4 -4
  15. package/dist/chunks/{LpAvatar-DVCld_L2.js → LpAvatar-BZq4nH53.js} +1 -1
  16. package/dist/chunks/{LpBadge-DXvpxk6z.js → LpBadge-Cz0pSSUu.js} +1 -1
  17. package/dist/chunks/{LpBottomNav-COQiBNUN.js → LpBottomNav-DVdR5Mry.js} +1 -1
  18. package/dist/chunks/{LpBreadcrumbs-Dt1O5G4O.js → LpBreadcrumbs-B2jphAel.js} +1 -1
  19. package/dist/chunks/{LpCalendar-C8Xlukd2.js → LpCalendar-C0_N5LVT.js} +1 -1
  20. package/dist/chunks/{LpCheckbox-Dpybd3oV.js → LpCheckbox-CwqLNdw4.js} +2 -2
  21. package/dist/chunks/{LpCodeBlock-C1Hu5RyT.js → LpCodeBlock-Dcss6bnl.js} +2 -2
  22. package/dist/chunks/{LpCodeDisplay-BVruT3-A.js → LpCodeDisplay-CLms1zDY.js} +4 -4
  23. package/dist/chunks/{LpColorPicker-BK-NRaGK.js → LpColorPicker-CbIY450X.js} +3 -3
  24. package/dist/chunks/{LpConfirmDialog-2CQFKtqU.js → LpConfirmDialog-RmP4z6va.js} +2 -2
  25. package/dist/chunks/{LpConfirmHost-CTkSytVl.js → LpConfirmHost-DheBgRMB.js} +1 -1
  26. package/dist/chunks/{LpContextMenu-DcHPdW-t.js → LpContextMenu-DIYhlap8.js} +2 -2
  27. package/dist/chunks/{LpContextSwitcher-C_nnDbeI.js → LpContextSwitcher-fDA6ERl-.js} +2 -2
  28. package/dist/chunks/{LpCopyButton-De7_elaO.js → LpCopyButton-DSqKFGEb.js} +1 -1
  29. package/dist/chunks/{LpDataList-BjZPfQjO.js → LpDataList-f9UjSx9m.js} +2 -2
  30. package/dist/chunks/{LpDatePicker-wOrHd0fg.js → LpDatePicker-3kDQLVbg.js} +5 -5
  31. package/dist/chunks/{LpDateRangePicker-CVMvvo5t.js → LpDateRangePicker-DUPiTt0_.js} +3 -3
  32. package/dist/chunks/{LpDisclosure-CtQ63LeO.js → LpDisclosure-C5WZjhbQ.js} +1 -1
  33. package/dist/chunks/{LpDrawer-TZBPzyOM.js → LpDrawer-CkYvIGIa.js} +1 -1
  34. package/dist/chunks/{LpDropdownMenu-DL9LgkS8.js → LpDropdownMenu-zSsK2Xra.js} +6 -3
  35. package/dist/chunks/{LpEditable-DuPE2tkl.js → LpEditable-CP7Y_lKg.js} +2 -2
  36. package/dist/chunks/{LpEmptyState-W1U_tKg-.js → LpEmptyState-dsc5x-ya.js} +1 -1
  37. package/dist/chunks/{LpFieldNotice-BLKlYLwu.js → LpFieldNotice-TOTbWvgv.js} +1 -1
  38. package/dist/chunks/{LpFileTree-CEiRIpj1.js → LpFileTree-BPjnOTln.js} +4 -4
  39. package/dist/chunks/{LpFileTreeNode-C0292AU2.js → LpFileTreeNode-_-MiWmie.js} +4 -4
  40. package/dist/chunks/{LpFormField-Bhgi0idH.js → LpFormField-BJwD1iEF.js} +1 -1
  41. package/dist/chunks/{LpHintLayer-D8ZKsvsT.js → LpHintLayer-CmfGnxYu.js} +1 -1
  42. package/dist/chunks/{LpHints-DbIV-wk4.js → LpHints-C2Xvc1Fd.js} +3 -3
  43. package/dist/chunks/{LpIcon-CytpcLmS.js → LpIcon-CoOLz1oJ.js} +1 -1
  44. package/dist/chunks/{LpInfraNode-8cGoY87Q.js → LpInfraNode-CW3qsM-U.js} +2 -2
  45. package/dist/chunks/{LpInput-P2C12X7s.js → LpInput-BVWhkPuN.js} +3 -3
  46. package/dist/chunks/{LpInspector-DjfMa4tD.js → LpInspector-REmoX_es.js} +4 -4
  47. package/dist/chunks/{LpLightbox-BEQtCz0m.js → LpLightbox-C3FFTYP5.js} +65 -20
  48. package/dist/chunks/{LpLogInspector-CyxK1d-V.js → LpLogInspector-Cs8Z_fM0.js} +2 -2
  49. package/dist/chunks/{LpLogViewer-CZrc_HG7.js → LpLogViewer-DQRGe-ZK.js} +6 -6
  50. package/dist/chunks/{LpMapPicker-CbTfFcEu.js → LpMapPicker-CQnQPgKm.js} +1 -1
  51. package/dist/chunks/{LpMediaGallery-C_SwNR4y.js → LpMediaGallery-DkKqTy9P.js} +20 -8
  52. package/dist/chunks/{LpModal-B6Yt0G7E.js → LpModal-DwejR7VP.js} +1 -1
  53. package/dist/chunks/{LpModalNav-CMYWEiUs.js → LpModalNav-D1-dSe6f.js} +3 -3
  54. package/dist/chunks/{LpNotificationBell-BJKPx0X1.js → LpNotificationBell-EKLkN0sp.js} +4 -4
  55. package/dist/chunks/{LpNotificationFeed-orWISVez.js → LpNotificationFeed-Dc-yQ4NH.js} +4 -4
  56. package/dist/chunks/{LpNumberField-C5MeLsoE.js → LpNumberField-DvUjEMp-.js} +3 -3
  57. package/dist/chunks/{LpOtpInput-6SCQ64uK.js → LpOtpInput-xvDO14f4.js} +1 -1
  58. package/dist/chunks/{LpPagination-CkwbdFa9.js → LpPagination-BFZY8dFn.js} +1 -1
  59. package/dist/chunks/{LpPaletteTheme-RWgbWFOz.js → LpPaletteTheme-EBJv0Bwz.js} +2 -2
  60. package/dist/chunks/{LpPasswordInput-DJBRo5lt.js → LpPasswordInput-DRJR3fF1.js} +2 -2
  61. package/dist/chunks/{LpPhoneInput-DcmcJwos.js → LpPhoneInput-BGOOtUoW.js} +3 -3
  62. package/dist/chunks/{LpPie-DmMlhgo2.js → LpPie-Deyn8h5J.js} +2 -2
  63. package/dist/chunks/{LpPipeline-BXcp2shV.js → LpPipeline-9ChjeNh5.js} +2 -2
  64. package/dist/chunks/{LpQuota-CtEiqRg4.js → LpQuota-CNh5eF_p.js} +1 -1
  65. package/dist/chunks/{LpRadioGroup-Ubkk_eSq.js → LpRadioGroup-Br2Nl9Gr.js} +2 -2
  66. package/dist/chunks/{LpRangeCalendar-DCswVYbU.js → LpRangeCalendar-D66Y3U_w.js} +1 -1
  67. package/dist/chunks/{LpRangeSlider-DM1qy5Tp.js → LpRangeSlider-eX-fIlOw.js} +1 -1
  68. package/dist/chunks/{LpRepeater-BMo2Eabn.js → LpRepeater-0XvnlVd_.js} +2 -2
  69. package/dist/chunks/{LpSecretField-SSRGVqRc.js → LpSecretField-1RCooChq.js} +5 -5
  70. package/dist/chunks/{LpSection-C3bTxD7t.js → LpSection-B9gbJJcP.js} +1 -1
  71. package/dist/chunks/{LpSegmented-C1DF6m6A.js → LpSegmented-hIjGzGTo.js} +2 -2
  72. package/dist/chunks/{LpSelect-DvDr1VPn.js → LpSelect-DugubwTc.js} +4 -4
  73. package/dist/chunks/{LpSidebar-Bql881r6.js → LpSidebar-DevSoN8W.js} +4 -4
  74. package/dist/chunks/{LpSidebarNav-CKx2KJvr.js → LpSidebarNav-DwQQ3KXe.js} +3 -3
  75. package/dist/chunks/{LpSidebarRow-BMOL9kOG.js → LpSidebarRow-CAGgtJCS.js} +3 -3
  76. package/dist/chunks/{LpSlider-DsSMe4b5.js → LpSlider-1fHpVxpB.js} +1 -1
  77. package/dist/chunks/{LpStat-CQYYDMS6.js → LpStat-Cm9-FaWQ.js} +2 -2
  78. package/dist/chunks/{LpStatus-g5nSJfU3.js → LpStatus-CE8FSAT1.js} +1 -1
  79. package/dist/chunks/{LpStepMark-seVNOuNJ.js → LpStepMark-BWSlV69R.js} +1 -1
  80. package/dist/chunks/{LpStepper-D6qEXihZ.js → LpStepper-1D0ZA7Nx.js} +1 -1
  81. package/dist/chunks/{LpSwitch-DM5CVm31.js → LpSwitch-D_kvE0oQ.js} +1 -1
  82. package/dist/chunks/{LpTabFace-ZWwelqRU.js → LpTabFace-CpbFKi-N.js} +2 -2
  83. package/dist/chunks/{LpTableFilter-CWU9Dmk-.js → LpTableFilter-BYHyCu38.js} +2 -2
  84. package/dist/chunks/{LpTagsInput-C2qVF2ax.js → LpTagsInput-Cqm38t5N.js} +5 -5
  85. package/dist/chunks/{LpTextarea-C3EoV_xv.js → LpTextarea-DaSKBkiV.js} +1 -1
  86. package/dist/chunks/{LpThemeStudio-BIBahaIs.js → LpThemeStudio-BNdfi-Ds.js} +5 -5
  87. package/dist/chunks/{LpThemeSwitcher-D4MqFLGS.js → LpThemeSwitcher-CbC1nqBP.js} +3 -3
  88. package/dist/chunks/{LpTimeField-DPOkbvyP.js → LpTimeField-CCfPCWPT.js} +2 -2
  89. package/dist/chunks/{LpToaster-Dj1VKxFv.js → LpToaster-BhlI7h-h.js} +2 -2
  90. package/dist/chunks/{LpTopologyCanvas-Bspzwm7x.js → LpTopologyCanvas-DlKbu7E5.js} +4 -4
  91. package/dist/chunks/{LpTour-CwpVdVN-.js → LpTour-CSLerA1B.js} +1 -1
  92. package/dist/chunks/{LpTruncate-JSaIW25q.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-RVcP436Z.js → LpWindow-YnF_knSj.js} +2 -2
  96. package/dist/chunks/{LpWorkspaceSwitcher-D0WOz1Fg.js → LpWorkspaceSwitcher-B-5UU6o4.js} +3 -3
  97. package/dist/chunks/{MenuTitle-D8Umab23.js → MenuTitle-BnZbFSwq.js} +1 -1
  98. package/dist/chunks/{OptionContent-DCnX_KRK.js → OptionContent-D5e_eklA.js} +1 -1
  99. package/dist/chunks/{PickerField-ChjY27bm.js → PickerField-KpHiowNI.js} +1 -1
  100. package/dist/chunks/{RevealToggle-MaL7PEjr.js → RevealToggle-CE1hAIJB.js} +1 -1
  101. package/dist/chunks/SteadyText-CwH-kduj.js +25 -0
  102. package/dist/chunks/{TourCard-oXZSpHKb.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 +19 -5
  213. package/dist/components/LpVideoTrim.vue.js +596 -445
  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/lightbox.d.ts +13 -0
  230. package/dist/components/lightbox.js +11 -1
  231. package/dist/components/menuWhen.js +1 -1
  232. package/dist/components/videoBackdrop.d.ts +15 -2
  233. package/dist/components/videoBackdrop.js +8 -2
  234. package/dist/components/videoMotion.d.ts +28 -0
  235. package/dist/components/videoMotion.js +26 -0
  236. package/dist/components/videoTrim.d.ts +2 -9
  237. package/dist/components/videoTrim.js +0 -6
  238. package/dist/composables/useVideoMedia.d.ts +74 -0
  239. package/dist/composables/useVideoMedia.js +291 -0
  240. package/dist/icons-all.js +1 -1
  241. package/dist/index.d.ts +4 -1
  242. package/dist/index.js +173 -168
  243. package/package.json +5 -2
  244. package/src/component-names.ts +1 -0
  245. package/src/components/LpDropdownMenu.vue +4 -0
  246. package/src/components/LpLightbox.vue +61 -8
  247. package/src/components/LpMediaGallery.vue +19 -2
  248. package/src/components/LpVideoBackdrop.vue +1 -7
  249. package/src/components/LpVideoPlayer.vue +774 -0
  250. package/src/components/LpVideoTrim.vue +437 -295
  251. package/src/components/SteadyText.vue +21 -0
  252. package/src/components/VideoFlash.vue +52 -0
  253. package/src/components/VideoThumb.vue +66 -0
  254. package/src/components/VideoVolume.vue +63 -0
  255. package/src/components/lightbox.ts +23 -0
  256. package/src/components/videoBackdrop.ts +20 -3
  257. package/src/components/videoMotion.ts +38 -0
  258. package/src/components/videoTrim.ts +2 -14
  259. package/src/composables/useVideoMedia.ts +411 -0
  260. package/src/index.ts +4 -1
  261. package/src/kit-icons.ts +1 -1
  262. package/dist/assets/LpVideoTrim-Ci6IIHW0.css +0 -8
  263. package/dist/chunks/icons-BOdqDUii.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>
@@ -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,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"
@@ -182,17 +182,5 @@ export function adjustRegion(r: FrameRegion, handle: RegionHandle, dx: number, d
182
182
  return { x: fix(x0), y: fix(y0), w: fix(x1 - x0), h: fix(y1 - y0) }
183
183
  }
184
184
 
185
- /**
186
- * Why the editor has no video to show, as its `error` event reports it:
187
- * - "load": the address did not answer (network, 404, an expired session);
188
- * - "decode": the file arrived but is damaged;
189
- * - "unsupported": a format or codec this browser cannot play (H.265 in
190
- * Chromium without hardware support, ProRes anywhere).
191
- */
192
- export type VideoTrimErrorCause = "load" | "decode" | "unsupported"
193
-
194
- export function mediaErrorCause(error: MediaError | null): VideoTrimErrorCause {
195
- if (error?.code === 3) return "decode"
196
- if (error?.code === 4) return "unsupported"
197
- return "load"
198
- }
185
+ /** Why the editor has no video to show; see `VideoErrorCause`. */
186
+ export type { VideoErrorCause as VideoTrimErrorCause } from "../composables/useVideoMedia"