import { useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import type { AiContextBinding } from '@shared/types' import { useBookStore } from '@renderer/stores/useBookStore' import { useAiStore } from '@renderer/stores/useAiStore' import { ipcCall } from '@renderer/lib/ipc-client' import { formatContextSummary } from '@renderer/lib/ai-context-summary' import i18n from '@renderer/i18n' type ContextTab = 'chapter' | 'outline' | 'setting' | 'inspiration' interface ContextEditorModalProps { open: boolean onClose: () => void } const EMPTY_BINDING: AiContextBinding = { chapterIds: [], outlineIds: [], settingIds: [], inspirationIds: [] } export function ContextEditorModal({ open, onClose }: ContextEditorModalProps): React.JSX.Element | null { const { t } = useTranslation() const bookId = useBookStore((s) => s.currentBookId) const chapters = useBookStore((s) => s.chapters) const outlines = useBookStore((s) => s.outlines) const settings = useBookStore((s) => s.settings) const inspirations = useBookStore((s) => s.inspirations) const activeSessionId = useAiStore((s) => s.activeSessionId) const sessions = useAiStore((s) => s.sessions) const [tab, setTab] = useState('setting') const [binding, setBinding] = useState(EMPTY_BINDING) useEffect(() => { if (!open) return const session = sessions.find((s) => s.id === activeSessionId) setBinding(session?.context ?? EMPTY_BINDING) }, [open, activeSessionId, sessions]) const toggle = (kind: ContextTab, id: string): void => { const key = kind === 'chapter' ? 'chapterIds' : kind === 'outline' ? 'outlineIds' : kind === 'setting' ? 'settingIds' : 'inspirationIds' setBinding((prev) => { const list = prev[key] return { ...prev, [key]: list.includes(id) ? list.filter((x) => x !== id) : [...list, id] } }) } const handleSave = async (): Promise => { if (!bookId || !activeSessionId) return const updated = await ipcCall(() => window.electronAPI.ai.sessionUpdate(bookId, activeSessionId, { context: binding }) ) useAiStore.setState((s) => ({ sessions: s.sessions.map((x) => (x.id === updated.id ? updated : x)) })) await useAiStore.getState().refreshContextPreview() onClose() } const handleRefresh = async (): Promise => { if (!bookId) return const result = await ipcCall(() => window.electronAPI.ai.buildContext(bookId, binding)) useAiStore.setState({ contextPreview: result.preview, contextSummary: formatContextSummary(result.preview, i18n.t) }) } if (!open) return null const items = tab === 'chapter' ? chapters.map((c) => ({ id: c.id, title: c.title })) : tab === 'outline' ? outlines.map((o) => ({ id: o.id, title: o.title })) : tab === 'setting' ? settings.map((s) => ({ id: s.id, title: s.name })) : inspirations.map((i) => ({ id: i.id, title: i.title || t('inspiration.untitled') })) const selectedIds = tab === 'chapter' ? binding.chapterIds : tab === 'outline' ? binding.outlineIds : tab === 'setting' ? binding.settingIds : binding.inspirationIds return (

{t('ai.contextEditorTitle')}

{(['chapter', 'outline', 'setting', 'inspiration'] as const).map((id) => ( ))}
{items.length === 0 &&
{t('ai.contextEmptyList')}
} {items.map((item) => ( ))}
) }