Skip to content

phase3-3: unified workbench + plugin-opener (stacked on #72)#73

Open
baochipham942-eng wants to merge 10 commits intophase3-2-preview-tabsfrom
phase3-3-sidebar-and-opener
Open

phase3-3: unified workbench + plugin-opener (stacked on #72)#73
baochipham942-eng wants to merge 10 commits intophase3-2-preview-tabsfrom
phase3-3-sidebar-and-opener

Conversation

@baochipham942-eng
Copy link
Copy Markdown
Owner

Stacked on top of #72. Two follow-ups from the Phase 3 artifact sidebar plan:

C — `shell:open-path` fix via plugin-opener

The "在 Finder 打开" button in WorkingFolder and file citations both routed through a `shell:open-path` IPC channel with no main-process handler, so clicks silently no-op'd. Same shape as the plugin-dialog bug fixed in #71.

  • Installs `tauri-plugin-opener` + `@tauri-apps/plugin-opener`
  • Grants `opener:allow-open-path` and `opener:allow-reveal-item-in-dir` in the default capability (with the `remote.urls` trick that Phase 3.1 + directory picker fix #71 introduced)
  • Renderer switches to `revealItemInDir` (WorkingFolder) and `openPath` (CitationList)
  • Removes the now-dead SHELL_OPEN_PATH channel + its type entry

B — Unified right workbench

Replaces the split layout where TaskPanel / SkillsPanel shared one panel slot (Bug #2 — Skills dropped below the fold) and PreviewPanel lived inside ChatView at a fixed `w-[500px]`. Borrows the Codex right-rail tab pattern.

New model in appStore:

  • `WorkbenchTabId = 'task' | 'skills' | `preview:${string}``
  • `workbenchTabs` ordered list + `activeWorkbenchTab` pointer
  • `openWorkbenchTab` / `closeWorkbenchTab` / `setActiveWorkbenchTab` actions
  • `closeWorkbenchTab` delegates to `closePreviewTab` for preview-kind ids so the backing file tab is evicted and `activePreviewTabId` migrates to the survivor (this was the bug where closing the active preview left stale content on screen)
  • Preview LRU still scoped to `previewTabs` (pinned tabs don't count)

UI:

  • New `WorkbenchTabs.tsx` tab bar — replaces `PreviewTabs.tsx` (deleted)
  • Panel body switches on `activeWorkbenchTab` kind → TaskPanel | SkillsPanel | PreviewPanel
  • TaskPanel / SkillsPanel / PreviewPanel headers lose their own title + close X (tab bar owns both now; method B per design alignment)
  • SkillsPanel's `w-72` becomes `w-full`, border-l removed across the three

Caller migration:

  • TitleBar Skills/Task toggle icons read `workbenchTabs.includes(id)` and flip via open/closeWorkbenchTab
  • Cmd+J toggles the task workbench tab
  • TaskStatusBar / App.tsx swarm handler use `openWorkbenchTab('task')`
  • SkillsPanel.handleOpenSettings uses `closeWorkbenchTab('skills')`, `onClose` prop removed

Legacy state trimmed: `showTaskPanel` / `showSkillsPanel` / `showPreviewPanel` booleans and their setters deleted.

Test plan

  • `npm run typecheck` clean at every commit
  • 29 renderer unit tests green (13 new `workbenchTabs.test.ts` cases covering mixed-type switching, LRU isolation, preview sync)
  • Full release build (`cargo tauri build` + `scripts/tauri-install.sh`)
  • Manual verification via computer-use:
    • Task default active on launch
    • Click Skills icon → Skills tab appears + activates, old Task stays as inactive tab
    • Open preview from chat → new tab appears + activates
    • Switch between all three kinds via tab click → correct content swaps
    • Middle-click close preview → falls back to most-recent surviving preview
  • Phase 3.1 + directory picker fix #71 merges → base auto-retargets to Phase 3.2: PreviewPanel multi-tab #72, then to `main` after Phase 3.2: PreviewPanel multi-tab #72 merges

Commits

```
2a0ecfb feat(tauri): install plugin-opener for native open/reveal
a2fd5fd fix(ipc): reveal/open paths via plugin-opener in renderer
a7878c0 chore: sync Cargo.lock for tauri-plugin-opener
9d130cd feat(workbench): unified tab model in appStore
34d523e test(workbench): mixed-type tab switching + LRU isolation
ff83674 refactor(panels): strip titles and close buttons from Task/Skills/Preview headers
39e274f feat(workbench): WorkbenchTabs tab bar component
a8e4207 refactor(workbench): unify right panel hosting Task/Skills/Preview
f462f6a refactor(workbench): migrate callers to openWorkbenchTab and remove legacy show*Panel
88af940 fix(workbench): closing preview tab via X now re-renders the survivor
```

🤖 Generated with Claude Code

林晨 and others added 10 commits April 22, 2026 22:45
Adds tauri-plugin-opener for opening files with default app and
revealing files in Finder. Capability grants both permissions to
the main window. Mirrors the plugin-dialog installation pattern
used in #71 for directory pickers.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Both WorkingFolder's "open in Finder" button and CitationList's file
citations previously routed shell:open-path through an IPC channel
that had no main-process handler, so clicks silently no-op'd.

Route directly through @tauri-apps/plugin-opener:
- WorkingFolder -> revealItemInDir (Finder highlights file)
- CitationList  -> openPath          (default app opens file)

Removes the now-unused SHELL_OPEN_PATH channel and its type entry.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Introduces WorkbenchTabId ('task' | 'skills' | 'preview:<path>') with
workbenchTabs ordered list + activeWorkbenchTab pointer, and three new
actions: openWorkbenchTab / closeWorkbenchTab / setActiveWorkbenchTab.

Legacy setters (setShowTaskPanel, setShowSkillsPanel) and preview tab
lifecycle (openPreview, closePreview, closePreviewTab, setActivePreviewTab)
are kept in sync with the unified state so existing UI continues to work.
The show*Panel booleans and their direct setters will be retired after
all callers migrate.

On LRU eviction of a preview tab, its workbench entry is also removed to
prevent orphan tabs pointing at vanished previewTabs entries.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
15 cases covering:
- Initial state (task pinned + active)
- openWorkbenchTab: append + dedup behavior
- closeWorkbenchTab: empty, fallback to pinned, fallback to recent preview
- openPreview sync into workbenchTabs, including no-dup on re-open
- LRU eviction during openPreview also removes evicted workbench entry
- closePreviewTab / closePreview sync
- setActivePreviewTab mirrors to activeWorkbenchTab
- Legacy setShowTaskPanel/setShowSkillsPanel sync

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…view headers

Method B per design alignment: the unified workbench tab bar becomes the
single source of truth for panel labels (Task / Skills / <filename>) and
for the close affordance. Each panel keeps its action-button row but no
longer renders its own title or close X.

Removed:
- TaskPanel: "任务" title span
- SkillsPanel: Sparkles + "Session Skills" h3, X close button
- PreviewPanel: green-dot + filename + dirty indicator, X close button

Justify-between → justify-end where only the action row remains.
onClose prop on SkillsPanel is left in place for now (still called
programmatically in handleOpenSettings); it will be replaced with
closeWorkbenchTab('skills') in the caller-migration commit.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Unified tab bar for the right workbench. Reads workbenchTabs +
activeWorkbenchTab from appStore and renders a pill for each:
- 'task' / 'skills' → pinned pills with i18n label
- 'preview:<path>'  → filename pill with dirty indicator
                      (looked up against previewTabs by path)

Click activates the tab, X or middle-click closes it. Visual language
mirrors the existing PreviewTabs component, which this will replace
when App.tsx is rewired to host a single right workbench.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Lifts PreviewPanel out of ChatView and hosts it alongside TaskPanel
and SkillsPanel inside a single resizable Panel in App.tsx, with
<WorkbenchTabs /> as the shared tab bar.

Behavior:
- Gate: !isNarrowViewport && workbenchTabs.length > 0
- Active tab switch: activeWorkbenchTab drives which panel renders
- Preview no longer fixed at w-[500px] — fills the Panel container
  like Task/Skills, user can resize via the Panel's ResizeHandle
- isMaximized still escapes via fixed inset-0 z-50

Container styling normalized across all three panels:
- TaskPanel/SkillsPanel/PreviewPanel: w-full h-full bg-zinc-900
- border-l removed from all three (ResizeHandle is the visual seam)
- SkillsPanel's w-72 replaced with w-full so it fills the container

PreviewTabs.tsx deleted — its job is now done by WorkbenchTabs.
ChatView no longer renders PreviewPanel or reads showPreviewPanel.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…egacy show*Panel

Call sites migrated:
- TitleBar: Task/Skills toggle icons read workbenchTabs.includes(id)
  and flip via openWorkbenchTab/closeWorkbenchTab
- useKeyboardShortcuts: Cmd/Ctrl+J toggles the 'task' workbench tab
- TaskStatusBar.openOrchestrationPanel -> openWorkbenchTab('task')
- App.tsx swarm event handler -> openWorkbenchTab('task')
- SkillsPanel.handleOpenSettings -> closeWorkbenchTab('skills');
  onClose prop removed from SkillsPanelProps and its App.tsx caller

State trimmed:
- Removed showTaskPanel / showSkillsPanel / showPreviewPanel booleans
- Removed setShowTaskPanel / setShowSkillsPanel / setShowPreviewPanel
- openPreview / closePreview / closePreviewTab no longer write the
  obsolete showPreviewPanel field
- PreviewPanel's guard simplifies to `if (!activeTab) return null`
  (rendering is already gated at App.tsx on activeWorkbenchTab kind)

Tests: dropped the two legacy-setter cases and the showPreviewPanel
assertions since those state fields no longer exist.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Repro: open two preview tabs (a.md, b.html), click X on the active
b.html. Tab bar correctly falls back to a.md, but content area kept
showing b.html because PreviewPanel resolves active tab via
activePreviewTabId — which closeWorkbenchTab never updated.

closeWorkbenchTab now detects preview-kind tabs and delegates to
closePreviewTab, which owns the full lifecycle (evict previewTab
entry, update activePreviewTabId, mirror workbench list). Non-preview
tabs keep the existing generic cleanup.

Updated the matching test: closing a preview workbench tab evicts
its backing previewTab and migrates activePreviewTabId to the
promoted survivor.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 22, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
code-agent Ready Ready Preview, Comment Apr 22, 2026 3:51pm

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