Repository navigation
fix(app-shell): the header draws its Search trigger only where a command palette is mounted (objectui#11912) - #11987
Merged
objectstack-fleet[bot] merged 2 commits intoOct 8, 2026
Conversation
…and palette is mounted On /home, /ai and the organizations frames no CommandPaletteProvider is mounted, so the header's Search trigger called the hook's inert fallback and neither a click nor Ctrl+K opened anything. AppHeader now asks a package-internal useCommandPaletteProviderMounted() and renders the search group only when it answers true. useCommandPalette()'s published return shape and no-op fallback are unchanged; the new hook is not re-exported. Claude-Session: https://claude.ai/code/session_01CGZy1BGCjdN5cXqL9cnvB8 Co-authored-by: Claude <noreply@anthropic.com>
…and add the patch changeset AppHeader.searchTriggerNeedsPalette-11912 renders the header through a real MemoryRouter: with no CommandPaletteProvider the home, orgs and app variants draw neither trigger testid; under the real provider both draw and each click opens the palette (open state true, ?palette=1). The 10899 pin reads the desktop trigger as its "header rendered" control, so its app-variant mount now sits under the provider ConsoleLayout gives that variant. 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
|
objectstack-fleet
Bot
deleted the
claude/issue-11912-inert-search-trigger
branch
October 8, 2026 16:40
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 #11912
Clause-②: no
What changed
The console header drew its "Search ⌘K" trigger in every frame, but only
ConsoleLayoutmounts aCommandPaletteProvider(and onlyAppContentmounts the palette). On/home,/ai,/organizationsand an organization's own pages,useCommandPalette()answered its documented no-op fallback, so a click opened nothing andCtrl+Kopened nothing either.This takes the card's second route: the header does not show the trigger where no palette is mounted. A search that works with no active app is objectui#11863's question and is not touched here.
packages/app-shell/src/context/CommandPaletteProvider.tsx— a newuseCommandPaletteProviderMounted()answers whether a provider is mounted above the caller. It is package-internal: not re-exported fromcontext/index.tsor the package entry.useCommandPalette()keeps its published return shape (CommandPaletteContextValue) and its no-op fallback; only its docblock changed, because it said the header trigger "is then inert".packages/app-shell/src/layout/AppHeader.tsx— the search group (desktopaction:command-palette:openand compactaction:command-palette:open-mobile) renders only when that hook answerstrue. The group is dropped whole, so no emptydata-topbar-groupis left behind. Inside an app nothing changes..changeset/11912-inert-search-trigger.md—patchon@object-ui/app-shell.The gate is the provider's presence, not the variant name. If a palette is later mounted outside apps (objectui#11863), the trigger comes back there without touching the header again.
Pins
AppHeader.searchTriggerNeedsPalette-11912.test.tsx(realAppHeader, realCommandPaletteProvider, realMemoryRouter):home,orgsandappvariants draw neither trigger testid and noaria-keyshortcutselement, while the header itself drew (the inbox bell is there);appandhomevariants draw both triggers, and a click on each one opens the palette (useCommandPalette().opengoesfalsetotrue, the URL gainspalette=1).AppHeader.designInStudioNeedsAuthoring-10899.test.tsxused the desktop trigger as its "the header rendered" check while mounting theappvariant with no provider. Its mount now sits underCommandPaletteProvider, which is howConsoleLayoutmounts that variant. No assertion changed.Reverse verification (ablation)
Run from the committed head
3664e20cwithablation-replace(anchor must hit; write, restore and blob checked on disk). The hook's bodyreturn useContext(CommandPaletteContext) !== null;was replaced byreturn true;:3e03354a65fctod9f466451f93;Tests 3 failed | 2 passed (5). The three no-provider cases (home,orgs,app) went red with "expected document not to contain element, found BUTTON"; the two provider controls stayed green;3e03354a65fc),git diff HEADempty.The subject is imported from
srcby relative path, so nodistrebuild sits between the mutation and the run.Browser check
Chromium at 1440x900 and 800x900. The console was served by
vitefrom this branch, and frommain989b1908as the control, both against a scratchobjectstack devbackend (17.7.0, one appprobe_search_app), signed in as the seeded admin.main989b190/home/home, 0 overlays;Ctrl+K: 0 overlaysaria-keyshortcuts;Ctrl+K: 0 overlays/organizations?create=1(OrganizationsLayout)Ctrl+K: 0Ctrl+K: 0/organizations/default/members(OrganizationLayout)Ctrl+K: 0Ctrl+K: 0/apps/probe_search_app/psrch_taskCtrl+Kopen the palette (?palette=1, 1 overlay)Ctrl+Kopen the palette (?palette=1, 1 overlay)The same at 800 px, where the compact trigger is the visible one. On every frame without a trigger the header keeps two
data-topbar-groupblocks (inbox and help, account), none empty, and the first one has no left divider (border 0px, margin 0px). No page errors./aiis NOT MEASURED in the browser: this backend serves no AI, so the route guard sends/aito/home.AiChatPagemounts the samehomevariant with no provider, which the unit pin covers.Gates (run from the worktree root)
All on head
3664e20c(the last commit; nothing was committed after these runs).pnpm exec vitest run packages/app-shell/(whole package,--maxWorkers=2, under the shared verify lock): exit 0,Test Files 1187 passed | 1 skipped (1188),Tests 11476 passed | 9 skipped (11485).pnpm exec vitest runon the two touched AppHeader test files: exit 0,Test Files 2 passed (2),Tests 8 passed (8).pnpm --filter @object-ui/app-shell type-check(tsc --noEmit && tsc -p tsconfig.test.json; the test project's file list includes both touched test files): exit 0.pnpm --filter @object-ui/app-shell build: exit 0,dist completeness: 1 package(s) complete. Built entry check:dist/index.d.tsre-exportsuseCommandPalette(control) and has noexport *;useCommandPaletteProviderMountedappears only indist/context/CommandPaletteProvider.d.ts, which the entry reaches only through named re-exports. The package entry's type graph does not change.pnpm exec eslinton the four touched source files: exit 0, 0 errors (warnings only, all of a kind the files already carried).pnpm check:control-bytes,check:test-path-roots,check:changeset-claims,check:pending-changeset-literals,check:new-line-citations(0 new citations),check:vi-mock-specifiers,check:vi-mock-inherit,check:vi-mock-override-shape,check:i18n-keys,check:unreferenced-sources: all exit 0.node scripts/check-changeset-presence.mjs: exit 0, 1 changeset declared for 4 changed source files of 1 released package.node scripts/check-changeset-no-major.mjs: exit 0.check:readme-exports(it needs every package built and refuses on unbuiltplugin-*entries; this PR changes no export), andBundle Analysis/check:eager-closure(CI). The change adds no import and no first-load module.Acceptance notes
packages/app-shell/README.md("Command palette (⌘K)") is not edited. It is off this card's file surface, and no sentence in it became false: it names the trigger's testids and does not say where the trigger renders.ConsoleLayoutis a published export that mounts the provider but not the palette (AppContentmountsCommandPalette). A host that usesConsoleLayoutwithoutCommandPalettestill gets a trigger that sets?palette=1and opens nothing. That is a read-only inference with no named producer, so it is noted here and not filed.react-refresh/only-export-componentswarning on the new hook is the same warninguseCommandPalette()already carries in that file. ESLint reports 0 errors on the four touched files.This was implemented by the os-dev seat for objectui#11912, session
https://claude.ai/code/session_01CGZy1BGCjdN5cXqL9cnvB8.Generated by Claude Code