Skip to content

Add the portable settings document for settings sync - #989

Merged
enaboapps merged 3 commits into
mainfrom
claude/985-portable-settings
Oct 5, 2026
Merged

enaboapps merged 3 commits into
mainfrom
claude/985-portable-settings

Conversation

@enaboapps

@enaboapps enaboapps commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor

Closes #985
Part of #988.

What

Adds src-tauri/src/portable_settings.rs, the versioned document (schemaVersion: 1) that the sync engine (#986) will store in desktop_preferences.payload.

Synced:

  • App settings: pointer speed, mouse and key repeat, dwell, cursor overlay
  • Custom (non-built-in) switch profiles
  • Switch settings, including physical switch keys, hold interval and hold actions
  • All point-scan config and scan preferences, plus the keyboard layout
  • The 8 remote switch slots

Kept local: desktopId, paired devices and pairing tokens, telemetry install id and consent, setup flags, startWithSystem, the remote config revision counter, the current scan mode, and the legacy point-scan key fields (superseded by switch bindings).

Deviation from the plan: shareDiagnostics is also kept local. It is the telemetry consent, and consent should be given on each install rather than arrive from another machine.

Applying incoming documents

  • parse rejects documents with a newer schemaVersion (ParseError::Newer, with an "update Switchify PC" message) as well as malformed ones. Unknown fields are rejected, so additions require a schema bump.

  • plan validates every section before anything changes. App settings use normalized(); switches and remote slots use validate(); point scan uses Config::validate(); profiles use the same validate_profile rules plus the uniqueness and 32-profile limits.

  • plan also merges in local-only values and returns only the sections that differ, so unchanged sections are never re-applied. (A point-scan apply resets the scanner.)

  • apply runs on the main thread and goes through the existing save paths:

    • switches: switch_runtime::Controller::save (pausing point scan first), then remote_scan::apply
    • remote slots: remote_scan::save
    • point scan: point_scan_runtime::configure
    • keyboard layout: update_point_setting
    • app settings: apply_app_settings, newly extracted from save_settings so startup, consent, dwell, repeat and overlay side effects stay identical
    • profiles: persisted through AppModel::persist

    It refuses to run during switch practice, like the settings commands.

  • Profile versions: a profile whose bindings, name or provider changed gets a version above both copies, so connected phones pick it up. An unchanged profile keeps its local version.

Nothing calls the module yet; #986 wires it into the sync engine. It carries #![allow(dead_code)] until then.

Validation

  • npm run prediction-model, npm run lint, npm test (254 passed) and npm run build
  • cargo fmt --check and cargo clippy --all-targets -D warnings: clean
  • cargo test: 660 passed, 3 ignored, including 21 new portable_settings tests covering:
    • device-only values never entering the document
    • switch keys being included
    • round-trip with no planned changes
    • rejection of newer, malformed and unknown-field documents
    • local-only settings, scan mode, legacy keys and remote revision being kept
    • keyboard layout being split out
    • every invalid section being rejected before applying
    • profile validation, the limits, version bumping, unchanged versions and removal

🤖 Generated with Claude Code

Define the versioned document that carries a user's preferences between
desktops: app settings, custom switch profiles, switch bindings including
keys, point scan and scan preferences, keyboard layout and remote switch
slots. Device identity, pairing, telemetry consent, setup progress and
startup registration stay local.

Incoming documents are parsed (rejecting newer schema versions), fully
validated, and merged with local-only values before anything changes. Only
sections that differ are applied, each through the same path as a local
save. save_settings now shares apply_app_settings with this path.

Refs #985

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@enaboapps enaboapps added this to the v1.0.0-rc.22 milestone Oct 5, 2026
Address review on the portable settings document:
- Require an exact JSON round trip when parsing, so partial documents or
  fields from a newer build are rejected instead of silently resetting point
  scan or scan preferences. A test pins the synced point scan fields.
- Reserve built-in profile names on every platform. A custom profile synced
  from a Mac named after Windows' Grid 3 is renamed rather than blocking the
  whole document, and local saves now reject reserved names.
- Compare profile names ASCII case-insensitively, as local saves do.
- Document main-thread use and retry-converges semantics of apply.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Address re-review: a custom profile saved under a now-reserved built-in
name (e.g. "Grid 3" on a Mac) is renamed once when the model loads, so
it stays editable, and synced documents rename the same way. The suffix
skips names already in use ("(custom 2)", ...) so two such profiles
cannot block sync.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@enaboapps
enaboapps marked this pull request as ready for review October 5, 2026 11:40
@enaboapps
enaboapps merged commit d17b882 into main Oct 5, 2026
6 checks passed
@enaboapps
enaboapps deleted the claude/985-portable-settings branch October 5, 2026 12:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Settings sync: portable settings document and apply path

2 participants