From 69db6a5dfcf890b3ba3007ad014b1b15fafded1c Mon Sep 17 00:00:00 2001 From: Fini Date: Sat, 9 May 2026 21:19:00 +0800 Subject: [PATCH] fix(ai): skip search-bar role styling inside nav-tab parents MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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.). --- .../src/services/ai/role-definitions/index.ts | 39 ++++++++++++++----- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/apps/web/src/services/ai/role-definitions/index.ts b/apps/web/src/services/ai/role-definitions/index.ts index 725454f19..78df42d46 100644 --- a/apps/web/src/services/ai/role-definitions/index.ts +++ b/apps/web/src/services/ai/role-definitions/index.ts @@ -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