From 0c8a632cff192ac9e488af0677e8f6786c640745 Mon Sep 17 00:00:00 2001 From: nmemmert <67961292+nmemmert@users.noreply.github.com> Date: Wed, 14 Oct 2026 12:25:49 -0400 Subject: [PATCH] Add own-work tracking, Claude hand-off check, and weekly recall - Long pastes into Observe/Interpret/Apply are tagged as pasted; notes show Mine / N pasted and an own-words share. Pastes follow text through merges. - The "-> Claude" buttons on the Script stage ask first when a chunk lacks enough of the user's own (non-pasted) words. - Weekly recall card on Home: write a passage from memory, compare with notes, self-rate; recalls are saved on the chunk and synced. - Recall history on Home with an eight-week strip and the full list. Co-Authored-By: Claude Sonnet 5.5 --- src/components/RecallHistory.jsx | 47 ++++++++++++++++++ src/components/WeeklyRecall.jsx | 77 ++++++++++++++++++++++++++++++ src/components/study/NotesPane.jsx | 28 ++++++++++- src/hooks/useProjectLibrary.js | 27 +++++++++++ src/pages/HomePage.jsx | 4 ++ src/pages/ScriptPage.jsx | 32 +++++++++++-- src/utils/mergeStudies.js | 3 ++ src/utils/ownWork.js | 49 +++++++++++++++++++ src/utils/ownWork.test.js | 58 ++++++++++++++++++++++ src/utils/recall.js | 51 ++++++++++++++++++++ src/utils/recall.test.js | 58 ++++++++++++++++++++++ 11 files changed, 429 insertions(+), 5 deletions(-) create mode 100644 src/components/RecallHistory.jsx create mode 100644 src/components/WeeklyRecall.jsx create mode 100644 src/utils/ownWork.js create mode 100644 src/utils/ownWork.test.js create mode 100644 src/utils/recall.js create mode 100644 src/utils/recall.test.js diff --git a/src/components/RecallHistory.jsx b/src/components/RecallHistory.jsx new file mode 100644 index 0000000..0dfb618 --- /dev/null +++ b/src/components/RecallHistory.jsx @@ -0,0 +1,47 @@ +// Home: how your weekly recalls have gone — the last eight weeks at a glance, then every recall you've written. +import { useMemo, useState } from 'react'; +import { useApp } from '../context/AppContext.js'; +import { loadProjectById } from '../utils/projectStorage.js'; +import { recallHistory, recallSummary } from '../utils/recall.js'; + +const LABEL = { solid: 'Had it', partly: 'Partly', forgot: 'Slipped' }; +const DOT = { solid: 'bg-[var(--r-accent)] border-[var(--r-accent)]', partly: 'bg-[var(--r-hl)] border-[var(--r-accent)]', forgot: 'border-[var(--r-sub)]' }; + +export default function RecallHistory() { + const { projectIndex, openStudyChunk } = useApp(); + const [open, setOpen] = useState(false); + const history = useMemo(() => recallHistory(projectIndex.map((e) => loadProjectById(e.id)).filter(Boolean)), [projectIndex]); + if (history.length === 0) return null; + const { counts, total, recent } = recallSummary(history); + + return ( +
+ + {open && ( + + )} +
+ ); +} diff --git a/src/components/WeeklyRecall.jsx b/src/components/WeeklyRecall.jsx new file mode 100644 index 0000000..7a30379 --- /dev/null +++ b/src/components/WeeklyRecall.jsx @@ -0,0 +1,77 @@ +// Home card: once a week, write what you remember of a passage you studied, then compare it with your notes. +import { useMemo, useState } from 'react'; +import { useApp } from '../context/AppContext.js'; +import { loadProjectById } from '../utils/projectStorage.js'; +import { pickRecall } from '../utils/recall.js'; + +const RATE = [ + { id: 'solid', label: 'I had it' }, + { id: 'partly', label: 'Partly' }, + { id: 'forgot', label: 'Most of it slipped' }, +]; +const box = 'mb-4 rounded-xl border border-[var(--r-line)] bg-[var(--r-bar)] px-4 py-3 text-sm'; +const noteLabels = [['observation', 'Observe'], ['interpretation', 'Interpret'], ['application', 'Apply']]; + +export default function WeeklyRecall() { + const { projectIndex, saveChunkRecall, openStudyChunk } = useApp(); + const [step, setStep] = useState('card'); // card -> write -> compare -> done + const [text, setText] = useState(''); + const pick = useMemo( + () => pickRecall(projectIndex.map((e) => loadProjectById(e.id)).filter(Boolean)), + [projectIndex], + ); + if (step === 'done') return
Recall saved. See you next week.
; + if (!pick) return null; + + const finish = (rating) => { + saveChunkRecall(pick.projectId, pick.chunk.id, { at: Date.now(), text: text.trim(), rating }); + setStep('done'); + }; + + if (step === 'card') { + return ( +
+ Weekly recall: {pick.reference} · {pick.projectTitle} + +
+ ); + } + return ( +
+

{pick.reference} · {pick.projectTitle}

+ {step === 'write' ? ( + <> +

From memory, no peeking: what did this passage say, what did it mean, and what did it ask of you?

+