Repository navigation
fix(app-shell): the command palette matches navigation entries on word prefixes and substrings (objectui#11812) - #11883
Merged
objectstack-fleet[bot] merged 2 commits intoOct 8, 2026
Conversation
…d prefixes and substrings The navigation groups (objects, dashboards, pages, reports, apps) and the theme and full-search commands went through cmdk's default subsequence scorer, so "zzzz" listed Field Zoo and a nonsense query never reached "No results". The palette now matches them itself with a module-private matcher (a word prefix, or a contiguous substring from three characters on, of the label or machine name; every Han or kana character starts a word) and renders only the matches. Record hits are unchanged. Claude-Session: https://claude.ai/code/session_01CGZy1BGCjdN5cXqL9cnvB8 Co-authored-by: Claude <noreply@anthropic.com>
…ovider Claude-Session: https://claude.ai/code/session_01CGZy1BGCjdN5cXqL9cnvB8 Co-authored-by: Claude <noreply@anthropic.com>
Contributor
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #11812
Clause-②: no
What changes
The ⌘K palette's navigation groups (Objects, Dashboards, Pages, Reports, Switch App) and its theme and full-search commands were matched by cmdk's default subsequence scorer:
CommandDialogmounts cmdk'sCommandwith nofilter, so every item goes throughcommandScore. The palette now matches those entries itself and renders only the matches.matchesPaletteQueryinpackages/app-shell/src/chrome/paletteMatch.ts(not re-exported). An entry matches when the query, ignoring case, starts a word of its label or machine name (fieldfinds "Field Zoo",zoofindsshowcase_field_zoo), or, from three characters on, is a contiguous substring of one of them (viewfinds "Task Overview"). A hyphen and a space read alike, which is cmdk's own normalisation. No typo tolerance.大屏and屏both find "Command Center (大屏)" (Zone 2 item 4, pinned).CommandPalettefilters each navigation group, the app list, the three theme commands and the full-search command through it, and renders a group, with its separator, only when something in it matched.valuechanges. Each rendered entry carries its label and name in its value, so the trimmed query is a contiguous run of that value and cmdk's scorer, which still ranks what is rendered and decides whenCommandEmptyshows, accepts every entry the palette rendered. "No results" now shows exactly when no entry matched and the record search found nothing.The mechanism, measured (Zone 2 item 1: confirmed)
cmdk 1.1.1's
commandScore, run on the showcase entries' values:zzzzobject Field Zoo showcase_field_zoozzzzpage New Project (Wizard) showcase_new_project_wizardingestobject In-Progress Tasks showcase_taskingestobject Cascading Select showcase_cascadeingestpage Page Authoring showcase_start_hereingestpage Styling (ADR-0065) showcase_styling_gallerywaynepage Styling (ADR-0065) showcase_styling_galleryThese are exactly the false hits the card's QA pass read, so the scorer alone explains them.
zzzzscores above zero because the scorer may skip a query letter that repeats the next one (a 0.1 factor each time), so the twozs of "Field Zoo ... zoo" absorb four.Route taken (Zone 2 item 2)
CommandPalette.tsx. NeitherforceMountnor a query carried in the value was needed, because every entry the matcher accepts already scores above zero in cmdk.forceMountwas read in cmdk 1.1.1's source and set aside: a force-mounted item that cmdk scores 0 is rendered but not counted infiltered.count, so "No results" could show beside it.DialogandCommandfrom@object-ui/componentswith afilter. Both are exported, but that copiesCommandDialog's composition (the visually-hidden title and description of ADR-0054 C4, the cmdk class string) out of the Shadcn-syncedui/command.tsxinto app-shell, and afiltersees only a value string, so record hits would need a kind marker in their value to stay exempt. Afilterpassthrough onCommandDialogwould be a new prop on a published component (a Clause-② stop) inui/**(AGENTS.md rule 7).CommandDialogis not edited; theoverlay:command-palettetest id and the accessible name are untouched (pinned by the empty-query control).Theme and full-search commands (Zone 2 item 3: they follow the rule)
They are matched by the same rule over the words they were already matched by (
theme light,theme dark,theme system,search all results full page). Under the old scorer scattered letters reached them too:hsyscored 1.515e-1 ontheme system,tht1.686e-1 ontheme light.Behaviour changes a reviewer should know
fzfound "Field Zoo" andsapfound the full-search command (8.003e-1) through scattered word starts; neither is a word prefix or a substring, so neither matches now. This is the card's Expected.object,page,dashboard,reportandappstay at the head of each cmdk value but are not a matching term:pagenow finds "Page Authoring" and "Page Variables", not every page. Matching is on what the entry is called, its label and machine name, per the triage direction.Real-browser reading (Chromium 141, 1440×900)
The console's Vite dev server served a temporary probe page, deleted afterwards and never committed, that mounts the real
CommandPaletteProvider(opened by?palette=1) and the realCommandPaletteunderI18nProviderinen, over the showcase app's whole navigation (its 50 leaf entries, read from objectstackexamples/app-showcase,ShowcaseApp.navigation), with asearchAllstub holding three records (Ingest pipeline, Wayne Enterprises, 华宁科技). Queries were typed key by key. Before isCommandPalette.tsxat this branch's base5d69133; after isa76ebb5.zzzzingestwaynefield大屏No page errors in either run. The showcase backend was not booted: the sister objectstack checkout is not built in this container, so the record hits come from the stub.
Tests (at
a76ebb5)paletteMatch.test.ts(new, 13 cases): the seven scattered pairs from the card do not match; word prefixes of the label and of the machine name match at any length; a mid-word substring matches from three characters on and not before; phrases, hyphen and space, case; Han and kana substrings; blank query and missing terms.CommandPalette.navMatch-11812.test.tsx(new, 7 cases), through the realCommandPaletteProviderand cmdk:zzzzshows no entry and "No results found." once the record search settles;ingestshows its record hit and only the entry whose machine name contains it;wayneshows Wayne Enterprises and no page;field,view,大屏and屏each show their entry; apps, theme and full-search commands follow the rule. Controls: the record hit's item is the one the palette always rendered (record wayne Wayne Enterprises Account showcase_account a1), and an empty query shows every entry, app and command.CommandPalette.navLabelInherits-9868,CommandPalette.searchLabelFls-10500) pass unchanged: no cmdk value moved.Ablation (direction: red, as expected)
node scripts/ablation-replace.mjs(objectstack's tool, run from this worktree) replaced thematcheshelper with one that accepts every entry (anchor 1 → 0, blob77b858975656→0168da5f2e1f), so cmdk's default scorer decides again, and ran the DOM suite: 5 failed, 2 passed. The five pins went red with the card's own readings (zzzz:['Field Zoo', 'New Project (Wizard)'];ingest: In-Progress Tasks, Cascading Select, Page Authoring and Styling (ADR-0065) beside Replay Queue;wayne: Styling (ADR-0065)); the two controls stayed green. Restore: blob back to77b858975656= HEAD,git diff HEADempty, marker count 0. The suite imports./CommandPalettefrom source, so nodistsat between the mutation and the run.Gates (all at
a76ebb5)pnpm exec vitest run packages/app-shell/pnpm --filter @object-ui/app-shell type-check(script name echoed;tsconfig.test.jsonlists all four touched files)tsc --noEmit && tsc -p tsconfig.test.jsonpnpm --filter @object-ui/app-shell buildpnpm exec eslinton the 4 touched filesCommandPalette.tsx, rule-for-rule the same 13 as on base5d69133pnpm check:control-bytespnpm check:test-path-rootspnpm check:changeset-claimspnpm check:pending-changeset-literalspnpm check:new-line-citationspnpm check:i18n-keyspnpm check:vi-mock-specifiers·check:vi-mock-inherit·check:vi-mock-override-shapepnpm check:unreferenced-sources·check:self-import·check:phantom-deps·check:esm-specifiersnode scripts/check-changeset-presence.mjs·check-changeset-no-major.mjsmajorThe eslint run is a narrowing, not the repo-wide
pnpm lint(CI's): eslint's own config globs**/*.{ts,tsx}cover all four files; its--format jsonoutput lists 4 files, none ignored; andeslint.config.jsenables no type-aware linting (noprojectServiceorparserOptions.project), so this diff cannot move the verdict of a file it does not touch. Locale packs and package exports are unchanged, so the i18n pack gates andcheck:readme-exportsdo not apply. NOT MEASURED locally:check:node-esm-load(its load leg builds the tree), left to CI.The public surface is unchanged: the built
dist/index.d.tsanddist/chrome/index.d.tsdo not reachpaletteMatch.d.ts; no export, prop, type member or language-pack key is added. Patch changeset:.changeset/11812-palette-nav-match.md.mainhas moved to04c8f56since the base; none of those commits touchespackages/app-shell/src/chrome/,ui/command.tsx,useRecordSearchorCommandPaletteProvider, andgit merge-treeagainst it is clean, so the branch was not merged.Acceptance notes
主题finds none of them, because their translated labels were never in their values. Unchanged here; noted, not filed. Carrier: none.useRecordSearch, the server call) and the?palette=1deep link are untouched, per the triage ruling.Session:
https://claude.ai/code/session_01CGZy1BGCjdN5cXqL9cnvB8(dispatched by thedomain:uiseat 3 PM).Generated by Claude Code