Skip to content

feat(desktop): merge command menu search into one ranked list - #96153

Draft
puemos wants to merge 1 commit into
posthog/desktop-search-rank-tasks-firstfrom
posthog/desktop-search-canvases
Draft

feat(desktop): merge command menu search into one ranked list#96153
puemos wants to merge 1 commit into
posthog/desktop-search-rank-tasks-firstfrom
posthog/desktop-search-canvases

Conversation

@puemos

@puemos puemos commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Problem

A person searching in the PostHog Desktop command menu reads a page of near-identical rows. Every remote match wears the same document icon, carries no context under its title, and long titles wrap to three lines with raw Slack links and pasted JSON in them. The same search splits across a "Search results" section at the top and separate Tasks and Spaces sections below, so one answer sits in three places.

Sits on #96121, which ranks the matches and answers with the context a row shows. This PR is the palette half.

Changes

  • Search answers in one ranked list. A task, a space, a canvas, a pull request and a file compete for the same rows, ordered by how closely the title matches, then by kind, then by what changed last.
  • Every row carries the icon its own surface uses: the task list's status icon (Slack, a scout, a cloud run's state), the canvas template icon, the space's hash or lock, a pull request mark, and the file-type icon the artifacts list draws.
  • Every row carries the metadata line a space's task list carries: what made the task, its repository, its author, and when it last changed. The space it is filed in sits at the right edge.
  • Titles read as one line. A title that arrived from Slack with a labeled link, a code fence, or a pasted JSON body now shows its prose, truncated to the row.
  • A long list stops at eight rows and offers "Show 18 more", so one letter no longer prints every task and space in the project.
  • A canvas match opens the canvas. It used to render as a row that did nothing.
  • Mechanical: useSearchSections becomes useSearchRows and returns rows instead of sections, ranking moves to rankResultRows, and the row's types, facts, and metadata line each get their own module.

command menu search, one ranked list

Every row type at once, over invented data, because one project rarely holds all five:

one row per kind

Before, the same query drew a "Search results" block of identical document icons, wrapped titles, and no context, above a separate Tasks section. That screenshot is left out because it carries a reporter's own task titles.

Note

The app updates separately from the backend. Until the base PR deploys, the author, source, run status, and timestamp are absent, and rows fall back to the repository and the space. A canvas row reaching an older client is still a row that opens nothing.

How did you test this code?

  • useSearchRows.test.tsx covers the dedupe of a task the palette already lists, the canvas row's space label and open action, spaces-off, and a Slack-linked title read as one line.
  • rankResultRows.test.ts covers the order: an exact title first, a task above a file on an equal title match, a title match above a keyword-only match, and recency as the tie-break.
  • CommandMenu.search.test.tsx renders the palette and asserts a locally known task leads the list with no repeat below. That is the wiring the hook test cannot reach.
  • title-text.test.ts covers the six title shapes a row has to flatten.
  • Drove the running app over CDP against a local Django on this branch: real rows, mocked payloads for every kind, one-letter queries, long and multiline titles, empty results, dark mode, keyboard highlight, and the "Show more" row. The screenshots above come from that.
  • Not checked: the search response from production, which has not deployed the base PR yet.

Automatic notifications

  • Publish to changelog?

Docs update

None.

🤖 Agent context

Autonomy: Human-driven (agent-assisted)

  • Written by Claude Code, as the top layer of a two-PR stack split backend then frontend.
  • Skills invoked: /posthog-desktop, /writing-ui-components, /writing-user-facing-copy, /writing-tests, /writing-pr-descriptions, /stacking-prs.
  • The design was iterated against the running app rather than Storybook: each round rendered the palette over CDP, and the reviewer's screenshots drove the next change.
  • Per-kind sections came first and were replaced by one ranked list on request. The kind now breaks a tie inside a match tier rather than grouping the page.
  • Row context first tried the task summaries endpoint from the client. It answers only for tasks the reader created, so the fields moved into the search payload in the base PR.
  • The metadata line does not follow the sidebar's "list item metadata" setting. A search row needs its context whatever a reader chose for their task list.
  • Nothing in this PR comes from the session that is not already public. The second screenshot's rows are invented.

Created with PostHog Desktop

@github-actions github-actions Bot added the feature/desktop Feature Tag: Desktop label Sep 7, 2026
@github-actions

github-actions Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

React Doctor found 5 issues in 3 files · 5 warnings.

5 warnings

apps/web/src/web-agent-config.ts

packages/ui/src/features/command/CommandMenu.tsx

packages/ui/src/features/command/commandSearch.ts

Reviewed by React Doctor for commit 6b0a045.

@github-actions

github-actions Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

🤖 CI report

⚠️ Trunk lane — backend Python lane

