@editframe/elements 0.15.0-beta.18 → 0.15.0-beta.20

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 (37) hide show
  1. package/dist/EF_FRAMEGEN.js +1 -0
  2. package/dist/elements/EFMedia.d.ts +7 -3
  3. package/dist/elements/EFMedia.js +42 -90
  4. package/dist/elements/EFTemporal.browsertest.d.ts +4 -3
  5. package/dist/elements/EFTemporal.d.ts +14 -11
  6. package/dist/elements/EFTemporal.js +63 -87
  7. package/dist/elements/EFTimegroup.d.ts +1 -1
  8. package/dist/elements/EFTimegroup.js +12 -107
  9. package/dist/elements/EFVideo.js +3 -1
  10. package/dist/elements/durationConverter.d.ts +8 -8
  11. package/dist/elements/durationConverter.js +2 -2
  12. package/dist/elements/updateAnimations.d.ts +9 -0
  13. package/dist/elements/updateAnimations.js +62 -0
  14. package/dist/getRenderInfo.d.ts +2 -2
  15. package/dist/gui/EFFilmstrip.js +7 -16
  16. package/dist/gui/EFFitScale.d.ts +27 -0
  17. package/dist/gui/EFFitScale.js +137 -0
  18. package/dist/gui/EFWorkbench.d.ts +1 -5
  19. package/dist/gui/EFWorkbench.js +7 -54
  20. package/dist/gui/TWMixin.css.js +1 -1
  21. package/dist/index.d.ts +1 -0
  22. package/dist/index.js +2 -0
  23. package/dist/style.css +0 -6
  24. package/package.json +2 -2
  25. package/src/elements/EFMedia.browsertest.ts +10 -10
  26. package/src/elements/EFMedia.ts +48 -118
  27. package/src/elements/EFTemporal.browsertest.ts +64 -31
  28. package/src/elements/EFTemporal.ts +99 -119
  29. package/src/elements/EFTimegroup.ts +12 -131
  30. package/src/elements/EFVideo.ts +3 -1
  31. package/src/elements/durationConverter.ts +9 -4
  32. package/src/elements/updateAnimations.ts +88 -0
  33. package/src/gui/ContextMixin.ts +0 -3
  34. package/src/gui/EFFilmstrip.ts +7 -16
  35. package/src/gui/EFFitScale.ts +151 -0
  36. package/src/gui/EFWorkbench.ts +13 -63
  37. package/types.json +1 -1
