Skip to content

Commit a48991c

Browse files
serpentbladeclaude
andcommitted
fix(command-palette): render composed palette on lit + angular; bless VR ×6
After the Svelte re-projection-collision fix (26de33c), the VR matrix exposed two MORE per-target composition-integration failures — distinct from each other and from the collision. Both fixed; CommandPaletteScreenshot now renders all 6 options ×6 and the single shared Linux baseline is blessed. Lit — the nested <Listbox> never opened. command-palette opens it by focusing the listbox's combobox <input> (the input's @Focus="open" opens the popup). The previous `panel.querySelector('input')` could not pierce <rozie-listbox>'s (open) SHADOW ROOT on Lit — the input lives in the child custom element's shadow DOM — so the Lit palette stayed closed → 0 options. focusInput now falls back to `panel.querySelector('rozie-listbox')?.shadowRoot?.querySelector('input')` on Lit; the five light-DOM targets still hit the direct input first (byte-stable behavior). A `$refs.listbox.focusControl()` handle call would be cleaner but is blocked by the refs-lowering type gap (a composed-component ref types inconsistently across targets — react: HTMLElement, solid: ListboxHandle — not uniformly as the handle). Angular — empty mount. @analogjs's NgtscProgram resolves AOT imports through tsconfig `paths`, NOT vite `resolve.alias`, so the ext-stripped stable specifier `@rozie-ui/listbox/Listbox` was unresolved → `imports: [Listbox]` collapsed to any[] → no ɵcmp → JIT fallback (compiler absent) → nothing rendered. Added a tsconfig.app.json `paths` entry mapping it to the SAME vendored copy the vite alias targets (so the AOT-analyzed class identity matches the bundled one). Verified: VR CommandPalette ×6 green (6 passed), typecheck ×6, command-palette unit 27/27, whole-repo build 226/226. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015dPKr7iUEs5duZFEcZxNS8
1 parent 26de33c commit a48991c

9 files changed

Lines changed: 77 additions & 43 deletions

File tree

