feat: ship v1.2.0 Wave 1 foundation with book settings, chapter management, and focus mode
Deliver schema v9, book/chapter IPC extensions, BookSettingsTab, chapter meta/tags, AI session menu, focus mode and TTS, template context enrich, and Wave 1 E2E coverage. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -19,7 +19,7 @@ interface AiStore {
|
||||
listenersAttached: boolean
|
||||
mount: () => void
|
||||
unmount: () => void
|
||||
loadSessions: (bookId: string) => Promise<void>
|
||||
loadSessions: (bookId: string, includeArchived?: boolean) => Promise<void>
|
||||
selectSession: (sessionId: string) => Promise<void>
|
||||
createSession: (bookId: string) => Promise<void>
|
||||
sendMessage: (content: string, prompt?: string) => Promise<void>
|
||||
@@ -63,8 +63,10 @@ export const useAiStore = create<AiStore>((set, get) => ({
|
||||
set({ listenersAttached: false })
|
||||
},
|
||||
|
||||
loadSessions: async (bookId) => {
|
||||
const sessions = await ipcCall(() => window.electronAPI.ai.sessionList(bookId))
|
||||
loadSessions: async (bookId, includeArchived = true) => {
|
||||
const sessions = await ipcCall(() =>
|
||||
window.electronAPI.ai.sessionList(bookId, includeArchived)
|
||||
)
|
||||
const activeSessionId = get().activeSessionId
|
||||
const stillActive = activeSessionId && sessions.some((s) => s.id === activeSessionId)
|
||||
set({ sessions })
|
||||
@@ -157,7 +159,7 @@ export const useAiStore = create<AiStore>((set, get) => ({
|
||||
try {
|
||||
await ipcCall(() => window.electronAPI.ai.chat(bookId, sessionId, trimmed, prompt))
|
||||
const messages = await ipcCall(() => window.electronAPI.ai.messageList(bookId, sessionId))
|
||||
const sessions = await ipcCall(() => window.electronAPI.ai.sessionList(bookId))
|
||||
const sessions = await ipcCall(() => window.electronAPI.ai.sessionList(bookId, true))
|
||||
set({ messages, sessions, streamingMessageId: null, streamingBuffer: '' })
|
||||
} finally {
|
||||
set({ sending: false })
|
||||
|
||||
@@ -10,6 +10,8 @@ interface AppState {
|
||||
landmarksModalOpen: boolean
|
||||
inspirationModalOpen: boolean
|
||||
importBookModalOpen: boolean
|
||||
focusMode: boolean
|
||||
namingCheckOpen: boolean
|
||||
toast: string | null
|
||||
setView: (view: AppView) => void
|
||||
setSidebarPanel: (panel: AppState['sidebarPanel']) => void
|
||||
@@ -18,6 +20,8 @@ interface AppState {
|
||||
setLandmarksModalOpen: (open: boolean) => void
|
||||
setInspirationModalOpen: (open: boolean) => void
|
||||
setImportBookModalOpen: (open: boolean) => void
|
||||
setFocusMode: (on: boolean) => void
|
||||
setNamingCheckOpen: (open: boolean) => void
|
||||
showToast: (message: string) => void
|
||||
clearToast: () => void
|
||||
}
|
||||
@@ -30,6 +34,8 @@ export const useAppStore = create<AppState>((set) => ({
|
||||
landmarksModalOpen: false,
|
||||
inspirationModalOpen: false,
|
||||
importBookModalOpen: false,
|
||||
focusMode: false,
|
||||
namingCheckOpen: false,
|
||||
toast: null,
|
||||
setView: (view) => set({ view }),
|
||||
setSidebarPanel: (sidebarPanel) => set({ sidebarPanel }),
|
||||
@@ -38,6 +44,8 @@ export const useAppStore = create<AppState>((set) => ({
|
||||
setLandmarksModalOpen: (landmarksModalOpen) => set({ landmarksModalOpen }),
|
||||
setInspirationModalOpen: (inspirationModalOpen) => set({ inspirationModalOpen }),
|
||||
setImportBookModalOpen: (importBookModalOpen) => set({ importBookModalOpen }),
|
||||
setFocusMode: (focusMode) => set({ focusMode }),
|
||||
setNamingCheckOpen: (namingCheckOpen) => set({ namingCheckOpen }),
|
||||
showToast: (toast) => {
|
||||
set({ toast })
|
||||
setTimeout(() => set({ toast: null }), 2500)
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
import { create } from 'zustand'
|
||||
import { ipcCall } from '@renderer/lib/ipc-client'
|
||||
|
||||
interface BookPrefsState {
|
||||
alwaysShowCockpit: boolean
|
||||
loadedBookId: string | null
|
||||
load: (bookId: string) => Promise<void>
|
||||
setAlwaysShowCockpit: (bookId: string, value: boolean) => Promise<void>
|
||||
}
|
||||
|
||||
export const useBookPrefsStore = create<BookPrefsState>((set, get) => ({
|
||||
alwaysShowCockpit: false,
|
||||
loadedBookId: null,
|
||||
load: async (bookId) => {
|
||||
const value = await ipcCall(() =>
|
||||
window.electronAPI.bookPrefs.get(bookId, 'alwaysShowCockpit')
|
||||
)
|
||||
set({ alwaysShowCockpit: value === 'true', loadedBookId: bookId })
|
||||
},
|
||||
setAlwaysShowCockpit: async (bookId, value) => {
|
||||
await ipcCall(() =>
|
||||
window.electronAPI.bookPrefs.set(bookId, 'alwaysShowCockpit', value ? 'true' : 'false')
|
||||
)
|
||||
if (get().loadedBookId === bookId) {
|
||||
set({ alwaysShowCockpit: value })
|
||||
}
|
||||
}
|
||||
}))
|
||||
Reference in New Issue
Block a user