diff --git a/src/App.jsx b/src/App.jsx index cf2e5d0..007521f 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -135,6 +135,7 @@ export function migrateChunk(chunk) { episodeNumber: chunk.episodeNumber ?? '', episodeTitle: chunk.episodeTitle ?? '', finalScript: chunk.finalScript ?? '', + tags: chunk.tags ?? [], }; // already new format } return { @@ -143,6 +144,7 @@ export function migrateChunk(chunk) { interpretation: '', application: '', crossReferences: [], + tags: [], spilloverEndVerse: null, generalNotes: '', episodeNumber: '', @@ -261,11 +263,19 @@ function saveProjectToStorage(project) { window.localStorage.setItem(projectKey(updated.id), JSON.stringify(updated)); const index = loadProjectIndex(); const existing = index.findIndex((e) => e.id === updated.id); + const tags = Array.from( + new Set( + (updated.chapters ?? []) + .flatMap((ch) => ch.chunks ?? []) + .flatMap((chunk) => chunk.tags ?? []), + ), + ); const summary = { id: updated.id, title: updated.title, lastEdited: updated.lastEdited, chapterSummary: buildChapterSummary(updated), + tags, }; if (existing >= 0) { index[existing] = summary; @@ -855,10 +865,23 @@ const App = () => { const [suggestSelection, setSuggestSelection] = useState(new Set()); const [homeSearch, setHomeSearch] = useState(''); const [homeSort, setHomeSort] = useState('recent'); // 'recent' | 'title' | 'passage' + const [homeTagFilter, setHomeTagFilter] = useState(''); const [renamingId, setRenamingId] = useState(null); const [renameValue, setRenameValue] = useState(''); - const [studyLayout, setStudyLayout] = useState('stacked'); // 'stacked' | 'split' - const [activeStudyTab, setActiveStudyTab] = useState('notes'); + const [studyLayout, setStudyLayout] = useState( + () => localStorage.getItem('studyLayout') || 'stacked', + ); // 'stacked' | 'split' + const [activeStudyTab, setActiveStudyTab] = useState( + () => localStorage.getItem('activeStudyTab') || 'notes', + ); + + useEffect(() => { + localStorage.setItem('studyLayout', studyLayout); + }, [studyLayout]); + + useEffect(() => { + localStorage.setItem('activeStudyTab', activeStudyTab); + }, [activeStudyTab]); const [verseSearch, setVerseSearch] = useState(''); const [collapsedSections, setCollapsedSections] = useState({}); const [commentarySource, setCommentarySource] = useState('matthew-henry'); @@ -943,6 +966,7 @@ const App = () => { useEffect(() => { setCrossRefSuggestions([]); + setTagInput(''); }, [selectedChunk?.id]); const selectedChunkChapter = selectedChunkChapterIndex >= 0 ? project.chapters[selectedChunkChapterIndex] @@ -1159,6 +1183,7 @@ const App = () => { episodeNumber: '', episodeTitle: '', finalScript: '', + tags: [], }; return { ...ch, chunks: [...ch.chunks, newChunk] }; }); @@ -1218,6 +1243,7 @@ const App = () => { application: '', crossReferences: [], greekWords: [], + tags: [], }; existing.add(key); newChunks.push(newChunk); @@ -1499,6 +1525,26 @@ const App = () => { }); }; + const [tagInput, setTagInput] = useState(''); + + const addTag = (chunkId) => { + const tag = tagInput.trim(); + if (!tag) return; + const existing = selectedChunk?.tags ?? []; + if (existing.some((t) => t.toLowerCase() === tag.toLowerCase())) { + setTagInput(''); + return; + } + updateChunk(chunkId, { tags: [...existing, tag] }); + setTagInput(''); + }; + + const removeTag = (chunkId, tag) => { + updateChunk(chunkId, { + tags: (selectedChunk?.tags ?? []).filter((t) => t !== tag), + }); + }; + const [suggestingCrossRefs, setSuggestingCrossRefs] = useState(false); const [crossRefSuggestions, setCrossRefSuggestions] = useState([]); const _crossRefDatasetCacheRef = useRef({}); @@ -2490,11 +2536,33 @@ const restoreRemoteProject = async (id) => { + {(() => { + const allTags = Array.from( + new Set(projectIndex.flatMap((entry) => entry.tags ?? [])), + ).sort((a, b) => a.localeCompare(b)); + if (allTags.length === 0) return null; + return ( + + ); + })()}
{entry.chapterSummary}
)} + {entry.tags?.length > 0 && ( +{formatRelativeDate(entry.lastEdited)}
Label this chunk by topic so you can search across studies later.
+