Skip to content

Open markdown files with shared viewer path - #4285

Merged
lawrencecchen merged 5 commits into
mainfrom
feat-markdown-open-path
May 18, 2026
Merged

lawrencecchen merged 5 commits into
mainfrom
feat-markdown-open-path

Conversation

@lawrencecchen

@lawrencecchen lawrencecchen commented May 18, 2026 •

Copy link
Copy Markdown
Contributor

Summary:

  • Route external file opens and file explorer opens through Workspace.openFileSurfaces so markdown files create MarkdownPanel surfaces.
  • Reuse the same file-surface helper for dropped-file insertions and remove the duplicate dropped-file opener.

Tests:

  • xcodebuildmcp macos test --scheme cmux-unit with MarkdownPanelTests/testFileOpenRoutesMarkdownFilesToPreviewMarkdownPanel and MarkdownPanelTests/testExternalFileOpenRoutesMarkdownFilesToPreviewMarkdownPanel
  • ./scripts/reload.sh --tag mdopen

Note

Medium Risk
Changes core file-opening paths (external open, sidebar open, drag-and-drop) to a unified helper that can reuse existing tabs and route .md files to MarkdownPanel, so regressions could affect tab creation/focus and drop behavior.

Overview
Unifies file opening behavior across the app. External file opens (AppDelegate), sidebar opens (ContentView/RightSidebarToolPanel), and drag-and-drop inserts (Workspace) now route through Workspace.openFileSurfaces(..., reuseExisting: true) so markdown paths open as preview MarkdownPanels and reopening focuses the existing surface instead of creating duplicates.

Simplifies drop handling. Removes the bespoke openDroppedFileSurfaces helper and renames splitPaneWithDroppedFile to splitPaneWithFileSurface, relying on the shared open/split logic and returning success based on whether any panels were opened.

Adds coverage for the new external-open path. Introduces MarkdownPanelTests.testExternalFileOpenRoutesMarkdownFilesToPreviewMarkdownPanel to verify .md external opens create a preview markdown surface and repeated opens reuse the same panel.

Reviewed by Cursor Bugbot for commit 2607bd1. Bugbot is set up for automated code reviews on this repo. Configure here.


Summary by cubic

Route all markdown file opens through the shared openFileSurfaces path so they always open as a preview MarkdownPanel and reuse existing tabs. This unifies external, file explorer, and drag-and-drop behavior and removes duplicate code.

  • Bug Fixes

    • External and file explorer opens now route to preview MarkdownPanel and reuse an existing tab.
    • Drag-and-drop uses the same opener for consistent behavior.
  • Refactors

    • Consolidated to openFileSurfaces; removed openDroppedFileSurfaces; renamed splitPaneWithDroppedFile to splitPaneWithFileSurface.
    • Updated AppDelegate, ContentView, and RightSidebarToolPanel to call openFileSurfaces(focus: true, reuseExisting: true).
    • Hardened the external-open test to assert preview mode, reuse of the same MarkdownPanel instance, and no FilePreviewPanel.

Written for commit 2607bd1. Summary will update on new commits. Review in cubic

Summary by CodeRabbit

  • Refactor

    • External file opens and sidebar previews now reuse and focus existing preview panels consistently.
    • Drag-and-drop and split-pane behavior for dropped files is streamlined for more predictable preview placement.
  • Tests

    • Added coverage to verify external markdown files open in preview panels and that reopening reuses the same panel.

Review Change Stack

@vercel

vercel Bot commented May 18, 2026 •

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
cmux Canceled Canceled May 18, 2026 11:03am
cmux-staging Building Building Preview, Comment May 18, 2026 11:03am

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@coderabbitai

coderabbitai Bot commented May 18, 2026 •

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

The PR migrates AppDelegate, ContentView, RightSidebarToolPanel, and Workspace drop handlers to use openFileSurfaces (with reuse/focus) and adds splitPaneWithFileSurface; a test verifies external markdown files open in preview MarkdownPanels and are reused on reopen.

