// The Script stage: for each chunk (a session, episode or lesson), name it and
// write the final script beside your study notes. Claude prompts and the
// pronunciation guide live here because this is where they're needed.
import { useState } from 'react';
import PresentMode from '../components/PresentMode.jsx';
import DictateButton, { appendSpoken } from '../components/DictateButton.jsx';
import { useIqAvailable } from '../hooks/useIq.js';
import { chunkRefs, gatherIqContext } from '../utils/iqbible.js';
import { useApp } from '../context/AppContext.js';
import { useToast } from '../components/Toasts.jsx';
import StudioShell from '../components/study/StudioShell.jsx';
import { formatChunkReference } from '../utils/bibleUtils.jsx';
import { buildClaudePrompt, buildPodcastPrompt } from '../utils/exportBuilders.js';
import { chunksNeedingWork } from '../utils/ownWork.js';
import { buildScriptDraft, spokenMinutes, wordCount } from '../utils/scriptDraft.js';
const has = (s) => (s ?? '').trim().length > 0;
const inputCls = 'w-full rounded-lg border border-[var(--r-line)] bg-[var(--r-bg)] px-2.5 py-1.5 text-sm text-[var(--r-text)] focus:outline-none focus:ring-2 focus:ring-[var(--r-line)]';
const btn = 'rounded-lg border border-[var(--r-line)] px-3 py-1.5 text-xs hover:bg-black/5 disabled:opacity-40';
export default function ScriptPage() {
const { project, allChunks, updateProject, updateChunk, authUser, setSaveStatus, setCurrentPage } = useApp();
const [notice, setNotice] = useState('');
const iq = useIqAvailable();
const [gathering, setGathering] = useState(false);
const [presenting, setPresenting] = useState(false);
const [heldAction, setHeldAction] = useState(null); // an AI hand-off waiting on the own-work check
const { toast } = useToast();
const chunk = allChunks.find((c) => c.id === project?.selectedChunkId) ?? allChunks[0] ?? null;
const chapterIndex = chunk ? project.chapters.findIndex((ch) => ch.chunks.some((c) => c.id === chunk.id)) : -1;
const reference = chunk ? formatChunkReference(project, chapterIndex, chunk, '–') : '';
const script = chunk?.finalScript ?? '';
const flash = (message) => { setNotice(message); window.setTimeout(() => setNotice(''), 2200); };
const select = (id) => updateProject((current) => ({ ...current, selectedChunkId: id }));
const set = (patch) => updateChunk(chunk.id, patch);
const append = (text) => set({ finalScript: script.trim() ? `${script.replace(/\s+$/, '')}\n\n${text}` : text });
// Replaces the script with an outline from your notes — and says how to get the old one back.
const draft = () => {
const before = script;
set({ finalScript: buildScriptDraft(chunk, reference) });
if (has(before)) toast('Script replaced with a fresh outline', { action: { label: 'Undo', onClick: () => set({ finalScript: before }) } });
};
const importDocx = async (file) => {
if (!file) return;
try {
const { default: mammoth } = await import('mammoth');
const { value } = await mammoth.extractRawText({ arrayBuffer: await file.arrayBuffer() });
set({ finalScript: value.trim() });
flash('Imported script from DOCX.');
} catch {
flash('Could not read that DOCX file.');
}
};
// AI hand-offs only go straight through once every chunk has your own notes; otherwise ask first.
const needingWork = chunksNeedingWork(allChunks);
const guarded = (action) => () => { if (needingWork.length === 0) action(); else setHeldAction(() => action); };
const goStudy = (id) => { select(id); setHeldAction(null); setCurrentPage('study'); };
// Copies a prompt, then opens claude.ai so it can be pasted straight in.
const copyPrompt = (prompt) => {
navigator.clipboard.writeText(prompt).then(() => {
setSaveStatus('Copied! Opening Claude…');
window.setTimeout(() => setSaveStatus(''), 3000);
window.open('https://claude.ai/new', '_blank', 'noopener,noreferrer');
}).catch(() => toast("Couldn't copy to the clipboard. Click the button again.", { tone: 'error' }));
};
// The podcast prompt plus IQ Bible's background on each passage (commentary, cross-references, word glosses).
const copyPodcastWithContext = async () => {
setGathering(true);
try {
const { text, count, skipped } = await gatherIqContext(project);
const base = buildPodcastPrompt(project, authUser?.podcastName);
const prompt = text ? `${base}\n\n---\nBACKGROUND REFERENCE (from IQ Bible; use it to inform the script, don't quote it at length):\n\n${text}\n` : base;
if (!text) toast('IQ Bible had no background to add; copied the standard prompt', { tone: 'error' });
else if (skipped > 0) toast(`Added background for ${count} passages (the first ${count} of ${count + skipped})`);
copyPrompt(prompt);
} finally {
setGathering(false);
}
};
const noteBlock = (label, text) => has(text) && (
{label}
{text}
);
return (
{chunk && (
{reference}
{wordCount(script)} words · about {spokenMinutes(script)} min spoken
{iq && (
)}
set({ finalScript: appendSpoken(script, said) })} />
{notice && {notice}}
{heldAction && (
Is this your study, or Claude's?
These chunks don't have much of your own thinking yet. Write it first, then let Claude polish it.
{needingWork.map(({ chunk: c, missing }) => (
-
{formatChunkReference(project, project.chapters.findIndex((ch) => ch.chunks.some((x) => x.id === c.id)), c, '–')}: {missing.map((m) => `${m.label} ${m.have}/${m.need}`).join(', ')}
))}
)}
)}
{presenting && setPresenting(false)} />}
);
}