This PR is assigned to the backend Python lane. It runs backend Python tests and may merge in parallel with PRs in other lanes.

Complexity (TypeScript) — clean

Cyclomatic complexity above the limit in changed typescript files (10 for production files, 15 for test files). Warn only: worth simplifying when you next touch these functions.

⚠️ Bundle size — 🔺 +482 B (+0.0%)

Uncompressed size of every built .js bundle, compared against the base branch.

Total: 68.62 MiB · 🔺 +482 B (+0.0%)

File Size Δ vs base
posthog-app/src/scenes/billing/BillingSection.js 18.3 KiB 🟢 -2.9 KiB (-13.9%)

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.50 MiB · 22 files no change ███░░░░░░░ 33.2% of 4.51 MiB
logged-out boot: index + App + bootApp (preloaded by every page, including /login)
src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
5.25 MiB · 668 files 🔺 +56 B (+0.0%) █████████░ 93.3% of 5.63 MiB
authenticated shell (every logged-in page)
src/scenes/AuthenticatedShell.tsx
7.67 MiB · 2,677 files 🔺 +809 B (+0.0%) █████████░ 87.4% of 8.77 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/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/layout/navigation-3000/navigationLogic.tsx stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/scenes/dashboard/dashboardLogic.tsx stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 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
🟢 src/scenes/session-recordings/player/sessionRecordingPlayerLogic.ts 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
1000 B src/index.tsx
912 B ../node_modules/.pnpm/ieee754@1.2.1/node_modules/ieee754/index.js
854 B src/scenes/ChunkLoadErrorBoundary.tsx
Largest files eagerly shipped from src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
Size File
276.1 KiB ../node_modules/.pnpm/posthog-js@1.427.2_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
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
255.2 KiB src/taxonomy/core-filter-definitions-by-group.json
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
104.4 KiB src/lib/api.ts
79.1 KiB src/products.tsx
68.8 KiB src/lib/lemon-ui/icons/icons.tsx
62.5 KiB src/lib/utils/eventUsageLogic.ts
38.8 KiB ../node_modules/.pnpm/@dnd-kit+core@6.0.8_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@dnd-kit/core/dist/core.esm.js
33.9 KiB ../node_modules/.pnpm/kea@4.0.0-pre.6_patch_hash=139b8d1f1304f9d9da452a9a1244c94ea679dbcb85687d8999563146879fb6f5_react@18.3.1/node_modules/kea/lib/index.cjs.js
Largest files eagerly shipped from src/scenes/AuthenticatedShell.tsx
Size File
276.1 KiB ../node_modules/.pnpm/posthog-js@1.427.2_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
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
255.2 KiB src/taxonomy/core-filter-definitions-by-group.json
153.8 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
104.4 KiB src/lib/api.ts
95.8 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
90.6 KiB ../node_modules/.pnpm/@tiptap+core@3.20.6_@tiptap+pm@3.20.6/node_modules/@tiptap/core/dist/index.js
79.1 KiB src/products.tsx

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.28 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.28 MiB · 18 files 🔺 +56 B (+0.0%) ████░░░░░░ 39.9% of 5.72 MiB
Deferred (lazy) 2.11 MiB · 45 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
756.4 KiB dist/toolbar/toolbar-app-TJXV6LBK.css
602.9 KiB dist/toolbar/chunk-chunk-JWEPY4DI.js
484.7 KiB dist/toolbar/chunk-chunk-D33R7XDZ.js
136.5 KiB dist/toolbar/chunk-chunk-YKMCCDSE.js
131.8 KiB dist/toolbar/chunk-chunk-FDH2IBXT.js
72.2 KiB dist/toolbar/toolbar-app-5LH6H543.js
69.0 KiB dist/toolbar/chunk-chunk-TSAL54PB.js
35.6 KiB dist/toolbar/chunk-chunk-QKSWTRVJ.js
21.0 KiB dist/toolbar/chunk-chunk-4F5LKQPS.js
6.8 KiB dist/toolbar/chunk-chunk-DV7IWQNF.js

Posted automatically by check-toolbar-size · sizes are toolbar output bytes (shipped, post-tree-shake) from the esbuild metafile

Dist folder size — 🔺 +74.1 KiB (+0.0%)

Total size of the built frontend/dist folder (all assets), compared against the base branch.

Total: 1457.54 MiB · 🔺 +74.1 KiB (+0.0%)

ℹ️ MCP UI apps size — 33 app(s), 17665.2 KB JS

Built size of each MCP UI app (main.js + styles.css).

