@editframe/elements 0.37.3-beta → 0.38.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 (327) hide show
  1. package/dist/EF_FRAMEGEN.js +17 -14
  2. package/dist/EF_FRAMEGEN.js.map +1 -1
  3. package/dist/EF_RENDERING.js.map +1 -1
  4. package/dist/canvas/EFCanvas.d.ts +9 -2
  5. package/dist/canvas/EFCanvas.js +14 -4
  6. package/dist/canvas/EFCanvas.js.map +1 -1
  7. package/dist/canvas/EFCanvasItem.d.ts +2 -2
  8. package/dist/canvas/overlays/SelectionOverlay.d.ts +10 -2
  9. package/dist/canvas/overlays/SelectionOverlay.js +5 -12
  10. package/dist/canvas/overlays/SelectionOverlay.js.map +1 -1
  11. package/dist/canvas/overlays/overlayState.js.map +1 -1
  12. package/dist/canvas/selection/SelectionController.js.map +1 -1
  13. package/dist/elements/EFAudio.d.ts +1 -11
  14. package/dist/elements/EFAudio.js +2 -10
  15. package/dist/elements/EFAudio.js.map +1 -1
  16. package/dist/elements/EFCaptions.d.ts +5 -9
  17. package/dist/elements/EFCaptions.js +34 -11
  18. package/dist/elements/EFCaptions.js.map +1 -1
  19. package/dist/elements/EFImage.d.ts +10 -8
  20. package/dist/elements/EFImage.js +117 -32
  21. package/dist/elements/EFImage.js.map +1 -1
  22. package/dist/elements/EFMedia/AssetMediaEngine.js +2 -2
  23. package/dist/elements/EFMedia/AssetMediaEngine.js.map +1 -1
  24. package/dist/elements/EFMedia/BaseMediaEngine.js +15 -92
  25. package/dist/elements/EFMedia/BaseMediaEngine.js.map +1 -1
  26. package/dist/elements/EFMedia/BufferedSeekingInput.js +10 -11
  27. package/dist/elements/EFMedia/BufferedSeekingInput.js.map +1 -1
  28. package/dist/elements/EFMedia/{AssetIdMediaEngine.js → FileMediaEngine.js} +44 -24
  29. package/dist/elements/EFMedia/FileMediaEngine.js.map +1 -0
  30. package/dist/elements/EFMedia/JitMediaEngine.js +14 -13
  31. package/dist/elements/EFMedia/JitMediaEngine.js.map +1 -1
  32. package/dist/elements/EFMedia/shared/AudioSpanUtils.js +3 -3
  33. package/dist/elements/EFMedia/shared/AudioSpanUtils.js.map +1 -1
  34. package/dist/elements/EFMedia/shared/ThumbnailExtractor.js +12 -7
  35. package/dist/elements/EFMedia/shared/ThumbnailExtractor.js.map +1 -1
  36. package/dist/elements/EFMedia/shared/timeoutUtils.js +44 -0
  37. package/dist/elements/EFMedia/shared/timeoutUtils.js.map +1 -0
  38. package/dist/elements/EFMedia/videoTasks/MainVideoInputCache.js +1 -1
  39. package/dist/elements/EFMedia/videoTasks/MainVideoInputCache.js.map +1 -1
  40. package/dist/elements/EFMedia/videoTasks/ScrubInputCache.js +4 -4
  41. package/dist/elements/EFMedia/videoTasks/ScrubInputCache.js.map +1 -1
  42. package/dist/elements/EFMedia.d.ts +14 -8
  43. package/dist/elements/EFMedia.js +52 -19
  44. package/dist/elements/EFMedia.js.map +1 -1
  45. package/dist/elements/EFPanZoom.d.ts +2 -2
  46. package/dist/elements/EFPanZoom.js +1 -1
  47. package/dist/elements/EFPanZoom.js.map +1 -1
  48. package/dist/elements/EFSourceMixin.js +16 -8
  49. package/dist/elements/EFSourceMixin.js.map +1 -1
  50. package/dist/elements/EFSurface.d.ts +5 -8
  51. package/dist/elements/EFSurface.js +4 -43
  52. package/dist/elements/EFSurface.js.map +1 -1
  53. package/dist/elements/EFTemporal.d.ts +33 -8
  54. package/dist/elements/EFTemporal.js +92 -40
  55. package/dist/elements/EFTemporal.js.map +1 -1
  56. package/dist/elements/EFText.d.ts +3 -0
  57. package/dist/elements/EFText.js +54 -21
  58. package/dist/elements/EFText.js.map +1 -1
  59. package/dist/elements/EFTextSegment.js +8 -4
  60. package/dist/elements/EFTextSegment.js.map +1 -1
  61. package/dist/elements/EFTimegroup.d.ts +26 -43
  62. package/dist/elements/EFTimegroup.js +295 -314
  63. package/dist/elements/EFTimegroup.js.map +1 -1
  64. package/dist/elements/EFVideo.d.ts +44 -42
  65. package/dist/elements/EFVideo.js +259 -172
  66. package/dist/elements/EFVideo.js.map +1 -1
  67. package/dist/elements/EFWaveform.d.ts +3 -8
  68. package/dist/elements/EFWaveform.js +18 -13
  69. package/dist/elements/EFWaveform.js.map +1 -1
  70. package/dist/elements/ElementPositionInfo.js.map +1 -1
  71. package/dist/elements/FetchMixin.js.map +1 -1
  72. package/dist/elements/TargetController.d.ts +0 -3
  73. package/dist/elements/TargetController.js +12 -35
  74. package/dist/elements/TargetController.js.map +1 -1
  75. package/dist/elements/TimegroupController.js.map +1 -1
  76. package/dist/elements/cloneFactoryRegistry.d.ts +14 -0
  77. package/dist/elements/cloneFactoryRegistry.js +15 -0
  78. package/dist/elements/cloneFactoryRegistry.js.map +1 -0
  79. package/dist/elements/renderTemporalAudio.js +8 -6
  80. package/dist/elements/renderTemporalAudio.js.map +1 -1
  81. package/dist/elements/setupTemporalHierarchy.js +62 -0
  82. package/dist/elements/setupTemporalHierarchy.js.map +1 -0
  83. package/dist/elements/updateAnimations.js +62 -87
  84. package/dist/elements/updateAnimations.js.map +1 -1
  85. package/dist/getRenderInfo.d.ts +3 -2
  86. package/dist/getRenderInfo.js +20 -4
  87. package/dist/getRenderInfo.js.map +1 -1
  88. package/dist/gui/ContextMixin.js +68 -12
  89. package/dist/gui/ContextMixin.js.map +1 -1
  90. package/dist/gui/Controllable.js +1 -1
  91. package/dist/gui/Controllable.js.map +1 -1
  92. package/dist/gui/EFActiveRootTemporal.d.ts +2 -2
  93. package/dist/gui/EFActiveRootTemporal.js.map +1 -1
  94. package/dist/gui/EFControls.d.ts +2 -2
  95. package/dist/gui/EFControls.js +2 -2
  96. package/dist/gui/EFControls.js.map +1 -1
  97. package/dist/gui/EFDial.d.ts +2 -2
  98. package/dist/gui/EFDial.js +12 -9
  99. package/dist/gui/EFDial.js.map +1 -1
  100. package/dist/gui/EFFilmstrip.d.ts +2 -0
  101. package/dist/gui/EFFilmstrip.js +18 -10
  102. package/dist/gui/EFFilmstrip.js.map +1 -1
  103. package/dist/gui/EFFitScale.d.ts +28 -4
  104. package/dist/gui/EFFitScale.js +88 -26
  105. package/dist/gui/EFFitScale.js.map +1 -1
  106. package/dist/gui/EFFocusOverlay.d.ts +2 -2
  107. package/dist/gui/EFFocusOverlay.js +3 -3
  108. package/dist/gui/EFFocusOverlay.js.map +1 -1
  109. package/dist/gui/EFOverlayItem.d.ts +2 -2
  110. package/dist/gui/EFOverlayLayer.d.ts +2 -2
  111. package/dist/gui/EFPause.d.ts +2 -2
  112. package/dist/gui/EFPause.js +1 -1
  113. package/dist/gui/EFPlay.d.ts +2 -2
  114. package/dist/gui/EFPlay.js +1 -1
  115. package/dist/gui/EFPreview.js +1 -1
  116. package/dist/gui/EFResizableBox.d.ts +2 -2
  117. package/dist/gui/EFResizableBox.js +5 -5
  118. package/dist/gui/EFResizableBox.js.map +1 -1
  119. package/dist/gui/EFScrubber.d.ts +2 -2
  120. package/dist/gui/EFScrubber.js +8 -13
  121. package/dist/gui/EFScrubber.js.map +1 -1
  122. package/dist/gui/EFTimeDisplay.d.ts +6 -2
  123. package/dist/gui/EFTimeDisplay.js +25 -7
  124. package/dist/gui/EFTimeDisplay.js.map +1 -1
  125. package/dist/gui/EFTimelineRuler.d.ts +2 -2
  126. package/dist/gui/EFTimelineRuler.js +3 -3
  127. package/dist/gui/EFTimelineRuler.js.map +1 -1
  128. package/dist/gui/EFToggleLoop.d.ts +2 -2
  129. package/dist/gui/EFToggleLoop.js +1 -1
  130. package/dist/gui/EFTogglePlay.d.ts +2 -2
  131. package/dist/gui/EFTogglePlay.js +1 -1
  132. package/dist/gui/EFTransformHandles.d.ts +2 -2
  133. package/dist/gui/EFTransformHandles.js +6 -6
  134. package/dist/gui/EFTransformHandles.js.map +1 -1
  135. package/dist/gui/EFWorkbench.d.ts +40 -36
  136. package/dist/gui/EFWorkbench.js +436 -822
  137. package/dist/gui/EFWorkbench.js.map +1 -1
  138. package/dist/gui/FitScaleHelpers.js.map +1 -1
  139. package/dist/gui/PlaybackController.d.ts +3 -8
  140. package/dist/gui/PlaybackController.js +59 -56
  141. package/dist/gui/PlaybackController.js.map +1 -1
  142. package/dist/gui/TWMixin.js +1 -1
  143. package/dist/gui/TWMixin.js.map +1 -1
  144. package/dist/gui/TargetOrContextMixin.js +43 -6
  145. package/dist/gui/TargetOrContextMixin.js.map +1 -1
  146. package/dist/gui/ef-theme.css +136 -0
  147. package/dist/gui/hierarchy/EFHierarchy.d.ts +2 -2
  148. package/dist/gui/hierarchy/EFHierarchy.js +14 -24
  149. package/dist/gui/hierarchy/EFHierarchy.js.map +1 -1
  150. package/dist/gui/hierarchy/EFHierarchyItem.d.ts +3 -3
  151. package/dist/gui/hierarchy/EFHierarchyItem.js +22 -10
  152. package/dist/gui/hierarchy/EFHierarchyItem.js.map +1 -1
  153. package/dist/gui/icons.js.map +1 -1
  154. package/dist/gui/previewSettingsContext.d.ts +18 -0
  155. package/dist/gui/previewSettingsContext.js.map +1 -1
  156. package/dist/gui/theme.js +34 -0
  157. package/dist/gui/theme.js.map +1 -0
  158. package/dist/gui/timeline/EFTimeline.d.ts +2 -2
  159. package/dist/gui/timeline/EFTimeline.js +70 -52
  160. package/dist/gui/timeline/EFTimeline.js.map +1 -1
  161. package/dist/gui/timeline/EFTimelineRow.d.ts +5 -3
  162. package/dist/gui/timeline/EFTimelineRow.js +55 -32
  163. package/dist/gui/timeline/EFTimelineRow.js.map +1 -1
  164. package/dist/gui/timeline/TrimHandles.d.ts +23 -9
  165. package/dist/gui/timeline/TrimHandles.js +224 -51
  166. package/dist/gui/timeline/TrimHandles.js.map +1 -1
  167. package/dist/gui/timeline/flattenHierarchy.js.map +1 -1
  168. package/dist/gui/timeline/timelineEditingContext.d.ts +34 -0
  169. package/dist/gui/timeline/timelineEditingContext.js +24 -0
  170. package/dist/gui/timeline/timelineEditingContext.js.map +1 -0
  171. package/dist/gui/timeline/timelineStateContext.js.map +1 -1
  172. package/dist/gui/timeline/tracks/AudioTrack.js +1 -1
  173. package/dist/gui/timeline/tracks/AudioTrack.js.map +1 -1
  174. package/dist/gui/timeline/tracks/CaptionsTrack.d.ts +2 -3
  175. package/dist/gui/timeline/tracks/CaptionsTrack.js +17 -75
  176. package/dist/gui/timeline/tracks/CaptionsTrack.js.map +1 -1
  177. package/dist/gui/timeline/tracks/EFThumbnailStrip.d.ts +52 -0
  178. package/dist/gui/timeline/tracks/EFThumbnailStrip.js +596 -0
  179. package/dist/gui/timeline/tracks/EFThumbnailStrip.js.map +1 -0
  180. package/dist/gui/timeline/tracks/HTMLTrack.js.map +1 -1
  181. package/dist/gui/timeline/tracks/ImageTrack.js.map +1 -1
  182. package/dist/gui/timeline/tracks/TextTrack.d.ts +3 -2
  183. package/dist/gui/timeline/tracks/TextTrack.js +17 -43
  184. package/dist/gui/timeline/tracks/TextTrack.js.map +1 -1
  185. package/dist/gui/timeline/tracks/TimegroupTrack.d.ts +3 -4
  186. package/dist/gui/timeline/tracks/TimegroupTrack.js +33 -23
  187. package/dist/gui/timeline/tracks/TimegroupTrack.js.map +1 -1
  188. package/dist/gui/timeline/tracks/TrackItem.d.ts +7 -9
  189. package/dist/gui/timeline/tracks/TrackItem.js +18 -17
  190. package/dist/gui/timeline/tracks/TrackItem.js.map +1 -1
  191. package/dist/gui/timeline/tracks/VideoTrack.d.ts +3 -3
  192. package/dist/gui/timeline/tracks/VideoTrack.js +11 -14
  193. package/dist/gui/timeline/tracks/VideoTrack.js.map +1 -1
  194. package/dist/gui/timeline/tracks/WaveformTrack.js.map +1 -1
  195. package/dist/gui/timeline/tracks/renderTrackChildren.js.map +1 -1
  196. package/dist/gui/timeline/tracks/waveformUtils.js +1 -1
  197. package/dist/gui/timeline/tracks/waveformUtils.js.map +1 -1
  198. package/dist/gui/tree/EFTree.d.ts +2 -2
  199. package/dist/gui/tree/EFTree.js +8 -14
  200. package/dist/gui/tree/EFTree.js.map +1 -1
  201. package/dist/gui/tree/EFTreeItem.d.ts +2 -2
  202. package/dist/gui/tree/EFTreeItem.js +3 -3
  203. package/dist/gui/tree/EFTreeItem.js.map +1 -1
  204. package/dist/gui/tree/treeContext.js.map +1 -1
  205. package/dist/index.d.ts +10 -8
  206. package/dist/index.js +6 -5
  207. package/dist/index.js.map +1 -1
  208. package/dist/node.d.ts +2 -2
  209. package/dist/node.js +2 -2
  210. package/dist/preview/AdaptiveResolutionTracker.js +3 -3
  211. package/dist/preview/AdaptiveResolutionTracker.js.map +1 -1
  212. package/dist/preview/FrameController.d.ts +2 -17
  213. package/dist/preview/FrameController.js +40 -63
  214. package/dist/preview/FrameController.js.map +1 -1
  215. package/dist/preview/QualityUpgradeScheduler.d.ts +76 -0
  216. package/dist/preview/QualityUpgradeScheduler.js +158 -0
  217. package/dist/preview/QualityUpgradeScheduler.js.map +1 -0
  218. package/dist/preview/RenderContext.d.ts +119 -1
  219. package/dist/preview/RenderContext.js +21 -3
  220. package/dist/preview/RenderContext.js.map +1 -1
  221. package/dist/preview/RenderProfiler.js.map +1 -1
  222. package/dist/preview/RenderStats.js +85 -0
  223. package/dist/preview/RenderStats.js.map +1 -0
  224. package/dist/preview/encoding/canvasEncoder.js +2 -52
  225. package/dist/preview/encoding/canvasEncoder.js.map +1 -1
  226. package/dist/preview/encoding/mainThreadEncoder.js.map +1 -1
  227. package/dist/preview/encoding/workerEncoder.js.map +1 -1
  228. package/dist/preview/logger.js.map +1 -1
  229. package/dist/preview/previewSettings.d.ts +34 -0
  230. package/dist/preview/previewSettings.js +29 -17
  231. package/dist/preview/previewSettings.js.map +1 -1
  232. package/dist/preview/previewTypes.js +4 -4
  233. package/dist/preview/previewTypes.js.map +1 -1
  234. package/dist/preview/renderElementToCanvas.d.ts +44 -0
  235. package/dist/preview/renderElementToCanvas.js +72 -0
  236. package/dist/preview/renderElementToCanvas.js.map +1 -0
  237. package/dist/preview/renderTimegroupToCanvas.d.ts +134 -32
  238. package/dist/preview/renderTimegroupToCanvas.js +321 -146
  239. package/dist/preview/renderTimegroupToCanvas.js.map +1 -1
  240. package/dist/preview/renderTimegroupToCanvas.types.d.ts +51 -0
  241. package/dist/preview/renderTimegroupToVideo.d.ts +20 -35
  242. package/dist/preview/renderTimegroupToVideo.js +94 -106
  243. package/dist/preview/renderTimegroupToVideo.js.map +1 -1
  244. package/dist/preview/renderTimegroupToVideo.types.d.ts +42 -0
  245. package/dist/preview/renderVideoToVideo.js +286 -0
  246. package/dist/preview/renderVideoToVideo.js.map +1 -0
  247. package/dist/preview/renderers.d.ts +56 -0
  248. package/dist/preview/renderers.js +13 -1
  249. package/dist/preview/renderers.js.map +1 -1
  250. package/dist/preview/rendering/ScaleConfig.js +74 -0
  251. package/dist/preview/rendering/ScaleConfig.js.map +1 -0
  252. package/dist/preview/rendering/inlineImages.d.ts +13 -0
  253. package/dist/preview/rendering/inlineImages.js +7 -44
  254. package/dist/preview/rendering/inlineImages.js.map +1 -1
  255. package/dist/preview/rendering/loadImage.d.ts +8 -0
  256. package/dist/preview/rendering/loadImage.js +22 -0
  257. package/dist/preview/rendering/loadImage.js.map +1 -0
  258. package/dist/preview/rendering/renderToImageNative.js +3 -3
  259. package/dist/preview/rendering/renderToImageNative.js.map +1 -1
  260. package/dist/preview/rendering/serializeTimelineDirect.js +224 -68
  261. package/dist/preview/rendering/serializeTimelineDirect.js.map +1 -1
  262. package/dist/preview/statsTrackingStrategy.js +1 -101
  263. package/dist/preview/statsTrackingStrategy.js.map +1 -1
  264. package/dist/preview/workers/WorkerPool.js +0 -1
  265. package/dist/preview/workers/WorkerPool.js.map +1 -1
  266. package/dist/preview/workers/encoderWorkerInline.js +21 -54
  267. package/dist/preview/workers/encoderWorkerInline.js.map +1 -1
  268. package/dist/render/EFRenderAPI.d.ts +2 -1
  269. package/dist/render/EFRenderAPI.js +12 -36
  270. package/dist/render/EFRenderAPI.js.map +1 -1
  271. package/dist/render/getRenderData.js +4 -4
  272. package/dist/render/getRenderData.js.map +1 -1
  273. package/dist/style.css +114 -163
  274. package/dist/transcoding/cache/RequestDeduplicator.js +1 -0
  275. package/dist/transcoding/cache/RequestDeduplicator.js.map +1 -1
  276. package/dist/transcoding/types/index.d.ts +1 -1
  277. package/dist/transcoding/utils/UrlGenerator.js +10 -3
  278. package/dist/transcoding/utils/UrlGenerator.js.map +1 -1
  279. package/dist/utils/LRUCache.js +1 -0
  280. package/dist/utils/LRUCache.js.map +1 -1
  281. package/dist/utils/frameTime.js +23 -1
  282. package/dist/utils/frameTime.js.map +1 -1
  283. package/package.json +45 -8
  284. package/scripts/build-css.js +8 -1
  285. package/test/setup.ts +0 -1
  286. package/test/useAssetMSW.ts +50 -0
  287. package/test/visualRegressionUtils.ts +23 -9
  288. package/tsdown.config.ts +6 -1
  289. package/dist/_virtual/rolldown_runtime.js +0 -27
  290. package/dist/elements/EFMedia/AssetIdMediaEngine.js.map +0 -1
  291. package/dist/elements/EFThumbnailStrip.d.ts +0 -167
  292. package/dist/elements/EFThumbnailStrip.js +0 -731
  293. package/dist/elements/EFThumbnailStrip.js.map +0 -1
  294. package/dist/elements/SessionThumbnailCache.js +0 -154
  295. package/dist/elements/SessionThumbnailCache.js.map +0 -1
  296. package/dist/node_modules/react/cjs/react-jsx-runtime.development.js +0 -688
  297. package/dist/node_modules/react/cjs/react-jsx-runtime.development.js.map +0 -1
  298. package/dist/node_modules/react/cjs/react.development.js +0 -1521
  299. package/dist/node_modules/react/cjs/react.development.js.map +0 -1
  300. package/dist/node_modules/react/index.js +0 -13
  301. package/dist/node_modules/react/index.js.map +0 -1
  302. package/dist/node_modules/react/jsx-runtime.js +0 -13
  303. package/dist/node_modules/react/jsx-runtime.js.map +0 -1
  304. package/dist/preview/encoding/types.d.ts +0 -1
  305. package/dist/preview/renderTimegroupPreview.js +0 -686
  306. package/dist/preview/renderTimegroupPreview.js.map +0 -1
  307. package/dist/preview/rendering/renderToImage.d.ts +0 -2
  308. package/dist/preview/rendering/renderToImage.js +0 -95
  309. package/dist/preview/rendering/renderToImage.js.map +0 -1
  310. package/dist/preview/rendering/renderToImageForeignObject.js +0 -163
  311. package/dist/preview/rendering/renderToImageForeignObject.js.map +0 -1
  312. package/dist/preview/rendering/renderToImageNative.d.ts +0 -1
  313. package/dist/preview/rendering/svgSerializer.js +0 -43
  314. package/dist/preview/rendering/svgSerializer.js.map +0 -1
  315. package/dist/preview/rendering/types.d.ts +0 -2
  316. package/dist/preview/thumbnailCacheSettings.js +0 -52
  317. package/dist/preview/thumbnailCacheSettings.js.map +0 -1
  318. package/dist/sandbox/PlaybackControls.d.ts +0 -1
  319. package/dist/sandbox/PlaybackControls.js +0 -10
  320. package/dist/sandbox/PlaybackControls.js.map +0 -1
  321. package/dist/sandbox/ScenarioRunner.d.ts +0 -1
  322. package/dist/sandbox/ScenarioRunner.js +0 -1
  323. package/dist/sandbox/defineSandbox.d.ts +0 -1
  324. package/dist/sandbox/index.d.ts +0 -3
  325. package/dist/sandbox/index.js +0 -2
  326. package/test/EFVideo.framegen.browsertest.ts +0 -80
  327. package/test/thumbnail-performance-test.html +0 -116
