Skip to content

Fix folder proxy icon drag - #3804

Merged
austinywang merged 6 commits into
manaflow-ai:mainfrom
lederniermagicien:fix-folder-proxy-icon-drag
May 10, 2026
Merged

austinywang merged 6 commits into
manaflow-ai:mainfrom
lederniermagicien:fix-folder-proxy-icon-drag

Conversation

@lederniermagicien

@lederniermagicien lederniermagicien commented May 9, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • Move the draggable folder proxy icon into a small borderless child panel so dragging the icon starts a file drag instead of being treated as a main-window titlebar drag.
  • Keep the visible titlebar slot stable and aligned so the current directory label stays next to the folder icon.
  • Preserve normal window movement outside the proxy icon area.
  • Follows up on 0d98db7, which fixed titlebar text dragging while preserving folder icon drag.

Testing

  • Built and launched tagged Debug app:
    CMUX_SKIP_ZIG_BUILD=1 ./scripts/reload.sh --tag fix-swish-folder-drag --launch
  • Manually verified the folder icon can be dragged without moving the whole window.
  • Manually verified the current directory label remains next to the folder icon.
  • Automated regression test not added: this depends on AppKit/window-manager drag behavior and requires interactive verification; a source-text or metadata test would not cover the regression.
  • Docs/changelog not updated: this is a narrow titlebar drag bug fix with no docs-facing behavior change.

Demo Video

For UI or behavior changes, include a short demo video (GitHub upload, Loom, or other direct link).

Review Trigger (Copy/Paste as PR comment)

@codex review
@coderabbitai review
@greptile-apps review
@cubic-dev-ai review

Checklist

  • I tested the change locally
  • I added or updated tests for behavior changes (N/A)
  • I updated docs/changelog if needed (N/A)
  • I requested bot reviews after my latest commit (copy/paste block above or equivalent)
  • All code review bot comments are resolved
  • All human review comments are resolved

Summary by cubic

Fixes folder proxy icon drags so they start a file drag, not a window drag. Keeps the titlebar layout stable and normal window movement, including with macOS and third‑party tiling tools.

  • Bug Fixes
    • Renders the folder icon in a 16×16 borderless, non‑activating child NSPanel above the titlebar so drags are file drags; delays drag start until slight cursor movement; adds double‑click to reveal in Finder, right/control‑click path menu, and a drag tooltip.
    • Keeps the window movable (isMovable = true) while background dragging stays off, including in minimal mode, to preserve UI interactions and tiling/window‑manager support.
    • Repositions and reattaches the detached icon on move/resize/minimize/deminiaturize, marks it auxiliary (.fullScreenAuxiliary, cmux.folderDragIcon), and replaces the old drag‑suppression view with DetachedFolderDragIcon.

Written for commit e4bf4c7. Summary will update on new commits.

Summary by CodeRabbit

  • New Features

    • Detached, draggable titlebar folder icon (16×16) that stays aligned with the main window.
  • Improvements

    • Window movement behavior refined to support macOS tiling/window-management tools while preserving expected drag interactions; background-click dragging disabled.
  • Bug Fixes

    • Auxiliary icon window now correctly owns the standard close shortcut, improving close-key behavior for the detached icon panel.

Review Change Stack

@vercel

vercel Bot commented May 9, 2026

Copy link
Copy Markdown

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

A member of the Team first needs to authorize it.

@chatgpt-codex-connector

Copy link
Copy Markdown

To use Codex here, create a Codex account and connect to github.

@coderabbitai

coderabbitai Bot commented May 9, 2026 •

Copy link
Copy Markdown

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

OS-level window movability is enabled in AppDelegate and ContentView (isMovable = true) while keeping background dragging disabled. A new DetachedFolderDragIcon NSViewRepresentable hosts a borderless NSPanel with DraggableFolderNSView synchronized to the parent window. Drag-session movable-state bookkeeping and related restoration calls were removed and some debug logging was simplified.

