feat: 实现笔临 P0/P1 Electron 桌面应用 v0.1.0

交付写作核心(TipTap、自动保存、分卷分章)、Onboarding、7 主题与双语 i18n、快捷键设置;主进程使用 node:sqlite;补全 Vitest 与 Playwright E2E;统一 design/ 目录并移除 desigin 拼写错误路径。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-06 10:39:20 +08:00
parent a06038035b
commit 011bd6d4ca
70 changed files with 14245 additions and 84 deletions
+79
View File
@@ -0,0 +1,79 @@
import { app, BrowserWindow, ipcMain } from 'electron'
import { existsSync } from 'fs'
import { join } from 'path'
import { IPC } from '../shared/ipc-channels'
import { getShortcutManager, registerIpc } from './ipc/register'
import { closeAllBookDbs } from './db/connection'
if (process.env.BILIN_E2E === '1' && process.env.BILIN_E2E_USER_DATA) {
app.setPath('userData', process.env.BILIN_E2E_USER_DATA)
}
let mainWindow: BrowserWindow | null = null
function preloadPath(): string {
const dir = join(__dirname, '../preload')
const mjs = join(dir, 'index.mjs')
if (existsSync(mjs)) return mjs
return join(dir, 'index.js')
}
function createWindow(): void { mainWindow = new BrowserWindow({
width: 1280,
height: 800,
minWidth: 960,
minHeight: 600,
frame: false,
titleBarStyle: 'hidden',
webPreferences: {
preload: preloadPath(),
contextIsolation: true,
nodeIntegration: false
}
})
if (process.env.ELECTRON_RENDERER_URL) {
mainWindow.loadURL(process.env.ELECTRON_RENDERER_URL)
} else {
mainWindow.loadFile(join(__dirname, '../renderer/index.html'))
}
mainWindow.on('closed', () => {
mainWindow = null
})
getShortcutManager()?.setWindow(mainWindow)
}
app.whenReady().then(() => {
registerIpc()
ipcMain.handle(IPC.WINDOW_MINIMIZE, (event) => {
BrowserWindow.fromWebContents(event.sender)?.minimize()
})
ipcMain.handle(IPC.WINDOW_MAXIMIZE, (event) => {
const win = BrowserWindow.fromWebContents(event.sender)
if (!win) return
if (win.isMaximized()) win.unmaximize()
else win.maximize()
})
ipcMain.handle(IPC.WINDOW_CLOSE, (event) => {
BrowserWindow.fromWebContents(event.sender)?.close()
})
createWindow()
})
app.on('window-all-closed', () => {
getShortcutManager()?.unregisterAll()
closeAllBookDbs()
if (process.platform !== 'darwin') app.quit()
})
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) createWindow()
})
app.on('will-quit', () => {
getShortcutManager()?.unregisterAll()
})