Skip to content

vscode: full webview Backlog for unified search input (Path B — replaces #891 stealth-merge) #906

Description

@amrmelsayed

Problem

The Codev sidebar's Backlog needs an embedded text input that filters the rows live as the user types. The user wants the input to feel like a first-class part of the Backlog section — visually indistinguishable from VSCode's own SCM "CHANGES" pattern where the input sits inside the section, with items rendered directly below it in the same scrollable area.

Why not the per-view-webview approach (#891)

#891 explored two lighter approaches and rejected both after visual review in the running worktree:

  1. Shared search webview above Backlog + Builders — felt disconnected (two separate sections with their own headers).
  2. Per-view search webview directly above each TreeView — also felt disconnected; the unavoidable section divider between the search webview and the TreeView below made them read as two peer widgets, not "one thing".
  3. Option i: name: " " stealth-merge of the per-view webview — VSCode still reserves the section header bar (~22px) regardless of name, leaving a residual empty strip above the input. User verdict: "not good at all".

The fundamental constraint: VSCode's view contribution model makes every contributes.views entry a peer section with its own header and divider. You cannot place a webview's content inside another view's section. The only way to render [input → items] as a single visually-unified section is to make the entire section a webview — i.e., replace the Backlog TreeView with a WebviewView.

#891's in-flight branch (builder/pir-891) has the prep work — see "Carry forward" below.

Scope (Path B — full webview Backlog)

Convert

  • codev.backlog becomes a WebviewView (was: TreeView).
  • The webview renders:
    • Search input as the first element inside the section (matching VSCode's SCM CHANGES pattern)
    • Backlog rows rendered as HTML below the input, in the same scrollable area
    • All visually unified — one Backlog section, search input as its first child, items underneath
  • The BacklogProvider TreeView class is removed (or repurposed as a pure data projector — issue numbers + titles + areas → render model — with the webview doing all DOM work).

Visual fidelity (REQUIRED)

  • Theme integration via VSCode CSS variables. At minimum:
    • `--vscode-sideBar-background`
    • `--vscode-foreground`
    • `--vscode-input-background`, `--vscode-input-foreground`, `--vscode-input-border`, `--vscode-input-placeholderForeground`
    • `--vscode-list-hoverBackground`
    • `--vscode-list-activeSelectionBackground`, `--vscode-list-activeSelectionForeground`
    • `--vscode-list-inactiveSelectionBackground`
    • `--vscode-focusBorder`
    • `--vscode-font-family`, `--vscode-font-size`
  • Codicons for row icons (load via `@vscode/codicons` package — matches what the TreeView renders today: `issues` / `account` icons).
  • Hover, focus, selection states matching native list styling pixel-for-pixel.
  • Live theme switching (dark/light) propagates without reload.

Behavioral parity (REQUIRED — do NOT drop)

  • Keyboard navigation: ↑ / ↓ / Home / End / PgUp / PgDn move selection through rows.

  • Enter on a selected row fires the row's primary click action (today: `codev.viewBacklogIssue`).

  • Right-click context menu — replicate today's Backlog row actions. Five actions:

    • Reference Issue in Architect (inline button on the row today)
    • View Issue
    • Spawn Builder
    • Open Issue in Browser
    • Copy Issue Number

    Implementation: HTML/JS custom context menu (VSCode webviews can't contribute to `view/item/context`). Visually close to native.

  • ARIA roles for accessibility (`role='list'` / `role='listitem'`, or `role='treegrid'` if grouping by area is retained).

  • Group-by-area behavior preserved (alphabetical specific areas, then Uncategorized last). The `@cluesmith/codev-core/area-grouping` projection (`groupByArea`, `formatAreaForDisplay`) is shared with other views — reuse it for the render model.

  • Mine-first sort preserved within each group (current user's assigned items sort to top with the `account` codicon).

  • Group expand/collapse persistence — current behavior stores per-area expansion in `workspaceState` under `codev.backlogGroupExpansion`. New webview should preserve this (post-message to the extension on toggle, read back on reload).

Filter scope (per the #891 design discussion)

Case-insensitive substring match across:

  • `id` (issue number)
  • `title`
  • `area` (raw + formatted-for-display)
  • `labels[]` (full label name list, including `area/*`)
  • `assignees[]`
  • `author`

Performance (REQUIRED)

  • Backlog can have hundreds of issues. Use virtual scrolling — only render visible rows + a small buffer. Don't render 500 DOM nodes upfront.
  • Either:
    • Add `lit-virtualizer` as a dependency (small, well-maintained), OR
    • Implement simple manual windowing (~80 LOC: track scrollTop, compute visible range, render that range, pad above/below with sentinel divs)
  • The previous design preference was zero new dependencies. Manual windowing is the path-of-least-resistance.
  • Validate performance with a 500-row fixture before signaling ready.

Carry forward from #891's branch (`builder/pir-891`)

The in-flight #891 branch has prep work that should land with this PR:

  • Wire-format change (commit `5538edfa`): `labels: string[]` added to `OverviewBacklogItem` and `OverviewBuilder` in `packages/types/src/api.ts` and populated in `packages/codev/src/agent-farm/servers/overview.ts`. The new webview's filter scope needs this.
  • Labels-iteration defensive guard (commit `527c8f59`): `Array.isArray(labels) ? labels : []` pattern guards against stale-Tower wire payloads. The new webview's row-render path that iterates `item.labels` must apply the same guard.

What to drop from the #891 branch:

  • The `BacklogSearchViewProvider` separate webview — its filter logic ports into the new combined webview, but the standalone view goes away.
  • The `SearchState` class — filter state becomes local to the new webview component (transient `useState`-style; the extension host doesn't need to know).
  • The `codev.toggleBacklogSearch` command — replaced by clicking the input or using the title-bar focus shortcut.
  • The `codev.backlogSearch` view contribution in package.json.

What to keep from the #891 branch:

  • The wire-format change (above).
  • The labels-iteration guard pattern (apply at the new iteration site).
  • The placeholder copy `"Search backlog..."`.

#891 itself should be closed without merge (the per-view-webview implementation it shipped is being replaced by this issue). The architect decides whether to rebase #891's prep commits onto this issue's branch or cherry-pick them fresh.

Out of scope

  • Builders search. The cost of webview-replacing Builders (loses per-builder changed-files tree, SCM decorations, ~10 context menu actions, accordion) isn't justified by the benefit (users typically have <10 active builders in flight). Builders stays as a TreeView with no search input. See vscode: Backlog search — embed input inside the Backlog section (full WebviewView, replaces TreeView) #891 for the analysis.
  • Search across other Codev views (Pull Requests, Recently Closed, Team) — separate concern.
  • Search history / saved searches / multi-criteria advanced search — over-engineering for v1.
  • Persisting the filter query across sessions — filter is transient; cleared on workspace reload.
  • Match modes (case-sensitive / whole-word / regex) — plain substring only for v1. Can add later without changing public surface.

Acceptance criteria

  • Backlog renders as a single unified section: search input first, items below, in the same scrollable area.
  • Search filters items live as the user types (debounced ~150ms).
  • All today's Backlog row actions still work: View Issue (click), Reference Issue in Architect (inline button), Spawn Builder (right-click), Open in Browser (right-click), Copy Issue Number (right-click).
  • Keyboard navigation works: ↑/↓ at minimum; Home/End/PgUp/PgDn ideally; Enter fires View Issue.
  • Performance acceptable with 500+ rows (smooth scroll, no jank, virtual rendering).
  • Theme variables in use; switching dark/light updates colors live.
  • Group-by-area preserved with expand/collapse persistence in `workspaceState`.
  • Mine-first sort preserved within groups.
  • No regression: Builders view is unchanged (no search section, no buildersSearch view, no toggle icon).
  • Wire-format change from vscode: Backlog search — embed input inside the Backlog section (full WebviewView, replaces TreeView) #891 (`labels: string[]` on `OverviewBacklogItem` / `OverviewBuilder`) landed.
  • Labels-iteration defensively guarded (`Array.isArray(...) ? ... : []`) at every iteration site.
  • Tests pass (DOM logic should be unit-testable via jsdom or vitest's happy-dom).

Suggested protocol

This is a UX-heavy webview rewrite — visual verification in the running extension is the only way to know it's right. The architect's earlier estimate was ~30-40 hours of careful CSS+JS work.

PIR (Plan → Implement → Review) gives the human reviewer the `dev-approval` gate where they can run the worktree and visually confirm. That gate is the right surface for "does this actually look and feel native?". The previous attempt (#891) iterated multiple times at this gate, which is what the gate is for.

SPIR if the architect prefers a fuller spec/plan/review ceremony with consult at every phase. The webview component design (virtual scrolling algorithm, context-menu structure, message protocol) might warrant a spec.

Architect decides; this issue is the WHAT, not the HOW.

Related

Discovered while

PIR #891 dev-approval review on 2026-05-28: tried three lighter approaches (shared webview, per-view webviews, `name: " "` stealth-merge) and visually verified all three felt disconnected. Full WebviewView is the only path to genuine visual unification with VSCode's view contribution model.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area/vscodeArea: VS Code extension

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions