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:
@@ -25,6 +25,9 @@ import { LandmarkModal } from '@renderer/components/landmark/LandmarkModal'
|
||||
import { CockpitModal } from '@renderer/components/cockpit/CockpitModal'
|
||||
import { CharacterGraphModal } from '@renderer/components/graph/CharacterGraphModal'
|
||||
import { ChapterBridgeModal } from '@renderer/components/bridge/ChapterBridgeModal'
|
||||
import { TemplateQuickCreateModal } from '@renderer/components/chapter/TemplateQuickCreateModal'
|
||||
import { ExportModal } from '@renderer/components/export/ExportModal'
|
||||
import { useExportStore } from '@renderer/stores/useExportStore'
|
||||
import {
|
||||
editorDirtyAtom,
|
||||
editorSavingAtom,
|
||||
@@ -117,6 +120,7 @@ export function EditorLayout(): React.JSX.Element {
|
||||
const documentTitle = useDocumentTitle()
|
||||
|
||||
const [bridgeOpen, setBridgeOpen] = useState(false)
|
||||
const [templateModalOpen, setTemplateModalOpen] = useState(false)
|
||||
const [cockpitSummary, setCockpitSummary] = useState<CockpitSummary | null>(null)
|
||||
|
||||
const bookMeta = useBookStore.getState().books.find((b) => b.id === currentBookId)
|
||||
@@ -148,10 +152,10 @@ export function EditorLayout(): React.JSX.Element {
|
||||
}, [currentBookId, pomodoroRunning, pomodoroBookId, pomodoroCancel])
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedChapterId && (!target || target.kind === 'chapter')) {
|
||||
setTarget({ kind: 'chapter', id: selectedChapterId })
|
||||
}
|
||||
}, [selectedChapterId, setTarget, target])
|
||||
if (!selectedChapterId) return
|
||||
if (target?.kind === 'chapter' && target.id === selectedChapterId) return
|
||||
setTarget({ kind: 'chapter', id: selectedChapterId })
|
||||
}, [selectedChapterId, setTarget, target?.kind, target?.id])
|
||||
|
||||
useEffect(() => {
|
||||
if (!currentBookId || target?.kind !== 'chapter') return
|
||||
@@ -405,6 +409,14 @@ export function EditorLayout(): React.JSX.Element {
|
||||
<button type="button" onClick={() => void handleNewChapter()}>
|
||||
+ {t('editor.newChapter')}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
data-testid="chapter-quick-new"
|
||||
style={{ marginTop: 6 }}
|
||||
onClick={() => setTemplateModalOpen(true)}
|
||||
>
|
||||
+ {t('template.quickNew')}
|
||||
</button>
|
||||
<button type="button" style={{ marginTop: 6 }} onClick={() => void handleNewVolume()}>
|
||||
+ {t('editor.newVolume')}
|
||||
</button>
|
||||
@@ -427,6 +439,16 @@ export function EditorLayout(): React.JSX.Element {
|
||||
) : (
|
||||
<>
|
||||
<div id="editor-toolbar">
|
||||
<button
|
||||
type="button"
|
||||
className="tool-btn"
|
||||
title={t('export.title')}
|
||||
data-testid="open-export"
|
||||
disabled={!isChapterTarget}
|
||||
onClick={() => useExportStore.getState().openModal()}
|
||||
>
|
||||
📦
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="tool-btn"
|
||||
@@ -572,6 +594,11 @@ export function EditorLayout(): React.JSX.Element {
|
||||
clearBridgeChapter()
|
||||
}}
|
||||
/>
|
||||
<TemplateQuickCreateModal
|
||||
open={templateModalOpen}
|
||||
onClose={() => setTemplateModalOpen(false)}
|
||||
/>
|
||||
<ExportModal />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user