Modify ttNFullscreen to have Image Viewer PopOut support

This commit is contained in:
TinyTerra
2026-02-26 22:31:29 +11:00
parent 97096a3984
commit b5bb57f4b1
5 changed files with 1148 additions and 985 deletions
+14 -14
View File
@@ -14,30 +14,29 @@ Navigate to the **_ComfyUI/custom_nodes_** directory with cmd, and run:
`git clone https://github.com/TinyTerra/ComfyUI_tinyterraNodes.git`
### Special Features
**Fullscreen Image Viewer**
**ttN Image Viewer**
*Enabled by default*
+ <details><summary>Adds 'Fullscreen 🌏' to the node right-click context menu</summary> Opens a Fullscreen image viewer - containing all images generated by the selected node during the current comfy session.
+ <details><summary>Adds 'Set Default Fullscreen Node 🌏' to the node right-click context menu</summary> Sets the currently selected node as the default Fullscreen node
+ <details><summary>Adds 'Clear Default Fullscreen Node 🌏' to the node right-click context menu</summary> Clears the assigned default Fullscreen node
+ <details><summary>Adds '🌏 Fullscreen Image Viewer' to the node right-click context menu</summary> Opens a Fullscreen image viewer - containing all images generated by the selected node during the current comfy session.
+ <details><summary>Adds '🌏 Popout Image Viewer' to the node right-click context menu</summary> Opens a Popout image viewer - containing all images generated by the selected node during the current comfy session.
+ <details><summary>Adds '🌏 Set Default Fullscreen Node' to the node right-click context menu</summary> Sets the currently selected node as the default Fullscreen node
+ <details><summary>Adds '🌏 Clear Default Fullscreen Node' to the node right-click context menu</summary> Clears the assigned default Fullscreen node
<break>
+ Slideshow Mode
+ Toggled On - Automatically jumps to New images as they are generated (Black Background) - the UI will auto hide after a set time.
+ Toggled On - Automatically jumps to New images as they are generated if the last image is selected (Black Background) - the UI will auto hide after a set time.
+ Toggled Off - Holds to the current user selected image (Light Background)
+ Fullscreen Overlay
+ UI Overlay
+ Toggles display of a navigable preview of all of the selected nodes images
+ Toggles display of the default comfy menu
+ Toggles display of the Image Viewer Settings button
+ *Shortcuts*
+ 'shift + F11' => _Open ttN-Fullscreen using selected node OR default fullscreen node_
+ 'shift + down arrow' (Node Selected) => _Set selected node as default fullscreen node_
+ 'shift + down arrow' (No Node Selected) => _removes default fullscreen node to None_
+ 'shift + F11' => _Open ttN-Fullscreen Image Viewer using selected node OR default fullscreen node_
+ 'shift + F10' => _Open ttN-Popout Image Viewer using selected node OR default fullscreen node_
+ *Shortcuts in Fullscreen*
+ 'shift + F11' => _Close ttN-Fullscreen_
+ 'up arrow' => _Toggle Fullscreen Overlay_
+ *Shortcuts in Image Viewer*
+ 'up arrow' => _Toggle UI Overlay_
+ 'down arrow' => _Toggle Slideshow Mode_
+ 'left arrow' => _Select Image to the left_
+ 'shift + left arrow' => _Select Image 5 to the left_
@@ -49,7 +48,8 @@ Navigate to the **_ComfyUI/custom_nodes_** directory with cmd, and run:
+ 'ctrl + mouse scroll' => _Select image to Left/Right_
+ 'left click + drag' => _Update the current image's position_
+ 'double click' => _Reset position of current image_
+ 'esc' => _Close Fullscreen Mode_
+ 'esc' => _Close Image Viewer_
+ 'F' => _Fit image to Viewer window_
+ Show UI with mouse hover in Slideshow mode
**Advanced XY(Z)Plot**
+1 -1
View File
@@ -132,7 +132,7 @@ if os.path.exists(web_extension_path):
js_files = {
"interface": "enable_interface",
"fullscreen": "enable_fullscreen",
"imgViewer": "enable_fullscreen",
"embedAC": "enable_embed_autocomplete",
"dynamicWidgets": "enable_dynamic_widgets",
}
+8 -4
View File
@@ -1,6 +1,6 @@
import { app } from "../../scripts/app.js";
import { tinyterraReloadNode, wait, rebootAPI, getConfig, convertToInput, hideWidget } from "./utils.js";
import { openFullscreenApp, _setDefaultFullscreenNode } from "./ttNfullscreen.js";
import { openFullscreenApp, openPopoutViewer, _setDefaultFullscreenNode } from "./ttNimgViewer.js";
class TinyTerra extends EventTarget {
constructor() {
@@ -564,15 +564,19 @@ class TinyTerra extends EventTarget {
getTinyTerraNodeMenuItems(node) {
return [
{
content: "🌏 Fullscreen",
content: "🌏 Fullscreen Image Viewer",
callback: () => { openFullscreenApp(node) }
},
{
content: "🌏 Pop-Out Image Viewer",
callback: () => { openPopoutViewer(node) }
},
{
content: "🌏 Set Default Fullscreen Node",
content: "🌏 Set Default Viewer Node",
callback: _setDefaultFullscreenNode
},
{
content: "🌏 Clear Default Fullscreen Node",
content: "🌏 Clear Default Viewer Node",
callback: function () {
sessionStorage.removeItem('Comfy.Settings.ttN.default_fullscreen_node');
}
-966
View File
@@ -1,966 +0,0 @@
import { app } from "../../scripts/app.js";
import { api } from "../../scripts/api.js";
import { ttN_CreateDropdown, ttN_RemoveDropdown } from "./ttNdropdown.js";
//GLOBAL CONSTANTS
const FULLSCREEN_WRAPPER_ID = "ttN-FullscreenWrapper";
const FULLSCREEN_IMAGE_ID = "ttN-FullscreenImage";
const IMAGE_PREVIEWS_WRAPPER_ID = "ttN-imagePreviewsWrapper";
//GLOBAL OPTS
let TTN_AUTOHIDE = JSON.parse(localStorage.getItem('Comfy.Settings.ttN.FullscreenAutohideToggle')) ?? true;
let TTN_INVERTCTRL = JSON.parse(localStorage.getItem('Comfy.Settings.ttN.FullscreenInvertCtrl')) ?? false;
let TTN_SLIDESHOWMODE = true;
//GLOBAL ELEMENTS
const TTN_LITEGRAPH = document.getElementsByClassName("litegraph")[0];
const TTN_COMFYMENU = document.getElementsByClassName("comfy-menu")[0];
const TTN_COMFYHAMBURGER = document.getElementsByClassName("comfy-menu-hamburger")[0];
let TTN_FULLSCREEN_WRAPPER;
let TTN_FULLSCREEN_IMAGE = new Image();
let TTN_PREVIEWS_WRAPPER;
//GLOBAL VARS
let TTN_isFullscreen = false;
let TTN_isDraggingImage = false;
let TTN_offsetX, TTN_offsetY;
let TTN_FS_ImageIndex = -1;
let TTN_FullscreenNode = null;
let TTN_ImageScale = 1;
let TTN_COMPARE1_INDEX;
let TTN_COMPARE2_INDEX;
let TTN_CompareFromImage = null;
let TTN_CompareToImage = null;
let TTN_Comparing = false;
let hideUI_Timeout;
let ttN_srcDict = {};
let ttN_imageElementsDict = {};
//COMFY SPECIFIC
loadSrcDict()
function _stealComfyMenu() {
if (TTN_FULLSCREEN_WRAPPER && TTN_COMFYMENU && TTN_COMFYHAMBURGER) {
TTN_FULLSCREEN_WRAPPER.append(TTN_COMFYMENU)
TTN_FULLSCREEN_WRAPPER.append(TTN_COMFYHAMBURGER)
}
}
function _replaceComfyMenu() {
TTN_LITEGRAPH.append(TTN_COMFYMENU)
TTN_LITEGRAPH.append(TTN_COMFYHAMBURGER)
}
function _getSelectedNode() {
const graphcanvas = LGraphCanvas.active_canvas;
if (graphcanvas.selected_nodes && Object.keys(graphcanvas.selected_nodes).length === 1) {
return Object.values(graphcanvas.selected_nodes)[0];
}
return null;
}
export function _setDefaultFullscreenNode() {
let selectedNode = _getSelectedNode();
if (selectedNode) {
sessionStorage.setItem('Comfy.Settings.ttN.default_fullscreen_node', JSON.stringify(selectedNode.id));
} else {
sessionStorage.removeItem('Comfy.Settings.ttN.default_fullscreen_node');
}
}
function clearSrcDict() {
ttN_srcDict = {};
sessionStorage.removeItem('ttN_srcDict');
}
//get the src list
function _findFullImageSRC(node) {
if (node.imgs) {
let img = node.imgs.find(imgElement => imgElement.src.includes("filename"));
return img ? img.src : null;
}
return null;
}
function _findLatentPreviewImageSRC(node) {
if (!node.imgs) return null;
if (node.imageIndex !== null && node.imageIndex < node.imgs.length) {
return node.imgs[node.imageIndex].src;
} else if (node.overIndex !== null && node.overIndex < node.imgs.length) {
return node.imgs[node.overIndex].src;
}
return null;
}
function _removeLatentPreviewImageSRC(node, imgSrc) {
let latentPreviewSrc = _findLatentPreviewImageSRC(node);
if (imgSrc && latentPreviewSrc) {
for (let i in node.imgs) {
if(!node.imgs[i].src.includes("filename")) {
node.imgs.splice(i, 1);
}
}
}
}
function _handleLatentPreview(imgDivList) {
let latentPreview = _findLatentPreviewImageSRC(TTN_FullscreenNode);
if (latentPreview && !latentPreview.includes("filename") && TTN_FS_ImageIndex === imgDivList.length - 1) {
TTN_FULLSCREEN_IMAGE.src = latentPreview
}
}
function clearCompareImages() {
if (!TTN_Comparing) return;
TTN_CompareToImage?.classList.remove('ttN-compare-to')
TTN_CompareFromImage?.classList.remove('ttN-compare-from')
TTN_CompareFromImage = null
TTN_CompareToImage = null
TTN_Comparing = false
}
function addPreviewImageDropdown(imgElement) {
let suggestions = ['Load Prompt from Image', 'Select for Compare']
imgElement.addEventListener('contextmenu', (e) => {
e.preventDefault();
if (TTN_CompareFromImage && !suggestions.includes('Compare With Selected')) {
suggestions.push('Compare With Selected')
} else if (!TTN_CompareFromImage && suggestions.includes('Compare With Selected')) {
suggestions.splice(suggestions.indexOf('Compare With Selected'), 1)
}
if (TTN_Comparing && !suggestions.includes('Clear Compare')) {
suggestions.push('Clear Compare')
} else if (!TTN_Comparing && suggestions.includes('Clear Compare')) {
suggestions.splice(suggestions.indexOf('Clear Compare'), 1)
}
const manualOffset = ['80%', '70%'];
ttN_CreateDropdown(imgElement, suggestions, async (s) => {
if (s === 'Load Prompt from Image') {
imgElement.classList.add('ttN-loadToGraph')
app.handleFile(await (await fetch(imgElement.src)).blob())
setTimeout(() => {
imgElement.classList.remove('ttN-loadToGraph')
}, 350)
}
if (s === 'Delete Image') {
console.log("Delete Image")
}
if (s === 'Select for Compare') {
if (TTN_CompareFromImage) {
TTN_CompareFromImage.classList.remove('ttN-compare-from')
}
imgElement.classList.add('ttN-compare-from')
TTN_CompareFromImage = imgElement
TTN_COMPARE1_INDEX = imgElement.index
}
if (s === 'Compare With Selected') {
if (!TTN_CompareFromImage) {
return;
}
if (TTN_CompareToImage) {
TTN_CompareToImage.classList.remove('ttN-compare-to')
}
imgElement.classList.add('ttN-compare-to')
TTN_CompareToImage = imgElement
TTN_COMPARE2_INDEX = imgElement.index
TTN_Comparing = true
_setCurrentImageIndex(TTN_COMPARE2_INDEX)
}
if (s === 'Clear Compare') {
clearCompareImages()
}
}, false, manualOffset, TTN_FULLSCREEN_WRAPPER);
});
}
function _createImageDivFromSrc(imgSrc, index) {
// If image element doesn't exist, create it
if (!ttN_imageElementsDict[imgSrc]) {
const imgWrapper = document.createElement('div');
imgWrapper.classList.add('ttN-imgWrapper');
const imgElement = document.createElement('img');
imgElement.src = imgSrc;
imgElement.index = index;
imgElement.classList.add('ttN-img');
imgWrapper.appendChild(imgElement);
imgElement.addEventListener('click', () => {
_setCurrentImageIndex(index);
});
// right click
addPreviewImageDropdown(imgElement);
ttN_imageElementsDict[imgSrc] = imgWrapper;
}
return ttN_imageElementsDict[imgSrc];
}
function updateImageElements(indexOverride = null) {
if (!TTN_isFullscreen) return;
const srcList = ttN_srcDict[TTN_FullscreenNode.id] || null;
if (!srcList) return;
if (!TTN_FULLSCREEN_WRAPPER) return
const imgDivList = srcList.map((src, index) => _createImageDivFromSrc(src, index));
TTN_FS_ImageIndex = indexOverride || TTN_FS_ImageIndex
if ((TTN_FS_ImageIndex > imgDivList.length - 1) || (TTN_FS_ImageIndex === -1)) {
TTN_FS_ImageIndex = imgDivList.length - 1
}
if (TTN_FS_ImageIndex < -1) TTN_FS_ImageIndex = 0
TTN_FULLSCREEN_IMAGE.src = imgDivList[TTN_FS_ImageIndex].children[0].src;
if (!TTN_PREVIEWS_WRAPPER) return
if (TTN_SLIDESHOWMODE) {
TTN_FULLSCREEN_WRAPPER.classList.add('ttN-slideshow')
_handleLatentPreview(imgDivList);
} else {
TTN_FULLSCREEN_WRAPPER.classList.remove('ttN-slideshow')
}
if (TTN_FS_ImageIndex > imgDivList.length - 1) TTN_FS_ImageIndex = imgDivList.length - 1;
if (TTN_FS_ImageIndex < 0) TTN_FS_ImageIndex = 0;
imgDivList.forEach((imgDiv, index) => {
if (TTN_PREVIEWS_WRAPPER.children[index] != imgDiv) TTN_PREVIEWS_WRAPPER.appendChild(imgDiv);
const orderValue = index - TTN_FS_ImageIndex;
imgDiv.style.order = orderValue;
if (index < TTN_FS_ImageIndex) {
// For images before the selected image
imgDiv.classList.remove('ttN-divSelected', 'ttN-divAfter');
imgDiv.children[0].classList.remove('ttN-imgSelected', 'ttN-imgAfter');
imgDiv.classList.add('ttN-divBefore');
//imgDiv.children[0].classList.add('ttN-imgBefore');
}
else if (index === TTN_FS_ImageIndex) {
// For the selected image
imgDiv.classList.remove('ttN-divBefore', 'ttN-divAfter');
imgDiv.children[0].classList.remove('ttN-imgBefore', 'ttN-imgAfter');
imgDiv.classList.add('ttN-divSelected');
imgDiv.children[0].classList.add('ttN-imgSelected');
}
else if (index > TTN_FS_ImageIndex) {
// For images after the selected image
imgDiv.classList.remove('ttN-divSelected', 'ttN-divBefore');
imgDiv.children[0].classList.remove('ttN-imgSelected', 'ttN-imgBefore');
imgDiv.classList.add('ttN-divAfter');
//imgDiv.children[0].classList.add('ttN-imgAfter');
}
});
_applyTranslation(TTN_PREVIEWS_WRAPPER, TTN_FS_ImageIndex, imgDivList);
}
function updateImageTLDE() {
for (let node of app.graph._nodes) {
if (!node.imgs) continue
let imgSrc = _findFullImageSRC(node);
if (!imgSrc) continue;
_removeLatentPreviewImageSRC(node, imgSrc)
ttN_srcDict[node.id] = ttN_srcDict[node.id] || [];
let index = ttN_srcDict[node.id].length;
if (!ttN_srcDict[node.id].includes((index, imgSrc))) {
ttN_srcDict[node.id].push((index, imgSrc));
if (TTN_SLIDESHOWMODE) {
updateImageElements(index);
}
}
}
saveSrcDict();
updateImageElements();
};
function saveSrcDict() {
sessionStorage.setItem('ttN_srcDict', JSON.stringify(ttN_srcDict));
}
function loadSrcDict() {
const savedData = sessionStorage.getItem('ttN_srcDict');
if (savedData) {
ttN_srcDict = JSON.parse(savedData);
}
}
function removeDictEntry(node_id, index) {
delete ttN_srcDict[node_id][index];
//TODO
}
function _appendFullscreenMenuButtons() {
const ttN_hr = document.createElement('hr');
ttN_hr.style.margin = '20px 0px';
ttN_hr.style.width = '100%';
ttN_hr.classList.add('ttN-hr');
if (TTN_COMFYMENU) {
TTN_COMFYMENU.appendChild(ttN_hr);
}
// Toggle hover auto-hide button
const toggleHoverButton = document.createElement('button');
toggleHoverButton.innerHTML = 'Toggle Hover Auto-hide';
toggleHoverButton.classList.add('ttN-toggle-hover-button');
if (TTN_AUTOHIDE == true) {
toggleHoverButton.classList.add('ttN-true');
}
toggleHoverButton.addEventListener('click', function() {
if (TTN_AUTOHIDE == true) {
localStorage.setItem('Comfy.Settings.ttN.FullscreenAutohideToggle', 'false');
TTN_AUTOHIDE = false;
toggleHoverButton.classList.remove('ttN-true');
__reset_hideUI_Timeout();
} else {
localStorage.setItem('Comfy.Settings.ttN.FullscreenAutohideToggle', 'true');
TTN_AUTOHIDE = true;
toggleHoverButton.classList.add('ttN-true');
__reset_hideUI_Timeout();
}
});
if (TTN_COMFYMENU) {
TTN_COMFYMENU.appendChild(toggleHoverButton);
}
// Toggle slideshow mode button
const toggleSlideshowButton = document.createElement('button');
toggleSlideshowButton.innerHTML = 'Toggle Slideshow Mode';
toggleSlideshowButton.classList.add('ttN-slideshow-button');
if (TTN_SLIDESHOWMODE) {
toggleSlideshowButton.classList.add('ttN-true');
}
toggleSlideshowButton.addEventListener('click', function() {
TTN_SLIDESHOWMODE = !TTN_SLIDESHOWMODE;
if (TTN_SLIDESHOWMODE) {
toggleSlideshowButton.classList.add('ttN-true');
TTN_FULLSCREEN_WRAPPER.classList.add('ttN-slideshow')
} else {
toggleSlideshowButton.classList.remove('ttN-true');
TTN_FULLSCREEN_WRAPPER.classList.remove('ttN-slideshow')
}
});
if (TTN_COMFYMENU) {
TTN_COMFYMENU.appendChild(toggleSlideshowButton);
}
// Invert Ctrl button
const invertCtrlButton = document.createElement('button');
invertCtrlButton.classList.add('ttN-invert-ctrl-button');
if (TTN_INVERTCTRL == true) {
invertCtrlButton.classList.add('ttN-true');
invertCtrlButton.innerHTML = 'Ctrl+Wheel: Scroll Images';
} else {
invertCtrlButton.classList.remove('ttN-true');
invertCtrlButton.innerHTML = 'Ctrl+Wheel: Zoom';
}
invertCtrlButton.addEventListener('click', function() {
if (TTN_INVERTCTRL == true) {
localStorage.setItem('Comfy.Settings.ttN.FullscreenInvertCtrl', 'false')
invertCtrlButton.classList.remove('ttN-true');
TTN_INVERTCTRL = false
invertCtrlButton.innerHTML = 'Ctrl+Wheel: Zoom';
} else {
localStorage.setItem('Comfy.Settings.ttN.FullscreenInvertCtrl', 'true')
invertCtrlButton.classList.add('ttN-true');
TTN_INVERTCTRL = true
invertCtrlButton.innerHTML = 'Ctrl+Wheel: Scroll Images';
}
});
if (TTN_COMFYMENU) {
TTN_COMFYMENU.appendChild(invertCtrlButton);
}
}
function _removeFullscreenMenuButtons() {
const ttN_hr = document.querySelector('.ttN-hr');
if (ttN_hr) {
ttN_hr.remove();
}
const toggleHoverButton = document.querySelector('.ttN-toggle-hover-button');
if (toggleHoverButton) {
toggleHoverButton.remove();
}
const toggleSlideshowButton = document.querySelector('.ttN-slideshow-button');
if (toggleSlideshowButton) {
toggleSlideshowButton.remove();
}
const invertCtrlButton = document.querySelector('.ttN-invert-ctrl-button');
if (invertCtrlButton) {
invertCtrlButton.remove();
}
}
//EVENT LISTENERS
function enable_document_listeners(toggle=true) {
document.removeEventListener("keydown", _handleKeyPress_doc, true);
if (toggle==true) {
document.addEventListener("keydown", _handleKeyPress_doc, true);
}
}
function enable_api_listeners(toggle=true) {
api.removeEventListener("status", _handleExecutedEvent);
api.removeEventListener("progress", _handleExecutedEvent);
api.removeEventListener("execution_cached", _handleExecutedEvent);
api.removeEventListener("reconnecting", _handleReconnectingEvent);
if (toggle==true) {
api.addEventListener("status", _handleExecutedEvent);
api.addEventListener("progress", _handleExecutedEvent);
api.addEventListener("execution_cached", _handleExecutedEvent);
api.addEventListener("reconnecting", _handleReconnectingEvent);
}
}
function enable_wrapper_listeners(toggle=true) {
TTN_FULLSCREEN_WRAPPER.removeEventListener("wheel", _handleMouse);
TTN_FULLSCREEN_WRAPPER.removeEventListener('mousemove', _handleMouse);
TTN_FULLSCREEN_WRAPPER.removeEventListener('mousedown', _handleMouse);
TTN_FULLSCREEN_WRAPPER.removeEventListener('mouseup', _handleMouse);
TTN_FULLSCREEN_WRAPPER.removeEventListener('click', _handleMouse);
TTN_FULLSCREEN_WRAPPER.removeEventListener('dblclick', _handleMouse);
if (toggle==true) {
TTN_FULLSCREEN_WRAPPER.addEventListener("wheel", _handleMouse);
TTN_FULLSCREEN_WRAPPER.addEventListener('mousemove', _handleMouse);
TTN_FULLSCREEN_WRAPPER.addEventListener('mousedown', _handleMouse);
TTN_FULLSCREEN_WRAPPER.addEventListener('mouseup', _handleMouse);
TTN_FULLSCREEN_WRAPPER.addEventListener('click', _handleMouse);
TTN_FULLSCREEN_WRAPPER.addEventListener('dblclick', _handleMouse);
}
}
//EVENT HANDLERS
function _handleKeyPress_doc(e) {
if (TTN_isFullscreen) {
const imageList = ttN_srcDict[TTN_FullscreenNode.id] || []; //TODO: ADD IMAGE LIST
switch (e.code) {
case 'ArrowLeft':
e.preventDefault();
if (TTN_Comparing) {
_setCurrentImageIndex(Math.min(TTN_COMPARE1_INDEX, TTN_COMPARE2_INDEX))
break;
}
if (e.ctrlKey) {
_setCurrentImageIndex(0);
break;
}
if (e.shiftKey) {
_setCurrentImageIndex(5, "-");
if (TTN_FS_ImageIndex < 0) _setCurrentImageIndex(0);
break;
}
if (TTN_FS_ImageIndex == 0) {
_setCurrentImageIndex(0);
} else {
_setCurrentImageIndex(1, "-");
}
__reset_hideUI_Timeout();
break;
case 'ArrowRight':
e.preventDefault();
if (TTN_Comparing) {
_setCurrentImageIndex(Math.max(TTN_COMPARE1_INDEX, TTN_COMPARE2_INDEX))
break;
}
if (e.ctrlKey) {
_setCurrentImageIndex(-1);
break;
}
if (e.shiftKey) {
_setCurrentImageIndex(5, "+");
if (TTN_FS_ImageIndex > imageList.length - 1) _setCurrentImageIndex(-1);
break;
}
_setCurrentImageIndex(1, "+");
if (TTN_FS_ImageIndex > imageList.length - 1) _setCurrentImageIndex(-1);
__reset_hideUI_Timeout();
break;
case 'ArrowUp':
e.preventDefault();
__toggleUI();
break;
case 'ArrowDown':
e.preventDefault();
TTN_SLIDESHOWMODE = !TTN_SLIDESHOWMODE;
if (TTN_SLIDESHOWMODE) {
__hideUI();
_setCurrentImageIndex(-1);
} else {
__showUI();
}
break;
case 'Escape':
case 'F11':
e.preventDefault();
closeFullscreenApp();
return;
}
updateImageElements();
_applyTranslation(TTN_PREVIEWS_WRAPPER, TTN_FS_ImageIndex, imageList);
return;
};
if ((e.code === 'F11' && e.shiftKey) && !e.ctrlKey) {
let selected_node = _getSelectedNode();
if (selected_node) {
openFullscreenApp(selected_node);
return;
}
let defaultNodeID = JSON.parse(sessionStorage.getItem('Comfy.Settings.ttN.default_fullscreen_node'));
if (defaultNodeID) {
let defaultNode = app.graph._nodes_by_id[defaultNodeID];
if (defaultNode) {
TTN_SLIDESHOWMODE = true
openFullscreenApp(defaultNode);
return
}
}
}
if ((e.code === 'ArrowDown' && e.shiftKey) && !e.ctrlKey) {
setDefaultNode();
}
return
}
function _handleExecutedEvent(e) {
setTimeout(updateImageTLDE, 500);
setTimeout(updateImageTLDE, 1000);
}
function _handleReconnectingEvent(e) {
clearSrcDict();
sessionStorage.removeItem('Comfy.Settings.ttN.default_fullscreen_node');
ttN_imageElementsDict = {};
}
function _handleWheelEvent(e) {
const isMouseOverPreviewWrapper = _isMouseOverElement(TTN_PREVIEWS_WRAPPER, e.clientX, e.clientY);
const invertMenuScrolling = localStorage.getItem('Comfy.Settings.Comfy.InvertMenuScrolling') === "true";
const deltaYSign = e.deltaY > 0 ? 1 : -1;
const zoomDirection = invertMenuScrolling ? deltaYSign === -1 ? "+" : "-" : deltaYSign === 1 ? "+" : "-";
const scrollDirection = invertMenuScrolling ? deltaYSign === -1 ? "-" : "+" : deltaYSign === 1 ? "-" : "+";
e.preventDefault();
if (((TTN_INVERTCTRL && !e.ctrlKey) || (e.ctrlKey && !TTN_INVERTCTRL)) && !isMouseOverPreviewWrapper) {
// SCALE IMAGE
_scrollScaleImage(e, TTN_FULLSCREEN_IMAGE, zoomDirection);
}
if ((!TTN_INVERTCTRL && !e.ctrlKey) || (e.ctrlKey && TTN_INVERTCTRL) || isMouseOverPreviewWrapper) {
//SCROLL PREVIEWS
if (scrollDirection === "-" && TTN_FS_ImageIndex <= 0) return;
if (TTN_Comparing && scrollDirection === "-") {
_setCurrentImageIndex(Math.min(TTN_COMPARE1_INDEX, TTN_COMPARE2_INDEX))
} else if (TTN_Comparing && scrollDirection === "+") {
_setCurrentImageIndex(Math.max(TTN_COMPARE1_INDEX, TTN_COMPARE2_INDEX))
} else {
_setCurrentImageIndex(1, scrollDirection);
}
if (TTN_FS_ImageIndex < 0) _setCurrentImageIndex(0);
}
const imageList = ttN_srcDict[TTN_FullscreenNode.id] || [];
_applyTranslation(TTN_PREVIEWS_WRAPPER, TTN_FS_ImageIndex, imageList);
}
function _handleMouse(e) {
const isMouseOverPreviewWrapper = _isMouseOverElement(TTN_PREVIEWS_WRAPPER, e.clientX, e.clientY);
const isMouseOverMenu = _isMouseOverElement(TTN_COMFYMENU, e.clientX, e.clientY) || _isMouseOverElement(TTN_COMFYHAMBURGER, e.clientX, e.clientY);
const isMouseOverImage = _isMouseOverElement(TTN_FULLSCREEN_IMAGE, e.clientX, e.clientY);
switch (e.type) {
case "wheel":
_handleWheelEvent(e);
break;
case "mousedown":
if (!isMouseOverMenu && !isMouseOverPreviewWrapper) {
e.preventDefault();
TTN_isDraggingImage = true
TTN_offsetX = TTN_FULLSCREEN_IMAGE.offsetLeft - e.clientX;
TTN_offsetY = TTN_FULLSCREEN_IMAGE.offsetTop - e.clientY;
}
break;
case "mouseup":
TTN_isDraggingImage = false
break;
case "click":
if (!isMouseOverMenu && !isMouseOverPreviewWrapper && TTN_SLIDESHOWMODE) {
__hideUI();
}
break;
case "dblclick":
if (!isMouseOverMenu && !isMouseOverPreviewWrapper) {
e.preventDefault();
e.stopPropagation();
TTN_FULLSCREEN_IMAGE.style.left = "";
TTN_FULLSCREEN_IMAGE.style.top = "";
_setCurrentImageScale(TTN_FULLSCREEN_IMAGE, 1);
}
break;
case "mousemove":
if (TTN_SLIDESHOWMODE) {
if (isMouseOverPreviewWrapper || isMouseOverMenu) {
__showUI();
__reset_hideUI_Timeout(7000);
}
}
if (TTN_isDraggingImage) {
TTN_FULLSCREEN_IMAGE.style.left = (e.clientX + TTN_offsetX) + 'px';
TTN_FULLSCREEN_IMAGE.style.top = (e.clientY + TTN_offsetY) + 'px';
}
break;
}
}
//UI FUNCTIONS
function createElement(tagName, attributes = {}, parentElement = document.body) {
if (attributes.id && document.getElementById(attributes.id)) {
return document.getElementById(attributes.id);
}
const element = document.createElement(tagName);
Object.entries(attributes).forEach(([key, value]) => {
element.setAttribute(key, value);
});
parentElement.appendChild(element);
return element;
}
function _createFullscreenBody() {
TTN_FULLSCREEN_WRAPPER = createElement("div", { id: FULLSCREEN_WRAPPER_ID, class: 'ttN-slideshow' });
TTN_FULLSCREEN_WRAPPER.onfullscreenchange = function (event) {
if (!document.fullscreenElement) {
closeFullscreenApp();
}
};
TTN_PREVIEWS_WRAPPER = createElement("div", { id: IMAGE_PREVIEWS_WRAPPER_ID }, TTN_FULLSCREEN_WRAPPER);
TTN_FULLSCREEN_IMAGE.src = _findFullImageSRC(TTN_FullscreenNode) || _findLatentPreviewImageSRC(TTN_FullscreenNode) || '';
TTN_FULLSCREEN_IMAGE.id = FULLSCREEN_IMAGE_ID;
TTN_FULLSCREEN_WRAPPER.appendChild(TTN_FULLSCREEN_IMAGE);
}
function _initiateFullscreen(Element) {
if (Element.requestFullscreen) {
return Element.requestFullscreen();
} else if (Element.mozRequestFullScreen) {
return Element.mozRequestFullScreen();
} else if (Element.webkitRequestFullscreen) {
return Element.webkitRequestFullscreen();
} else if (Element.msRequestFullscreen) {
return Element.msRequestFullscreen();
}
}
export function openFullscreenApp(node) {
if (TTN_isFullscreen) return
TTN_FullscreenNode = node;
_createFullscreenBody();
enable_wrapper_listeners();
_stealComfyMenu();
_appendFullscreenMenuButtons();
__hideUI();
TTN_FULLSCREEN_IMAGE.style.left = "";
TTN_FULLSCREEN_IMAGE.style.top = "";
_setCurrentImageScale(TTN_FULLSCREEN_IMAGE, 1);
TTN_FS_ImageIndex = -1;
clearCompareImages();
_initiateFullscreen(TTN_FULLSCREEN_WRAPPER);
TTN_isFullscreen = true;
updateImageTLDE();
}
function closeFullscreenApp() {
if (!TTN_isFullscreen) return
__showUI();
clearTimeout(hideUI_Timeout);
_replaceComfyMenu();
_removeFullscreenMenuButtons();
enable_wrapper_listeners(false);
document.body.removeChild(TTN_FULLSCREEN_WRAPPER);
TTN_isFullscreen = false;
}
function _scrollScaleImage(e, image, zoomDirection, partner=null) {
let scaleFactor = 0.14;
if (TTN_ImageScale > 1) scaleFactor = scaleFactor * 2
if (TTN_ImageScale > 3) scaleFactor = scaleFactor * 2
if (TTN_ImageScale > 5) scaleFactor = scaleFactor * 2
if (TTN_ImageScale > 7) scaleFactor = scaleFactor * 2
if (TTN_ImageScale < 0.14) scaleFactor = scaleFactor / 2
const rect = image.getBoundingClientRect();
// Step 1: Get the Mouse's Position Relative to the Image
const offsetX = e.clientX - rect.left;
const offsetY = e.clientY - rect.top;
const relativeX = offsetX / rect.width;
const relativeY = offsetY / rect.height;
// Step 2: Zoom
_setCurrentImageScale(image, scaleFactor, zoomDirection);
const newRect = image.getBoundingClientRect();
// Step 3: Adjust the Image's Position
const newX = e.clientX - relativeX * newRect.width;
const newY = e.clientY - relativeY * newRect.height;
image.style.left = (image.offsetLeft + newX - newRect.left) + 'px';
image.style.top = (image.offsetTop + newY - newRect.top) + 'px';
}
function _setCurrentImageScale(image, s, symbol=null) {
switch (symbol) {
case "+":
s = TTN_ImageScale + s;
break;
case "-":
s = TTN_ImageScale - s;
break;
}
if (s < 0.01) s = 0.01
if (s > 9) s = 9
// round s to 2 decimal places
s = Math.round(s * 100) / 100
TTN_ImageScale = s
image.style.transform = "scale(" + TTN_ImageScale + ")";
}
function _applyTranslation(Element, Index, List) {
let translationConst = (Index / (List.length - 1)) * 100;
translationConst = translationConst - (0.5 / (List.length - 1)) * 100
if (Index === 0) { translationConst = 0; }
Element.style.transform = 'translateX(-' + translationConst + '%)';
}
function _isMouseOverElement(element, mouseX, mouseY) {
if (!element) return false;
const rect = element.getBoundingClientRect();
return (
mouseX >= rect.left &&
mouseX <= rect.right &&
mouseY >= rect.top &&
mouseY <= rect.bottom
);
}
function __hideUI() {
if (TTN_COMFYMENU) TTN_COMFYMENU.classList.add('hidden')
if (TTN_COMFYHAMBURGER) TTN_COMFYHAMBURGER.classList.add('hidden');
if (TTN_PREVIEWS_WRAPPER) TTN_PREVIEWS_WRAPPER.classList.add('hidden');
ttN_RemoveDropdown();
}
function __showUI() {
if(TTN_COMFYMENU) TTN_COMFYMENU.classList.remove('hidden')
if (TTN_COMFYHAMBURGER) TTN_COMFYHAMBURGER.classList.remove('hidden');
if (TTN_PREVIEWS_WRAPPER) TTN_PREVIEWS_WRAPPER.classList.remove('hidden');
__reset_hideUI_Timeout();
}
function __toggleUI() {
if (TTN_COMFYMENU) TTN_COMFYMENU.classList.toggle('hidden')
if (TTN_COMFYHAMBURGER) TTN_COMFYHAMBURGER.classList.toggle('hidden');
if (TTN_PREVIEWS_WRAPPER) TTN_PREVIEWS_WRAPPER.classList.toggle('hidden');
__reset_hideUI_Timeout();
}
function __reset_hideUI_Timeout(timeout=3700) {
clearTimeout(hideUI_Timeout);
hideUI_Timeout = setTimeout(() => {
if (TTN_SLIDESHOWMODE && TTN_AUTOHIDE) {
__hideUI();
}
}, timeout);
}
function _setCurrentImageIndex(i, symbol=null) {
switch (symbol) {
case null:
TTN_FS_ImageIndex = i;
break
case "+":
TTN_FS_ImageIndex += i;
break
case "-":
TTN_FS_ImageIndex -= i;
break
}
updateImageElements();
}
enable_document_listeners();
enable_api_listeners();
var styleElement = document.createElement("style");
const cssCode = `
.hidden {
transition: opacity 0.5s, visibility 0.5s, transform 0.2s ease!important;
opacity: 0!important;
visibility: hidden!important;
}
#ttN-FullscreenWrapper {
display: flex;
justify-content: center;
align-items: end;
background-color: #1f1f1f;
}
#ttN-FullscreenImage {
height: inherit;
position: absolute;
}
#ttN-Compare1Image, #ttN-Compare2Image {
object-fit: contain;
}
#ttN-imagePreviewsWrapper {
display: flex;
position: absolute;
width: max-content;
z-index: 10;
height: 14vh;
left: 50vw;
opacity: 1;
visibility: visible;
transition: transform 0.2s, visibility 0.2s ease;
}
#ttN-CompareWrapper {
display: flex;
position: absolute;
width: 100vw;
height: 100vh;
z-index: 1;
}
#ttN-Compare1, #ttN-Compare2 {
width: 50vw;
height: 100vh;
display: flex;
justify-content: center;
}
.ttN-imgWrapper {
position: sticky;
transition: transform 0.2s ease;
align-self: end;
}
.ttN-img {
height: 121px;
margin: 7px;
cursor: pointer;
display: block;
border: 3px solid rgba(255,255,255);
box-shadow: 0px 0px 0px 10px;
transition: all 0.4s ease;
}
.ttN-img:hover {
transform: scale(1.1);
z-index: 10;
}
.ttN-imgSelected {
height: 200px!important;
z-index: 10;
transition: 0.1s;
}
.ttN-slideshow {
background: black!important;
}
.ttN-true {
color: green!important;
}
.ttN-compare-from {
border: 7px solid cyan;
}
.ttN-compare-to {
border: 7px solid red;
}
.ttN-loadToGraph {
height: 0px!important;
transition: 0.2s ease-in-out;
}
`;
styleElement.innerHTML = cssCode
document.head.appendChild(styleElement);
+1125
View File
File diff suppressed because it is too large Load Diff