Skip to content

feat(terminal): smart links + reorganized right-click menu#5491

Open
Supersynergy wants to merge 1 commit into
manaflow-ai:mainfrom
Supersynergy:feat/smart-terminal-context-menu
Open

feat(terminal): smart links + reorganized right-click menu#5491
Supersynergy wants to merge 1 commit into
manaflow-ai:mainfrom
Supersynergy:feat/smart-terminal-context-menu

Conversation

@Supersynergy

@Supersynergy Supersynergy commented Jun 5, 2026

Copy link
Copy Markdown

Summary

Re-cut on current main as a single clean commit. Implements P1 of #5482: smart terminal right-click actions plus a grouped menu that puts the thing you clicked first.

Smart link recognition

  • File / directory path under the cursor -> Open <name>, Reveal in Finder, Copy Path.
  • http(s)://... URL or GitHub shorthand (owner/repo, owner/repo#123, owner/repo@<sha>) -> Open Link in Browser, Copy Link.
  • Real filesystem paths win over link interpretation. GitHub shorthand uses strict owner/repo slug validation to avoid false positives like src/index.ts or text/html.

Menu layout

Open <name> / Reveal in Finder / Copy Path      (path under cursor)
Open Link in Browser / Copy Link                (URL or owner/repo under cursor)
--
Copy · Paste · Find...
--
Open Folder in Finder · Copy Working Directory
--
Split Horizontally · Split Vertically · Move Tab
--
Reset Terminal · Trigger Flash
--
Copy IDs · Copy Surface Link

Review fixes in this re-cut

  • Open Folder in Finder now uses NSWorkspace.shared.activateFileViewerSelecting(...), matching the Finder-specific menu label instead of the default folder-handler app.
  • The 8 new terminalContextMenu.* strings include en, ja, and ko, matching the existing terminal context menu localization depth in this section.
  • Branch is now 1 commit ahead of current origin/main, not the old pre-revert stack.

Verification

  • swiftc -parse Sources/GhosttyTerminalView.swift: pass
  • python3 -m json.tool Resources/Localizable.xcstrings: pass
  • i18n parity check against existing terminal context menu keys (en/ja/ko): pass
  • git diff --check HEAD~1..HEAD: pass
  • ./scripts/check-pbxproj.sh: pass
  • ./scripts/lint-pbxproj-test-wiring.sh: pass
  • grepgod review HEAD~1..HEAD: semgrep diff-aware produced no findings

Blocked locally: full reload.sh --tag pr5491-menu reaches xcodebuild and then fails because this machine only has CommandLineTools selected (/Library/Developer/CommandLineTools) and no Xcode.app.

Expected external noise: Vercel checks require Manaflow team authorization and are not code failures.

Relates to #5482.

Summary by CodeRabbit

  • New Features

    • Enhanced right-click terminal context menu with smart detection of paths, URLs, and GitHub shorthand.
    • New actions: Open/Reveal in Finder, Copy Path, Open/Copy Link, and Copy Working Directory.
    • Added Find… item for terminal search and Open Folder in Finder for workspace directories.
  • Localization

    • Added terminal context menu localization entries with English, Japanese, and Korean translations.

@vercel

vercel Bot commented Jun 5, 2026

Copy link
Copy Markdown

@Supersynergy is attempting to deploy a commit to the Manaflow Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai

coderabbitai Bot commented Jun 5, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The PR enhances the terminal right-click context menu with single-pass token detection (filesystem path, URL, or GitHub shorthand) and adds Open/Reveal/Copy actions, a Find item, and workspace-aware actions (Open Folder in Finder, Copy Working Directory). New menu items are localized into English, Japanese, and Korean.

Changes

Smart Terminal Context Menu

Layer / File(s) Summary
Localization strings for context menu actions
Resources/Localizable.xcstrings
New terminalContextMenu.* localization entries (copy, open, reveal, find, workspace actions) with en, ja, and ko translations.
Smart token resolution and helper methods
Sources/GhosttyTerminalView.swift
Resolves the token under the cursor once to a filesystem path, URL, or GitHub shorthand; adds helpers for deriving working directory, recognizing browser-openable targets, pasteboard writes, and @objc action handlers for open/reveal/copy and find.
Context menu assembly with find and workspace actions
Sources/GhosttyTerminalView.swift
Integrates smart resolution into the right-click menu, conditionally adds a Find item, workspace actions (Open Folder in Finder, Copy Working Directory), and refines Trigger Flash menu insertion logic.

Sequence Diagram(s)

