Skip to content

Write the Chrome settings Google does not sync: vertical tabs, side panel - #33

Draft
landsman wants to merge 2 commits into
mainfrom
chrome-prefs
Draft

Write the Chrome settings Google does not sync: vertical tabs, side panel#33
landsman wants to merge 2 commits into
mainfrom
chrome-prefs

Conversation

@landsman

Copy link
Copy Markdown
Owner

Chrome syncs bookmarks, extensions, passwords and most of the Settings page, but a handful of window-chrome toggles are per-installation and never leave the machine. Vertical tabs is the one that hurts — every new Mac starts with the horizontal strip again.

make chrome writes them, the same way make macos writes the System Settings panes.

vertical_tabs.enabled           true
vertical_tabs.collapsed_state   false
vertical_tabs.uncollapsed_width 240
side_panel.is_right_aligned     false

Everything else worth having — pinned toolbar actions, bookmark bar, theme, languages — already sits under account_values in the profile, so Chrome syncs it and it would only be noise here.

Why a script and not stow

Chrome keeps these in Default/Preferences: one JSON blob that also holds site permissions, engagement scores, an upload seed and the last window rectangle. Symlinking it into git would track the noise and publish the rest, so only the four keys named in the script are patched and the rest of the file is left as the machine has it. Same reasoning as bin/macos/defaults.sh.

Two refusals, both deliberate:

  • Not while Chrome runs. It holds the file in memory and rewrites it on exit, so a write made underneath it vanishes hours later — the worst kind of no-op. The script checks and exits 1.
  • Not in place. Temp file plus rename, so an interrupted run leaves the old Preferences intact rather than half a JSON document.

python3 rather than jq: it ships with macOS and with both runners, and jq is not in the Brewfile.

Checks

bin/chrome/prefs.test.sh runs against a throwaway profile via $CHROME_PREFS, with a pgrep stub on PATH — so it never touches the real Chrome profile and gives the same answer on a runner with no Chrome at all. It covers: the file stays valid JSON, sibling and unrelated keys survive, a missing parent is created, the run is idempotent, the guard refuses while Chrome runs, and --dry-run reaches nothing.

make qa passes on this branch.

landsman added 2 commits July 29, 2026 10:00
…anel

Chrome syncs bookmarks, extensions, passwords and most of the Settings page,
but a handful of window-chrome toggles are per-installation and never leave the
machine. Vertical tabs is the one that hurts: every new Mac starts with the
horizontal strip again, and the fix is a menu item nobody remembers the name of.

So `make chrome` writes them, the same way `make macos` writes the System
Settings panes. Not a stow package: Chrome keeps them in `Default/Preferences`,
one JSON blob that also holds site permissions, engagement scores, an upload
seed and the last window rectangle. Symlinking that into git would track the
noise and publish the rest, so only the four keys named in the script are
patched and the rest of the file is left exactly as the machine has it.

Two things the script refuses to do quietly. It will not write while Chrome is
running, because Chrome holds the file in memory and rewrites it on exit — the
change would disappear hours later, which is the worst kind of no-op. And it
writes through a temp file and a rename, so an interrupted run leaves the old
Preferences intact rather than half a JSON document.

python3 rather than jq: it is on macOS and on both runners, and jq is not in
the Brewfile.
@landsman

Copy link
Copy Markdown
Owner Author

How to test this

make qa covers the script's logic on both runners, but it runs against a throwaway file with a pgrep stub — it never touches a real profile. So the two things that can only fail on a real machine are untested by CI: whether the profile path is right, and whether the guard actually recognises a running Chrome. Both are below.

Set the path once. macOS:

prefs="$HOME/Library/Application Support/Google/Chrome/Default/Preferences"

Linux:

prefs="$HOME/.config/google-chrome/Default/Preferences"

1. Back up first

The script patches four keys and leaves the rest of the file alone, but that file also holds every site permission you have ever granted. Restoring it is a cp, so there is no reason not to have one.

cp "$prefs" /tmp/Preferences.bak

2. The guard, with Chrome open

This is the failure this whole script is shaped around: Chrome holds Preferences in memory and rewrites it on exit, so anything written underneath it disappears hours later, silently. With Chrome running:

make chrome

Expected — it refuses and exits non-zero:

quit Chrome first - it overwrites …/Default/Preferences on exit
echo $?   # 1

If it instead reports applied, the guard did not match this platform's process name and the rest of the test is meaningless. Stop there.

3. Break the settings, so a no-op cannot pass

Running the script against a profile that already matches proves nothing. In Chrome:

  • drag the vertical tab strip noticeably wider or narrower — that is uncollapsed_width, which the script pins to 240
  • then right-click the tab strip and turn vertical tabs off

Quit Chrome completely — ⌘Q on macOS, not just closing the window. A lingering window means the file on disk never changed and you are about to test nothing.

Confirm it really is gone before continuing:

pgrep -x "Google Chrome" || pgrep -x chrome || echo "not running"
python3 -c 'import json,sys;print(json.load(open(sys.argv[1]))["vertical_tabs"])' "$prefs"
# {'enabled': False, ..., 'uncollapsed_width': <whatever you dragged it to>}

4. Run it

make chrome

Expected:

set vertical_tabs.enabled = true
set vertical_tabs.collapsed_state = false
set vertical_tabs.uncollapsed_width = 240
set side_panel.is_right_aligned = false
applied - start Chrome

5. Check what actually moved

The point is that it changed four keys and nothing else. Diff the backup against the result, both pretty-printed:

diff <(python3 -m json.tool /tmp/Preferences.bak) <(python3 -m json.tool "$prefs")

Expected: only vertical_tabs.enabled and vertical_tabs.uncollapsed_width differ — the two you broke. Everything else in a 380 KB file is untouched, and the file still parses.

Then run it a second time and diff again: the output must be empty. Idempotence is what makes this safe to leave in the install sequence.

6. Reopen Chrome

Vertical tabs are back, the strip is 240 wide, the side panel opens on the left. That last hop matters on its own — it proves Chrome accepted the edited file rather than discarding it as tampered-with, which is the failure mode these keys would have if Chrome tracked them in Secure Preferences. It does not; protection.macs is empty for all four.

Restore

With Chrome closed:

cp /tmp/Preferences.bak "$prefs"

Linux

Same six steps against the Linux $prefs above. Two things there have no coverage on this branch and are worth a look on the first machine that runs it:

  • the path assumes the google-chrome package (.deb or AUR). Flatpak and Snap put the profile elsewhere — CHROME_PREFS=… ./bin/chrome/prefs.sh takes any path.
  • step 2 is the one to actually watch. pgrep -x chrome is the guard there, and if Chrome's process name is not what it expects, the write is accepted and then silently eaten on Chrome's next exit — which looks exactly like the script not working, days later.

@landsman
landsman marked this pull request as draft July 29, 2026 09:59
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.

1 participant