feat: 新增启动欢迎通知、流式预览节点及多项功能更新

- 新增启动弹窗通知(绿色主题,支持关闭)
- 新增 StreamPreview 流式文本预览节点
- 新增 fileUpload、updateNotifier 前端 JS 模块
- 重构多个 client,统一错误处理
- 删除废弃节点 batch_nano_banana_v2、quan_neng_sheng_tu 等
- 将 .config 纳入版本控制(已清空密钥)
This commit is contained in:
Jony
2026-04-13 00:49:51 +08:00
parent bbc5f4a2c4
commit 92bcf65d14
28 changed files with 1381 additions and 3518 deletions
+122
View File
@@ -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() {},
});
}
};
},
});