Skip to content

feat(desktop,ipc,ui): chrome title overflow menu — thread actions, reveal, open in editor - #265

Open
AprilNEA wants to merge 6 commits into
masterfrom
xuan/code-379
Open

feat(desktop,ipc,ui): chrome title overflow menu — thread actions, reveal, open in editor#265
AprilNEA wants to merge 6 commits into
masterfrom
xuan/code-379

Conversation

@AprilNEA

@AprilNEA AprilNEA commented Jul 23, 2026

Copy link
Copy Markdown
Member

Closes CODE-379.

Wires the disabled ellipsis placeholder right of the document title into the active thread's overflow menu.

┌──────────────────────────┐
│ 📌  Pin thread           │
│ 📋  Copy title           │
├──────────────────────────┤
│ 📁  Reveal in Finder     │
│ ✎   Open in editor    ▸  │
├──────────────────────────┤
│ ✕   Close thread         │
└──────────────────────────┘

Stacked on #239

Base is xuan/code-380, not master. This branch needs the renderer-crash fix from #239 to run — without it the desktop app white-screens (that bug is still shipped on master, 0.6.3). Merge #239 first, then retarget this to master. The diff here is only the four feature commits; the fix commit belongs to #239.

What's in it

  • New shell system capability (packages/system-plane/ipc): revealPath / listEditors / openInEditor, mirrored across the eventa contract + main impl. The renderer half stays zod-free (sandboxed preload).
  • Editor detection (apps/desktop/src/main/editors.ts): probes PATH + macOS app bundles for installed editors. Coverage follows vitejs/launch-editor's editor-info catalog (MIT) — VS Code family (incl. VSCodium/Cursor/Windsurf/Trae/Antigravity), Sublime, Zed, and the common JetBrains IDEs. Terminal editors (vim/emacs) and EOL apps (Atom/Brackets) are excluded — the menu opens a workspace folder in a GUI editor. JetBrains carry no windowsExe (version-stamped install dir this model can't address), so Windows stays uncovered rather than guessed.
  • Menu component (packages/presentation/ui/src/shell/thread-title-menu.tsx): pure props-driven coss-ui Menu; the editor item is hidden at 0 detected, a plain item at 1, a submenu at >1. Reveal label varies by platform (Finder / File Explorer / file manager).
  • Chrome wiring: DesktopChromeProps.titleMenu slot; desktop supplies it reusing existing pin/close/cwd props + systemBridge.shell.*. An untitled thread or the draft page hides the whole title area (and the menu) as before.
  • i18n: new workbench.threadMenu namespace; the bare label="More" is gone.

Verification

  • Unit tests for editorTargets (all shapes incl. JetBrains-on-Windows → []).
  • New e2e:thread-menu: seeds a titled session straight into the daemon DB (no agent CLI needed) and drives the menu — pin round-trips through the sidebar's Pinned group, copy lands on the clipboard, reveal reaches main with the thread cwd, close drops the thread. Open-in-editor is presence-only (clicking would launch a real editor). Ran green on this machine (Zed detected); screenshot confirms the menu.
  • pnpm check:ci and pnpm test (1737 tests) pass.

Not covered: renaming a thread — there is no session.rename wire op (would need a WIRE_PROTOCOL_VERSION bump).

Summary by CodeRabbit

  • New Features

    • Added a “More actions” menu for threads.
    • Threads can now be pinned or unpinned, renamed titles copied, workspace locations revealed, opened in detected editors, or closed.
    • Added platform-specific file manager actions and editor support.
    • Added English and Chinese translations for the new menu.
  • Tests

    • Added automated coverage for thread menu actions and editor detection.

@linear-code

linear-code Bot commented Jul 23, 2026

Copy link
Copy Markdown

CODE-379

@greptile-apps

greptile-apps Bot commented Jul 23, 2026

Copy link
Copy Markdown

Greptile Summary

Adds a desktop thread-title overflow menu.

  • Adds pin, copy-title, reveal, open-in-editor, and close actions.
  • Extends the Electron system bridge with shell capabilities and editor discovery.
  • Adds localized menu labels and automated coverage for editor detection and menu behavior.

Confidence Score: 5/5

The PR appears safe to merge with no blocking failures remaining from the reviewed threads.

No blocking failure remains.

T-Rex T-Rex Logs

