feat(mobile): persistent Book Chat parity — history, markdown, quote-and-ask, spoiler toggle#443
Open
mrviduus wants to merge 3 commits into
Open
feat(mobile): persistent Book Chat parity — history, markdown, quote-and-ask, spoiler toggle#443mrviduus wants to merge 3 commits into
mrviduus wants to merge 3 commits into
Conversation
…r toggle, quote-and-ask - AskSheet rewritten onto /me/chat: server-owned history loads on open (skeleton), optimistic send + SSE stream (expo/fetch ReadableStream — RN fetch buffers; wire identical to legacy ask), JSON fallback, one-shot 401 refresh-retry - custom ~180-line RN markdown renderer (headings/bold/lists/code/fenced/blockquote; tables degrade to scrollable monospace) — no unmaintained dep, no Data-Safety delta - Spoiler-safe switch (subtle on user books) + Clear chat w/ confirm, optimistic+revert - quote-and-ask: selection bar 'Ask about this' -> detachable quote card -> shared codec - chatQuote codec moved to packages/shared (web re-exports; imports unchanged) - stream aborts when the sheet closes; citation chips + jump preserved - legacy /ask endpoints untouched (evals/fallback) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…eanup - SseUnsupportedError on ANY can't-stream shape (missing getReader/TextDecoder) -> JSON fallback - memo(AskMarkdown) + scroll animates only on new turns (no per-token re-animation) - close clears attached quote + typed draft (sheet stays mounted) - hard send failure reverts the optimistic turn (no ghost blank-answer question) - starters hidden while a quote is attached (can't silently drop it) - table separator requires '|' (pure isTableSeparator helper + 6 tests); stale comment fixed Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes the last big mobile tail: the Ask sheet moves onto the persistent
/me/chatendpoints — the same thread as web, surviving app restarts, inheriting gpt-4.1 tutor answers, rolling-summary memory, highlights-as-context, and chapter-summary retrieval for free.Key engineering
postSseoverexpo/fetch'sReadableStream(RN fetch buffers), byte-faithful port of the web parser (same wire as legacy ask), one-shot 401 refresh-retry via the existing single-flight (duplicate-safe — auth precedes any persist), JSON fallback on any can't-stream runtime shape.react-native-markdown-displayis unmaintained vs RN 0.83/new-arch; no raw-HTML path.chatQuotecodec moved topackages/shared(web re-exports; web tests pass through it)./askuntouched.Adversarial QA (core verified sound; fixed)
Narrow stream-fallback → all shapes; per-token re-render/scroll jank (
memo+ animate-only-on-new-turn); stale quote/draft after close; ghost blank-answer turns on hard failures; starters dropping an attached quote; table-detector false positive (+6 pure tests).Accepted: inline
[n]plain text on mobile (chips = jump surface); no-donestream settles as truncated-but-finished (web parity).Verify
mobile
tsc+ 70 vitest · shared 209 (8 new codec) · webtsc+ 730 vitest (re-export exercised). JS-only → ships via OTA. On-device musts listed in CHANGELOG (multi-byte chunk splits, abort-on-close, long-answer jank, streams-less degrade).🤖 Generated with Claude Code