@@ -1,4 +1,4 @@
1
- const twStyle = "/*\n! tailwindcss v3.4.4 | 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*,\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\n::before,\n::after {\n --tw-content: '';\n}\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*/\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\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*/\n\nbody {\n margin: 0; /* 1 */\n line-height: inherit; /* 2 */\n}\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*/\n\nhr {\n height: 0; /* 1 */\n color: inherit; /* 2 */\n border-top-width: 1px; /* 3 */\n}\n\n/*\nAdd the correct text decoration in Chrome, Edge, and Safari.\n*/\n\nabbr:where([title]) {\n -webkit-text-decoration: underline dotted;\n text-decoration: underline dotted;\n}\n\n/*\nRemove the default font size and weight for headings.\n*/\n\nh1,\nh2,\nh3,\nh4,\nh5,\nh6 {\n font-size: inherit;\n font-weight: inherit;\n}\n\n/*\nReset links to optimize for opt-in styling instead of opt-out.\n*/\n\na {\n color: inherit;\n text-decoration: inherit;\n}\n\n/*\nAdd the correct font weight in Edge and Safari.\n*/\n\nb,\nstrong {\n font-weight: bolder;\n}\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*/\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\n/*\nAdd the correct font size in all browsers.\n*/\n\nsmall {\n font-size: 80%;\n}\n\n/*\nPrevent `sub` and `sup` elements from affecting the line height in all browsers.\n*/\n\nsub,\nsup {\n font-size: 75%;\n line-height: 0;\n position: relative;\n vertical-align: baseline;\n}\n\nsub {\n bottom: -0.25em;\n}\n\nsup {\n top: -0.5em;\n}\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*/\n\ntable {\n text-indent: 0; /* 1 */\n border-color: inherit; /* 2 */\n border-collapse: collapse; /* 3 */\n}\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*/\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\n/*\nRemove the inheritance of text transform in Edge and Firefox.\n*/\n\nbutton,\nselect {\n text-transform: none;\n}\n\n/*\n1. Correct the inability to style clickable types in iOS and Safari.\n2. Remove default button styles.\n*/\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\n/*\nUse the modern Firefox focus style for all focusable elements.\n*/\n\n:-moz-focusring {\n outline: auto;\n}\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\n:-moz-ui-invalid {\n box-shadow: none;\n}\n\n/*\nAdd the correct vertical alignment in Chrome and Firefox.\n*/\n\nprogress {\n vertical-align: baseline;\n}\n\n/*\nCorrect the cursor style of increment and decrement buttons in Safari.\n*/\n\n::-webkit-inner-spin-button,\n::-webkit-outer-spin-button {\n height: auto;\n}\n\n/*\n1. Correct the odd appearance in Chrome and Safari.\n2. Correct the outline style in Safari.\n*/\n\n[type='search'] {\n -webkit-appearance: textfield; /* 1 */\n outline-offset: -2px; /* 2 */\n}\n\n/*\nRemove the inner padding in Chrome and Safari on macOS.\n*/\n\n::-webkit-search-decoration {\n -webkit-appearance: none;\n}\n\n/*\n1. Correct the inability to style clickable types in iOS and Safari.\n2. Change font properties to `inherit` in Safari.\n*/\n\n::-webkit-file-upload-button {\n -webkit-appearance: button; /* 1 */\n font: inherit; /* 2 */\n}\n\n/*\nAdd the correct display in Chrome and Safari.\n*/\n\nsummary {\n display: list-item;\n}\n\n/*\nRemoves the default spacing and border for appropriate elements.\n*/\n\nblockquote,\ndl,\ndd,\nh1,\nh2,\nh3,\nh4,\nh5,\nh6,\nhr,\nfigure,\np,\npre {\n margin: 0;\n}\n\nfieldset {\n margin: 0;\n padding: 0;\n}\n\nlegend {\n padding: 0;\n}\n\nol,\nul,\nmenu {\n list-style: none;\n margin: 0;\n padding: 0;\n}\n\n/*\nReset default styling for dialogs.\n*/\ndialog {\n padding: 0;\n}\n\n/*\nPrevent resizing textareas horizontally by default.\n*/\n\ntextarea {\n resize: vertical;\n}\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*/\n\ninput::-moz-placeholder, textarea::-moz-placeholder {\n opacity: 1; /* 1 */\n color: #9ca3af; /* 2 */\n}\n\ninput::placeholder,\ntextarea::placeholder {\n opacity: 1; /* 1 */\n color: #9ca3af; /* 2 */\n}\n\n/*\nSet the default cursor for buttons.\n*/\n\nbutton,\n[role=\"button\"] {\n cursor: pointer;\n}\n\n/*\nMake sure disabled buttons don't get the pointer cursor.\n*/\n:disabled {\n cursor: default;\n}\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*/\n\nimg,\nsvg,\nvideo,\ncanvas,\naudio,\niframe,\nembed,\nobject {\n display: block; /* 1 */\n vertical-align: middle; /* 2 */\n}\n\n/*\nConstrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14)\n*/\n\nimg,\nvideo {\n max-width: 100%;\n height: auto;\n}\n\n/* Make elements with the HTML hidden attribute stay hidden by default */\n[hidden] {\n display: none;\n}\n\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\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.container {\n width: 100%;\n}\n@media (min-width: 640px) {\n\n .container {\n max-width: 640px;\n }\n}\n@media (min-width: 768px) {\n\n .container {\n max-width: 768px;\n }\n}\n@media (min-width: 1024px) {\n\n .container {\n max-width: 1024px;\n }\n}\n@media (min-width: 1280px) {\n\n .container {\n max-width: 1280px;\n }\n}\n@media (min-width: 1536px) {\n\n .container {\n max-width: 1536px;\n }\n}\n.pointer-events-none {\n pointer-events: none;\n}\n.visible {\n visibility: visible;\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.inset-0 {\n inset: 0px;\n}\n.top-0 {\n top: 0px;\n}\n.z-10 {\n z-index: 10;\n}\n.z-20 {\n z-index: 20;\n}\n.col-span-2 {\n grid-column: span 2 / span 2;\n}\n.mx-2 {\n margin-left: 0.5rem;\n margin-right: 0.5rem;\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.grid {\n display: grid;\n}\n.contents {\n display: contents;\n}\n.hidden {\n display: none;\n}\n.h-\\[1\\.1rem\\] {\n height: 1.1rem;\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-\\[2px\\] {\n width: 2px;\n}\n.w-full {\n width: 100%;\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-crosshair {\n cursor: crosshair;\n}\n.resize {\n resize: both;\n}\n.flex-wrap {\n flex-wrap: wrap;\n}\n.place-content-center {\n place-content: center;\n}\n.place-items-center {\n place-items: center;\n}\n.items-center {\n align-items: center;\n}\n.justify-center {\n justify-content: center;\n}\n.overflow-auto {\n overflow: auto;\n}\n.overflow-hidden {\n overflow: hidden;\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.border-r-2 {\n border-right-width: 2px;\n}\n.border-blue-500 {\n --tw-border-opacity: 1;\n border-color: rgb(59 130 246 / var(--tw-border-opacity));\n}\n.border-red-700 {\n --tw-border-opacity: 1;\n border-color: rgb(185 28 28 / var(--tw-border-opacity));\n}\n.border-slate-500 {\n --tw-border-opacity: 1;\n border-color: rgb(100 116 139 / var(--tw-border-opacity));\n}\n.border-b-slate-600 {\n --tw-border-opacity: 1;\n border-bottom-color: rgb(71 85 105 / var(--tw-border-opacity));\n}\n.bg-blue-200 {\n --tw-bg-opacity: 1;\n background-color: rgb(191 219 254 / var(--tw-bg-opacity));\n}\n.bg-blue-500 {\n --tw-bg-opacity: 1;\n background-color: rgb(59 130 246 / var(--tw-bg-opacity));\n}\n.bg-red-500 {\n --tw-bg-opacity: 1;\n background-color: rgb(239 68 68 / var(--tw-bg-opacity));\n}\n.bg-slate-100 {\n --tw-bg-opacity: 1;\n background-color: rgb(241 245 249 / var(--tw-bg-opacity));\n}\n.bg-slate-200 {\n --tw-bg-opacity: 1;\n background-color: rgb(226 232 240 / var(--tw-bg-opacity));\n}\n.bg-slate-300 {\n --tw-bg-opacity: 1;\n background-color: rgb(203 213 225 / var(--tw-bg-opacity));\n}\n.bg-slate-800 {\n --tw-bg-opacity: 1;\n background-color: rgb(30 41 59 / var(--tw-bg-opacity));\n}\n.bg-opacity-20 {\n --tw-bg-opacity: 0.2;\n}\n.p-\\[1px\\] {\n padding: 1px;\n}\n.pb-0 {\n padding-bottom: 0px;\n}\n.pl-1 {\n padding-left: 0.25rem;\n}\n.pl-2 {\n padding-left: 0.5rem;\n}\n.pr-0 {\n padding-right: 0px;\n}\n.pr-1 {\n padding-right: 0.25rem;\n}\n.pt-\\[8px\\] {\n padding-top: 8px;\n}\n.font-mono {\n font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace;\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.line-through {\n text-decoration-line: line-through;\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.shadow-slate-300 {\n --tw-shadow-color: #cbd5e1;\n --tw-shadow: var(--tw-shadow-colored);\n}\n.shadow-slate-600 {\n --tw-shadow-color: #475569;\n --tw-shadow: var(--tw-shadow-colored);\n}\n.outline {\n outline-style: solid;\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.hover\\:bg-slate-400:hover {\n --tw-bg-opacity: 1;\n background-color: rgb(148 163 184 / var(--tw-bg-opacity));\n}\n.peer:hover ~ .peer-hover\\:border-slate-400 {\n --tw-border-opacity: 1;\n border-color: rgb(148 163 184 / var(--tw-border-opacity));\n}\n.peer:hover ~ .peer-hover\\:bg-slate-300 {\n --tw-bg-opacity: 1;\n background-color: rgb(203 213 225 / var(--tw-bg-opacity));\n}\n.data-\\[focused\\]\\:bg-slate-400[data-focused] {\n --tw-bg-opacity: 1;\n background-color: rgb(148 163 184 / var(--tw-bg-opacity));\n}\n.peer[data-focused] ~ .peer-data-\\[focused\\]\\:border-slate-400 {\n --tw-border-opacity: 1;\n border-color: rgb(148 163 184 / var(--tw-border-opacity));\n}\n.peer[data-focused] ~ .peer-data-\\[focused\\]\\:bg-slate-300 {\n --tw-bg-opacity: 1;\n background-color: rgb(203 213 225 / var(--tw-bg-opacity));\n}\n";
1
+ const twStyle = "/*\n! tailwindcss v3.4.4 | 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*,\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\n::before,\n::after {\n --tw-content: '';\n}\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*/\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\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*/\n\nbody {\n margin: 0; /* 1 */\n line-height: inherit; /* 2 */\n}\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*/\n\nhr {\n height: 0; /* 1 */\n color: inherit; /* 2 */\n border-top-width: 1px; /* 3 */\n}\n\n/*\nAdd the correct text decoration in Chrome, Edge, and Safari.\n*/\n\nabbr:where([title]) {\n -webkit-text-decoration: underline dotted;\n text-decoration: underline dotted;\n}\n\n/*\nRemove the default font size and weight for headings.\n*/\n\nh1,\nh2,\nh3,\nh4,\nh5,\nh6 {\n font-size: inherit;\n font-weight: inherit;\n}\n\n/*\nReset links to optimize for opt-in styling instead of opt-out.\n*/\n\na {\n color: inherit;\n text-decoration: inherit;\n}\n\n/*\nAdd the correct font weight in Edge and Safari.\n*/\n\nb,\nstrong {\n font-weight: bolder;\n}\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*/\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\n/*\nAdd the correct font size in all browsers.\n*/\n\nsmall {\n font-size: 80%;\n}\n\n/*\nPrevent `sub` and `sup` elements from affecting the line height in all browsers.\n*/\n\nsub,\nsup {\n font-size: 75%;\n line-height: 0;\n position: relative;\n vertical-align: baseline;\n}\n\nsub {\n bottom: -0.25em;\n}\n\nsup {\n top: -0.5em;\n}\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*/\n\ntable {\n text-indent: 0; /* 1 */\n border-color: inherit; /* 2 */\n border-collapse: collapse; /* 3 */\n}\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*/\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\n/*\nRemove the inheritance of text transform in Edge and Firefox.\n*/\n\nbutton,\nselect {\n text-transform: none;\n}\n\n/*\n1. Correct the inability to style clickable types in iOS and Safari.\n2. Remove default button styles.\n*/\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\n/*\nUse the modern Firefox focus style for all focusable elements.\n*/\n\n:-moz-focusring {\n outline: auto;\n}\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\n:-moz-ui-invalid {\n box-shadow: none;\n}\n\n/*\nAdd the correct vertical alignment in Chrome and Firefox.\n*/\n\nprogress {\n vertical-align: baseline;\n}\n\n/*\nCorrect the cursor style of increment and decrement buttons in Safari.\n*/\n\n::-webkit-inner-spin-button,\n::-webkit-outer-spin-button {\n height: auto;\n}\n\n/*\n1. Correct the odd appearance in Chrome and Safari.\n2. Correct the outline style in Safari.\n*/\n\n[type='search'] {\n -webkit-appearance: textfield; /* 1 */\n outline-offset: -2px; /* 2 */\n}\n\n/*\nRemove the inner padding in Chrome and Safari on macOS.\n*/\n\n::-webkit-search-decoration {\n -webkit-appearance: none;\n}\n\n/*\n1. Correct the inability to style clickable types in iOS and Safari.\n2. Change font properties to `inherit` in Safari.\n*/\n\n::-webkit-file-upload-button {\n -webkit-appearance: button; /* 1 */\n font: inherit; /* 2 */\n}\n\n/*\nAdd the correct display in Chrome and Safari.\n*/\n\nsummary {\n display: list-item;\n}\n\n/*\nRemoves the default spacing and border for appropriate elements.\n*/\n\nblockquote,\ndl,\ndd,\nh1,\nh2,\nh3,\nh4,\nh5,\nh6,\nhr,\nfigure,\np,\npre {\n margin: 0;\n}\n\nfieldset {\n margin: 0;\n padding: 0;\n}\n\nlegend {\n padding: 0;\n}\n\nol,\nul,\nmenu {\n list-style: none;\n margin: 0;\n padding: 0;\n}\n\n/*\nReset default styling for dialogs.\n*/\ndialog {\n padding: 0;\n}\n\n/*\nPrevent resizing textareas horizontally by default.\n*/\n\ntextarea {\n resize: vertical;\n}\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*/\n\ninput::-moz-placeholder, textarea::-moz-placeholder {\n opacity: 1; /* 1 */\n color: #9ca3af; /* 2 */\n}\n\ninput::placeholder,\ntextarea::placeholder {\n opacity: 1; /* 1 */\n color: #9ca3af; /* 2 */\n}\n\n/*\nSet the default cursor for buttons.\n*/\n\nbutton,\n[role=\"button\"] {\n cursor: pointer;\n}\n\n/*\nMake sure disabled buttons don't get the pointer cursor.\n*/\n:disabled {\n cursor: default;\n}\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*/\n\nimg,\nsvg,\nvideo,\ncanvas,\naudio,\niframe,\nembed,\nobject {\n display: block; /* 1 */\n vertical-align: middle; /* 2 */\n}\n\n/*\nConstrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14)\n*/\n\nimg,\nvideo {\n max-width: 100%;\n height: auto;\n}\n\n/* Make elements with the HTML hidden attribute stay hidden by default */\n[hidden] {\n display: none;\n}\n\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\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.container {\n width: 100%;\n}\n@media (min-width: 640px) {\n\n .container {\n max-width: 640px;\n }\n}\n@media (min-width: 768px) {\n\n .container {\n max-width: 768px;\n }\n}\n@media (min-width: 1024px) {\n\n .container {\n max-width: 1024px;\n }\n}\n@media (min-width: 1280px) {\n\n .container {\n max-width: 1280px;\n }\n}\n@media (min-width: 1536px) {\n\n .container {\n max-width: 1536px;\n }\n}\n.pointer-events-none {\n pointer-events: none;\n}\n.visible {\n visibility: visible;\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.inset-0 {\n inset: 0px;\n}\n.top-0 {\n top: 0px;\n}\n.z-10 {\n z-index: 10;\n}\n.z-20 {\n z-index: 20;\n}\n.col-span-2 {\n grid-column: span 2 / span 2;\n}\n.mx-2 {\n margin-left: 0.5rem;\n margin-right: 0.5rem;\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.grid {\n display: grid;\n}\n.contents {\n display: contents;\n}\n.hidden {\n display: none;\n}\n.h-\\[1\\.1rem\\] {\n height: 1.1rem;\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-\\[2px\\] {\n width: 2px;\n}\n.w-full {\n width: 100%;\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-crosshair {\n cursor: crosshair;\n}\n.resize {\n resize: both;\n}\n.flex-wrap {\n flex-wrap: wrap;\n}\n.place-content-center {\n place-content: center;\n}\n.items-center {\n align-items: center;\n}\n.overflow-auto {\n overflow: auto;\n}\n.overflow-hidden {\n overflow: hidden;\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.border-r-2 {\n border-right-width: 2px;\n}\n.border-blue-500 {\n --tw-border-opacity: 1;\n border-color: rgb(59 130 246 / var(--tw-border-opacity));\n}\n.border-red-700 {\n --tw-border-opacity: 1;\n border-color: rgb(185 28 28 / var(--tw-border-opacity));\n}\n.border-slate-500 {\n --tw-border-opacity: 1;\n border-color: rgb(100 116 139 / var(--tw-border-opacity));\n}\n.border-b-slate-600 {\n --tw-border-opacity: 1;\n border-bottom-color: rgb(71 85 105 / var(--tw-border-opacity));\n}\n.bg-blue-200 {\n --tw-bg-opacity: 1;\n background-color: rgb(191 219 254 / var(--tw-bg-opacity));\n}\n.bg-blue-500 {\n --tw-bg-opacity: 1;\n background-color: rgb(59 130 246 / var(--tw-bg-opacity));\n}\n.bg-red-500 {\n --tw-bg-opacity: 1;\n background-color: rgb(239 68 68 / var(--tw-bg-opacity));\n}\n.bg-slate-100 {\n --tw-bg-opacity: 1;\n background-color: rgb(241 245 249 / var(--tw-bg-opacity));\n}\n.bg-slate-200 {\n --tw-bg-opacity: 1;\n background-color: rgb(226 232 240 / var(--tw-bg-opacity));\n}\n.bg-slate-300 {\n --tw-bg-opacity: 1;\n background-color: rgb(203 213 225 / var(--tw-bg-opacity));\n}\n.bg-slate-800 {\n --tw-bg-opacity: 1;\n background-color: rgb(30 41 59 / var(--tw-bg-opacity));\n}\n.bg-opacity-20 {\n --tw-bg-opacity: 0.2;\n}\n.p-\\[1px\\] {\n padding: 1px;\n}\n.pb-0 {\n padding-bottom: 0px;\n}\n.pl-1 {\n padding-left: 0.25rem;\n}\n.pl-2 {\n padding-left: 0.5rem;\n}\n.pr-0 {\n padding-right: 0px;\n}\n.pr-1 {\n padding-right: 0.25rem;\n}\n.pt-\\[8px\\] {\n padding-top: 8px;\n}\n.font-mono {\n font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace;\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.line-through {\n text-decoration-line: line-through;\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.shadow-slate-300 {\n --tw-shadow-color: #cbd5e1;\n --tw-shadow: var(--tw-shadow-colored);\n}\n.shadow-slate-600 {\n --tw-shadow-color: #475569;\n --tw-shadow: var(--tw-shadow-colored);\n}\n.outline {\n outline-style: solid;\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.hover\\:bg-slate-400:hover {\n --tw-bg-opacity: 1;\n background-color: rgb(148 163 184 / var(--tw-bg-opacity));\n}\n.peer:hover ~ .peer-hover\\:border-slate-400 {\n --tw-border-opacity: 1;\n border-color: rgb(148 163 184 / var(--tw-border-opacity));\n}\n.peer:hover ~ .peer-hover\\:bg-slate-300 {\n --tw-bg-opacity: 1;\n background-color: rgb(203 213 225 / var(--tw-bg-opacity));\n}\n.data-\\[focused\\]\\:bg-slate-400[data-focused] {\n --tw-bg-opacity: 1;\n background-color: rgb(148 163 184 / var(--tw-bg-opacity));\n}\n.peer[data-focused] ~ .peer-data-\\[focused\\]\\:border-slate-400 {\n --tw-border-opacity: 1;\n border-color: rgb(148 163 184 / var(--tw-border-opacity));\n}\n.peer[data-focused] ~ .peer-data-\\[focused\\]\\:bg-slate-300 {\n --tw-bg-opacity: 1;\n background-color: rgb(203 213 225 / var(--tw-bg-opacity));\n}\n";
2
2
  export {
3
3
  twStyle as default
4
4
  };
