代码提交

This commit is contained in:
2026-03-03 10:38:37 +08:00
parent e904d7af1d
commit 000d1ef1a8
22 changed files with 7043 additions and 0 deletions

166
electron/main.js Normal file
View File

@@ -0,0 +1,166 @@
const path = require("path");
const fs = require("fs");
const { app, BrowserWindow, ipcMain, dialog } = require("electron");
const { runGeneration } = require("../core/generator");
const isDev = process.env.NODE_ENV === "development" || !app.isPackaged;
const configPath = path.join(app.getPath("userData"), "config_nano_banana.json");
const defaultConfig = {
api_key: "",
api_create_url: "https://api.wuyinkeji.com/api/async/image_nanoBanana2",
api_result_url: "https://api.wuyinkeji.com/api/async/detail",
poll_interval_seconds: 10,
max_wait_seconds: 300,
r2_account_id: "",
r2_access_key_id: "",
r2_secret_access_key: "",
r2_bucket: "",
r2_public_url: "",
default_save_dir: "",
extra_params: [],
};
function loadConfig() {
try {
const raw = fs.readFileSync(configPath, "utf8");
const data = JSON.parse(raw);
return { ...defaultConfig, ...data };
} catch {
const fallback = path.join(app.getAppPath(), "config_nano_banana.json");
try {
if (fs.existsSync(fallback)) {
const raw = fs.readFileSync(fallback, "utf8");
const data = JSON.parse(raw);
return { ...defaultConfig, ...data };
}
} catch (_) {}
return { ...defaultConfig };
}
}
function saveConfig(config) {
fs.mkdirSync(path.dirname(configPath), { recursive: true });
fs.writeFileSync(configPath, JSON.stringify(config, null, 2), "utf8");
}
function createWindow() {
const win = new BrowserWindow({
width: 900,
height: 680,
minWidth: 640,
minHeight: 480,
webPreferences: {
preload: path.join(__dirname, "preload.js"),
contextIsolation: true,
nodeIntegration: false,
},
title: "NanoBanana 生图",
show: false,
});
win.once("ready-to-show", () => win.show());
if (isDev) {
win.loadURL("http://localhost:5173");
win.webContents.openDevTools();
} else {
win.loadFile(path.join(__dirname, "../dist/index.html"));
}
}
app.whenReady().then(() => {
createWindow();
app.on("activate", () => {
if (BrowserWindow.getAllWindows().length === 0) createWindow();
});
});
app.on("window-all-closed", () => {
if (process.platform !== "darwin") app.quit();
});
ipcMain.handle("getConfig", () => loadConfig());
ipcMain.handle("saveConfig", (_e, config) => {
const current = loadConfig();
const merged = { ...current, ...config };
saveConfig(merged);
return loadConfig();
});
ipcMain.handle("selectDirectory", async (_e, defaultPath) => {
const { canceled, filePaths } = await dialog.showOpenDialog({
properties: ["openDirectory"],
defaultPath: defaultPath || undefined,
});
if (canceled || !filePaths.length) return null;
return filePaths[0];
});
ipcMain.handle("selectReferenceFiles", async () => {
const { canceled, filePaths } = await dialog.showOpenDialog({
properties: ["openFile", "multiSelections"],
filters: [
{ name: "图片", extensions: ["jpg", "jpeg", "png", "webp", "bmp", "gif"] },
],
});
if (canceled || !filePaths.length) return [];
return filePaths;
});
ipcMain.handle("getImageDataUrl", async (_e, filePath) => {
if (!filePath || typeof filePath !== "string") return null;
try {
const ext = filePath.replace(/^.*\./, "").toLowerCase();
const mime = { jpg: "image/jpeg", jpeg: "image/jpeg", png: "image/png", webp: "image/webp", bmp: "image/bmp", gif: "image/gif" }[ext] || "image/jpeg";
const buf = fs.readFileSync(filePath);
return `data:${mime};base64,${buf.toString("base64")}`;
} catch {
return null;
}
});
ipcMain.handle(
"startGeneration",
async (
_e,
{
prompt,
size,
aspectRatio,
refFilePaths,
saveDir,
extraParams,
}
) => {
const config = loadConfig();
const win = BrowserWindow.getFocusedWindow() || BrowserWindow.getAllWindows()[0];
const send = (step, message) => {
if (win && !win.isDestroyed()) win.webContents.send("generationProgress", { step, message });
};
const extra = Array.isArray(config.extra_params)
? config.extra_params.reduce((acc, { key, value }) => {
if (key != null && key !== "") acc[key] = value;
return acc;
}, {})
: {};
const mergedExtra = { ...extra, ...(extraParams || {}) };
try {
const result = await runGeneration(
config,
{
prompt,
size: size || config.size || "1K",
aspectRatio: aspectRatio || config.aspectRatio || "auto",
refFilePaths: refFilePaths || [],
saveDir: saveDir || config.default_save_dir,
extraParams: Object.keys(mergedExtra).length ? mergedExtra : undefined,
},
send
);
return { success: true, ...result };
} catch (err) {
return { success: false, error: err.message || String(err) };
}
}
);