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

+29
View File
@@ -468,6 +468,35 @@ describe('Study workspace', () => {
expect(screen.queryByText(/is this from ai\?/i)).not.toBeInTheDocument();
});
test('a note keeps earlier versions that can be restored', async () => {
const user = await studyOneChunk();
const observe = screen.getByRole('textbox', { name: 'Observe' });
fireEvent.focus(observe);
fireEvent.change(observe, { target: { value: 'First thought about the passage.' } });
fireEvent.blur(observe);
fireEvent.focus(observe);
fireEvent.change(observe, { target: { value: 'Completely rewritten.' } });
fireEvent.blur(observe);
await user.click(screen.getByRole('button', { name: 'History' }));
const list = screen.getByRole('list', { name: /observe history/i });
expect(within(list).getByText(/first thought about the passage/i)).toBeInTheDocument();
await user.click(within(list).getAllByRole('button', { name: 'Restore' }).find((b) => !b.disabled));
expect(screen.getByRole('textbox', { name: 'Observe' })).toHaveValue('First thought about the passage.');
});
test('keyboard: 1 / 2 / 3 jump into a note, Esc leaves it, and V opens Compare', async () => {
const user = await studyOneChunk();
await user.keyboard('2');
expect(screen.getByRole('textbox', { name: 'Interpret' })).toHaveFocus();
await user.keyboard('{Escape}');
expect(screen.getByRole('textbox', { name: 'Interpret' })).not.toHaveFocus();
await user.keyboard('3');
expect(screen.getByRole('textbox', { name: 'Apply' })).toHaveFocus();
await user.keyboard('{Escape}');
await user.keyboard('v');
expect(screen.getByRole('dialog', { name: /compare/i })).toBeInTheDocument();
});
test('compare translations: the Observe / Interpret / Apply boxes edit the same notes', async () => {
const user = await studyOneChunk();
await user.click(screen.getByRole('button', { name: /compare…/i }));