feat(product-source): HoloLake Desktop v0.5.0 — Electron 桌面知识库应用

铸渊 2026-08-08 开发,冰朔架构决策:
- 知识库放桌面,不放服务器
- Forgejo 是 Git 引擎,光湖自己维护更新,不接上游
- 不部署 Forgejo 源码,只引用 MANIFEST + 离线包

架构:
- Electron v35(Mac arm64)+ Vite + React 19
- 复用 guanghu-knowledge-base 前端组件
- Electron 主进程自动启动 Express + Git 引擎后端
- 数据存储:~/Library/Application Support/HoloLake Era/data/
- Forgejo 离线包(桌面)作为 Git 远程仓库对接

构建:
- npx electron-builder --mac --dir → HoloLake Era.app
- 签名后续用 Developer ID 补(CSC_IDENTITY_AUTO_DISCOVERY=false)

文件:
- electron/main.ts(Electron 主进程:窗口+服务器+生命周期)
- electron/preload.ts(IPC 安全桥梁:Forgejo 远程管理接口)
- forgejo/INTEGRATION.md(Forgejo 集成声明 + 更新策略)
- forgejo/MANIFEST.sha256(离线包校验清单)
- forgejo/icon.icns(应用图标,来自 HoloLake Era v0.4.6)
- vite.config.ts(复用知识库前端,构建到 dist/)
- tsconfig.electron.json(Electron TypeScript 配置)
This commit is contained in:
冰朔 2026-08-08 07:21:04 +08:00
commit 047f37e87e
10 changed files with 7617 additions and 0 deletions

View file

@ -0,0 +1,148 @@
/**
* HoloLake Desktop · Electron
*
*
* 1.
* 2. Express + Git
* 3.
*
*
* Electron Main Process
* Express Git 3890
* BrowserWindow Vite dist
* ~/Library/Application Support/HoloLake Era/data/
*/
import { app, BrowserWindow, shell, dialog } from 'electron';
import path from 'path';
import { spawn, ChildProcess } from 'child_process';
// ─── 配置 ───
const isDev = process.env.NODE_ENV !== 'production';
const SERVER_PORT = 3890;
const CLIENT_PORT = 5180;
// 数据目录macOS 标准位置
const DATA_DIR = path.join(app.getPath('userData'), 'data');
const KB_REPO_PATH = path.join(DATA_DIR, 'knowledge-base');
// ─── 后端服务器 ───
let serverProcess: ChildProcess | null = null;
function startServer(): Promise<void> {
return new Promise((resolve, reject) => {
// 在开发模式下,服务器由 concurrently 启动
if (isDev && process.env.KB_SERVER_RUNNING === '1') {
resolve();
return;
}
const serverScript = path.resolve(__dirname, '../../guanghu-knowledge-base/server/index.ts');
serverProcess = spawn('npx', ['tsx', serverScript], {
env: {
...process.env,
KB_PORT: String(SERVER_PORT),
KB_REPO_PATH,
NODE_ENV: 'production',
},
cwd: path.resolve(__dirname, '../..'),
stdio: ['pipe', 'pipe', 'pipe'],
});
serverProcess.stdout?.on('data', (data: Buffer) => {
const msg = data.toString().trim();
console.log(`[KB Server] ${msg}`);
if (msg.includes('已启动')) resolve();
});
serverProcess.stderr?.on('data', (data: Buffer) => {
console.error(`[KB Server Error] ${data.toString().trim()}`);
});
serverProcess.on('error', (err: Error) => {
console.error('服务器启动失败:', err);
reject(err);
});
serverProcess.on('exit', (code: number | null) => {
console.log(`[KB Server] 退出code=${code}`);
serverProcess = null;
});
// 超时兜底
setTimeout(() => resolve(), 5000);
});
}
function stopServer(): void {
if (serverProcess) {
serverProcess.kill('SIGTERM');
serverProcess = null;
}
}
// ─── 窗口 ───
let mainWindow: BrowserWindow | null = null;
function createWindow(): void {
mainWindow = new BrowserWindow({
width: 1280,
height: 860,
minWidth: 800,
minHeight: 600,
titleBarStyle: 'hiddenInset',
trafficLightPosition: { x: 16, y: 16 },
backgroundColor: '#0d1117',
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
contextIsolation: true,
nodeIntegration: false,
},
});
if (isDev) {
mainWindow.loadURL(`http://localhost:${CLIENT_PORT}`);
mainWindow.webContents.openDevTools({ mode: 'detach' });
} else {
mainWindow.loadFile(path.join(__dirname, '../dist/index.html'));
}
// 外部链接用系统浏览器打开
mainWindow.webContents.setWindowOpenHandler(({ url }) => {
if (url.startsWith('http')) shell.openExternal(url);
return { action: 'deny' };
});
mainWindow.on('closed', () => {
mainWindow = null;
});
}
// ─── 应用生命周期 ───
app.whenReady().then(async () => {
try {
await startServer();
createWindow();
} catch (err) {
dialog.showErrorBox('启动失败', `知识库引擎启动失败:${err}`);
app.quit();
}
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) createWindow();
});
});
app.on('window-all-closed', () => {
stopServer();
if (process.platform !== 'darwin') app.quit();
});
app.on('before-quit', () => {
stopServer();
});

View file

@ -0,0 +1,26 @@
/**
* HoloLake Desktop · Preload
*
*
* 访 Node.js API
*/
import { contextBridge, ipcRenderer } from 'electron';
// 暴露给渲染进程的安全 API
contextBridge.exposeInMainWorld('hololake', {
// 应用信息
platform: process.platform,
version: '0.5.0',
// 数据目录
getDataPath: () => ipcRenderer.invoke('get-data-path'),
// Forgejo 远程仓库管理(预留接口)
forgejo: {
getRemotes: () => ipcRenderer.invoke('forgejo:get-remotes'),
addRemote: (url: string) => ipcRenderer.invoke('forgejo:add-remote', url),
push: () => ipcRenderer.invoke('forgejo:push'),
pull: () => ipcRenderer.invoke('forgejo:pull'),
},
});