Skip to content

feat: drop dragged windows at the gap under the pointer on any display - #147

Closed
qeude wants to merge 2 commits into
mainfrom
feat/95-single-window-drag
Closed

qeude wants to merge 2 commits into
mainfrom
feat/95-single-window-drag

Conversation

@qeude

@qeude qeude commented Oct 19, 2026 •

Copy link
Copy Markdown
Owner

Closes #95

Stacked on #150. Review this diff against feat/window-proxy-ribbon.

Why

#150 already moves single windows by drag. Its overlay previews the drop and commits it on release. That covers extraction from a stack, column moves and stacking. Two gaps remain from the first version of this PR:

  • A drop on another display goes through the native cross-display move. The window lands where that move puts it, not at the gap under the pointer.
  • A pointer resting on a target boundary can flip the target back and forth. Each flip restarts the overlay springs.

What changed

  • MouseDropTarget gains .display(MonitorID, workspaceID:gap:). When the pointer is on another display, mouseDropTarget returns the gap under the pointer in that display's active workspace. The gap counts the columns whose center is left of the pointer.
  • applyMouseDrop handles .display. It revalidates the target first: the window is still tiled, the destination workspace is still active, and the gap is in range. Otherwise it changes nothing. The move reuses moveFocusedSelection, so transient windows and width conversion behave as for keyboard moves. The gap is anchored to the column after it, so transient columns that move along do not shift it.
  • mouseDropTarget takes the previous target. It keeps that target while the pointer is within 24 pt of it.
  • The daemon passes the previewed target and the monitor frames. The cross-display bookkeeping (active monitor, native focus monitor, scroll anchor) now runs after an overlay drop too, not only after a native one.
  • The daemon traces each preview change as mouse-drop-preview window=<id> target=<target>.
  • New verify-defi journey: .agents/skills/verify-defi/drag_check.py and features/window-drag.md. They drive real title-bar drags with cliclick, check column membership before, during and after, and read the previewed target from defi trace.

Scope

Tradeoffs

Blast Radius

  • Sources/DefiRuntime/MouseReordering.swift: the new target case, the hysteresis and the cross-display drop. Pure and covered by MouseReorderingTests.
  • Sources/DefiDaemon/DaemonDesktopSynchronization.swift: argument changes in the overlay drop branch, one trace line, and the cross-display bookkeeping moved after both drop branches. No new Accessibility writes. No per-frame main-thread work. The preview is still computed only when the target changes.

Verification

  • python3 script/verify.py local on the final head: passed (build, non-desktop Swift tests, workflow tests). One earlier staged run failed in FrameGeometryReadTests (a DefiMacOS timing test this branch does not touch). It passed alone, then the full staged run passed. MouseReorderingTests: 44 tests passed.
  • Desktop E2E on the final head (verify.py desktop, full DesktopE2ETests, Accessibility available, one display): 42 of 50 passed, 5 skipped, 3 failed. The failures were testManagedResizeAnimationConvergesWithAdaptiveSizeWrites, testOverviewNavigationRenderBudget and testPointerTransitionsUseWindowUnderPointerAndWarpDoesNotLoop. Other agents were sharing the desktop at the time. The first two passed when rerun alone. The pointer test skipped twice when rerun alone ("Focused on-screen managed window required"). It passed alone on the previous head of this branch, which had the same drop path. Session restoration passed.
  • Real native drags on the installed final build: drag_check.py passed all four cases. They are a short drag inside its own column, extracting the bottom of a 2-stack to the right, moving a single column left across two, and dragging out and back to the origin. Membership did not change while the button was held. The trace showed the expected previewed target for both real moves. One defi-daemon remained.
  • Cross-display drops: unit tests only. One display was connected.
  • Computer Use: not available in this session. Nobody has judged the overlay motion and borders during a drag by eye.

Note