packages/ui/command-palette/packages/angular/src/CommandPalette.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,7 @@ export class CommandPalette {
260260
focusInput = () => {
261261
const panel = this.panel()?.nativeElement;
262262
if (!panel) return;
263-
const input = panel.querySelector('input');
263+
const input = panel.querySelector('input') || panel.querySelector('rozie-listbox')?.shadowRoot?.querySelector('input');
264264
if (input && input.focus) input.focus();
265265
};
266266
onOpen = () => {

packages/ui/command-palette/packages/lit/src/CommandPalette.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,7 @@ ${this.open ? html`<div class="rozie-command-palette" @click=${($event: Event) =
302302
focusInput = () => {
303303
const panel = this._refPanel;
304304
if (!panel) return;
305-
const input = panel.querySelector('input');
305+
const input = panel.querySelector('input') || panel.querySelector('rozie-listbox')?.shadowRoot?.querySelector('input');
306306
if (input && input.focus) input.focus();
307307
};
308308

packages/ui/command-palette/packages/react/src/CommandPalette.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ const CommandPalette = forwardRef<CommandPaletteHandle, CommandPaletteProps>(fun
144144
function focusInput() {
145145
const panel$local = panel.current;
146146
if (!panel$local) return;
147-
const input = panel$local.querySelector('input');
147+
const input = panel$local.querySelector('input') || panel$local.querySelector('rozie-listbox')?.shadowRoot?.querySelector('input');
148148
if (input && input.focus) input.focus();
149149
}
150150
const onOpen = useCallback(() => {

packages/ui/command-palette/packages/solid/src/CommandPalette.tsx

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -242,16 +242,21 @@ export default function CommandPalette(_props: CommandPaletteProps): JSX.Element
242242
}
243243

244244
// ---- open/close reconcile ----------------------------------------------
245-
// Focus the vendored <Listbox>'s combobox <input> by reaching into the panel
246-
// element and querying for it. The Listbox owns the input now; reaching it via a
247-
// plain DOM query off command-palette's own panel ref types cleanly across all
248-
// six leaves (a `$refs.listbox` COMPONENT-handle read still types as the host
249-
// element, not the ListboxHandle — the refs-lowering type gap — so a DOM query is
250-
// the source-only path). $refs is read in a post-mount callback only (ROZ123-safe).
245+
// Focus the vendored <Listbox>'s combobox <input>; focusing it fires the
246+
// listbox's `@focus="open"` → the popup opens (the screenshot demo seeds the
247+
// palette open, so this runs on mount). The five light-DOM targets render the
248+
// input directly under the panel, so a plain `querySelector('input')` finds it.
249+
// On Lit the <Listbox> compiles to a `<rozie-listbox>` CUSTOM ELEMENT whose input
250+
// lives in its (open) SHADOW ROOT — a panel-level query can't reach it, so the
251+
// palette never opened and rendered 0 options. Fall back to piercing the child
252+
// element's open shadow root on Lit. A `$refs.listbox.focusControl()` handle call
253+
// would be cleaner but is blocked by the refs-lowering type gap (a composed-
254+
// component ref types inconsistently across targets, not as the ListboxHandle).
255+
// $refs read in a post-mount callback only (ROZ123-safe).
251256
function focusInput() {
252257
const panel = panelRef;
253258
if (!panel) return;
254-
const input = panel.querySelector('input');
259+
const input = panel.querySelector('input') || panel.querySelector('rozie-listbox')?.shadowRoot?.querySelector('input');
255260
if (input && input.focus) input.focus();
256261
}
257262

packages/ui/command-palette/packages/svelte/src/CommandPalette.svelte

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -170,23 +170,33 @@ const onBackdropClick = (e: any) => {
170170
};
171171
172172
// ---- open/close reconcile ----------------------------------------------
173-
// Focus the vendored <Listbox>'s combobox <input> by reaching into the panel
174-
// element and querying for it. The Listbox owns the input now; reaching it via a
175-
// plain DOM query off command-palette's own panel ref types cleanly across all
176-
// six leaves (a `$refs.listbox` COMPONENT-handle read still types as the host
177-
// element, not the ListboxHandle — the refs-lowering type gap — so a DOM query is
178-
// the source-only path). $refs is read in a post-mount callback only (ROZ123-safe).
173+
// Focus the vendored <Listbox>'s combobox <input>; focusing it fires the
174+
// listbox's `@focus="open"` → the popup opens (the screenshot demo seeds the
175+
// palette open, so this runs on mount). The five light-DOM targets render the
176+
// input directly under the panel, so a plain `querySelector('input')` finds it.
177+
// On Lit the <Listbox> compiles to a `<rozie-listbox>` CUSTOM ELEMENT whose input
178+
// lives in its (open) SHADOW ROOT — a panel-level query can't reach it, so the
179+
// palette never opened and rendered 0 options. Fall back to piercing the child
180+
// element's open shadow root on Lit. A `$refs.listbox.focusControl()` handle call
181+
// would be cleaner but is blocked by the refs-lowering type gap (a composed-
182+
// component ref types inconsistently across targets, not as the ListboxHandle).
183+
// $refs read in a post-mount callback only (ROZ123-safe).
179184
// ---- open/close reconcile ----------------------------------------------
180-
// Focus the vendored <Listbox>'s combobox <input> by reaching into the panel
181-
// element and querying for it. The Listbox owns the input now; reaching it via a
182-
// plain DOM query off command-palette's own panel ref types cleanly across all
183-
// six leaves (a `$refs.listbox` COMPONENT-handle read still types as the host
184-
// element, not the ListboxHandle — the refs-lowering type gap — so a DOM query is
185-
// the source-only path). $refs is read in a post-mount callback only (ROZ123-safe).
185+
// Focus the vendored <Listbox>'s combobox <input>; focusing it fires the
186+
// listbox's `@focus="open"` → the popup opens (the screenshot demo seeds the
187+
// palette open, so this runs on mount). The five light-DOM targets render the
188+
// input directly under the panel, so a plain `querySelector('input')` finds it.
189+
// On Lit the <Listbox> compiles to a `<rozie-listbox>` CUSTOM ELEMENT whose input
190+
// lives in its (open) SHADOW ROOT — a panel-level query can't reach it, so the
191+
// palette never opened and rendered 0 options. Fall back to piercing the child
192+
// element's open shadow root on Lit. A `$refs.listbox.focusControl()` handle call
193+
// would be cleaner but is blocked by the refs-lowering type gap (a composed-
194+
// component ref types inconsistently across targets, not as the ListboxHandle).
195+
// $refs read in a post-mount callback only (ROZ123-safe).
186196
const focusInput = () => {
187197
const panel$local = panel;
188198
if (!panel$local) return;
189-
const input = panel$local.querySelector('input');
199+
const input = panel$local.querySelector('input') || panel$local.querySelector('rozie-listbox')?.shadowRoot?.querySelector('input');
190200
if (input && input.focus) input.focus();
191201
};
192202

packages/ui/command-palette/packages/vue/src/CommandPalette.vue

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -173,23 +173,33 @@ const onBackdropClick = (e: any) => {
173173
};
174174
175175
// ---- open/close reconcile ----------------------------------------------
176-
// Focus the vendored <Listbox>'s combobox <input> by reaching into the panel
177-
// element and querying for it. The Listbox owns the input now; reaching it via a
178-
// plain DOM query off command-palette's own panel ref types cleanly across all
179-
// six leaves (a `$refs.listbox` COMPONENT-handle read still types as the host
180-
// element, not the ListboxHandle — the refs-lowering type gap — so a DOM query is
181-
// the source-only path). $refs is read in a post-mount callback only (ROZ123-safe).
176+
// Focus the vendored <Listbox>'s combobox <input>; focusing it fires the
177+
// listbox's `@focus="open"` → the popup opens (the screenshot demo seeds the
178+
// palette open, so this runs on mount). The five light-DOM targets render the
179+
// input directly under the panel, so a plain `querySelector('input')` finds it.
180+
// On Lit the <Listbox> compiles to a `<rozie-listbox>` CUSTOM ELEMENT whose input
181+
// lives in its (open) SHADOW ROOT — a panel-level query can't reach it, so the
182+
// palette never opened and rendered 0 options. Fall back to piercing the child
183+
// element's open shadow root on Lit. A `$refs.listbox.focusControl()` handle call
184+
// would be cleaner but is blocked by the refs-lowering type gap (a composed-
185+
// component ref types inconsistently across targets, not as the ListboxHandle).
186+
// $refs read in a post-mount callback only (ROZ123-safe).
182187
// ---- open/close reconcile ----------------------------------------------
183-
// Focus the vendored <Listbox>'s combobox <input> by reaching into the panel
184-
// element and querying for it. The Listbox owns the input now; reaching it via a
185-
// plain DOM query off command-palette's own panel ref types cleanly across all
186-
// six leaves (a `$refs.listbox` COMPONENT-handle read still types as the host
187-
// element, not the ListboxHandle — the refs-lowering type gap — so a DOM query is
188-
// the source-only path). $refs is read in a post-mount callback only (ROZ123-safe).
188+
// Focus the vendored <Listbox>'s combobox <input>; focusing it fires the
189+
// listbox's `@focus="open"` → the popup opens (the screenshot demo seeds the
190+
// palette open, so this runs on mount). The five light-DOM targets render the
191+
// input directly under the panel, so a plain `querySelector('input')` finds it.
192+
// On Lit the <Listbox> compiles to a `<rozie-listbox>` CUSTOM ELEMENT whose input
193+
// lives in its (open) SHADOW ROOT — a panel-level query can't reach it, so the
194+
// palette never opened and rendered 0 options. Fall back to piercing the child
195+
// element's open shadow root on Lit. A `$refs.listbox.focusControl()` handle call
196+
// would be cleaner but is blocked by the refs-lowering type gap (a composed-
197+
// component ref types inconsistently across targets, not as the ListboxHandle).
198+
// $refs read in a post-mount callback only (ROZ123-safe).
189199
const focusInput = () => {
190200
const panel = panelRef.value;
191201
if (!panel) return;
192-
const input = panel.querySelector('input');
202+
const input = panel.querySelector('input') || panel.querySelector('rozie-listbox')?.shadowRoot?.querySelector('input');
193203
if (input && input.focus) input.focus();
194204
};
195205

packages/ui/command-palette/src/CommandPalette.rozie

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -235,16 +235,23 @@ const onBackdropClick = (e) => {
235235
}
236236

237237
// ---- open/close reconcile ----------------------------------------------
238-
// Focus the vendored <Listbox>'s combobox <input> by reaching into the panel
239-
// element and querying for it. The Listbox owns the input now; reaching it via a
240-
// plain DOM query off command-palette's own panel ref types cleanly across all
241-
// six leaves (a `$refs.listbox` COMPONENT-handle read still types as the host
242-
// element, not the ListboxHandle — the refs-lowering type gap — so a DOM query is
243-
// the source-only path). $refs is read in a post-mount callback only (ROZ123-safe).
238+
// Focus the vendored <Listbox>'s combobox <input>; focusing it fires the
239+
// listbox's `@focus="open"` → the popup opens (the screenshot demo seeds the
240+
// palette open, so this runs on mount). The five light-DOM targets render the
241+
// input directly under the panel, so a plain `querySelector('input')` finds it.
242+
// On Lit the <Listbox> compiles to a `<rozie-listbox>` CUSTOM ELEMENT whose input
243+
// lives in its (open) SHADOW ROOT — a panel-level query can't reach it, so the
244+
// palette never opened and rendered 0 options. Fall back to piercing the child
245+
// element's open shadow root on Lit. A `$refs.listbox.focusControl()` handle call
246+
// would be cleaner but is blocked by the refs-lowering type gap (a composed-
247+
// component ref types inconsistently across targets, not as the ListboxHandle).
248+
// $refs read in a post-mount callback only (ROZ123-safe).
244249
const focusInput = () => {
245250
const panel = $refs.panel
246251
if (!panel) return
247-
const input = panel.querySelector('input')
252+
const input =
253+
panel.querySelector('input') ||
254+
panel.querySelector('rozie-listbox')?.shadowRoot?.querySelector('input')
248255
if (input && input.focus) input.focus()
249256
}
250257

-8.49 KB
Loading

tests/visual-regression/tsconfig.app.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
"paths": {
1212
"@angular/*": ["node_modules/@angular/*"],
1313
"@analogjs/*": ["node_modules/@analogjs/*"],
14+
"listboxPathComment": "Phase 999.4 cross-family composition: command-palette's CommandPalette.rozie composes the vendored listbox via the STABLE D-07 specifier `@rozie-ui/listbox/Listbox.rozie`; the Angular emit ext-strips it to `@rozie-ui/listbox/Listbox`. The vite build resolves that via resolve.alias, but @analogjs's NgtscProgram resolves AOT imports through tsconfig `paths`, NOT vite aliases — without this entry the import is unresolved, `imports: [Listbox]` collapses to any[], no `ɵcmp` is emitted, and the cell falls back to (browser-absent) JIT → empty mount. Maps to the SAME vendored copy the vite alias targets (`command-palette/src/Listbox.rozie` → +`.ts` → the prebuilt `Listbox.rozie.ts` disk-cache, which is in `include`) so the AOT-analyzed class identity matches the bundled one.",
15+
"@rozie-ui/listbox/*": ["../../packages/ui/command-palette/src/*.rozie"],
1416
"zone.js": ["node_modules/zone.js"],
1517
"zone.js/*": ["node_modules/zone.js/*"],
1618
"rxjs": ["node_modules/rxjs"],

0 commit comments

Comments
 (0)