Files
1hew-ComfyUI-1hewNodes/web/js/core/preview_menu.js
T

257 lines
7.2 KiB
JavaScript

const PREVIEW_HIDDEN_PROPERTY = "comfy1hew_preview_hidden";
const FORCE_HIDDEN_DATASET_KEY = "comfy1hewForceHidden";
const PREV_DISPLAY_DATASET_KEY = "comfy1hewPrevDisplay";
const PREV_COMPUTE_SIZE_STATE_KEY = "_comfy1hewPrevComputeSizeState";
function safePlay(videoEl, respectFrameAccurate) {
if (!videoEl || videoEl.dataset.comfy1hewUserPaused === "1") {
return;
}
if (respectFrameAccurate && videoEl.dataset.comfy1hewFrameAccurate === "1") {
return;
}
const p = videoEl.play();
if (p && typeof p.catch === "function") {
p.catch(() => {});
}
}
function getTargetNodes(app, currentNode) {
const canvas = app?.canvas;
const selectedNodes = canvas?.selected_nodes || {};
const targetNodes = Object.values(selectedNodes);
if (targetNodes.length > 0) {
return targetNodes;
}
return [currentNode];
}
function listPreviewWidgets(node) {
const items = [];
if (!node?.widgets) {
return items;
}
for (const w of node.widgets) {
const element = w?.element;
if (!element) {
continue;
}
const hasMedia =
element.tagName === "VIDEO" ||
element.tagName === "IMG" ||
element.querySelector("video") ||
element.querySelector("img");
if (!hasMedia) {
continue;
}
const videos =
element.tagName === "VIDEO"
? [element]
: Array.from(element.querySelectorAll("video"));
items.push({ widget: w, element, videos });
}
return items;
}
function fitNodeHeight(node) {
try {
const computed = node?.computeSize?.([node.size?.[0], node.size?.[1]]);
if (Array.isArray(computed) && computed.length >= 2) {
let nextH = computed[1];
const preserve = node?._comfy1hewPreserveFrameHeightUntilPreview;
if (
Number.isFinite(preserve)
&& Number.isFinite(nextH)
&& nextH < preserve
) {
nextH = preserve;
}
node.setSize([node.size[0], nextH]);
}
node?.graph?.setDirtyCanvas?.(true, true);
} catch (e) {}
}
function isPreviewHidden(node) {
return node?.properties?.[PREVIEW_HIDDEN_PROPERTY] === 1;
}
function setPreviewHidden(
node,
hidden,
{ respectFrameAccurateOnShow = false } = {},
) {
if (!node) {
return;
}
node.properties ??= {};
node.properties[PREVIEW_HIDDEN_PROPERTY] = hidden ? 1 : 0;
const items = listPreviewWidgets(node);
let layoutChanged = false;
for (const { widget, element, videos } of items) {
element.dataset[FORCE_HIDDEN_DATASET_KEY] = hidden ? "1" : "0";
if (hidden) {
if (!widget[PREV_COMPUTE_SIZE_STATE_KEY]) {
widget[PREV_COMPUTE_SIZE_STATE_KEY] = {
hadOwn: Object.prototype.hasOwnProperty.call(
widget,
"computeSize",
),
value: widget.computeSize,
};
layoutChanged = true;
}
widget.computeSize = (width) => [width, -4];
} else if (widget[PREV_COMPUTE_SIZE_STATE_KEY]) {
const state = widget[PREV_COMPUTE_SIZE_STATE_KEY];
if (state.hadOwn) {
widget.computeSize = state.value;
} else {
delete widget.computeSize;
}
delete widget[PREV_COMPUTE_SIZE_STATE_KEY];
layoutChanged = true;
}
if (hidden) {
const prevDisplay = element.style.display ?? "";
element.dataset[PREV_DISPLAY_DATASET_KEY] ??= prevDisplay;
if (prevDisplay !== "none") {
layoutChanged = true;
}
element.style.display = "none";
} else {
const restoredDisplay = element.dataset[PREV_DISPLAY_DATASET_KEY] ?? "";
if ((element.style.display ?? "") !== restoredDisplay) {
layoutChanged = true;
}
element.style.display = restoredDisplay;
delete element.dataset[PREV_DISPLAY_DATASET_KEY];
}
for (const v of videos) {
if (hidden) {
v.pause();
} else {
safePlay(v, respectFrameAccurateOnShow);
}
}
}
try {
node?.updateVideoLayout?.();
} catch (e) {}
try {
node?.updateImageLayout?.();
} catch (e) {}
if (layoutChanged) {
fitNodeHeight(node);
}
}
function syncPreview(nodes) {
for (const node of nodes) {
for (const { videos } of listPreviewWidgets(node)) {
for (const v of videos) {
v.currentTime = 0;
v.play();
if (v.dataset.comfy1hewUserPaused) {
v.dataset.comfy1hewUserPaused = "0";
}
v.muted = true;
}
}
}
}
function hasAnyVideo(nodes) {
for (const node of nodes) {
for (const { videos } of listPreviewWidgets(node)) {
if (Array.isArray(videos) && videos.length > 0) {
return true;
}
}
}
return false;
}
function isMuted(node) {
for (const { videos } of listPreviewWidgets(node)) {
const v = videos?.[0];
if (v && v.dataset.comfy1hewForceMute === "1") {
return true;
}
}
return false;
}
function setMuted(nodes, forcedMute) {
for (const node of nodes) {
for (const { videos } of listPreviewWidgets(node)) {
for (const v of videos) {
v.dataset.comfy1hewForceMute = forcedMute ? "1" : "0";
v.muted = !!forcedMute;
if (!forcedMute) {
v.volume = 1.0;
}
}
}
}
}
export function applyPreviewHiddenState(
node,
{ respectFrameAccurateOnShow = false } = {},
) {
setPreviewHidden(node, isPreviewHidden(node), { respectFrameAccurateOnShow });
}
export function addPreviewMenuOptions(
options,
{ app, currentNode, respectFrameAccurateOnShow = false } = {},
) {
if (!options || !app || !currentNode) {
return;
}
const targetNodes = getTargetNodes(app, currentNode);
const firstNode = targetNodes[0];
const anyVideo = hasAnyVideo(targetNodes);
if (anyVideo) {
options.push({
content: "Sync Preview",
callback: () => syncPreview(targetNodes),
});
}
const hiddenDesc = isPreviewHidden(firstNode) ? "Show Preview" : "Hide Preview";
options.push({
content: hiddenDesc,
callback: () => {
const nextHidden = !isPreviewHidden(firstNode);
for (const node of targetNodes) {
setPreviewHidden(node, nextHidden, { respectFrameAccurateOnShow });
}
},
});
if (!anyVideo) {
return;
}
if (isMuted(firstNode)) {
options.push({
content: "Unmute Preview",
callback: () => setMuted(targetNodes, false),
});
} else {
options.push({
content: "Mute Preview",
callback: () => setMuted(targetNodes, true),
});
}
}