Skip to content

fix(ui): move the single-line chip family onto Astryx Badge - #1881

Merged
Astro-Han merged 3 commits into
mainfrom
fix/chips-pin-box-height
Aug 2, 2026
Merged

fix(ui): move the single-line chip family onto Astryx Badge#1881
Astro-Han merged 3 commits into
mainfrom
fix/chips-pin-box-height

Conversation

@Astro-Han

@Astro-HanAstro-Han commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Summary

Closes#1879.

The issue asked for pinned box heights so a line-height change can no longer move layout. Pinning was the right diagnosis and — for most of the family — the wrong owner. Astryx already ships Badge, and its box IS the recipe these chips hand-wrote: 20px off --spacing-5, pill radius, supporting tier, align-items: center, nowrap. Twelve chips are <Badge variant=...> at the call site now, and their height, chrome and per-state colour left product CSS with them. Net +347 / −304 across 27 files: the conversion pays for the guards.

What stays product CSS is what Badge does not do — a chip with no chrome at all (.maka-message-time-inline, .maka-nav-kbd), one that truncates (.maka-skill-governance-summary span), and the compound permission pill that holds a Badge inside it. Those keep a --h-control-* pin.

Three defects the first revision shipped

Eleven pins sat below their own arithmetic floor. The floor rule (line box + padding + border, under border-box) was applied by hand in four CSS comments and skipped everywhere else. Measured live, those chips were 2–4px shorter than they read and their declared vertical padding was vertically inert:

settingsAboutVersion pinned 20 natural 24 pad 2/2 ink 上 0.5px
maka-list-row-stale-pill 20 22 pad 1/1
maka-turn-truncation-badge 20 22 border 1/1 scroll 19 > client 18

Not a clipping bug — the line box centres and the ink fits, verified including CJK — but an undocumented shrink. The rule now lives in the e2e, not in prose: a chip's height: auto natural height must fit its pinned tier.

The derived scan was a narrower whitelist. Three independent bypasses, all now closed and mutation-verified:

bypassconsequence
three features required in ONE rule body.plan-proposal-revision was invisible, five lines from a chip the scan caught
"declared anywhere" instead of the effective declarationheight: var(--h-control-xs); … height: auto passed both checks while the browser saw no pin
parseCssBlocks keyed multi-line selector lists by their last linetwo exemptions matched zero rules; appending an exempt selector to a group exempted the whole group

The containment assertion was inert twice over. It read scrollHeight/clientHeightafter restoring the stressed leading, and on an overflow: visible box the two are equal regardless. Replaced with the floor measure, which can actually fail.

Astryx layering

  • --h-control-md/lg/xl now derive from --size-element-sm/md/lg instead of restating 28/32/36, so a chip and the Astryx control beside it can no longer agree by coincidence. Verified live: md → 28, lg → 32.
  • The three composer .astryx-selector rules were forcing an Astryx Selector to 24px — below its own smallest tier (--size-element-sm = 28) — and desyncing it from ModelChipStatic, the inert state of the same chip, a Button size="sm" at 28px. The pill changed height when it became clickable. Both declare no height now. Their contract exemption previously read "declaring a height here would reintroduce the override fix(ui): pin the box height of single-line chips so leading cannot move them #1879 removed" while the rule declared exactly that; the reason is true of every entry under it now.
  • .maka-sidebar-update-button used the background shorthand, which also resets background-image — where Astryx ghost keeps its hover and pressed overlays. The variant the component was passed had been silently cancelled. Radius moves to --_button-radius; four declarations that restated Button's own computed values are gone.

What was tried and reverted

Widening the scan to --radius-control so squared badges were covered: it reported 27 offenders including pre blocks, failure banners, sidebar rows and buttons — every one a box a pin would clip. --radius-control is this repo's general control shape, not a chip signal. The squared chips that motivated it are Badges now, so that population is empty rather than unguarded.

Verification

npm run lint 0 · npm --workspace @maka/desktop run typecheck 0 · desktop tests 1325 pass · ui tests 239 pass · e2e 73/73 · contract 15/15 · check-dead-css --check clean · astryx:theme --check current.

npm run format:check fails on scripts/check-console.mjs, which this branch does not touch (git diff main...HEAD -- scripts/check-console.mjs is empty) — inherited from #1880.

Every guard mutation-verified with cp backup/restore:

mutationresult
height: var(--h-control-xs); … height: auto✖ 2 (passed the previous revision)
chip split across two rules✖ 2
exempt selector appended to a group✖ 2
plain unpinned chip✖ 1
pinned but display: block✖ 1
height added to a wrapping box✖ 1
chrome-less chip unpinned✖ 1
tier below its own floor (e2e)Received: 28 vs ≤ 20
pin removed entirely (e2e)

All restored green. The first attempt at the floor mutation was ineffective — the injected padding-block was overridden by a later padding: 0 in the same rule — and was redone rather than counted.

Live evidence

Plan reminders: the countdown chip is now the same object as the 已触发 / 已暂停 badges it sits beside, which is the convergence the component swap buys.

image

Permissions: 影响功能 is a neutral Badge; the status chips route through the same statusBadgeVariant seam as the rest of Settings.

image

Review focus

The visual direction was chosen against a side-by-side render of every affected chip in both colour schemes. Two deliberate appearance changes worth a second opinion:

  • .providerCatalogBadge had a comment reading "compact squared corners, never pills". Badge hardcodes --radius-full with no radius prop, so that intent is retired rather than kept as an override.
  • The permission capability chip had a note that a granted permission "doesn't need a green pill" and fell through to neutral. One tone table for the page beat a per-chip exception, so granted reads success like every other status badge in Settings.