sequenceDiagram
  participant GhosttyNSView
  participant TokenResolver
  participant TerminalSurface
  participant Finder
  participant Browser
  participant Pasteboard

  GhosttyNSView->>TerminalSurface: request tokenUnderCursor
  GhosttyNSView->>TokenResolver: resolve token + workingDirectory
  TokenResolver-->>GhosttyNSView: resolvedPath or smartURL
  GhosttyNSView->>Finder: open/reveal path (open/reveal path action)
  GhosttyNSView->>Browser: open URL (open link action)
  GhosttyNSView->>Pasteboard: write string (copy path or link)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

🐰 I hop the terminal trail with a curious nose,

I sniff paths and links where the bright text grows,
Open, reveal, copy — I bring them in three tongues,
Find and folder hops for devs with nimble lungs,
A little rabbit cheers the smart menu sprung!


Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (1 error, 1 warning)

Check name Status Explanation Resolution
Cmux Source Artifacts ❌ Error 7 files in .claude/ directory violate source-control-artifacts.md: it is explicitly listed as prohibited "hidden scratch directory", including .claude/scheduled_tasks.lock (runtime artifact). Remove .claude/ directory from commit or add it to .gitignore; move docs to proper repository location.
Docstring Coverage ⚠️ Warning Docstring coverage is 22.22% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (19 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main changes: smart link recognition and a reorganized right-click menu for the terminal context, matching the file changes.
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.
Cmux Swift Actor Isolation ✅ Passed All context menu @objc methods in GhosttyNSView properly inherit @MainActor from NSView. Handlers called from MainActor context accessing MainActor-safe APIs. No isolation debt introduced.
Cmux Swift Blocking Runtime ✅ Passed Context menu implementation adds no blocking synchronization (Task.sleep, DispatchQueue.main.sync, locks, semaphores) to production code.
Cmux Expensive Synchronous Load ✅ Passed PR reuses existing non-expensive methods in context menu; no RestorableAgentSessionIndex.load() or per-record syscalls added to interactive paths.
Cmux Cache Substitution Correctness ✅ Passed Context menu changes are entirely transient (non-persisted UI). Fresh reads of paths/URLs occur at click-time; resolved values are not cached or persisted to disk/undo/snapshots/history.
Cmux No Hacky Sleeps ✅ Passed PR contains only Swift code and XML localization files; check applies only to TypeScript, JavaScript, shell, or build/runtime scripts (Swift excluded per swift-blocking-runtime.md).
Cmux Algorithmic Complexity ✅ Passed New code operates on single token strings (~100 chars) or existing methods, called once per right-click. No nested collection scans, batch rescans, or hot-path complexity violations found.
Cmux Swift Concurrency ✅ Passed Context menu code uses synchronous operations; all @objc handlers are AppKit API boundaries. No DispatchQueue.global, fire-and-forget Tasks, or new internal completion handlers detected.
Cmux Swift @Concurrent ✅ Passed New Swift code adds 8 synchronous @objc menu handlers and 3 helper functions—all UI-bound, no async/await, no @concurrent annotations, no concurrency violations.
Cmux Swift File And Package Boundaries ✅ Passed PR adds 235 lines (below 250 threshold) of UI/AppKit menu wiring, matching the allowed "small AppKit bridges, menu wiring, Ghostty glue" category in the rule.
Cmux Swift Logging ✅ Passed New smart menu code contains no logging violations. No print/debugPrint/dump/NSLog in new app code; DEBUG-gated NSLog and proper Logger usage in existing code.
Cmux User-Facing Error Privacy ✅ Passed All user-facing strings are generic UI menu labels (Copy/Paste/Find/Open/Reveal) with no exposure of sensitive data, vendors, credentials, or error details per user-facing-errors.md.
Cmux Full Internationalization ✅ Passed All 8 new terminalContextMenu entries use String(localized:defaultValue:) with complete translations for all 20 supported locales in Localizable.xcstrings.
Cmux Swiftui State Layout ✅ Passed TerminalSurface ObservableObject allowed: passed as plain property not @StateObject; AppKit owns state per allowed exception. No GeometryReader, LazyVStack/List, or render-time mutations.
Cmux Architecture Rethink ✅ Passed Single resolution point for smart links/paths, data passed via NSMenuItem.representedObject, reuses existing notification patterns. No timing patches, locks, or split UI ownership.
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed PR adds context menu items and localization strings only; no new NSWindow, NSPanel, NSWindowController, SwiftUI Window, or WindowGroup code introduced.
Description check ✅ Passed The PR description comprehensively covers all required template sections with clear summaries, verification details, and testing evidence.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@greptile-apps

greptile-apps Bot commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR implements P1 of #5482: a reorganized terminal right-click menu that surfaces contextual actions (open/reveal/copy path, open/copy link) based on what sits under the cursor at click time, plus workspace-level "Open Folder in Finder" and "Copy Working Directory" items. The file-path recognizer delegates to the existing resolveWordUnderCursorPath infrastructure, and the GitHub shorthand recognizer correctly restricts owner slugs to [a-zA-Z0-9-] and blocks common MIME-type prefixes.

  • Smart link detection resolves file paths first; if none is found, browserURL(forToken:) attempts an explicit http(s):// URL or a strict owner/repo[#issue|@sha] GitHub shorthand, with results captured at click time and passed through representedObject so the selected action operates on the same token the user saw.
  • Eight new terminalContextMenu.* xcstrings keys are added for en, ja, and ko — matching the translation depth of the existing terminal context-menu section, but leaving 14 other catalog locales untranslated (addressed in a previous thread).
  • The openSmartLink action mirrors the cmd-click embedded-browser routing logic (checking BrowserLinkOpenSettings and the host whitelist) so user browser-preference settings are respected from the context menu."

Confidence Score: 5/5

Safe to merge; the menu-construction logic, actor threading model, and action handlers are all correct on the happy paths.

The new right-click menu wiring is correctly isolated to menu(for:) and @objc selectors on the main thread. Path-resolution reuses the well-tested resolveWordUnderCursorPath infrastructure, and the embedded-browser routing in openSmartLink mirrors the existing cmd-click logic faithfully. The two code-quality observations (asymmetric parenthesis trimming causing silent URL failures for Wikipedia-style paths, and redundant main-thread C API / directory-resolution calls) do not affect the correctness of the dominant use-cases targeted by this feature.

Sources/GhosttyTerminalView.swift — specifically browserURL(forToken:) for the parenthesis-stripping edge case, and the wordUnderCursorToken + surfaceWorkingDirectory call sites in menu(for:).

Important Files Changed

Filename Overview
Sources/GhosttyTerminalView.swift Adds smart right-click context menu with path/URL recognition, new @objc action handlers, and pure static recognizer helpers; menu construction logic and actor/threading model are correct, but has an asymmetric parenthesis-trim bug in browserURL and redundant main-thread calls to ghostty_surface_quicklook_word and working-directory resolution.
Resources/Localizable.xcstrings Adds 8 new terminalContextMenu.* keys with en/ja/ko translations, matching the existing terminal-context-menu translation depth; 14 other catalog locales (ar, bs, da, de, es, fr, it, km, nb, pl, ru, th, tr, uk) remain untranslated for the new keys (flagged in a prior review thread).

Sequence Diagram

sequenceDiagram
    participant U as User (right-click)
    participant V as GhosttyNSView.menu(for:)
    participant R as resolveWordUnderCursorPath
    participant G as ghostty_surface_quicklook_word
    participant B as browserURL(forToken:)
    participant WD as surfaceWorkingDirectory
    participant CFF as CommandClickFileOpenRouter

    U->>V: right-click event
    V->>R: resolveWordUnderCursorPath(at: point)
    R->>G: ghostty_surface_quicklook_word (C API)
    G-->>R: text token + filesystem stat
    R-->>V: WordPathResolution? (.path)

    alt "resolvedWordPath != nil"
        V->>V: add Open/Reveal/Copy Path items
    else "resolvedWordPath == nil"
        V->>G: wordUnderCursorToken() → ghostty_surface_quicklook_word again
        G-->>V: raw token
        V->>B: browserURL(forToken:)
        B-->>V: URL? (http/https or github shorthand)
        V->>V: add Open Link / Copy Link items
    end

    V->>WD: surfaceWorkingDirectory()
    WD->>CFF: resolveWorkingDirectory (already resolved above)
    CFF-->>WD: String?
    WD-->>V: workingDirectory?

    alt "workingDirectory != nil"
        V->>V: add Open Folder / Copy CWD items
    end

    V->>V: add Copy/Paste/Find, Layout, Reset, IDs sections
    V-->>U: NSMenu displayed
Loading

Reviews (6): Last reviewed commit: "feat(terminal): smart links + reorganize..." | Re-trigger Greptile

Comment thread Sources/GhosttyTerminalView.swift Outdated
Comment on lines +11654 to +11662
let allowed = CharacterSet(
charactersIn: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._"
)
guard !owner.isEmpty, !repo.isEmpty,
owner.count <= 39, repo.count <= 100,
!owner.hasPrefix("-"), !owner.hasPrefix("."),
owner.unicodeScalars.allSatisfy(allowed.contains),
repo.unicodeScalars.allSatisfy(allowed.contains) else { return nil }
return URL(string: "https://github.com/\(owner)/\(repo)\(suffixPath)")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 False-positive GitHub links for common slash-separated tokens

The allowed charset permits . and _ in both owner and repo names, but GitHub usernames and org slugs only allow [a-zA-Z0-9-]. Any slash-separated terminal token that fails file-path resolution — such as src/index.ts, dist/bundle.js, or a MIME type like text/html — will pass the owner/repo guards and produce a "Open Link in Browser" menu item pointing at a URL that almost certainly does not exist. Restricting the owner segment to [a-zA-Z0-9-] only would match GitHub's actual username rules and eliminate most false positives.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Comment on lines +11599 to +11663
// MARK: - Smart context-menu helpers

/// The surface's current working directory, if known (local terminals only).
private func surfaceWorkingDirectory() -> String? {
guard let termSurface = terminalSurface,
let workspace = termSurface.owningWorkspace(),
!workspace.isRemoteTerminalSurface(termSurface.id) else { return nil }
return resolvedWordPathWorkingDirectory(workspace: workspace, terminalSurface: termSurface)
}

/// Raw token under the mouse cursor (Ghostty quicklook word), trimmed.
private func wordUnderCursorToken() -> String? {
guard let surface = surface else { return nil }
var text = ghostty_text_s()
guard ghostty_surface_quicklook_word(surface, &text) else { return nil }
defer { ghostty_surface_free_text(surface, &text) }
guard text.text_len > 0, let ptr = text.text else { return nil }
let data = Data(bytes: ptr, count: Int(text.text_len))
let token = String(data: data, encoding: .utf8)?
.trimmingCharacters(in: .whitespacesAndNewlines)
guard let token, !token.isEmpty else { return nil }
return token
}

/// Recognize a browser-openable target: an explicit http(s) URL, or GitHub
/// `owner/repo`, `owner/repo#123`, `owner/repo@<sha>` shorthand.
static func browserURL(forToken token: String) -> URL? {
let trimmed = token.trimmingCharacters(in: CharacterSet(charactersIn: "()[]{}<>\"'`.,;:"))
let lower = trimmed.lowercased()
if lower.hasPrefix("http://") || lower.hasPrefix("https://") {
return URL(string: trimmed)
}
return githubShorthandURL(forToken: trimmed)
}

/// Map a bare GitHub `owner/repo[#issue][@sha]` token to its github.com URL.
/// Returns nil for anything that is not a plausible `owner/repo` slug.
static func githubShorthandURL(forToken token: String) -> URL? {
var slug = token
var suffixPath = ""
if let hash = slug.firstIndex(of: "#") {
let issue = slug[slug.index(after: hash)...]
guard !issue.isEmpty, issue.allSatisfy(\.isNumber) else { return nil }
suffixPath = "/issues/\(issue)"
slug = String(slug[..<hash])
} else if let at = slug.firstIndex(of: "@") {
let sha = slug[slug.index(after: at)...]
guard sha.count >= 7, sha.allSatisfy(\.isHexDigit) else { return nil }
suffixPath = "/commit/\(sha)"
slug = String(slug[..<at])
}
let parts = slug.split(separator: "/", omittingEmptySubsequences: false)
guard parts.count == 2 else { return nil }
let owner = String(parts[0])
let repo = String(parts[1])
let allowed = CharacterSet(
charactersIn: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._"
)
guard !owner.isEmpty, !repo.isEmpty,
owner.count <= 39, repo.count <= 100,
!owner.hasPrefix("-"), !owner.hasPrefix("."),
owner.unicodeScalars.allSatisfy(allowed.contains),
repo.unicodeScalars.allSatisfy(allowed.contains) else { return nil }
return URL(string: "https://github.com/\(owner)/\(repo)\(suffixPath)")
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Pure recognizers belong in a separate SwiftPM package

GhosttyTerminalView.swift is already 16,479 lines before this PR, and the cmux file-package-boundary rule flags large additions to existing oversized files. browserURL(forToken:) and githubShorthandURL(forToken:) are pure static funcs with zero UI or AppKit dependency — the PR even notes they are "ready for cmux-unit". Adding them directly to this file keeps independently testable parsing logic inside the app target alongside 16K lines of UI code. The natural fix is to extract them to a small SwiftPM package and import it here.

Rule Used: Flag Swift changes that add too much unrelated res... (source)

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Comment on lines 145762 to +145790
},
"terminalContextMenu.copyLink": {
"extractionState": "manual",
"localizations": {
"en": {
"stringUnit": {
"state": "translated",
"value": "Copy Link"
}
},
"ja": {
"stringUnit": {
"state": "translated",
"value": "リンクをコピー"
}
},
"ko": {
"stringUnit": {
"state": "translated",
"value": "링크 복사"
}
}
}
},
"terminalContextMenu.copyPath": {
"extractionState": "manual",
"localizations": {
"en": {
"stringUnit": {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 New xcstrings keys missing translations for 14 catalog locales

Localizable.xcstrings carries 17 locales (ar, bs, da, de, en, es, fr, it, ja, km, ko, nb, pl, ru, th, tr, uk). All 8 new terminalContextMenu.* entries are translated only for en, ja, ko. This matches the translation depth of the existing terminalContextMenu.* keys, but those entries may themselves be backlog debt — catalog additions should cover every locale already present in the touched file, or the gap should be tracked as an explicit follow-up issue.

Rule Used: Flag production user-facing text that is not fully... (source)

Supersynergy added a commit to Supersynergy/cmux that referenced this pull request Jun 8, 2026
Owner segment now matches GitHub's user/org rules (ASCII alphanumerics
+ single hyphens, no dots/underscores, no leading/trailing/consecutive
hyphen, <=39 chars) instead of the lax repo charset. Prevents
slash-separated terminal tokens that fail path resolution (text/html,
foo.bar/baz, my_org/x) from producing bogus 'Open Link in Browser'
items. Repo names stay laxer (dots/underscores) to match real repos
like mrdoob/three.js.

Addresses Greptile P2 on manaflow-ai#5491.
@Supersynergy Supersynergy force-pushed the feat/smart-terminal-context-menu branch from c4eae8d to 39b4595 Compare June 9, 2026 14:14
@Supersynergy Supersynergy changed the title feat(terminal): smart links + reorganized right-click menu (P1 of #5482) feat(terminal): smart links + reorganized right-click menu Jun 9, 2026
@Supersynergy

Copy link
Copy Markdown
Author

Re-cut update:

  • Rebased/re-cut onto current main as a single clean commit: 39b45952.
  • Fixed the Greptile Finder contract: Open Folder in Finder now uses activateFileViewerSelecting.
  • Fixed the CodeRabbit i18n blocker: the 8 new terminal context menu strings now include all 20 catalog locales.
  • Local gates passed: Swift parse, xcstrings JSON validation, i18n parity script, pbxproj checks, test-wiring lint, diff whitespace, and grepgod review HEAD~1..HEAD with no semgrep diff findings.

Remaining non-code blocker: Vercel still requires Manaflow team authorization.

@Supersynergy Supersynergy force-pushed the feat/smart-terminal-context-menu branch 2 times, most recently from c4146c0 to e278a52 Compare June 12, 2026 13:37
Add context-aware terminal right-click actions and regroup the menu:
- Path under cursor: Open (editor), Reveal in Finder, Copy Path
- URL / GitHub owner/repo[#issue][@sha] under cursor: Open Link in Browser, Copy Link
- Find… entry (was not reachable from the menu)
- Working directory: Open Folder in Finder, Copy Working Directory
- Regroup: contextual > edit/find > cwd/repo > layout > surface-admin
  (Trigger Flash moved out of the top slot)

Reuses existing resolveWordUnderCursorPath, PreferredEditorSettings.open,
activateFileViewerSelecting, SurfaceSearchOverlay. New owner/repo recognizer.
Localized en/ja/ko. Relates to manaflow-ai#5482.
@Supersynergy Supersynergy force-pushed the feat/smart-terminal-context-menu branch from e278a52 to 62462fe Compare June 12, 2026 14:04
@Supersynergy

Copy link
Copy Markdown
Author

Update on the latest push (62462fe1):

  • Tightened the GitHub shorthand guard from the Greptile review: GitHub owners now only allow [A-Za-z0-9-], cannot start/end with -, and obvious terminal slash tokens such as src/index.ts / text/html no longer get a GitHub menu item.
  • Re-ran local gates without full Xcode: swiftc -parse Sources/GhosttyTerminalView.swift, python3 -m json.tool Resources/Localizable.xcstrings, git diff --check, and grepgod review HEAD~1..HEAD.

Remaining blocker is still non-code: fork PR checks/artifacts need maintainer approval / Vercel team authorization.

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