Skip to content

feat(webui): workspace picker entry coverage — home chip dropdown and sidebar project-row switch - #27

Merged
fengzhi09 merged 2 commits into
mainfrom
feat/workspace-picker-entry
Sep 25, 2026
Merged

fengzhi09 merged 2 commits into
mainfrom
feat/workspace-picker-entry

Conversation

@fengzhi09

Copy link
Copy Markdown
Collaborator

What

Follow-up to #26: the workspace picker only existed inside the conversation view's right panel — the home screen had no path to it (dead workspace chip), and sidebar project rows could never switch the workspace (click = expand only). Both were reproduced and reported by the user.

  • Home workspace chip → two-level dropdown (pr-22 interaction, user-approved with reference screenshots): Level 1 antd Dropdown anchored to the chip — 最近 recents list (current marked ✓, click switches immediately) / 选择新项目 → Level 2 WorkspacePickerModal (Recents/Browse, same single-source component) / 不需要项目 → no-workspace (tmpdir) mode.
  • Sidebar project rows → switch action: hover reveals a folder icon next to the existing "+"; clicking switches the workspace. stopPropagation on click + mousedown keeps expand/collapse and drag-select intact.
  • WorkspacePickerModal exported from panels.tsx as the single source of truth; conversation-view entry unchanged.
  • 4 bilingual i18n keys; 9 new webapp tests (chip helpers + static-source tripwire pinning popupRender on the Dropdown).

Acceptance (2 rounds, independent agent)

Round 1 FAIL — the <Dropdown> was missing popupRender, React.Children.only crashed and unmounted the whole app on chip click. Fixed in 023303b; developer now live-self-checks render paths before handing off (new workflow rule).

Round 2 PASS — live-verified on an isolated instance: dropdown renders/3× open-close cycles; recents row switch (chip label + /api/state agree); 选择新项目 → Level-2 browse → switch; 不需要项目 → tmpdir; sidebar hover switch + expand/collapse + "+" unaffected; conversation-view entry regression clean; static tripwire test judged legitimate.

Gates (fresh, re-run by acceptance): pnpm typecheck 0 errors · pnpm test:webapp 201/201 · pnpm build ✓ · pnpm check:source 4560 files ✓. Full pnpm verify deferred to CI.

Non-blocking nits noted for later: Escape doesn't close the dropdown (click-away does); no-workspace chip shows the tmpdir leaf name.

feat-workspace-picker-entry agent added 2 commits September 26, 2026 00:01
The picker built in basic-features/01 only had an entry point inside
the conversation view's WorkspacePanel. Two surfaces were dead:

  1. The home-screen workspace chip ("demo002" button on the
     greeting screen, chat.tsx HomeState) was a button with no
     onClick — clicking it did nothing.
  2. Sidebar project rows (session-tree.tsx ProjectNode) only
     expanded/collapsed on click; there was no "switch to this
     workspace" action.

This ticket adds both entry points without changing the existing
conversation-view entry or the picker itself.

Implementation

A. WorkspaceChipDropdown (new components/workspace-picker.tsx) —
   Two-level pattern, pr-22 style:
   - Level 1: an antd Dropdown anchored to the home chip, with the
     same mavis-dropdown classes the composer uses so the chrome
     reads native.
       * 最近       — recents from /api/workspace/recent; the active
                      row carries a ✓ glyph. Single click switches.
       * 选择新项目 — opens the full WorkspacePickerModal (Level 2).
       * 不需要项目 — switches to no-workspace (tmpdir), same path
                      as the existing Recents tab's no-workspace
                      button.
   - Level 2 is the existing WorkspacePickerModal (exported from
     panels.tsx) — one component, one source of truth, mounted
     independently by the home chip. Both call sites use the same
     instance type; nothing shared.

B. ProjectRowSwitchAction (new components/workspace-picker.tsx) —
   A 22px icon button on the hover slot next to the existing
   'new task in this project' plus. Click calls
   runAction(api.setWorkspace(repoPath)) with the project's first
   repoPath. event.stopPropagation() keeps single-click expand/
   collapse unchanged.

Files

