// 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(', ')}
  • ))}
)}