feat: ship v0.3.0 with P2.1 editor polish and P3 AI collaboration
Add chapter reorder, search replace, inspiration capture, and AI chat with context editing, settings, offline handling, and naming checks. Fix dev black screen by keeping process.env reads in the main process only. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+23
-10
@@ -16,10 +16,12 @@ import { useTabStore } from '@renderer/stores/useTabStore'
|
||||
import { useBookStore } from '@renderer/stores/useBookStore'
|
||||
import { useEditStore } from '@renderer/stores/useEditStore'
|
||||
import { SearchModal } from '@renderer/components/search/SearchModal'
|
||||
import { InspirationModal } from '@renderer/components/inspiration/InspirationModal'
|
||||
import { useSearchStore } from '@renderer/stores/useSearchStore'
|
||||
import { useReferenceStore } from '@renderer/stores/useReferenceStore'
|
||||
import { flushEditorSave } from '@renderer/components/editor/TipTapEditor'
|
||||
import { insertLandmarkInEditor } from '@renderer/lib/editor-commands'
|
||||
import { useAiStore } from '@renderer/stores/useAiStore'
|
||||
import { ipcCall } from '@renderer/lib/ipc-client'
|
||||
|
||||
function AppInner(): React.JSX.Element {
|
||||
@@ -48,6 +50,11 @@ function AppInner(): React.JSX.Element {
|
||||
useEffect(() => {
|
||||
const openSearch = (): void => useSearchStore.getState().setOpen(true)
|
||||
const openLandmarks = (): void => useAppStore.getState().setLandmarksModalOpen(true)
|
||||
const openInspiration = (): void => {
|
||||
if (useAppStore.getState().view !== 'editor') return
|
||||
if (!useBookStore.getState().currentBookId) return
|
||||
useAppStore.getState().setInspirationModalOpen(true)
|
||||
}
|
||||
const onInsertLandmark = (e: Event): void => {
|
||||
const label = (e as CustomEvent<{ label: string }>).detail?.label
|
||||
if (!label) return
|
||||
@@ -59,13 +66,26 @@ function AppInner(): React.JSX.Element {
|
||||
window.electronAPI.bookmark.create(currentBookId, target.id, 0, label, 'todo')
|
||||
)
|
||||
}
|
||||
const onSetAiOnline = (e: Event): void => {
|
||||
const online = (e as CustomEvent<{ online: boolean }>).detail?.online
|
||||
if (typeof online === 'boolean') useAiStore.setState({ online })
|
||||
}
|
||||
const onFlushEditor = (): void => {
|
||||
void flushEditorSave()
|
||||
}
|
||||
window.addEventListener('bilin:e2e-open-search', openSearch)
|
||||
window.addEventListener('bilin:e2e-open-landmarks', openLandmarks)
|
||||
window.addEventListener('bilin:e2e-capture-inspiration', openInspiration)
|
||||
window.addEventListener('bilin:e2e-insert-landmark', onInsertLandmark)
|
||||
window.addEventListener('bilin:e2e-set-ai-online', onSetAiOnline)
|
||||
window.addEventListener('bilin:e2e-flush-editor', onFlushEditor)
|
||||
return () => {
|
||||
window.removeEventListener('bilin:e2e-open-search', openSearch)
|
||||
window.removeEventListener('bilin:e2e-open-landmarks', openLandmarks)
|
||||
window.removeEventListener('bilin:e2e-capture-inspiration', openInspiration)
|
||||
window.removeEventListener('bilin:e2e-insert-landmark', onInsertLandmark)
|
||||
window.removeEventListener('bilin:e2e-set-ai-online', onSetAiOnline)
|
||||
window.removeEventListener('bilin:e2e-flush-editor', onFlushEditor)
|
||||
}
|
||||
}, [])
|
||||
|
||||
@@ -123,16 +143,8 @@ function AppInner(): React.JSX.Element {
|
||||
}
|
||||
if (action === 'captureInspiration') {
|
||||
if (view !== 'editor') return
|
||||
const { currentBookId, addInspirationLocal } = useBookStore.getState()
|
||||
if (!currentBookId) return
|
||||
void (async () => {
|
||||
const item = await ipcCall(() =>
|
||||
window.electronAPI.inspiration.create(currentBookId, t('inspiration.newItem'))
|
||||
)
|
||||
addInspirationLocal(item)
|
||||
useAppStore.getState().setSidebarPanel('inspiration')
|
||||
await useEditStore.getState().switchTarget({ kind: 'inspiration', id: item.id })
|
||||
})()
|
||||
if (!useBookStore.getState().currentBookId) return
|
||||
useAppStore.getState().setInspirationModalOpen(true)
|
||||
return
|
||||
}
|
||||
showToast(t('feature.comingSoon'))
|
||||
@@ -154,6 +166,7 @@ function AppInner(): React.JSX.Element {
|
||||
</div>
|
||||
{showOnboarding && <OnboardingWizard onComplete={handleOnboardingComplete} />}
|
||||
<SearchModal />
|
||||
<InspirationModal />
|
||||
{toast && <div className="toast">{toast}</div>}
|
||||
</div>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user