What T-Rex did

  • The thread-title-menu-01-before.log recorded the missing Electron GTK runtime dependency.
  • The thread-title-menu-02-after.log captured a narrowly scoped direct-Xvfb attempt, a seeded-daemon startup, and an Electron launch failure, with the executed command, working directory, and exit status documented.

View all artifacts

T-Rex Ran code and verified through T-Rex

Important Files Changed

Filename Overview
apps/desktop/src/main/editors.ts Detects supported GUI editors and resolves a fresh launch target before opening the thread workspace.
apps/desktop/src/main/system-context.ts Exposes reveal-path and editor operations through the desktop system context.
apps/desktop/src/renderer/src/shell/desktop-shell.tsx Connects active-thread state and system shell operations to the new title menu.
packages/presentation/ui/src/shell/thread-title-menu.tsx Implements the props-driven thread actions menu and editor chooser.
packages/system-plane/ipc/src/electron-main.ts Validates and dispatches the new shell IPC operations in the Electron main process.
apps/desktop/e2e/thread-menu.e2e.mts Exercises the thread menu’s pin, copy, reveal, editor-presence, and close behavior.

Reviews (3): Last reviewed commit: "test(desktop): let the thread-menu e2e f..." | Re-trigger Greptile

Comment thread apps/desktop/src/main/editors.ts Outdated
Base automatically changed from xuan/code-380 to master July 24, 2026 17:48
AprilNEA added 4 commits July 25, 2026 02:03
Adds VSCodium, Trae, Antigravity, and the common JetBrains IDEs. JetBrains
carry no windowsExe: their installer nests the binary under a version-stamped
directory this model can't address, so Windows stays uncovered rather than
guessed.
@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@AprilNEA, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 8 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: fd66a271-3d7f-44e6-b436-ff4f9c03802f

📥 Commits

Reviewing files that changed from the base of the PR and between 5671ae4 and 41cd3e7.

📒 Files selected for processing (3)
  • apps/desktop/e2e/thread-menu.e2e.mts
  • apps/desktop/src/main/__tests__/editors.test.ts
  • apps/desktop/src/main/editors.ts
📝 Walkthrough

Walkthrough

Adds a desktop thread title menu with pin, clipboard, reveal, editor, and close actions. Introduces shell/editor IPC capabilities, platform-specific editor detection, localization, desktop integration, and an isolated end-to-end test.

Changes

Thread title menu

