feat(w4): ship v1.5.0 timeline, character arc, and compliance

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-08 17:40:42 +08:00
parent c5c0b7329b
commit cb6b4c3731
51 changed files with 1528 additions and 42 deletions
@@ -126,6 +126,7 @@ export function EditorLayout(): React.JSX.Element {
const [templateModalOpen, setTemplateModalOpen] = useState(false)
const [chapterMetaOpen, setChapterMetaOpen] = useState(false)
const openReadMode = useReadModeStore((s) => s.openForVolume)
const setTimelineModalOpen = useAppStore((s) => s.setTimelineModalOpen)
const [volumeMenu, setVolumeMenu] = useState<{ volumeId: string; x: number; y: number } | null>(
null
)
@@ -163,6 +164,13 @@ export function EditorLayout(): React.JSX.Element {
useEffect(() => {
if (!selectedChapterId) return
if (
target?.kind === 'setting' ||
target?.kind === 'outline' ||
target?.kind === 'inspiration'
) {
return
}
if (target?.kind === 'chapter' && target.id === selectedChapterId) return
setTarget({ kind: 'chapter', id: selectedChapterId })
}, [selectedChapterId, setTarget, target?.kind, target?.id])
@@ -405,6 +413,11 @@ export function EditorLayout(): React.JSX.Element {
className={`chapter-item ${target?.kind === 'chapter' && target.id === ch.id ? 'active' : ''} ${dragOverChapterId === ch.id ? 'drag-over' : ''}`}
draggable
data-testid={`chapter-item-${ch.id}`}
title={
ch.summary?.trim() ||
ch.content.replace(/<[^>]+>/g, '').trim().slice(0, 80) ||
undefined
}
onDragStart={(e) => {
e.dataTransfer.setData('application/x-bilin-chapter', ch.id)
e.dataTransfer.effectAllowed = 'move'
@@ -499,6 +512,14 @@ export function EditorLayout(): React.JSX.Element {
>
+ {t('template.quickNew')}
</button>
<button
type="button"
data-testid="open-timeline"
style={{ marginTop: 6 }}
onClick={() => setTimelineModalOpen(true)}
>
🕐 {t('timeline.open')}
</button>
<button
type="button"
data-testid="chapter-read-mode"