代码提交
This commit is contained in:
166
electron/main.js
Normal file
166
electron/main.js
Normal 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) };
|
||||
}
|
||||
}
|
||||
);
|
||||
Reference in New Issue
Block a user