feat: 新增启动欢迎通知、流式预览节点及多项功能更新
- 新增启动弹窗通知(绿色主题,支持关闭) - 新增 StreamPreview 流式文本预览节点 - 新增 fileUpload、updateNotifier 前端 JS 模块 - 重构多个 client,统一错误处理 - 删除废弃节点 batch_nano_banana_v2、quan_neng_sheng_tu 等 - 将 .config 纳入版本控制(已清空密钥)
This commit is contained in:
@@ -0,0 +1,122 @@
|
||||
import { app } from "../../../scripts/app.js";
|
||||
import { api } from "../../../scripts/api.js";
|
||||
|
||||
// 上传单个文件到 ComfyUI input 目录,返回服务端绝对路径
|
||||
async function uploadFile(file) {
|
||||
const formData = new FormData();
|
||||
formData.append("image", file, file.name);
|
||||
const resp = await api.fetchApi("/upload/image", { method: "POST", body: formData });
|
||||
if (!resp.ok) throw new Error(`上传失败: ${file.name}`);
|
||||
const data = await resp.json();
|
||||
const inputDir = await getInputDir();
|
||||
// 拼成绝对路径(Windows 用反斜杠也可以,用正斜杠 Python 也认)
|
||||
return inputDir ? inputDir.replace(/\\/g, "/") + "/" + data.name : data.name;
|
||||
}
|
||||
|
||||
// 获取 ComfyUI input 目录绝对路径(缓存)
|
||||
let _inputDir = null;
|
||||
async function getInputDir() {
|
||||
if (_inputDir !== null) return _inputDir;
|
||||
try {
|
||||
const resp = await api.fetchApi("/o1key/input_dir");
|
||||
if (resp.ok) _inputDir = (await resp.json()).path;
|
||||
else _inputDir = "";
|
||||
} catch { _inputDir = ""; }
|
||||
return _inputDir;
|
||||
}
|
||||
|
||||
// 创建一个"选择文件"按钮,点击后弹出文件选择框
|
||||
// onPaths(paths: string[]) 回调拿到上传后的路径列表
|
||||
function makeUploadButton(label, accept, multiple, onPaths) {
|
||||
const btn = document.createElement("button");
|
||||
btn.textContent = label;
|
||||
btn.style.cssText =
|
||||
"width:100%;padding:4px 8px;cursor:pointer;margin-top:2px;" +
|
||||
"background:#3a5a3a;color:#ddd;border:1px solid #666;" +
|
||||
"border-radius:4px;font-size:12px;";
|
||||
|
||||
const fileInput = document.createElement("input");
|
||||
fileInput.type = "file";
|
||||
fileInput.multiple = multiple;
|
||||
fileInput.accept = accept;
|
||||
fileInput.style.display = "none";
|
||||
document.body.appendChild(fileInput);
|
||||
|
||||
btn.addEventListener("click", () => fileInput.click());
|
||||
|
||||
fileInput.addEventListener("change", async () => {
|
||||
const files = Array.from(fileInput.files);
|
||||
if (!files.length) return;
|
||||
btn.textContent = "⏳ 上传中...";
|
||||
btn.disabled = true;
|
||||
try {
|
||||
const paths = [];
|
||||
for (const f of files) paths.push(await uploadFile(f));
|
||||
onPaths(paths);
|
||||
btn.textContent = `✅ 已上传 ${files.length} 个`;
|
||||
setTimeout(() => { btn.textContent = label; }, 2000);
|
||||
} catch (e) {
|
||||
console.error("[o1key fileUpload]", e);
|
||||
btn.textContent = "❌ 上传失败";
|
||||
setTimeout(() => { btn.textContent = label; }, 2000);
|
||||
} finally {
|
||||
btn.disabled = false;
|
||||
fileInput.value = "";
|
||||
}
|
||||
});
|
||||
|
||||
return btn;
|
||||
}
|
||||
|
||||
const ACCEPT = ".pdf,.txt,.md,.csv,.json,.py,.js,.ts,.html,.xml,.docx,.xlsx,.pptx,.zip,.wav,.mp3,.png,.jpg,.jpeg,.webp";
|
||||
|
||||
app.registerExtension({
|
||||
name: "o1key.fileUpload",
|
||||
|
||||
async beforeRegisterNodeDef(nodeType, nodeData) {
|
||||
if (nodeData.name !== "LoadFile") return;
|
||||
|
||||
const origCreated = nodeType.prototype.onNodeCreated;
|
||||
nodeType.prototype.onNodeCreated = function () {
|
||||
origCreated?.call(this);
|
||||
|
||||
const singleWidget = this.widgets?.find(w => w.name === "单文件路径");
|
||||
const folderWidget = this.widgets?.find(w => w.name === "文件夹路径");
|
||||
|
||||
// "单文件路径"下方加按钮(支持多选,追加路径)
|
||||
if (singleWidget) {
|
||||
const btn = makeUploadButton("📂 选择文件(可多选)", ACCEPT, true, (paths) => {
|
||||
const existing = singleWidget.value?.trim();
|
||||
singleWidget.value = existing
|
||||
? existing + ", " + paths.join(", ")
|
||||
: paths.join(", ");
|
||||
singleWidget.callback?.(singleWidget.value);
|
||||
app.graph.setDirtyCanvas(true);
|
||||
});
|
||||
this.addDOMWidget("upload_single_btn", "btn", btn, {
|
||||
getValue() { return null; },
|
||||
setValue() {},
|
||||
});
|
||||
}
|
||||
|
||||
// 清空按钮:同时清空单文件路径和文件夹路径
|
||||
if (singleWidget || folderWidget) {
|
||||
const clearBtn = document.createElement("button");
|
||||
clearBtn.textContent = "🗑 清空文件路径";
|
||||
clearBtn.style.cssText =
|
||||
"width:100%;padding:4px 8px;cursor:pointer;margin-top:2px;" +
|
||||
"background:#5a3a3a;color:#ddd;border:1px solid #666;" +
|
||||
"border-radius:4px;font-size:12px;";
|
||||
clearBtn.addEventListener("click", () => {
|
||||
if (singleWidget) { singleWidget.value = ""; singleWidget.callback?.(""); }
|
||||
if (folderWidget) { folderWidget.value = ""; folderWidget.callback?.(""); }
|
||||
app.graph.setDirtyCanvas(true);
|
||||
});
|
||||
this.addDOMWidget("clear_paths_btn", "btn", clearBtn, {
|
||||
getValue() { return null; },
|
||||
setValue() {},
|
||||
});
|
||||
}
|
||||
};
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user