feat(sync): bundle dictionary settings into the settings replica kind#4096
Merged
Conversation
Adds three entries to the SETTINGS_WHITELIST so `providerOrder`, `providerEnabled`, and `webSearches` flow through the bundled settings replica with whole-field LWW. `defaultProviderId` (last- used tab) is deliberately excluded — it's per-device state. The customDictionaryStore exposes `applyRemoteDictionarySettings` so pulled values propagate into the in-memory mirror that the reader popup and the dictionary settings panel read from. Without this the mirror would stay stale until the next panel mount. Also fixes `saveCustomDictionaries`: it mutated the existing settings object in place and called `setSettings` with the same reference, so the replicaSettingsSync subscriber saw no change and never published. Build a fresh settings reference instead. Collapses the original PR 6 (`dict_provider_position`) and PR 7 (`dict_web_search`) plans, which proposed per-element CRDT rows with deterministic actor-id tiebreaks. Whole-field LWW is the right call given how rarely users edit these on two devices at once — same precedent as `customHighlightColors` and `customThemes` shipping through the bundled kind in PR 5. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces the per-device `Math.random()` bundleDir as `dict.id` with the cross-device-stable `contentId`. `bundleDir` keeps tracking the device-local on-disk path, so on-disk layout is unchanged. Touch points: - 4 import paths in `dictionaryService.ts` + `buildLocalDictFromRow` in `replicaDictionaryApply.ts` set `id: contentId` instead of `id: bundleDir`. - Every `providerOrder` / `providerEnabled` entry that came from the dict store is now uniformly contentId-keyed, so the bundled `settings` replica syncs them across devices without any seam translation. Dicts with no contentId (very old, never synced) keep their bundleDir as id and remain local-only. Co-Authored-By: Claude Opus 4.7 (1M context) <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.
Summary
Adds three entries to
SETTINGS_WHITELISTso dictionaryproviderOrder,providerEnabled, andwebSearchesflow through the bundledsettingsreplica with whole-field LWW. Collapses the original PR 6 (dict_provider_position) and PR 7 (dict_web_search) plans — per-element CRDT rows were over-engineered for settings users edit a few times a year, and the existing precedent (customHighlightColors,customThemes) is whole-field LWW too.defaultProviderId(last-used tab) is deliberately excluded — that's per-device state.What's in here
Commit 1 — bundle dictionary settings into the
settingsreplica kinddictionarySettings.{providerOrder, providerEnabled, webSearches}customDictionaryStore.applyRemoteDictionarySettingsmirrors pulled values into the in-memory store the reader popup + settings panel read from, so changes show up without a panel reloadsaveCustomDictionariesmutated the existing settings object in place and calledsetSettingswith the same reference — the replicaSettingsSync subscriber's reference-equality check saw no change and never published. Build a fresh settings reference instead.Commit 2 — make
dict.idstable across devices (= contentId)dict.idused to equal the per-device randomMath.random()-derivedbundleDir, so two devices that imported the same.mdxhad differentids for the same dictionary. The syncedproviderOrderwas meaningless on the receiving side —providerOrder.filter((id) => known.has(id))filtered every imported-dict entry out.id: bundleDir→id: contentIdat the 4 import paths indictionaryService.tsand inbuildLocalDictFromRow.bundleDirkeeps tracking the device-local on-disk path; on-disk layout is unchanged.providerOrder/providerEnabledare uniformly contentId-keyed end-to-end, so the bundled settings replica syncs them across devices with no seam translation.Test plan
pnpm test— 4062 passed, 8 skippedpnpm lint— cleanreplicas?kind=settingsPOST → Device B's next pull applies the same orderdict.id === contentId(verified via the 4-provider stardict / mdict / dict / slob import paths)🤖 Generated with Claude Code