@Astro-Han
Astro-Hanforce-pushed the fix/chips-pin-box-height branch 2 times, most recently from 5fdb294 to 1db0e8eCompareAugust 2, 2026 07:13
@Astro-HanAstro-Han changed the title fix(ui): pin the box height of single-line chips to the control rulerfix(ui): pin single-line chip box heights to the control rulerAug 2, 2026
@Astro-HanAstro-Han changed the title fix(ui): pin single-line chip box heights to the control rulerfix(ui): move the single-line chip family onto Astryx BadgeAug 2, 2026
Squashed for rebase onto #1893; final message written at the end.
@Astro-Han
Astro-Hanforce-pushed the fix/chips-pin-box-height branch from 38aae0f to 5263398CompareAugust 2, 2026 10:14
… exemption
Review of the previous revision found three ways the guards were narrower than
their own documentation, all of the same shape: a check that reports green
because it cannot see the thing it claims to govern.
- `isChip` required a rule to declare its own type. A compound chip delegates
type to a child and a simple one may just inherit, so the arm was hiding
three shipping chips with the #1879 defect while reporting the other 15
green. Two arms — pill radius and padding — see all 20 pill boxes, and no
longer read a vocabulary that #1893 already moved once, so the unanchored
`chips.length >= 8` floor that watched for exactly that drift is deleted with
it.
- The exemption set was one list honoured by skipping, so a mutation adding
`height: 1px` to a rule excused as component-sized stayed green. It is now
three reason-groups, each asserted: WRAPS must declare no block size (and is
the wrap contract's own list rather than a second copy of it), COMPONENT_OWNED
must declare none at all, PINNED_OFF_RULER must still be pinned.
- The Badge contract read the unconditional view, on which
`.settingsHealthBlockerBadge` — which lives entirely inside a breakpoint —
merged to nothing, so the exemption naming it suppressed nothing. It reads a
conditional-inclusive view now and asserts that the release is real.
Also: `min-height`/`max-height`/`padding-inline-start` all constrained a box
while passing both checks; the ruler accepted tiers it does not define; and the
JSX scanner ended a tag at the first `>` in a prop, and then, once rewritten,
at an apostrophe inside a prop comment — which silently dropped three live call
sites until the mutation sweep caught it.
Four boxes the widened scan surfaced, each measured live before its tier:
`.maka-quote-chip-collapsed` and `.maka-deep-research-run-count` at 24px
natural (pixel-neutral on `--h-control-sm`), `.maka-firstrun-step` at 29px
(`--h-control-lg`, +3px), and `.maka-mcp-install-button`, a bare 34px square
that swaps with a 28px `Button size="sm"` in the same slot.
…ip contracts
Every finding below was reproduced as a mutation that passed green, and each
fix is re-verified by the same mutation failing. Four root causes, three of
which delete something.
The predicate read text, not declarations. `/padding/` also matches
`background-clip: padding-box`, which is how a scrollbar thumb — padding-less,
and a box no reader perceives as an object — entered the chip population and
earned an exemption group of its own. The group is deleted with the substring
that created it, and the padding vocabulary becomes one shared authority.
The same guard-narrower-than-CSS mistake, twice more. The Badge contract's
`OWNED` gained the logical padding family and lost `padding-top/right/bottom/
left`, so `padding-left: 9px` on a Badge className redrew the component's box
while green. And the off-ruler exemption proved "still pinned" with the
constrains-a-block-size vocabulary, so rewriting its `height` as `min-height`
handed the box back to the line box and stayed green.
Which merge view follows from which question, applied to all three sites
rather than one. A must-NOT contract has no conditional distinction: pinning a
wrapping row, or re-adding the height override #1879 removed, inside
`@media (max-width: 620px)` passed the unconditional view. A must-HOLD PAIR
needs the opposite — the flattened view let a Badge release `height` in one
breakpoint and `white-space` in the mutually exclusive one, releasing the box
at no viewport at all, so releases are now read per cascade context.
A scan that cannot read a call site has to say so. `<Badge {...{ className:
'x' }} />` is legal JSX that the static scan cannot read and the `className={`
scan does not match, so both contracts concluded there was nothing to govern.
Spreads join computed classNames as unreadable; `//` prop comments parse.
(TypeScript 7 is the Go port and ships no JS compiler API, so an AST is not
available to reach for — but the property that matters is loud-vs-silent, not
lexer-vs-parser.)
Three smaller ones: rungs are read from the ruler's own `:root` scope, so a
`--h-control-*` declared under `.dark` is no longer a tier a chip may name;
`--radius-pill` must exist, because renaming it reported CHIP POPULATION 0 and
passed; `.maka-sidebar-update-button` returns to COMPONENT_OWNED, where an
entry that was dead weight as a skip is the only assertion reaching it.
The e2e no longer credits the tier-choice arithmetic to a check that does not
exist. Measured with a throwaway probe: all four newly pinned chips resolve to
zero nodes in both booted fixtures, so that choice is unasserted, and the
comment says so.
Verified: 10/10 mutations caught, each confirmed to land before being trusted.
lint, format:check, typecheck, desktop 1364, ui 241, e2e 77, check-dead-css.
@Astro-Han
Astro-Han marked this pull request as ready for review August 2, 2026 11:46
@Astro-Han
Astro-Han merged commit a314d4f into mainAug 2, 2026
5 checks passed
@Astro-Han
Astro-Han deleted the fix/chips-pin-box-height branch August 2, 2026 11:46
Sign up for freeto 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.

fix(ui): pin the box height of single-line chips so leading cannot move them

1 participant

@Astro-Han
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
fix(ui): move the single-line chip family onto Astryx Badge by Astro-Han · Pull Request #1881 · apache/maka · GitHub
Skip to content

fix(ui): move the single-line chip family onto Astryx Badge - #1881

Merged
Astro-Han merged 3 commits into
mainfrom
fix/chips-pin-box-height
Aug 2, 2026
Merged

fix(ui): move the single-line chip family onto Astryx Badge#1881
Astro-Han merged 3 commits into
mainfrom
fix/chips-pin-box-height

Conversation

@Astro-Han

@Astro-HanAstro-Han commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Summary

Closes#1879.

The issue asked for pinned box heights so a line-height change can no longer move layout. Pinning was the right diagnosis and — for most of the family — the wrong owner. Astryx already ships Badge, and its box IS the recipe these chips hand-wrote: 20px off --spacing-5, pill radius, supporting tier, align-items: center, nowrap. Twelve chips are <Badge variant=...> at the call site now, and their height, chrome and per-state colour left product CSS with them. Net +347 / −304 across 27 files: the conversion pays for the guards.

What stays product CSS is what Badge does not do — a chip with no chrome at all (.maka-message-time-inline, .maka-nav-kbd), one that truncates (.maka-skill-governance-summary span), and the compound permission pill that holds a Badge inside it. Those keep a --h-control-* pin.

Three defects the first revision shipped

Eleven pins sat below their own arithmetic floor. The floor rule (line box + padding + border, under border-box) was applied by hand in four CSS comments and skipped everywhere else. Measured live, those chips were 2–4px shorter than they read and their declared vertical padding was vertically inert:

settingsAboutVersion pinned 20 natural 24 pad 2/2 ink 上 0.5px
maka-list-row-stale-pill 20 22 pad 1/1
maka-turn-truncation-badge 20 22 border 1/1 scroll 19 > client 18

Not a clipping bug — the line box centres and the ink fits, verified including CJK — but an undocumented shrink. The rule now lives in the e2e, not in prose: a chip's height: auto natural height must fit its pinned tier.

The derived scan was a narrower whitelist. Three independent bypasses, all now closed and mutation-verified:

bypassconsequence
three features required in ONE rule body.plan-proposal-revision was invisible, five lines from a chip the scan caught
"declared anywhere" instead of the effective declarationheight: var(--h-control-xs); … height: auto passed both checks while the browser saw no pin
parseCssBlocks keyed multi-line selector lists by their last linetwo exemptions matched zero rules; appending an exempt selector to a group exempted the whole group

The containment assertion was inert twice over. It read scrollHeight/clientHeightafter restoring the stressed leading, and on an overflow: visible box the two are equal regardless. Replaced with the floor measure, which can actually fail.

Astryx layering

  • --h-control-md/lg/xl now derive from --size-element-sm/md/lg instead of restating 28/32/36, so a chip and the Astryx control beside it can no longer agree by coincidence. Verified live: md → 28, lg → 32.
  • The three composer .astryx-selector rules were forcing an Astryx Selector to 24px — below its own smallest tier (--size-element-sm = 28) — and desyncing it from ModelChipStatic, the inert state of the same chip, a Button size="sm" at 28px. The pill changed height when it became clickable. Both declare no height now. Their contract exemption previously read "declaring a height here would reintroduce the override fix(ui): pin the box height of single-line chips so leading cannot move them #1879 removed" while the rule declared exactly that; the reason is true of every entry under it now.
  • .maka-sidebar-update-button used the background shorthand, which also resets background-image — where Astryx ghost keeps its hover and pressed overlays. The variant the component was passed had been silently cancelled. Radius moves to --_button-radius; four declarations that restated Button's own computed values are gone.

What was tried and reverted

Widening the scan to --radius-control so squared badges were covered: it reported 27 offenders including pre blocks, failure banners, sidebar rows and buttons — every one a box a pin would clip. --radius-control is this repo's general control shape, not a chip signal. The squared chips that motivated it are Badges now, so that population is empty rather than unguarded.

Verification

npm run lint 0 · npm --workspace @maka/desktop run typecheck 0 · desktop tests 1325 pass · ui tests 239 pass · e2e 73/73 · contract 15/15 · check-dead-css --check clean · astryx:theme --check current.

npm run format:check fails on scripts/check-console.mjs, which this branch does not touch (git diff main...HEAD -- scripts/check-console.mjs is empty) — inherited from #1880.

Every guard mutation-verified with cp backup/restore:

mutationresult
height: var(--h-control-xs); … height: auto✖ 2 (passed the previous revision)
chip split across two rules✖ 2
exempt selector appended to a group✖ 2
plain unpinned chip✖ 1
pinned but display: block✖ 1
height added to a wrapping box✖ 1
chrome-less chip unpinned✖ 1
tier below its own floor (e2e)Received: 28 vs ≤ 20
pin removed entirely (e2e)

All restored green. The first attempt at the floor mutation was ineffective — the injected padding-block was overridden by a later padding: 0 in the same rule — and was redone rather than counted.

Live evidence

Plan reminders: the countdown chip is now the same object as the 已触发 / 已暂停 badges it sits beside, which is the convergence the component swap buys.

image

Permissions: 影响功能 is a neutral Badge; the status chips route through the same statusBadgeVariant seam as the rest of Settings.

image

Review focus

The visual direction was chosen against a side-by-side render of every affected chip in both colour schemes. Two deliberate appearance changes worth a second opinion:

  • .providerCatalogBadge had a comment reading "compact squared corners, never pills". Badge hardcodes --radius-full with no radius prop, so that intent is retired rather than kept as an override.
  • The permission capability chip had a note that a granted permission "doesn't need a green pill" and fell through to neutral. One tone table for the page beat a per-chip exception, so granted reads success like every other status badge in Settings.

@Astro-Han
Astro-Hanforce-pushed the fix/chips-pin-box-height branch 2 times, most recently from 5fdb294 to 1db0e8eCompareAugust 2, 2026 07:13
@Astro-HanAstro-Han changed the title fix(ui): pin the box height of single-line chips to the control rulerfix(ui): pin single-line chip box heights to the control rulerAug 2, 2026
@Astro-HanAstro-Han changed the title fix(ui): pin single-line chip box heights to the control rulerfix(ui): move the single-line chip family onto Astryx BadgeAug 2, 2026
Squashed for rebase onto #1893; final message written at the end.
@Astro-Han
Astro-Hanforce-pushed the fix/chips-pin-box-height branch from 38aae0f to 5263398CompareAugust 2, 2026 10:14
… exemption
Review of the previous revision found three ways the guards were narrower than
their own documentation, all of the same shape: a check that reports green
because it cannot see the thing it claims to govern.
- `isChip` required a rule to declare its own type. A compound chip delegates
type to a child and a simple one may just inherit, so the arm was hiding
three shipping chips with the #1879 defect while reporting the other 15
green. Two arms — pill radius and padding — see all 20 pill boxes, and no
longer read a vocabulary that #1893 already moved once, so the unanchored
`chips.length >= 8` floor that watched for exactly that drift is deleted with
it.
- The exemption set was one list honoured by skipping, so a mutation adding
`height: 1px` to a rule excused as component-sized stayed green. It is now
three reason-groups, each asserted: WRAPS must declare no block size (and is
the wrap contract's own list rather than a second copy of it), COMPONENT_OWNED
must declare none at all, PINNED_OFF_RULER must still be pinned.
- The Badge contract read the unconditional view, on which
`.settingsHealthBlockerBadge` — which lives entirely inside a breakpoint —
merged to nothing, so the exemption naming it suppressed nothing. It reads a
conditional-inclusive view now and asserts that the release is real.
Also: `min-height`/`max-height`/`padding-inline-start` all constrained a box
while passing both checks; the ruler accepted tiers it does not define; and the
JSX scanner ended a tag at the first `>` in a prop, and then, once rewritten,
at an apostrophe inside a prop comment — which silently dropped three live call
sites until the mutation sweep caught it.
Four boxes the widened scan surfaced, each measured live before its tier:
`.maka-quote-chip-collapsed` and `.maka-deep-research-run-count` at 24px
natural (pixel-neutral on `--h-control-sm`), `.maka-firstrun-step` at 29px
(`--h-control-lg`, +3px), and `.maka-mcp-install-button`, a bare 34px square
that swaps with a 28px `Button size="sm"` in the same slot.
…ip contracts
Every finding below was reproduced as a mutation that passed green, and each
fix is re-verified by the same mutation failing. Four root causes, three of
which delete something.
The predicate read text, not declarations. `/padding/` also matches
`background-clip: padding-box`, which is how a scrollbar thumb — padding-less,
and a box no reader perceives as an object — entered the chip population and
earned an exemption group of its own. The group is deleted with the substring
that created it, and the padding vocabulary becomes one shared authority.
The same guard-narrower-than-CSS mistake, twice more. The Badge contract's
`OWNED` gained the logical padding family and lost `padding-top/right/bottom/
left`, so `padding-left: 9px` on a Badge className redrew the component's box
while green. And the off-ruler exemption proved "still pinned" with the
constrains-a-block-size vocabulary, so rewriting its `height` as `min-height`
handed the box back to the line box and stayed green.
Which merge view follows from which question, applied to all three sites
rather than one. A must-NOT contract has no conditional distinction: pinning a
wrapping row, or re-adding the height override #1879 removed, inside
`@media (max-width: 620px)` passed the unconditional view. A must-HOLD PAIR
needs the opposite — the flattened view let a Badge release `height` in one
breakpoint and `white-space` in the mutually exclusive one, releasing the box
at no viewport at all, so releases are now read per cascade context.
A scan that cannot read a call site has to say so. `<Badge {...{ className:
'x' }} />` is legal JSX that the static scan cannot read and the `className={`
scan does not match, so both contracts concluded there was nothing to govern.
Spreads join computed classNames as unreadable; `//` prop comments parse.
(TypeScript 7 is the Go port and ships no JS compiler API, so an AST is not
available to reach for — but the property that matters is loud-vs-silent, not
lexer-vs-parser.)
Three smaller ones: rungs are read from the ruler's own `:root` scope, so a
`--h-control-*` declared under `.dark` is no longer a tier a chip may name;
`--radius-pill` must exist, because renaming it reported CHIP POPULATION 0 and
passed; `.maka-sidebar-update-button` returns to COMPONENT_OWNED, where an
entry that was dead weight as a skip is the only assertion reaching it.
The e2e no longer credits the tier-choice arithmetic to a check that does not
exist. Measured with a throwaway probe: all four newly pinned chips resolve to
zero nodes in both booted fixtures, so that choice is unasserted, and the
comment says so.
Verified: 10/10 mutations caught, each confirmed to land before being trusted.
lint, format:check, typecheck, desktop 1364, ui 241, e2e 77, check-dead-css.
@Astro-Han
Astro-Han marked this pull request as ready for review August 2, 2026 11:46
@Astro-Han
Astro-Han merged commit a314d4f into mainAug 2, 2026
5 checks passed
@Astro-Han
Astro-Han deleted the fix/chips-pin-box-height branch August 2, 2026 11:46
Sign up for freeto 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.

fix(ui): pin the box height of single-line chips so leading cannot move them

1 participant

@Astro-Han
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' fix(ui): move the single-line chip family onto Astryx Badge by Astro-Han · Pull Request #1881 · apache/maka · GitHub
Skip to content

fix(ui): move the single-line chip family onto Astryx Badge - #1881

Merged
Astro-Han merged 3 commits into
mainfrom
fix/chips-pin-box-height
Aug 2, 2026
Merged

fix(ui): move the single-line chip family onto Astryx Badge#1881
Astro-Han merged 3 commits into
mainfrom
fix/chips-pin-box-height

Conversation

@Astro-Han

@Astro-HanAstro-Han commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Summary

Closes#1879.

The issue asked for pinned box heights so a line-height change can no longer move layout. Pinning was the right diagnosis and — for most of the family — the wrong owner. Astryx already ships Badge, and its box IS the recipe these chips hand-wrote: 20px off --spacing-5, pill radius, supporting tier, align-items: center, nowrap. Twelve chips are <Badge variant=...> at the call site now, and their height, chrome and per-state colour left product CSS with them. Net +347 / −304 across 27 files: the conversion pays for the guards.

What stays product CSS is what Badge does not do — a chip with no chrome at all (.maka-message-time-inline, .maka-nav-kbd), one that truncates (.maka-skill-governance-summary span), and the compound permission pill that holds a Badge inside it. Those keep a --h-control-* pin.

Three defects the first revision shipped

Eleven pins sat below their own arithmetic floor. The floor rule (line box + padding + border, under border-box) was applied by hand in four CSS comments and skipped everywhere else. Measured live, those chips were 2–4px shorter than they read and their declared vertical padding was vertically inert:

settingsAboutVersion pinned 20 natural 24 pad 2/2 ink 上 0.5px
maka-list-row-stale-pill 20 22 pad 1/1
maka-turn-truncation-badge 20 22 border 1/1 scroll 19 > client 18

Not a clipping bug — the line box centres and the ink fits, verified including CJK — but an undocumented shrink. The rule now lives in the e2e, not in prose: a chip's height: auto natural height must fit its pinned tier.

The derived scan was a narrower whitelist. Three independent bypasses, all now closed and mutation-verified:

bypassconsequence
three features required in ONE rule body.plan-proposal-revision was invisible, five lines from a chip the scan caught
"declared anywhere" instead of the effective declarationheight: var(--h-control-xs); … height: auto passed both checks while the browser saw no pin
parseCssBlocks keyed multi-line selector lists by their last linetwo exemptions matched zero rules; appending an exempt selector to a group exempted the whole group

The containment assertion was inert twice over. It read scrollHeight/clientHeightafter restoring the stressed leading, and on an overflow: visible box the two are equal regardless. Replaced with the floor measure, which can actually fail.

Astryx layering

  • --h-control-md/lg/xl now derive from --size-element-sm/md/lg instead of restating 28/32/36, so a chip and the Astryx control beside it can no longer agree by coincidence. Verified live: md → 28, lg → 32.
  • The three composer .astryx-selector rules were forcing an Astryx Selector to 24px — below its own smallest tier (--size-element-sm = 28) — and desyncing it from ModelChipStatic, the inert state of the same chip, a Button size="sm" at 28px. The pill changed height when it became clickable. Both declare no height now. Their contract exemption previously read "declaring a height here would reintroduce the override fix(ui): pin the box height of single-line chips so leading cannot move them #1879 removed" while the rule declared exactly that; the reason is true of every entry under it now.
  • .maka-sidebar-update-button used the background shorthand, which also resets background-image — where Astryx ghost keeps its hover and pressed overlays. The variant the component was passed had been silently cancelled. Radius moves to --_button-radius; four declarations that restated Button's own computed values are gone.

What was tried and reverted

Widening the scan to --radius-control so squared badges were covered: it reported 27 offenders including pre blocks, failure banners, sidebar rows and buttons — every one a box a pin would clip. --radius-control is this repo's general control shape, not a chip signal. The squared chips that motivated it are Badges now, so that population is empty rather than unguarded.

Verification

npm run lint 0 · npm --workspace @maka/desktop run typecheck 0 · desktop tests 1325 pass · ui tests 239 pass · e2e 73/73 · contract 15/15 · check-dead-css --check clean · astryx:theme --check current.

npm run format:check fails on scripts/check-console.mjs, which this branch does not touch (git diff main...HEAD -- scripts/check-console.mjs is empty) — inherited from #1880.

Every guard mutation-verified with cp backup/restore:

mutationresult
height: var(--h-control-xs); … height: auto✖ 2 (passed the previous revision)
chip split across two rules✖ 2
exempt selector appended to a group✖ 2
plain unpinned chip✖ 1
pinned but display: block✖ 1
height added to a wrapping box✖ 1
chrome-less chip unpinned✖ 1
tier below its own floor (e2e)Received: 28 vs ≤ 20
pin removed entirely (e2e)

All restored green. The first attempt at the floor mutation was ineffective — the injected padding-block was overridden by a later padding: 0 in the same rule — and was redone rather than counted.

Live evidence

Plan reminders: the countdown chip is now the same object as the 已触发 / 已暂停 badges it sits beside, which is the convergence the component swap buys.

image

Permissions: 影响功能 is a neutral Badge; the status chips route through the same statusBadgeVariant seam as the rest of Settings.

image

Review focus

The visual direction was chosen against a side-by-side render of every affected chip in both colour schemes. Two deliberate appearance changes worth a second opinion:

  • .providerCatalogBadge had a comment reading "compact squared corners, never pills". Badge hardcodes --radius-full with no radius prop, so that intent is retired rather than kept as an override.
  • The permission capability chip had a note that a granted permission "doesn't need a green pill" and fell through to neutral. One tone table for the page beat a per-chip exception, so granted reads success like every other status badge in Settings.

@Astro-Han
Astro-Hanforce-pushed the fix/chips-pin-box-height branch 2 times, most recently from 5fdb294 to 1db0e8eCompareAugust 2, 2026 07:13
@Astro-HanAstro-Han changed the title fix(ui): pin the box height of single-line chips to the control rulerfix(ui): pin single-line chip box heights to the control rulerAug 2, 2026
@Astro-HanAstro-Han changed the title fix(ui): pin single-line chip box heights to the control rulerfix(ui): move the single-line chip family onto Astryx BadgeAug 2, 2026
Squashed for rebase onto #1893; final message written at the end.
@Astro-Han
Astro-Hanforce-pushed the fix/chips-pin-box-height branch from 38aae0f to 5263398CompareAugust 2, 2026 10:14
… exemption
Review of the previous revision found three ways the guards were narrower than
their own documentation, all of the same shape: a check that reports green
because it cannot see the thing it claims to govern.
- `isChip` required a rule to declare its own type. A compound chip delegates
type to a child and a simple one may just inherit, so the arm was hiding
three shipping chips with the #1879 defect while reporting the other 15
green. Two arms — pill radius and padding — see all 20 pill boxes, and no
longer read a vocabulary that #1893 already moved once, so the unanchored
`chips.length >= 8` floor that watched for exactly that drift is deleted with
it.
- The exemption set was one list honoured by skipping, so a mutation adding
`height: 1px` to a rule excused as component-sized stayed green. It is now
three reason-groups, each asserted: WRAPS must declare no block size (and is
the wrap contract's own list rather than a second copy of it), COMPONENT_OWNED
must declare none at all, PINNED_OFF_RULER must still be pinned.
- The Badge contract read the unconditional view, on which
`.settingsHealthBlockerBadge` — which lives entirely inside a breakpoint —
merged to nothing, so the exemption naming it suppressed nothing. It reads a
conditional-inclusive view now and asserts that the release is real.
Also: `min-height`/`max-height`/`padding-inline-start` all constrained a box
while passing both checks; the ruler accepted tiers it does not define; and the
JSX scanner ended a tag at the first `>` in a prop, and then, once rewritten,
at an apostrophe inside a prop comment — which silently dropped three live call
sites until the mutation sweep caught it.
Four boxes the widened scan surfaced, each measured live before its tier:
`.maka-quote-chip-collapsed` and `.maka-deep-research-run-count` at 24px
natural (pixel-neutral on `--h-control-sm`), `.maka-firstrun-step` at 29px
(`--h-control-lg`, +3px), and `.maka-mcp-install-button`, a bare 34px square
that swaps with a 28px `Button size="sm"` in the same slot.
…ip contracts
Every finding below was reproduced as a mutation that passed green, and each
fix is re-verified by the same mutation failing. Four root causes, three of
which delete something.
The predicate read text, not declarations. `/padding/` also matches
`background-clip: padding-box`, which is how a scrollbar thumb — padding-less,
and a box no reader perceives as an object — entered the chip population and
earned an exemption group of its own. The group is deleted with the substring
that created it, and the padding vocabulary becomes one shared authority.
The same guard-narrower-than-CSS mistake, twice more. The Badge contract's
`OWNED` gained the logical padding family and lost `padding-top/right/bottom/
left`, so `padding-left: 9px` on a Badge className redrew the component's box
while green. And the off-ruler exemption proved "still pinned" with the
constrains-a-block-size vocabulary, so rewriting its `height` as `min-height`
handed the box back to the line box and stayed green.
Which merge view follows from which question, applied to all three sites
rather than one. A must-NOT contract has no conditional distinction: pinning a
wrapping row, or re-adding the height override #1879 removed, inside
`@media (max-width: 620px)` passed the unconditional view. A must-HOLD PAIR
needs the opposite — the flattened view let a Badge release `height` in one
breakpoint and `white-space` in the mutually exclusive one, releasing the box
at no viewport at all, so releases are now read per cascade context.
A scan that cannot read a call site has to say so. `<Badge {...{ className:
'x' }} />` is legal JSX that the static scan cannot read and the `className={`
scan does not match, so both contracts concluded there was nothing to govern.
Spreads join computed classNames as unreadable; `//` prop comments parse.
(TypeScript 7 is the Go port and ships no JS compiler API, so an AST is not
available to reach for — but the property that matters is loud-vs-silent, not
lexer-vs-parser.)
Three smaller ones: rungs are read from the ruler's own `:root` scope, so a
`--h-control-*` declared under `.dark` is no longer a tier a chip may name;
`--radius-pill` must exist, because renaming it reported CHIP POPULATION 0 and
passed; `.maka-sidebar-update-button` returns to COMPONENT_OWNED, where an
entry that was dead weight as a skip is the only assertion reaching it.
The e2e no longer credits the tier-choice arithmetic to a check that does not
exist. Measured with a throwaway probe: all four newly pinned chips resolve to
zero nodes in both booted fixtures, so that choice is unasserted, and the
comment says so.
Verified: 10/10 mutations caught, each confirmed to land before being trusted.
lint, format:check, typecheck, desktop 1364, ui 241, e2e 77, check-dead-css.
@Astro-Han
Astro-Han marked this pull request as ready for review August 2, 2026 11:46
@Astro-Han
Astro-Han merged commit a314d4f into mainAug 2, 2026
5 checks passed
@Astro-Han
Astro-Han deleted the fix/chips-pin-box-height branch August 2, 2026 11:46
Sign up for freeto 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.

fix(ui): pin the box height of single-line chips so leading cannot move them

1 participant

@Astro-Han
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' fix(ui): move the single-line chip family onto Astryx Badge by Astro-Han · Pull Request #1881 · apache/maka · GitHub
Skip to content

fix(ui): move the single-line chip family onto Astryx Badge - #1881

Merged
Astro-Han merged 3 commits into
mainfrom
fix/chips-pin-box-height
Aug 2, 2026
Merged

fix(ui): move the single-line chip family onto Astryx Badge#1881
Astro-Han merged 3 commits into
mainfrom
fix/chips-pin-box-height

Conversation

@Astro-Han

@Astro-HanAstro-Han commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Summary

Closes#1879.

The issue asked for pinned box heights so a line-height change can no longer move layout. Pinning was the right diagnosis and — for most of the family — the wrong owner. Astryx already ships Badge, and its box IS the recipe these chips hand-wrote: 20px off --spacing-5, pill radius, supporting tier, align-items: center, nowrap. Twelve chips are <Badge variant=...> at the call site now, and their height, chrome and per-state colour left product CSS with them. Net +347 / −304 across 27 files: the conversion pays for the guards.

What stays product CSS is what Badge does not do — a chip with no chrome at all (.maka-message-time-inline, .maka-nav-kbd), one that truncates (.maka-skill-governance-summary span), and the compound permission pill that holds a Badge inside it. Those keep a --h-control-* pin.

Three defects the first revision shipped

Eleven pins sat below their own arithmetic floor. The floor rule (line box + padding + border, under border-box) was applied by hand in four CSS comments and skipped everywhere else. Measured live, those chips were 2–4px shorter than they read and their declared vertical padding was vertically inert:

settingsAboutVersion pinned 20 natural 24 pad 2/2 ink 上 0.5px
maka-list-row-stale-pill 20 22 pad 1/1
maka-turn-truncation-badge 20 22 border 1/1 scroll 19 > client 18

Not a clipping bug — the line box centres and the ink fits, verified including CJK — but an undocumented shrink. The rule now lives in the e2e, not in prose: a chip's height: auto natural height must fit its pinned tier.

The derived scan was a narrower whitelist. Three independent bypasses, all now closed and mutation-verified:

bypassconsequence
three features required in ONE rule body.plan-proposal-revision was invisible, five lines from a chip the scan caught
"declared anywhere" instead of the effective declarationheight: var(--h-control-xs); … height: auto passed both checks while the browser saw no pin
parseCssBlocks keyed multi-line selector lists by their last linetwo exemptions matched zero rules; appending an exempt selector to a group exempted the whole group

The containment assertion was inert twice over. It read scrollHeight/clientHeightafter restoring the stressed leading, and on an overflow: visible box the two are equal regardless. Replaced with the floor measure, which can actually fail.

Astryx layering

  • --h-control-md/lg/xl now derive from --size-element-sm/md/lg instead of restating 28/32/36, so a chip and the Astryx control beside it can no longer agree by coincidence. Verified live: md → 28, lg → 32.
  • The three composer .astryx-selector rules were forcing an Astryx Selector to 24px — below its own smallest tier (--size-element-sm = 28) — and desyncing it from ModelChipStatic, the inert state of the same chip, a Button size="sm" at 28px. The pill changed height when it became clickable. Both declare no height now. Their contract exemption previously read "declaring a height here would reintroduce the override fix(ui): pin the box height of single-line chips so leading cannot move them #1879 removed" while the rule declared exactly that; the reason is true of every entry under it now.
  • .maka-sidebar-update-button used the background shorthand, which also resets background-image — where Astryx ghost keeps its hover and pressed overlays. The variant the component was passed had been silently cancelled. Radius moves to --_button-radius; four declarations that restated Button's own computed values are gone.

What was tried and reverted

Widening the scan to --radius-control so squared badges were covered: it reported 27 offenders including pre blocks, failure banners, sidebar rows and buttons — every one a box a pin would clip. --radius-control is this repo's general control shape, not a chip signal. The squared chips that motivated it are Badges now, so that population is empty rather than unguarded.

Verification

npm run lint 0 · npm --workspace @maka/desktop run typecheck 0 · desktop tests 1325 pass · ui tests 239 pass · e2e 73/73 · contract 15/15 · check-dead-css --check clean · astryx:theme --check current.

npm run format:check fails on scripts/check-console.mjs, which this branch does not touch (git diff main...HEAD -- scripts/check-console.mjs is empty) — inherited from #1880.

Every guard mutation-verified with cp backup/restore:

mutationresult
height: var(--h-control-xs); … height: auto✖ 2 (passed the previous revision)
chip split across two rules✖ 2
exempt selector appended to a group✖ 2
plain unpinned chip✖ 1
pinned but display: block✖ 1
height added to a wrapping box✖ 1
chrome-less chip unpinned✖ 1
tier below its own floor (e2e)Received: 28 vs ≤ 20
pin removed entirely (e2e)

All restored green. The first attempt at the floor mutation was ineffective — the injected padding-block was overridden by a later padding: 0 in the same rule — and was redone rather than counted.

Live evidence

Plan reminders: the countdown chip is now the same object as the 已触发 / 已暂停 badges it sits beside, which is the convergence the component swap buys.

image

Permissions: 影响功能 is a neutral Badge; the status chips route through the same statusBadgeVariant seam as the rest of Settings.

image

Review focus

The visual direction was chosen against a side-by-side render of every affected chip in both colour schemes. Two deliberate appearance changes worth a second opinion:

  • .providerCatalogBadge had a comment reading "compact squared corners, never pills". Badge hardcodes --radius-full with no radius prop, so that intent is retired rather than kept as an override.
  • The permission capability chip had a note that a granted permission "doesn't need a green pill" and fell through to neutral. One tone table for the page beat a per-chip exception, so granted reads success like every other status badge in Settings.

@Astro-Han
Astro-Hanforce-pushed the fix/chips-pin-box-height branch 2 times, most recently from 5fdb294 to 1db0e8eCompareAugust 2, 2026 07:13
@Astro-HanAstro-Han changed the title fix(ui): pin the box height of single-line chips to the control rulerfix(ui): pin single-line chip box heights to the control rulerAug 2, 2026
@Astro-HanAstro-Han changed the title fix(ui): pin single-line chip box heights to the control rulerfix(ui): move the single-line chip family onto Astryx BadgeAug 2, 2026
Squashed for rebase onto #1893; final message written at the end.
@Astro-Han
Astro-Hanforce-pushed the fix/chips-pin-box-height branch from 38aae0f to 5263398CompareAugust 2, 2026 10:14
… exemption
Review of the previous revision found three ways the guards were narrower than
their own documentation, all of the same shape: a check that reports green
because it cannot see the thing it claims to govern.
- `isChip` required a rule to declare its own type. A compound chip delegates
type to a child and a simple one may just inherit, so the arm was hiding
three shipping chips with the #1879 defect while reporting the other 15
green. Two arms — pill radius and padding — see all 20 pill boxes, and no
longer read a vocabulary that #1893 already moved once, so the unanchored
`chips.length >= 8` floor that watched for exactly that drift is deleted with
it.
- The exemption set was one list honoured by skipping, so a mutation adding
`height: 1px` to a rule excused as component-sized stayed green. It is now
three reason-groups, each asserted: WRAPS must declare no block size (and is
the wrap contract's own list rather than a second copy of it), COMPONENT_OWNED
must declare none at all, PINNED_OFF_RULER must still be pinned.
- The Badge contract read the unconditional view, on which
`.settingsHealthBlockerBadge` — which lives entirely inside a breakpoint —
merged to nothing, so the exemption naming it suppressed nothing. It reads a
conditional-inclusive view now and asserts that the release is real.
Also: `min-height`/`max-height`/`padding-inline-start` all constrained a box
while passing both checks; the ruler accepted tiers it does not define; and the
JSX scanner ended a tag at the first `>` in a prop, and then, once rewritten,
at an apostrophe inside a prop comment — which silently dropped three live call
sites until the mutation sweep caught it.
Four boxes the widened scan surfaced, each measured live before its tier:
`.maka-quote-chip-collapsed` and `.maka-deep-research-run-count` at 24px
natural (pixel-neutral on `--h-control-sm`), `.maka-firstrun-step` at 29px
(`--h-control-lg`, +3px), and `.maka-mcp-install-button`, a bare 34px square
that swaps with a 28px `Button size="sm"` in the same slot.
…ip contracts
Every finding below was reproduced as a mutation that passed green, and each
fix is re-verified by the same mutation failing. Four root causes, three of
which delete something.
The predicate read text, not declarations. `/padding/` also matches
`background-clip: padding-box`, which is how a scrollbar thumb — padding-less,
and a box no reader perceives as an object — entered the chip population and
earned an exemption group of its own. The group is deleted with the substring
that created it, and the padding vocabulary becomes one shared authority.
The same guard-narrower-than-CSS mistake, twice more. The Badge contract's
`OWNED` gained the logical padding family and lost `padding-top/right/bottom/
left`, so `padding-left: 9px` on a Badge className redrew the component's box
while green. And the off-ruler exemption proved "still pinned" with the
constrains-a-block-size vocabulary, so rewriting its `height` as `min-height`
handed the box back to the line box and stayed green.
Which merge view follows from which question, applied to all three sites
rather than one. A must-NOT contract has no conditional distinction: pinning a
wrapping row, or re-adding the height override #1879 removed, inside
`@media (max-width: 620px)` passed the unconditional view. A must-HOLD PAIR
needs the opposite — the flattened view let a Badge release `height` in one
breakpoint and `white-space` in the mutually exclusive one, releasing the box
at no viewport at all, so releases are now read per cascade context.
A scan that cannot read a call site has to say so. `<Badge {...{ className:
'x' }} />` is legal JSX that the static scan cannot read and the `className={`
scan does not match, so both contracts concluded there was nothing to govern.
Spreads join computed classNames as unreadable; `//` prop comments parse.
(TypeScript 7 is the Go port and ships no JS compiler API, so an AST is not
available to reach for — but the property that matters is loud-vs-silent, not
lexer-vs-parser.)
Three smaller ones: rungs are read from the ruler's own `:root` scope, so a
`--h-control-*` declared under `.dark` is no longer a tier a chip may name;
`--radius-pill` must exist, because renaming it reported CHIP POPULATION 0 and
passed; `.maka-sidebar-update-button` returns to COMPONENT_OWNED, where an
entry that was dead weight as a skip is the only assertion reaching it.
The e2e no longer credits the tier-choice arithmetic to a check that does not
exist. Measured with a throwaway probe: all four newly pinned chips resolve to
zero nodes in both booted fixtures, so that choice is unasserted, and the
comment says so.
Verified: 10/10 mutations caught, each confirmed to land before being trusted.
lint, format:check, typecheck, desktop 1364, ui 241, e2e 77, check-dead-css.
@Astro-Han
Astro-Han marked this pull request as ready for review August 2, 2026 11:46
@Astro-Han
Astro-Han merged commit a314d4f into mainAug 2, 2026
5 checks passed
@Astro-Han
Astro-Han deleted the fix/chips-pin-box-height branch August 2, 2026 11:46
Sign up for freeto 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.

fix(ui): pin the box height of single-line chips so leading cannot move them

1 participant

@Astro-Han
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' fix(ui): move the single-line chip family onto Astryx Badge by Astro-Han · Pull Request #1881 · apache/maka · GitHub
Skip to content

fix(ui): move the single-line chip family onto Astryx Badge - #1881

Merged
Astro-Han merged 3 commits into
mainfrom
fix/chips-pin-box-height
Aug 2, 2026
Merged

fix(ui): move the single-line chip family onto Astryx Badge#1881
Astro-Han merged 3 commits into
mainfrom
fix/chips-pin-box-height

Conversation

@Astro-Han

@Astro-HanAstro-Han commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Summary

Closes#1879.

The issue asked for pinned box heights so a line-height change can no longer move layout. Pinning was the right diagnosis and — for most of the family — the wrong owner. Astryx already ships Badge, and its box IS the recipe these chips hand-wrote: 20px off --spacing-5, pill radius, supporting tier, align-items: center, nowrap. Twelve chips are <Badge variant=...> at the call site now, and their height, chrome and per-state colour left product CSS with them. Net +347 / −304 across 27 files: the conversion pays for the guards.

What stays product CSS is what Badge does not do — a chip with no chrome at all (.maka-message-time-inline, .maka-nav-kbd), one that truncates (.maka-skill-governance-summary span), and the compound permission pill that holds a Badge inside it. Those keep a --h-control-* pin.

Three defects the first revision shipped

Eleven pins sat below their own arithmetic floor. The floor rule (line box + padding + border, under border-box) was applied by hand in four CSS comments and skipped everywhere else. Measured live, those chips were 2–4px shorter than they read and their declared vertical padding was vertically inert:

settingsAboutVersion pinned 20 natural 24 pad 2/2 ink 上 0.5px
maka-list-row-stale-pill 20 22 pad 1/1
maka-turn-truncation-badge 20 22 border 1/1 scroll 19 > client 18

Not a clipping bug — the line box centres and the ink fits, verified including CJK — but an undocumented shrink. The rule now lives in the e2e, not in prose: a chip's height: auto natural height must fit its pinned tier.

The derived scan was a narrower whitelist. Three independent bypasses, all now closed and mutation-verified:

bypassconsequence
three features required in ONE rule body.plan-proposal-revision was invisible, five lines from a chip the scan caught
"declared anywhere" instead of the effective declarationheight: var(--h-control-xs); … height: auto passed both checks while the browser saw no pin
parseCssBlocks keyed multi-line selector lists by their last linetwo exemptions matched zero rules; appending an exempt selector to a group exempted the whole group

The containment assertion was inert twice over. It read scrollHeight/clientHeightafter restoring the stressed leading, and on an overflow: visible box the two are equal regardless. Replaced with the floor measure, which can actually fail.

Astryx layering

  • --h-control-md/lg/xl now derive from --size-element-sm/md/lg instead of restating 28/32/36, so a chip and the Astryx control beside it can no longer agree by coincidence. Verified live: md → 28, lg → 32.
  • The three composer .astryx-selector rules were forcing an Astryx Selector to 24px — below its own smallest tier (--size-element-sm = 28) — and desyncing it from ModelChipStatic, the inert state of the same chip, a Button size="sm" at 28px. The pill changed height when it became clickable. Both declare no height now. Their contract exemption previously read "declaring a height here would reintroduce the override fix(ui): pin the box height of single-line chips so leading cannot move them #1879 removed" while the rule declared exactly that; the reason is true of every entry under it now.
  • .maka-sidebar-update-button used the background shorthand, which also resets background-image — where Astryx ghost keeps its hover and pressed overlays. The variant the component was passed had been silently cancelled. Radius moves to --_button-radius; four declarations that restated Button's own computed values are gone.

What was tried and reverted

Widening the scan to --radius-control so squared badges were covered: it reported 27 offenders including pre blocks, failure banners, sidebar rows and buttons — every one a box a pin would clip. --radius-control is this repo's general control shape, not a chip signal. The squared chips that motivated it are Badges now, so that population is empty rather than unguarded.

Verification

npm run lint 0 · npm --workspace @maka/desktop run typecheck 0 · desktop tests 1325 pass · ui tests 239 pass · e2e 73/73 · contract 15/15 · check-dead-css --check clean · astryx:theme --check current.

npm run format:check fails on scripts/check-console.mjs, which this branch does not touch (git diff main...HEAD -- scripts/check-console.mjs is empty) — inherited from #1880.

Every guard mutation-verified with cp backup/restore:

mutationresult
height: var(--h-control-xs); … height: auto✖ 2 (passed the previous revision)
chip split across two rules✖ 2
exempt selector appended to a group✖ 2
plain unpinned chip✖ 1
pinned but display: block✖ 1
height added to a wrapping box✖ 1
chrome-less chip unpinned✖ 1
tier below its own floor (e2e)Received: 28 vs ≤ 20
pin removed entirely (e2e)

All restored green. The first attempt at the floor mutation was ineffective — the injected padding-block was overridden by a later padding: 0 in the same rule — and was redone rather than counted.

Live evidence

Plan reminders: the countdown chip is now the same object as the 已触发 / 已暂停 badges it sits beside, which is the convergence the component swap buys.

image

Permissions: 影响功能 is a neutral Badge; the status chips route through the same statusBadgeVariant seam as the rest of Settings.

image

Review focus

The visual direction was chosen against a side-by-side render of every affected chip in both colour schemes. Two deliberate appearance changes worth a second opinion:

  • .providerCatalogBadge had a comment reading "compact squared corners, never pills". Badge hardcodes --radius-full with no radius prop, so that intent is retired rather than kept as an override.
  • The permission capability chip had a note that a granted permission "doesn't need a green pill" and fell through to neutral. One tone table for the page beat a per-chip exception, so granted reads success like every other status badge in Settings.

@Astro-Han
Astro-Hanforce-pushed the fix/chips-pin-box-height branch 2 times, most recently from 5fdb294 to 1db0e8eCompareAugust 2, 2026 07:13
@Astro-HanAstro-Han changed the title fix(ui): pin the box height of single-line chips to the control rulerfix(ui): pin single-line chip box heights to the control rulerAug 2, 2026
@Astro-HanAstro-Han changed the title fix(ui): pin single-line chip box heights to the control rulerfix(ui): move the single-line chip family onto Astryx BadgeAug 2, 2026
Squashed for rebase onto #1893; final message written at the end.
@Astro-Han
Astro-Hanforce-pushed the fix/chips-pin-box-height branch from 38aae0f to 5263398CompareAugust 2, 2026 10:14
… exemption
Review of the previous revision found three ways the guards were narrower than
their own documentation, all of the same shape: a check that reports green
because it cannot see the thing it claims to govern.
- `isChip` required a rule to declare its own type. A compound chip delegates
type to a child and a simple one may just inherit, so the arm was hiding
three shipping chips with the #1879 defect while reporting the other 15
green. Two arms — pill radius and padding — see all 20 pill boxes, and no
longer read a vocabulary that #1893 already moved once, so the unanchored
`chips.length >= 8` floor that watched for exactly that drift is deleted with
it.
- The exemption set was one list honoured by skipping, so a mutation adding
`height: 1px` to a rule excused as component-sized stayed green. It is now
three reason-groups, each asserted: WRAPS must declare no block size (and is
the wrap contract's own list rather than a second copy of it), COMPONENT_OWNED
must declare none at all, PINNED_OFF_RULER must still be pinned.
- The Badge contract read the unconditional view, on which
`.settingsHealthBlockerBadge` — which lives entirely inside a breakpoint —
merged to nothing, so the exemption naming it suppressed nothing. It reads a
conditional-inclusive view now and asserts that the release is real.
Also: `min-height`/`max-height`/`padding-inline-start` all constrained a box
while passing both checks; the ruler accepted tiers it does not define; and the
JSX scanner ended a tag at the first `>` in a prop, and then, once rewritten,
at an apostrophe inside a prop comment — which silently dropped three live call
sites until the mutation sweep caught it.
Four boxes the widened scan surfaced, each measured live before its tier:
`.maka-quote-chip-collapsed` and `.maka-deep-research-run-count` at 24px
natural (pixel-neutral on `--h-control-sm`), `.maka-firstrun-step` at 29px
(`--h-control-lg`, +3px), and `.maka-mcp-install-button`, a bare 34px square
that swaps with a 28px `Button size="sm"` in the same slot.
…ip contracts
Every finding below was reproduced as a mutation that passed green, and each
fix is re-verified by the same mutation failing. Four root causes, three of
which delete something.
The predicate read text, not declarations. `/padding/` also matches
`background-clip: padding-box`, which is how a scrollbar thumb — padding-less,
and a box no reader perceives as an object — entered the chip population and
earned an exemption group of its own. The group is deleted with the substring
that created it, and the padding vocabulary becomes one shared authority.
The same guard-narrower-than-CSS mistake, twice more. The Badge contract's
`OWNED` gained the logical padding family and lost `padding-top/right/bottom/
left`, so `padding-left: 9px` on a Badge className redrew the component's box
while green. And the off-ruler exemption proved "still pinned" with the
constrains-a-block-size vocabulary, so rewriting its `height` as `min-height`
handed the box back to the line box and stayed green.
Which merge view follows from which question, applied to all three sites
rather than one. A must-NOT contract has no conditional distinction: pinning a
wrapping row, or re-adding the height override #1879 removed, inside
`@media (max-width: 620px)` passed the unconditional view. A must-HOLD PAIR
needs the opposite — the flattened view let a Badge release `height` in one
breakpoint and `white-space` in the mutually exclusive one, releasing the box
at no viewport at all, so releases are now read per cascade context.
A scan that cannot read a call site has to say so. `<Badge {...{ className:
'x' }} />` is legal JSX that the static scan cannot read and the `className={`
scan does not match, so both contracts concluded there was nothing to govern.
Spreads join computed classNames as unreadable; `//` prop comments parse.
(TypeScript 7 is the Go port and ships no JS compiler API, so an AST is not
available to reach for — but the property that matters is loud-vs-silent, not
lexer-vs-parser.)
Three smaller ones: rungs are read from the ruler's own `:root` scope, so a
`--h-control-*` declared under `.dark` is no longer a tier a chip may name;
`--radius-pill` must exist, because renaming it reported CHIP POPULATION 0 and
passed; `.maka-sidebar-update-button` returns to COMPONENT_OWNED, where an
entry that was dead weight as a skip is the only assertion reaching it.
The e2e no longer credits the tier-choice arithmetic to a check that does not
exist. Measured with a throwaway probe: all four newly pinned chips resolve to
zero nodes in both booted fixtures, so that choice is unasserted, and the
comment says so.
Verified: 10/10 mutations caught, each confirmed to land before being trusted.
lint, format:check, typecheck, desktop 1364, ui 241, e2e 77, check-dead-css.
@Astro-Han
Astro-Han marked this pull request as ready for review August 2, 2026 11:46
@Astro-Han
Astro-Han merged commit a314d4f into mainAug 2, 2026
5 checks passed
@Astro-Han
Astro-Han deleted the fix/chips-pin-box-height branch August 2, 2026 11:46
Sign up for freeto 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.

fix(ui): pin the box height of single-line chips so leading cannot move them

1 participant

@Astro-Han
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' fix(ui): move the single-line chip family onto Astryx Badge by Astro-Han · Pull Request #1881 · apache/maka · GitHub
Skip to content

fix(ui): move the single-line chip family onto Astryx Badge - #1881

Merged
Astro-Han merged 3 commits into
mainfrom
fix/chips-pin-box-height
Aug 2, 2026
Merged

fix(ui): move the single-line chip family onto Astryx Badge#1881
Astro-Han merged 3 commits into
mainfrom
fix/chips-pin-box-height

Conversation

@Astro-Han

@Astro-HanAstro-Han commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Summary

Closes#1879.

The issue asked for pinned box heights so a line-height change can no longer move layout. Pinning was the right diagnosis and — for most of the family — the wrong owner. Astryx already ships Badge, and its box IS the recipe these chips hand-wrote: 20px off --spacing-5, pill radius, supporting tier, align-items: center, nowrap. Twelve chips are <Badge variant=...> at the call site now, and their height, chrome and per-state colour left product CSS with them. Net +347 / −304 across 27 files: the conversion pays for the guards.

What stays product CSS is what Badge does not do — a chip with no chrome at all (.maka-message-time-inline, .maka-nav-kbd), one that truncates (.maka-skill-governance-summary span), and the compound permission pill that holds a Badge inside it. Those keep a --h-control-* pin.

Three defects the first revision shipped

Eleven pins sat below their own arithmetic floor. The floor rule (line box + padding + border, under border-box) was applied by hand in four CSS comments and skipped everywhere else. Measured live, those chips were 2–4px shorter than they read and their declared vertical padding was vertically inert:

settingsAboutVersion pinned 20 natural 24 pad 2/2 ink 上 0.5px
maka-list-row-stale-pill 20 22 pad 1/1
maka-turn-truncation-badge 20 22 border 1/1 scroll 19 > client 18

Not a clipping bug — the line box centres and the ink fits, verified including CJK — but an undocumented shrink. The rule now lives in the e2e, not in prose: a chip's height: auto natural height must fit its pinned tier.

The derived scan was a narrower whitelist. Three independent bypasses, all now closed and mutation-verified:

bypassconsequence
three features required in ONE rule body.plan-proposal-revision was invisible, five lines from a chip the scan caught
"declared anywhere" instead of the effective declarationheight: var(--h-control-xs); … height: auto passed both checks while the browser saw no pin
parseCssBlocks keyed multi-line selector lists by their last linetwo exemptions matched zero rules; appending an exempt selector to a group exempted the whole group

The containment assertion was inert twice over. It read scrollHeight/clientHeightafter restoring the stressed leading, and on an overflow: visible box the two are equal regardless. Replaced with the floor measure, which can actually fail.

Astryx layering

  • --h-control-md/lg/xl now derive from --size-element-sm/md/lg instead of restating 28/32/36, so a chip and the Astryx control beside it can no longer agree by coincidence. Verified live: md → 28, lg → 32.
  • The three composer .astryx-selector rules were forcing an Astryx Selector to 24px — below its own smallest tier (--size-element-sm = 28) — and desyncing it from ModelChipStatic, the inert state of the same chip, a Button size="sm" at 28px. The pill changed height when it became clickable. Both declare no height now. Their contract exemption previously read "declaring a height here would reintroduce the override fix(ui): pin the box height of single-line chips so leading cannot move them #1879 removed" while the rule declared exactly that; the reason is true of every entry under it now.
  • .maka-sidebar-update-button used the background shorthand, which also resets background-image — where Astryx ghost keeps its hover and pressed overlays. The variant the component was passed had been silently cancelled. Radius moves to --_button-radius; four declarations that restated Button's own computed values are gone.

What was tried and reverted

Widening the scan to --radius-control so squared badges were covered: it reported 27 offenders including pre blocks, failure banners, sidebar rows and buttons — every one a box a pin would clip. --radius-control is this repo's general control shape, not a chip signal. The squared chips that motivated it are Badges now, so that population is empty rather than unguarded.

Verification

npm run lint 0 · npm --workspace @maka/desktop run typecheck 0 · desktop tests 1325 pass · ui tests 239 pass · e2e 73/73 · contract 15/15 · check-dead-css --check clean · astryx:theme --check current.

npm run format:check fails on scripts/check-console.mjs, which this branch does not touch (git diff main...HEAD -- scripts/check-console.mjs is empty) — inherited from #1880.

Every guard mutation-verified with cp backup/restore:

mutationresult
height: var(--h-control-xs); … height: auto✖ 2 (passed the previous revision)
chip split across two rules✖ 2
exempt selector appended to a group✖ 2
plain unpinned chip✖ 1
pinned but display: block✖ 1
height added to a wrapping box✖ 1
chrome-less chip unpinned✖ 1
tier below its own floor (e2e)Received: 28 vs ≤ 20
pin removed entirely (e2e)

All restored green. The first attempt at the floor mutation was ineffective — the injected padding-block was overridden by a later padding: 0 in the same rule — and was redone rather than counted.

Live evidence

Plan reminders: the countdown chip is now the same object as the 已触发 / 已暂停 badges it sits beside, which is the convergence the component swap buys.

image

Permissions: 影响功能 is a neutral Badge; the status chips route through the same statusBadgeVariant seam as the rest of Settings.

image

Review focus

The visual direction was chosen against a side-by-side render of every affected chip in both colour schemes. Two deliberate appearance changes worth a second opinion:

  • .providerCatalogBadge had a comment reading "compact squared corners, never pills". Badge hardcodes --radius-full with no radius prop, so that intent is retired rather than kept as an override.
  • The permission capability chip had a note that a granted permission "doesn't need a green pill" and fell through to neutral. One tone table for the page beat a per-chip exception, so granted reads success like every other status badge in Settings.

@Astro-Han
Astro-Hanforce-pushed the fix/chips-pin-box-height branch 2 times, most recently from 5fdb294 to 1db0e8eCompareAugust 2, 2026 07:13
@Astro-HanAstro-Han changed the title fix(ui): pin the box height of single-line chips to the control rulerfix(ui): pin single-line chip box heights to the control rulerAug 2, 2026
@Astro-HanAstro-Han changed the title fix(ui): pin single-line chip box heights to the control rulerfix(ui): move the single-line chip family onto Astryx BadgeAug 2, 2026
Squashed for rebase onto #1893; final message written at the end.
@Astro-Han
Astro-Hanforce-pushed the fix/chips-pin-box-height branch from 38aae0f to 5263398CompareAugust 2, 2026 10:14
… exemption
Review of the previous revision found three ways the guards were narrower than
their own documentation, all of the same shape: a check that reports green
because it cannot see the thing it claims to govern.
- `isChip` required a rule to declare its own type. A compound chip delegates
type to a child and a simple one may just inherit, so the arm was hiding
three shipping chips with the #1879 defect while reporting the other 15
green. Two arms — pill radius and padding — see all 20 pill boxes, and no
longer read a vocabulary that #1893 already moved once, so the unanchored
`chips.length >= 8` floor that watched for exactly that drift is deleted with
it.
- The exemption set was one list honoured by skipping, so a mutation adding
`height: 1px` to a rule excused as component-sized stayed green. It is now
three reason-groups, each asserted: WRAPS must declare no block size (and is
the wrap contract's own list rather than a second copy of it), COMPONENT_OWNED
must declare none at all, PINNED_OFF_RULER must still be pinned.
- The Badge contract read the unconditional view, on which
`.settingsHealthBlockerBadge` — which lives entirely inside a breakpoint —
merged to nothing, so the exemption naming it suppressed nothing. It reads a
conditional-inclusive view now and asserts that the release is real.
Also: `min-height`/`max-height`/`padding-inline-start` all constrained a box
while passing both checks; the ruler accepted tiers it does not define; and the
JSX scanner ended a tag at the first `>` in a prop, and then, once rewritten,
at an apostrophe inside a prop comment — which silently dropped three live call
sites until the mutation sweep caught it.
Four boxes the widened scan surfaced, each measured live before its tier:
`.maka-quote-chip-collapsed` and `.maka-deep-research-run-count` at 24px
natural (pixel-neutral on `--h-control-sm`), `.maka-firstrun-step` at 29px
(`--h-control-lg`, +3px), and `.maka-mcp-install-button`, a bare 34px square
that swaps with a 28px `Button size="sm"` in the same slot.
…ip contracts
Every finding below was reproduced as a mutation that passed green, and each
fix is re-verified by the same mutation failing. Four root causes, three of
which delete something.
The predicate read text, not declarations. `/padding/` also matches
`background-clip: padding-box`, which is how a scrollbar thumb — padding-less,
and a box no reader perceives as an object — entered the chip population and
earned an exemption group of its own. The group is deleted with the substring
that created it, and the padding vocabulary becomes one shared authority.
The same guard-narrower-than-CSS mistake, twice more. The Badge contract's
`OWNED` gained the logical padding family and lost `padding-top/right/bottom/
left`, so `padding-left: 9px` on a Badge className redrew the component's box
while green. And the off-ruler exemption proved "still pinned" with the
constrains-a-block-size vocabulary, so rewriting its `height` as `min-height`
handed the box back to the line box and stayed green.
Which merge view follows from which question, applied to all three sites
rather than one. A must-NOT contract has no conditional distinction: pinning a
wrapping row, or re-adding the height override #1879 removed, inside
`@media (max-width: 620px)` passed the unconditional view. A must-HOLD PAIR
needs the opposite — the flattened view let a Badge release `height` in one
breakpoint and `white-space` in the mutually exclusive one, releasing the box
at no viewport at all, so releases are now read per cascade context.
A scan that cannot read a call site has to say so. `<Badge {...{ className:
'x' }} />` is legal JSX that the static scan cannot read and the `className={`
scan does not match, so both contracts concluded there was nothing to govern.
Spreads join computed classNames as unreadable; `//` prop comments parse.
(TypeScript 7 is the Go port and ships no JS compiler API, so an AST is not
available to reach for — but the property that matters is loud-vs-silent, not
lexer-vs-parser.)
Three smaller ones: rungs are read from the ruler's own `:root` scope, so a
`--h-control-*` declared under `.dark` is no longer a tier a chip may name;
`--radius-pill` must exist, because renaming it reported CHIP POPULATION 0 and
passed; `.maka-sidebar-update-button` returns to COMPONENT_OWNED, where an
entry that was dead weight as a skip is the only assertion reaching it.
The e2e no longer credits the tier-choice arithmetic to a check that does not
exist. Measured with a throwaway probe: all four newly pinned chips resolve to
zero nodes in both booted fixtures, so that choice is unasserted, and the
comment says so.
Verified: 10/10 mutations caught, each confirmed to land before being trusted.
lint, format:check, typecheck, desktop 1364, ui 241, e2e 77, check-dead-css.
@Astro-Han
Astro-Han marked this pull request as ready for review August 2, 2026 11:46
@Astro-Han
Astro-Han merged commit a314d4f into mainAug 2, 2026
5 checks passed
@Astro-Han
Astro-Han deleted the fix/chips-pin-box-height branch August 2, 2026 11:46
Sign up for freeto 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.

fix(ui): pin the box height of single-line chips so leading cannot move them

1 participant

@Astro-Han
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); })(); fix(ui): move the single-line chip family onto Astryx Badge by Astro-Han · Pull Request #1881 · apache/maka · GitHub
Skip to content

fix(ui): move the single-line chip family onto Astryx Badge - #1881

Merged
Astro-Han merged 3 commits into
mainfrom
fix/chips-pin-box-height
Aug 2, 2026
Merged

fix(ui): move the single-line chip family onto Astryx Badge#1881
Astro-Han merged 3 commits into
mainfrom
fix/chips-pin-box-height

Conversation

@Astro-Han

@Astro-HanAstro-Han commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Summary

Closes#1879.

The issue asked for pinned box heights so a line-height change can no longer move layout. Pinning was the right diagnosis and — for most of the family — the wrong owner. Astryx already ships Badge, and its box IS the recipe these chips hand-wrote: 20px off --spacing-5, pill radius, supporting tier, align-items: center, nowrap. Twelve chips are <Badge variant=...> at the call site now, and their height, chrome and per-state colour left product CSS with them. Net +347 / −304 across 27 files: the conversion pays for the guards.

What stays product CSS is what Badge does not do — a chip with no chrome at all (.maka-message-time-inline, .maka-nav-kbd), one that truncates (.maka-skill-governance-summary span), and the compound permission pill that holds a Badge inside it. Those keep a --h-control-* pin.

Three defects the first revision shipped

Eleven pins sat below their own arithmetic floor. The floor rule (line box + padding + border, under border-box) was applied by hand in four CSS comments and skipped everywhere else. Measured live, those chips were 2–4px shorter than they read and their declared vertical padding was vertically inert:

settingsAboutVersion pinned 20 natural 24 pad 2/2 ink 上 0.5px
maka-list-row-stale-pill 20 22 pad 1/1
maka-turn-truncation-badge 20 22 border 1/1 scroll 19 > client 18

Not a clipping bug — the line box centres and the ink fits, verified including CJK — but an undocumented shrink. The rule now lives in the e2e, not in prose: a chip's height: auto natural height must fit its pinned tier.

The derived scan was a narrower whitelist. Three independent bypasses, all now closed and mutation-verified:

bypassconsequence
three features required in ONE rule body.plan-proposal-revision was invisible, five lines from a chip the scan caught
"declared anywhere" instead of the effective declarationheight: var(--h-control-xs); … height: auto passed both checks while the browser saw no pin
parseCssBlocks keyed multi-line selector lists by their last linetwo exemptions matched zero rules; appending an exempt selector to a group exempted the whole group

The containment assertion was inert twice over. It read scrollHeight/clientHeightafter restoring the stressed leading, and on an overflow: visible box the two are equal regardless. Replaced with the floor measure, which can actually fail.

Astryx layering

  • --h-control-md/lg/xl now derive from --size-element-sm/md/lg instead of restating 28/32/36, so a chip and the Astryx control beside it can no longer agree by coincidence. Verified live: md → 28, lg → 32.
  • The three composer .astryx-selector rules were forcing an Astryx Selector to 24px — below its own smallest tier (--size-element-sm = 28) — and desyncing it from ModelChipStatic, the inert state of the same chip, a Button size="sm" at 28px. The pill changed height when it became clickable. Both declare no height now. Their contract exemption previously read "declaring a height here would reintroduce the override fix(ui): pin the box height of single-line chips so leading cannot move them #1879 removed" while the rule declared exactly that; the reason is true of every entry under it now.
  • .maka-sidebar-update-button used the background shorthand, which also resets background-image — where Astryx ghost keeps its hover and pressed overlays. The variant the component was passed had been silently cancelled. Radius moves to --_button-radius; four declarations that restated Button's own computed values are gone.

What was tried and reverted

Widening the scan to --radius-control so squared badges were covered: it reported 27 offenders including pre blocks, failure banners, sidebar rows and buttons — every one a box a pin would clip. --radius-control is this repo's general control shape, not a chip signal. The squared chips that motivated it are Badges now, so that population is empty rather than unguarded.

Verification

npm run lint 0 · npm --workspace @maka/desktop run typecheck 0 · desktop tests 1325 pass · ui tests 239 pass · e2e 73/73 · contract 15/15 · check-dead-css --check clean · astryx:theme --check current.

npm run format:check fails on scripts/check-console.mjs, which this branch does not touch (git diff main...HEAD -- scripts/check-console.mjs is empty) — inherited from #1880.

Every guard mutation-verified with cp backup/restore:

mutationresult
height: var(--h-control-xs); … height: auto✖ 2 (passed the previous revision)
chip split across two rules✖ 2
exempt selector appended to a group✖ 2
plain unpinned chip✖ 1
pinned but display: block✖ 1
height added to a wrapping box✖ 1
chrome-less chip unpinned✖ 1
tier below its own floor (e2e)Received: 28 vs ≤ 20
pin removed entirely (e2e)

All restored green. The first attempt at the floor mutation was ineffective — the injected padding-block was overridden by a later padding: 0 in the same rule — and was redone rather than counted.

Live evidence

Plan reminders: the countdown chip is now the same object as the 已触发 / 已暂停 badges it sits beside, which is the convergence the component swap buys.

image

Permissions: 影响功能 is a neutral Badge; the status chips route through the same statusBadgeVariant seam as the rest of Settings.

image

Review focus

The visual direction was chosen against a side-by-side render of every affected chip in both colour schemes. Two deliberate appearance changes worth a second opinion:

  • .providerCatalogBadge had a comment reading "compact squared corners, never pills". Badge hardcodes --radius-full with no radius prop, so that intent is retired rather than kept as an override.
  • The permission capability chip had a note that a granted permission "doesn't need a green pill" and fell through to neutral. One tone table for the page beat a per-chip exception, so granted reads success like every other status badge in Settings.

@Astro-Han
Astro-Hanforce-pushed the fix/chips-pin-box-height branch 2 times, most recently from 5fdb294 to 1db0e8eCompareAugust 2, 2026 07:13
@Astro-HanAstro-Han changed the title fix(ui): pin the box height of single-line chips to the control rulerfix(ui): pin single-line chip box heights to the control rulerAug 2, 2026
@Astro-HanAstro-Han changed the title fix(ui): pin single-line chip box heights to the control rulerfix(ui): move the single-line chip family onto Astryx BadgeAug 2, 2026
Squashed for rebase onto #1893; final message written at the end.
@Astro-Han
Astro-Hanforce-pushed the fix/chips-pin-box-height branch from 38aae0f to 5263398CompareAugust 2, 2026 10:14
… exemption
Review of the previous revision found three ways the guards were narrower than
their own documentation, all of the same shape: a check that reports green
because it cannot see the thing it claims to govern.
- `isChip` required a rule to declare its own type. A compound chip delegates
type to a child and a simple one may just inherit, so the arm was hiding
three shipping chips with the #1879 defect while reporting the other 15
green. Two arms — pill radius and padding — see all 20 pill boxes, and no
longer read a vocabulary that #1893 already moved once, so the unanchored
`chips.length >= 8` floor that watched for exactly that drift is deleted with
it.
- The exemption set was one list honoured by skipping, so a mutation adding
`height: 1px` to a rule excused as component-sized stayed green. It is now
three reason-groups, each asserted: WRAPS must declare no block size (and is
the wrap contract's own list rather than a second copy of it), COMPONENT_OWNED
must declare none at all, PINNED_OFF_RULER must still be pinned.
- The Badge contract read the unconditional view, on which
`.settingsHealthBlockerBadge` — which lives entirely inside a breakpoint —
merged to nothing, so the exemption naming it suppressed nothing. It reads a
conditional-inclusive view now and asserts that the release is real.
Also: `min-height`/`max-height`/`padding-inline-start` all constrained a box
while passing both checks; the ruler accepted tiers it does not define; and the
JSX scanner ended a tag at the first `>` in a prop, and then, once rewritten,
at an apostrophe inside a prop comment — which silently dropped three live call
sites until the mutation sweep caught it.
Four boxes the widened scan surfaced, each measured live before its tier:
`.maka-quote-chip-collapsed` and `.maka-deep-research-run-count` at 24px
natural (pixel-neutral on `--h-control-sm`), `.maka-firstrun-step` at 29px
(`--h-control-lg`, +3px), and `.maka-mcp-install-button`, a bare 34px square
that swaps with a 28px `Button size="sm"` in the same slot.
…ip contracts
Every finding below was reproduced as a mutation that passed green, and each
fix is re-verified by the same mutation failing. Four root causes, three of
which delete something.
The predicate read text, not declarations. `/padding/` also matches
`background-clip: padding-box`, which is how a scrollbar thumb — padding-less,
and a box no reader perceives as an object — entered the chip population and
earned an exemption group of its own. The group is deleted with the substring
that created it, and the padding vocabulary becomes one shared authority.
The same guard-narrower-than-CSS mistake, twice more. The Badge contract's
`OWNED` gained the logical padding family and lost `padding-top/right/bottom/
left`, so `padding-left: 9px` on a Badge className redrew the component's box
while green. And the off-ruler exemption proved "still pinned" with the
constrains-a-block-size vocabulary, so rewriting its `height` as `min-height`
handed the box back to the line box and stayed green.
Which merge view follows from which question, applied to all three sites
rather than one. A must-NOT contract has no conditional distinction: pinning a
wrapping row, or re-adding the height override #1879 removed, inside
`@media (max-width: 620px)` passed the unconditional view. A must-HOLD PAIR
needs the opposite — the flattened view let a Badge release `height` in one
breakpoint and `white-space` in the mutually exclusive one, releasing the box
at no viewport at all, so releases are now read per cascade context.
A scan that cannot read a call site has to say so. `<Badge {...{ className:
'x' }} />` is legal JSX that the static scan cannot read and the `className={`
scan does not match, so both contracts concluded there was nothing to govern.
Spreads join computed classNames as unreadable; `//` prop comments parse.
(TypeScript 7 is the Go port and ships no JS compiler API, so an AST is not
available to reach for — but the property that matters is loud-vs-silent, not
lexer-vs-parser.)
Three smaller ones: rungs are read from the ruler's own `:root` scope, so a
`--h-control-*` declared under `.dark` is no longer a tier a chip may name;
`--radius-pill` must exist, because renaming it reported CHIP POPULATION 0 and
passed; `.maka-sidebar-update-button` returns to COMPONENT_OWNED, where an
entry that was dead weight as a skip is the only assertion reaching it.
The e2e no longer credits the tier-choice arithmetic to a check that does not
exist. Measured with a throwaway probe: all four newly pinned chips resolve to
zero nodes in both booted fixtures, so that choice is unasserted, and the
comment says so.
Verified: 10/10 mutations caught, each confirmed to land before being trusted.
lint, format:check, typecheck, desktop 1364, ui 241, e2e 77, check-dead-css.
@Astro-Han
Astro-Han marked this pull request as ready for review August 2, 2026 11:46
@Astro-Han
Astro-Han merged commit a314d4f into mainAug 2, 2026
5 checks passed
@Astro-Han
Astro-Han deleted the fix/chips-pin-box-height branch August 2, 2026 11:46
Sign up for freeto 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.

fix(ui): pin the box height of single-line chips so leading cannot move them

1 participant

@Astro-Han