Add cross-display tiled-window drag drop targets and 24-point preview hysteresis

  • Dragging a tiled window onto another monitor now resolves to an insertion gap in that monitor's active workspace, using mouseDisplayDropTarget in MouseReordering.swift
  • The drop moves only the dragged window, preserves the destination column width, tracks an anchor window to keep the requested gap stable, and rejects stale, same-monitor, floating, fullscreen, or invalid targets without state mutation
  • Preview targets now use 24-point hysteresis to stay stable when the pointer rests near a boundary; the daemon records preview transitions and applies drops with physical monitor geometry in DaemonDesktopSynchronization.swift
  • Adds unit tests for two-monitor scenarios (gap selection, transient column shifts, empty workspace gap zero, stale-workspace rejection) and an end-to-end drag verification harness in drag_check.py plus feature docs
  • Behavioral Change: pointers over the source window's own stack now route to native row reordering instead of column targets, and active-monitor/native-focus updates run after all drag reorder paths, not just overlay ones

Macroscope summarized 5b648da.

Comment thread Sources/DefiRuntime/MouseReordering.swift Outdated
Comment thread Sources/DefiRuntime/MouseReordering.swift Outdated
qeude added a commit that referenced this pull request Oct 19, 2026

@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.

All reported issues were addressed

Reply with feedback, questions, or to request a fix.

View guided diff | Turn on auto-fix | Re-trigger cubic

Comment thread Sources/DefiDaemon/DaemonDesktopSynchronization.swift Outdated
Comment thread Sources/DefiMacOS/DropPreviewOverlay.swift Outdated
Comment thread .agents/skills/verify-defi/drag_check.py
Comment thread .agents/skills/verify-defi/drag_check.py Outdated
Comment thread .agents/skills/verify-defi/drag_check.py Outdated
Comment thread Sources/DefiMacOS/MacOSPlatform+WindowBorderObservation.swift Outdated
Comment thread .agents/skills/verify-defi/drag_check.py Outdated

@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.

7 issues found across 15 files

Requires human review: Auto-approval blocked because this review re-detected 3 unresolved issues already reported by Cubic.

View guided diff | Turn on auto-fix | Re-trigger cubic

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name=".agents/skills/verify-defi/features/window-drag.md">

<violation number="1" location=".agents/skills/verify-defi/features/window-drag.md:21">
P3: This describes a stronger fixture guarantee than the harness provides. Say “empty workspace,” or make `drag_check.py` filter for `kind == 'trailing'` before documenting it as trailing.</violation>
</file>

<file name="Sources/DefiRuntime/MouseReordering.swift">

<violation number="1" location="Sources/DefiRuntime/MouseReordering.swift:477">
P2: A valid drop next to a native-fullscreen-only column loses the accent preview because the gap has no finite X coordinate. Keep hidden columns out of target-gap geometry or provide a visible fallback X so every committable target has a preview.</violation>
</file>

<file name="Sources/DefiMacOS/NativeFullscreenPlaceholder.swift">

<violation number="1" location="Sources/DefiMacOS/NativeFullscreenPlaceholder.swift:183">
P3: `appKitRect(for:)` duplicates the identical conversion still defined in `WindowBorderOverlayPanel`. Remove the private copy and use one shared helper so coordinate conversion changes cannot diverge.</violation>
</file>

<file name="Sources/DefiMacOS/DropPreviewOverlay.swift">

<violation number="1" location="Sources/DefiMacOS/DropPreviewOverlay.swift:47">
P2: The preview is ordered below the window being dragged, so the dragged window can cover the destination bar while it follows the pointer. Order this click-through panel in front of the dragged window, such as with `panel.orderFront(nil)`, so the insertion indicator remains visible.</violation>
</file>

<file name=".agents/skills/verify-defi/drag_check.py">

<violation number="1" location=".agents/skills/verify-defi/drag_check.py:43">
P2: `converge` reports success after its timeout whenever membership matches, even if native frames never settle; fail the check when settlement is still false so `results.json` cannot claim a false PASS.</violation>

<violation number="2" location=".agents/skills/verify-defi/drag_check.py:83">
P3: The probe can attach to another user's daemon because this `pgrep` is unscoped; restrict it to the current UID like `desktop_session.daemon_count()` does.</violation>