Layer / File(s) Summary
Shell IPC contract and transport
packages/system-plane/ipc/src/{bridge,context,electron-main,electron-renderer,events}.ts, apps/desktop/src/renderer/src/ipc.ts
Adds validated shell IPC contracts and bridge methods for revealing paths, listing editors, and opening editor targets.
Editor detection and shell implementation
apps/desktop/src/main/editors.ts, apps/desktop/src/main/system-context.ts, apps/desktop/src/main/__tests__/editors.test.ts
Detects supported platform-specific editors, launches selected targets, exposes shell operations, and tests target resolution.
Thread menu UI and desktop wiring
packages/presentation/ui/src/shell/*, packages/presentation/i18n/src/locales/*, apps/desktop/src/renderer/src/shell/*
Adds the localized thread menu, title-slot plumbing, editor loading, platform file-manager mapping, and session action callbacks.
Thread menu end-to-end validation
apps/desktop/e2e/thread-menu.e2e.mts, apps/desktop/package.json
Adds an isolated daemon/Electron test and script covering thread pinning, title copying, reveal behavior, editor-menu presence, and closing.

Estimated code review effort: 4 (Complex) | ~45 minutes

Suggested reviewers: zerlight

Sequence Diagram(s)

sequenceDiagram
  participant ThreadMenu
  participant RendererBridge
  participant ElectronIPC
  participant DesktopShell
  ThreadMenu->>RendererBridge: revealPath or openInEditor
  RendererBridge->>ElectronIPC: invoke shell operation
  ElectronIPC->>DesktopShell: validate and execute request
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 30.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and clearly describes the main change: adding a desktop thread overflow menu with thread actions and editor/reveal support.
Description check ✅ Passed The description includes a clear summary, verification notes, and implementation details, and is mostly aligned with the required template.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch xuan/code-379

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/desktop/e2e/thread-menu.e2e.mts`:
- Around line 35-38: Update fail() to throw an Error with the supplied message
instead of calling process.exit(1), allowing existing catch and finally handling
in main() to capture diagnostics and clean up resources. Preserve the failure
reporting and exit-code behavior already implemented by main().

In `@packages/presentation/ui/src/shell/thread-title-menu.tsx`:
- Around line 29-65: Rename the shared menu module and its ThreadTitleMenu and
ThreadTitleMenuEditor symbols to session-based names, preserving the existing
behavior and UI/i18n terminology. Update the re-export in
packages/presentation/ui/src/shell/index.ts at line 30 and all corresponding
imports, component usages, and type references in
apps/desktop/src/renderer/src/shell/desktop-shell.tsx at lines 2-11; do not
rename wire or other code identifiers outside this UI naming scope.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 46170f41-078f-4b6e-8ba5-5cc958d1e541

📥 Commits

Reviewing files that changed from the base of the PR and between 364dd55 and 5671ae4.

📒 Files selected for processing (17)
  • apps/desktop/e2e/thread-menu.e2e.mts
  • apps/desktop/package.json
  • apps/desktop/src/main/__tests__/editors.test.ts
  • apps/desktop/src/main/editors.ts
  • apps/desktop/src/main/system-context.ts
  • apps/desktop/src/renderer/src/ipc.ts
  • apps/desktop/src/renderer/src/shell/chrome/chrome.tsx
  • apps/desktop/src/renderer/src/shell/desktop-shell.tsx
  • packages/presentation/i18n/src/locales/en.ts
  • packages/presentation/i18n/src/locales/zh-cn.ts
  • packages/presentation/ui/src/shell/index.ts
  • packages/presentation/ui/src/shell/thread-title-menu.tsx
  • packages/system-plane/ipc/src/bridge.ts
  • packages/system-plane/ipc/src/context.ts
  • packages/system-plane/ipc/src/electron-main.ts
  • packages/system-plane/ipc/src/electron-renderer.ts
  • packages/system-plane/ipc/src/events.ts

Comment thread apps/desktop/e2e/thread-menu.e2e.mts
Comment on lines +29 to +65
/** One editor the host detected; `id` is opaque and travels back on open. */
export interface ThreadTitleMenuEditor {
id: string;
label: string;
}

export interface ThreadTitleMenuProps {
/** Copied verbatim by the copy item. */
title: string;
pinned: boolean;
fileManager: FileManagerKind;
/** Detected editors; empty hides the open-in-editor item entirely. */
editors: ThreadTitleMenuEditor[];
onTogglePin: () => void;
onReveal: () => void;
onOpenInEditor: (editorId: string) => void;
/** Stop the thread if live and drop it from the list. */
onClose: () => void;
}

const REVEAL_KEY = {
darwin: 'reveal.darwin',
win32: 'reveal.win32',
other: 'reveal.other',
} as const;

