Phase 18: settings that belong to the writer, not the browser
The mute toggle, the falling-petals toggle and the chosen companion lived in localStorage, which is a property of the machine. Now that two people can sign in to one Petal, sharing a laptop would have meant sharing a mascot and one person's silence muting the other. Each key is namespaced by user id. The awkward part is timing: sounds.ts and petals.ts read their value the moment they are imported, long before /api/me can have answered. Rather than block startup on the network for a mute flag, a read before the answer arrives sees the old un-namespaced key -- on a single-writer browser, exactly the right value -- and setPrefsScope then adopts it into that account's namespace and tells every reader to look again. Adoption moves rather than copies, so the first account inherits what was set before accounts existed and the second starts from Petal's defaults. The personal spelling dictionary moves further than that: onto the server. It is built from her own writing, so it should not be readable by whoever sits down at the same browser next -- but merely namespacing it would have split the list she already has between her laptop and her tablet, which is worse than where we started. A table keyed (user_id, lang, word) follows her instead. The lang is the dictionary's, not hers: an English exception must not silence a pt-PT flag once the second pair ships. Adding a word takes effect in the editor immediately and persists in the background, so the underline goes away the instant she asks. A browser still holding the old list hands it over on first load, and only lets go once the server has taken it. Claude-Session: https://claude.ai/code/session_016y6gyuHkQXPiEuW8RGQyua
This commit is contained in:
@@ -436,6 +436,29 @@ CREATE TABLE images (
|
||||
CREATE INDEX idx_images_user_id ON images(user_id);
|
||||
|
||||
ALTER TABLE users ADD COLUMN pair_lang TEXT NOT NULL DEFAULT 'zh';
|
||||
`,
|
||||
},
|
||||
{
|
||||
// The personal spelling dictionary moves off the browser. It used to be a
|
||||
// single `petal.spell.personal` key in localStorage, which meant two
|
||||
// people sharing a device shared a word list built from one person's
|
||||
// private writing — and one person writing on two devices had two
|
||||
// unrelated lists.
|
||||
//
|
||||
// `lang` is the *dictionary's* language, not the writer's: a word is only
|
||||
// ever added while a particular Hunspell dictionary flagged it, and an
|
||||
// en-US personal word must not silence a pt-PT flag (or vice versa) once
|
||||
// the second pair ships. `word` is stored as typed; matching is exact,
|
||||
// because case carries meaning to a speller ("polish" vs "Polish").
|
||||
name: "0011_personal_dictionary",
|
||||
stmt: `
|
||||
CREATE TABLE personal_words (
|
||||
user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
lang TEXT NOT NULL,
|
||||
word TEXT NOT NULL,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (user_id, lang, word)
|
||||
);
|
||||
`,
|
||||
},
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user