Skip to content

fix(marketplace): render the tool-row switch with official 0.78.1's recipes - #29

Merged
tyonger-stack merged 3 commits into
mainfrom
feat/marketplace-detail-switch-0781
Nov 2, 2026
Merged

tyonger-stack merged 3 commits into
mainfrom
feat/marketplace-detail-switch-0781

Conversation

@tyonger-stack

Copy link
Copy Markdown
Owner

The defect

Official 0.78.1's plugin detail page draws each tool row's on/off switch with its own Switch: the checked track is the accent primary, the knob is the luminous surface. Upstream 0.18 draws the same control with its shared switch, whose checked track is the grey --sand-fill-control-checked, so this build rendered a grey switch where official renders a blue one.

Measured over CDP on the two apps running side by side, Gmail detail page open, 工具 list expanded:

property official 0.78.1 this build before
track background rgb(17, 109, 210) rgb(90, 90, 90)
track box-shadow rgba(245, 245, 245, 0.04) 0 0 0 .5px inset none
track border none 0.5px solid --sand-border-subtle
knob background rgb(245, 245, 245) rgb(252, 252, 252)
knob placement inset-inline-start: 2px off, 12px on left: 1.5px + translate(10px)

Both sides rendered role="switch", aria-checked="true", a 26×16 track and a 12×12 knob — only the recipes differed.

The fix

  • The detail view patch swaps the one tool-row switch call site for an injected component carrying official's own class strings. They are byte-exact against official's own switch recipe table (SD + knob recipes xD in index.eager-app-D1juOWZp.js) and against the class attribute on the running official app's live switch.
  • The injected component reproduces official's DOM exactly — the same five attributes on the button, the same aria-hidden span — and official's class string is passed straight through rather than through the chunk's k() helper, which would prepend a marker class.
  • Five official rules upstream 0.18 does not declare are lifted verbatim into the detail companion.
  • The dialog's token scope declares the five tokens the switch reads and upstream 0.18 does not: --sand-border-quaternary-translucent, --sand-shadow-control, --sand-bg-accent-primary-hover, --sand-bg-accent-primary-disabled, --sand-bg-secondary-disabled. Without the first two the track's rim and the knob's shadow are invalid at computed-value time; without the third the track loses its background on hover. All values are official's own.
  • The tool row's aria-label is aligned with official's locale ids y5UgU8 ("Disable {0}" / "停用 {0}") and FZtBeR ("Enable {0}" / "启用 {0}"); this build only had the "Enable" half translated.

Upstream 0.18's shared switch is left alone on purpose. This build's only [role="switch"] in the whole app is the detail page's tool row, so the detail call site is the whole blast radius.

The commits

commit what the detail-page suite
32e6ba3 the guards only 42 tests, 37 pass, 5 fail
b4c18f2 the fix 42 tests, 42 pass, 0 fail
a8f98bb the port document —

Verification

  • whole repository's offline suite: 2737 tests, 2703 pass, 0 fail, 34 skipped
  • packaged and deployed app.asar sha256 e921d4e4b085f3a8ccd32c78a63009e7c642dc219d6eb42dd86dded12607da9c, equal on both sides
  • shipped renderer chunks that parse: 102 of 102; "(RLocT(" quote-bug chunks: 0
  • driven against the live apps: 16 of 16 measured switch properties match official, in the checked state and in the unchecked state reached by a clone
  • the two screenshots of the live switch, one from each app, are byte-for-byte identical (ec247f09f4a69bf1fc4ee671933d6adadb99b400f34750bb1f410d366326402f)

The switch was deliberately not clicked, because that toggles a tool's permission mode on the installed plugin. The wiring was checked without clicking: React's own props on the deployed button carry an onClick whose source is ()=>onToggle(!on), upstream's contract for the same control, and the onToggle and isChecked props the port passes are byte-identical to upstream's.

🤖 Generated with Claude Code

Tian and others added 3 commits November 2, 2026 11:50
The plugin detail page's per-tool rows render upstream 0.18's shared switch,
whose checked track is the grey --sand-fill-control-checked and whose knob is
upstream's own. Official 0.78.1 renders the same control with its own Switch:
an accent-primary track and a luminous knob. These guards pin the port's
contract for that control before the port changes it.

The switch's own recipes are written out in the test rather than read from the
port, so a port that swapped official's recipes for upstream's cannot satisfy
itself. The guard evaluates the port's own component and reads what it
renders: the button's attribute set, the track's recipe and the knob's. A
second guard reads the label the port writes into the chunk, because upstream's
extra-i18n pass localizes only the Enable half of it and the enabled branch
therefore reads "Disable <name>" inside a Chinese page.

The token guard reads the union of upstream 0.18's stylesheet and this
repository's official CSS companions, so it bites on a runner that carries no
upstream payload at all, and it proves that every token the switch's recipes
read is declared in both the light and the dark .sand-plugins-dialog block with
official's own values.

Co-authored-by: Mavis <mavis@local>
…ecipes

Official 0.78.1's plugin detail page draws each tool row's on/off switch
with its own Switch: the checked track is the accent primary and the knob is
the luminous surface. Upstream 0.18 draws the same control with its shared
switch, whose checked track is the grey `--sand-fill-control-checked`, so the
rebuild rendered a grey switch where official renders a blue one.

The detail view patch now swaps the one tool-row switch call site for an
injected component that carries official's own class strings. The class strings
are byte-exact against official's own switch recipe table in
`index.eager-app-D1juOWZp.js` (`SD` and its knob recipes `xD`) and against
the class attribute on the running official app's live switch. The injected
component reproduces official's DOM exactly — the same five attributes on the
button and the same `aria-hidden` span — and official's class string is passed
straight through rather than through the chunk's `k()` helper, which would
prepend a marker class and break the byte-for-byte match.

Five official rules that upstream 0.18's stylesheet does not declare are lifted
verbatim into the detail companion: the track's 0.5px inset rim, the track's
minimum width and maximum height, the knob's drop shadow and the knob's
checked offset.

The dialog's token scope now declares the five tokens the switch reads and
upstream 0.18 does not declare: `--sand-border-quaternary-translucent`,
`--sand-shadow-control`, `--sand-bg-accent-primary-hover`,
`--sand-bg-accent-primary-disabled` and `--sand-bg-secondary-disabled`.
Without the first two the track's rim and the knob's shadow resolve to
invalid values at computed-value time; without the third the track loses its
background while the pointer is over it. Their values are official's own.

The tool row's label is aligned with official's too: official builds it from
the locale ids `y5UgU8` ("Disable {0}" / "停用 {0}") and `FZtBeR`
("Enable {0}" / "启用 {0}"), and the rebuild only had the "Enable" half
translated.

Co-authored-by: Mavis <mavis@local>
Documents the switch wave end to end: the defect as it was measured on the
two apps side by side, why upstream 0.18's shared switch could not carry
official's recipe, the four class strings and where each of them comes from,
the five official CSS rules that had to be lifted, the five tokens the dialog
scope was missing and why each one changes what is rendered, the tool row's
label, the two commits and the deployment record.

Co-authored-by: Mavis <mavis@local>
@tyonger-stack
tyonger-stack merged commit 2f7fde7 into main Nov 2, 2026
6 checks passed
@tyonger-stack
tyonger-stack deleted the feat/marketplace-detail-switch-0781 branch November 2, 2026 04:05
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