package/dist/index.d.ts CHANGED
@@ -14,4 +14,5 @@ export { EFToggleLoop } from './gui/EFToggleLoop.js';
14
14
  export { EFScrubber } from './gui/EFScrubber.js';
15
15
  export { EFTimeDisplay } from './gui/EFTimeDisplay.js';
16
16
  export { EFFocusOverlay } from './gui/EFFocusOverlay.js';
17
+ export { EFFitScale } from './gui/EFFitScale.js';
17
18
  export { getRenderInfo, RenderInfo } from './getRenderInfo.js';
package/dist/index.js CHANGED
@@ -15,6 +15,7 @@ import { EFToggleLoop } from "./gui/EFToggleLoop.js";
15
15
  import { EFScrubber } from "./gui/EFScrubber.js";
16
16
  import { EFTimeDisplay } from "./gui/EFTimeDisplay.js";
17
17
  import { EFFocusOverlay } from "./gui/EFFocusOverlay.js";
18
+ import { EFFitScale } from "./gui/EFFitScale.js";
18
19
  import "./EF_FRAMEGEN.js";
19
20
  import { RenderInfo, getRenderInfo } from "./getRenderInfo.js";
20
21
  if (typeof window !== "undefined") {
@@ -29,6 +30,7 @@ export {
29
30
  EFCaptionsSegment,
30
31
  EFConfiguration,
31
32
  EFFilmstrip,
33
+ EFFitScale,
32
34
  EFFocusOverlay,
33
35
  EFImage,
34
36
  EFPreview,
package/dist/style.css CHANGED
@@ -628,15 +628,9 @@ video {
628
628
  .place-content-center {
629
629
  place-content: center;
630
630
  }
631
- .place-items-center {
632
- place-items: center;
633
- }
634
631
  .items-center {
635
632
  align-items: center;
636
633
  }
637
- .justify-center {
638
- justify-content: center;
639
- }
640
634
  .overflow-auto {
641
635
  overflow: auto;
642
636
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@editframe/elements",
3
- "version": "0.15.0-beta.18",
3
+ "version": "0.15.0-beta.20",
4
4
  "description": "",
5
5
  "exports": {
6
6
  ".": {
@@ -27,7 +27,7 @@
27
27
  "license": "UNLICENSED",
28
28
  "dependencies": {
29
29
  "@bramus/style-observer": "^1.3.0",
30
- "@editframe/assets": "0.15.0-beta.18",
30
+ "@editframe/assets": "0.15.0-beta.20",
31
31
  "@lit/context": "^1.1.2",
32
32
  "@lit/task": "^1.0.1",
33
33
  "d3": "^7.9.0",
@@ -148,7 +148,7 @@ describe("EFMedia", () => {
148
148
  timegroup.mode = "sequence";
149
149
  const element = document.createElement("test-media");
150
150
  element.src = "/assets/10s-bars.mp4";
151
- element.sourcein = "1s";
151
+ element.sourceInMs = 1_000;
152
152
 
153
153
  const preview = document.createElement("ef-preview");
154
154
  timegroup.appendChild(element);
@@ -180,7 +180,7 @@ describe("EFMedia", () => {
180
180
  timegroup.mode = "sequence";
181
181
  const element = document.createElement("test-media");
182
182
  element.src = "/assets/10s-bars.mp4";
183
- element.sourcein = "6s";
183
+ element.sourceInMs = 6_000;
184
184
 
185
185
  const preview = document.createElement("ef-preview");
186
186
  timegroup.appendChild(element);
@@ -212,7 +212,7 @@ describe("EFMedia", () => {
212
212
  timegroup.mode = "sequence";
213
213
  const element = document.createElement("test-media");
214
214
  element.src = "/assets/10s-bars.mp4";
215
- element.sourceout = "6s";
215
+ element.sourceOutMs = 6_000;
216
216
 
217
217
  const preview = document.createElement("ef-preview");
218
218
  timegroup.appendChild(element);
@@ -224,8 +224,8 @@ describe("EFMedia", () => {
224
224
 
225
225
  await element.trackFragmentIndexLoader.taskComplete;
226
226
 
227
- expect(element.durationMs).toBe(4_000);
228
- expect(timegroup.durationMs).toBe(4_000);
227
+ expect(element.durationMs).toBe(6_000);
228
+ expect(timegroup.durationMs).toBe(6_000);
229
229
  });
230
230
  test("Computes duration from track fragment index sourceout", async () => {
231
231
  // Mock the request for the track fragment index, responds with a 10 second duration
@@ -244,7 +244,7 @@ describe("EFMedia", () => {
244
244
  timegroup.mode = "sequence";
245
245
  const element = document.createElement("test-media");
246
246
  element.src = "/assets/10s-bars.mp4";
247
- element.sourceout = "5s";
247
+ element.sourceOutMs = 5_000;
248
248
 
249
249
  const preview = document.createElement("ef-preview");
250
250
  timegroup.appendChild(element);
@@ -276,8 +276,8 @@ describe("EFMedia", () => {
276
276
  timegroup.mode = "sequence";
277
277
  const element = document.createElement("test-media");
278
278
  element.src = "/assets/10s-bars.mp4";
279
- element.sourcein = "1s";
280
- element.sourceout = "5s";
279
+ element.sourceInMs = 1_000;
280
+ element.sourceOutMs = 5_000;
281
281
 
282
282
  const preview = document.createElement("ef-preview");
283
283
  timegroup.appendChild(element);
@@ -310,8 +310,8 @@ describe("EFMedia", () => {
310
310
  timegroup.mode = "sequence";
311
311
  const element = document.createElement("test-media");
312
312
  element.src = "/assets/10s-bars.mp4";
313
- element.sourcein = "9s";
314
- element.sourceout = "10s";
313
+ element.sourceInMs = 9_000;
314
+ element.sourceOutMs = 10_000;
315
315
 
316
316
  const preview = document.createElement("ef-preview");
317
317
  timegroup.appendChild(element);
@@ -12,9 +12,10 @@ import { MP4File } from "@editframe/assets/MP4File.js";
12
12
  import { EF_INTERACTIVE } from "../EF_INTERACTIVE.js";
13
13
  import { EF_RENDERING } from "../EF_RENDERING.js";
14
14
  import { EFSourceMixin } from "./EFSourceMixin.js";
15
- import { EFTemporal, isEFTemporal } from "./EFTemporal.js";
15
+ import { EFTemporal } from "./EFTemporal.js";
16
16
  import { FetchMixin } from "./FetchMixin.js";
17
17
  import { EFTargetable } from "./TargetController.ts";
18
+ import { updateAnimations } from "./updateAnimations.ts";
18
19
 
19
20
  const log = debug("ef:elements:EFMedia");
20
21
 
@@ -136,8 +137,11 @@ export class EFMedia extends EFTargetable(
136
137
  }
137
138
  },
138
139
  onComplete: () => {
140
+ this.requestUpdate("intrinsicDurationMs");
139
141
  this.requestUpdate("ownCurrentTimeMs");
142
+ console.log("Requesting update for durationMs", this, this.rootTimegroup);
140
143
  this.rootTimegroup?.requestUpdate("ownCurrentTimeMs");
144
+ this.rootTimegroup?.requestUpdate("durationMs");
141
145
  },
142
146
  });
143
147
 
@@ -347,75 +351,7 @@ export class EFMedia extends EFTargetable(
347
351
  changedProperties.has("currentTime") ||
348
352
  changedProperties.has("ownCurrentTimeMs")
349
353
  ) {
350
- const timelineTimeMs = (this.rootTimegroup ?? this).currentTimeMs;
351
- if (
352
- this.startTimeMs > timelineTimeMs ||
353
- this.endTimeMs < timelineTimeMs
354
- ) {
355
- this.style.display = "none";
356
- return;
357
- }
358
- this.style.display = "";
359
- const animations = this.getAnimations({ subtree: true });
360
-
361
- this.style.setProperty("--ef-duration", `${this.durationMs}ms`);
362
- this.style.setProperty(
363
- "--ef-transition-duration",
364
- `${this.parentTimegroup?.overlapMs ?? 0}ms`,
365
- );
366
- this.style.setProperty(
367
- "--ef-transition-out-start",
368
- `${this.durationMs - (this.parentTimegroup?.overlapMs ?? 0)}ms`,
369
- );
370
-
371
- for (const animation of animations) {
372
- if (animation.playState === "running") {
373
- animation.pause();
374
- }
375
- const effect = animation.effect;
376
- if (!(effect && effect instanceof KeyframeEffect)) {
377
- return;
378
- }
379
- const target = effect.target;
380
- // TODO: better generalize work avoidance for temporal elements
381
- if (!target) {
382
- return;
383
- }
384
- if (target.closest("ef-video, ef-audio") !== this) {
385
- return;
386
- }
387
-
388
- // Important to avoid going to the end of the animation
389
- // or it will reset awkwardly.
390
- if (isEFTemporal(target)) {
391
- const timing = effect.getTiming();
392
- const duration = Number(timing.duration) ?? 0;
393
- const delay = Number(timing.delay);
394
- const newTime = Math.floor(
395
- Math.min(target.ownCurrentTimeMs, duration - 1 + delay),
396
- );
397
- if (Number.isNaN(newTime)) {
398
- return;
399
- }
400
- animation.currentTime = newTime;
401
- } else if (target) {
402
- const nearestTimegroup = target.closest("ef-timegroup");
403
- if (!nearestTimegroup) {
404
- return;
405
- }
406
- const timing = effect.getTiming();
407
- const duration = Number(timing.duration) ?? 0;
408
- const delay = Number(timing.delay);
409
- const newTime = Math.floor(
410
- Math.min(nearestTimegroup.ownCurrentTimeMs, duration - 1 + delay),
411
- );
412
-
413
- if (Number.isNaN(newTime)) {
414
- return;
415
- }
416
- animation.currentTime = newTime;
417
- }
418
- }
354
+ updateAnimations(this);
419
355
  }
420
356
  }
421
357
 
@@ -423,7 +359,7 @@ export class EFMedia extends EFTargetable(
423
359
  return true;
424
360
  }
425
361
 
426
- get durationMs() {
362
+ get intrinsicDurationMs() {
427
363
  if (!this.trackFragmentIndexLoader.value) {
428
364
  return 0;
429
365
  }
@@ -436,39 +372,7 @@ export class EFMedia extends EFTargetable(
436
372
  if (durations.length === 0) {
437
373
  return 0;
438
374
  }
439
- if (
440
- this.sourceInMs &&
441
- this.sourceOutMs &&
442
- this.sourceOutMs > this.sourceInMs
443
- ) {
444
- return Math.max(this.sourceOutMs - this.sourceInMs);
445
- }
446
- if (this.sourceInMs) {
447
- return (
448
- Math.max(...durations) -
449
- this.trimStartMs -
450
- this.trimEndMs -
451
- this.sourceInMs
452
- );
453
- }
454
- if (this.sourceOutMs) {
455
- return (
456
- Math.max(...durations) -
457
- this.trimStartMs -
458
- this.trimEndMs -
459
- this.sourceOutMs
460
- );
461
- }
462
- if (this.sourceInMs && this.sourceOutMs) {
463
- return (
464
- Math.max(...durations) -
465
- this.trimStartMs -
466
- this.trimEndMs -
467
- this.sourceOutMs -
468
- this.sourceInMs
469
- );
470
- }
471
- return Math.max(...durations) - this.trimStartMs - this.trimEndMs;
375
+ return Math.max(...durations);
472
376
  }
473
377
 
474
378
  #audioContext = new OfflineAudioContext(2, 48000 / 30, 48000);
@@ -506,13 +410,15 @@ export class EFMedia extends EFTargetable(
506
410
  async fetchAudioSpanningTime(fromMs: number, toMs: number) {
507
411
  // Adjust range for track's own time
508
412
  if (this.sourceInMs) {
509
- fromMs -= this.startTimeMs - this.trimStartMs - this.sourceInMs;
413
+ fromMs -=
414
+ this.startTimeMs - (this.trimStartMs ?? 0) - (this.sourceInMs ?? 0);
510
415
  }
511
416
  if (this.sourceOutMs) {
512
- toMs -= this.startTimeMs - this.trimStartMs - this.sourceOutMs;
417
+ toMs -=
418
+ this.startTimeMs - (this.trimStartMs ?? 0) - (this.sourceOutMs ?? 0);
513
419
  }
514
- fromMs -= this.startTimeMs - this.trimStartMs;
515
- toMs -= this.startTimeMs - this.trimStartMs;
420
+ fromMs -= this.startTimeMs - (this.trimStartMs ?? 0);
421
+ toMs -= this.startTimeMs - (this.trimStartMs ?? 0);
516
422
 
517
423
  await this.trackFragmentIndexLoader.taskComplete;
518
424
  const audioTrackId = this.defaultAudioTrackId;
@@ -582,11 +488,11 @@ export class EFMedia extends EFTargetable(
582
488
  blob: audioBlob,
583
489
  startMs:
584
490
  (firstFragment.dts / audioTrackIndex.timescale) * 1000 -
585
- this.trimStartMs,
491
+ (this.trimStartMs ?? 0),
586
492
  endMs:
587
493
  (lastFragment.dts / audioTrackIndex.timescale) * 1000 +
588
494
  (lastFragment.duration / audioTrackIndex.timescale) * 1000 -
589
- this.trimEndMs,
495
+ (this.trimEndMs ?? 0),
590
496
  };
591
497
  }
592
498
 
@@ -610,6 +516,16 @@ export class EFMedia extends EFTargetable(
610
516
  return Number.parseInt(this.getAttribute("fft-decay") ?? "8", 10);
611
517
  }
612
518
 
519
+ set interpolateFrequencies(value: boolean) {
520
+ const oldValue = this.interpolateFrequencies;
521
+ this.setAttribute("interpolate-frequencies", String(value));
522
+ this.requestUpdate("interpolate-frequencies", oldValue);
523
+ }
524
+
525
+ get interpolateFrequencies() {
526
+ return this.getAttribute("interpolate-frequencies") !== "false";
527
+ }
528
+
613
529
  get shouldInterpolateFrequencies() {
614
530
  if (this.hasAttribute("interpolate-frequencies")) {
615
531
  return this.getAttribute("interpolate-frequencies") !== "false";
@@ -651,6 +567,8 @@ export class EFMedia extends EFTargetable(
651
567
  this.currentSourceTimeMs,
652
568
  this.fftSize,
653
569
  this.fftDecay,
570
+ this.fftGain,
571
+ this.shouldInterpolateFrequencies,
654
572
  ] as const,
655
573
  task: async () => {
656
574
  await this.audioBufferTask.taskComplete;
@@ -661,7 +579,7 @@ export class EFMedia extends EFTargetable(
661
579
  const startOffsetMs = this.audioBufferTask.value.startOffsetMs;
662
580
  const audioBuffer = this.audioBufferTask.value.buffer;
663
581
 
664
- const smoothedKey = `${this.fftSize}:${this.fftDecay}:${startOffsetMs}:${currentTimeMs}`;
582
+ const smoothedKey = `${this.shouldInterpolateFrequencies}:${this.fftSize}:${this.fftDecay}:${this.fftGain}:${startOffsetMs}:${currentTimeMs}`;
665
583
  const cachedData = this.#byteTimeDomainCache.get(smoothedKey);
666
584
  if (cachedData) return cachedData;
667
585
 
@@ -674,7 +592,7 @@ export class EFMedia extends EFTargetable(
674
592
  (currentTimeMs - frameOffset - startOffsetMs) / 1000,
675
593
  );
676
594
 
677
- const cacheKey = `${this.fftSize}:${startOffsetMs}:${startTime}`;
595
+ const cacheKey = `${this.shouldInterpolateFrequencies}:${this.fftSize}:${this.fftGain}:${startOffsetMs}:${startTime}`;
678
596
  const cachedFrame = this.#byteTimeDomainCache.get(cacheKey);
679
597
  if (cachedFrame) return cachedFrame;
680
598
 
@@ -694,7 +612,7 @@ export class EFMedia extends EFTargetable(
694
612
  analyser.maxDecibels = -20;
695
613
 
696
614
  const gainNode = audioContext.createGain();
697
- gainNode.gain.value = 2.0; // Amplify the signal
615
+ gainNode.gain.value = this.fftGain; // Amplify the signal
698
616
 
699
617
  source.connect(gainNode);
700
618
  gainNode.connect(analyser);
@@ -773,8 +691,10 @@ export class EFMedia extends EFTargetable(
773
691
  [
774
692
  this.audioBufferTask.status,
775
693
  this.currentSourceTimeMs,
776
- this.fftSize, // Add fftSize to dependency array
777
- this.fftDecay, // Add fftDecay to dependency array
694
+ this.fftSize,
695
+ this.fftDecay,
696
+ this.fftGain,
697
+ this.shouldInterpolateFrequencies,
778
698
  ] as const,
779
699
  task: async () => {
780
700
  await this.audioBufferTask.taskComplete;
@@ -784,7 +704,7 @@ export class EFMedia extends EFTargetable(
784
704
  const currentTimeMs = this.currentSourceTimeMs;
785
705
  const startOffsetMs = this.audioBufferTask.value.startOffsetMs;
786
706
  const audioBuffer = this.audioBufferTask.value.buffer;
787
- const smoothedKey = `${this.fftSize}:${this.fftDecay}:${startOffsetMs}:${currentTimeMs}`;
707
+ const smoothedKey = `${this.shouldInterpolateFrequencies}:${this.fftSize}:${this.fftDecay}:${this.fftGain}:${startOffsetMs}:${currentTimeMs}`;
788
708
 
789
709
  const cachedSmoothedData = this.#frequencyDataCache.get(smoothedKey);
790
710
  if (cachedSmoothedData) {
@@ -800,7 +720,7 @@ export class EFMedia extends EFTargetable(
800
720
  );
801
721
 
802
722
  // Cache key for this specific frame
803
- const cacheKey = `${this.fftSize}:${startOffsetMs}:${startTime}`;
723
+ const cacheKey = `${this.shouldInterpolateFrequencies}:${this.fftSize}:${this.fftGain}:${startOffsetMs}:${startTime}`;
804
724
 
805
725
  // Check cache for this specific frame
806
726
  const cachedFrame = this.#frequencyDataCache.get(cacheKey);
@@ -819,7 +739,7 @@ export class EFMedia extends EFTargetable(
819
739
  analyser.maxDecibels = -10;
820
740
 
821
741
  const gainNode = audioContext.createGain();
822
- gainNode.gain.value = 3.0;
742
+ gainNode.gain.value = this.fftGain;
823
743
 
824
744
  const filter = audioContext.createBiquadFilter();
825
745
  filter.type = "bandpass";
@@ -889,6 +809,16 @@ export class EFMedia extends EFTargetable(
889
809
  return processedData;
890
810
  },
891
811
  });
812
+
813
+ set fftGain(value: number) {
814
+ const oldValue = this.fftGain;
815
+ this.setAttribute("fft-gain", String(value));
816
+ this.requestUpdate("fft-gain", oldValue);
817
+ }
818
+
819
+ get fftGain() {
820
+ return Number.parseFloat(this.getAttribute("fft-gain") ?? "3.0");
821
+ }
892
822
  }
893
823
 
894
824
  function processFFTData(fftData: Uint8Array, zeroThresholdPercent = 0.1) {
@@ -3,76 +3,109 @@ import { customElement } from "lit/decorators/custom-element.js";
3
3
  import { describe, expect, test } from "vitest";
4
4
  import { EFTemporal } from "./EFTemporal.js";
5
5
 
6
- @customElement("test-temporal")
7
- class TestTemporal extends EFTemporal(LitElement) {}
6
+ @customElement("ten-seconds")
7
+ class TenSeconds extends EFTemporal(LitElement) {
8
+ get intrinsicDurationMs() {
9
+ return 10_000;
10
+ }
11
+ }
8
12
 
9
13
  declare global {
10
14
  interface HTMLElementTagNameMap {
11
- "test-temporal": TestTemporal;
15
+ "ten-seconds": TenSeconds;
12
16
  }
13
17
  }
14
18
 
15
19
  describe("sourcein and sourceout", () => {
16
20
  test("sourcein and sourceout are parsed correctly", () => {
17
- const element = document.createElement("test-temporal");
21
+ const element = document.createElement("ten-seconds");
18
22
  element.setAttribute("sourcein", "1s");
19
23
  element.setAttribute("sourceout", "5s");
20
24
  expect(element.sourceInMs).toBe(1_000);
21
25
  expect(element.sourceOutMs).toBe(5_000);
26
+ expect(element.durationMs).toBe(4_000);
22
27
  });
23
28
 
24
- test("sourcein and sourceout can be set directly on the element", () => {
25
- const element = document.createElement("test-temporal");
26
- element.sourcein = "1s";
27
- element.sourceout = "5s";
28
- expect(element.sourceInMs).toBe(1_000);
29
- expect(element.sourceOutMs).toBe(5_000);
29
+ describe("only srcin is set", () => {
30
+ test("duration is calculated", () => {
31
+ const element = document.createElement("ten-seconds");
32
+ element.sourceInMs = 1_000;
33
+ expect(element.durationMs).toBe(9_000);
34
+ });
35
+ });
36
+
37
+ describe("only srcout is set", () => {
38
+ test("duration is calculated", () => {
39
+ const element = document.createElement("ten-seconds");
40
+ element.sourceOutMs = 5_000;
41
+ expect(element.durationMs).toBe(5_000);
42
+ });
30
43
  });
31
44
 
32
- test("sourcein and sourceout are reflected correctly", () => {
33
- const element = document.createElement("test-temporal");
34
- element.sourceInMs = 1_000;
35
- element.sourceOutMs = 5_000;
36
- expect(element.getAttribute("sourcein")).toBe("1s");
37
- expect(element.getAttribute("sourceout")).toBe("5s");
45
+ describe("srcout is before srcin", () => {
46
+ test("duration is zero", () => {
47
+ const element = document.createElement("ten-seconds");
48
+ element.sourceInMs = 5_000;
49
+ element.sourceOutMs = 1_000;
50
+ console.log(element.sourceInMs, element.sourceOutMs, element.durationMs);
51
+ expect(element.durationMs).toBe(0);
52
+ });
53
+ });
54
+
55
+ describe("srcin is negative", () => {
56
+ test("srcin is normalized to 0 ", () => {
57
+ const element = document.createElement("ten-seconds");
58
+ element.sourceInMs = -1_000;
59
+ expect(element.sourceInMs).toBe(0);
60
+ expect(element.durationMs).toBe(10_000);
61
+ });
62
+ });
63
+
64
+ describe("srcout is beyond the intrinsic duration", () => {
65
+ test("srcout is normalized to the intrinsic duration", () => {
66
+ const element = document.createElement("ten-seconds");
67
+ element.sourceOutMs = 15_000;
68
+ expect(element.sourceOutMs).toBe(10_000);
69
+ });
38
70
  });
39
71
  });
40
72
 
41
73
  describe("trimstart and trimend", () => {
42
74
  test("trimstart and trimend attributes are parsed correctly", () => {
43
- const element = document.createElement("test-temporal");
75
+ const element = document.createElement("ten-seconds");
44
76
  element.setAttribute("trimstart", "1s");
45
77
  element.setAttribute("trimend", "5s");
46
78
  expect(element.trimStartMs).toBe(1_000);
47
79
  expect(element.trimEndMs).toBe(5_000);
80
+ expect(element.durationMs).toBe(4_000);
48
81
  });
49
82
 
50
- test("trimstart and trimend properties are reflected correctly", () => {
51
- const element = document.createElement("test-temporal");
52
- element.trimStartMs = 1_000;
53
- element.trimEndMs = 5_000;
54
- expect(element.getAttribute("trimstart")).toBe("1s");
55
- expect(element.getAttribute("trimend")).toBe("5s");
83
+ describe("trimstart is beyond the intrinsic duration", () => {
84
+ test("trimstart is normalized to the intrinsic duration", () => {
85
+ const element = document.createElement("ten-seconds");
86
+ element.trimStartMs = 15_000;
87
+ expect(element.trimStartMs).toBe(10_000);
88
+ });
56
89
  });
57
90
 
58
- test("trimstart and trimend can be set directly on the element", () => {
59
- const element = document.createElement("test-temporal");
60
- element.trimstart = "1s";
61
- element.trimend = "5s";
62
- expect(element.trimStartMs).toBe(1_000);
63
- expect(element.trimEndMs).toBe(5_000);
91
+ describe("trimend is beyond the intrinsic duration", () => {
92
+ test("trimend is normalized to the intrinsic duration", () => {
93
+ const element = document.createElement("ten-seconds");
94
+ element.trimEndMs = 15_000;
95
+ expect(element.trimEndMs).toBe(10_000);
96
+ });
64
97
  });
65
98
  });
66
99
 
67
100
  describe("duration", () => {
68
101
  test("duration is parsed correctly", () => {
69
- const element = document.createElement("test-temporal");
102
+ const element = document.createElement("ten-seconds");
70
103
  element.setAttribute("duration", "10s");
71
104
  expect(element.durationMs).toBe(10_000);
72
105
  });
73
106
 
74
107
  test("duration can be set directly on the element", () => {
75
- const element = document.createElement("test-temporal");
108
+ const element = document.createElement("ten-seconds");
76
109
  element.duration = "10s";
77
110
  expect(element.durationMs).toBe(10_000);
78
111
  });