Changes

File-open routing migration to openFileSurfaces

Layer / File(s) Summary
AppDelegate external file routing
Sources/AppDelegate.swift
openFilePreviewInPreferredMainWindow now calls openFileSurfaces(..., reuseExisting: true, focus: true), returns success only if surfaces opened, and changes the DEBUG log key from file.preview.externalOpen to file.externalOpen.
UI panel callsite updates
Sources/ContentView.swift, Sources/RightSidebarToolPanel.swift
Sidebar and right-panel open methods now call openFileSurfaces with filePaths as a single-element array and focus/reuseExisting enabled, replacing single-file preview API calls.
Workspace drop handlers and split helper
Sources/Workspace.swift
Drop handlers for file-preview/external drops now use openFileSurfaces for inserts, introduce splitPaneWithFileSurface(...) for split creation, and adjust success-return semantics (insert checks emptiness; split returns success if first-surface creation succeeded).
Test for external markdown file routing
cmuxTests/MarkdownPanelTests.swift
testExternalFileOpenRoutesMarkdownFilesToPreviewMarkdownPanel sets up a temp workspace, opens an external markdown file via openFilePreviewInPreferredMainWindow, asserts a single .preview MarkdownPanel is created and reused on reopen, and restores AppDelegate state.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • manaflow-ai/cmux#4085: Related refactor of file-open routing to openFileSurfaces and markdown-vs-file-preview placement.
  • manaflow-ai/cmux#3139: Earlier changes around preview-surface open/focus APIs that this PR replaces with batch-surface APIs.
  • manaflow-ai/cmux#3539: Pane-level drop routing updates that align with the Workspace split/open changes here.

Poem

🐰 I hopped from sidebar to pane so bright,
One API gathered files in flight,
Markdown found its snug preview bed,
Reopened the same—no duplicates spread,
A tidy hop, the workspace sleeps well tonight.