Changes

Window Movability and Detached Folder Drag Icon

Layer / File(s) Summary
Window Movability Configuration
Sources/AppDelegate.swift, Sources/ContentView.swift
AppDelegate.createMainWindow sets window.isMovable = true with comments clarifying intent for OS tiling/window-management tools. ContentView window accessor sets isMovable = true and isMovableByWindowBackground = false.
Detached Folder Icon Component
Sources/ContentView.swift, Sources/DetachedFolderDragIcon.swift
Adds DetachedFolderDragIcon NSViewRepresentable and host view that create and maintain a borderless, non-activating NSPanel containing DraggableFolderNSView; synchronizes panel frame with parent window and tears down observers/panel on deinit.
UI Integration
Sources/ContentView.swift
ContentView titlebar folder icon switches from DraggableFolderIcon to DetachedFolderDragIcon with an explicit 16×16 frame.
Draggable View Implementation
Sources/DetachedFolderDragIcon.swift
Implements DraggableFolderNSView rendering, drag behavior, hit testing, mouse interactions, and contextual path menu handlers.
Draggable View Logging & Drag-State
Sources/ContentView.swift, Sources/DetachedFolderDragIcon.swift
Removes hasActiveDragSession = true at drag start, removes restoreWindowMovableStateIfNeeded() calls after drag end and mouse-up, removes movable-state bookkeeping fields, and simplifies hit-test and mouse-down debug logging in DraggableFolderNSView.
Auxiliary Window Identifier
Sources/cmuxApp.swift
Adds "cmux.folderDragIcon" to cmuxAuxiliaryWindowIdentifiers so the detached icon window is treated as an auxiliary window for close-shortcut ownership.
Xcode Project Wiring
GhosttyTabs.xcodeproj/project.pbxproj
Adds DetachedFolderDragIcon.swift file reference and PBX build entries and wires it into the app target's sources build phase.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • manaflow-ai/cmux#1473: Main PR updates auxiliary-window ownership by adding "cmux.folderDragIcon" to cmuxAuxiliaryWindowIdentifiers (the same helper introduced in PR #1473).
  • manaflow-ai/cmux#3753: Updates cmuxAuxiliaryWindowIdentifiers to include "cmux.folderDragIcon", registering the new detached-folder panel.
  • manaflow-ai/cmux#2030: Related changes to the titlebar folder icon in ContentView and its positioning/behavior.

Poem

🐰 A tiny panel hops out to play,
The window wanders safely through the day,
The folder floats, sixteen by sixteen,
Observers tidy, no state left unseen,
SwiftUI and AppKit dance — hooray!

🚥 Pre-merge checks | ✅ 14 | ❌ 1

