diff --git a/CHANGELOG.md b/CHANGELOG.md index eb205f46..16cf3df4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,16 @@ ## [Unreleased] +### Phase 6 — Study Buddy wired into the reader (2026-06-15) + +Phase 6 **AI-038, slice b** — the panel is now reachable: select a passage in the reader → "Help me understand this" → the agent investigates live. + +- **`SelectionToolbar`** gains a **"Help me understand this"** action (sparkles icon) next to Explain, shown only when `onStudyBuddy` is wired (catalog editions). +- **`ReaderHighlights`** passes the whole selected passage up via a new `onStudyBuddy(passage)` prop and clears the selection. +- **`ReaderPage`** holds the passage state and renders `StudyBuddyPanel` (catalog editions only, like Ask), threading the **current chapter number** so the agent's chapter tools have context. Opening it for a new passage re-runs. +- i18n: `reader.selectionToolbar.studyBuddy`. +- Verified: `tsc` + `pnpm build` clean; full web suite green (517); no e2e clicks the selection toolbar positionally (no index drift). The live agent run is exercised on prod (key + corpus). + ### Phase 6 — Study Buddy web panel (2026-06-15) Phase 6 **AI-038, slice a** — the web UI for the agent: an `api` client, a streaming hook, and the panel. Wiring it into the reader's selection toolbar is slice b. diff --git a/apps/web/src/components/reader/ReaderHighlights.tsx b/apps/web/src/components/reader/ReaderHighlights.tsx index 041cdef6..ac835404 100644 --- a/apps/web/src/components/reader/ReaderHighlights.tsx +++ b/apps/web/src/components/reader/ReaderHighlights.tsx @@ -37,6 +37,8 @@ interface ReaderHighlightsProps { ttsSpeed?: number scrollToHighlightId?: string | null showInlineTranslations?: boolean + /** Open the Study Buddy panel for a highlighted passage (AI-038b). Catalog editions only. */ + onStudyBuddy?: (passage: string) => void children: React.ReactNode } @@ -67,6 +69,7 @@ export function ReaderHighlights({ ttsSpeed = 1.0, scrollToHighlightId, showInlineTranslations = false, + onStudyBuddy, children, }: ReaderHighlightsProps) { const { nativeLanguage, setNativeLanguage, hasConfirmedLanguage } = useNativeLanguage() @@ -401,6 +404,14 @@ export function ReaderHighlights({ explainPopup.openFromSelection(selection.text, selection.range, selection.rect) }, [explainPopup, selection.text, selection.range, selection.rect]) + // --- Study Buddy: hand the whole selected passage up to the reader's panel --- + const handleStudyBuddy = useCallback(() => { + const passage = selection.text?.trim() + if (!passage || !onStudyBuddy) return + onStudyBuddy(passage) + clearSelection() + }, [selection.text, onStudyBuddy, clearSelection]) + // --- Selection toolbar --- const handleHighlight = useCallback( async (color: HighlightColor) => { @@ -462,6 +473,7 @@ export function ReaderHighlights({ onHighlight={handleHighlight} onTranslate={handleTranslate} onExplain={handleExplain} + onStudyBuddy={onStudyBuddy ? handleStudyBuddy : undefined} onSpeak={() => handleSpeak(selection.text)} onCopy={handleCopy} /> diff --git a/apps/web/src/components/reader/SelectionToolbar.tsx b/apps/web/src/components/reader/SelectionToolbar.tsx index 60bd1b28..49d6a66a 100644 --- a/apps/web/src/components/reader/SelectionToolbar.tsx +++ b/apps/web/src/components/reader/SelectionToolbar.tsx @@ -16,6 +16,7 @@ interface SelectionToolbarProps { onHighlight: (color: HighlightColor) => void onTranslate?: () => void onExplain?: () => void + onStudyBuddy?: () => void onSpeak?: () => void onCopy?: () => void } @@ -27,6 +28,7 @@ export function SelectionToolbar({ onHighlight, onTranslate, onExplain, + onStudyBuddy, onSpeak, onCopy, }: SelectionToolbarProps) { @@ -130,6 +132,18 @@ export function SelectionToolbar({ )} + {onStudyBuddy && ( + + )} {onSpeak && text.trim().length <= 500 && (