@@ -1 +1 @@
1
- {"version":3,"file":"FitScaleHelpers.js","names":[],"sources":["../../src/gui/FitScaleHelpers.ts"],"sourcesContent":["import type { ContainerInfo } from \"../elements/ContainerInfo.js\";\nimport { getContainerInfoFromElement } from \"../elements/ContainerInfo.js\";\n\n/**\n * Determines if an element needs FitScale wrapping based on its parent container\n * and whether it has explicit size.\n *\n * FitScale is needed when:\n * - Parent is a grid container (grid or flex display)\n * - Element doesn't have explicit width/height\n *\n * @param parentElement - The parent element (can be HTMLElement or null)\n * @param elementHasExplicitSize - Whether the element has explicit width/height\n * @returns true if FitScale should be applied\n *\n * @public\n */\nexport function needsFitScale(\n parentElement: HTMLElement | null,\n elementHasExplicitSize: boolean,\n): boolean {\n if (!parentElement) {\n return false;\n }\n\n const containerInfo = getContainerInfoFromElement(parentElement);\n const isInGridContainer =\n containerInfo.displayMode === \"grid\" ||\n containerInfo.displayMode === \"flex\";\n\n return isInGridContainer && !elementHasExplicitSize;\n}\n\n/**\n * Determines if an element needs FitScale wrapping by checking computed styles.\n * This is a convenience function that reads the element's computed style to check\n * for explicit size.\n *\n * @param element - The element to check\n * @param parentElement - The parent element (can be HTMLElement or null)\n * @returns true if FitScale should be applied\n *\n * @public\n */\nexport function elementNeedsFitScale(\n element: HTMLElement,\n parentElement: HTMLElement | null,\n): boolean {\n const computedStyle = window.getComputedStyle(element);\n const hasExplicitSize =\n !!(computedStyle.width && computedStyle.width !== \"auto\") ||\n !!(computedStyle.height && computedStyle.height !== \"auto\");\n\n return needsFitScale(parentElement, hasExplicitSize);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAiBA,SAAgB,cACd,eACA,wBACS;AACT,KAAI,CAAC,cACH,QAAO;CAGT,MAAM,gBAAgB,4BAA4B,cAAc;AAKhE,SAHE,cAAc,gBAAgB,UAC9B,cAAc,gBAAgB,WAEJ,CAAC;;;;;;;;;;;;;AAc/B,SAAgB,qBACd,SACA,eACS;CACT,MAAM,gBAAgB,OAAO,iBAAiB,QAAQ;AAKtD,QAAO,cAAc,eAHnB,CAAC,EAAE,cAAc,SAAS,cAAc,UAAU,WAClD,CAAC,EAAE,cAAc,UAAU,cAAc,WAAW,QAEF"}
1
+ {"version":3,"file":"FitScaleHelpers.js","names":[],"sources":["../../src/gui/FitScaleHelpers.ts"],"sourcesContent":["import { getContainerInfoFromElement } from \"../elements/ContainerInfo.js\";\n\n/**\n * Determines if an element needs FitScale wrapping based on its parent container\n * and whether it has explicit size.\n *\n * FitScale is needed when:\n * - Parent is a grid container (grid or flex display)\n * - Element doesn't have explicit width/height\n *\n * @param parentElement - The parent element (can be HTMLElement or null)\n * @param elementHasExplicitSize - Whether the element has explicit width/height\n * @returns true if FitScale should be applied\n *\n * @public\n */\nexport function needsFitScale(\n parentElement: HTMLElement | null,\n elementHasExplicitSize: boolean,\n): boolean {\n if (!parentElement) {\n return false;\n }\n\n const containerInfo = getContainerInfoFromElement(parentElement);\n const isInGridContainer =\n containerInfo.displayMode === \"grid\" ||\n containerInfo.displayMode === \"flex\";\n\n return isInGridContainer && !elementHasExplicitSize;\n}\n\n/**\n * Determines if an element needs FitScale wrapping by checking computed styles.\n * This is a convenience function that reads the element's computed style to check\n * for explicit size.\n *\n * @param element - The element to check\n * @param parentElement - The parent element (can be HTMLElement or null)\n * @returns true if FitScale should be applied\n *\n * @public\n */\nexport function elementNeedsFitScale(\n element: HTMLElement,\n parentElement: HTMLElement | null,\n): boolean {\n const computedStyle = window.getComputedStyle(element);\n const hasExplicitSize =\n !!(computedStyle.width && computedStyle.width !== \"auto\") ||\n !!(computedStyle.height && computedStyle.height !== \"auto\");\n\n return needsFitScale(parentElement, hasExplicitSize);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAgBA,SAAgB,cACd,eACA,wBACS;AACT,KAAI,CAAC,cACH,QAAO;CAGT,MAAM,gBAAgB,4BAA4B,cAAc;AAKhE,SAHE,cAAc,gBAAgB,UAC9B,cAAc,gBAAgB,WAEJ,CAAC;;;;;;;;;;;;;AAc/B,SAAgB,qBACd,SACA,eACS;CACT,MAAM,gBAAgB,OAAO,iBAAiB,QAAQ;AAKtD,QAAO,cAAc,eAHnB,CAAC,EAAE,cAAc,SAAS,cAAc,UAAU,WAClD,CAAC,EAAE,cAAc,UAAU,cAAc,WAAW,QAEF"}
@@ -6,11 +6,7 @@ interface PlaybackHost extends HTMLElement, ReactiveControllerHost {
6
6
  currentTimeMs: number;
7
7
  durationMs: number;
8
8
  endTimeMs: number;
9
- frameTask: {
10
- run(): void;
11
- taskComplete: Promise<unknown>;
12
- };
13
- /** New centralized frame controller (replaces frameTask) */
9
+ /** Centralized frame controller (present on EFTimegroup) */
14
10
  frameController?: {
15
11
  renderFrame(timeMs: number, options?: RenderFrameOptions): Promise<void>;
16
12
  abort(): void;
@@ -62,10 +58,9 @@ declare class PlaybackController implements ReactiveController {
62
58
  hostConnected(): void;
63
59
  hostDisconnected(): void;
64
60
  hostUpdated(): void;
65
- static readonly THROTTLED_FRAME_TASK_MAX_WAITS = 100;
66
61
  /**
67
- * Run frame rendering with throttling to prevent concurrent executions.
68
- * Uses the new FrameController when available, falling back to frameTask.
62
+ * Run frame rendering via FrameController, or directly on the host if it
63
+ * implements FrameRenderable (standalone media element without a Timegroup).
69
64
  */
70
65
  runThrottledFrameTask(): Promise<void>;
71
66
  addListener(listener: (event: PlaybackControllerUpdateEvent) => void): void;
@@ -18,7 +18,7 @@ import { ContextProvider } from "@lit/context";
18
18
  *
19
19
  * Works with any temporal element (timegroups or standalone media) via PlaybackHost interface
20
20
  */
21
- var PlaybackController = class PlaybackController {
21
+ var PlaybackController = class {
22
22
  #host;
23
23
  #playing = false;
24
24
  #loop = false;
@@ -33,9 +33,6 @@ var PlaybackController = class PlaybackController {
33
33
  #playbackAnimationFrameRequest = null;
34
34
  #pendingAudioContext = null;
35
35
  #AUDIO_PLAYBACK_SLICE_MS = 47 * 1024 / 48e3 * 1e3;
36
- #frameTaskInProgress = false;
37
- #pendingFrameTaskRun = false;
38
- #processingPendingFrameTask = false;
39
36
  #currentTime = void 0;
40
37
  #seekInProgress = false;
41
38
  #pendingSeekTime;
@@ -84,7 +81,9 @@ var PlaybackController = class PlaybackController {
84
81
  }
85
82
  this.#currentTime = time;
86
83
  this.#seekInProgress = true;
87
- this.#runSeek(time).finally(() => {
84
+ this.#runSeek(time).finally(async () => {
85
+ const { updateAnimations: updateAnimations$1 } = await import("../elements/updateAnimations.js");
86
+ updateAnimations$1(this.#host);
88
87
  if (this.#pendingSeekTime !== void 0 && this.#pendingSeekTime !== time) {
89
88
  const pendingTime = this.#pendingSeekTime;
90
89
  this.#pendingSeekTime = void 0;
@@ -185,19 +184,25 @@ var PlaybackController = class PlaybackController {
185
184
  requestAnimationFrame(() => {
186
185
  if (this.#removed || this.#host.playbackController !== this) return;
187
186
  if (this.#playing) this.startPlayback();
188
- else this.#host.waitForMediaDurations?.().then(() => {
189
- if (this.#removed || this.#host.playbackController !== this) return;
190
- const maybeLoadedTime = this.#host.loadTimeFromLocalStorage?.();
191
- if (maybeLoadedTime !== void 0) {
192
- this.#host.setRestoringFromLocalStorage?.(true);
193
- this.currentTime = maybeLoadedTime;
194
- } else if (this.#currentTime === void 0) this.#currentTime = 0;
195
- }).catch((err) => {
196
- if (!(err instanceof DOMException && err.name === "AbortError" || err instanceof Error && (err.name === "AbortError" || err.message.includes("signal is aborted") || err.message.includes("The user aborted a request")))) console.error("Error in PlaybackController hostConnected:", err);
197
- });
187
+ else this.#initializeTime();
198
188
  });
199
189
  });
200
190
  }
191
+ async #initializeTime() {
192
+ try {
193
+ const waitPromise = this.#host.waitForMediaDurations?.();
194
+ if (waitPromise) await waitPromise;
195
+ } catch (err) {
196
+ if (!(err instanceof DOMException && err.name === "AbortError" || err instanceof Error && (err.name === "AbortError" || err.message.includes("signal is aborted") || err.message.includes("The user aborted a request")))) console.error("Error in PlaybackController hostConnected:", err);
197
+ return;
198
+ }
199
+ if (this.#removed || this.#host.playbackController !== this) return;
200
+ const maybeLoadedTime = this.#host.loadTimeFromLocalStorage?.();
201
+ if (maybeLoadedTime !== void 0) {
202
+ this.#host.setRestoringFromLocalStorage?.(true);
203
+ this.currentTime = maybeLoadedTime;
204
+ } else if (this.#currentTime === void 0) this.currentTime = 0;
205
+ }
201
206
  hostDisconnected() {
202
207
  this.pause();
203
208
  }
@@ -205,17 +210,18 @@ var PlaybackController = class PlaybackController {
205
210
  this.#durationMsProvider.setValue(this.#host.durationMs);
206
211
  this.#currentTimeMsProvider.setValue(this.currentTimeMs);
207
212
  }
208
- static {
209
- this.THROTTLED_FRAME_TASK_MAX_WAITS = 100;
210
- }
213
+ #selfRenderAbortController;
214
+ #selfRenderPromise;
215
+ #selfRenderDirty = false;
211
216
  /**
212
- * Run frame rendering with throttling to prevent concurrent executions.
213
- * Uses the new FrameController when available, falling back to frameTask.
217
+ * Run frame rendering via FrameController, or directly on the host if it
218
+ * implements FrameRenderable (standalone media element without a Timegroup).
214
219
  */
215
220
  async runThrottledFrameTask() {
221
+ const timeMs = this.currentTimeMs;
216
222
  if (this.#host.frameController) {
217
223
  try {
218
- await this.#host.frameController.renderFrame(this.currentTimeMs, { onAnimationsUpdate: (root) => {
224
+ await this.#host.frameController.renderFrame(timeMs, { onAnimationsUpdate: (root) => {
219
225
  updateAnimations(root);
220
226
  } });
221
227
  } catch (error) {
@@ -224,33 +230,35 @@ var PlaybackController = class PlaybackController {
224
230
  }
225
231
  return;
226
232
  }
227
- if (this.#frameTaskInProgress) {
228
- this.#pendingFrameTaskRun = true;
229
- let waitLoopCount = 0;
230
- while (this.#frameTaskInProgress) {
231
- waitLoopCount++;
232
- if (waitLoopCount > PlaybackController.THROTTLED_FRAME_TASK_MAX_WAITS) break;
233
- await this.#host.frameTask.taskComplete;
234
- }
235
- return;
236
- }
237
- this.#frameTaskInProgress = true;
238
- try {
239
- await this.#host.frameTask.run();
240
- } catch (error) {
241
- console.error("Frame task error:", error);
242
- } finally {
243
- this.#frameTaskInProgress = false;
244
- if (this.#pendingFrameTaskRun && !this.#processingPendingFrameTask) {
245
- this.#pendingFrameTaskRun = false;
246
- this.#processingPendingFrameTask = true;
247
- try {
248
- await this.runThrottledFrameTask();
249
- } finally {
250
- this.#processingPendingFrameTask = false;
251
- }
252
- } else this.#pendingFrameTaskRun = false;
233
+ const host = this.#host;
234
+ if (!host.prepareFrame || !host.renderFrame) return;
235
+ if (this.#selfRenderPromise) {
236
+ this.#selfRenderDirty = true;
237
+ return this.#selfRenderPromise;
253
238
  }
239
+ return this.#startSelfRender(host, timeMs);
240
+ }
241
+ #startSelfRender(host, timeMs) {
242
+ this.#selfRenderAbortController?.abort();
243
+ this.#selfRenderAbortController = new AbortController();
244
+ const signal = this.#selfRenderAbortController.signal;
245
+ this.#selfRenderDirty = false;
246
+ this.#selfRenderPromise = (async () => {
247
+ try {
248
+ await host.prepareFrame(timeMs, signal);
249
+ signal.throwIfAborted();
250
+ host.renderFrame(timeMs);
251
+ updateAnimations(this.#host);
252
+ } catch (error) {
253
+ if (error instanceof DOMException && error.name === "AbortError") return;
254
+ if (error?.name === "AbortError") return;
255
+ console.error("Standalone frame render error:", error);
256
+ } finally {
257
+ this.#selfRenderPromise = void 0;
258
+ if (this.#selfRenderDirty) this.#startSelfRender(host, this.currentTimeMs);
259
+ }
260
+ })();
261
+ return this.#selfRenderPromise;
254
262
  }
255
263
  addListener(listener) {
256
264
  this.#listeners.add(listener);
@@ -351,20 +359,15 @@ var PlaybackController = class PlaybackController {
351
359
  let hasWrapped = false;
352
360
  const fillBuffer = async () => {
353
361
  if (bufferCount > 2) return;
354
- if (await queueBufferSource()) fillBuffer();
362
+ if (await queueBufferSource()) fillBuffer().catch(() => {});
355
363
  };
356
364
  const queueBufferSource = async () => {
357
365
  if (hasWrapped && !this.#loopingPlayback) return false;
358
366
  const startMs = logicalTimeMs;
359
367
  const endMs = Math.min(logicalTimeMs + this.#AUDIO_PLAYBACK_SLICE_MS, toMs);
360
368
  const willReachEnd = endMs >= toMs;
361
- if (!host.renderAudio) {
362
- console.log("[PlaybackController] host.renderAudio is not defined");
363
- return false;
364
- }
365
- console.log(`[PlaybackController] Rendering audio from ${startMs}ms to ${endMs}ms`);
369
+ if (!host.renderAudio) return false;
366
370
  const audioBuffer = await host.renderAudio(startMs, endMs);
367
- console.log(`[PlaybackController] Got audio buffer: ${audioBuffer.length} samples, ${audioBuffer.duration}s`);
368
371
  bufferCount++;
369
372
  const source = playbackContext.createBufferSource();
370
373
  source.buffer = audioBuffer;
@@ -374,8 +377,8 @@ var PlaybackController = class PlaybackController {
374
377
  source.onended = () => {
375
378
  bufferCount--;
376
379
  if (willReachEnd) if (!this.#loopingPlayback) this.maybeLoopPlayback();
377
- else fillBuffer();
378
- else fillBuffer();
380
+ else fillBuffer().catch(() => {});
381
+ else fillBuffer().catch(() => {});
379
382
  };
380
383
  audioContextTimeMs += sliceDurationMs;
381
384
  if (willReachEnd && this.#loopingPlayback) {
@@ -1 +1 @@
1
- {"version":3,"file":"PlaybackController.js","names":["#FPS","#host","#playingProvider","#playing","#loopProvider","#loop","#currentTimeMsProvider","#durationMsProvider","#currentTime","#processingPendingSeek","#pendingSeekTime","#seekInProgress","#runSeek","#seekAbortController","#notifyListeners","#removed","#frameTaskInProgress","#pendingFrameTaskRun","#processingPendingFrameTask","#listeners","#pendingAudioContext","#playbackAudioContext","rawTimeMs: number","#playbackWrapTimeSeconds","#loopingPlayback","#MS_PER_FRAME","#updatePlaybackTime","#playbackAnimationFrameRequest","#syncPlayheadToAudioContext","#AUDIO_PLAYBACK_SLICE_MS"],"sources":["../../src/gui/PlaybackController.ts"],"sourcesContent":["import { ContextProvider } from \"@lit/context\";\nimport type { ReactiveController, ReactiveControllerHost } from \"lit\";\nimport { currentTimeContext } from \"./currentTimeContext.js\";\nimport { durationContext } from \"./durationContext.js\";\nimport { loopContext, playingContext } from \"./playingContext.js\";\nimport { updateAnimations, type AnimatableElement } from \"../elements/updateAnimations.js\";\nimport type { RenderFrameOptions } from \"../preview/FrameController.js\";\n\ninterface PlaybackHost extends HTMLElement, ReactiveControllerHost {\n currentTimeMs: number;\n durationMs: number;\n endTimeMs: number;\n frameTask: { run(): void; taskComplete: Promise<unknown> };\n /** New centralized frame controller (replaces frameTask) */\n frameController?: { \n renderFrame(timeMs: number, options?: RenderFrameOptions): Promise<void>; \n abort(): void;\n };\n renderAudio?(fromMs: number, toMs: number): Promise<AudioBuffer>;\n waitForMediaDurations?(signal?: AbortSignal): Promise<void>;\n saveTimeToLocalStorage?(time: number): void;\n loadTimeFromLocalStorage?(): number | undefined;\n requestUpdate(property?: string): void;\n updateComplete: Promise<boolean>;\n playing: boolean;\n loop: boolean;\n play(): void;\n pause(): void;\n playbackController?: PlaybackController;\n parentTimegroup?: any;\n rootTimegroup?: any;\n}\n\nexport type PlaybackControllerUpdateEvent = {\n property: \"playing\" | \"loop\" | \"currentTimeMs\";\n value: boolean | number;\n};\n\n/**\n * Manages playback state and audio-driven timing for root temporal elements\n *\n * Created automatically when a temporal element becomes a root (no parent timegroup)\n * Provides playback contexts (playing, loop, currentTimeMs, durationMs) to descendants\n * Handles:\n * - Audio-driven playback with Web Audio API\n * - Seek and frame rendering throttling\n * - Time state management with pending seek handling\n * - Playback loop behavior\n *\n * Works with any temporal element (timegroups or standalone media) via PlaybackHost interface\n */\nexport class PlaybackController implements ReactiveController {\n #host: PlaybackHost;\n #playing = false;\n #loop = false;\n #listeners = new Set<(event: PlaybackControllerUpdateEvent) => void>();\n #playingProvider: ContextProvider<typeof playingContext>;\n #loopProvider: ContextProvider<typeof loopContext>;\n #currentTimeMsProvider: ContextProvider<typeof currentTimeContext>;\n #durationMsProvider: ContextProvider<typeof durationContext>;\n\n #FPS = 30;\n #MS_PER_FRAME = 1000 / this.#FPS;\n #playbackAudioContext: AudioContext | null = null;\n #playbackAnimationFrameRequest: number | null = null;\n #pendingAudioContext: AudioContext | null = null;\n #AUDIO_PLAYBACK_SLICE_MS = ((47 * 1024) / 48000) * 1000;\n\n #frameTaskInProgress = false;\n #pendingFrameTaskRun = false;\n #processingPendingFrameTask = false;\n\n #currentTime: number | undefined = undefined;\n #seekInProgress = false;\n #pendingSeekTime: number | undefined;\n #processingPendingSeek = false;\n #loopingPlayback = false; // Track if we're in a looping playback session\n #playbackWrapTimeSeconds = 0; // The AudioContext time when we wrapped\n\n #seekAbortController: AbortController | null = null;\n\n constructor(host: PlaybackHost) {\n this.#host = host;\n host.addController(this);\n\n this.#playingProvider = new ContextProvider(host, {\n context: playingContext,\n initialValue: this.#playing,\n });\n this.#loopProvider = new ContextProvider(host, {\n context: loopContext,\n initialValue: this.#loop,\n });\n this.#currentTimeMsProvider = new ContextProvider(host, {\n context: currentTimeContext,\n initialValue: host.currentTimeMs,\n });\n this.#durationMsProvider = new ContextProvider(host, {\n context: durationContext,\n initialValue: host.durationMs,\n });\n }\n\n get currentTime(): number {\n const rawTime = this.#currentTime ?? 0;\n // Quantize to frame boundaries based on host's fps\n const fps = (this.#host as any).fps ?? 30;\n if (!fps || fps <= 0) return rawTime;\n const frameDurationS = 1 / fps;\n const quantizedTime = Math.round(rawTime / frameDurationS) * frameDurationS;\n // Clamp to valid range after quantization to prevent exceeding duration\n const durationS = this.#host.durationMs / 1000;\n return Math.max(0, Math.min(quantizedTime, durationS));\n }\n\n set currentTime(time: number) {\n time = Math.max(0, Math.min(this.#host.durationMs / 1000, time));\n if (Number.isNaN(time)) {\n return;\n }\n if (time === this.#currentTime && !this.#processingPendingSeek) {\n return;\n }\n if (this.#pendingSeekTime === time) {\n return;\n }\n\n if (this.#seekInProgress) {\n this.#pendingSeekTime = time;\n this.#currentTime = time;\n return;\n }\n\n this.#currentTime = time;\n this.#seekInProgress = true;\n\n this.#runSeek(time).finally(() => {\n if (\n this.#pendingSeekTime !== undefined &&\n this.#pendingSeekTime !== time\n ) {\n const pendingTime = this.#pendingSeekTime;\n this.#pendingSeekTime = undefined;\n this.#processingPendingSeek = true;\n try {\n this.currentTime = pendingTime;\n } finally {\n this.#processingPendingSeek = false;\n }\n } else {\n this.#pendingSeekTime = undefined;\n }\n });\n }\n\n async #runSeek(targetTime: number): Promise<number | undefined> {\n // Abort any in-flight seek\n this.#seekAbortController?.abort();\n this.#seekAbortController = new AbortController();\n const signal = this.#seekAbortController.signal;\n\n try {\n signal.throwIfAborted();\n \n await this.#host.waitForMediaDurations?.(signal);\n signal.throwIfAborted();\n \n const newTime = Math.max(\n 0,\n Math.min(targetTime, this.#host.durationMs / 1000),\n );\n this.#currentTime = newTime;\n this.#host.requestUpdate(\"currentTime\");\n this.#currentTimeMsProvider.setValue(this.currentTimeMs);\n this.#notifyListeners({\n property: \"currentTimeMs\",\n value: this.currentTimeMs,\n });\n \n signal.throwIfAborted();\n \n await this.runThrottledFrameTask();\n signal.throwIfAborted();\n \n // Save to localStorage for persistence (only if not restoring to avoid loops)\n const isRestoring = (this.#host as any).isRestoringFromLocalStorage?.() ?? false;\n if (!isRestoring) {\n this.#host.saveTimeToLocalStorage?.(newTime);\n } else {\n (this.#host as any).setRestoringFromLocalStorage?.(false);\n }\n this.#seekInProgress = false;\n return newTime;\n } catch (error) {\n if (error instanceof DOMException && error.name === \"AbortError\") {\n // Expected - don't log\n return undefined;\n }\n throw error;\n }\n }\n\n get playing(): boolean {\n return this.#playing;\n }\n\n setPlaying(value: boolean): void {\n if (this.#playing === value) return;\n this.#playing = value;\n this.#playingProvider.setValue(value);\n this.#host.requestUpdate(\"playing\");\n this.#notifyListeners({ property: \"playing\", value });\n\n if (value) {\n this.startPlayback();\n } else {\n this.stopPlayback();\n }\n }\n\n get loop(): boolean {\n return this.#loop;\n }\n\n setLoop(value: boolean): void {\n if (this.#loop === value) return;\n this.#loop = value;\n this.#loopProvider.setValue(value);\n this.#host.requestUpdate(\"loop\");\n this.#notifyListeners({ property: \"loop\", value });\n }\n\n get currentTimeMs(): number {\n return this.currentTime * 1000;\n }\n\n setCurrentTimeMs(value: number): void {\n this.currentTime = value / 1000;\n }\n\n // Update time during playback without triggering a seek\n // Used by #syncPlayheadToAudioContext to avoid frame drops\n #updatePlaybackTime(timeMs: number): void {\n // Clamp to valid range to prevent time exceeding duration\n const durationMs = this.#host.durationMs;\n const clampedTimeMs = Math.max(0, Math.min(timeMs, durationMs));\n const timeSec = clampedTimeMs / 1000;\n if (this.#currentTime === timeSec) {\n return;\n }\n this.#currentTime = timeSec;\n this.#host.requestUpdate(\"currentTime\");\n this.#currentTimeMsProvider.setValue(clampedTimeMs);\n this.#notifyListeners({\n property: \"currentTimeMs\",\n value: clampedTimeMs,\n });\n // Trigger frame rendering without the async seek mechanism\n this.runThrottledFrameTask();\n }\n\n play(): void {\n this.setPlaying(true);\n }\n\n pause(): void {\n this.setPlaying(false);\n }\n\n #removed = false;\n \n hostConnected(): void {\n // Defer all operations to avoid blocking during initialization\n // This prevents deadlocks when many timegroups are initializing simultaneously\n requestAnimationFrame(() => {\n requestAnimationFrame(() => {\n // Check if this controller was removed before the RAF callback executed.\n // This happens when wrapWithWorkbench moves the element, causing disconnect/reconnect.\n if (this.#removed || this.#host.playbackController !== this) {\n return;\n }\n \n if (this.#playing) {\n this.startPlayback();\n } else {\n // Wait for media durations then restore from localStorage\n this.#host.waitForMediaDurations?.().then(() => {\n // Check again after async operation - controller could have been removed\n if (this.#removed || this.#host.playbackController !== this) {\n return;\n }\n \n const maybeLoadedTime = this.#host.loadTimeFromLocalStorage?.();\n if (maybeLoadedTime !== undefined) {\n // Set restoration flag to prevent seek from saving back to localStorage\n (this.#host as any).setRestoringFromLocalStorage?.(true);\n this.currentTime = maybeLoadedTime;\n // Flag is cleared by runSeek after seek finishes\n } else if (this.#currentTime === undefined) {\n this.#currentTime = 0;\n }\n }).catch(err => {\n // Don't log AbortError - these are intentional cancellations when element is disconnected\n const isAbortError = \n err instanceof DOMException && err.name === \"AbortError\" ||\n err instanceof Error && (\n err.name === \"AbortError\" ||\n err.message.includes(\"signal is aborted\") ||\n err.message.includes(\"The user aborted a request\")\n );\n \n if (!isAbortError) {\n console.error(\"Error in PlaybackController hostConnected:\", err);\n }\n });\n }\n });\n });\n }\n\n hostDisconnected(): void {\n this.pause();\n }\n\n hostUpdated(): void {\n this.#durationMsProvider.setValue(this.#host.durationMs);\n this.#currentTimeMsProvider.setValue(this.currentTimeMs);\n }\n\n static readonly THROTTLED_FRAME_TASK_MAX_WAITS = 100;\n \n /**\n * Run frame rendering with throttling to prevent concurrent executions.\n * Uses the new FrameController when available, falling back to frameTask.\n */\n async runThrottledFrameTask(): Promise<void> {\n // Use FrameController if available (new centralized system)\n if (this.#host.frameController) {\n // FrameController handles its own cancellation and queuing internally\n // Animation updates are centralized via the onAnimationsUpdate callback\n try {\n await this.#host.frameController.renderFrame(this.currentTimeMs, {\n onAnimationsUpdate: (root: Element) => {\n // Update CSS visibility and animation synchronization after frame renders\n // This sets display:none on elements outside their time range\n updateAnimations(root as unknown as AnimatableElement);\n },\n });\n } catch (error) {\n // Silently ignore AbortErrors (expected during cancellation)\n if (error instanceof DOMException && error.name === \"AbortError\") {\n return;\n }\n console.error(\"FrameController error:\", error);\n }\n return;\n }\n\n // Fallback to old frameTask system (for backwards compatibility)\n if (this.#frameTaskInProgress) {\n this.#pendingFrameTaskRun = true;\n let waitLoopCount = 0;\n while (this.#frameTaskInProgress) {\n waitLoopCount++;\n if (waitLoopCount > PlaybackController.THROTTLED_FRAME_TASK_MAX_WAITS) {\n // Safety break to prevent infinite loops\n break;\n }\n await this.#host.frameTask.taskComplete;\n }\n return;\n }\n\n this.#frameTaskInProgress = true;\n\n try {\n await this.#host.frameTask.run();\n } catch (error) {\n console.error(\"Frame task error:\", error);\n } finally {\n this.#frameTaskInProgress = false;\n\n if (this.#pendingFrameTaskRun && !this.#processingPendingFrameTask) {\n this.#pendingFrameTaskRun = false;\n this.#processingPendingFrameTask = true;\n try {\n await this.runThrottledFrameTask();\n } finally {\n this.#processingPendingFrameTask = false;\n }\n } else {\n this.#pendingFrameTaskRun = false;\n }\n }\n }\n\n addListener(listener: (event: PlaybackControllerUpdateEvent) => void): void {\n this.#listeners.add(listener);\n }\n\n removeListener(\n listener: (event: PlaybackControllerUpdateEvent) => void,\n ): void {\n this.#listeners.delete(listener);\n }\n\n #notifyListeners(event: PlaybackControllerUpdateEvent): void {\n for (const listener of this.#listeners) {\n listener(event);\n }\n }\n\n remove(): void {\n this.#removed = true; // Mark as removed to abort any pending RAF callbacks\n this.stopPlayback();\n this.#listeners.clear();\n this.#host.removeController(this);\n }\n\n setPendingAudioContext(context: AudioContext): void {\n this.#pendingAudioContext = context;\n }\n\n #syncPlayheadToAudioContext(startMs: number) {\n const audioContextTime = this.#playbackAudioContext?.currentTime ?? 0;\n const endMs = this.#host.endTimeMs;\n\n // Calculate raw time based on audio context\n let rawTimeMs: number;\n if (\n this.#playbackWrapTimeSeconds > 0 &&\n audioContextTime >= this.#playbackWrapTimeSeconds\n ) {\n // After wrap: time since wrap, wrapped to duration\n const timeSinceWrap =\n (audioContextTime - this.#playbackWrapTimeSeconds) * 1000;\n rawTimeMs = timeSinceWrap % endMs;\n } else {\n // Before wrap or no wrap: normal calculation\n rawTimeMs = startMs + audioContextTime * 1000;\n\n // If looping and we've reached the end, wrap around\n if (this.#loopingPlayback && rawTimeMs >= endMs) {\n rawTimeMs = rawTimeMs % endMs;\n }\n }\n\n const nextTimeMs =\n Math.round(rawTimeMs / this.#MS_PER_FRAME) * this.#MS_PER_FRAME;\n\n // During playback, update time directly without triggering seek\n // This avoids frame drops at the loop boundary\n this.#updatePlaybackTime(nextTimeMs);\n\n // Only check for end if we haven't already handled looping\n if (!this.#loopingPlayback && nextTimeMs >= endMs) {\n this.maybeLoopPlayback();\n return;\n }\n\n this.#playbackAnimationFrameRequest = requestAnimationFrame(() => {\n this.#syncPlayheadToAudioContext(startMs);\n });\n }\n\n private async maybeLoopPlayback() {\n if (this.#loop) {\n // Loop enabled: reset to beginning and restart playback\n // We restart the audio system directly without changing #playing state\n // to keep the play button in sync\n this.setCurrentTimeMs(0);\n // Restart in next frame without awaiting to minimize gap\n requestAnimationFrame(() => {\n this.startPlayback();\n });\n } else {\n // No loop: reset to beginning and stop\n // This ensures play button works when clicked again\n this.setCurrentTimeMs(0);\n this.pause();\n }\n }\n\n private async stopPlayback() {\n if (this.#playbackAudioContext) {\n if (this.#playbackAudioContext.state !== \"closed\") {\n await this.#playbackAudioContext.close();\n }\n }\n if (this.#playbackAnimationFrameRequest) {\n cancelAnimationFrame(this.#playbackAnimationFrameRequest);\n }\n this.#playbackAudioContext = null;\n this.#playbackAnimationFrameRequest = null;\n this.#pendingAudioContext = null;\n }\n\n private async startPlayback() {\n // Guard against starting playback on a removed controller\n if (this.#removed) {\n return;\n }\n \n await this.stopPlayback();\n const host = this.#host;\n if (!host) {\n return;\n }\n\n if (host.waitForMediaDurations) {\n await host.waitForMediaDurations();\n }\n \n // Check again after async - controller could have been removed\n if (this.#removed) {\n return;\n }\n\n const currentMs = this.currentTimeMs;\n const fromMs = currentMs;\n const toMs = host.endTimeMs;\n\n if (fromMs >= toMs) {\n this.pause();\n return;\n }\n\n let bufferCount = 0;\n // Check for pre-resumed AudioContext from synchronous user interaction\n if (this.#pendingAudioContext) {\n this.#playbackAudioContext = this.#pendingAudioContext;\n this.#pendingAudioContext = null;\n } else {\n this.#playbackAudioContext = new AudioContext({\n latencyHint: \"playback\",\n });\n }\n this.#loopingPlayback = this.#loop; // Remember if we're in a looping session\n this.#playbackWrapTimeSeconds = 0; // Reset wrap time\n\n if (this.#playbackAnimationFrameRequest) {\n cancelAnimationFrame(this.#playbackAnimationFrameRequest);\n }\n this.#syncPlayheadToAudioContext(currentMs);\n const playbackContext = this.#playbackAudioContext;\n\n // Check if context is suspended (fallback for newly-created contexts)\n if (playbackContext.state === \"suspended\") {\n // Attempt to resume (may not work on mobile if user interaction context is lost)\n try {\n await playbackContext.resume();\n // Check state again after resume attempt\n if (playbackContext.state === \"suspended\") {\n console.warn(\n \"AudioContext is suspended and resume() failed. \" +\n \"On mobile devices, AudioContext.resume() must be called synchronously within a user interaction handler. \" +\n \"Media playback will not work until user has interacted with page.\",\n );\n this.setPlaying(false);\n return;\n }\n } catch (error) {\n console.warn(\n \"Failed to resume AudioContext:\",\n error,\n \"On mobile devices, AudioContext.resume() must be called synchronously within a user interaction handler.\",\n );\n this.setPlaying(false);\n return;\n }\n }\n await playbackContext.suspend();\n\n // Track the logical media time (what position in the media we're rendering)\n // vs the AudioContext schedule time (when to play it)\n let logicalTimeMs = currentMs;\n let audioContextTimeMs = 0; // Tracks the schedule position in the AudioContext timeline\n let hasWrapped = false;\n\n const fillBuffer = async () => {\n if (bufferCount > 2) {\n return;\n }\n const canFillBuffer = await queueBufferSource();\n if (canFillBuffer) {\n fillBuffer();\n }\n };\n\n const queueBufferSource = async () => {\n // Check if we've already wrapped and aren't looping anymore\n if (hasWrapped && !this.#loopingPlayback) {\n return false;\n }\n\n const startMs = logicalTimeMs;\n const endMs = Math.min(\n logicalTimeMs + this.#AUDIO_PLAYBACK_SLICE_MS,\n toMs,\n );\n\n // Will this slice reach the end?\n const willReachEnd = endMs >= toMs;\n\n if (!host.renderAudio) {\n console.log('[PlaybackController] host.renderAudio is not defined');\n return false;\n }\n\n console.log(`[PlaybackController] Rendering audio from ${startMs}ms to ${endMs}ms`);\n const audioBuffer = await host.renderAudio(startMs, endMs);\n console.log(`[PlaybackController] Got audio buffer: ${audioBuffer.length} samples, ${audioBuffer.duration}s`);\n bufferCount++;\n const source = playbackContext.createBufferSource();\n source.buffer = audioBuffer;\n source.connect(playbackContext.destination);\n // Schedule this buffer to play at the current audioContextTime position\n source.start(audioContextTimeMs / 1000);\n\n const sliceDurationMs = endMs - startMs;\n\n source.onended = () => {\n bufferCount--;\n\n if (willReachEnd) {\n if (!this.#loopingPlayback) {\n // Not looping, end playback\n this.maybeLoopPlayback();\n } else {\n // Looping: continue filling buffer after wrap\n fillBuffer();\n }\n } else {\n // Continue filling buffer\n fillBuffer();\n }\n };\n\n // Advance the AudioContext schedule time\n audioContextTimeMs += sliceDurationMs;\n\n // If this buffer reaches the end and we're looping, immediately queue the wraparound\n if (willReachEnd && this.#loopingPlayback) {\n // Mark that we've wrapped\n hasWrapped = true;\n // Store when we wrapped (relative to when playback started, which is time 0 in AudioContext)\n // This is the duration from start to end\n this.#playbackWrapTimeSeconds = (toMs - fromMs) / 1000;\n // Reset logical time to beginning\n logicalTimeMs = 0;\n // Continue buffering will happen in fillBuffer() call below\n } else {\n // Normal advance\n logicalTimeMs = endMs;\n }\n\n return true;\n };\n\n try {\n await fillBuffer();\n await playbackContext.resume();\n } catch (error) {\n // Ignore errors if AudioContext is closed or during test cleanup\n if (\n error instanceof Error &&\n (error.name === \"InvalidStateError\" || error.message.includes(\"closed\"))\n ) {\n return;\n }\n throw error;\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"PlaybackController.js","names":["#FPS","#host","#playingProvider","#playing","#loopProvider","#loop","#currentTimeMsProvider","#durationMsProvider","#currentTime","#processingPendingSeek","#pendingSeekTime","#seekInProgress","#runSeek","#seekAbortController","#notifyListeners","#removed","#initializeTime","#selfRenderPromise","#selfRenderDirty","#startSelfRender","#selfRenderAbortController","#listeners","#pendingAudioContext","#playbackAudioContext","rawTimeMs: number","#playbackWrapTimeSeconds","#loopingPlayback","#MS_PER_FRAME","#updatePlaybackTime","#playbackAnimationFrameRequest","#syncPlayheadToAudioContext","#AUDIO_PLAYBACK_SLICE_MS"],"sources":["../../src/gui/PlaybackController.ts"],"sourcesContent":["import { ContextProvider } from \"@lit/context\";\nimport type { ReactiveController, ReactiveControllerHost } from \"lit\";\nimport { currentTimeContext } from \"./currentTimeContext.js\";\nimport { durationContext } from \"./durationContext.js\";\nimport { loopContext, playingContext } from \"./playingContext.js\";\nimport {\n updateAnimations,\n type AnimatableElement,\n} from \"../elements/updateAnimations.js\";\nimport type {\n RenderFrameOptions,\n FrameRenderable,\n} from \"../preview/FrameController.js\";\n\ninterface PlaybackHost extends HTMLElement, ReactiveControllerHost {\n currentTimeMs: number;\n durationMs: number;\n endTimeMs: number;\n /** Centralized frame controller (present on EFTimegroup) */\n frameController?: {\n renderFrame(timeMs: number, options?: RenderFrameOptions): Promise<void>;\n abort(): void;\n };\n renderAudio?(fromMs: number, toMs: number): Promise<AudioBuffer>;\n waitForMediaDurations?(signal?: AbortSignal): Promise<void>;\n saveTimeToLocalStorage?(time: number): void;\n loadTimeFromLocalStorage?(): number | undefined;\n requestUpdate(property?: string): void;\n updateComplete: Promise<boolean>;\n playing: boolean;\n loop: boolean;\n play(): void;\n pause(): void;\n playbackController?: PlaybackController;\n parentTimegroup?: any;\n rootTimegroup?: any;\n}\n\nexport type PlaybackControllerUpdateEvent = {\n property: \"playing\" | \"loop\" | \"currentTimeMs\";\n value: boolean | number;\n};\n\n/**\n * Manages playback state and audio-driven timing for root temporal elements\n *\n * Created automatically when a temporal element becomes a root (no parent timegroup)\n * Provides playback contexts (playing, loop, currentTimeMs, durationMs) to descendants\n * Handles:\n * - Audio-driven playback with Web Audio API\n * - Seek and frame rendering throttling\n * - Time state management with pending seek handling\n * - Playback loop behavior\n *\n * Works with any temporal element (timegroups or standalone media) via PlaybackHost interface\n */\nexport class PlaybackController implements ReactiveController {\n #host: PlaybackHost;\n #playing = false;\n #loop = false;\n #listeners = new Set<(event: PlaybackControllerUpdateEvent) => void>();\n #playingProvider: ContextProvider<typeof playingContext>;\n #loopProvider: ContextProvider<typeof loopContext>;\n #currentTimeMsProvider: ContextProvider<typeof currentTimeContext>;\n #durationMsProvider: ContextProvider<typeof durationContext>;\n\n #FPS = 30;\n #MS_PER_FRAME = 1000 / this.#FPS;\n #playbackAudioContext: AudioContext | null = null;\n #playbackAnimationFrameRequest: number | null = null;\n #pendingAudioContext: AudioContext | null = null;\n #AUDIO_PLAYBACK_SLICE_MS = ((47 * 1024) / 48000) * 1000;\n\n #currentTime: number | undefined = undefined;\n #seekInProgress = false;\n #pendingSeekTime: number | undefined;\n #processingPendingSeek = false;\n #loopingPlayback = false; // Track if we're in a looping playback session\n #playbackWrapTimeSeconds = 0; // The AudioContext time when we wrapped\n\n #seekAbortController: AbortController | null = null;\n\n constructor(host: PlaybackHost) {\n this.#host = host;\n host.addController(this);\n\n this.#playingProvider = new ContextProvider(host, {\n context: playingContext,\n initialValue: this.#playing,\n });\n this.#loopProvider = new ContextProvider(host, {\n context: loopContext,\n initialValue: this.#loop,\n });\n this.#currentTimeMsProvider = new ContextProvider(host, {\n context: currentTimeContext,\n initialValue: host.currentTimeMs,\n });\n this.#durationMsProvider = new ContextProvider(host, {\n context: durationContext,\n initialValue: host.durationMs,\n });\n }\n\n get currentTime(): number {\n const rawTime = this.#currentTime ?? 0;\n // Quantize to frame boundaries based on host's fps\n const fps = (this.#host as any).fps ?? 30;\n if (!fps || fps <= 0) return rawTime;\n const frameDurationS = 1 / fps;\n const quantizedTime = Math.round(rawTime / frameDurationS) * frameDurationS;\n // Clamp to valid range after quantization to prevent exceeding duration\n const durationS = this.#host.durationMs / 1000;\n return Math.max(0, Math.min(quantizedTime, durationS));\n }\n\n set currentTime(time: number) {\n time = Math.max(0, Math.min(this.#host.durationMs / 1000, time));\n if (Number.isNaN(time)) {\n return;\n }\n if (time === this.#currentTime && !this.#processingPendingSeek) {\n return;\n }\n if (this.#pendingSeekTime === time) {\n return;\n }\n\n if (this.#seekInProgress) {\n this.#pendingSeekTime = time;\n this.#currentTime = time;\n return;\n }\n\n this.#currentTime = time;\n this.#seekInProgress = true;\n\n this.#runSeek(time).finally(async () => {\n // CRITICAL: Coordinate animations after seek completes\n // This ensures animations are positioned correctly, not playing naturally\n const { updateAnimations } =\n await import(\"../elements/updateAnimations.js\");\n updateAnimations(this.#host as any);\n\n if (\n this.#pendingSeekTime !== undefined &&\n this.#pendingSeekTime !== time\n ) {\n const pendingTime = this.#pendingSeekTime;\n this.#pendingSeekTime = undefined;\n this.#processingPendingSeek = true;\n try {\n this.currentTime = pendingTime;\n } finally {\n this.#processingPendingSeek = false;\n }\n } else {\n this.#pendingSeekTime = undefined;\n }\n });\n }\n\n async #runSeek(targetTime: number): Promise<number | undefined> {\n // Abort any in-flight seek\n this.#seekAbortController?.abort();\n this.#seekAbortController = new AbortController();\n const signal = this.#seekAbortController.signal;\n\n try {\n signal.throwIfAborted();\n\n await this.#host.waitForMediaDurations?.(signal);\n signal.throwIfAborted();\n\n const newTime = Math.max(\n 0,\n Math.min(targetTime, this.#host.durationMs / 1000),\n );\n this.#currentTime = newTime;\n this.#host.requestUpdate(\"currentTime\");\n this.#currentTimeMsProvider.setValue(this.currentTimeMs);\n this.#notifyListeners({\n property: \"currentTimeMs\",\n value: this.currentTimeMs,\n });\n\n signal.throwIfAborted();\n\n await this.runThrottledFrameTask();\n signal.throwIfAborted();\n\n // Save to localStorage for persistence (only if not restoring to avoid loops)\n const isRestoring =\n (this.#host as any).isRestoringFromLocalStorage?.() ?? false;\n if (!isRestoring) {\n this.#host.saveTimeToLocalStorage?.(newTime);\n } else {\n (this.#host as any).setRestoringFromLocalStorage?.(false);\n }\n this.#seekInProgress = false;\n return newTime;\n } catch (error) {\n if (error instanceof DOMException && error.name === \"AbortError\") {\n // Expected - don't log\n return undefined;\n }\n throw error;\n }\n }\n\n get playing(): boolean {\n return this.#playing;\n }\n\n setPlaying(value: boolean): void {\n if (this.#playing === value) return;\n this.#playing = value;\n this.#playingProvider.setValue(value);\n this.#host.requestUpdate(\"playing\");\n this.#notifyListeners({ property: \"playing\", value });\n\n if (value) {\n this.startPlayback();\n } else {\n this.stopPlayback();\n }\n }\n\n get loop(): boolean {\n return this.#loop;\n }\n\n setLoop(value: boolean): void {\n if (this.#loop === value) return;\n this.#loop = value;\n this.#loopProvider.setValue(value);\n this.#host.requestUpdate(\"loop\");\n this.#notifyListeners({ property: \"loop\", value });\n }\n\n get currentTimeMs(): number {\n return this.currentTime * 1000;\n }\n\n setCurrentTimeMs(value: number): void {\n this.currentTime = value / 1000;\n }\n\n // Update time during playback without triggering a seek\n // Used by #syncPlayheadToAudioContext to avoid frame drops\n #updatePlaybackTime(timeMs: number): void {\n // Clamp to valid range to prevent time exceeding duration\n const durationMs = this.#host.durationMs;\n const clampedTimeMs = Math.max(0, Math.min(timeMs, durationMs));\n const timeSec = clampedTimeMs / 1000;\n if (this.#currentTime === timeSec) {\n return;\n }\n this.#currentTime = timeSec;\n this.#host.requestUpdate(\"currentTime\");\n this.#currentTimeMsProvider.setValue(clampedTimeMs);\n this.#notifyListeners({\n property: \"currentTimeMs\",\n value: clampedTimeMs,\n });\n // Trigger frame rendering without the async seek mechanism\n this.runThrottledFrameTask();\n }\n\n play(): void {\n this.setPlaying(true);\n }\n\n pause(): void {\n this.setPlaying(false);\n }\n\n #removed = false;\n\n hostConnected(): void {\n // Defer all operations to avoid blocking during initialization\n // This prevents deadlocks when many timegroups are initializing simultaneously\n requestAnimationFrame(() => {\n requestAnimationFrame(() => {\n // Check if this controller was removed before the RAF callback executed.\n // This happens when wrapWithWorkbench moves the element, causing disconnect/reconnect.\n if (this.#removed || this.#host.playbackController !== this) {\n return;\n }\n\n if (this.#playing) {\n this.startPlayback();\n } else {\n this.#initializeTime();\n }\n });\n });\n }\n\n async #initializeTime(): Promise<void> {\n try {\n const waitPromise = this.#host.waitForMediaDurations?.();\n if (waitPromise) {\n await waitPromise;\n }\n } catch (err) {\n const isAbortError =\n (err instanceof DOMException && err.name === \"AbortError\") ||\n (err instanceof Error &&\n (err.name === \"AbortError\" ||\n err.message.includes(\"signal is aborted\") ||\n err.message.includes(\"The user aborted a request\")));\n if (!isAbortError) {\n console.error(\"Error in PlaybackController hostConnected:\", err);\n }\n return;\n }\n\n if (this.#removed || this.#host.playbackController !== this) {\n return;\n }\n\n const maybeLoadedTime = this.#host.loadTimeFromLocalStorage?.();\n if (maybeLoadedTime !== undefined) {\n (this.#host as any).setRestoringFromLocalStorage?.(true);\n this.currentTime = maybeLoadedTime;\n } else if (this.#currentTime === undefined) {\n this.currentTime = 0;\n }\n }\n\n hostDisconnected(): void {\n this.pause();\n }\n\n hostUpdated(): void {\n this.#durationMsProvider.setValue(this.#host.durationMs);\n this.#currentTimeMsProvider.setValue(this.currentTimeMs);\n }\n\n #selfRenderAbortController?: AbortController;\n #selfRenderPromise?: Promise<void>;\n #selfRenderDirty = false;\n\n /**\n * Run frame rendering via FrameController, or directly on the host if it\n * implements FrameRenderable (standalone media element without a Timegroup).\n */\n async runThrottledFrameTask(): Promise<void> {\n const timeMs = this.currentTimeMs;\n\n if (this.#host.frameController) {\n try {\n await this.#host.frameController.renderFrame(timeMs, {\n onAnimationsUpdate: (root: Element) => {\n updateAnimations(root as unknown as AnimatableElement);\n },\n });\n } catch (error) {\n if (error instanceof DOMException && error.name === \"AbortError\")\n return;\n console.error(\"FrameController error:\", error);\n }\n return;\n }\n\n // Standalone FrameRenderable host (e.g. bare ef-video without a Timegroup)\n const host = this.#host as unknown as Partial<FrameRenderable>;\n if (!host.prepareFrame || !host.renderFrame) return;\n\n // If a render is in-flight, mark dirty so we re-render after it\n // completes (source mapping may have changed due to trim drag).\n if (this.#selfRenderPromise) {\n this.#selfRenderDirty = true;\n return this.#selfRenderPromise;\n }\n\n return this.#startSelfRender(host, timeMs);\n }\n\n #startSelfRender(\n host: Partial<FrameRenderable>,\n timeMs: number,\n ): Promise<void> {\n this.#selfRenderAbortController?.abort();\n this.#selfRenderAbortController = new AbortController();\n const signal = this.#selfRenderAbortController.signal;\n this.#selfRenderDirty = false;\n\n this.#selfRenderPromise = (async () => {\n try {\n await host.prepareFrame!(timeMs, signal);\n signal.throwIfAborted();\n host.renderFrame!(timeMs);\n updateAnimations(this.#host as unknown as AnimatableElement);\n } catch (error) {\n if (error instanceof DOMException && error.name === \"AbortError\")\n return;\n if ((error as any)?.name === \"AbortError\") return;\n console.error(\"Standalone frame render error:\", error);\n } finally {\n this.#selfRenderPromise = undefined;\n // Re-render if source mapping changed while we were rendering\n if (this.#selfRenderDirty) {\n this.#startSelfRender(host, this.currentTimeMs);\n }\n }\n })();\n\n return this.#selfRenderPromise;\n }\n\n addListener(listener: (event: PlaybackControllerUpdateEvent) => void): void {\n this.#listeners.add(listener);\n }\n\n removeListener(\n listener: (event: PlaybackControllerUpdateEvent) => void,\n ): void {\n this.#listeners.delete(listener);\n }\n\n #notifyListeners(event: PlaybackControllerUpdateEvent): void {\n for (const listener of this.#listeners) {\n listener(event);\n }\n }\n\n remove(): void {\n this.#removed = true; // Mark as removed to abort any pending RAF callbacks\n this.stopPlayback();\n this.#listeners.clear();\n this.#host.removeController(this);\n }\n\n setPendingAudioContext(context: AudioContext): void {\n this.#pendingAudioContext = context;\n }\n\n #syncPlayheadToAudioContext(startMs: number) {\n const audioContextTime = this.#playbackAudioContext?.currentTime ?? 0;\n const endMs = this.#host.endTimeMs;\n\n // Calculate raw time based on audio context\n let rawTimeMs: number;\n if (\n this.#playbackWrapTimeSeconds > 0 &&\n audioContextTime >= this.#playbackWrapTimeSeconds\n ) {\n // After wrap: time since wrap, wrapped to duration\n const timeSinceWrap =\n (audioContextTime - this.#playbackWrapTimeSeconds) * 1000;\n rawTimeMs = timeSinceWrap % endMs;\n } else {\n // Before wrap or no wrap: normal calculation\n rawTimeMs = startMs + audioContextTime * 1000;\n\n // If looping and we've reached the end, wrap around\n if (this.#loopingPlayback && rawTimeMs >= endMs) {\n rawTimeMs = rawTimeMs % endMs;\n }\n }\n\n const nextTimeMs =\n Math.round(rawTimeMs / this.#MS_PER_FRAME) * this.#MS_PER_FRAME;\n\n // During playback, update time directly without triggering seek\n // This avoids frame drops at the loop boundary\n this.#updatePlaybackTime(nextTimeMs);\n\n // Only check for end if we haven't already handled looping\n if (!this.#loopingPlayback && nextTimeMs >= endMs) {\n this.maybeLoopPlayback();\n return;\n }\n\n this.#playbackAnimationFrameRequest = requestAnimationFrame(() => {\n this.#syncPlayheadToAudioContext(startMs);\n });\n }\n\n private async maybeLoopPlayback() {\n if (this.#loop) {\n // Loop enabled: reset to beginning and restart playback\n // We restart the audio system directly without changing #playing state\n // to keep the play button in sync\n this.setCurrentTimeMs(0);\n // Restart in next frame without awaiting to minimize gap\n requestAnimationFrame(() => {\n this.startPlayback();\n });\n } else {\n // No loop: reset to beginning and stop\n // This ensures play button works when clicked again\n this.setCurrentTimeMs(0);\n this.pause();\n }\n }\n\n private async stopPlayback() {\n if (this.#playbackAudioContext) {\n if (this.#playbackAudioContext.state !== \"closed\") {\n await this.#playbackAudioContext.close();\n }\n }\n if (this.#playbackAnimationFrameRequest) {\n cancelAnimationFrame(this.#playbackAnimationFrameRequest);\n }\n this.#playbackAudioContext = null;\n this.#playbackAnimationFrameRequest = null;\n this.#pendingAudioContext = null;\n }\n\n private async startPlayback() {\n // Guard against starting playback on a removed controller\n if (this.#removed) {\n return;\n }\n\n await this.stopPlayback();\n const host = this.#host;\n if (!host) {\n return;\n }\n\n if (host.waitForMediaDurations) {\n await host.waitForMediaDurations();\n }\n\n // Check again after async - controller could have been removed\n if (this.#removed) {\n return;\n }\n\n const currentMs = this.currentTimeMs;\n const fromMs = currentMs;\n const toMs = host.endTimeMs;\n\n if (fromMs >= toMs) {\n this.pause();\n return;\n }\n\n let bufferCount = 0;\n // Check for pre-resumed AudioContext from synchronous user interaction\n if (this.#pendingAudioContext) {\n this.#playbackAudioContext = this.#pendingAudioContext;\n this.#pendingAudioContext = null;\n } else {\n this.#playbackAudioContext = new AudioContext({\n latencyHint: \"playback\",\n });\n }\n this.#loopingPlayback = this.#loop; // Remember if we're in a looping session\n this.#playbackWrapTimeSeconds = 0; // Reset wrap time\n\n if (this.#playbackAnimationFrameRequest) {\n cancelAnimationFrame(this.#playbackAnimationFrameRequest);\n }\n this.#syncPlayheadToAudioContext(currentMs);\n const playbackContext = this.#playbackAudioContext;\n\n // Check if context is suspended (fallback for newly-created contexts)\n if (playbackContext.state === \"suspended\") {\n // Attempt to resume (may not work on mobile if user interaction context is lost)\n try {\n await playbackContext.resume();\n // Check state again after resume attempt\n if (playbackContext.state === \"suspended\") {\n console.warn(\n \"AudioContext is suspended and resume() failed. \" +\n \"On mobile devices, AudioContext.resume() must be called synchronously within a user interaction handler. \" +\n \"Media playback will not work until user has interacted with page.\",\n );\n this.setPlaying(false);\n return;\n }\n } catch (error) {\n console.warn(\n \"Failed to resume AudioContext:\",\n error,\n \"On mobile devices, AudioContext.resume() must be called synchronously within a user interaction handler.\",\n );\n this.setPlaying(false);\n return;\n }\n }\n await playbackContext.suspend();\n\n // Track the logical media time (what position in the media we're rendering)\n // vs the AudioContext schedule time (when to play it)\n let logicalTimeMs = currentMs;\n let audioContextTimeMs = 0; // Tracks the schedule position in the AudioContext timeline\n let hasWrapped = false;\n\n const fillBuffer = async () => {\n if (bufferCount > 2) {\n return;\n }\n const canFillBuffer = await queueBufferSource();\n if (canFillBuffer) {\n fillBuffer().catch(() => {});\n }\n };\n\n const queueBufferSource = async () => {\n // Check if we've already wrapped and aren't looping anymore\n if (hasWrapped && !this.#loopingPlayback) {\n return false;\n }\n\n const startMs = logicalTimeMs;\n const endMs = Math.min(\n logicalTimeMs + this.#AUDIO_PLAYBACK_SLICE_MS,\n toMs,\n );\n\n // Will this slice reach the end?\n const willReachEnd = endMs >= toMs;\n\n if (!host.renderAudio) {\n return false;\n }\n\n const audioBuffer = await host.renderAudio(startMs, endMs);\n bufferCount++;\n const source = playbackContext.createBufferSource();\n source.buffer = audioBuffer;\n source.connect(playbackContext.destination);\n // Schedule this buffer to play at the current audioContextTime position\n source.start(audioContextTimeMs / 1000);\n\n const sliceDurationMs = endMs - startMs;\n\n source.onended = () => {\n bufferCount--;\n\n if (willReachEnd) {\n if (!this.#loopingPlayback) {\n // Not looping, end playback\n this.maybeLoopPlayback();\n } else {\n // Looping: continue filling buffer after wrap\n fillBuffer().catch(() => {});\n }\n } else {\n // Continue filling buffer\n fillBuffer().catch(() => {});\n }\n };\n\n // Advance the AudioContext schedule time\n audioContextTimeMs += sliceDurationMs;\n\n // If this buffer reaches the end and we're looping, immediately queue the wraparound\n if (willReachEnd && this.#loopingPlayback) {\n // Mark that we've wrapped\n hasWrapped = true;\n // Store when we wrapped (relative to when playback started, which is time 0 in AudioContext)\n // This is the duration from start to end\n this.#playbackWrapTimeSeconds = (toMs - fromMs) / 1000;\n // Reset logical time to beginning\n logicalTimeMs = 0;\n // Continue buffering will happen in fillBuffer() call below\n } else {\n // Normal advance\n logicalTimeMs = endMs;\n }\n\n return true;\n };\n\n try {\n await fillBuffer();\n await playbackContext.resume();\n } catch (error) {\n // Ignore errors if AudioContext is closed or during test cleanup\n if (\n error instanceof Error &&\n (error.name === \"InvalidStateError\" || error.message.includes(\"closed\"))\n ) {\n return;\n }\n throw error;\n }\n }\n}\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  //#region src/gui/TWMixin.css?inline
2
- var TWMixin_default = "/* biome-ignore lint/suspicious/noUnknownAtRules: @tailwind is a valid Tailwind CSS directive */\n*, ::before, ::after {\n --tw-border-spacing-x: 0;\n --tw-border-spacing-y: 0;\n --tw-translate-x: 0;\n --tw-translate-y: 0;\n --tw-rotate: 0;\n --tw-skew-x: 0;\n --tw-skew-y: 0;\n --tw-scale-x: 1;\n --tw-scale-y: 1;\n --tw-pan-x: ;\n --tw-pan-y: ;\n --tw-pinch-zoom: ;\n --tw-scroll-snap-strictness: proximity;\n --tw-gradient-from-position: ;\n --tw-gradient-via-position: ;\n --tw-gradient-to-position: ;\n --tw-ordinal: ;\n --tw-slashed-zero: ;\n --tw-numeric-figure: ;\n --tw-numeric-spacing: ;\n --tw-numeric-fraction: ;\n --tw-ring-inset: ;\n --tw-ring-offset-width: 0px;\n --tw-ring-offset-color: #fff;\n --tw-ring-color: rgb(59 130 246 / 0.5);\n --tw-ring-offset-shadow: 0 0 #0000;\n --tw-ring-shadow: 0 0 #0000;\n --tw-shadow: 0 0 #0000;\n --tw-shadow-colored: 0 0 #0000;\n --tw-blur: ;\n --tw-brightness: ;\n --tw-contrast: ;\n --tw-grayscale: ;\n --tw-hue-rotate: ;\n --tw-invert: ;\n --tw-saturate: ;\n --tw-sepia: ;\n --tw-drop-shadow: ;\n --tw-backdrop-blur: ;\n --tw-backdrop-brightness: ;\n --tw-backdrop-contrast: ;\n --tw-backdrop-grayscale: ;\n --tw-backdrop-hue-rotate: ;\n --tw-backdrop-invert: ;\n --tw-backdrop-opacity: ;\n --tw-backdrop-saturate: ;\n --tw-backdrop-sepia: ;\n --tw-contain-size: ;\n --tw-contain-layout: ;\n --tw-contain-paint: ;\n --tw-contain-style: ;\n}\n::backdrop {\n --tw-border-spacing-x: 0;\n --tw-border-spacing-y: 0;\n --tw-translate-x: 0;\n --tw-translate-y: 0;\n --tw-rotate: 0;\n --tw-skew-x: 0;\n --tw-skew-y: 0;\n --tw-scale-x: 1;\n --tw-scale-y: 1;\n --tw-pan-x: ;\n --tw-pan-y: ;\n --tw-pinch-zoom: ;\n --tw-scroll-snap-strictness: proximity;\n --tw-gradient-from-position: ;\n --tw-gradient-via-position: ;\n --tw-gradient-to-position: ;\n --tw-ordinal: ;\n --tw-slashed-zero: ;\n --tw-numeric-figure: ;\n --tw-numeric-spacing: ;\n --tw-numeric-fraction: ;\n --tw-ring-inset: ;\n --tw-ring-offset-width: 0px;\n --tw-ring-offset-color: #fff;\n --tw-ring-color: rgb(59 130 246 / 0.5);\n --tw-ring-offset-shadow: 0 0 #0000;\n --tw-ring-shadow: 0 0 #0000;\n --tw-shadow: 0 0 #0000;\n --tw-shadow-colored: 0 0 #0000;\n --tw-blur: ;\n --tw-brightness: ;\n --tw-contrast: ;\n --tw-grayscale: ;\n --tw-hue-rotate: ;\n --tw-invert: ;\n --tw-saturate: ;\n --tw-sepia: ;\n --tw-drop-shadow: ;\n --tw-backdrop-blur: ;\n --tw-backdrop-brightness: ;\n --tw-backdrop-contrast: ;\n --tw-backdrop-grayscale: ;\n --tw-backdrop-hue-rotate: ;\n --tw-backdrop-invert: ;\n --tw-backdrop-opacity: ;\n --tw-backdrop-saturate: ;\n --tw-backdrop-sepia: ;\n --tw-contain-size: ;\n --tw-contain-layout: ;\n --tw-contain-paint: ;\n --tw-contain-style: ;\n}\n/* ! tailwindcss v3.4.18 | MIT License | https://tailwindcss.com */\n/*\n1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4)\n2. Allow adding a border to an element by just adding a border-width. (https://github.com/tailwindcss/tailwindcss/pull/116)\n*/\n*,\n::before,\n::after {\n box-sizing: border-box; /* 1 */\n border-width: 0; /* 2 */\n border-style: solid; /* 2 */\n border-color: #e5e7eb; /* 2 */\n}\n::before,\n::after {\n --tw-content: '';\n}\n/*\n1. Use a consistent sensible line-height in all browsers.\n2. Prevent adjustments of font size after orientation changes in iOS.\n3. Use a more readable tab size.\n4. Use the user's configured `sans` font-family by default.\n5. Use the user's configured `sans` font-feature-settings by default.\n6. Use the user's configured `sans` font-variation-settings by default.\n7. Disable tap highlights on iOS\n*/\nhtml,\n:host {\n line-height: 1.5; /* 1 */\n -webkit-text-size-adjust: 100%; /* 2 */\n -moz-tab-size: 4; /* 3 */\n -o-tab-size: 4;\n tab-size: 4; /* 3 */\n font-family: ui-sans-serif, system-ui, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\"; /* 4 */\n font-feature-settings: normal; /* 5 */\n font-variation-settings: normal; /* 6 */\n -webkit-tap-highlight-color: transparent; /* 7 */\n}\n/*\n1. Remove the margin in all browsers.\n2. Inherit line-height from `html` so users can set them as a class directly on the `html` element.\n*/\nbody {\n margin: 0; /* 1 */\n line-height: inherit; /* 2 */\n}\n/*\n1. Add the correct height in Firefox.\n2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655)\n3. Ensure horizontal rules are visible by default.\n*/\nhr {\n height: 0; /* 1 */\n color: inherit; /* 2 */\n border-top-width: 1px; /* 3 */\n}\n/*\nAdd the correct text decoration in Chrome, Edge, and Safari.\n*/\nabbr:where([title]) {\n -webkit-text-decoration: underline dotted;\n text-decoration: underline dotted;\n}\n/*\nRemove the default font size and weight for headings.\n*/\nh1,\nh2,\nh3,\nh4,\nh5,\nh6 {\n font-size: inherit;\n font-weight: inherit;\n}\n/*\nReset links to optimize for opt-in styling instead of opt-out.\n*/\na {\n color: inherit;\n text-decoration: inherit;\n}\n/*\nAdd the correct font weight in Edge and Safari.\n*/\nb,\nstrong {\n font-weight: bolder;\n}\n/*\n1. Use the user's configured `mono` font-family by default.\n2. Use the user's configured `mono` font-feature-settings by default.\n3. Use the user's configured `mono` font-variation-settings by default.\n4. Correct the odd `em` font sizing in all browsers.\n*/\ncode,\nkbd,\nsamp,\npre {\n font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace; /* 1 */\n font-feature-settings: normal; /* 2 */\n font-variation-settings: normal; /* 3 */\n font-size: 1em; /* 4 */\n}\n/*\nAdd the correct font size in all browsers.\n*/\nsmall {\n font-size: 80%;\n}\n/*\nPrevent `sub` and `sup` elements from affecting the line height in all browsers.\n*/\nsub,\nsup {\n font-size: 75%;\n line-height: 0;\n position: relative;\n vertical-align: baseline;\n}\nsub {\n bottom: -0.25em;\n}\nsup {\n top: -0.5em;\n}\n/*\n1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297)\n2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016)\n3. Remove gaps between table borders by default.\n*/\ntable {\n text-indent: 0; /* 1 */\n border-color: inherit; /* 2 */\n border-collapse: collapse; /* 3 */\n}\n/*\n1. Change the font styles in all browsers.\n2. Remove the margin in Firefox and Safari.\n3. Remove default padding in all browsers.\n*/\nbutton,\ninput,\noptgroup,\nselect,\ntextarea {\n font-family: inherit; /* 1 */\n font-feature-settings: inherit; /* 1 */\n font-variation-settings: inherit; /* 1 */\n font-size: 100%; /* 1 */\n font-weight: inherit; /* 1 */\n line-height: inherit; /* 1 */\n letter-spacing: inherit; /* 1 */\n color: inherit; /* 1 */\n margin: 0; /* 2 */\n padding: 0; /* 3 */\n}\n/*\nRemove the inheritance of text transform in Edge and Firefox.\n*/\nbutton,\nselect {\n text-transform: none;\n}\n/*\n1. Correct the inability to style clickable types in iOS and Safari.\n2. Remove default button styles.\n*/\nbutton,\ninput:where([type='button']),\ninput:where([type='reset']),\ninput:where([type='submit']) {\n -webkit-appearance: button; /* 1 */\n background-color: transparent; /* 2 */\n background-image: none; /* 2 */\n}\n/*\nUse the modern Firefox focus style for all focusable elements.\n*/\n:-moz-focusring {\n outline: auto;\n}\n/*\nRemove the additional `:invalid` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737)\n*/\n:-moz-ui-invalid {\n box-shadow: none;\n}\n/*\nAdd the correct vertical alignment in Chrome and Firefox.\n*/\nprogress {\n vertical-align: baseline;\n}\n/*\nCorrect the cursor style of increment and decrement buttons in Safari.\n*/\n::-webkit-inner-spin-button,\n::-webkit-outer-spin-button {\n height: auto;\n}\n/*\n1. Correct the odd appearance in Chrome and Safari.\n2. Correct the outline style in Safari.\n*/\n[type='search'] {\n -webkit-appearance: textfield; /* 1 */\n outline-offset: -2px; /* 2 */\n}\n/*\nRemove the inner padding in Chrome and Safari on macOS.\n*/\n::-webkit-search-decoration {\n -webkit-appearance: none;\n}\n/*\n1. Correct the inability to style clickable types in iOS and Safari.\n2. Change font properties to `inherit` in Safari.\n*/\n::-webkit-file-upload-button {\n -webkit-appearance: button; /* 1 */\n font: inherit; /* 2 */\n}\n/*\nAdd the correct display in Chrome and Safari.\n*/\nsummary {\n display: list-item;\n}\n/*\nRemoves the default spacing and border for appropriate elements.\n*/\nblockquote,\ndl,\ndd,\nh1,\nh2,\nh3,\nh4,\nh5,\nh6,\nhr,\nfigure,\np,\npre {\n margin: 0;\n}\nfieldset {\n margin: 0;\n padding: 0;\n}\nlegend {\n padding: 0;\n}\nol,\nul,\nmenu {\n list-style: none;\n margin: 0;\n padding: 0;\n}\n/*\nReset default styling for dialogs.\n*/\ndialog {\n padding: 0;\n}\n/*\nPrevent resizing textareas horizontally by default.\n*/\ntextarea {\n resize: vertical;\n}\n/*\n1. Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300)\n2. Set the default placeholder color to the user's configured gray 400 color.\n*/\ninput::-moz-placeholder, textarea::-moz-placeholder {\n opacity: 1; /* 1 */\n color: #9ca3af; /* 2 */\n}\ninput::placeholder,\ntextarea::placeholder {\n opacity: 1; /* 1 */\n color: #9ca3af; /* 2 */\n}\n/*\nSet the default cursor for buttons.\n*/\nbutton,\n[role=\"button\"] {\n cursor: pointer;\n}\n/*\nMake sure disabled buttons don't get the pointer cursor.\n*/\n:disabled {\n cursor: default;\n}\n/*\n1. Make replaced elements `display: block` by default. (https://github.com/mozdevs/cssremedy/issues/14)\n2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210)\n This can trigger a poorly considered lint error in some tools but is included by design.\n*/\nimg,\nsvg,\nvideo,\ncanvas,\naudio,\niframe,\nembed,\nobject {\n display: block; /* 1 */\n vertical-align: middle; /* 2 */\n}\n/*\nConstrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14)\n*/\nimg,\nvideo {\n max-width: 100%;\n height: auto;\n}\n/* Make elements with the HTML hidden attribute stay hidden by default */\n[hidden]:where(:not([hidden=\"until-found\"])) {\n display: none;\n}\n/* biome-ignore lint/suspicious/noUnknownAtRules: @tailwind is a valid Tailwind CSS directive */\n.\\!container {\n width: 100% !important;\n}\n.container {\n width: 100%;\n}\n@media (min-width: 640px) {\n .\\!container {\n max-width: 640px !important;\n }\n .container {\n max-width: 640px;\n }\n}\n@media (min-width: 768px) {\n .\\!container {\n max-width: 768px !important;\n }\n .container {\n max-width: 768px;\n }\n}\n@media (min-width: 1024px) {\n .\\!container {\n max-width: 1024px !important;\n }\n .container {\n max-width: 1024px;\n }\n}\n@media (min-width: 1280px) {\n .\\!container {\n max-width: 1280px !important;\n }\n .container {\n max-width: 1280px;\n }\n}\n@media (min-width: 1536px) {\n .\\!container {\n max-width: 1536px !important;\n }\n .container {\n max-width: 1536px;\n }\n}\n/* biome-ignore lint/suspicious/noUnknownAtRules: @tailwind is a valid Tailwind CSS directive */\n.\\!visible {\n visibility: visible !important;\n}\n.visible {\n visibility: visible;\n}\n.invisible {\n visibility: hidden;\n}\n.collapse {\n visibility: collapse;\n}\n.static {\n position: static;\n}\n.fixed {\n position: fixed;\n}\n.absolute {\n position: absolute;\n}\n.relative {\n position: relative;\n}\n.sticky {\n position: sticky;\n}\n.inset-0 {\n inset: 0px;\n}\n.left-0 {\n left: 0px;\n}\n.top-0 {\n top: 0px;\n}\n.isolate {\n isolation: isolate;\n}\n.z-\\[5\\] {\n z-index: 5;\n}\n.mb-0 {\n margin-bottom: 0px;\n}\n.mb-\\[1px\\] {\n margin-bottom: 1px;\n}\n.block {\n display: block;\n}\n.inline-block {\n display: inline-block;\n}\n.inline {\n display: inline;\n}\n.flex {\n display: flex;\n}\n.inline-flex {\n display: inline-flex;\n}\n.table {\n display: table;\n}\n.grid {\n display: grid;\n}\n.inline-grid {\n display: inline-grid;\n}\n.contents {\n display: contents;\n}\n.hidden {\n display: none;\n}\n.size-full {\n width: 100%;\n height: 100%;\n}\n.h-\\[1\\.1rem\\] {\n height: 1.1rem;\n}\n.h-\\[1080px\\] {\n height: 1080px;\n}\n.h-\\[200px\\] {\n height: 200px;\n}\n.h-\\[270px\\] {\n height: 270px;\n}\n.h-\\[300px\\] {\n height: 300px;\n}\n.h-\\[360px\\] {\n height: 360px;\n}\n.h-\\[400px\\] {\n height: 400px;\n}\n.h-\\[500px\\] {\n height: 500px;\n}\n.h-\\[5px\\] {\n height: 5px;\n}\n.h-full {\n height: 100%;\n}\n.w-1 {\n width: 0.25rem;\n}\n.w-\\[1000px\\] {\n width: 1000px;\n}\n.w-\\[1080px\\] {\n width: 1080px;\n}\n.w-\\[1920px\\] {\n width: 1920px;\n}\n.w-\\[200px\\] {\n width: 200px;\n}\n.w-\\[420px\\] {\n width: 420px;\n}\n.w-\\[480px\\] {\n width: 480px;\n}\n.w-\\[600px\\] {\n width: 600px;\n}\n.w-\\[640px\\] {\n width: 640px;\n}\n.w-full {\n width: 100%;\n}\n.flex-shrink {\n flex-shrink: 1;\n}\n.shrink {\n flex-shrink: 1;\n}\n.\\!transform {\n transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)) !important;\n}\n.transform {\n transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));\n}\n.cursor-grabbing {\n cursor: grabbing;\n}\n.cursor-pointer {\n cursor: pointer;\n}\n.resize {\n resize: both;\n}\n.flex-row {\n flex-direction: row;\n}\n.flex-wrap {\n flex-wrap: wrap;\n}\n.items-center {\n align-items: center;\n}\n.gap-2 {\n gap: 0.5rem;\n}\n.overflow-hidden {\n overflow: hidden;\n}\n.overflow-visible {\n overflow: visible;\n}\n.whitespace-nowrap {\n white-space: nowrap;\n}\n.text-nowrap {\n text-wrap: nowrap;\n}\n.rounded {\n border-radius: 0.25rem;\n}\n.border {\n border-width: 1px;\n}\n.bg-black {\n --tw-bg-opacity: 1;\n background-color: rgb(0 0 0 / var(--tw-bg-opacity, 1));\n}\n.bg-lime-400 {\n --tw-bg-opacity: 1;\n background-color: rgb(163 230 53 / var(--tw-bg-opacity, 1));\n}\n.bg-slate-500 {\n --tw-bg-opacity: 1;\n background-color: rgb(100 116 139 / var(--tw-bg-opacity, 1));\n}\n.bg-yellow-400 {\n --tw-bg-opacity: 1;\n background-color: rgb(250 204 21 / var(--tw-bg-opacity, 1));\n}\n.object-contain {\n -o-object-fit: contain;\n object-fit: contain;\n}\n.p-4 {\n padding: 1rem;\n}\n.px-0\\.5 {\n padding-left: 0.125rem;\n padding-right: 0.125rem;\n}\n.text-center {\n text-align: center;\n}\n.text-\\[8px\\] {\n font-size: 8px;\n}\n.text-sm {\n font-size: 0.875rem;\n line-height: 1.25rem;\n}\n.text-xs {\n font-size: 0.75rem;\n line-height: 1rem;\n}\n.font-bold {\n font-weight: 700;\n}\n.uppercase {\n text-transform: uppercase;\n}\n.capitalize {\n text-transform: capitalize;\n}\n.italic {\n font-style: italic;\n}\n.text-black {\n --tw-text-opacity: 1;\n color: rgb(0 0 0 / var(--tw-text-opacity, 1));\n}\n.text-green-200 {\n --tw-text-opacity: 1;\n color: rgb(187 247 208 / var(--tw-text-opacity, 1));\n}\n.text-green-900 {\n --tw-text-opacity: 1;\n color: rgb(20 83 45 / var(--tw-text-opacity, 1));\n}\n.opacity-50 {\n opacity: 0.5;\n}\n.shadow {\n --tw-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);\n --tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color);\n box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);\n}\n.outline {\n outline-style: solid;\n}\n.blur {\n --tw-blur: blur(8px);\n filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);\n}\n.drop-shadow {\n --tw-drop-shadow: drop-shadow(0 1px 2px rgb(0 0 0 / 0.1)) drop-shadow(0 1px 1px rgb(0 0 0 / 0.06));\n filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);\n}\n.filter {\n filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);\n}\n.backdrop-filter {\n backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);\n}\n.transition {\n transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter;\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n transition-duration: 150ms;\n}\n.ease-in {\n transition-timing-function: cubic-bezier(0.4, 0, 1, 1);\n}\n.ease-in-out {\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n}\n.ease-out {\n transition-timing-function: cubic-bezier(0, 0, 0.2, 1);\n}\n";
2
+ var TWMixin_default = "/* biome-ignore lint/suspicious/noUnknownAtRules: @tailwind is a valid Tailwind CSS directive */\n*, ::before, ::after {\n --tw-border-spacing-x: 0;\n --tw-border-spacing-y: 0;\n --tw-translate-x: 0;\n --tw-translate-y: 0;\n --tw-rotate: 0;\n --tw-skew-x: 0;\n --tw-skew-y: 0;\n --tw-scale-x: 1;\n --tw-scale-y: 1;\n --tw-pan-x: ;\n --tw-pan-y: ;\n --tw-pinch-zoom: ;\n --tw-scroll-snap-strictness: proximity;\n --tw-gradient-from-position: ;\n --tw-gradient-via-position: ;\n --tw-gradient-to-position: ;\n --tw-ordinal: ;\n --tw-slashed-zero: ;\n --tw-numeric-figure: ;\n --tw-numeric-spacing: ;\n --tw-numeric-fraction: ;\n --tw-ring-inset: ;\n --tw-ring-offset-width: 0px;\n --tw-ring-offset-color: #fff;\n --tw-ring-color: rgb(59 130 246 / 0.5);\n --tw-ring-offset-shadow: 0 0 #0000;\n --tw-ring-shadow: 0 0 #0000;\n --tw-shadow: 0 0 #0000;\n --tw-shadow-colored: 0 0 #0000;\n --tw-blur: ;\n --tw-brightness: ;\n --tw-contrast: ;\n --tw-grayscale: ;\n --tw-hue-rotate: ;\n --tw-invert: ;\n --tw-saturate: ;\n --tw-sepia: ;\n --tw-drop-shadow: ;\n --tw-backdrop-blur: ;\n --tw-backdrop-brightness: ;\n --tw-backdrop-contrast: ;\n --tw-backdrop-grayscale: ;\n --tw-backdrop-hue-rotate: ;\n --tw-backdrop-invert: ;\n --tw-backdrop-opacity: ;\n --tw-backdrop-saturate: ;\n --tw-backdrop-sepia: ;\n --tw-contain-size: ;\n --tw-contain-layout: ;\n --tw-contain-paint: ;\n --tw-contain-style: ;\n}\n::backdrop {\n --tw-border-spacing-x: 0;\n --tw-border-spacing-y: 0;\n --tw-translate-x: 0;\n --tw-translate-y: 0;\n --tw-rotate: 0;\n --tw-skew-x: 0;\n --tw-skew-y: 0;\n --tw-scale-x: 1;\n --tw-scale-y: 1;\n --tw-pan-x: ;\n --tw-pan-y: ;\n --tw-pinch-zoom: ;\n --tw-scroll-snap-strictness: proximity;\n --tw-gradient-from-position: ;\n --tw-gradient-via-position: ;\n --tw-gradient-to-position: ;\n --tw-ordinal: ;\n --tw-slashed-zero: ;\n --tw-numeric-figure: ;\n --tw-numeric-spacing: ;\n --tw-numeric-fraction: ;\n --tw-ring-inset: ;\n --tw-ring-offset-width: 0px;\n --tw-ring-offset-color: #fff;\n --tw-ring-color: rgb(59 130 246 / 0.5);\n --tw-ring-offset-shadow: 0 0 #0000;\n --tw-ring-shadow: 0 0 #0000;\n --tw-shadow: 0 0 #0000;\n --tw-shadow-colored: 0 0 #0000;\n --tw-blur: ;\n --tw-brightness: ;\n --tw-contrast: ;\n --tw-grayscale: ;\n --tw-hue-rotate: ;\n --tw-invert: ;\n --tw-saturate: ;\n --tw-sepia: ;\n --tw-drop-shadow: ;\n --tw-backdrop-blur: ;\n --tw-backdrop-brightness: ;\n --tw-backdrop-contrast: ;\n --tw-backdrop-grayscale: ;\n --tw-backdrop-hue-rotate: ;\n --tw-backdrop-invert: ;\n --tw-backdrop-opacity: ;\n --tw-backdrop-saturate: ;\n --tw-backdrop-sepia: ;\n --tw-contain-size: ;\n --tw-contain-layout: ;\n --tw-contain-paint: ;\n --tw-contain-style: ;\n}\n/* ! tailwindcss v3.4.18 | MIT License | https://tailwindcss.com */\n/*\n1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4)\n2. Allow adding a border to an element by just adding a border-width. (https://github.com/tailwindcss/tailwindcss/pull/116)\n*/\n*,\n::before,\n::after {\n box-sizing: border-box; /* 1 */\n border-width: 0; /* 2 */\n border-style: solid; /* 2 */\n border-color: #e5e7eb; /* 2 */\n}\n::before,\n::after {\n --tw-content: '';\n}\n/*\n1. Use a consistent sensible line-height in all browsers.\n2. Prevent adjustments of font size after orientation changes in iOS.\n3. Use a more readable tab size.\n4. Use the user's configured `sans` font-family by default.\n5. Use the user's configured `sans` font-feature-settings by default.\n6. Use the user's configured `sans` font-variation-settings by default.\n7. Disable tap highlights on iOS\n*/\nhtml,\n:host {\n line-height: 1.5; /* 1 */\n -webkit-text-size-adjust: 100%; /* 2 */\n -moz-tab-size: 4; /* 3 */\n -o-tab-size: 4;\n tab-size: 4; /* 3 */\n font-family: ui-sans-serif, system-ui, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\"; /* 4 */\n font-feature-settings: normal; /* 5 */\n font-variation-settings: normal; /* 6 */\n -webkit-tap-highlight-color: transparent; /* 7 */\n}\n/*\n1. Remove the margin in all browsers.\n2. Inherit line-height from `html` so users can set them as a class directly on the `html` element.\n*/\nbody {\n margin: 0; /* 1 */\n line-height: inherit; /* 2 */\n}\n/*\n1. Add the correct height in Firefox.\n2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655)\n3. Ensure horizontal rules are visible by default.\n*/\nhr {\n height: 0; /* 1 */\n color: inherit; /* 2 */\n border-top-width: 1px; /* 3 */\n}\n/*\nAdd the correct text decoration in Chrome, Edge, and Safari.\n*/\nabbr:where([title]) {\n -webkit-text-decoration: underline dotted;\n text-decoration: underline dotted;\n}\n/*\nRemove the default font size and weight for headings.\n*/\nh1,\nh2,\nh3,\nh4,\nh5,\nh6 {\n font-size: inherit;\n font-weight: inherit;\n}\n/*\nReset links to optimize for opt-in styling instead of opt-out.\n*/\na {\n color: inherit;\n text-decoration: inherit;\n}\n/*\nAdd the correct font weight in Edge and Safari.\n*/\nb,\nstrong {\n font-weight: bolder;\n}\n/*\n1. Use the user's configured `mono` font-family by default.\n2. Use the user's configured `mono` font-feature-settings by default.\n3. Use the user's configured `mono` font-variation-settings by default.\n4. Correct the odd `em` font sizing in all browsers.\n*/\ncode,\nkbd,\nsamp,\npre {\n font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace; /* 1 */\n font-feature-settings: normal; /* 2 */\n font-variation-settings: normal; /* 3 */\n font-size: 1em; /* 4 */\n}\n/*\nAdd the correct font size in all browsers.\n*/\nsmall {\n font-size: 80%;\n}\n/*\nPrevent `sub` and `sup` elements from affecting the line height in all browsers.\n*/\nsub,\nsup {\n font-size: 75%;\n line-height: 0;\n position: relative;\n vertical-align: baseline;\n}\nsub {\n bottom: -0.25em;\n}\nsup {\n top: -0.5em;\n}\n/*\n1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297)\n2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016)\n3. Remove gaps between table borders by default.\n*/\ntable {\n text-indent: 0; /* 1 */\n border-color: inherit; /* 2 */\n border-collapse: collapse; /* 3 */\n}\n/*\n1. Change the font styles in all browsers.\n2. Remove the margin in Firefox and Safari.\n3. Remove default padding in all browsers.\n*/\nbutton,\ninput,\noptgroup,\nselect,\ntextarea {\n font-family: inherit; /* 1 */\n font-feature-settings: inherit; /* 1 */\n font-variation-settings: inherit; /* 1 */\n font-size: 100%; /* 1 */\n font-weight: inherit; /* 1 */\n line-height: inherit; /* 1 */\n letter-spacing: inherit; /* 1 */\n color: inherit; /* 1 */\n margin: 0; /* 2 */\n padding: 0; /* 3 */\n}\n/*\nRemove the inheritance of text transform in Edge and Firefox.\n*/\nbutton,\nselect {\n text-transform: none;\n}\n/*\n1. Correct the inability to style clickable types in iOS and Safari.\n2. Remove default button styles.\n*/\nbutton,\ninput:where([type='button']),\ninput:where([type='reset']),\ninput:where([type='submit']) {\n -webkit-appearance: button; /* 1 */\n background-color: transparent; /* 2 */\n background-image: none; /* 2 */\n}\n/*\nUse the modern Firefox focus style for all focusable elements.\n*/\n:-moz-focusring {\n outline: auto;\n}\n/*\nRemove the additional `:invalid` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737)\n*/\n:-moz-ui-invalid {\n box-shadow: none;\n}\n/*\nAdd the correct vertical alignment in Chrome and Firefox.\n*/\nprogress {\n vertical-align: baseline;\n}\n/*\nCorrect the cursor style of increment and decrement buttons in Safari.\n*/\n::-webkit-inner-spin-button,\n::-webkit-outer-spin-button {\n height: auto;\n}\n/*\n1. Correct the odd appearance in Chrome and Safari.\n2. Correct the outline style in Safari.\n*/\n[type='search'] {\n -webkit-appearance: textfield; /* 1 */\n outline-offset: -2px; /* 2 */\n}\n/*\nRemove the inner padding in Chrome and Safari on macOS.\n*/\n::-webkit-search-decoration {\n -webkit-appearance: none;\n}\n/*\n1. Correct the inability to style clickable types in iOS and Safari.\n2. Change font properties to `inherit` in Safari.\n*/\n::-webkit-file-upload-button {\n -webkit-appearance: button; /* 1 */\n font: inherit; /* 2 */\n}\n/*\nAdd the correct display in Chrome and Safari.\n*/\nsummary {\n display: list-item;\n}\n/*\nRemoves the default spacing and border for appropriate elements.\n*/\nblockquote,\ndl,\ndd,\nh1,\nh2,\nh3,\nh4,\nh5,\nh6,\nhr,\nfigure,\np,\npre {\n margin: 0;\n}\nfieldset {\n margin: 0;\n padding: 0;\n}\nlegend {\n padding: 0;\n}\nol,\nul,\nmenu {\n list-style: none;\n margin: 0;\n padding: 0;\n}\n/*\nReset default styling for dialogs.\n*/\ndialog {\n padding: 0;\n}\n/*\nPrevent resizing textareas horizontally by default.\n*/\ntextarea {\n resize: vertical;\n}\n/*\n1. Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300)\n2. Set the default placeholder color to the user's configured gray 400 color.\n*/\ninput::-moz-placeholder, textarea::-moz-placeholder {\n opacity: 1; /* 1 */\n color: #9ca3af; /* 2 */\n}\ninput::placeholder,\ntextarea::placeholder {\n opacity: 1; /* 1 */\n color: #9ca3af; /* 2 */\n}\n/*\nSet the default cursor for buttons.\n*/\nbutton,\n[role=\"button\"] {\n cursor: pointer;\n}\n/*\nMake sure disabled buttons don't get the pointer cursor.\n*/\n:disabled {\n cursor: default;\n}\n/*\n1. Make replaced elements `display: block` by default. (https://github.com/mozdevs/cssremedy/issues/14)\n2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210)\n This can trigger a poorly considered lint error in some tools but is included by design.\n*/\nimg,\nsvg,\nvideo,\ncanvas,\naudio,\niframe,\nembed,\nobject {\n display: block; /* 1 */\n vertical-align: middle; /* 2 */\n}\n/*\nConstrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14)\n*/\nimg,\nvideo {\n max-width: 100%;\n height: auto;\n}\n/* Make elements with the HTML hidden attribute stay hidden by default */\n[hidden]:where(:not([hidden=\"until-found\"])) {\n display: none;\n}\n/* biome-ignore lint/suspicious/noUnknownAtRules: @tailwind is a valid Tailwind CSS directive */\n.\\!container {\n width: 100% !important;\n}\n.container {\n width: 100%;\n}\n@media (min-width: 640px) {\n .\\!container {\n max-width: 640px !important;\n }\n .container {\n max-width: 640px;\n }\n}\n@media (min-width: 768px) {\n .\\!container {\n max-width: 768px !important;\n }\n .container {\n max-width: 768px;\n }\n}\n@media (min-width: 1024px) {\n .\\!container {\n max-width: 1024px !important;\n }\n .container {\n max-width: 1024px;\n }\n}\n@media (min-width: 1280px) {\n .\\!container {\n max-width: 1280px !important;\n }\n .container {\n max-width: 1280px;\n }\n}\n@media (min-width: 1536px) {\n .\\!container {\n max-width: 1536px !important;\n }\n .container {\n max-width: 1536px;\n }\n}\n/* biome-ignore lint/suspicious/noUnknownAtRules: @tailwind is a valid Tailwind CSS directive */\n.visible {\n visibility: visible;\n}\n.invisible {\n visibility: hidden;\n}\n.collapse {\n visibility: collapse;\n}\n.static {\n position: static;\n}\n.fixed {\n position: fixed;\n}\n.absolute {\n position: absolute;\n}\n.relative {\n position: relative;\n}\n.sticky {\n position: sticky;\n}\n.inset-0 {\n inset: 0px;\n}\n.left-0 {\n left: 0px;\n}\n.right-0 {\n right: 0px;\n}\n.top-0 {\n top: 0px;\n}\n.top-8 {\n top: 2rem;\n}\n.isolate {\n isolation: isolate;\n}\n.z-\\[5\\] {\n z-index: 5;\n}\n.mb-0 {\n margin-bottom: 0px;\n}\n.mb-\\[1px\\] {\n margin-bottom: 1px;\n}\n.block {\n display: block;\n}\n.inline-block {\n display: inline-block;\n}\n.inline {\n display: inline;\n}\n.flex {\n display: flex;\n}\n.inline-flex {\n display: inline-flex;\n}\n.table {\n display: table;\n}\n.grid {\n display: grid;\n}\n.inline-grid {\n display: inline-grid;\n}\n.contents {\n display: contents;\n}\n.hidden {\n display: none;\n}\n.size-full {\n width: 100%;\n height: 100%;\n}\n.h-\\[1\\.1rem\\] {\n height: 1.1rem;\n}\n.h-\\[1080px\\] {\n height: 1080px;\n}\n.h-\\[500px\\] {\n height: 500px;\n}\n.h-\\[5px\\] {\n height: 5px;\n}\n.h-\\[calc\\(50vh-4rem\\)\\] {\n height: calc(50vh - 4rem);\n}\n.h-full {\n height: 100%;\n}\n.w-1 {\n width: 0.25rem;\n}\n.w-\\[1000px\\] {\n width: 1000px;\n}\n.w-\\[1920px\\] {\n width: 1920px;\n}\n.w-\\[420px\\] {\n width: 420px;\n}\n.w-full {\n width: 100%;\n}\n.flex-1 {\n flex: 1 1 0%;\n}\n.flex-shrink {\n flex-shrink: 1;\n}\n.shrink {\n flex-shrink: 1;\n}\n.\\!transform {\n transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)) !important;\n}\n.transform {\n transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));\n}\n.resize {\n resize: both;\n}\n.flex-wrap {\n flex-wrap: wrap;\n}\n.items-center {\n align-items: center;\n}\n.overflow-hidden {\n overflow: hidden;\n}\n.overflow-visible {\n overflow: visible;\n}\n.whitespace-nowrap {\n white-space: nowrap;\n}\n.text-nowrap {\n text-wrap: nowrap;\n}\n.rounded {\n border-radius: 0.25rem;\n}\n.border {\n border-width: 1px;\n}\n.bg-slate-500 {\n --tw-bg-opacity: 1;\n background-color: rgb(100 116 139 / var(--tw-bg-opacity, 1));\n}\n.object-cover {\n -o-object-fit: cover;\n object-fit: cover;\n}\n.px-0\\.5 {\n padding-left: 0.125rem;\n padding-right: 0.125rem;\n}\n.text-center {\n text-align: center;\n}\n.text-3xl {\n font-size: 1.875rem;\n line-height: 2.25rem;\n}\n.text-\\[8px\\] {\n font-size: 8px;\n}\n.text-sm {\n font-size: 0.875rem;\n line-height: 1.25rem;\n}\n.text-xs {\n font-size: 0.75rem;\n line-height: 1rem;\n}\n.font-bold {\n font-weight: 700;\n}\n.uppercase {\n text-transform: uppercase;\n}\n.capitalize {\n text-transform: capitalize;\n}\n.italic {\n font-style: italic;\n}\n.text-white {\n --tw-text-opacity: 1;\n color: rgb(255 255 255 / var(--tw-text-opacity, 1));\n}\n.opacity-50 {\n opacity: 0.5;\n}\n.shadow {\n --tw-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);\n --tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color);\n box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);\n}\n.outline {\n outline-style: solid;\n}\n.blur {\n --tw-blur: blur(8px);\n filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);\n}\n.grayscale {\n --tw-grayscale: grayscale(100%);\n filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);\n}\n.filter {\n filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);\n}\n.backdrop-filter {\n backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);\n}\n.transition {\n transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter;\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n transition-duration: 150ms;\n}\n.ease-in {\n transition-timing-function: cubic-bezier(0.4, 0, 1, 1);\n}\n.ease-in-out {\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n}\n.ease-out {\n transition-timing-function: cubic-bezier(0, 0, 0.2, 1);\n}\n";
3
3
 
4
4
  //#endregion
5
5
  export { TWMixin_default as default };
@@ -1 +1 @@
1
- {"version":3,"file":"TWMixin.js","names":[],"sources":["../../src/gui/TWMixin.css?inline"],"sourcesContent":["export default \"/* biome-ignore lint/suspicious/noUnknownAtRules: @tailwind is a valid Tailwind CSS directive */\\n*, ::before, ::after {\\n --tw-border-spacing-x: 0;\\n --tw-border-spacing-y: 0;\\n --tw-translate-x: 0;\\n --tw-translate-y: 0;\\n --tw-rotate: 0;\\n --tw-skew-x: 0;\\n --tw-skew-y: 0;\\n --tw-scale-x: 1;\\n --tw-scale-y: 1;\\n --tw-pan-x: ;\\n --tw-pan-y: ;\\n --tw-pinch-zoom: ;\\n --tw-scroll-snap-strictness: proximity;\\n --tw-gradient-from-position: ;\\n --tw-gradient-via-position: ;\\n --tw-gradient-to-position: ;\\n --tw-ordinal: ;\\n --tw-slashed-zero: ;\\n --tw-numeric-figure: ;\\n --tw-numeric-spacing: ;\\n --tw-numeric-fraction: ;\\n --tw-ring-inset: ;\\n --tw-ring-offset-width: 0px;\\n --tw-ring-offset-color: #fff;\\n --tw-ring-color: rgb(59 130 246 / 0.5);\\n --tw-ring-offset-shadow: 0 0 #0000;\\n --tw-ring-shadow: 0 0 #0000;\\n --tw-shadow: 0 0 #0000;\\n --tw-shadow-colored: 0 0 #0000;\\n --tw-blur: ;\\n --tw-brightness: ;\\n --tw-contrast: ;\\n --tw-grayscale: ;\\n --tw-hue-rotate: ;\\n --tw-invert: ;\\n --tw-saturate: ;\\n --tw-sepia: ;\\n --tw-drop-shadow: ;\\n --tw-backdrop-blur: ;\\n --tw-backdrop-brightness: ;\\n --tw-backdrop-contrast: ;\\n --tw-backdrop-grayscale: ;\\n --tw-backdrop-hue-rotate: ;\\n --tw-backdrop-invert: ;\\n --tw-backdrop-opacity: ;\\n --tw-backdrop-saturate: ;\\n --tw-backdrop-sepia: ;\\n --tw-contain-size: ;\\n --tw-contain-layout: ;\\n --tw-contain-paint: ;\\n --tw-contain-style: ;\\n}\\n::backdrop {\\n --tw-border-spacing-x: 0;\\n --tw-border-spacing-y: 0;\\n --tw-translate-x: 0;\\n --tw-translate-y: 0;\\n --tw-rotate: 0;\\n --tw-skew-x: 0;\\n --tw-skew-y: 0;\\n --tw-scale-x: 1;\\n --tw-scale-y: 1;\\n --tw-pan-x: ;\\n --tw-pan-y: ;\\n --tw-pinch-zoom: ;\\n --tw-scroll-snap-strictness: proximity;\\n --tw-gradient-from-position: ;\\n --tw-gradient-via-position: ;\\n --tw-gradient-to-position: ;\\n --tw-ordinal: ;\\n --tw-slashed-zero: ;\\n --tw-numeric-figure: ;\\n --tw-numeric-spacing: ;\\n --tw-numeric-fraction: ;\\n --tw-ring-inset: ;\\n --tw-ring-offset-width: 0px;\\n --tw-ring-offset-color: #fff;\\n --tw-ring-color: rgb(59 130 246 / 0.5);\\n --tw-ring-offset-shadow: 0 0 #0000;\\n --tw-ring-shadow: 0 0 #0000;\\n --tw-shadow: 0 0 #0000;\\n --tw-shadow-colored: 0 0 #0000;\\n --tw-blur: ;\\n --tw-brightness: ;\\n --tw-contrast: ;\\n --tw-grayscale: ;\\n --tw-hue-rotate: ;\\n --tw-invert: ;\\n --tw-saturate: ;\\n --tw-sepia: ;\\n --tw-drop-shadow: ;\\n --tw-backdrop-blur: ;\\n --tw-backdrop-brightness: ;\\n --tw-backdrop-contrast: ;\\n --tw-backdrop-grayscale: ;\\n --tw-backdrop-hue-rotate: ;\\n --tw-backdrop-invert: ;\\n --tw-backdrop-opacity: ;\\n --tw-backdrop-saturate: ;\\n --tw-backdrop-sepia: ;\\n --tw-contain-size: ;\\n --tw-contain-layout: ;\\n --tw-contain-paint: ;\\n --tw-contain-style: ;\\n}\\n/* ! tailwindcss v3.4.18 | MIT License | https://tailwindcss.com */\\n/*\\n1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4)\\n2. Allow adding a border to an element by just adding a border-width. (https://github.com/tailwindcss/tailwindcss/pull/116)\\n*/\\n*,\\n::before,\\n::after {\\n box-sizing: border-box; /* 1 */\\n border-width: 0; /* 2 */\\n border-style: solid; /* 2 */\\n border-color: #e5e7eb; /* 2 */\\n}\\n::before,\\n::after {\\n --tw-content: '';\\n}\\n/*\\n1. Use a consistent sensible line-height in all browsers.\\n2. Prevent adjustments of font size after orientation changes in iOS.\\n3. Use a more readable tab size.\\n4. Use the user's configured `sans` font-family by default.\\n5. Use the user's configured `sans` font-feature-settings by default.\\n6. Use the user's configured `sans` font-variation-settings by default.\\n7. Disable tap highlights on iOS\\n*/\\nhtml,\\n:host {\\n line-height: 1.5; /* 1 */\\n -webkit-text-size-adjust: 100%; /* 2 */\\n -moz-tab-size: 4; /* 3 */\\n -o-tab-size: 4;\\n tab-size: 4; /* 3 */\\n font-family: ui-sans-serif, system-ui, sans-serif, \\\"Apple Color Emoji\\\", \\\"Segoe UI Emoji\\\", \\\"Segoe UI Symbol\\\", \\\"Noto Color Emoji\\\"; /* 4 */\\n font-feature-settings: normal; /* 5 */\\n font-variation-settings: normal; /* 6 */\\n -webkit-tap-highlight-color: transparent; /* 7 */\\n}\\n/*\\n1. Remove the margin in all browsers.\\n2. Inherit line-height from `html` so users can set them as a class directly on the `html` element.\\n*/\\nbody {\\n margin: 0; /* 1 */\\n line-height: inherit; /* 2 */\\n}\\n/*\\n1. Add the correct height in Firefox.\\n2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655)\\n3. Ensure horizontal rules are visible by default.\\n*/\\nhr {\\n height: 0; /* 1 */\\n color: inherit; /* 2 */\\n border-top-width: 1px; /* 3 */\\n}\\n/*\\nAdd the correct text decoration in Chrome, Edge, and Safari.\\n*/\\nabbr:where([title]) {\\n -webkit-text-decoration: underline dotted;\\n text-decoration: underline dotted;\\n}\\n/*\\nRemove the default font size and weight for headings.\\n*/\\nh1,\\nh2,\\nh3,\\nh4,\\nh5,\\nh6 {\\n font-size: inherit;\\n font-weight: inherit;\\n}\\n/*\\nReset links to optimize for opt-in styling instead of opt-out.\\n*/\\na {\\n color: inherit;\\n text-decoration: inherit;\\n}\\n/*\\nAdd the correct font weight in Edge and Safari.\\n*/\\nb,\\nstrong {\\n font-weight: bolder;\\n}\\n/*\\n1. Use the user's configured `mono` font-family by default.\\n2. Use the user's configured `mono` font-feature-settings by default.\\n3. Use the user's configured `mono` font-variation-settings by default.\\n4. Correct the odd `em` font sizing in all browsers.\\n*/\\ncode,\\nkbd,\\nsamp,\\npre {\\n font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \\\"Liberation Mono\\\", \\\"Courier New\\\", monospace; /* 1 */\\n font-feature-settings: normal; /* 2 */\\n font-variation-settings: normal; /* 3 */\\n font-size: 1em; /* 4 */\\n}\\n/*\\nAdd the correct font size in all browsers.\\n*/\\nsmall {\\n font-size: 80%;\\n}\\n/*\\nPrevent `sub` and `sup` elements from affecting the line height in all browsers.\\n*/\\nsub,\\nsup {\\n font-size: 75%;\\n line-height: 0;\\n position: relative;\\n vertical-align: baseline;\\n}\\nsub {\\n bottom: -0.25em;\\n}\\nsup {\\n top: -0.5em;\\n}\\n/*\\n1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297)\\n2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016)\\n3. Remove gaps between table borders by default.\\n*/\\ntable {\\n text-indent: 0; /* 1 */\\n border-color: inherit; /* 2 */\\n border-collapse: collapse; /* 3 */\\n}\\n/*\\n1. Change the font styles in all browsers.\\n2. Remove the margin in Firefox and Safari.\\n3. Remove default padding in all browsers.\\n*/\\nbutton,\\ninput,\\noptgroup,\\nselect,\\ntextarea {\\n font-family: inherit; /* 1 */\\n font-feature-settings: inherit; /* 1 */\\n font-variation-settings: inherit; /* 1 */\\n font-size: 100%; /* 1 */\\n font-weight: inherit; /* 1 */\\n line-height: inherit; /* 1 */\\n letter-spacing: inherit; /* 1 */\\n color: inherit; /* 1 */\\n margin: 0; /* 2 */\\n padding: 0; /* 3 */\\n}\\n/*\\nRemove the inheritance of text transform in Edge and Firefox.\\n*/\\nbutton,\\nselect {\\n text-transform: none;\\n}\\n/*\\n1. Correct the inability to style clickable types in iOS and Safari.\\n2. Remove default button styles.\\n*/\\nbutton,\\ninput:where([type='button']),\\ninput:where([type='reset']),\\ninput:where([type='submit']) {\\n -webkit-appearance: button; /* 1 */\\n background-color: transparent; /* 2 */\\n background-image: none; /* 2 */\\n}\\n/*\\nUse the modern Firefox focus style for all focusable elements.\\n*/\\n:-moz-focusring {\\n outline: auto;\\n}\\n/*\\nRemove the additional `:invalid` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737)\\n*/\\n:-moz-ui-invalid {\\n box-shadow: none;\\n}\\n/*\\nAdd the correct vertical alignment in Chrome and Firefox.\\n*/\\nprogress {\\n vertical-align: baseline;\\n}\\n/*\\nCorrect the cursor style of increment and decrement buttons in Safari.\\n*/\\n::-webkit-inner-spin-button,\\n::-webkit-outer-spin-button {\\n height: auto;\\n}\\n/*\\n1. Correct the odd appearance in Chrome and Safari.\\n2. Correct the outline style in Safari.\\n*/\\n[type='search'] {\\n -webkit-appearance: textfield; /* 1 */\\n outline-offset: -2px; /* 2 */\\n}\\n/*\\nRemove the inner padding in Chrome and Safari on macOS.\\n*/\\n::-webkit-search-decoration {\\n -webkit-appearance: none;\\n}\\n/*\\n1. Correct the inability to style clickable types in iOS and Safari.\\n2. Change font properties to `inherit` in Safari.\\n*/\\n::-webkit-file-upload-button {\\n -webkit-appearance: button; /* 1 */\\n font: inherit; /* 2 */\\n}\\n/*\\nAdd the correct display in Chrome and Safari.\\n*/\\nsummary {\\n display: list-item;\\n}\\n/*\\nRemoves the default spacing and border for appropriate elements.\\n*/\\nblockquote,\\ndl,\\ndd,\\nh1,\\nh2,\\nh3,\\nh4,\\nh5,\\nh6,\\nhr,\\nfigure,\\np,\\npre {\\n margin: 0;\\n}\\nfieldset {\\n margin: 0;\\n padding: 0;\\n}\\nlegend {\\n padding: 0;\\n}\\nol,\\nul,\\nmenu {\\n list-style: none;\\n margin: 0;\\n padding: 0;\\n}\\n/*\\nReset default styling for dialogs.\\n*/\\ndialog {\\n padding: 0;\\n}\\n/*\\nPrevent resizing textareas horizontally by default.\\n*/\\ntextarea {\\n resize: vertical;\\n}\\n/*\\n1. Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300)\\n2. Set the default placeholder color to the user's configured gray 400 color.\\n*/\\ninput::-moz-placeholder, textarea::-moz-placeholder {\\n opacity: 1; /* 1 */\\n color: #9ca3af; /* 2 */\\n}\\ninput::placeholder,\\ntextarea::placeholder {\\n opacity: 1; /* 1 */\\n color: #9ca3af; /* 2 */\\n}\\n/*\\nSet the default cursor for buttons.\\n*/\\nbutton,\\n[role=\\\"button\\\"] {\\n cursor: pointer;\\n}\\n/*\\nMake sure disabled buttons don't get the pointer cursor.\\n*/\\n:disabled {\\n cursor: default;\\n}\\n/*\\n1. Make replaced elements `display: block` by default. (https://github.com/mozdevs/cssremedy/issues/14)\\n2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210)\\n This can trigger a poorly considered lint error in some tools but is included by design.\\n*/\\nimg,\\nsvg,\\nvideo,\\ncanvas,\\naudio,\\niframe,\\nembed,\\nobject {\\n display: block; /* 1 */\\n vertical-align: middle; /* 2 */\\n}\\n/*\\nConstrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14)\\n*/\\nimg,\\nvideo {\\n max-width: 100%;\\n height: auto;\\n}\\n/* Make elements with the HTML hidden attribute stay hidden by default */\\n[hidden]:where(:not([hidden=\\\"until-found\\\"])) {\\n display: none;\\n}\\n/* biome-ignore lint/suspicious/noUnknownAtRules: @tailwind is a valid Tailwind CSS directive */\\n.\\\\!container {\\n width: 100% !important;\\n}\\n.container {\\n width: 100%;\\n}\\n@media (min-width: 640px) {\\n .\\\\!container {\\n max-width: 640px !important;\\n }\\n .container {\\n max-width: 640px;\\n }\\n}\\n@media (min-width: 768px) {\\n .\\\\!container {\\n max-width: 768px !important;\\n }\\n .container {\\n max-width: 768px;\\n }\\n}\\n@media (min-width: 1024px) {\\n .\\\\!container {\\n max-width: 1024px !important;\\n }\\n .container {\\n max-width: 1024px;\\n }\\n}\\n@media (min-width: 1280px) {\\n .\\\\!container {\\n max-width: 1280px !important;\\n }\\n .container {\\n max-width: 1280px;\\n }\\n}\\n@media (min-width: 1536px) {\\n .\\\\!container {\\n max-width: 1536px !important;\\n }\\n .container {\\n max-width: 1536px;\\n }\\n}\\n/* biome-ignore lint/suspicious/noUnknownAtRules: @tailwind is a valid Tailwind CSS directive */\\n.\\\\!visible {\\n visibility: visible !important;\\n}\\n.visible {\\n visibility: visible;\\n}\\n.invisible {\\n visibility: hidden;\\n}\\n.collapse {\\n visibility: collapse;\\n}\\n.static {\\n position: static;\\n}\\n.fixed {\\n position: fixed;\\n}\\n.absolute {\\n position: absolute;\\n}\\n.relative {\\n position: relative;\\n}\\n.sticky {\\n position: sticky;\\n}\\n.inset-0 {\\n inset: 0px;\\n}\\n.left-0 {\\n left: 0px;\\n}\\n.top-0 {\\n top: 0px;\\n}\\n.isolate {\\n isolation: isolate;\\n}\\n.z-\\\\[5\\\\] {\\n z-index: 5;\\n}\\n.mb-0 {\\n margin-bottom: 0px;\\n}\\n.mb-\\\\[1px\\\\] {\\n margin-bottom: 1px;\\n}\\n.block {\\n display: block;\\n}\\n.inline-block {\\n display: inline-block;\\n}\\n.inline {\\n display: inline;\\n}\\n.flex {\\n display: flex;\\n}\\n.inline-flex {\\n display: inline-flex;\\n}\\n.table {\\n display: table;\\n}\\n.grid {\\n display: grid;\\n}\\n.inline-grid {\\n display: inline-grid;\\n}\\n.contents {\\n display: contents;\\n}\\n.hidden {\\n display: none;\\n}\\n.size-full {\\n width: 100%;\\n height: 100%;\\n}\\n.h-\\\\[1\\\\.1rem\\\\] {\\n height: 1.1rem;\\n}\\n.h-\\\\[1080px\\\\] {\\n height: 1080px;\\n}\\n.h-\\\\[200px\\\\] {\\n height: 200px;\\n}\\n.h-\\\\[270px\\\\] {\\n height: 270px;\\n}\\n.h-\\\\[300px\\\\] {\\n height: 300px;\\n}\\n.h-\\\\[360px\\\\] {\\n height: 360px;\\n}\\n.h-\\\\[400px\\\\] {\\n height: 400px;\\n}\\n.h-\\\\[500px\\\\] {\\n height: 500px;\\n}\\n.h-\\\\[5px\\\\] {\\n height: 5px;\\n}\\n.h-full {\\n height: 100%;\\n}\\n.w-1 {\\n width: 0.25rem;\\n}\\n.w-\\\\[1000px\\\\] {\\n width: 1000px;\\n}\\n.w-\\\\[1080px\\\\] {\\n width: 1080px;\\n}\\n.w-\\\\[1920px\\\\] {\\n width: 1920px;\\n}\\n.w-\\\\[200px\\\\] {\\n width: 200px;\\n}\\n.w-\\\\[420px\\\\] {\\n width: 420px;\\n}\\n.w-\\\\[480px\\\\] {\\n width: 480px;\\n}\\n.w-\\\\[600px\\\\] {\\n width: 600px;\\n}\\n.w-\\\\[640px\\\\] {\\n width: 640px;\\n}\\n.w-full {\\n width: 100%;\\n}\\n.flex-shrink {\\n flex-shrink: 1;\\n}\\n.shrink {\\n flex-shrink: 1;\\n}\\n.\\\\!transform {\\n transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)) !important;\\n}\\n.transform {\\n transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));\\n}\\n.cursor-grabbing {\\n cursor: grabbing;\\n}\\n.cursor-pointer {\\n cursor: pointer;\\n}\\n.resize {\\n resize: both;\\n}\\n.flex-row {\\n flex-direction: row;\\n}\\n.flex-wrap {\\n flex-wrap: wrap;\\n}\\n.items-center {\\n align-items: center;\\n}\\n.gap-2 {\\n gap: 0.5rem;\\n}\\n.overflow-hidden {\\n overflow: hidden;\\n}\\n.overflow-visible {\\n overflow: visible;\\n}\\n.whitespace-nowrap {\\n white-space: nowrap;\\n}\\n.text-nowrap {\\n text-wrap: nowrap;\\n}\\n.rounded {\\n border-radius: 0.25rem;\\n}\\n.border {\\n border-width: 1px;\\n}\\n.bg-black {\\n --tw-bg-opacity: 1;\\n background-color: rgb(0 0 0 / var(--tw-bg-opacity, 1));\\n}\\n.bg-lime-400 {\\n --tw-bg-opacity: 1;\\n background-color: rgb(163 230 53 / var(--tw-bg-opacity, 1));\\n}\\n.bg-slate-500 {\\n --tw-bg-opacity: 1;\\n background-color: rgb(100 116 139 / var(--tw-bg-opacity, 1));\\n}\\n.bg-yellow-400 {\\n --tw-bg-opacity: 1;\\n background-color: rgb(250 204 21 / var(--tw-bg-opacity, 1));\\n}\\n.object-contain {\\n -o-object-fit: contain;\\n object-fit: contain;\\n}\\n.p-4 {\\n padding: 1rem;\\n}\\n.px-0\\\\.5 {\\n padding-left: 0.125rem;\\n padding-right: 0.125rem;\\n}\\n.text-center {\\n text-align: center;\\n}\\n.text-\\\\[8px\\\\] {\\n font-size: 8px;\\n}\\n.text-sm {\\n font-size: 0.875rem;\\n line-height: 1.25rem;\\n}\\n.text-xs {\\n font-size: 0.75rem;\\n line-height: 1rem;\\n}\\n.font-bold {\\n font-weight: 700;\\n}\\n.uppercase {\\n text-transform: uppercase;\\n}\\n.capitalize {\\n text-transform: capitalize;\\n}\\n.italic {\\n font-style: italic;\\n}\\n.text-black {\\n --tw-text-opacity: 1;\\n color: rgb(0 0 0 / var(--tw-text-opacity, 1));\\n}\\n.text-green-200 {\\n --tw-text-opacity: 1;\\n color: rgb(187 247 208 / var(--tw-text-opacity, 1));\\n}\\n.text-green-900 {\\n --tw-text-opacity: 1;\\n color: rgb(20 83 45 / var(--tw-text-opacity, 1));\\n}\\n.opacity-50 {\\n opacity: 0.5;\\n}\\n.shadow {\\n --tw-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);\\n --tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color);\\n box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);\\n}\\n.outline {\\n outline-style: solid;\\n}\\n.blur {\\n --tw-blur: blur(8px);\\n filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);\\n}\\n.drop-shadow {\\n --tw-drop-shadow: drop-shadow(0 1px 2px rgb(0 0 0 / 0.1)) drop-shadow(0 1px 1px rgb(0 0 0 / 0.06));\\n filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);\\n}\\n.filter {\\n filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);\\n}\\n.backdrop-filter {\\n backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);\\n}\\n.transition {\\n transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter;\\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\\n transition-duration: 150ms;\\n}\\n.ease-in {\\n transition-timing-function: cubic-bezier(0.4, 0, 1, 1);\\n}\\n.ease-in-out {\\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\\n}\\n.ease-out {\\n transition-timing-function: cubic-bezier(0, 0, 0.2, 1);\\n}\\n\""],"mappings":";AAAA,sBAAe"}
1
+ {"version":3,"file":"TWMixin.js","names":[],"sources":["../../src/gui/TWMixin.css?inline"],"sourcesContent":["export default \"/* biome-ignore lint/suspicious/noUnknownAtRules: @tailwind is a valid Tailwind CSS directive */\\n*, ::before, ::after {\\n --tw-border-spacing-x: 0;\\n --tw-border-spacing-y: 0;\\n --tw-translate-x: 0;\\n --tw-translate-y: 0;\\n --tw-rotate: 0;\\n --tw-skew-x: 0;\\n --tw-skew-y: 0;\\n --tw-scale-x: 1;\\n --tw-scale-y: 1;\\n --tw-pan-x: ;\\n --tw-pan-y: ;\\n --tw-pinch-zoom: ;\\n --tw-scroll-snap-strictness: proximity;\\n --tw-gradient-from-position: ;\\n --tw-gradient-via-position: ;\\n --tw-gradient-to-position: ;\\n --tw-ordinal: ;\\n --tw-slashed-zero: ;\\n --tw-numeric-figure: ;\\n --tw-numeric-spacing: ;\\n --tw-numeric-fraction: ;\\n --tw-ring-inset: ;\\n --tw-ring-offset-width: 0px;\\n --tw-ring-offset-color: #fff;\\n --tw-ring-color: rgb(59 130 246 / 0.5);\\n --tw-ring-offset-shadow: 0 0 #0000;\\n --tw-ring-shadow: 0 0 #0000;\\n --tw-shadow: 0 0 #0000;\\n --tw-shadow-colored: 0 0 #0000;\\n --tw-blur: ;\\n --tw-brightness: ;\\n --tw-contrast: ;\\n --tw-grayscale: ;\\n --tw-hue-rotate: ;\\n --tw-invert: ;\\n --tw-saturate: ;\\n --tw-sepia: ;\\n --tw-drop-shadow: ;\\n --tw-backdrop-blur: ;\\n --tw-backdrop-brightness: ;\\n --tw-backdrop-contrast: ;\\n --tw-backdrop-grayscale: ;\\n --tw-backdrop-hue-rotate: ;\\n --tw-backdrop-invert: ;\\n --tw-backdrop-opacity: ;\\n --tw-backdrop-saturate: ;\\n --tw-backdrop-sepia: ;\\n --tw-contain-size: ;\\n --tw-contain-layout: ;\\n --tw-contain-paint: ;\\n --tw-contain-style: ;\\n}\\n::backdrop {\\n --tw-border-spacing-x: 0;\\n --tw-border-spacing-y: 0;\\n --tw-translate-x: 0;\\n --tw-translate-y: 0;\\n --tw-rotate: 0;\\n --tw-skew-x: 0;\\n --tw-skew-y: 0;\\n --tw-scale-x: 1;\\n --tw-scale-y: 1;\\n --tw-pan-x: ;\\n --tw-pan-y: ;\\n --tw-pinch-zoom: ;\\n --tw-scroll-snap-strictness: proximity;\\n --tw-gradient-from-position: ;\\n --tw-gradient-via-position: ;\\n --tw-gradient-to-position: ;\\n --tw-ordinal: ;\\n --tw-slashed-zero: ;\\n --tw-numeric-figure: ;\\n --tw-numeric-spacing: ;\\n --tw-numeric-fraction: ;\\n --tw-ring-inset: ;\\n --tw-ring-offset-width: 0px;\\n --tw-ring-offset-color: #fff;\\n --tw-ring-color: rgb(59 130 246 / 0.5);\\n --tw-ring-offset-shadow: 0 0 #0000;\\n --tw-ring-shadow: 0 0 #0000;\\n --tw-shadow: 0 0 #0000;\\n --tw-shadow-colored: 0 0 #0000;\\n --tw-blur: ;\\n --tw-brightness: ;\\n --tw-contrast: ;\\n --tw-grayscale: ;\\n --tw-hue-rotate: ;\\n --tw-invert: ;\\n --tw-saturate: ;\\n --tw-sepia: ;\\n --tw-drop-shadow: ;\\n --tw-backdrop-blur: ;\\n --tw-backdrop-brightness: ;\\n --tw-backdrop-contrast: ;\\n --tw-backdrop-grayscale: ;\\n --tw-backdrop-hue-rotate: ;\\n --tw-backdrop-invert: ;\\n --tw-backdrop-opacity: ;\\n --tw-backdrop-saturate: ;\\n --tw-backdrop-sepia: ;\\n --tw-contain-size: ;\\n --tw-contain-layout: ;\\n --tw-contain-paint: ;\\n --tw-contain-style: ;\\n}\\n/* ! tailwindcss v3.4.18 | MIT License | https://tailwindcss.com */\\n/*\\n1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4)\\n2. Allow adding a border to an element by just adding a border-width. (https://github.com/tailwindcss/tailwindcss/pull/116)\\n*/\\n*,\\n::before,\\n::after {\\n box-sizing: border-box; /* 1 */\\n border-width: 0; /* 2 */\\n border-style: solid; /* 2 */\\n border-color: #e5e7eb; /* 2 */\\n}\\n::before,\\n::after {\\n --tw-content: '';\\n}\\n/*\\n1. Use a consistent sensible line-height in all browsers.\\n2. Prevent adjustments of font size after orientation changes in iOS.\\n3. Use a more readable tab size.\\n4. Use the user's configured `sans` font-family by default.\\n5. Use the user's configured `sans` font-feature-settings by default.\\n6. Use the user's configured `sans` font-variation-settings by default.\\n7. Disable tap highlights on iOS\\n*/\\nhtml,\\n:host {\\n line-height: 1.5; /* 1 */\\n -webkit-text-size-adjust: 100%; /* 2 */\\n -moz-tab-size: 4; /* 3 */\\n -o-tab-size: 4;\\n tab-size: 4; /* 3 */\\n font-family: ui-sans-serif, system-ui, sans-serif, \\\"Apple Color Emoji\\\", \\\"Segoe UI Emoji\\\", \\\"Segoe UI Symbol\\\", \\\"Noto Color Emoji\\\"; /* 4 */\\n font-feature-settings: normal; /* 5 */\\n font-variation-settings: normal; /* 6 */\\n -webkit-tap-highlight-color: transparent; /* 7 */\\n}\\n/*\\n1. Remove the margin in all browsers.\\n2. Inherit line-height from `html` so users can set them as a class directly on the `html` element.\\n*/\\nbody {\\n margin: 0; /* 1 */\\n line-height: inherit; /* 2 */\\n}\\n/*\\n1. Add the correct height in Firefox.\\n2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655)\\n3. Ensure horizontal rules are visible by default.\\n*/\\nhr {\\n height: 0; /* 1 */\\n color: inherit; /* 2 */\\n border-top-width: 1px; /* 3 */\\n}\\n/*\\nAdd the correct text decoration in Chrome, Edge, and Safari.\\n*/\\nabbr:where([title]) {\\n -webkit-text-decoration: underline dotted;\\n text-decoration: underline dotted;\\n}\\n/*\\nRemove the default font size and weight for headings.\\n*/\\nh1,\\nh2,\\nh3,\\nh4,\\nh5,\\nh6 {\\n font-size: inherit;\\n font-weight: inherit;\\n}\\n/*\\nReset links to optimize for opt-in styling instead of opt-out.\\n*/\\na {\\n color: inherit;\\n text-decoration: inherit;\\n}\\n/*\\nAdd the correct font weight in Edge and Safari.\\n*/\\nb,\\nstrong {\\n font-weight: bolder;\\n}\\n/*\\n1. Use the user's configured `mono` font-family by default.\\n2. Use the user's configured `mono` font-feature-settings by default.\\n3. Use the user's configured `mono` font-variation-settings by default.\\n4. Correct the odd `em` font sizing in all browsers.\\n*/\\ncode,\\nkbd,\\nsamp,\\npre {\\n font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \\\"Liberation Mono\\\", \\\"Courier New\\\", monospace; /* 1 */\\n font-feature-settings: normal; /* 2 */\\n font-variation-settings: normal; /* 3 */\\n font-size: 1em; /* 4 */\\n}\\n/*\\nAdd the correct font size in all browsers.\\n*/\\nsmall {\\n font-size: 80%;\\n}\\n/*\\nPrevent `sub` and `sup` elements from affecting the line height in all browsers.\\n*/\\nsub,\\nsup {\\n font-size: 75%;\\n line-height: 0;\\n position: relative;\\n vertical-align: baseline;\\n}\\nsub {\\n bottom: -0.25em;\\n}\\nsup {\\n top: -0.5em;\\n}\\n/*\\n1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297)\\n2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016)\\n3. Remove gaps between table borders by default.\\n*/\\ntable {\\n text-indent: 0; /* 1 */\\n border-color: inherit; /* 2 */\\n border-collapse: collapse; /* 3 */\\n}\\n/*\\n1. Change the font styles in all browsers.\\n2. Remove the margin in Firefox and Safari.\\n3. Remove default padding in all browsers.\\n*/\\nbutton,\\ninput,\\noptgroup,\\nselect,\\ntextarea {\\n font-family: inherit; /* 1 */\\n font-feature-settings: inherit; /* 1 */\\n font-variation-settings: inherit; /* 1 */\\n font-size: 100%; /* 1 */\\n font-weight: inherit; /* 1 */\\n line-height: inherit; /* 1 */\\n letter-spacing: inherit; /* 1 */\\n color: inherit; /* 1 */\\n margin: 0; /* 2 */\\n padding: 0; /* 3 */\\n}\\n/*\\nRemove the inheritance of text transform in Edge and Firefox.\\n*/\\nbutton,\\nselect {\\n text-transform: none;\\n}\\n/*\\n1. Correct the inability to style clickable types in iOS and Safari.\\n2. Remove default button styles.\\n*/\\nbutton,\\ninput:where([type='button']),\\ninput:where([type='reset']),\\ninput:where([type='submit']) {\\n -webkit-appearance: button; /* 1 */\\n background-color: transparent; /* 2 */\\n background-image: none; /* 2 */\\n}\\n/*\\nUse the modern Firefox focus style for all focusable elements.\\n*/\\n:-moz-focusring {\\n outline: auto;\\n}\\n/*\\nRemove the additional `:invalid` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737)\\n*/\\n:-moz-ui-invalid {\\n box-shadow: none;\\n}\\n/*\\nAdd the correct vertical alignment in Chrome and Firefox.\\n*/\\nprogress {\\n vertical-align: baseline;\\n}\\n/*\\nCorrect the cursor style of increment and decrement buttons in Safari.\\n*/\\n::-webkit-inner-spin-button,\\n::-webkit-outer-spin-button {\\n height: auto;\\n}\\n/*\\n1. Correct the odd appearance in Chrome and Safari.\\n2. Correct the outline style in Safari.\\n*/\\n[type='search'] {\\n -webkit-appearance: textfield; /* 1 */\\n outline-offset: -2px; /* 2 */\\n}\\n/*\\nRemove the inner padding in Chrome and Safari on macOS.\\n*/\\n::-webkit-search-decoration {\\n -webkit-appearance: none;\\n}\\n/*\\n1. Correct the inability to style clickable types in iOS and Safari.\\n2. Change font properties to `inherit` in Safari.\\n*/\\n::-webkit-file-upload-button {\\n -webkit-appearance: button; /* 1 */\\n font: inherit; /* 2 */\\n}\\n/*\\nAdd the correct display in Chrome and Safari.\\n*/\\nsummary {\\n display: list-item;\\n}\\n/*\\nRemoves the default spacing and border for appropriate elements.\\n*/\\nblockquote,\\ndl,\\ndd,\\nh1,\\nh2,\\nh3,\\nh4,\\nh5,\\nh6,\\nhr,\\nfigure,\\np,\\npre {\\n margin: 0;\\n}\\nfieldset {\\n margin: 0;\\n padding: 0;\\n}\\nlegend {\\n padding: 0;\\n}\\nol,\\nul,\\nmenu {\\n list-style: none;\\n margin: 0;\\n padding: 0;\\n}\\n/*\\nReset default styling for dialogs.\\n*/\\ndialog {\\n padding: 0;\\n}\\n/*\\nPrevent resizing textareas horizontally by default.\\n*/\\ntextarea {\\n resize: vertical;\\n}\\n/*\\n1. Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300)\\n2. Set the default placeholder color to the user's configured gray 400 color.\\n*/\\ninput::-moz-placeholder, textarea::-moz-placeholder {\\n opacity: 1; /* 1 */\\n color: #9ca3af; /* 2 */\\n}\\ninput::placeholder,\\ntextarea::placeholder {\\n opacity: 1; /* 1 */\\n color: #9ca3af; /* 2 */\\n}\\n/*\\nSet the default cursor for buttons.\\n*/\\nbutton,\\n[role=\\\"button\\\"] {\\n cursor: pointer;\\n}\\n/*\\nMake sure disabled buttons don't get the pointer cursor.\\n*/\\n:disabled {\\n cursor: default;\\n}\\n/*\\n1. Make replaced elements `display: block` by default. (https://github.com/mozdevs/cssremedy/issues/14)\\n2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210)\\n This can trigger a poorly considered lint error in some tools but is included by design.\\n*/\\nimg,\\nsvg,\\nvideo,\\ncanvas,\\naudio,\\niframe,\\nembed,\\nobject {\\n display: block; /* 1 */\\n vertical-align: middle; /* 2 */\\n}\\n/*\\nConstrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14)\\n*/\\nimg,\\nvideo {\\n max-width: 100%;\\n height: auto;\\n}\\n/* Make elements with the HTML hidden attribute stay hidden by default */\\n[hidden]:where(:not([hidden=\\\"until-found\\\"])) {\\n display: none;\\n}\\n/* biome-ignore lint/suspicious/noUnknownAtRules: @tailwind is a valid Tailwind CSS directive */\\n.\\\\!container {\\n width: 100% !important;\\n}\\n.container {\\n width: 100%;\\n}\\n@media (min-width: 640px) {\\n .\\\\!container {\\n max-width: 640px !important;\\n }\\n .container {\\n max-width: 640px;\\n }\\n}\\n@media (min-width: 768px) {\\n .\\\\!container {\\n max-width: 768px !important;\\n }\\n .container {\\n max-width: 768px;\\n }\\n}\\n@media (min-width: 1024px) {\\n .\\\\!container {\\n max-width: 1024px !important;\\n }\\n .container {\\n max-width: 1024px;\\n }\\n}\\n@media (min-width: 1280px) {\\n .\\\\!container {\\n max-width: 1280px !important;\\n }\\n .container {\\n max-width: 1280px;\\n }\\n}\\n@media (min-width: 1536px) {\\n .\\\\!container {\\n max-width: 1536px !important;\\n }\\n .container {\\n max-width: 1536px;\\n }\\n}\\n/* biome-ignore lint/suspicious/noUnknownAtRules: @tailwind is a valid Tailwind CSS directive */\\n.visible {\\n visibility: visible;\\n}\\n.invisible {\\n visibility: hidden;\\n}\\n.collapse {\\n visibility: collapse;\\n}\\n.static {\\n position: static;\\n}\\n.fixed {\\n position: fixed;\\n}\\n.absolute {\\n position: absolute;\\n}\\n.relative {\\n position: relative;\\n}\\n.sticky {\\n position: sticky;\\n}\\n.inset-0 {\\n inset: 0px;\\n}\\n.left-0 {\\n left: 0px;\\n}\\n.right-0 {\\n right: 0px;\\n}\\n.top-0 {\\n top: 0px;\\n}\\n.top-8 {\\n top: 2rem;\\n}\\n.isolate {\\n isolation: isolate;\\n}\\n.z-\\\\[5\\\\] {\\n z-index: 5;\\n}\\n.mb-0 {\\n margin-bottom: 0px;\\n}\\n.mb-\\\\[1px\\\\] {\\n margin-bottom: 1px;\\n}\\n.block {\\n display: block;\\n}\\n.inline-block {\\n display: inline-block;\\n}\\n.inline {\\n display: inline;\\n}\\n.flex {\\n display: flex;\\n}\\n.inline-flex {\\n display: inline-flex;\\n}\\n.table {\\n display: table;\\n}\\n.grid {\\n display: grid;\\n}\\n.inline-grid {\\n display: inline-grid;\\n}\\n.contents {\\n display: contents;\\n}\\n.hidden {\\n display: none;\\n}\\n.size-full {\\n width: 100%;\\n height: 100%;\\n}\\n.h-\\\\[1\\\\.1rem\\\\] {\\n height: 1.1rem;\\n}\\n.h-\\\\[1080px\\\\] {\\n height: 1080px;\\n}\\n.h-\\\\[500px\\\\] {\\n height: 500px;\\n}\\n.h-\\\\[5px\\\\] {\\n height: 5px;\\n}\\n.h-\\\\[calc\\\\(50vh-4rem\\\\)\\\\] {\\n height: calc(50vh - 4rem);\\n}\\n.h-full {\\n height: 100%;\\n}\\n.w-1 {\\n width: 0.25rem;\\n}\\n.w-\\\\[1000px\\\\] {\\n width: 1000px;\\n}\\n.w-\\\\[1920px\\\\] {\\n width: 1920px;\\n}\\n.w-\\\\[420px\\\\] {\\n width: 420px;\\n}\\n.w-full {\\n width: 100%;\\n}\\n.flex-1 {\\n flex: 1 1 0%;\\n}\\n.flex-shrink {\\n flex-shrink: 1;\\n}\\n.shrink {\\n flex-shrink: 1;\\n}\\n.\\\\!transform {\\n transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)) !important;\\n}\\n.transform {\\n transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));\\n}\\n.resize {\\n resize: both;\\n}\\n.flex-wrap {\\n flex-wrap: wrap;\\n}\\n.items-center {\\n align-items: center;\\n}\\n.overflow-hidden {\\n overflow: hidden;\\n}\\n.overflow-visible {\\n overflow: visible;\\n}\\n.whitespace-nowrap {\\n white-space: nowrap;\\n}\\n.text-nowrap {\\n text-wrap: nowrap;\\n}\\n.rounded {\\n border-radius: 0.25rem;\\n}\\n.border {\\n border-width: 1px;\\n}\\n.bg-slate-500 {\\n --tw-bg-opacity: 1;\\n background-color: rgb(100 116 139 / var(--tw-bg-opacity, 1));\\n}\\n.object-cover {\\n -o-object-fit: cover;\\n object-fit: cover;\\n}\\n.px-0\\\\.5 {\\n padding-left: 0.125rem;\\n padding-right: 0.125rem;\\n}\\n.text-center {\\n text-align: center;\\n}\\n.text-3xl {\\n font-size: 1.875rem;\\n line-height: 2.25rem;\\n}\\n.text-\\\\[8px\\\\] {\\n font-size: 8px;\\n}\\n.text-sm {\\n font-size: 0.875rem;\\n line-height: 1.25rem;\\n}\\n.text-xs {\\n font-size: 0.75rem;\\n line-height: 1rem;\\n}\\n.font-bold {\\n font-weight: 700;\\n}\\n.uppercase {\\n text-transform: uppercase;\\n}\\n.capitalize {\\n text-transform: capitalize;\\n}\\n.italic {\\n font-style: italic;\\n}\\n.text-white {\\n --tw-text-opacity: 1;\\n color: rgb(255 255 255 / var(--tw-text-opacity, 1));\\n}\\n.opacity-50 {\\n opacity: 0.5;\\n}\\n.shadow {\\n --tw-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);\\n --tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color);\\n box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);\\n}\\n.outline {\\n outline-style: solid;\\n}\\n.blur {\\n --tw-blur: blur(8px);\\n filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);\\n}\\n.grayscale {\\n --tw-grayscale: grayscale(100%);\\n filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);\\n}\\n.filter {\\n filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);\\n}\\n.backdrop-filter {\\n backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);\\n}\\n.transition {\\n transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter;\\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\\n transition-duration: 150ms;\\n}\\n.ease-in {\\n transition-timing-function: cubic-bezier(0.4, 0, 1, 1);\\n}\\n.ease-in-out {\\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\\n}\\n.ease-out {\\n transition-timing-function: cubic-bezier(0, 0, 0.2, 1);\\n}\\n\""],"mappings":";AAAA,sBAAe"}