From 4c06ec752a1a4b51b5451535e26ea3972bd439fa Mon Sep 17 00:00:00 2001 From: Christian Bastian Date: Wed, 3 Jan 2024 10:09:54 -0500 Subject: [PATCH] added add button --- README.md | 8 ++++- web/model-manager.css | 29 ++++++++------- web/model-manager.js | 84 ++++++++++++++++++++++++++++++++++++------- 3 files changed, 96 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 18f35e7..8f4caf1 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,7 @@ Currently it is still missing some features it should have. - Search `/`subdirectories of model directories based on your file structure (for example, `/styles/clothing`). - Include models listed in ComfyUI's `extra_model_paths.yaml`. - Button to copy a model to the ComfyUI clipboard. (Embedding cannot be copied to the system clipboard with an http connection.) +- Button to add model to ComfyUI graph. (For small screens/low resolution.) - Right, left and bottom toggleable sidebar modes. - Drag model to graph to add or existing node to set model. - Drag embedding to textarea to append. @@ -51,10 +52,11 @@ Currently it is still missing some features it should have. - ☐ Exclude hidden folders with a `.` prefix. - ☐ Include a optional string to always add to searches. - ☐ Enable optional checksum to detect if a model is already downloaded. -- ☐ Change copy icon to an add icon `✚`. +- ☐ Enable/Disable add and copy buttons. - ☐ Allow user to drag width of sidebar or height of bottom bar and remember it. - ☐ Hide/Show model extension. - ☐ Optionally remove embedding extension. +- ☐ Strict model drag on node widget textbox. ### Model info window/panel (server load/send on demand) @@ -68,3 +70,7 @@ Currently it is still missing some features it should have. - ☐ Show preview images for videos. - ☐ If ffmpeg or cv2 available, extract the first frame of the video and use as image preview. - ☐ Play preview video? + +### Accessibility + +- ☐ Proper naming and labeling. diff --git a/web/model-manager.css b/web/model-manager.css index dac618f..bd62784 100644 --- a/web/model-manager.css +++ b/web/model-manager.css @@ -55,10 +55,8 @@ .comfy-grid .item { position: relative; - aspect-ratio: 2/3; - max-width: 230px; - /*width: 230px;*/ - /*height: 345px;*/ + width: 230px; + height: 345px; text-align: center; overflow: hidden; border-radius: 8px; @@ -70,7 +68,7 @@ object-fit: cover; } -.comfy-grid .item div { +.comfy-grid .model-label { background-color: #000a; width: 100%; height: 2.2rem; @@ -80,7 +78,7 @@ line-height: 2.2rem; } -.comfy-grid .item div > p { +.comfy-grid .model-label > p { width: calc(100% - 2rem); overflow-x: scroll; white-space: nowrap; @@ -89,17 +87,18 @@ margin: 0; } -.comfy-grid .item div { +.comfy-grid .model-label { scrollbar-width: none; -ms-overflow-style: none; } -.comfy-grid .item div ::-webkit-scrollbar { +.comfy-grid .model-label ::-webkit-scrollbar { width: 0; height: 0; } .comfy-grid .item .model-preview-overlay { + position: absolute; top: 0; left: 0; height: 100%; @@ -107,23 +106,29 @@ background-color: rgba(0, 0, 0, 0); } -.comfy-grid .copy-model-button { +.comfy-grid .model-preview-top-right { position: absolute; + display: flex; + flex-direction: column; + gap: 8px; top: 8px; right: 8px; +} + +.comfy-grid .model-button { opacity: 0.65; } -.comfy-grid .copy-model-button:hover { +.comfy-grid .model-button:hover { opacity: 1; } -button.icon-button.copy-model-button.copy-alert-success { +.comfy-grid .model-button.copy-alert-success { color: green; border-color: green; } -button.icon-button.copy-model-button.copy-alert-fail { +.comfy-grid .model-button.copy-alert-fail { color: darkred; border-color: darkred; } diff --git a/web/model-manager.js b/web/model-manager.js index daba9e7..a28d719 100644 --- a/web/model-manager.js +++ b/web/model-manager.js @@ -274,15 +274,61 @@ class ModelGrid { }, true); }); } - + + static #addModel(event, modelType, path) { + if (modelType !== "embeddings") { + const nodeType = modelNodeType(modelType); + const widgetIndex = modelWidgetIndex(nodeType); + let node = LiteGraph.createNode(nodeType, null, []); + if (node) { + node.widgets[widgetIndex].value = path; + const selectedNodes = app.canvas.selected_nodes; + let isSelectedNode = false; + for (var i in selectedNodes) { + const selectedNode = selectedNodes[i]; + // TODO: settings.model_add_offset + node.pos[0] = selectedNode.pos[0] + 25; + node.pos[1] = selectedNode.pos[1] + 25; + isSelectedNode = true; + break; + } + if (!isSelectedNode) { + const graphMouse = app.canvas.graph_mouse; + node.pos[0] = graphMouse[0]; + node.pos[1] = graphMouse[1]; + } + app.graph.add(node, {doProcessChange: true}); + app.canvas.selectNode(node); + } + event.stopPropagation(); + } + else if (modelType === "embeddings") { + const text = pathToEmbeddingString(path); + const selectedNodes = app.canvas.selected_nodes; + for (var i in selectedNodes) { + const selectedNode = selectedNodes[i]; + const nodeType = modelNodeType(modelType); + const widgetIndex = modelWidgetIndex(nodeType); + const target = selectedNode.widgets[widgetIndex].element; + if (target.type === "textarea") { + const currentText = target.value; + const sep = currentText.length === 0 || currentText.slice(-1).match(/\s/) ? "" : " "; + target.value = currentText + sep + text; + } + } + event.stopPropagation(); + } + } + + static #dragAddModel(event, modelType, path) { const target = document.elementFromPoint(event.x, event.y); if (modelType !== "embeddings" && target.id === "graph-canvas") { const nodeType = modelNodeType(modelType); const widgetIndex = modelWidgetIndex(nodeType); const pos = app.canvas.convertEventToCanvasOffset(event); const nodeAtPos = app.graph.getNodeOnPos(pos[0], pos[1], app.canvas.visible_nodes); - //if (nodeAtPos && nodeAtPos.type === nodeType && app.canvas.processNodeWidgets(nodeAtPos, pos, event) !== nodeAtPos.widgets[widgetIndex]) { + //if (nodeAtPos && nodeAtPos.type === nodeType && app.canvas.processNodeWidgets(nodeAtPos, pos, event) !== nodeAtPos.widgets[widgetIndex]) { // TODO: settings.strict_model_drag if (nodeAtPos && nodeAtPos.type === nodeType) { let node = nodeAtPos; node.widgets[widgetIndex].value = path; @@ -319,7 +365,7 @@ class ModelGrid { successful = true; } else { - console.warn("Cannot copy embedding to the system clipboard; Try dragging the element instead."); + console.warn("Cannot copy the embedding to the system clipboard; Try dragging it instead."); } } else if (nodeType) { @@ -345,11 +391,14 @@ class ModelGrid { static generateInnerHtml(models, modelType) { if (models.length > 0) { + // TODO: settings.show_model_add_button + // TODO: settings.show_model_copy_button return models.map((item) => { const uri = item.post ?? "no-post"; const imgUrl = `/model-manager/image-preview?uri=${uri}`; - const addModel = (e) => ModelGrid.#addModel(e, modelType, item.path); - const copy = (e) => ModelGrid.#copyModelToClipboard(e, modelType, item.path); + const dragAdd = (e) => ModelGrid.#dragAddModel(e, modelType, item.path); + const clickCopy = (e) => ModelGrid.#copyModelToClipboard(e, modelType, item.path); + const clickAdd = (e) => ModelGrid.#addModel(e, modelType, item.path); return $el("div.item", {}, [ $el("img.model-preview", { src: imgUrl, @@ -357,17 +406,28 @@ class ModelGrid { }), $el("div.model-preview-overlay", { src: imgUrl, - ondragend: (e) => addModel(e), + ondragend: (e) => dragAdd(e), draggable: true, }), - $el("button.icon-button.copy-model-button", { - type: "button", - textContent: "⧉︎", - onclick: (e) => copy(e), + $el("div.model-preview-top-right", { draggable: false, - }), + }, + [ + $el("button.icon-button.model-button", { + type: "button", + textContent: "⧉︎", + onclick: (e) => clickCopy(e), + draggable: false, + }), + $el("button.icon-button.model-button", { + type: "button", + textContent: "✚", + onclick: (e) => clickAdd(e), + draggable: false, + }), + ]), $el("div.model-label", { - ondragend: (e) => addModel(e), + ondragend: (e) => dragAdd(e), draggable: true, }, [ $el("p", [item.name])