feat(navigation): experiment with surfacing Cmd+K in the left nav - #73032
Conversation
Adds the cmd-k-nav-experiment multivariate flag (control, search-bar, footer-hint) to test more prominent placements for the command menu: a full-width input-styled search bar below the nav header, and a Search row with the keybind chip in the nav footer. Also instruments a `command menu opened` event with a `source` property so opens can be measured across all entry points (keyboard shortcut, nav button, new variants, quickstart). Individual shortcut presses were already tracked via `keybind triggered`. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
😎 Merged manually by @rafaeelaudibert - details. |
🤖 CI report
|
| File | Size | Δ vs base |
|---|---|---|
posthog-app/src/scenes/AuthenticatedShell.js |
218.8 KiB | 🔺 +3.1 KiB (+1.4%) |
Posted automatically by build-bundle-size-report · uncompressed bytes from dist-report
✅ Eager graph — within budget
How much code each root ships on the eager path — downloaded and parsed before the surface is interactive. Measured from the esbuild output chunks (post-tree-shake, static imports only); lazy import() / React.lazy chunks are not counted.
| Root | Eager (shipped) | Δ vs base | Budget |
|---|---|---|---|
entry (logged-out pages, app bootstrap)src/index.tsx |
1.24 MiB · 22 files | no change | ███░░░░░░░ 27.5% of 4.51 MiB |
authenticated shell (every logged-in page)src/scenes/AuthenticatedShell.tsx |
8.21 MiB · 3,001 files | 🔺 +3.4 KiB (+0.0%) | ████████░░ 84.5% of 9.71 MiB |
🟢 node_modules/monaco-editor/ stays out of src/index.tsx
🟢 src/lib/components/ActivityLog/describers stays out of src/index.tsx
🟢 [object Object] stays out of src/index.tsx
🟢 [object Object] stays out of src/index.tsx
🟢 node_modules/monaco-editor/ stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/lib/components/ActivityLog/describers stays out of src/scenes/AuthenticatedShell.tsx
🟢 [object Object] stays out of src/scenes/AuthenticatedShell.tsx
🟢 [object Object] stays out of src/scenes/AuthenticatedShell.tsx
Largest files eagerly shipped from src/index.tsx
| Size | File |
|---|---|
| 126.8 KiB | ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js |
| 24.6 KiB | ../node_modules/.pnpm/buffer@6.0.3/node_modules/buffer/index.js |
| 6.3 KiB | ../node_modules/.pnpm/react@18.3.1/node_modules/react/cjs/react.production.min.js |
| 4.5 KiB | ../node_modules/.pnpm/@jspm+core@2.1.0/node_modules/@jspm/core/nodelibs/browser/process.js |
| 3.9 KiB | ../node_modules/.pnpm/scheduler@0.23.2/node_modules/scheduler/cjs/scheduler.production.min.js |
| 1.4 KiB | ../node_modules/.pnpm/base64-js@1.5.1/node_modules/base64-js/index.js |
| 1.3 KiB | src/RootErrorBoundary.tsx |
| 912 B | ../node_modules/.pnpm/ieee754@1.2.1/node_modules/ieee754/index.js |
| 789 B | src/scenes/ChunkLoadErrorBoundary.tsx |
| 762 B | src/index.tsx |
Largest files eagerly shipped from src/scenes/AuthenticatedShell.tsx
| Size | File |
|---|---|
| 281.3 KiB | ../node_modules/.pnpm/posthog-js@1.406.2/node_modules/posthog-js/dist/rrweb.js |
| 267.7 KiB | ../node_modules/.pnpm/@posthog+icons@0.38.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js |
| 236.0 KiB | src/taxonomy/core-filter-definitions-by-group.json |
| 224.7 KiB | ../node_modules/.pnpm/posthog-js@1.406.2/node_modules/posthog-js/dist/module.js |
| 167.1 KiB | src/queries/validators.js |
| 154.3 KiB | ../node_modules/.pnpm/re2js@0.4.1/node_modules/re2js/build/index.esm.js |
| 126.8 KiB | ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js |
| 105.8 KiB | src/lib/api.ts |
| 94.0 KiB | ../packages/quill/packages/quill/dist/index.js |
| 93.3 KiB | ../node_modules/.pnpm/prosemirror-view@1.40.1/node_modules/prosemirror-view/dist/index.js |
Posted automatically by check-eager-graph · sizes are eager output bytes (shipped, post-tree-shake) from the esbuild metafile · part of #32479
✅ Toolbar bundle — eager 2.18 MiB within budget
What the toolbar ships to customer pages, measured from the esbuild output (minified, post-tree-shake). The eager set is the entry plus everything statically imported from it — fetched before any feature runs; deferred chunks load lazily. The eager guardrail is 5.72 MiB. Each output file must also stay below 10 MB, where CloudFront stops compressing it. The module boundary is enforced separately by check-toolbar-graph.
| Metric | Size | Δ vs base | Budget |
|---|---|---|---|
| Eager (shipped) entry + static imports |
2.18 MiB · 17 files | 🔺 +44 B (+0.0%) | ████░░░░░░ 38.1% of 5.72 MiB |
| Deferred (lazy) | 2.07 MiB · 33 files | no change | n/a — loads on demand |
Loader dist/toolbar.js |
1.1 KiB | no change | █░░░░░░░░░ 5.8% of 19.5 KiB |
Largest eagerly-shipped chunks
| Size | File |
|---|---|
| 713.8 KiB | dist/toolbar/toolbar-app-HY7HJI4V.css |
| 543.6 KiB | dist/toolbar/chunk-chunk-6LXPUJWA.js |
| 484.2 KiB | dist/toolbar/chunk-chunk-QS5AHYGW.js |
| 133.6 KiB | dist/toolbar/chunk-chunk-YAXKFW74.js |
| 131.8 KiB | dist/toolbar/chunk-chunk-T5KY5WYR.js |
| 71.0 KiB | dist/toolbar/toolbar-app-C3PNMI3R.js |
| 69.0 KiB | dist/toolbar/chunk-chunk-27JL52RE.js |
| 35.6 KiB | dist/toolbar/chunk-chunk-T7IBPYAQ.js |
| 20.9 KiB | dist/toolbar/chunk-chunk-E5ZPZBER.js |
| 12.2 KiB | dist/toolbar/chunk-chunk-PIK3PADE.js |
Posted automatically by check-toolbar-size · sizes are toolbar output bytes (shipped, post-tree-shake) from the esbuild metafile
✅ Dist folder size — 🔺 +19.5 KiB (+0.0%)
Total size of the built frontend/dist folder (all assets), compared against the base branch.
Total: 1357.19 MiB · 🔺 +19.5 KiB (+0.0%)
…ariants Extends cmd-k-nav-experiment with two more placements: a Search row with the keybind chip right after the Tools item in the Project section, and a dismissible callout card in the nav advertisement slot shown only to users who joined at least 3 days ago. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
🦔 Hogbox preview · ✅ ready▶ Open the preview
commit |
|
Reviews (1): Last reviewed commit: "feat(navigation): add tools-row and foot..." | Re-trigger Greptile |
There was a problem hiding this comment.
Purely additive, feature-flag-gated frontend UI experiment (new nav placements for the search trigger) plus analytics instrumentation; no auth, billing, API contract, migration, dependency, or CI changes — outside risky territory and easily reversible via the flag.
- Author wrote 8% of the modified lines and has 188 merged PRs in these paths (familiarity MODERATE).
- 👍 on the PR from greptile-apps[bot], hex-security-app[bot].
Gate mechanics and policy version
| Gate | Result | |
|---|---|---|
| prerequisites | ✓ | all clear |
| deny-list | ✓ | no deny categories matched |
| size | ✓ | 313L, 11F substantive — within ceiling |
| tier | ✓ | T1-agent / T1d-complex (313L, 11F, single-area, feat) |
| stamphog 2.0.0b3 | .stamphog/policy.yml @ 0797905 · reviewed head aabd979 |
Problem
The Cmd+K command menu is one of the fastest ways to get around PostHog, but usage is lower than we'd like. Its only persistent entry point in the left nav is an icon-only magnifier button, and the keybind is only discoverable via its tooltip.
We also had no unified event for "the command menu was opened" - keyboard opens were only visible via the generic
keybind triggeredevent, and button opens vianav search clicked, with no way to see totals or compare entry points in one place.Changes
Adds a multivariate experiment (
cmd-k-nav-experiment, five variants) that tries four more prominent placements for the search trigger in the left nav:navPanelAdvertisementLogic, and it reuses thenav panel campaign shown/dismissedevents withcampaign: "cmd-k-discovery".Instrumentation:
command menu openedevent captured incommandLogicwith asourceproperty (keyboard-shortcut,nav-search-button,nav-search-bar,nav-footer-hint,nav-tools-row,nav-panel-callout,quickstart), so every open is counted once regardless of entry point.openCommand/toggleCommandnow take a required typed source argument.shortcutLogic.triggerShortcutanduseKeyboardHotkeysboth capturekeybind triggeredfor every shortcut, so no changes were needed there.The draft experiment lives at Cmd+K nav prominence (project 2), five variants at 20% each. Primary metric is mean
command menu openedper user; secondaries arenav search clickedandkeybind triggeredfiltered to the Search shortcut. It's left in draft - launch it once this PR is deployed so exposures line up with the instrumented event.How did you test this code?
localhost:8010with asetup_testworkspace,date_joinedbackdated 10 days for the callout gate) with the flag overridden to each variant: all five render as in the screenshots above, clicking each new surface opens the command menu, and ⌘K still works.nav search clicked+command menu opened{source: "nav-search-bar"}; tools row emits{source: "nav-tools-row"}; callout card emitsnav panel campaign shownon render,{source: "nav-panel-callout"}on click, andnav panel campaign dismissedon dismiss (card stays hidden afterward); ⌘K emitskeybind triggered(nameSearch) +{source: "keyboard-shortcut"}; closing with Escape emits nothing.pnpm --filter=@posthog/frontend typescript:checkpasses with no errors; kea typegen forcommandLogicis up to date;pnpm --filter=@posthog/frontend fix(oxlint + oxfmt) passes.Command menu opened via the search-bar variant
Automatic notifications
Docs update
🤖 Agent context
Autonomy: Human-driven (agent-assisted)
Authored with Claude Code (Claude Fable 5) directed by @rafaeelaudibert. The agent explored the nav/command-bar code, implemented the four treatment variants and instrumentation, created and configured the draft experiment (with its auto-created flag) via the PostHog MCP, and verified all variants headlessly against the local dev stack (skills used:
/run-posthog).Decisions along the way:
keybind triggeredinshortcutLogic/useKeyboardHotkeys), so the only new event iscommand menu opened; sources are typed (CommandOpenSource) to keep the property enumerable.NavPanelAdvertisementslot, card component, and persisted-dismissal logic rather than introducing a new surface;AdvertisementCard'stextprop was widened toReactNodeso the callout can render a platform-aware keybind chip instead of a hardcoded ⌘.user.date_joined; a "modal after signup" treatment was considered and rejected as heavier-handed than the in-nav placements.nav search clickedcapture was kept on all click surfaces for continuity with old dashboards.