Fix floating viewer compare modes with the Studio player
- Tag the Studio player's video with the media class the floating viewer uses, so zoom, pan, exposure/channel tone and overlay guides apply to it in A/B, side by side and grid like they do to the other slots. - Stop the A/B divider above the player controls instead of drawing over them. - Keep B's aspect ratio when comparing images of different sizes in the Studio viewer.
This commit is contained in:
@@ -46,6 +46,9 @@ All notable changes to this project are documented in this file.
|
||||
- **Floating viewer: one player everywhere**: A/B, side-by-side and grid compare now also use the Studio player for the primary video / audio (the other slots stay bare, synced elements). The legacy video controls (in/out, Nuke-style bar, ~2 000 lines, their styles and tests) are gone; the asset sidebar keeps its small preview controls. GIF / animated WebP keep their own simple player.
|
||||
|
||||
### Fixed (follow-ups)
|
||||
- **Floating viewer compare modes ignored the Studio player's video**: zoom, pan, exposure / channel tone and the overlay guides only addressed bare media elements, so in A/B, side-by-side and grid the primary video (the Studio player) stayed untouched while the other slots changed. The player's video now takes part in all of them.
|
||||
- **Floating viewer A/B divider drew over the player controls**: it now stops above the control bar (and extends fully again when the controls auto-hide).
|
||||
- **Studio compare of different sizes**: B keeps its own aspect ratio instead of being stretched to A's frame (wipe, blend and side by side).
|
||||
- **Floating viewer topbar button position**: the *Viewer* button sometimes landed at the far end of the actionbar after reloading the interface. The Run group is rendered asynchronously inside the actionbar and its late arrival was never observed; the button now re-anchors next to Run as soon as it appears.
|
||||
- **Floating viewer A/B hid the player**: with the Studio player on A, the B layer covered the right half of its controls. A now sits on top and only its picture is clipped at the divider, so the controls stay visible and usable everywhere.
|
||||
- **Light dismiss in the Studio viewer**: clicking the empty area around the image or video (not the picture, a tool, the inspector or the player bar) closes the viewer again, as the classic viewer did. Dragging to pan never closes it.
|
||||
|
||||
+5
-3
@@ -4,7 +4,7 @@ import { c as S, l as C, m as w, p as ee, s as T, t as E, u as D, v as O } from
|
||||
import { r as k } from "./events-1iwo5blr.js";
|
||||
import { C as A, D as j, E as M, T as N, _ as P, a as F, b as I, d as L, f as R, g as z, h as B, m as te, n as V, o as ne, p as H, r as re, s as ie, t as U, v as ae, w as oe, y as se } from "./tooltipShortcuts-Ury2eTek.js";
|
||||
import { I as ce } from "./mjr-primevue-C07IsEdU.js";
|
||||
import { C as le, S as ue, n as de, t as fe, w as pe } from "./genInfo-DZ8thzbX.js";
|
||||
import { C as le, S as ue, n as de, t as fe, w as pe } from "./genInfo-Dk8cerKp.js";
|
||||
// ui/features/viewer/videoSync.ts
|
||||
var me = () => {
|
||||
try {
|
||||
@@ -524,7 +524,8 @@ function je(e) {
|
||||
frameCount: e.frameCount ?? null,
|
||||
initialMuted: !!e.initialMuted,
|
||||
initialRate: e.initialRate,
|
||||
compact: e.compact !== !1
|
||||
compact: e.compact !== !1,
|
||||
mediaClass: e.mediaClass
|
||||
}) });
|
||||
r.mount(t);
|
||||
let i = !1;
|
||||
@@ -696,7 +697,8 @@ function He(e, { initialMuted: t = !1, initialPlaybackRate: n = 1, fill: r = !1
|
||||
initialMuted: e?._isPreview ? !0 : t,
|
||||
initialRate: n,
|
||||
compact: !0,
|
||||
fill: r
|
||||
fill: r,
|
||||
mediaClass: "mjr-mfv-media"
|
||||
});
|
||||
} catch (e) {
|
||||
return console.debug?.(e), null;
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
import { Vn as e, t } from "./config-C9a-wT5M.js";
|
||||
import { t as n } from "./floatingViewerManager-DVF62mxS.js";
|
||||
import { t as n } from "./floatingViewerManager-B_I-SRbd.js";
|
||||
import { r } from "./events-1iwo5blr.js";
|
||||
// ui/features/viewer/LiveStreamTracker.ts
|
||||
var i = !1, a = null, o = null, s = null, c = null, l = null, u = null, d = null, f = null, p = 0, m = 0, h = !1, g = 400, _ = "kj_preview_override", v = /* @__PURE__ */ new Set([
|
||||
@@ -1,5 +1,5 @@
|
||||
import { D as e, E as t, It as n, Jn as r, Lt as i, T as a, Wt as o, b as s, dn as c, dr as l, fr as u, g as d, h as f, i as p, in as m, l as h, ln as g, p as _, st as v, ur as y, w as b, xn as x, y as ee } from "./config-C9a-wT5M.js";
|
||||
import { t as S } from "./floatingViewerManager-DVF62mxS.js";
|
||||
import { t as S } from "./floatingViewerManager-B_I-SRbd.js";
|
||||
import { A as te, N as ne, a as C, d as w, i as T, k as E, m as D, n as O, o as re, p as k, r as A, t as j, v as ie, y as M } from "./generationSectionState-DuXxSw-j.js";
|
||||
import { n as N, t as P } from "./events-1iwo5blr.js";
|
||||
import { $ as ae, B as oe, C as F, D as se, E as I, H as ce, J as le, K as L, M as R, O as z, P as B, Q as V, S as ue, T as H, V as de, W as U, X as W, b as fe, gt as G, ht as K, j as pe, k as q, mt as me, ot as he, pt as J, st as Y, y as ge } from "./mjr-primevue-C07IsEdU.js";
|
||||
@@ -1385,7 +1385,7 @@ var xn = {
|
||||
setup(e) {
|
||||
let t = e, n = Y(0), i = Y(!1), a = null;
|
||||
function c() {
|
||||
return a ||= import("./floatingViewerManager-DVF62mxS.js").then((e) => e.n), a;
|
||||
return a ||= import("./floatingViewerManager-B_I-SRbd.js").then((e) => e.n), a;
|
||||
}
|
||||
function l() {
|
||||
return (Array.isArray(t.inputFile?.previewCandidates) ? t.inputFile.previewCandidates : [])[n.value] || "";
|
||||
+3
-3
@@ -1,9 +1,9 @@
|
||||
import { T as e, Wt as t, Yn as n, f as r, in as i, ir as a, s as o, u as s } from "./config-C9a-wT5M.js";
|
||||
import { a as c, i as l, r as u } from "./floatingViewerManager-DVF62mxS.js";
|
||||
import { a as c, i as l, r as u } from "./floatingViewerManager-B_I-SRbd.js";
|
||||
import { i as d, r as f, t as p } from "./events-1iwo5blr.js";
|
||||
import { a as ee, i as m, n as h, r as g, t as _ } from "./TagsEditor-CUU3SlW5.js";
|
||||
import { a as ee, i as m, n as h, r as g, t as _ } from "./TagsEditor-MUk741GH.js";
|
||||
import { $ as te, A as v, B as ne, C as y, D as b, E as x, H as S, J as re, K as C, M as w, O as T, P as E, Q as D, S as O, T as k, U as A, V as ie, W as j, X as M, Y as N, b as ae, gt as P, ht as F, j as I, k as L, lt as oe, mt as R, ot as se, pt as z, q as B, rt as ce, st as V, w as le, z as H } from "./mjr-primevue-C07IsEdU.js";
|
||||
import { C as U, _ as W, a as ue, b as de, c as G, d as fe, f as pe, g as me, h as he, i as ge, l as _e, m as ve, n as ye, o as K, p as be, r as xe, s as q, t as Se, u as Ce, v as we, x as Te, y as Ee } from "./genInfo-DZ8thzbX.js";
|
||||
import { C as U, _ as W, a as ue, b as de, c as G, d as fe, f as pe, g as me, h as he, i as ge, l as _e, m as ve, n as ye, o as K, p as be, r as xe, s as q, t as Se, u as Ce, v as we, x as Te, y as Ee } from "./genInfo-Dk8cerKp.js";
|
||||
// ui/vue/components/viewer/FloatingViewerHost.vue
|
||||
var De = /* @__PURE__ */ E({
|
||||
__name: "FloatingViewerHost",
|
||||
Vendored
+1
-1
@@ -201,7 +201,7 @@ var he = /* @__PURE__ */ e({
|
||||
teardownFloatingViewerManager: () => Ue
|
||||
}), S = null, ge = null;
|
||||
async function _e() {
|
||||
return S || (ge ||= import("./FloatingViewer-CexKk67v.js").then((e) => (S = e.FloatingViewer, S)), ge);
|
||||
return S || (ge ||= import("./FloatingViewer--x61GAzy.js").then((e) => (S = e.FloatingViewer, S)), ge);
|
||||
}
|
||||
var C = null, ve = null;
|
||||
async function ye() {
|
||||
@@ -1536,7 +1536,8 @@ var it = ["src"], at = ["src"], ot = {
|
||||
compareSrc: {},
|
||||
compare: {},
|
||||
wipe: {},
|
||||
lightDismiss: { type: Boolean }
|
||||
lightDismiss: { type: Boolean },
|
||||
mediaClass: {}
|
||||
},
|
||||
emits: [
|
||||
"ended",
|
||||
@@ -1874,7 +1875,7 @@ var it = ["src"], at = ["src"], ot = {
|
||||
}, [d("video", {
|
||||
ref_key: "media",
|
||||
ref: o,
|
||||
class: "mjr-studio-player__media",
|
||||
class: T(["mjr-studio-player__media", t.mediaClass]),
|
||||
src: t.src,
|
||||
playsinline: "",
|
||||
preload: "auto",
|
||||
@@ -1885,7 +1886,7 @@ var it = ["src"], at = ["src"], ot = {
|
||||
onProgress: Pe,
|
||||
onEnded: Fe,
|
||||
onError: Ie
|
||||
}, null, 40, it), Se.value ? (y(), w("video", {
|
||||
}, null, 42, it), Se.value ? (y(), w("video", {
|
||||
ref_key: "mediaB",
|
||||
ref: oe,
|
||||
key: `b:${t.compareSrc}`,
|
||||
Vendored
+10
-10
File diff suppressed because one or more lines are too long
@@ -214,6 +214,9 @@ export function buildFloatingViewerStudioPlayer(
|
||||
initialRate: initialPlaybackRate,
|
||||
compact: true,
|
||||
fill,
|
||||
// Same hook as the bare elements: zoom, pan, exposure / channel tone
|
||||
// and the overlay guides all address `.mjr-mfv-media`.
|
||||
mediaClass: "mjr-mfv-media",
|
||||
});
|
||||
} catch (e: any) {
|
||||
console.debug?.(e);
|
||||
|
||||
@@ -24,6 +24,8 @@ export interface StudioPlayerHostOptions {
|
||||
compact?: boolean;
|
||||
/** Fill the slot of a compare layout (A/B, side by side, grid). */
|
||||
fill?: boolean;
|
||||
/** Extra class for the <video>, so host-level zoom / tone controls can find it. */
|
||||
mediaClass?: string;
|
||||
}
|
||||
|
||||
export interface StudioPlayerHandle {
|
||||
@@ -59,6 +61,7 @@ export function mountStudioPlayerHost(options: StudioPlayerHostOptions): HTMLEle
|
||||
initialMuted: !!options.initialMuted,
|
||||
initialRate: options.initialRate,
|
||||
compact: options.compact !== false,
|
||||
mediaClass: options.mediaClass,
|
||||
}),
|
||||
});
|
||||
app.mount(host);
|
||||
|
||||
@@ -42,6 +42,8 @@ describe("floating viewer media player bridge", () => {
|
||||
expect(root?.classList.contains("mjr-mfv-player-host--studio")).toBe(true);
|
||||
expect(root?._mjrMediaControlsHandle?.destroy).toBeTypeOf("function");
|
||||
expect(root?.querySelector(".mjr-studio-player video")).toBeTruthy();
|
||||
// Zoom / pan / tone controls address the media through this class.
|
||||
expect(root?.querySelector(".mjr-studio-player video.mjr-mfv-media")).toBeTruthy();
|
||||
expect(mountFloatingViewerSimplePlayerMock).not.toHaveBeenCalled();
|
||||
root._mjrMediaControlsHandle.destroy();
|
||||
});
|
||||
|
||||
@@ -9269,3 +9269,17 @@ body.mjr-mfv-popout-body {
|
||||
.mjr-mfv-ab-container.has-studio-a .mjr-mfv-ab-divider {
|
||||
z-index: 10;
|
||||
}
|
||||
/* The divider stops above the player controls instead of drawing over them
|
||||
(clip-path also removes its hit area there). */
|
||||
.mjr-mfv-ab-container.has-studio-a .mjr-mfv-ab-divider {
|
||||
clip-path: inset(0 -20px 72px -20px);
|
||||
}
|
||||
.mjr-mfv-ab-container.has-studio-a:has(.mjr-studio-player.controls-hidden) .mjr-mfv-ab-divider {
|
||||
clip-path: none;
|
||||
}
|
||||
|
||||
/* B keeps its own aspect ratio inside A's frame (A/B of different sizes). */
|
||||
.mjr-studio-stage__img.is-b,
|
||||
.mjr-studio-stage__side .mjr-studio-stage__img {
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
@@ -43,6 +43,8 @@ const props = defineProps<{
|
||||
wipe?: number;
|
||||
/** Clicking the bare background (not the picture) emits `dismiss` instead of toggling play. */
|
||||
lightDismiss?: boolean;
|
||||
/** Extra class on the <video> (the floating viewer's zoom / pan / tone controls target it). */
|
||||
mediaClass?: string;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
@@ -607,6 +609,7 @@ defineExpose({
|
||||
<video
|
||||
ref="media"
|
||||
class="mjr-studio-player__media"
|
||||
:class="mediaClass"
|
||||
:src="src"
|
||||
playsinline
|
||||
preload="auto"
|
||||
|
||||
Reference in New Issue
Block a user