222 lines
6.7 KiB
JavaScript
222 lines
6.7 KiB
JavaScript
import { app } from "../../../scripts/app.js";
|
|
import { api } from "../../../scripts/api.js";
|
|
import { setTextPreview } from "./text_preview.js";
|
|
|
|
const LOAD_TEXT_NODE = "1hew_LoadTxt";
|
|
const UPLOAD_SUBFOLDER = "text";
|
|
|
|
const previewTimers = new WeakMap();
|
|
|
|
async function refreshTextPreview(node) {
|
|
if (!node || node.type !== LOAD_TEXT_NODE) {
|
|
return;
|
|
}
|
|
const fileWidget = node.widgets?.find((item) => item.name === "file");
|
|
const indexWidget = node.widgets?.find((item) => item.name === "index");
|
|
const includeWidget = node.widgets?.find((item) => item.name === "include_subdir");
|
|
const file = String(fileWidget?.value ?? "").trim();
|
|
if (!file) {
|
|
setTextPreview(node, "", { resize: true });
|
|
return;
|
|
}
|
|
|
|
const requestId = (node._comfy1hewTextPreviewReq || 0) + 1;
|
|
node._comfy1hewTextPreviewReq = requestId;
|
|
const params = new URLSearchParams({
|
|
file,
|
|
index: String(indexWidget?.value ?? 0),
|
|
include_subdir: includeWidget?.value ? "true" : "false",
|
|
});
|
|
|
|
try {
|
|
const response = await api.fetchApi(
|
|
`/1hew/read_text_preview?${params.toString()}`,
|
|
);
|
|
if (requestId !== node._comfy1hewTextPreviewReq) {
|
|
return;
|
|
}
|
|
if (!response.ok) {
|
|
setTextPreview(node, "", { resize: true });
|
|
return;
|
|
}
|
|
const data = await response.json();
|
|
if (requestId !== node._comfy1hewTextPreviewReq) {
|
|
return;
|
|
}
|
|
setTextPreview(node, data?.text ?? "", { resize: true });
|
|
} catch (error) {
|
|
if (requestId !== node._comfy1hewTextPreviewReq) {
|
|
return;
|
|
}
|
|
console.warn("[1hewNodes] Failed to preview text file:", error);
|
|
setTextPreview(node, "", { resize: true });
|
|
}
|
|
}
|
|
|
|
function scheduleTextPreview(node, delay = 120) {
|
|
if (!node || node.type !== LOAD_TEXT_NODE) {
|
|
return;
|
|
}
|
|
const previous = previewTimers.get(node);
|
|
if (previous) {
|
|
clearTimeout(previous);
|
|
}
|
|
const timer = setTimeout(() => {
|
|
previewTimers.delete(node);
|
|
void refreshTextPreview(node);
|
|
}, delay);
|
|
previewTimers.set(node, timer);
|
|
}
|
|
|
|
function bindTextPreview(node) {
|
|
if (!node || node._comfy1hewTextPreviewBound) {
|
|
return;
|
|
}
|
|
node._comfy1hewTextPreviewBound = true;
|
|
|
|
for (const name of ["file", "index", "include_subdir"]) {
|
|
const widget = node.widgets?.find((item) => item.name === name);
|
|
if (!widget) {
|
|
continue;
|
|
}
|
|
const originalCallback = widget.callback;
|
|
widget.callback = function () {
|
|
const result = originalCallback?.apply(this, arguments);
|
|
scheduleTextPreview(node, 120);
|
|
return result;
|
|
};
|
|
}
|
|
|
|
scheduleTextPreview(node, 0);
|
|
}
|
|
|
|
function setFileWidget(node, value) {
|
|
const widget = node?.widgets?.find((item) => item.name === "file");
|
|
if (!widget) {
|
|
return false;
|
|
}
|
|
widget.value = value;
|
|
widget.callback?.(value);
|
|
node.graph?.setDirtyCanvas?.(true, true);
|
|
return true;
|
|
}
|
|
|
|
async function uploadTextFile(node, file) {
|
|
if (!/\.txt$/i.test(file.name)) {
|
|
window.alert("Please choose a .txt file.");
|
|
return;
|
|
}
|
|
|
|
const body = new FormData();
|
|
body.append("image", file, file.name);
|
|
body.append("type", "input");
|
|
body.append("subfolder", UPLOAD_SUBFOLDER);
|
|
body.append("overwrite", "false");
|
|
|
|
const response = await api.fetchApi("/upload/image", {
|
|
method: "POST",
|
|
body,
|
|
});
|
|
if (!response.ok) {
|
|
throw new Error(`HTTP ${response.status}`);
|
|
}
|
|
|
|
const data = await response.json();
|
|
const name = String(data?.name || file.name);
|
|
const subfolder = String(data?.subfolder || "").replace(/\\/g, "/").replace(/\/+$/, "");
|
|
const value = subfolder ? `${subfolder}/${name}` : name;
|
|
if (!setFileWidget(node, value)) {
|
|
throw new Error("file widget not found");
|
|
}
|
|
}
|
|
|
|
function pickTextFile(node) {
|
|
const input = document.createElement("input");
|
|
input.type = "file";
|
|
input.id = "comfy-1hew-load-text-upload";
|
|
input.accept = ".txt,text/plain";
|
|
input.style.display = "none";
|
|
document.body.appendChild(input);
|
|
|
|
const cleanup = () => {
|
|
input.remove();
|
|
};
|
|
input.addEventListener("cancel", cleanup);
|
|
input.addEventListener("change", async () => {
|
|
const file = input.files?.[0];
|
|
cleanup();
|
|
if (!file) {
|
|
return;
|
|
}
|
|
try {
|
|
await uploadTextFile(node, file);
|
|
} catch (error) {
|
|
console.error("[1hewNodes] Failed to upload text file:", error);
|
|
window.alert(`Upload failed: ${error}`);
|
|
}
|
|
});
|
|
input.click();
|
|
}
|
|
|
|
function installUploadButton(node) {
|
|
if (!node || node._comfy1hewLoadTextUpload) {
|
|
return;
|
|
}
|
|
node._comfy1hewLoadTextUpload = true;
|
|
|
|
const button = node.addWidget(
|
|
"button",
|
|
"choose file to upload",
|
|
null,
|
|
() => pickTextFile(node),
|
|
);
|
|
if (!button) {
|
|
return;
|
|
}
|
|
button.serialize = false;
|
|
button.options = button.options || {};
|
|
button.options.serialize = false;
|
|
button.tooltip = "Choose a .txt file to upload to ComfyUI input/text";
|
|
|
|
// Keep the upload action next to the file/index controls, before the
|
|
// growable text preview so the preview continues to occupy the bottom.
|
|
const includeIndex = node.widgets.findIndex((item) => item.name === "include_subdir");
|
|
const currentIndex = node.widgets.indexOf(button);
|
|
if (includeIndex >= 0 && currentIndex > includeIndex + 1) {
|
|
node.widgets.splice(currentIndex, 1);
|
|
node.widgets.splice(includeIndex + 1, 0, button);
|
|
}
|
|
|
|
const computed = node.computeSize?.();
|
|
if (Array.isArray(computed) && Number.isFinite(computed[1])) {
|
|
const width = Number(node.size?.[0]) || 240;
|
|
const height = Number(node.size?.[1]) || 0;
|
|
if (computed[1] > height) {
|
|
node.setSize?.([width, computed[1]]);
|
|
}
|
|
}
|
|
}
|
|
|
|
app.registerExtension({
|
|
name: "ComfyUI-1hewNodes.LoadTextUpload",
|
|
async beforeRegisterNodeDef(nodeType, nodeData) {
|
|
if (nodeData?.name !== LOAD_TEXT_NODE) {
|
|
return;
|
|
}
|
|
const originalOnNodeCreated = nodeType.prototype.onNodeCreated;
|
|
nodeType.prototype.onNodeCreated = function () {
|
|
const result = originalOnNodeCreated?.apply(this, arguments);
|
|
installUploadButton(this);
|
|
bindTextPreview(this);
|
|
return result;
|
|
};
|
|
|
|
const originalOnConfigure = nodeType.prototype.onConfigure;
|
|
nodeType.prototype.onConfigure = function () {
|
|
const result = originalOnConfigure?.apply(this, arguments);
|
|
scheduleTextPreview(this, 50);
|
|
return result;
|
|
};
|
|
},
|
|
});
|