<violation number="3" location=".agents/skills/verify-defi/drag_check.py:86">
P2: A fixture-construction failure leaks any processes created earlier in this comprehension because `fixtures` is assigned only after it finishes; append each fixture before constructing the next one.</violation>
</file>

Comment thread Sources/DefiRuntime/MouseReordering.swift Outdated
Comment thread Sources/DefiMacOS/DropPreviewOverlay.swift Outdated
Comment thread .agents/skills/verify-defi/drag_check.py Outdated
Comment thread .agents/skills/verify-defi/drag_check.py Outdated
Comment thread .agents/skills/verify-defi/features/window-drag.md Outdated
Comment thread Sources/DefiMacOS/NativeFullscreenPlaceholder.swift Outdated
Comment thread .agents/skills/verify-defi/drag_check.py Outdated
qeude added a commit that referenced this pull request Oct 19, 2026
qeude added a commit that referenced this pull request Oct 19, 2026

@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.

All reported issues were addressed across 11 files (changes from recent commits).

Reply with feedback, questions, or to request a fix.

View guided diff | Turn on auto-fix | Re-trigger cubic

Comment thread Sources/DefiMacOS/DropPreviewOverlay.swift Outdated
@macroscopeapp

macroscopeapp Bot commented Oct 19, 2026 •

Copy link
Copy Markdown

Approvability

Verdict: Needs Human Approval

Macroscope's review found this PR requires human approval — This PR adds cross-display tiled-window drop placement and changes the existing drag preview/commit path, including workspace movement and focus-state updates. Because it is a substantial user-facing runtime capability rather than a small isolated adjustment, human review is warranted.

Notes:

  • All code in this push has already been reviewed. Approvability was decided on eligibility alone.

No code changes detected at 5b648da. Prior analysis still applies.

You can add or adjust custom eligibility rules. Learn more.

qeude added a commit that referenced this pull request Oct 19, 2026
@qeude
qeude force-pushed the feat/95-single-window-drag branch from 62623e4 to 7b225ea Compare October 22, 2026 23:12
@qeude
qeude changed the base branch from main to feat/window-proxy-ribbon October 22, 2026 23:12
@qeude
qeude force-pushed the feat/95-single-window-drag branch from 7b225ea to 3e3f510 Compare October 22, 2026 23:25
@qeude qeude changed the title feat: reorder individual windows with native drag previews feat: reorder individual windows at pointer-chosen drop targets Oct 22, 2026
@qeude
qeude force-pushed the feat/95-single-window-drag branch from 3e3f510 to 5e7e246 Compare October 22, 2026 23:39
@qeude qeude changed the title feat: reorder individual windows at pointer-chosen drop targets feat: drop dragged windows at the gap under the pointer on any display Oct 22, 2026
@qeude
qeude force-pushed the feat/95-single-window-drag branch from 5e7e246 to f8887e7 Compare October 22, 2026 23:46
Base automatically changed from feat/window-proxy-ribbon to main October 23, 2026 12:02
@qeude
qeude force-pushed the feat/95-single-window-drag branch from f8887e7 to 5467aa1 Compare October 23, 2026 12:24
qeude added 2 commits October 23, 2026 20:48
A drag whose pointer leaves its display now targets the gap under the
pointer in the other display's active workspace. Release inserts the
window there instead of where the native cross-display move puts it.
The drop revalidates the destination and changes nothing when that
workspace is no longer active or the gap is out of range.

A 24 pt hysteresis keeps a pointer resting on a target boundary from
flipping the target, so the overlay preview does not restart its
springs. The daemon traces each preview change for the drag harness.
@qeude

qeude commented Oct 24, 2026

Copy link
Copy Markdown
Owner Author

[Claude] Superseded by #153. It carries this PR's runtime change (drop at the gap under the pointer on any display, 24 pt hysteresis) and its drag harness, rebased onto the overlay from #152, and adds the overlay preview on the target display.

@qeude qeude closed this Oct 24, 2026
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.

[P1] Reorder individual windows with native drag previews

1 participant