App JS CSS
debug 600.0 KB 195.2 KB
action 458.2 KB 195.2 KB
action-list 564.9 KB 195.2 KB
cohort 457.2 KB 195.2 KB
cohort-list 563.8 KB 195.2 KB
email-template 457.0 KB 195.2 KB
error-details 472.9 KB 195.2 KB
error-issue 457.9 KB 195.2 KB
error-issue-list 564.7 KB 195.2 KB
experiment 562.0 KB 195.2 KB
experiment-list 565.6 KB 195.2 KB
experiment-results 567.1 KB 195.2 KB
feature-flag 567.6 KB 195.2 KB
feature-flag-list 571.4 KB 195.2 KB
feature-flag-testing 461.4 KB 195.2 KB
inline-scan 457.7 KB 195.2 KB
insight-actors 563.0 KB 195.2 KB
invite-email-preview 456.4 KB 195.2 KB
llm-costs 560.0 KB 195.2 KB
session-recording 459.0 KB 195.2 KB
survey 458.8 KB 195.2 KB
survey-global-stats 562.7 KB 195.2 KB
survey-list 565.5 KB 195.2 KB
survey-stats 562.7 KB 195.2 KB
trace-span 457.6 KB 195.2 KB
trace-span-list 564.7 KB 195.2 KB
vision-observation-list 563.9 KB 195.2 KB
workflow 457.5 KB 195.2 KB
workflow-list 564.2 KB 195.2 KB
loops-review 461.9 KB 195.2 KB
query-results 756.3 KB 195.2 KB
render-ui 839.5 KB 195.2 KB
visual-review-snapshots 462.0 KB 195.2 KB
⚠️ Django migration SQL — 2 new migrations to review

We've detected new migrations on this PR. Review the SQL output for each migration:

products/tasks/backend/migrations/0119_alter_tasksearchdocument_kind.py

BEGIN;
--
-- Alter field kind on tasksearchdocument
--
-- (no-op)
COMMIT;

products/tasks/backend/migrations/0120_backfill_canvas_search_documents.py

--
-- Raw Python operation
--
-- THIS OPERATION CANNOT BE WRITTEN AS SQL

Last updated: 2026-09-07 15:54 UTC (09b9c18)

Django migration risk — blocked migration detected

We've analyzed your migrations for potential risks.

Summary: 0 Safe | 1 Needs Review | 1 Blocked

❌ Blocked

Causes locks or breaks compatibility

tasks.0120_backfill_canvas_search_documents
  │  └─ #1 ⚠️ RunPython: RunPython data migration needs review for performance
  │
  └──> �[91m📋 POSTHOG POLICY VIOLATIONS:�[0m
       ⚠️ WARNING: atomic=False without CONCURRENTLY operations. This
       loses transaction rollback safety. If migration fails midway,
       partial changes are committed and retry will fail on non-
       idempotent ops. Only use atomic=False if: (1) using CONCURRENTLY,
       or (2) intentional for long-running ops with idempotent SQL (IF
       NOT EXISTS, WHERE NOT EXISTS). Consider async migrations for
       large data backfills instead. If this is an intentional long-
       running data migration, add
       'tasks.0120_backfill_canvas_search_documents' to posthog/manageme
       nt/migration_analysis/atomic_false_acknowledged_migrations.txt to
       accept the risk.

⚠️ Needs Review

May have performance impact

tasks.0119_alter_tasksearchdocument_kind
  └─ #1 ⚠️ AlterField
     Field alteration may cause table locks or data loss (check if changing type or constraints)
     model: tasksearchdocument, field: kind, field_type: CharField

📚 How to Deploy These Changes Safely

RunPython:

Use batching for large data migrations:

  • Use .iterator() to avoid loading all rows into memory
  • Use .bulk_update() instead of saving individual objects
  • Batch size: 1,000-10,000 rows per batch
  • Add pauses between batches
  • Consider background jobs for very large updates (millions of rows)

See the migration safety guide

Last updated: 2026-09-07 15:54 UTC (09b9c18)

@puemos
puemos force-pushed the posthog/desktop-search-canvases branch from 480ef8e to 09b9c18 Compare September 7, 2026 15:49
@puemos
puemos force-pushed the posthog/desktop-search-canvases branch from 09b9c18 to 1671c92 Compare September 7, 2026 17:59
@puemos puemos changed the title feat(desktop): find canvases from the command menu feat(desktop): merge command menu search into one ranked list Sep 7, 2026
Typing in the command menu split its answers across a remote "Search results"
section and the local Tasks and Spaces sections, and every remote row wore the
same document icon with no context under it. The palette now ranks tasks,
spaces, canvases, pull requests and files into one list, draws each row with
the icon and metadata line its task list already uses, and keeps every title to
one line.
@puemos
puemos force-pushed the posthog/desktop-search-canvases branch from 1671c92 to 6b0a045 Compare September 7, 2026 20:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature/desktop Feature Tag: Desktop

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant