feat: ship v0.4.0 with interactive writing mode

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-06 17:46:16 +08:00
parent 0a054606fe
commit a8e0ba9ac9
39 changed files with 2113 additions and 89 deletions
+61
View File
@@ -17,6 +17,11 @@ import type {
NamingConflict,
AiStreamChunkEvent,
AiNetworkStatusEvent,
InteractiveFlow,
InteractiveGateResult,
InteractiveStreamChunkEvent,
PlotOption,
PlotSelection,
IpcResult,
LandmarkType,
OutlineItem,
@@ -264,6 +269,55 @@ const electronAPI = {
namingCheck: (bookId: string): Promise<IpcResult<NamingConflict[]>> =>
ipcRenderer.invoke(IPC.AI_NAMING_CHECK, { bookId })
},
interactive: {
checkGate: (bookId: string): Promise<IpcResult<InteractiveGateResult>> =>
ipcRenderer.invoke(IPC.INTERACTIVE_CHECK_GATE, { bookId }),
getFlow: (bookId: string, sessionId: string): Promise<IpcResult<InteractiveFlow | null>> =>
ipcRenderer.invoke(IPC.INTERACTIVE_GET_FLOW, { bookId, sessionId }),
start: (bookId: string, sessionId: string): Promise<IpcResult<InteractiveFlow>> =>
ipcRenderer.invoke(IPC.INTERACTIVE_START, { bookId, sessionId }),
confirmContext: (
bookId: string,
flowId: string,
binding: AiContextBinding
): Promise<IpcResult<InteractiveFlow>> =>
ipcRenderer.invoke(IPC.INTERACTIVE_CONFIRM_CONTEXT, { bookId, flowId, binding }),
suggestPlots: (bookId: string, flowId: string): Promise<IpcResult<PlotOption[]>> =>
ipcRenderer.invoke(IPC.INTERACTIVE_SUGGEST_PLOTS, { bookId, flowId }),
selectPlot: (
bookId: string,
flowId: string,
selection: PlotSelection
): Promise<IpcResult<InteractiveFlow>> =>
ipcRenderer.invoke(IPC.INTERACTIVE_SELECT_PLOT, { bookId, flowId, selection }),
generateScene: (bookId: string, flowId: string): Promise<IpcResult<InteractiveFlow>> =>
ipcRenderer.invoke(IPC.INTERACTIVE_GENERATE_SCENE, { bookId, flowId }),
resolveNaming: (
bookId: string,
flowId: string,
chosenName: string
): Promise<IpcResult<InteractiveFlow>> =>
ipcRenderer.invoke(IPC.INTERACTIVE_RESOLVE_NAMING, { bookId, flowId, chosenName }),
refineScene: (
bookId: string,
flowId: string,
instruction: string
): Promise<IpcResult<InteractiveFlow>> =>
ipcRenderer.invoke(IPC.INTERACTIVE_REFINE_SCENE, { bookId, flowId, instruction }),
acceptScene: (bookId: string, flowId: string): Promise<IpcResult<InteractiveFlow>> =>
ipcRenderer.invoke(IPC.INTERACTIVE_ACCEPT_SCENE, { bookId, flowId }),
finishChapter: (
bookId: string,
flowId: string,
volumeId: string,
title?: string
): Promise<IpcResult<{ chapterId: string }>> =>
ipcRenderer.invoke(IPC.INTERACTIVE_FINISH_CHAPTER, { bookId, flowId, volumeId, title }),
abort: (flowId: string): Promise<IpcResult<void>> =>
ipcRenderer.invoke(IPC.INTERACTIVE_ABORT, { flowId }),
getSceneDraft: (bookId: string, flowId: string): Promise<IpcResult<string>> =>
ipcRenderer.invoke(IPC.INTERACTIVE_GET_SCENE_DRAFT, { bookId, flowId })
},
wordfreq: {
analyze: (
bookId: string,
@@ -297,6 +351,13 @@ const electronAPI = {
}
ipcRenderer.on(IPC.AI_NETWORK_STATUS, handler)
return () => ipcRenderer.removeListener(IPC.AI_NETWORK_STATUS, handler)
},
onInteractiveStreamChunk: (callback: (payload: InteractiveStreamChunkEvent) => void): (() => void) => {
const handler = (_event: IpcRendererEvent, payload: InteractiveStreamChunkEvent) => {
callback(payload)
}
ipcRenderer.on(IPC.INTERACTIVE_STREAM_CHUNK, handler)
return () => ipcRenderer.removeListener(IPC.INTERACTIVE_STREAM_CHUNK, handler)
}
}