| import { app } from "../../scripts/app.js"; |
| import { api } from "../../scripts/api.js"; |
|
|
| const FULLSCREEN_WRAPPER_ID = "ttN-FullscreenWrapper"; |
| const FULLSCREEN_IMAGE_ID = "ttN-FullscreenImage"; |
| const IMAGE_PREVIEWS_WRAPPER_ID = "ttN-imagePreviewsWrapper"; |
|
|
| let ttN_isFullscreen = false; |
| let ttN_FullscreenImage = new Image(); |
| let ttN_FullscreenImageIndex = 0; |
| let ttN_FullscreenNode = null; |
| let ttN_Slideshow = true; |
|
|
| let ttN_srcDict = {}; |
| let ttN_imageElementsDict = {}; |
|
|
| loadSrcDict() |
|
|
| const ARROW_KEYS = ['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight']; |
|
|
| 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 clearSrcDict() { |
| ttN_srcDict = {}; |
| sessionStorage.removeItem('ttN_srcDict'); |
| } |
|
|
| 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; |
| } |
|
|
| 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 _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(); |
| } |
| } |
|
|
| 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 _handleArrowKeys(e) { |
| e.stopPropagation(); |
|
|
| const FullscreenWrapper = document.getElementById(FULLSCREEN_WRAPPER_ID); |
| const imagePreviewsWrapper = document.getElementById(IMAGE_PREVIEWS_WRAPPER_ID); |
| const imageList = ttN_srcDict[ttN_FullscreenNode.id] || []; |
| const comfyMenu = document.getElementsByClassName("comfy-menu")[0] |
| const litegraph = document.getElementsByClassName("litegraph")[0] |
|
|
|
|
| switch (e.code) { |
| case 'ArrowLeft': |
| if (e.ctrlKey) { |
| ttN_FullscreenImageIndex = 0; |
| break; |
| } |
|
|
| if (e.shiftKey) { |
| ttN_FullscreenImageIndex -= 5; |
| if (ttN_FullscreenImageIndex < 0) ttN_FullscreenImageIndex = 0 |
| break; |
| } |
|
|
| ttN_FullscreenImageIndex -= 1; |
| if (ttN_FullscreenImageIndex < 0) ttN_FullscreenImageIndex = 0 |
| break; |
|
|
| case 'ArrowRight': |
| if (e.ctrlKey) { |
| ttN_FullscreenImageIndex = imageList.length - 1; |
| break; |
| } |
|
|
| if (e.shiftKey) { |
| ttN_FullscreenImageIndex += 5; |
| if (ttN_FullscreenImageIndex > imageList.length - 1) ttN_FullscreenImageIndex = imageList.length - 1 |
| break; |
| } |
|
|
| ttN_FullscreenImageIndex += 1; |
| if (ttN_FullscreenImageIndex > imageList.length - 1) ttN_FullscreenImageIndex = imageList.length - 1 |
| break; |
|
|
| case 'ArrowUp': |
| if (imagePreviewsWrapper) { |
| if (imagePreviewsWrapper.style.display === 'none') { |
| FullscreenWrapper.append(comfyMenu) |
| imagePreviewsWrapper.style.display = 'flex' |
| } else { |
| litegraph.append(comfyMenu) |
| imagePreviewsWrapper.style.display = 'none' |
| } |
| } |
| break; |
|
|
| case 'ArrowDown': |
| ttN_Slideshow = !ttN_Slideshow; |
|
|
| if (ttN_Slideshow) { |
| ttN_FullscreenImageIndex = -1; |
| imagePreviewsWrapper.style.display = 'none' |
| litegraph.append(comfyMenu) |
| } else { |
| imagePreviewsWrapper.style.display = 'flex' |
| FullscreenWrapper.append(comfyMenu) |
| } |
| break; |
| } |
| _applyTranslation(imagePreviewsWrapper, ttN_FullscreenImageIndex, imageList); |
| updateImageElements() |
| } |
|
|
| function _handleWheelEvent(e) { |
| e.stopPropagation(); |
|
|
| var InvertMenuScrolling = localStorage.getItem('Comfy.Settings.Comfy.InvertMenuScrolling') |
| console.log("InvertMenuScrolling", InvertMenuScrolling) |
| |
| if (InvertMenuScrolling === "true") { |
| console.log('yes') |
| if (e.deltaY > 0) { |
| |
| ttN_FullscreenImageIndex += 1; |
| } else if (e.deltaY < 0) { |
| |
| ttN_FullscreenImageIndex -= 1; |
| if (ttN_FullscreenImageIndex < 0) ttN_FullscreenImageIndex = 0; |
| } |
| } else { |
| console.log('no') |
| if (e.deltaY < 0) { |
| |
| ttN_FullscreenImageIndex += 1; |
| } else if (e.deltaY > 0) { |
| |
| ttN_FullscreenImageIndex -= 1; |
| if (ttN_FullscreenImageIndex < 0) ttN_FullscreenImageIndex = 0; |
| } |
| } |
| updateImageElements() |
| } |
|
|
| function _handleEscapeKey(e) { |
| e.stopPropagation(); |
| LGraphCanvas.prototype.ttNcloseFullscreen(); |
| } |
|
|
| function _handleExecutedEvent(event) { |
| setTimeout(updateImageTLDE, 500); |
| } |
|
|
| function _handleReconnectingEvent(e) { |
| clearSrcDict(); |
| sessionStorage.removeItem('Comfy.Settings.ttN.default_fullscreen_node'); |
| ttN_imageElementsDict = {}; |
| } |
|
|
| function _triggerFullscreen(node) { |
| updateImageTLDE(); |
| ttN_FullscreenImageIndex = -1; |
| LGraphCanvas.prototype.ttNcreateFullscreen(node); |
| ttN_FullscreenNode = node; |
| } |
|
|
| function ttNfullscreenEventListener(e) { |
| if (!ttN_isFullscreen) { |
| if ((e.code === 'ArrowUp' && e.shiftKey) && !e.ctrlKey) { |
| e.stopPropagation(); |
|
|
| let selected_node = _getSelectedNode(); |
| if (selected_node) { |
| _triggerFullscreen(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) { |
| _triggerFullscreen(defaultNode); |
| return |
| } |
| } |
| } |
|
|
| return; |
| } |
|
|
| e.stopPropagation(); |
|
|
| updateImageTLDE(); |
|
|
| const imagePreviewsWrapper = document.getElementById(IMAGE_PREVIEWS_WRAPPER_ID); |
| const imageList = ttN_srcDict[ttN_FullscreenNode.id] || []; |
|
|
| switch (e.type) { |
| case 'wheel': |
| _handleWheelEvent(e); |
| _applyTranslation(imagePreviewsWrapper, ttN_FullscreenImageIndex, imageList); |
| break; |
| case 'keydown': |
| if (ARROW_KEYS.includes(e.code)) { |
| _handleArrowKeys(e); |
| _applyTranslation(imagePreviewsWrapper, ttN_FullscreenImageIndex, imageList); |
| } else if (e.code === 'Escape') { |
| _handleEscapeKey(e); |
| _applyTranslation(imagePreviewsWrapper, ttN_FullscreenImageIndex, imageList); |
| } |
| break; |
| } |
| } |
|
|
| 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_Slideshow) { |
| updateImageElements(index); |
| } |
| } |
| |
| } |
| saveSrcDict(); |
| updateImageElements(); |
| }; |
|
|
| function _getImageDivFromSrc(imgSrc, index) { |
| |
| if (!ttN_imageElementsDict[imgSrc]) { |
| const imgWrapper = document.createElement('div'); |
| imgWrapper.classList.add('ttN-imgWrapper'); |
|
|
| const imgElement = document.createElement('img'); |
| imgElement.src = imgSrc; |
| imgElement.classList.add('ttN-img'); |
| imgWrapper.appendChild(imgElement); |
|
|
| imgElement.addEventListener('click', () => { |
| ttN_FullscreenImageIndex = index; |
| updateImageElements(index); |
| }); |
|
|
| ttN_imageElementsDict[imgSrc] = imgWrapper; |
| } |
| return ttN_imageElementsDict[imgSrc]; |
| } |
|
|
| 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_FullscreenImageIndex === imgDivList.length - 1) { |
| ttN_FullscreenImage.src = latentPreview |
| } |
| } |
|
|
| function updateImageElements(indexOverride = null) { |
| if (!ttN_isFullscreen) return; |
|
|
| const srcList = ttN_srcDict[ttN_FullscreenNode.id] || null; |
| if (!srcList) return; |
|
|
| const fullscreenWrapper = document.getElementById(FULLSCREEN_WRAPPER_ID); |
| if (!fullscreenWrapper) return |
|
|
| const imgDivList = srcList.map((src, index) => _getImageDivFromSrc(src, index)); |
|
|
| ttN_FullscreenImageIndex = indexOverride || ttN_FullscreenImageIndex |
| if ((ttN_FullscreenImageIndex > imgDivList.length - 1) || (ttN_FullscreenImageIndex === -1)) { |
| ttN_FullscreenImageIndex = imgDivList.length - 1 |
| } |
| if (ttN_FullscreenImageIndex < -1) ttN_FullscreenImageIndex = 0 |
|
|
| ttN_FullscreenImage.src = imgDivList[ttN_FullscreenImageIndex].children[0].src; |
|
|
| const previewsWrapper = document.getElementById(IMAGE_PREVIEWS_WRAPPER_ID); |
| if (!previewsWrapper) return |
|
|
| if (ttN_Slideshow) { |
| fullscreenWrapper.classList.add('ttN-slideshow') |
| _handleLatentPreview(imgDivList); |
| } else { |
| fullscreenWrapper.classList.remove('ttN-slideshow') |
| } |
|
|
| if (ttN_FullscreenImageIndex > imgDivList.length - 1) ttN_FullscreenImageIndex = imgDivList.length - 1; |
| if (ttN_FullscreenImageIndex < 0) ttN_FullscreenImageIndex = 0; |
|
|
| imgDivList.forEach((imgDiv, index) => { |
| if (previewsWrapper.children[index] != imgDiv) previewsWrapper.appendChild(imgDiv); |
|
|
| const orderValue = index - ttN_FullscreenImageIndex; |
| imgDiv.style.order = orderValue; |
|
|
| if (index < ttN_FullscreenImageIndex) { |
| |
| imgDiv.classList.remove('ttN-divSelected', 'ttN-divAfter'); |
| imgDiv.children[0].classList.remove('ttN-imgSelected', 'ttN-imgAfter'); |
|
|
| imgDiv.classList.add('ttN-divBefore'); |
| |
| } |
| else if (index === ttN_FullscreenImageIndex) { |
| |
| 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_FullscreenImageIndex) { |
| |
| imgDiv.classList.remove('ttN-divSelected', 'ttN-divBefore'); |
| imgDiv.children[0].classList.remove('ttN-imgSelected', 'ttN-imgBefore'); |
|
|
| imgDiv.classList.add('ttN-divAfter'); |
| |
| } |
| }); |
|
|
| _applyTranslation(previewsWrapper, ttN_FullscreenImageIndex, imgDivList); |
| } |
|
|
| api.addEventListener("status", _handleExecutedEvent); |
| api.addEventListener("progress", _handleExecutedEvent); |
| api.addEventListener("execution_cached", _handleExecutedEvent); |
| api.addEventListener("reconnecting", _handleReconnectingEvent); |
|
|
| app.registerExtension({ |
| name: "comfy.ttN.fullscreen", |
| init() { |
| document.addEventListener("keydown", ttNfullscreenEventListener, true); |
| }, |
| setup() { |
| LGraphCanvas.prototype.ttNcreateFullscreen = function (node) { |
| if (!node || ttN_isFullscreen) return; |
|
|
| document.addEventListener("wheel", ttNfullscreenEventListener, true); |
|
|
| const fullscreenWrapper = document.createElement('div'); |
| fullscreenWrapper.id = FULLSCREEN_WRAPPER_ID; |
| fullscreenWrapper.classList.add('ttN-slideshow'); |
| document.body.appendChild(fullscreenWrapper); |
|
|
| const fullscreenImage = new Image(); |
| fullscreenImage.src = _findFullImageSRC(node) || _findLatentPreviewImageSRC(node) || ''; |
| fullscreenImage.id = FULLSCREEN_IMAGE_ID; |
| fullscreenWrapper.appendChild(fullscreenImage); |
|
|
| const previewsWrapper = document.createElement('div'); |
| previewsWrapper.id = IMAGE_PREVIEWS_WRAPPER_ID; |
| previewsWrapper.style.display = 'none'; |
|
|
| fullscreenWrapper.appendChild(previewsWrapper); |
|
|
| _initiateFullscreen(fullscreenWrapper).then(() => { |
| ttN_isFullscreen = true; |
| ttN_FullscreenImage = fullscreenImage; |
| updateImageElements(); |
| }).catch(err => { |
| console.error("Error attempting to enable full-screen mode:", err.message, err.name); |
| }); |
|
|
| fullscreenWrapper.onfullscreenchange = function (event) { |
| if (!document.fullscreenElement) { |
| LGraphCanvas.prototype.ttNcloseFullscreen(); |
| } |
| }; |
| } |
|
|
| LGraphCanvas.prototype.ttNcloseFullscreen = function () { |
| if (!ttN_isFullscreen) return; |
|
|
| const comfyMenu = document.getElementsByClassName("comfy-menu")[0] |
| const litegraph = document.getElementsByClassName("litegraph")[0] |
| litegraph.append(comfyMenu); |
|
|
| const fullscreenWrapper = document.getElementById(FULLSCREEN_WRAPPER_ID); |
| document.body.removeChild(fullscreenWrapper); |
|
|
| document.removeEventListener("wheel", ttNfullscreenEventListener, true); |
|
|
| ttN_isFullscreen = false; |
| } |
|
|
| const getNodeMenuOptions = LGraphCanvas.prototype.getNodeMenuOptions; |
| LGraphCanvas.prototype.getNodeMenuOptions = function (node) { |
| const options = getNodeMenuOptions.apply(this, arguments); |
| node.setDirtyCanvas(true, true); |
|
|
| options.splice(options.length - 1, 0, |
| { |
| content: "Fullscreen (ttN)", |
| callback: () => { LGraphCanvas.prototype.ttNcreateFullscreen(node) } |
| }, |
| { |
| content: "Set Default Fullscreen Node (ttN)", |
| callback: function () { |
| let selectedNode = _getSelectedNode(); |
| if (selectedNode) { |
| sessionStorage.setItem('Comfy.Settings.ttN.default_fullscreen_node', JSON.stringify(selectedNode.id)); |
| } |
| } |
| }, |
| { |
| content: "Clear Default Fullscreen Node (ttN)", |
| callback: function () { |
| sessionStorage.removeItem('Comfy.Settings.ttN.default_fullscreen_node'); |
| } |
| }, |
| null |
| ); |
|
|
| return options; |
| }; |
| } |
| }); |
|
|
| var styleElement = document.createElement("style"); |
| const cssCode = ` |
| |
| #ttN-FullscreenWrapper { |
| display: flex; |
| justify-content: center; |
| align-items: end; |
| background-color: #1f1f1f; |
| } |
| |
| #ttN-FullscreenImage { |
| height: inherit; |
| position: absolute; |
| } |
| |
| #ttN-imagePreviewsWrapper { |
| position: absolute; |
| width: max-content; |
| z-index: 1; |
| height: 14vh; |
| left: 50vw; |
| transition: transform 0.2s ease; |
| } |
| |
| .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: 1; |
| } |
| |
| .ttN-imgSelected { |
| height: 200px!important; |
| z-index: 1; |
| transition: 0.1s; |
| } |
| .ttN-slideshow { |
| background: black!important; |
| } |
| |
| `; |
|
|
| styleElement.innerHTML = cssCode |
| document.head.appendChild(styleElement); |