fix(ai): skip search-bar role styling inside nav-tab parents

Why: end-to-end test of "Design a bottom nav with Home / Search /
Orders / Cart / Profile" surfaced a stray coloured pill highlight
wrapping the Search tab. Root cause: the model labels the cell
\`role: 'search-bar'\` (intending "this tab whose icon is search"),
and the role-resolver dutifully stamps the input-shaped 44px-tall,
22-corner, filled-surface look onto the nav cell. Inside a 56px tall
tab row that pill swallows the icon + label, looks broken on canvas,
and competes for click area with the nav-item active state.

What: search-bar role now early-outs with `{}` (no overrides) when
ctx.parentRole is one of `bottom-tab-bar` / `tab-bar` / `tab-row` —
mirroring the same check the `button` role already uses to skip its
text-button defaults inside tab containers. Nav-cell layout / fill
remains the responsibility of nav-item / nav-item-active.

1070 / 1070 AI tests still pass; the input-shape default still applies
in every other context (forms, headers, hero search, etc.).
This commit is contained in:
Fini 2026-05-09 21:19:00 +08:00
parent ac689182cc
commit 69db6a5dfc

View file

@ -309,16 +309,35 @@ registerRole('form-input', (_node, ctx) => ({
stroke: inputStroke(ctx.theme),
}));
registerRole('search-bar', (_node, ctx) => ({
layout: 'horizontal' as const,
height: 44,
padding: [10, 16] as [number, number],
gap: 8,
alignItems: 'center' as const,
cornerRadius: 22,
fill: inputFill(ctx.theme),
stroke: inputStroke(ctx.theme),
}));
registerRole('search-bar', (_node, ctx) => {
// Models sometimes mis-tag a bottom-nav "Search" tab with role
// `search-bar`, expecting it to mean "the tab whose icon is a
// magnifier". Without a guard, the input-shaped pill styling
// (44px tall + 22 corner-radius + filled surface) gets stamped onto
// the nav cell — visible as a coloured pill highlight wrapping the
// icon + label inside an otherwise transparent tab row. Detect by
// the same TAB_PARENT_ROLES set used by the button branch and
// return zero style overrides; the nav-item / nav-item-active role
// (registered separately) governs the cell layout.
if (
ctx.parentRole &&
(ctx.parentRole === 'bottom-tab-bar' ||
ctx.parentRole === 'tab-bar' ||
ctx.parentRole === 'tab-row')
) {
return {};
}
return {
layout: 'horizontal' as const,
height: 44,
padding: [10, 16] as [number, number],
gap: 8,
alignItems: 'center' as const,
cornerRadius: 22,
fill: inputFill(ctx.theme),
stroke: inputStroke(ctx.theme),
};
});
// ---------------------------------------------------------------------------
// Display roles