feat(chat): save by default + share anonymously — auto-saved chats, guest-facing Share, success toast, action row

This commit is contained in:
2026-08-31 05:20:25 -04:00
parent c564e317ed
commit 914097abcf
17 changed files with 1803 additions and 491 deletions
+23 -5
View File
@@ -6,7 +6,10 @@ styles.css so a silent regression (key rename, dropped try/catch, missing
restore, New chat control lost) is caught without a browser.
Pinned design (PLAN §7.4 note / phase 14):
* versioned key ``bor.chat.v1`` → ``{v: 1, messages: [...]}``, raw text only;
* versioned key ``bor.chat.v1`` → ``{v: 1, chatId: string | null,
messages: [...]}`` (phase 55 A2: the shape extends IN PLACE with the
saved_chats row link — a pre-55 record without it reads as null),
raw text only;
* save points: user message on send, brain message on ``done``;
* every ``localStorage`` access wrapped in try/catch (failure-safe);
* size budget ~700k chars, oldest dropped first;
@@ -45,13 +48,28 @@ def _index() -> str:
def test_versioned_storage_key_and_v1_payload() -> None:
"""`bor.chat.v1` (versioned — a format bump is a clean start) with the
{v, messages} payload shape (A11: raw localStorage JSON, no library)."""
{v, chatId, messages} payload shape (A11: raw localStorage JSON, no
library). Phase 55 (A2): the shape extends IN PLACE with `chatId` —
the saved_chats row link (null when unlinked), so a reload restores
the conversation AND its link; the trimToBudget size probe measures
the same shape."""
js = _js()
assert 'const STORAGE_KEY = "bor.chat.v1"' in js
assert "export const STORAGE_VERSION = 1" in js
# The payload written to the key is always {v: STORAGE_VERSION, messages}
# (two write paths: saveConversation and the trimToBudget size probe).
assert js.count("v: STORAGE_VERSION, messages") >= 2
# The write carries the version, the row link, and the trimmed
# messages (the single write path — saveConversation).
save_start = js.find("function saveConversation")
save_body = js[save_start : js.find("\n}\n", save_start)]
assert "v: STORAGE_VERSION" in save_body
assert "chatId: currentChatId" in save_body, (
"phase 55: the link is written with the record (null when unlinked)"
)
assert "trimToBudget(conversation)" in save_body
# The size probe measures the same shape (the link is a fixed-length
# field — null stands in for the size estimate).
probe_start = js.find("function trimToBudget")
probe = js[probe_start : js.find("\n}\n", probe_start)]
assert "v: STORAGE_VERSION" in probe and "chatId: null" in probe
# Restore validates the version before trusting anything.
assert "data.v !== STORAGE_VERSION" in js