❌ Failed checks (1 warning)

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.
✅ Passed checks (14 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Fix folder proxy icon drag' directly describes the main change: resolving the folder icon drag behavior to start a file drag instead of window movement.
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 PR complies with Swift 6 actor isolation rules. DetachedFolderDragIcon properly isolates @MainActor classes and complies with SwiftUI view exemption.
Cmux Swift Blocking Runtime ✅ Passed PR introduces no blocking synchronization. DetachedFolderDragIcon uses @MainActor and event-driven NotificationCenter updates. No semaphores, sleeps, locks, or asyncAfter found.
Cmux No Hacky Sleeps ✅ Passed Check not applicable to PR. Scope is TypeScript/JavaScript/shell/build scripts; all changes here are Swift code (covered by separate check) and project config file.
Cmux Swift Concurrency ✅ Passed PR introduces no legacy async patterns. New DetachedFolderDragIcon.swift uses only @MainActor and AppKit. Existing files modified without adding DispatchQueue, Task, async/await, or Combine patterns.
Cmux Swift @Concurrent ✅ Passed No @concurrent annotation violations. PR introduces no async functions, no @concurrent annotations, and no concurrent annotation rule violations. All code is synchronous and properly @MainActor-bound.
Cmux Swift File And Package Boundaries ✅ Passed DetachedFolderDragIcon.swift (358 lines) is under 400-line threshold, contains only AppKit/UI bridge code, no state/persistence/network mixing. No large additions to oversized files.
Cmux Swift Logging ✅ Passed PR passes logging rules: only #if DEBUG cmuxDebugLog calls (4), no print/NSLog, no file-scoped Logger, no secrets.
Cmux Swiftui State Layout ✅ Passed AppKit bridge pattern using NSViewRepresentable with NSView subclasses for state. No ObservableObject, @Published, @StateObject, GeometryReader, or render-time mutations present.
Cmux Architecture Rethink ✅ Passed Focused fix with clear single owner and documented invariant. Required AppKit platform callback properly managed with weak self and cleanup. No timing repairs, duplicate wiring, or competing state.
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed New NSPanel properly registered with cmux.folderDragIcon identifier in cmuxAuxiliaryWindowIdentifiers. Marked .fullScreenAuxiliary and .nonactivatingPanel. Linting script passes.
Description check ✅ Passed The pull request description comprehensively covers the summary, testing approach, demo video, and checklist items required by the template.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


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 9, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR replaces the in-process window-drag-suppression mechanism (isMovable toggle + a chain of suppression/restore helpers) with a floating borderless NSPanel child window that hosts the folder icon, so mouse-down events on the icon are always routed as file drags rather than titlebar drags. It also lifts isMovable = false to true in both the normal and minimal-mode window setup so macOS tiling tools (e.g., Swish) correctly recognise the window as movable.

  • DetachedFolderDragIcon.swift (new): DetachedFolderDragIconHostView manages the child panel lifecycle — creation, reparenting, frame sync via NotificationCenter, and teardown. DraggableFolderNSView is now @MainActor-annotated, simplified to use a drag-threshold gesture instead of window-movability suppression.
  • ContentView.swift / AppDelegate.swift: Removes the old drag-icon hierarchy and suppression helpers; flips isMovable to true with an updated comment.
  • cmuxApp.swift: Registers \"cmux.folderDragIcon\" as an auxiliary window identifier so the panel is excluded from the main-window set.

Confidence Score: 5/5

Safe to merge; the child-panel approach cleanly eliminates the drag-suppression state machine and actor isolation is correctly enforced throughout.

The new NSPanel child-window correctly intercepts mouse events before the parent window sees them, deinit teardown is guarded by MainActor.assumeIsolated, the installParentWindowObservers guard now re-installs when the observed window changes, and canJoinAllSpaces is absent. The only finding is a dead-code fallback in mouseDragged that is unreachable in normal AppKit event delivery.

Sources/DetachedFolderDragIcon.swift — new file carrying the core panel-lifecycle logic; worth a final read-through, but no blocking issues found.

Important Files Changed

Filename Overview
Sources/DetachedFolderDragIcon.swift New 389-line file introducing the child-panel architecture; actor isolation and observer guard are correctly handled; one unreachable fallback in mouseDragged.
Sources/ContentView.swift Removes old DraggableFolderIcon hierarchy and drag-suppression helpers; swaps in DetachedFolderDragIcon; updates isMovable comment in minimal-mode setup.
Sources/AppDelegate.swift Changes isMovable from false to true with updated comment explaining background-drag stays disabled; the tiling-tool rationale is clear.
Sources/cmuxApp.swift Adds "cmux.folderDragIcon" to the auxiliary window identifier set; straightforward one-liner change.
GhosttyTabs.xcodeproj/project.pbxproj Adds DetachedFolderDragIcon.swift to PBXBuildFile, PBXFileReference, and Sources build phase; no issues.

Sequence Diagram

sequenceDiagram
    participant SwiftUI as SwiftUI Layout
    participant Host as DetachedFolderDragIconHostView
    participant Panel as NSPanel (child window)
    participant DragView as DraggableFolderNSView
    participant Parent as Parent NSWindow
    participant NC as NotificationCenter

    SwiftUI->>Host: makeNSView / updateNSView
    Host->>Host: syncDetachedIcon()
    alt "childWindow == nil (first time)"
        Host->>DragView: init(directory:)
        Host->>Panel: NSPanel(borderless, nonactivating)
        Panel->>DragView: "contentView = iconView"
        Host->>Parent: addChildWindow(panel, ordered: .above)
        Host->>NC: addObserver(didMove/didResize/didMiniaturize/didDeminiaturize)
    end
    Host->>Panel: orderFront(nil)
    Host->>Host: syncDetachedIconFrame()
    Host->>Panel: setFrame(screenRect)

    NC-->>Host: didMove / didResize
    Host->>Host: syncDetachedIconFrame()
    Host->>Panel: setFrame(updatedRect)

    Note over DragView: User drags icon
    DragView->>DragView: mouseDown to pendingDragEvent
    DragView->>DragView: mouseDragged (threshold met)
    DragView->>DragView: beginDraggingSession(with: mouseDownEvent)

    Note over Host: View removed from window
    SwiftUI->>Host: viewDidMoveToWindow(nil)
    Host->>Host: tearDownDetachedIcon()
    Host->>NC: removeObserver (all)
    Host->>Parent: removeChildWindow(panel)
    Host->>Panel: orderOut(nil)
Loading

Reviews (7): Last reviewed commit: "Delay folder icon drag until movement" | Re-trigger Greptile

Comment thread Sources/ContentView.swift Outdated
Comment thread Sources/ContentView.swift Outdated
Comment thread Sources/ContentView.swift Outdated

@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 `@Sources/ContentView.swift`:
- Around line 14617-14774: The DetachedFolderDragIcon and
DetachedFolderDragIconHostView (and any small helper such as
DraggableFolderNSView references) should be moved out of ContentView.swift into
their own platform-bridge file (e.g. Sources/DetachedFolderDragIcon.swift):
create the new Swift file, copy the private struct DetachedFolderDragIcon and
the `@MainActor` final class DetachedFolderDragIconHostView (keeping their exact
symbols, access levels, MainActor annotation and methods like
makeDetachedIconWindow, installParentWindowObservers, syncDetachedIconFrame,
tearDownDetachedIcon), add necessary AppKit import(s), remove the duplicated
definitions from ContentView.swift so usages there continue to compile, and
ensure the new file is included in the target build settings; keep
DraggableFolderNSView reference unchanged (or move it too if it’s defined
nearby) so symbol resolution remains correct.
- Around line 2282-2283: DetachedFolderDragIcon currently replaces
DraggableFolderIcon but loses the tooltip and double-click-to-open-Finder
behavior; restore parity by re-applying the same UI modifiers/handlers
DraggableFolderIcon used. Wrap or modify the DetachedFolderDragIcon view
instance (the call in ContentView where DetachedFolderDragIcon(directory:
directory) is used) to add a .help(...) tooltip (e.g., the folder name/path) and
an onTapGesture(count: 2) handler that opens the folder in Finder (use
NSWorkspace.shared.activateFileViewerSelecting or call the existing
open-in-Finder helper if one exists), matching DraggableFolderIcon’s behavior.
Ensure the tooltip text and the double-click handler use the same directory
value as passed into DetachedFolderDragIcon.
🪄 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: eed196e2-b930-42ee-a045-25256c12a923

📥 Commits

Reviewing files that changed from the base of the PR and between 0b7118a and f7c4b81.

📒 Files selected for processing (2)
  • Sources/ContentView.swift
  • Sources/cmuxApp.swift

Comment thread Sources/ContentView.swift
Comment thread Sources/ContentView.swift Outdated

@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 `@Sources/DetachedFolderDragIcon.swift`:
- Line 242: The debug log in DetachedFolderDragIcon.swift leaks full directory
paths; update the cmuxDebugLog calls (e.g., the call that currently includes
directory in the message and the similar calls around the symbols at the other
two locations) to remove or redact the full path — log only non-sensitive
metadata such as a boolean presence, a safe identifier, or the directory's
lastPathComponent or a constant like "[REDACTED]" instead of the full directory
string; ensure the formatPoint(screenPoint), operation.rawValue, nowMovable and
windowOrigin remain unchanged in the message.
- Around line 258-263: In mouseDown(with event: NSEvent) on
DetachedFolderDragIcon, detect a Control-click
(event.modifierFlags.contains(.control)) at the top of the method and handle it
by invoking the contextual menu instead of continuing into drag logic—e.g., call
rightMouseDown(with: event) or NSMenu.popUpContextMenu(menuForEvent(event) ??
myContextMenu, with: event, for: self) and return; ensure this check occurs
before any drag-start or double-click handling so Control-click opens the
context menu reliably.
🪄 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: cbd8c656-5945-456f-b7d5-73b984a51741

📥 Commits

Reviewing files that changed from the base of the PR and between f7c4b81 and cbb1bf6.

📒 Files selected for processing (3)
  • GhosttyTabs.xcodeproj/project.pbxproj
  • Sources/ContentView.swift
  • Sources/DetachedFolderDragIcon.swift

Comment thread Sources/DetachedFolderDragIcon.swift Outdated
Comment thread Sources/DetachedFolderDragIcon.swift
@lederniermagicien

Copy link
Copy Markdown
Contributor Author

All good guys (and girls)?

@codex review
@coderabbitai review
@greptile-apps review
@cubic-dev-ai review

@cubic-dev-ai

cubic-dev-ai Bot commented May 9, 2026

Copy link
Copy Markdown

All good guys (and girls)?

@codex review
@coderabbitai review
@greptile-apps review
...

@lederniermagicien I have started the AI code review. It will take a few minutes to complete.

@chatgpt-codex-connector

Copy link
Copy Markdown

To use Codex here, create a Codex account and connect to github.

1 similar comment
@chatgpt-codex-connector

Copy link
Copy Markdown

To use Codex here, create a Codex account and connect to github.

@coderabbitai

coderabbitai Bot commented May 9, 2026

Copy link
Copy Markdown

@lederniermagicien: All good here! I'll take a look at the PR right away. 🐇

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@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 `@Sources/DetachedFolderDragIcon.swift`:
- Around line 354-357: The openComputer(_:) implementation and its comment are
inconsistent: NSWorkspace.shared.open(URL(fileURLWithPath: "/", isDirectory:
true)) opens the root volume, not the Finder "Computer" container. Either update
the comment to reflect that it opens the root filesystem, or replace the body of
openComputer(_:) with code that runs an AppleScript to open the Computer view
(for example, execute an AppleScript via NSAppleScript or a shell osascript
invocation that tells Finder to open the computer container / show the computer
window). Modify the function openComputer(_:) accordingly and ensure the comment
matches the chosen behavior.
🪄 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: bb2daf32-ca91-4157-b731-c96a66c8ec01

📥 Commits

Reviewing files that changed from the base of the PR and between cbb1bf6 and 9fc1d05.

📒 Files selected for processing (2)
  • Sources/ContentView.swift
  • Sources/DetachedFolderDragIcon.swift

Comment thread Sources/DetachedFolderDragIcon.swift
@vercel

vercel Bot commented May 10, 2026 •

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
cmux Ready Ready Preview, Comment May 10, 2026 0:50am

@vercel
vercel Bot temporarily deployed to Preview – cmux May 10, 2026 00:49 Inactive
@austinywang
austinywang merged commit ed70f7f into manaflow-ai:main May 10, 2026
20 checks passed

This branch was previously deployed

1 inactive deployment
Preview – cmux — e4bf4c7b Deployed May 10, 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.

2 participants