/** The chrome title's overflow menu: what you can do to the thread you're looking at. */
export function ThreadTitleMenu({
title,
pinned,
fileManager,
editors,
onTogglePin,
onReveal,
onOpenInEditor,
onClose,
}: ThreadTitleMenuProps): React.ReactNode {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Use session consistently for the new menu’s code identifiers. Thread is UI/i18n terminology only; the shared component introduced it into module, type, and component names.

  • packages/presentation/ui/src/shell/thread-title-menu.tsx#L29-L65: rename the module, ThreadTitleMenu, and ThreadTitleMenuEditor to session-based names.
  • packages/presentation/ui/src/shell/index.ts#L30-L30: re-export the renamed session-based module.
  • apps/desktop/src/renderer/src/shell/desktop-shell.tsx#L2-L11: update imports and all corresponding type/component usages.

As per coding guidelines, “Use session as the code and wire term for the UI product concept Thread; the rename is UI/i18n-only and code or wire identifiers must not be renamed.”

📍 Affects 3 files
  • packages/presentation/ui/src/shell/thread-title-menu.tsx#L29-L65 (this comment)
  • packages/presentation/ui/src/shell/index.ts#L30-L30
  • apps/desktop/src/renderer/src/shell/desktop-shell.tsx#L2-L11
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/presentation/ui/src/shell/thread-title-menu.tsx` around lines 29 -
65, Rename the shared menu module and its ThreadTitleMenu and
ThreadTitleMenuEditor symbols to session-based names, preserving the existing
behavior and UI/i18n terminology. Update the re-export in
packages/presentation/ui/src/shell/index.ts at line 30 and all corresponding
imports, component usages, and type references in
apps/desktop/src/renderer/src/shell/desktop-shell.tsx at lines 2-11; do not
rename wire or other code identifiers outside this UI naming scope.

Source: Coding guidelines

Comment thread apps/desktop/src/main/editors.ts Outdated
AprilNEA added 2 commits July 25, 2026 04:11
…e executable

A cached existence-only snapshot let a present-but-non-executable PATH entry
outrank a working application bundle, and went stale as soon as an editor moved
or was uninstalled.
@AprilNEA

Copy link
Copy Markdown
Member Author

Thanks — three of the four are addressed in a9f65d75 + 41cd3e76; the naming one I'm declining, with reasoning below.

Greptile P2 (non-executable target wins) + P1 (cached target goes stale) — fixed together

Both had one root cause: a cached, existence-only snapshot. detectInstalls() memoized the first target passing existsSync, so a present-but-non-executable PATH entry outranked a working bundle, and the snapshot went stale the moment an editor moved or was uninstalled.

Rather than patch each symptom, the snapshot is gone:

export function isLaunchable(target: EditorTarget): boolean {
  if (target.kind === 'mac-app') return existsSync(target.bundle);
  try { accessSync(target.file, constants.X_OK); return true; } catch { return false; }
}

/** Resolved per call rather than cached: a snapshot goes stale the moment an editor is
 * installed, moved, upgraded, or removed. */
function resolveInstall(candidate) { return editorTargets(candidate, process.platform).find(isLaunchable); }

listEditors() and openInEditor() both resolve fresh. The cache was premature optimization — a few dozen stats on a menu open and on a click — and dropping it also removes the documented wart that a mid-session install needed an app restart. Regression test added for the P2 case: a file that exists but is 0644 is rejected, 0755 accepted.

CodeRabbit: fail() skipping cleanup — fixed, and verified by actually failing

Correct, and it was doing real damage. fail() now throws so main()'s catch (screenshot + body dump) and finally (reap daemon + Electron) run.

Verified rather than assumed — injected a deliberate failure and ran it: the finally fired (kept for debugging: HOME=…) and the run's daemon was reaped. That also surfaced the evidence for the bug: three orphaned daemon processes on this machine, started 14 h and 2 d ago, left by earlier process.exit failures.

CodeRabbit: rename ThreadTitleMenu → session-based — declining

The guideline is "the product term Thread is the code/wire term sessionthe rename is UI/i18n-only. Never rename session in wire or code identifiers." That forbids renaming session in wire/data code; it does not push Thread out of the presentation layer, which is exactly where the UI/i18n terminology is meant to live.

packages/presentation/ui already names its presentation components after the product term — 8 files and ~17 exported identifiers:

shell/sidebar/thread-row.tsx ThreadRow, ThreadRowProps
shell/sidebar/thread-im-menu.tsx ThreadImMenuItems, ThreadImMenuComponentType, ThreadImMenuChatView
shell/sidebar/group-header.tsx ThreadGroupHeader, ThreadGroupHeaderProps
shell/threads-view.tsx ThreadsView, ThreadGroupViewModel, ThreadsViewProps
shell/sidebar/thread-group-actions.ts ThreadGroupActions, ThreadGroupState
thread-groups.ts ThreadGroup

ThreadTitleMenu sits in that same directory next to ThreadImMenu. Renaming only the new one would make it the single inconsistent member. Note the data still travels as session throughout — the component takes SessionInfo-derived props and the desktop wiring calls onToggleSessionPinned / onCloseSession; no wire or data identifier was renamed.

Happy to rename the whole family in a separate sweep if that's the preferred direction, but that's a repo-wide decision, not this PR's.

CI

The Desktop App Entry failure was flaky, not caused by this PR — it's e2e:window-bounds asserting the maximized state survives a relaunch (Linux/xvfb). Re-ran the same commit with no changes: passed. Nothing here touches window state; master@364dd559 hit the same assertion for the same reason.

pnpm check:ci + pnpm test (1920 pass) green, and e2e:thread-menu PASS after the changes.

@lucas77778
lucas77778 self-requested a review July 28, 2026 12:48
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