主题与快捷键:透明主题真透明 + 完整 Typora 快捷键

This commit is contained in:
hunter-dev
2026-08-30 23:08:01 +08:00
parent ab46ec3cce
commit 3cc36c6450
18 changed files with 587 additions and 52 deletions
+37 -15
View File
@@ -1,7 +1,8 @@
// main 进程入口:创建窗口 + 初始化库 + 注册 IPC + 迁移检测
import { app, BrowserWindow, ipcMain, protocol, net } from 'electron';
import { app, BrowserWindow, ipcMain, protocol, net, dialog } from 'electron';
import nodePath from 'node:path';
import { fileURLToPath, pathToFileURL } from 'node:url';
import { promises as fs } from 'node:fs';
import { VaultService } from './services/vault-service.js';
import { registerIpc } from './ipc/handlers.js';
import { IPC } from '@hunter/shared';
@@ -14,35 +15,56 @@ protocol.registerSchemesAsPrivileged([
]);
let win: BrowserWindow | null = null;
// 多窗口支持:窗口控制作用于当前聚焦窗口,其次主窗口
const focusedWin = () => BrowserWindow.getFocusedWindow() ?? win;
function createWindow() {
win = new BrowserWindow({
const w = new BrowserWindow({
width: 1200, height: 760, minWidth: 900, minHeight: 580,
center: true, frame: false, show: false,
transparent: true, backgroundColor: '#00000000', backgroundMaterial: 'acrylic',
transparent: true, backgroundColor: '#00000000', backgroundMaterial: 'none',
// 真透明:透明主题下窗口直接透出下层软件(不用 acrylic 模糊,保证可清晰看到背后内容)
webPreferences: {
preload: nodePath.join(__dirname, '../preload/index.mjs'),
contextIsolation: true, nodeIntegration: false, sandbox: false
}
});
win.on('ready-to-show', () => win?.show());
if (!win) win = w;
w.on('ready-to-show', () => w.show());
// 转发渲染进程控制台消息到主进程终端(便于开发调试)
win.webContents.on('console-message', (e, level, message, line, sourceId) => {
w.webContents.on('console-message', (e, level, message, line, sourceId) => {
const lv = ['log', 'info', 'warn', 'error'][level] || 'log';
console.log('[renderer:' + lv + '] ' + message + (sourceId ? ' (' + sourceId + ':' + line + ')' : ''));
});
win.on('closed', () => { win = null; });
// 窗口控制(FR-W2)
ipcMain.on(IPC.winMinimize, () => win?.minimize());
ipcMain.on(IPC.winMaximize, () => { if (win?.isMaximized()) win.unmaximize(); else win?.maximize(); });
// FR-W2:返回窗口最大化状态供 TitleBar 切换图标
ipcMain.handle(IPC.winIsMaximized, () => win?.isMaximized() ?? false);
ipcMain.on(IPC.winClose, () => win?.close());
if (process.env.ELECTRON_RENDERER_URL) win.loadURL(process.env.ELECTRON_RENDERER_URL);
else win.loadFile(nodePath.join(__dirname, '../renderer/index.html'));
return win;
w.on('closed', () => { if (win === w) win = null; });
if (process.env.ELECTRON_RENDERER_URL) w.loadURL(process.env.ELECTRON_RENDERER_URL);
else w.loadFile(nodePath.join(__dirname, '../renderer/index.html'));
return w;
}
// 窗口控制(FR-W2 + 多窗口安全 + Typora 快捷键:全屏 F11 / 新窗口 Ctrl+Shift+N)
ipcMain.on(IPC.winMinimize, () => focusedWin()?.minimize());
ipcMain.on(IPC.winMaximize, () => { const w = focusedWin(); if (w?.isMaximized()) w.unmaximize(); else w?.maximize(); });
ipcMain.handle(IPC.winIsMaximized, () => focusedWin()?.isMaximized() ?? false);
ipcMain.on(IPC.winClose, () => focusedWin()?.close());
ipcMain.on(IPC.winFullscreen, () => { const w = focusedWin(); if (w) w.setFullScreen(!w.isFullScreen()); });
ipcMain.on(IPC.winNew, () => { createWindow(); });
// Ctrl+O 打开文件(选 .md → 返回绝对路径,渲染层换算为库内相对路径)
ipcMain.handle(IPC.fileOpen, async () => {
const w = focusedWin();
const r = await dialog.showOpenDialog(w ?? undefined as never, { properties: ['openFile'], filters: [{ name: 'Markdown', extensions: ['md'] }] });
if (r.canceled || r.filePaths.length === 0) return null;
return r.filePaths[0];
});
// Ctrl+Shift+S 另存为:弹保存对话框写入正文副本
ipcMain.handle(IPC.docSaveAs, async (_e, payload: { body: string; title: string }) => {
const w = focusedWin();
const r = await dialog.showSaveDialog(w ?? undefined as never, { defaultPath: (payload.title || 'untitled') + '.md', filters: [{ name: 'Markdown', extensions: ['md'] }] });
if (r.canceled || !r.filePath) return null;
await fs.writeFile(r.filePath, payload.body, 'utf8');
return r.filePath;
});
app.whenReady().then(async () => {
const vault = new VaultService();
await vault.init();