b33d2e7b34
Deliver P5 writer workflow: writing cockpit, manual knowledge CRUD with review, chapter bridge with optional AI, publish status, and stock buffer reminders. Co-authored-by: Cursor <cursoragent@cursor.com>
136 lines
3.8 KiB
TypeScript
136 lines
3.8 KiB
TypeScript
import { ipcMain } from 'electron'
|
|
import { IPC } from '../../../shared/ipc-channels'
|
|
import type { ChapterStatus, PublishStatus } from '../../../shared/types'
|
|
import { BookRegistryService } from '../../services/book-registry'
|
|
import { ftsSync } from '../../services/fts-sync.service'
|
|
import { wrap } from '../result'
|
|
|
|
export function registerChapterHandlers(registry: BookRegistryService): void {
|
|
ipcMain.handle(
|
|
IPC.VOLUME_CREATE,
|
|
(_event, { bookId, name }: { bookId: string; name: string }) =>
|
|
wrap(() => {
|
|
const repo = registry.getVolumeRepo(bookId)
|
|
const volumes = repo.list()
|
|
return repo.create(name, volumes.length)
|
|
})
|
|
)
|
|
|
|
ipcMain.handle(
|
|
IPC.VOLUME_UPDATE,
|
|
(
|
|
_event,
|
|
{ bookId, volumeId, name, description }: { bookId: string; volumeId: string; name?: string; description?: string }
|
|
) => wrap(() => registry.getVolumeRepo(bookId).update(volumeId, { name, description }))
|
|
)
|
|
|
|
ipcMain.handle(
|
|
IPC.VOLUME_DELETE,
|
|
(_event, { bookId, volumeId }: { bookId: string; volumeId: string }) =>
|
|
wrap(() => {
|
|
registry.getVolumeRepo(bookId).delete(volumeId)
|
|
})
|
|
)
|
|
|
|
ipcMain.handle(
|
|
IPC.CHAPTER_CREATE,
|
|
(
|
|
_event,
|
|
{ bookId, volumeId, title }: { bookId: string; volumeId: string; title: string }
|
|
) =>
|
|
wrap(() => {
|
|
const repo = registry.getChapterRepo(bookId)
|
|
const sortOrder = repo.nextSortOrder(volumeId)
|
|
const chapter = repo.create(volumeId, title, sortOrder)
|
|
ftsSync.upsert(registry.getDb(bookId), 'chapter', chapter.id, chapter.title, chapter.content)
|
|
return chapter
|
|
})
|
|
)
|
|
|
|
ipcMain.handle(
|
|
IPC.CHAPTER_GET,
|
|
(_event, { bookId, chapterId }: { bookId: string; chapterId: string }) =>
|
|
wrap(() => {
|
|
const ch = registry.getChapterRepo(bookId).get(chapterId)
|
|
if (!ch) throw new Error('Chapter not found')
|
|
return ch
|
|
})
|
|
)
|
|
|
|
ipcMain.handle(
|
|
IPC.CHAPTER_UPDATE,
|
|
(
|
|
_event,
|
|
{
|
|
bookId,
|
|
chapterId,
|
|
title,
|
|
content,
|
|
status,
|
|
cursorOffset
|
|
}: {
|
|
bookId: string
|
|
chapterId: string
|
|
title?: string
|
|
content?: string
|
|
status?: ChapterStatus
|
|
cursorOffset?: number
|
|
}
|
|
) =>
|
|
wrap(() => {
|
|
const chapter = registry.getChapterRepo(bookId).update(chapterId, {
|
|
title,
|
|
content,
|
|
status,
|
|
cursorOffset
|
|
})
|
|
ftsSync.upsert(
|
|
registry.getDb(bookId),
|
|
'chapter',
|
|
chapter.id,
|
|
chapter.title,
|
|
chapter.content
|
|
)
|
|
registry.updateMeta(bookId, { lastChapterId: chapterId })
|
|
return chapter
|
|
})
|
|
)
|
|
|
|
ipcMain.handle(
|
|
IPC.CHAPTER_DELETE,
|
|
(_event, { bookId, chapterId }: { bookId: string; chapterId: string }) =>
|
|
wrap(() => {
|
|
registry.getChapterRepo(bookId).delete(chapterId)
|
|
ftsSync.remove(registry.getDb(bookId), 'chapter', chapterId)
|
|
})
|
|
)
|
|
|
|
ipcMain.handle(
|
|
IPC.CHAPTER_REORDER,
|
|
(_event, { bookId, volumeId, orderedIds }: { bookId: string; volumeId: string; orderedIds: string[] }) =>
|
|
wrap(() => registry.getChapterRepo(bookId).reorderVolume(volumeId, orderedIds))
|
|
)
|
|
|
|
ipcMain.handle(
|
|
IPC.CHAPTER_MOVE_TO_VOLUME,
|
|
(
|
|
_event,
|
|
{
|
|
bookId,
|
|
chapterId,
|
|
targetVolumeId,
|
|
targetIndex
|
|
}: { bookId: string; chapterId: string; targetVolumeId: string; targetIndex: number }
|
|
) =>
|
|
wrap(() => registry.getChapterRepo(bookId).moveToVolume(chapterId, targetVolumeId, targetIndex))
|
|
)
|
|
|
|
ipcMain.handle(
|
|
IPC.CHAPTER_SET_PUBLISH_STATUS,
|
|
(
|
|
_event,
|
|
{ bookId, chapterId, publishStatus }: { bookId: string; chapterId: string; publishStatus: PublishStatus }
|
|
) => wrap(() => registry.getChapterRepo(bookId).setPublishStatus(chapterId, publishStatus))
|
|
)
|
|
}
|