- components/workspace-picker.tsx (new) — WorkspaceChipDropdown +
  ProjectRowSwitchAction + the dropdown chrome. lastSegment is a
  duplicate of shell.tsx#workspaceLeaf so the chip's display can
  live in this module without dragging in the entire shell.
- components/panels.tsx — WorkspacePickerModal is now exported
  (was a module-local function), so the home chip's Level 2 can
  mount it independently.
- components/chat.tsx — HomeState now mounts WorkspaceChipDropdown
  in place of the inert button. Removed the manual
  workspace.dir.split('/').filter(Boolean).pop() display; the
  dropdown owns it now.
- components/session-tree.tsx — ProjectNode's hover slot now
  carries ProjectRowSwitchAction alongside the existing RowAction.
- lib/i18n.ts — 4 new bilingual keys
  (workspace.chipDropdown.recent|chooseNew|noProject,
   workspace.projectRow.switch).
- webapp/test/workspace-chip.test.ts (new) — pins lastSegment
  parsing, the active-row predicate, and the row order.

Out of scope (per ticket)

- Sidebar git-branch chip and quick-action chips from the pr-22
  reference screenshots.
- Sidebar per-file 'switch' affordance (the chip dropdown covers
  workspace-level switching; the per-directory case is reachable
  through Level 2's directory picker).

Gates

- pnpm --filter @mavis/webui webapp:typecheck — 0 errors
- pnpm test:webapp — 200 / 200 / 0 fail
- pnpm build — passes (6253 source files)
- pnpm check:source — passes (4560 files)
- pnpm typecheck (root) — 0 errors
Acceptance round 2 found the workspace chip crashed the entire app
on click: rc-dropdown threw 'React.Children.only expected to receive
a single React element child' on overlay mount, and the uncaught
render error unmounted the document.

Root cause: in components/workspace-picker.tsx, <Dropdown> was given
an overlayClassName but no popupRender or menu prop, so antd had
nothing to render into the overlay and rc-dropdown threw. composer.tsx
already uses the popupRender={() => <customPanel/>} pattern for its
permission / model selectors; match it here.

Fix:
- components/workspace-picker.tsx: <Dropdown> now declares
  popupRender={() => dropdownContent}, matching composer.tsx#SelectPanel.
  Comment explains the antd overlay contract so a future refactor
  doesn't reintroduce the regression.

Test: the webapp test suite has no jsdom / @testing-library/react
('chat-virtual-list.test.ts: no jsdom in this suite' is explicit), so
mounting the Dropdown here would require pulling in a heavyweight
new harness. Instead, the new test in workspace-chip.test.ts reads
the source file and asserts the <Dropdown> mount has 'popupRender='
or 'menu=' as a static-source pin — same regression-against-future-
refactor shape as webapp-picker-wire.test.ts (the wire-shape pin from
the previous branch), but on the JSX surface rather than a wire
field. A real render smoke test would be preferred if the infra
existed; this is the lightweight alternative.

Live self-check (mandatory per acceptance feedback): started an
isolated dev instance (PORT=18092 MCODE_WEBUI_DEV_FRONTEND_PORT=18093
MCODE_WEBUI_DATA_DIR=/tmp/dev-wpe-check pnpm webui:dev) and verified
every path with agent-browser:

  - chip click                  → dropdown renders, no crash
  - 选择新项目 click            → Level-2 modal opens (title 切换工作区,
                                  Recents / Browse tabs visible)
  - Escape                      → Level-2 modal closes cleanly
  - 不需要项目 click            → chip text becomes 'tmp'
                                  (no-workspace state)
  - sidebar 切换到此工作区 click → chip text becomes 'town'
                                  (sidebar project switch)
  - document.body has children throughout; the chip click is the only
    way to reach the dropdown and there is no remaining crash path.

Gates
- pnpm --filter @mavis/webui webapp:typecheck — 0 errors
- pnpm test:webapp — 201 / 201 / 0 fail (the new static-source test
  pins the popupRender contract)
- pnpm build — passes
@fengzhi09
fengzhi09 merged commit 2aea5df into main Sep 25, 2026
8 checks passed
@fengzhi09
fengzhi09 deleted the feat/workspace-picker-entry branch September 25, 2026 16:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant