feat: ship v1.1.0 writer toolkit with templates, import, export, and inspiration capture

Implement chapter templates, txt/md/docx import, submission export presets, and global inspiration shortcut. Split import handlers into a separate main bundle and fix EditorLayout setTarget loop that broke E2E.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-08 13:53:22 +08:00
parent ea4819847f
commit adf877861d
49 changed files with 2450 additions and 87 deletions
+38
View File
@@ -0,0 +1,38 @@
import { BrowserWindow, ipcMain } from 'electron'
import { IPC } from '../../../shared/ipc-channels'
import type { ImportExecuteParams, ImportSplitMode } from '../../../shared/types'
import { ExportService } from '../../services/export.service'
import type { BookRegistryService } from '../../services/book-registry'
import type { GlobalSettingsService } from '../../services/global-settings'
import { wrap } from '../result'
export function registerExportHandlers(
registry: BookRegistryService,
settings: GlobalSettingsService
): void {
const svc = new ExportService(registry, settings)
ipcMain.handle(
IPC.EXPORT_FORMAT_CHAPTER,
(
_e,
{
bookId,
chapterId,
presetId
}: { bookId: string; chapterId: string; presetId: string }
) => wrap(() => svc.formatChapter(bookId, chapterId, presetId))
)
ipcMain.handle(IPC.EXPORT_COPY_CLIPBOARD, (_e, { text }: { text: string }) =>
wrap(() => {
svc.copyToClipboard(text)
})
)
ipcMain.handle(
IPC.EXPORT_SAVE_TXT,
(_e, { text, defaultName }: { text: string; defaultName: string }) =>
wrap(() => svc.saveTxt(text, defaultName))
)
}
+35
View File
@@ -0,0 +1,35 @@
import { BrowserWindow, ipcMain } from 'electron'
import { IPC } from '../../../shared/ipc-channels'
import type { ImportExecuteParams, ImportSplitMode } from '../../../shared/types'
import { ImportService } from '../../services/import.service'
import type { BookRegistryService } from '../../services/book-registry'
import { wrap } from '../result'
export function registerImportHandlers(registry: BookRegistryService): void {
const svc = new ImportService(registry)
ipcMain.handle(IPC.IMPORT_PICK_FILE, () => wrap(() => svc.pickFile()))
ipcMain.handle(
IPC.IMPORT_PREVIEW,
(
_e,
{
filePath,
splitMode,
customRegex
}: { filePath: string; splitMode: ImportSplitMode; customRegex?: string }
) => wrap(() => svc.preview(filePath, splitMode, customRegex))
)
ipcMain.handle(IPC.IMPORT_EXECUTE, (_e, params: ImportExecuteParams) =>
wrap(async () => {
const bookId = await svc.execute(params, (current, total) => {
for (const win of BrowserWindow.getAllWindows()) {
win.webContents.send(IPC.IMPORT_PROGRESS, { current, total })
}
})
return bookId
})
)
}
+2
View File
@@ -34,6 +34,7 @@ import { registerAchievementHandlers } from './handlers/achievement.handler'
import { registerGraphHandlers } from './handlers/graph.handler'
import { registerAnalyticsHandlers } from './handlers/analytics.handler'
import { registerBridgeHandlers } from './handlers/bridge.handler'
import { registerExportHandlers } from './handlers/export.handler'
import { AiClientService } from '../services/ai-client.service'
import { NetworkMonitorService } from '../services/network-monitor.service'
@@ -89,6 +90,7 @@ export function registerIpc(): { settings: GlobalSettingsService; books: BookReg
registerGraphHandlers(books)
registerAnalyticsHandlers(books, writingLogs, writingSessionRepo, pomodoroDailyRepo)
registerBridgeHandlers(books, aiClient)
registerExportHandlers(books, settings)
return { settings, books, shortcuts: shortcutManager }
}