🚥 Pre-merge checks | ✅ 14 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ❓ Inconclusive The pull request description covers the main changes and testing approach but lacks a demo video and has incomplete checklist items. Add a demo video showing the external file open and markdown preview behavior. Clarify which checklist items have been completed or add evidence (e.g., test results, bot review confirmations).
✅ Passed checks (14 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: routing markdown file opens through a shared viewer/surface path instead of separate handlers.
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 production changes maintain Swift 6 actor isolation. Methods remain @MainActor (AppDelegate, Workspace, RightSidebarToolPanel, ContentView). No isolation mistakes introduced.
Cmux Swift Blocking Runtime ✅ Passed PR changes route file opens via openFileSurfaces() without blocking. Blocking patterns (DispatchSemaphore, Task.sleep) are pre-existing, not introduced here.
Cmux No Hacky Sleeps ✅ Passed PR adds no hacky sleeps to production code. File-open routing uses openFileSurfaces without timing constructs. Test code uses deterministic fulfillment. Only build infrastructure files contain sleeps.
Cmux Swift Concurrency ✅ Passed Synchronous routing changes only. No new DispatchQueues, Combine patterns, or fire-and-forget Tasks. Does not materially expand legacy async patterns.
Cmux Swift @Concurrent ✅ Passed All changes are synchronous, MainActor-bound functions. No @concurrent, nonisolated async, or async patterns introduced. No concurrency violations found.
Cmux Swift File And Package Boundaries ✅ Passed Small incidental changes (≤8 lines) to consolidate file-opening logic. Workspace.swift shrinks by 42 lines. Allowed case: existing oversized files touched minimally with no responsibility mixing.
Cmux Swift Logging ✅ Passed The PR's logging changes comply with swift-logging.md rules. The only new logging statement uses cmuxDebugLog wrapped in #if DEBUG, avoiding forbidden functions and not exposing secrets.
Cmux User-Facing Error Privacy ✅ Passed The PR does not add or materially change any user-facing errors, alerts, or copy. DEBUG-only logging is exempt. No sensitive information is exposed in user-visible text.
Cmux Swiftui State Layout ✅ Passed PR changes do not violate SwiftUI state layout rules. Changes are AppKit delegate and model code only. No new @Published/@StateObject, GeometryReader layout changes, or store references in lists.
Cmux Architecture Rethink ✅ Passed Consolidates file opens through unified openFileSurfaces() with clear ownership and single source of truth. No timing repairs, observers, side channels, or split lifecycle.
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed No new NSWindow, NSPanel, NSWindowController classes. Routes through existing panels. No new cmux.* identifiers. Test window allowed.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat-markdown-open-path

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 May 18, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

Routes all file-opening entry points (external opens via AppDelegate, file-explorer/sidebar opens, and drag-and-drop) through the unified openFileSurfaces helper so .md files open as MarkdownPanel preview surfaces with tab reuse. The PR also removes the now-redundant openDroppedFileSurfaces private method and renames splitPaneWithDroppedFile to splitPaneWithFileSurface.

  • AppDelegate, ContentView, RightSidebarToolPanel: each call-site switches from openOrFocusFilePreviewSurface to openFileSurfaces(focus: true, reuseExisting: true), which dispatches to either openOrFocusMarkdownSurface or openOrFocusFilePreviewSurface based on path extension.
  • Workspace.swift: handleFilePreviewDrop and handleExternalFileDrop replace the deleted openDroppedFileSurfaces with inline openFileSurfaces(reuseExisting: false) calls, preserving always-create semantics for drops; splitPaneWithDroppedFile is renamed to splitPaneWithFileSurface with identical logic.
  • MarkdownPanelTests: new test verifies external .md opens create a MarkdownPanel (not a FilePreviewPanel) and that a second open reuses the existing panel instead of duplicating it.

Confidence Score: 5/5

Safe to merge; the change is a clean unification of three independent file-open entry points through a well-tested shared helper with no new state or timing dependencies.

All three call-sites (external open, file explorer, sidebar) are updated consistently. Drop insertion preserves always-create semantics by omitting reuseExisting. The removed openDroppedFileSurfaces was a strict subset of openFileSurfaces with no behavioral difference. The return-value fix in AppDelegate (was always true, now reflects actual success) is a correctness improvement. The new unit test exercises the key reuse path end-to-end.

No files require special attention.

Important Files Changed

Filename Overview
Sources/AppDelegate.swift Switches openFilePreviewInPreferredMainWindow from openOrFocusFilePreviewSurface to openFileSurfaces(reuseExisting: true); now correctly returns false when panel creation fails instead of always returning true.
Sources/ContentView.swift File-explorer open now calls openFileSurfaces(reuseExisting: true) so markdown files open as MarkdownPanel and existing tabs are reused.
Sources/RightSidebarToolPanel.swift Sidebar file-open now uses openFileSurfaces(reuseExisting: true), consistent with the other surfaces.
Sources/Workspace.swift Removes openDroppedFileSurfaces; drop-insert calls use openFileSurfaces(reuseExisting: false) (equivalent logic); renames splitPaneWithDroppedFile to splitPaneWithFileSurface with no logic change.
cmuxTests/MarkdownPanelTests.swift New test testExternalFileOpenRoutesMarkdownFilesToPreviewMarkdownPanel verifies external .md opens produce a MarkdownPanel, no FilePreviewPanel is created, and a second open reuses the same panel object.

Reviews (3): Last reviewed commit: "harden markdown reuse test" | Re-trigger Greptile

Comment thread cmuxTests/MarkdownPanelTests.swift Outdated
Comment on lines +131 to +134
XCTAssertEqual(markdownPanels.first?.filePath, fileURL.path)
XCTAssertEqual(markdownPanels.first?.displayMode, .preview)
XCTAssertTrue(workspace.panels.values.compactMap { $0 as? FilePreviewPanel }.isEmpty)

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 XCTFail without early return in #else branch

XCTFail records a test failure but does not stop execution. In a non-DEBUG build the test continues past the #else block and calls openFilePreviewInPreferredMainWindow on an AppDelegate whose window context was never registered, which could produce a misleading second failure or a crash rather than a single clear signal. Adding return after XCTFail keeps the failure atomic and avoids confusing downstream assertions.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed by returning after the non-DEBUG XCTFail so that branch reports a single atomic failure.

— Claude Code

@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: 1

🤖 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 `@cmuxTests/MarkdownPanelTests.swift`:
- Around line 104-120: The test creates a TabManager but never sets it as the
active manager, so call TerminalController.shared.setActiveTabManager(manager)
right after creating manager (same spot as in
testFileOpenRoutesMarkdownFilesToPreviewMarkdownPanel) and add cleanup in the
defer to reset the active manager to nil
(TerminalController.shared.setActiveTabManager(nil)) to restore test isolation;
references: TabManager, TerminalController.shared.setActiveTabManager(...), and
AppDelegate.openFilePreviewInPreferredMainWindow.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 17dbc686-0a40-4b8b-906a-a2b12e59ebd8

📥 Commits

Reviewing files that changed from the base of the PR and between 98cd225 and 2e66669.

📒 Files selected for processing (5)
  • Sources/AppDelegate.swift
  • Sources/ContentView.swift
  • Sources/RightSidebarToolPanel.swift
  • Sources/Workspace.swift
  • cmuxTests/MarkdownPanelTests.swift

Comment thread cmuxTests/MarkdownPanelTests.swift

@cubic-dev-ai cubic-dev-ai 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.

No issues found across 5 files

Re-trigger cubic

@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: 1

🤖 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 `@cmuxTests/MarkdownPanelTests.swift`:
- Around line 132-145: Capture the initial MarkdownPanel instance (e.g., let
original = workspace.panels.values.compactMap { $0 as? MarkdownPanel }.first)
and store its identity (use the instance itself or a unique property like
objectIdentifier(original) or original.id if available), then call
appDelegate.openFilePreviewInPreferredMainWindow(...) and assert that the same
MarkdownPanel instance still exists in workspace.panels (compare identities, not
just count) to ensure reuse rather than replacement.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: a5d2f8b7-a9e7-47aa-9489-0791d7fff041

📥 Commits

Reviewing files that changed from the base of the PR and between 2e66669 and 3060526.

📒 Files selected for processing (1)
  • cmuxTests/MarkdownPanelTests.swift

Comment thread cmuxTests/MarkdownPanelTests.swift
@lawrencecchen
lawrencecchen merged commit 681dc97 into main May 18, 2026
29 checks passed
@lawrencecchen
lawrencecchen deleted the feat-markdown-open-path branch May 18, 2026 11:04
shishirsharma added a commit to shishirsharma/cmux that referenced this pull request May 20, 2026
manaflow-ai#4285 routes all .md file opens through Workspace.openFileSurfaces and
sends them directly to MarkdownPanel (the rendered viewer), so a .md file
no longer lands in FilePreviewPanel from any current entry point. The
in-header markdown preview button is therefore unreachable in normal
flows and would only fire on legacy session restores.

Remove the button, isMarkdownFile, and owningWorkspace() helper. Keep
the .mkd/.mkdn/.mdwn/.mdown additions in textExtensions inline, since
upstream MarkdownPanelFileLinkResolver.isMarkdownPathLike only covers
md/markdown/mkd/mdx — the long-tail aliases still benefit text-mode
detection if they ever reach FilePreviewPanel.

PR scope is now syntax highlighting only.

This branch was successfully deployed

1 active deployment
Preview – cmux — 2607bd1b Deployed May 18, 2026 by vercel[bot]
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