chore(desktop): add build configuration and output files
- Add electron-vite configuration for main and preload processes - Add .gitignore to exclude node_modules - Add compiled output files for main and preload processes - Add TypeScript configuration for project setup - Add package-lock.json for dependency management - Initialize build artifacts for desktop application distribution
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
node_modules
|
||||||
26
electron.vite.config.ts
Normal file
26
electron.vite.config.ts
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import { defineConfig, externalizeDepsPlugin } from 'electron-vite';
|
||||||
|
import { resolve } from 'path';
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
main: {
|
||||||
|
plugins: [externalizeDepsPlugin()],
|
||||||
|
build: {
|
||||||
|
rollupOptions: {
|
||||||
|
input: {
|
||||||
|
index: resolve(__dirname, 'src/main/main.ts'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
preload: {
|
||||||
|
plugins: [externalizeDepsPlugin()],
|
||||||
|
build: {
|
||||||
|
rollupOptions: {
|
||||||
|
input: {
|
||||||
|
index: resolve(__dirname, 'src/main/preload.ts'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// No renderer - desktop app loads external web frontend
|
||||||
|
});
|
||||||
233
out/main/index.js
Normal file
233
out/main/index.js
Normal file
@@ -0,0 +1,233 @@
|
|||||||
|
"use strict";
|
||||||
|
const electron = require("electron");
|
||||||
|
const electronUpdater = require("electron-updater");
|
||||||
|
const path = require("path");
|
||||||
|
const url = require("url");
|
||||||
|
const __dirname$1 = path.dirname(url.fileURLToPath(require("url").pathToFileURL(__filename).href));
|
||||||
|
let mainWindow = null;
|
||||||
|
let tray = null;
|
||||||
|
const isDev = process.env.NODE_ENV === "development" || !electron.app.isPackaged;
|
||||||
|
const API_URL = isDev ? "http://localhost:8080" : "https://api.timeline.com";
|
||||||
|
function createMainWindow() {
|
||||||
|
mainWindow = new electron.BrowserWindow({
|
||||||
|
width: 1200,
|
||||||
|
height: 800,
|
||||||
|
minWidth: 800,
|
||||||
|
minHeight: 600,
|
||||||
|
title: "Timeline",
|
||||||
|
icon: path.join(__dirname$1, "../build/icon.png"),
|
||||||
|
webPreferences: {
|
||||||
|
nodeIntegration: false,
|
||||||
|
contextIsolation: true,
|
||||||
|
preload: path.join(__dirname$1, "preload.js"),
|
||||||
|
webSecurity: true
|
||||||
|
},
|
||||||
|
frame: true,
|
||||||
|
show: false,
|
||||||
|
backgroundColor: "#ffffff"
|
||||||
|
});
|
||||||
|
if (isDev) {
|
||||||
|
mainWindow.loadURL("http://localhost:8000");
|
||||||
|
mainWindow.webContents.openDevTools();
|
||||||
|
} else {
|
||||||
|
mainWindow.loadURL(API_URL);
|
||||||
|
}
|
||||||
|
mainWindow.once("ready-to-show", () => {
|
||||||
|
mainWindow?.show();
|
||||||
|
});
|
||||||
|
mainWindow.webContents.setWindowOpenHandler(({ url: url2 }) => {
|
||||||
|
electron.shell.openExternal(url2);
|
||||||
|
return { action: "deny" };
|
||||||
|
});
|
||||||
|
mainWindow.on("close", (event) => {
|
||||||
|
if (process.platform === "darwin") {
|
||||||
|
event.preventDefault();
|
||||||
|
mainWindow?.hide();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
mainWindow.on("closed", () => {
|
||||||
|
mainWindow = null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function createTray() {
|
||||||
|
const iconPath = path.join(__dirname$1, "../build/tray.png");
|
||||||
|
const icon = electron.nativeImage.createFromPath(iconPath);
|
||||||
|
tray = new electron.Tray(icon.resize({ width: 16, height: 16 }));
|
||||||
|
const contextMenu = electron.Menu.buildFromTemplate([
|
||||||
|
{
|
||||||
|
label: "显示主窗口",
|
||||||
|
click: () => {
|
||||||
|
mainWindow?.show();
|
||||||
|
mainWindow?.focus();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "创建时刻",
|
||||||
|
click: () => {
|
||||||
|
mainWindow?.show();
|
||||||
|
mainWindow?.webContents.send("navigate", "/story/create");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ type: "separator" },
|
||||||
|
{
|
||||||
|
label: "退出",
|
||||||
|
click: () => {
|
||||||
|
electron.app.quit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
tray.setToolTip("Timeline");
|
||||||
|
tray.setContextMenu(contextMenu);
|
||||||
|
tray.on("click", () => {
|
||||||
|
mainWindow?.show();
|
||||||
|
mainWindow?.focus();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function createMenu() {
|
||||||
|
const template = [
|
||||||
|
{
|
||||||
|
label: "Timeline",
|
||||||
|
submenu: [
|
||||||
|
{ role: "about", label: "关于 Timeline" },
|
||||||
|
{ type: "separator" },
|
||||||
|
{
|
||||||
|
label: "偏好设置",
|
||||||
|
accelerator: "CmdOrCtrl+,",
|
||||||
|
click: () => {
|
||||||
|
mainWindow?.webContents.send("navigate", "/settings");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ type: "separator" },
|
||||||
|
{ role: "quit", label: "退出" }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "编辑",
|
||||||
|
submenu: [
|
||||||
|
{ role: "undo", label: "撤销" },
|
||||||
|
{ role: "redo", label: "重做" },
|
||||||
|
{ type: "separator" },
|
||||||
|
{ role: "cut", label: "剪切" },
|
||||||
|
{ role: "copy", label: "复制" },
|
||||||
|
{ role: "paste", label: "粘贴" },
|
||||||
|
{ role: "selectAll", label: "全选" }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "视图",
|
||||||
|
submenu: [
|
||||||
|
{ role: "reload", label: "刷新" },
|
||||||
|
{ role: "forceReload", label: "强制刷新" },
|
||||||
|
{ type: "separator" },
|
||||||
|
{ role: "resetZoom", label: "重置缩放" },
|
||||||
|
{ role: "zoomIn", label: "放大" },
|
||||||
|
{ role: "zoomOut", label: "缩小" },
|
||||||
|
{ type: "separator" },
|
||||||
|
{ role: "togglefullscreen", label: "全屏" }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "窗口",
|
||||||
|
submenu: [
|
||||||
|
{ role: "minimize", label: "最小化" },
|
||||||
|
{ role: "close", label: "关闭" }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "帮助",
|
||||||
|
submenu: [
|
||||||
|
{
|
||||||
|
label: "查看文档",
|
||||||
|
click: () => electron.shell.openExternal("https://docs.timeline.com")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "反馈问题",
|
||||||
|
click: () => electron.shell.openExternal("https://github.com/timeline/issues")
|
||||||
|
},
|
||||||
|
{ type: "separator" },
|
||||||
|
{
|
||||||
|
label: "检查更新",
|
||||||
|
click: () => checkForUpdates()
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
if (isDev) {
|
||||||
|
template[2].submenu?.push(
|
||||||
|
{ type: "separator" },
|
||||||
|
{ role: "toggleDevTools", label: "开发者工具" }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const menu = electron.Menu.buildFromTemplate(template);
|
||||||
|
electron.Menu.setApplicationMenu(menu);
|
||||||
|
}
|
||||||
|
async function checkForUpdates() {
|
||||||
|
try {
|
||||||
|
const result = await electronUpdater.autoUpdater.checkForUpdates();
|
||||||
|
if (result) {
|
||||||
|
console.log("发现新版本:", result.updateInfo.version);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("检查更新失败:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function setupAutoUpdater() {
|
||||||
|
electronUpdater.autoUpdater.autoDownload = false;
|
||||||
|
electronUpdater.autoUpdater.autoInstallOnAppQuit = true;
|
||||||
|
electronUpdater.autoUpdater.on("update-available", (info) => {
|
||||||
|
mainWindow?.webContents.send("update-available", info);
|
||||||
|
});
|
||||||
|
electronUpdater.autoUpdater.on("update-downloaded", (info) => {
|
||||||
|
mainWindow?.webContents.send("update-downloaded", info);
|
||||||
|
});
|
||||||
|
electronUpdater.autoUpdater.on("error", (error) => {
|
||||||
|
console.error("更新错误:", error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function setupIpc() {
|
||||||
|
electron.ipcMain.handle("get-version", () => {
|
||||||
|
return electron.app.getVersion();
|
||||||
|
});
|
||||||
|
electron.ipcMain.handle("get-platform", () => {
|
||||||
|
return process.platform;
|
||||||
|
});
|
||||||
|
electron.ipcMain.handle("check-update", async () => {
|
||||||
|
try {
|
||||||
|
const result = await electronUpdater.autoUpdater.checkForUpdates();
|
||||||
|
return result?.updateInfo;
|
||||||
|
} catch (error) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
electron.ipcMain.handle("download-update", async () => {
|
||||||
|
await electronUpdater.autoUpdater.downloadUpdate();
|
||||||
|
});
|
||||||
|
electron.ipcMain.handle("install-update", () => {
|
||||||
|
electronUpdater.autoUpdater.quitAndInstall();
|
||||||
|
});
|
||||||
|
electron.ipcMain.handle("open-external", (_, url2) => {
|
||||||
|
electron.shell.openExternal(url2);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
electron.app.whenReady().then(() => {
|
||||||
|
createMainWindow();
|
||||||
|
createMenu();
|
||||||
|
createTray();
|
||||||
|
setupAutoUpdater();
|
||||||
|
setupIpc();
|
||||||
|
electron.app.on("activate", () => {
|
||||||
|
if (electron.BrowserWindow.getAllWindows().length === 0) {
|
||||||
|
createMainWindow();
|
||||||
|
} else {
|
||||||
|
mainWindow?.show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
electron.app.on("window-all-closed", () => {
|
||||||
|
if (process.platform !== "darwin") {
|
||||||
|
electron.app.quit();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
electron.app.on("before-quit", () => {
|
||||||
|
tray?.destroy();
|
||||||
|
});
|
||||||
28
out/preload/index.js
Normal file
28
out/preload/index.js
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
"use strict";
|
||||||
|
const electron = require("electron");
|
||||||
|
electron.contextBridge.exposeInMainWorld("electronAPI", {
|
||||||
|
// 应用信息
|
||||||
|
getVersion: () => electron.ipcRenderer.invoke("get-version"),
|
||||||
|
getPlatform: () => electron.ipcRenderer.invoke("get-platform"),
|
||||||
|
// 更新相关
|
||||||
|
checkUpdate: () => electron.ipcRenderer.invoke("check-update"),
|
||||||
|
downloadUpdate: () => electron.ipcRenderer.invoke("download-update"),
|
||||||
|
installUpdate: () => electron.ipcRenderer.invoke("install-update"),
|
||||||
|
// 更新事件监听
|
||||||
|
onUpdateAvailable: (callback) => {
|
||||||
|
electron.ipcRenderer.on("update-available", (_, info) => callback(info));
|
||||||
|
},
|
||||||
|
onUpdateDownloaded: (callback) => {
|
||||||
|
electron.ipcRenderer.on("update-downloaded", (_, info) => callback(info));
|
||||||
|
},
|
||||||
|
// 外部链接
|
||||||
|
openExternal: (url) => electron.ipcRenderer.invoke("open-external", url),
|
||||||
|
// 导航事件
|
||||||
|
onNavigate: (callback) => {
|
||||||
|
electron.ipcRenderer.on("navigate", (_, path) => callback(path));
|
||||||
|
},
|
||||||
|
// 移除监听器
|
||||||
|
removeAllListeners: (channel) => {
|
||||||
|
electron.ipcRenderer.removeAllListeners(channel);
|
||||||
|
}
|
||||||
|
});
|
||||||
6141
package-lock.json
generated
Normal file
6141
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
17
tsconfig.json
Normal file
17
tsconfig.json
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2020",
|
||||||
|
"module": "commonjs",
|
||||||
|
"lib": ["ES2020"],
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"strict": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"outDir": "./dist",
|
||||||
|
"rootDir": "./src",
|
||||||
|
"types": ["node"]
|
||||||
|
},
|
||||||
|
"include": ["src/**/*"],
|
||||||
|
"exclude": ["node_modules", "dist", "out", "release"]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user