App quality: offline badge, per-note history, keyboard study mode, notes import

- Study cards say whether the Bible text works offline, with a button to save
  the missing book (notes are always kept on the device).
- Each note keeps its last 12 versions with Restore (History under each note).
- Keyboard-only study: 1/2/3 write in Observe/Interpret/Apply, C/W/X/P switch
  resource tabs, V compares, Esc leaves a note box; listed under "?".
- Export > Import notes: bring Word/Markdown/text notes into the matching
  chunks without overwriting anything; can mark them as not your own.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
nmemmertandClaude Sonnet 5.5 committed 2026-10-14 13:01:11 -04:00
1 parent 648c3206df
commit 67645cf1df
14 files changed
+421 -7

No files matched your search

+15
View File
@@ -42,3 +42,18 @@ test('stops when told to', async () => {
await downloadChapters('BSB', bookRefs('PSA'), { signal: controller.signal, concurrency: 1 });
expect(fetchMock.mock.calls.length).toBeLessThan(5);
});
import { offlineStatus } from './offline.js';
test('a study works offline once every book in it is saved in its translation', () => {
const project = { translation: 'BSB', chapters: [{ book: 'John', bookAbbrev: 'JHN' }, { book: 'John', bookAbbrev: 'JHN' }, { book: 'Titus', bookAbbrev: 'TIT' }] };
expect(offlineStatus(project, { BSB: ['JHN', 'TIT'] })).toMatchObject({ state: 'ready', missing: [] });
const partial = offlineStatus(project, { BSB: ['JHN'] });
expect(partial.state).toBe('partial');
expect(partial.missing).toEqual([{ abbrev: 'TIT', name: 'Titus' }]);
expect(offlineStatus(project, { NET: ['JHN', 'TIT'] }).state).toBe('partial'); // saved in a different translation
});
test('licensed translations cannot be saved offline', () => {
expect(offlineStatus({ translation: 'ESV', chapters: [{ book: 'John', bookAbbrev: 'JHN' }] }, {}).state).toBe('licensed');
});