这是indexloc提供的服务,不要输入任何密码
Skip to content

Conversation

@plyght
Copy link
Owner

@plyght plyght commented May 25, 2025

Summary by CodeRabbit

  • New Features
    • Enhanced macOS window management with comprehensive window enumeration across all running processes and stable window ID generation.
    • Added batch window movement and multi-window snapping with drag state tracking.
    • Introduced window snapping regions, drag-based snap, swap, and return-to-original actions.
    • Dynamic BSP layout updates with window insertion, removal, and spiral split orientation.
    • Added workspace detection and retrieval methods for current workspace.
    • Implemented macOS window drag notifications integration for drag start and end event handling.
  • Improvements
    • Increased refresh intervals and reduced window scan intervals for better performance.
    • Detailed debug logging for window filtering, workspace detection, and layout application.
    • Robust window caching and stable window ID generation to improve reliability.
    • Improved error handling and retry logic for window movements.
    • Programmatic move tracking to prevent snap conflicts during window management.
    • Style and formatting refinements in hotkey processing and other modules.
  • Bug Fixes
    • Fixed workspace and window attribute detection for accurate window management.

@sourcery-ai

This comment was marked as outdated.

@coderabbitai
Copy link

coderabbitai bot commented May 25, 2025

Walkthrough

The updates introduce comprehensive enhancements to macOS window and workspace management. New methods enable batch window movement, workspace detection, and improved window enumeration using process IDs and the Accessibility API. The Window struct gains an owner_pid field, and debug logging is expanded throughout. Workspace awareness and error handling are significantly improved across modules.

Changes

File(s) Change Summary
src/macos/accessibility.rs Rewrote window movement logic; added batch move, window enumeration, PID-based caching, and new helpers.
src/macos/cgwindow.rs Enhanced window parsing to extract owner_pid and workspace_id; improved filtering and logging.
src/macos/window_system.rs Added workspace detection, batch window move, reduced scan interval, and detailed debug logging.
src/window_manager.rs Added owner_pid to Window; improved workspace/layout logic, batch move with fallback, and logging.
src/hotkeys.rs Formatting and minor style improvements without functional changes.
src/layout.rs Added BSP tree methods for dynamic window insertion/removal, balanced splits, and rectangle updates.
src/lib.rs Added new public module declaration snap.
src/snap.rs Introduced SnapManager for window snapping with drag tracking, snap zones, and snap target detection.
src/bin/skew-cli.rs Removed unused import tokio.
src/config.rs Improved idiomatic check for empty key combination parts in hotkey validation.
src/focus.rs Simplified logic for cycling focus backward to previous window index.
src/ipc.rs Replaced get(0) with first() for argument extraction in IPC commands.
src/main.rs Enhanced logger initialization with debug level and timestamp formatting.
src/plugins.rs Added #[allow(dead_code)] to unused fields in LuaPlugin struct.
src/macos/mod.rs Added public module declaration window_notifications.
src/macos/window_notifications.rs Added macOS window drag notification observer integrating Cocoa notifications with async event handling.

Sequence Diagram(s)

sequenceDiagram
    participant Manager as WindowManager
    participant WS as MacOSWindowSystem
    participant AM as AccessibilityManager
    participant OS as macOS

    Manager->>WS: apply_layout(layouts, windows)
    WS->>AM: move_all_windows(layouts, windows)
    AM->>OS: Enumerate all windows by PID
    AM->>OS: Move each window via Accessibility API
    AM-->>WS: Result (success/failure)
    WS-->>Manager: Result (success/failure)
Loading
sequenceDiagram
    participant Manager as WindowManager
    participant WS as MacOSWindowSystem
    participant OS as macOS

    Manager->>WS: get_current_workspace()
    WS->>OS: CGSGetActiveSpace()
    OS-->>WS: workspace_id
    WS-->>Manager: workspace_id
Loading
sequenceDiagram
    participant Observer as WindowDragNotificationObserver
    participant macOS as Cocoa NotificationCenter
    participant EventLoop as Tokio mpsc::Sender

    macOS->>Observer: NSWindowWillMoveNotification
    Observer->>EventLoop: Send DragStarted event

    macOS->>Observer: NSWindowDidMoveNotification
    Observer->>EventLoop: Send DragEnded event
Loading

Poem

In the land of Apple, windows hop and glide,
With PIDs and spaces, they no longer hide.
Batch moves abound, layouts align,
Debug logs sparkle, everything’s fine!
A rabbit’s delight—workspace aware,
Now macOS windows dance with flair.
🐇✨

✨ Finishing Touches
  • 📝 Generate Docstrings

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🧹 Nitpick comments (5)
src/macos/cgwindow.rs (1)

111-114: Consider using -1 as the default PID value instead of 0.

Since 0 is a valid PID on Unix systems (representing the kernel), using it as a default for "unknown" could be misleading. Consider using -1 to clearly indicate an unknown or invalid PID.

// Extract owner PID
let owner_pid = Self::get_number_from_dict(dict, "kCGWindowOwnerPID")
-    .unwrap_or(0.0) as i32;
+    .unwrap_or(-1.0) as i32;
src/macos/window_system.rs (1)

125-125: Consider the performance impact of the reduced monitoring interval.

Reducing the interval from 500ms to 200ms means window enumeration happens 5 times per second instead of 2. Since CGWindowListCopyWindowInfo is a relatively expensive operation, this could impact:

  • CPU usage
  • Battery life on laptops
  • Overall system responsiveness with many windows

Consider making this interval configurable or implementing a more efficient change detection mechanism.

src/window_manager.rs (1)

406-408: Redundant workspace fetch.

The current workspace is already updated in refresh_windows() which is called before apply_layout(). This additional fetch seems redundant.

-// Get current macOS workspace/Space
-let current_workspace = self.macos.get_current_workspace().await?;
+// Use the workspace already updated in refresh_windows
+let current_workspace = self.current_workspace;
src/macos/accessibility.rs (2)

400-411: Consider removing or deprecating redundant methods.

These methods are marked as redundant/legacy but remain in the codebase:

  • try_get_windows_from_other_apps - explicitly says it's redundant
  • get_windows_for_app_by_name - marked as legacy, returns empty Vec

Consider either:

  1. Removing these methods entirely if no other code depends on them
  2. Adding #[deprecated] attributes with migration guidance
  3. Adding clear documentation about their obsolete status

This will help prevent confusion for future maintainers.


545-603: Code duplication between move_window and move_window_element.

This method duplicates the AXValue creation and positioning logic from the move_window method (lines 175-225). While the detailed error handling and logging are excellent, this creates maintenance overhead.

Consider refactoring both methods to use a shared helper function for the AXValue creation and attribute setting:

fn set_window_position_and_size(&self, element: AXUIElementRef, rect: Rect) -> Result<()> {
    // Common AXValue creation and setting logic
}

This would eliminate duplication while maintaining the current functionality.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 299b7ce and c2e017b.

📒 Files selected for processing (4)
  • src/macos/accessibility.rs (4 hunks)
  • src/macos/cgwindow.rs (2 hunks)
  • src/macos/window_system.rs (4 hunks)
  • src/window_manager.rs (6 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (2)
src/macos/window_system.rs (3)
src/macos/cgwindow.rs (2)
  • new (305-311)
  • get_all_windows (65-97)
src/macos/accessibility.rs (5)
  • new (79-98)
  • move_all_windows (276-326)
  • std (374-374)
  • std (381-381)
  • get_focused_window (100-145)
src/window_manager.rs (1)
  • new (74-99)
src/window_manager.rs (1)
src/layout.rs (1)
  • get_current_layout (420-422)
🔇 Additional comments (21)
src/macos/cgwindow.rs (11)

129-132: LGTM!

The workspace ID extraction is implemented correctly with a reasonable default value of 1.


136-138: Good debug logging additions!

The enhanced debug messages provide valuable context for understanding window filtering decisions.

Also applies to: 143-145


148-153: LGTM! Good additions to the system apps exclusion list.

Including "Menubar", "Menu Bar", and "SystemPreferences" ensures these system UI elements won't be incorrectly tiled.


159-163: Good improvement to system panel filtering logic!

The more specific condition (empty title AND system-related owner) reduces false positives while still filtering out system panels effectively.


111-113: LGTM! Owner PID extraction implemented correctly.

The extraction of owner process ID from the window dictionary follows the established pattern and provides valuable metadata for window management operations.


129-131: LGTM! Workspace ID detection properly implemented.

The workspace ID extraction from the window dictionary replaces the previous hardcoded default and enables proper workspace-aware window management. The fallback to workspace 1 is reasonable for cases where the workspace information is unavailable.


136-137: Enhanced debug logging improves troubleshooting capabilities.

The added debug logging for filtered windows provides valuable insights into why specific windows are excluded from management, which will help with debugging and fine-tuning the filtering logic.

Also applies to: 143-144


149-153: Expanded system application filtering improves tiling behavior.

The addition of "Menubar", "Menu Bar", and "SystemPreferences" to the never-tile applications list is a good enhancement that prevents inappropriate tiling attempts on system UI elements.


160-162: Refined system window filtering with improved logging.

The updated filtering logic for system panels is more precise, checking for empty titles combined with system-related owners or very short owner names. The debug logging helps track which windows are being filtered out.


165-165: Comprehensive success logging aids in debugging.

The debug log for successfully parsed windows provides visibility into the window detection process, including the newly captured workspace information.


171-171: Window struct properly updated with new metadata fields.

The Window struct construction correctly incorporates the newly extracted owner_pid and workspace_id fields, enabling enhanced window management capabilities throughout the application.

Also applies to: 175-175

src/macos/window_system.rs (2)

133-137: Good debug logging additions!

The enhanced logging provides valuable insights into window discovery and workspace tracking.

Also applies to: 158-158


11-15:

❓ Verification inconclusive

⚠️ Using undocumented private macOS APIs - verify compatibility and add error handling.

The CGSGetActiveSpace and CGSMainConnectionID functions are private CoreGraphics APIs that are not officially documented or supported by Apple. This raises several concerns:

  1. These APIs could change or be removed in future macOS versions
  2. The function signatures might not be correct
  3. There's no verification that these symbols exist at runtime

Consider adding runtime symbol verification or implementing a fallback mechanism if these functions are not available.

Run the following script to check if these symbols are exported in the CoreGraphics framework:


🏁 Script executed:

#!/bin/bash
# Check if the CGS functions are exported in CoreGraphics framework
nm -g /System/Library/Frameworks/CoreGraphics.framework/CoreGraphics | grep -E "CGSGetActiveSpace|CGSMainConnectionID" || echo "Symbols not found in CoreGraphics framework"

# Alternative: Check in SkyLight framework where these might actually be located
if [ -f /System/Library/PrivateFrameworks/SkyLight.framework/SkyLight ]; then
    echo "Checking SkyLight framework..."
    nm -g /System/Library/PrivateFrameworks/SkyLight.framework/SkyLight | grep -E "CGSGetActiveSpace|CGSMainConnectionID" || echo "Symbols not found in SkyLight framework"
fi

Length of output: 385


⚠️ Private macOS APIs: verify existence and add runtime checks

The extern "C" declarations for CGSGetActiveSpace and CGSMainConnectionID reference undocumented CoreGraphics/SkyLight functions that Apple can change or remove at any time. This may lead to signature mismatches or missing symbols at runtime, causing crashes.

We attempted to locate these symbols using nm in the sandbox but couldn’t (no nm available). Please verify on a macOS host:

# Preferred: use otool to inspect exported symbols
if [ -f "/System/Library/PrivateFrameworks/SkyLight.framework/SkyLight" ]; then
  otool -ov /System/Library/PrivateFrameworks/SkyLight.framework/SkyLight \
    | grep -E "CGSGetActiveSpace|CGSMainConnectionID" \
    || echo "Symbols not found in SkyLight"
else
  echo "SkyLight framework not found"
fi

# Fallback: use strings + grep
strings /System/Library/PrivateFrameworks/SkyLight.framework/SkyLight \
  | grep -E "CGSGetActiveSpace|CGSMainConnectionID" \
  || echo "Symbols not found"

Action items:

  • Confirm these symbols exist in the targeted macOS version(s).
  • Load them at runtime (e.g., via libloading) and wrap in Option<unsafe extern "C" fn…> so you can fall back gracefully if they’re missing.
  • Document compatibility and any fallbacks for unsupported macOS releases.

Location: src/macos/window_system.rs (lines 11–15)

src/window_manager.rs (5)

18-18: LGTM!

The owner_pid field addition is consistent with the window metadata extraction in cgwindow.rs.


109-114: Good addition for initial window layout!

Applying layout to existing windows on startup ensures a consistent state when the window manager starts.


373-385: Good error handling for workspace detection!

The implementation gracefully handles workspace detection failures by logging a warning and continuing with the last known workspace.


409-415: Address the unreliable workspace detection issue.

The comment indicates workspace filtering is disabled due to unreliable detection. This is a significant limitation that should be addressed. Consider:

  1. Investigating why workspace detection is unreliable
  2. Implementing alternative workspace detection methods
  3. Adding a configuration option to enable/disable workspace filtering

436-476: Excellent implementation of batch window movement with fallback!

The implementation:

  • Attempts efficient batch movement first
  • Falls back to individual moves with retries on failure
  • Provides detailed logging for debugging
  • Updates window state consistently

The retry logic with 3 attempts and 50ms delays is well-designed for handling transient failures.

src/macos/accessibility.rs (3)

2-57: LGTM! Well-structured FFI declarations.

The new imports and external C function declarations are properly structured for the enhanced window management functionality. The CGPoint and CGSize structs correctly match the Core Foundation ABI.


175-225: Enhanced window movement implementation looks good.

The rewritten method properly uses AXValue creation for direct window manipulation. Memory management is handled correctly with CFRelease calls for the created values.

However, verify that find_window_element can reliably locate the correct window element, as the current implementation appears simplified.


364-469: Excellent process enumeration implementation.

The PID enumeration with dynamic buffer resizing is well-handled:

  • Proper loop with buffer doubling when needed
  • Correct filtering of invalid PIDs (≤ 0)
  • Good error handling for system call failures

The window enumeration by PID and tileability checking are also well-implemented with proper memory management.

sourcery-ai[bot]

This comment was marked as outdated.

Repository owner deleted a comment from cubic-dev-ai bot May 25, 2025
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (2)
src/window_manager.rs (1)

115-118: Refresh interval rationale is well-documented.

The comment clearly explains why the refresh interval differs from the window monitoring interval, addressing the previous concern about inconsistency.

src/macos/accessibility.rs (1)

267-291: ⚠️ Potential issue

Window-to-layout mapping issue persists.

The current implementation maps windows to layouts by index, which assumes the order of window elements matches the order of windows in the provided array. This can lead to incorrect window positioning.

for window in windows {
    if element_index < all_window_elements.len() {
        element_to_window.insert(all_window_elements[element_index], window.id);
        element_index += 1;
    }
}

A more reliable approach would be to match windows based on properties like title or position rather than relying on order.

🧹 Nitpick comments (2)
src/window_manager.rs (1)

419-422: Consider tracking workspace filtering as a future enhancement.

The decision to disable workspace filtering due to unreliable detection is pragmatic. However, this might lead to unexpected behavior when users have windows on multiple workspaces.

Would you like me to create an issue to track implementing reliable workspace detection as a future enhancement?

src/macos/accessibility.rs (1)

441-457: Consider adding a removal timeline for deprecated methods.

The deprecated methods are properly marked but lack a removal timeline. Consider adding a comment indicating when these will be removed (e.g., "Will be removed in v2.0").

-    #[deprecated(note = "Use enumerate_all_accessible_applications instead")]
+    #[deprecated(since = "1.0.0", note = "Use enumerate_all_accessible_applications instead. Will be removed in v2.0")]
     fn try_get_windows_from_other_apps(
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c2e017b and 35d170a.

📒 Files selected for processing (6)
  • src/hotkeys.rs (7 hunks)
  • src/layout.rs (1 hunks)
  • src/macos/accessibility.rs (5 hunks)
  • src/macos/cgwindow.rs (7 hunks)
  • src/macos/window_system.rs (4 hunks)
  • src/window_manager.rs (10 hunks)
✅ Files skipped from review due to trivial changes (2)
  • src/layout.rs
  • src/hotkeys.rs
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/macos/window_system.rs
  • src/macos/cgwindow.rs
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/window_manager.rs (2)
src/layout.rs (1)
  • get_current_layout (420-422)
src/focus.rs (2)
  • get_focused_window_id (312-317)
  • find_window_in_direction (205-268)
🔇 Additional comments (8)
src/window_manager.rs (4)

18-18: LGTM! Good addition for PID-based window management.

The owner_pid field enables better window identification and correlation with the Accessibility API's PID-based enumeration.


109-114: LGTM! Excellent startup initialization.

Applying the initial layout on startup ensures existing windows are properly tiled immediately, improving the user experience.


377-392: LGTM! Robust workspace detection with proper error handling.

The implementation gracefully handles workspace detection failures with warnings while continuing operation, preventing crashes in environments where workspace detection might be unreliable.


450-513: LGTM! Well-designed batch window movement with robust fallback.

The implementation provides optimal performance with batch operations while ensuring reliability through:

  • Fallback to individual window moves on batch failure
  • Retry logic (3 attempts) for transient failures
  • Proper state synchronization after successful moves
  • Comprehensive debug logging for troubleshooting
src/macos/accessibility.rs (4)

181-203: LGTM! Proper window movement with resource management.

The rewritten method correctly:

  • Uses find_window_element to locate windows by ID
  • Properly releases the retained element after use
  • Provides clear debug logging for troubleshooting

205-228: LGTM! Properly implements window finding by ID.

This implementation correctly addresses the previous concern by:

  • Actually using the window_id parameter to find specific windows
  • Checking cache and refreshing as needed
  • Properly retaining elements before returning them

This fixes the critical issue where the method previously ignored the window_id parameter.


718-736: LGTM! Proper memory management for cached window elements.

The implementation correctly:

  • Retains window elements before caching (line 727)
  • Releases them during cache refresh and in the Drop implementation
  • Uses a stable window ID based on PID and index

This properly addresses the previous use-after-free concern.


492-503: LGTM! Correct element retention preventing dangling pointers.

The code properly retains window elements (line 498) before they're returned, ensuring they remain valid after the windows array is released. This prevents the use-after-free issue identified in previous reviews.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 35d170a and 0a93388.

📒 Files selected for processing (3)
  • src/macos/accessibility.rs (5 hunks)
  • src/macos/window_system.rs (4 hunks)
  • src/window_manager.rs (10 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/macos/window_system.rs
🧰 Additional context used
🧬 Code Graph Analysis (2)
src/window_manager.rs (2)
src/layout.rs (1)
  • get_current_layout (420-422)
src/focus.rs (2)
  • get_focused_window_id (312-317)
  • find_window_in_direction (205-268)
src/macos/accessibility.rs (4)
src/macos/cgwindow.rs (4)
  • CFRelease (27-27)
  • new (319-325)
  • CFArrayGetCount (25-25)
  • CFArrayGetValueAtIndex (26-26)
src/macos/window_system.rs (3)
  • move_window (300-302)
  • move_all_windows (304-310)
  • new (32-41)
src/ipc.rs (3)
  • move_window (343-355)
  • new (31-36)
  • new (290-292)
src/lib.rs (1)
  • new (27-34)
🔇 Additional comments (13)
src/window_manager.rs (5)

18-18: LGTM: Well-integrated owner_pid field addition.

The addition of the owner_pid field to the Window struct properly aligns with the accessibility manager's PID-based window identification strategy implemented in src/macos/accessibility.rs. This enhancement supports the improved window enumeration and batch operations.


109-113: Excellent startup enhancement for consistent window state.

Applying the initial layout to existing windows on startup ensures that the window manager maintains a consistent state from the beginning, rather than leaving pre-existing windows unmanaged until the next event.


115-121: Well-documented timer interval adjustment addresses past concerns.

The detailed comments explaining the rationale for different refresh intervals (1000ms for refresh vs 200ms for monitoring) and the TODO for making them configurable demonstrate good engineering practices. This addresses the inconsistency noted in past reviews.


380-394: Robust workspace detection with proper error handling.

The workspace detection logic properly handles both success and failure cases, logging workspace changes and gracefully handling errors without breaking the refresh flow.


453-515: Excellent batch operation implementation with robust fallback strategy.

The implementation properly:

  • Attempts batch window movement first for efficiency
  • Falls back to individual moves with retry logic (up to 3 attempts)
  • Updates internal window state in both success and failure cases
  • Provides comprehensive logging for debugging

The retry mechanism with 50ms delays and the proper state synchronization demonstrate good error handling practices.

src/macos/accessibility.rs (8)

139-145: Significant improvement in window ID generation.

The new hashing approach using multiple hash operations with different constants (0x9e3779b9, 0x85ebca6b) and combining PID with element pointer provides much better distribution and collision resistance compared to the previous simple pointer casting.


184-206: Excellent rewrite of move_window with proper accessibility API usage.

The new implementation properly:

  • Searches for the window element using find_window_element
  • Uses the dedicated set_window_rect helper for consistent positioning
  • Manages Core Foundation references with proper CFRelease
  • Provides clear error messaging when windows cannot be found

233-303: Well-designed batch window movement with precise window matching.

The implementation addresses the previous concern about window-to-layout mapping by:

  • Building a HashMap for O(1) layout lookups by WindowId
  • Grouping windows by PID for efficient enumeration
  • Matching windows by PID and index within the PID group
  • Properly managing Core Foundation references throughout the process

This ensures accurate window-to-layout correspondence.


305-360: Robust window positioning using proper AXValue creation.

The set_window_rect method properly:

  • Creates AXValue objects for position and size using the correct Core Foundation types
  • Handles null value creation failures gracefully
  • Cleans up Core Foundation resources in all code paths
  • Provides detailed error information for debugging

407-441: Comprehensive process enumeration with proper buffer management.

The get_all_running_pids implementation correctly:

  • Uses dynamic buffer sizing to handle varying numbers of processes
  • Properly handles the proc_listpids return value semantics
  • Filters out invalid PIDs (≤ 0)
  • Implements a growth strategy for insufficient buffer sizes

731-752: Enhanced window ID generation with collision detection.

The improved window ID generation now:

  • Incorporates PID, array index, and element pointer for better uniqueness
  • Detects and warns about ID collisions
  • Implements a timestamp-based fallback for collision resolution

This addresses the stability concerns raised in past reviews.


659-667: Critical improvement: Proper Core Foundation resource management.

The cache refresh now properly releases all cached elements before clearing the cache, preventing resource leaks. This addresses the memory management concerns identified in previous reviews.


728-730:

✅ Verification successful

Verify CFRetain usage aligns with Core Foundation memory management.

The code retains window elements from CFArrayGetValueAtIndex before caching them, which is correct since the array will be released. However, ensure this doesn't create over-retention when combined with the existing cache cleanup logic.

Let me verify the Core Foundation reference counting patterns are consistent:


🏁 Script executed:

#!/bin/bash
# Description: Check CFRetain/CFRelease patterns in accessibility.rs

echo "=== CFRetain calls ==="
rg "CFRetain" src/macos/accessibility.rs -A 2 -B 2

echo "=== CFRelease calls ==="
rg "CFRelease" src/macos/accessibility.rs -A 2 -B 2

echo "=== Window cache insertions ==="
rg "window_cache\.insert" src/macos/accessibility.rs -A 2 -B 2

Length of output: 4478


No over-retention: CFRetain/CFRelease usage is balanced

I’ve verified that every call to CFRetain is paired with a corresponding CFRelease:

  • In process_windows_array, elements retained for tiling are released in the loop over app_window_elements.
  • Cache updates release old entries before inserting new ones, and the Drop implementation releases all retained window elements (and system_element) when the manager is torn down.

No further changes needed here.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

♻️ Duplicate comments (2)
src/window_manager.rs (2)

123-129: Good documentation of the interval differences.

The comment clearly explains the rationale for different refresh intervals and provides a clear TODO for making them configurable. This appropriately addresses the concern about inconsistent intervals.


437-440: Workspace filtering is still disabled.

This issue about disabled workspace filtering due to unreliable detection was previously raised and could lead to unexpected behavior where windows from inactive workspaces get repositioned.

🧹 Nitpick comments (4)
src/window_manager.rs (3)

87-90: Consider making the snap threshold configurable.

The snap threshold is hardcoded to 50.0 pixels. Consider adding it to the configuration to allow users to customize snap sensitivity.

-        let snap_manager = SnapManager::new(screen_rect, 50.0); // 50px snap threshold
+        let snap_threshold = config.general.snap_threshold.unwrap_or(50.0);
+        let snap_manager = SnapManager::new(screen_rect, snap_threshold);

And add to your config structure:

pub struct GeneralConfig {
    // ... existing fields ...
    pub snap_threshold: Option<f64>,
}

536-567: Well-implemented snap drag detection.

The movement threshold (20px) and delayed check (800ms) provide good UX. However, consider handling the case where multiple rapid movements occur.

Consider canceling previous scheduled checks when a new drag starts to avoid queuing multiple snap checks:

// Add to WindowDragState:
drag_check_handle: Option<tokio::task::JoinHandle<()>>,

// In handle_window_move_snap, cancel previous check:
if let Some(handle) = previous_drag_state.drag_check_handle.take() {
    handle.abort();
}

605-620: Placeholder method for future enhancement.

This method is clearly marked as a placeholder. When implementing, consider tracking manual positions separately from layout-managed positions to preserve user adjustments.

Would you like me to help design the logic for updating the BSP tree based on manual window movements?

src/layout.rs (1)

289-356: Well-implemented BSP tree synchronization!

The rewritten compute_bsp_layout properly maintains consistency between the BSP tree and the current window set. The logic handles all cases: removing stale windows, adding new ones, and recreating the tree when needed.

Consider renaming collect_all_windows_static to collect_all_window_ids for clarity:

-    fn collect_all_windows_static(node: &BSPNode, windows: &mut Vec<WindowId>) {
+    fn collect_all_window_ids(node: &BSPNode, window_ids: &mut Vec<WindowId>) {
         if let Some(window_id) = node.window_id {
-            windows.push(window_id);
+            window_ids.push(window_id);
         }
         if let Some(ref left) = node.left {
-            Self::collect_all_windows_static(left, windows);
+            Self::collect_all_window_ids(left, window_ids);
         }
         if let Some(ref right) = node.right {
-            Self::collect_all_windows_static(right, windows);
+            Self::collect_all_window_ids(right, window_ids);
         }
     }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4829697 and 602e4be.

📒 Files selected for processing (5)
  • src/layout.rs (5 hunks)
  • src/lib.rs (1 hunks)
  • src/macos/accessibility.rs (5 hunks)
  • src/snap.rs (1 hunks)
  • src/window_manager.rs (15 hunks)
✅ Files skipped from review due to trivial changes (1)
  • src/lib.rs
🔇 Additional comments (16)
src/snap.rs (4)

1-32: LGTM!

The SnapRegion enum and its implementation are well-structured for representing the nine standard snap zones.


34-54: LGTM!

The struct definitions are well-designed with clear separation of concerns between detection bounds and snap target rectangles.


275-329: LGTM!

The drag tracking implementation uses conservative thresholds (500ms duration, 100px distance) which helps prevent accidental snapping. The double-check for position changes is a good safeguard.


331-393: LGTM!

The snap target finding logic correctly prioritizes corner zones over edges and center, which provides intuitive snapping behavior. The utility methods are well-implemented.

src/window_manager.rs (1)

468-531: Excellent implementation of batch window movement with fallback.

The implementation properly attempts batch movement first and falls back to individual moves with retry logic. The error handling and logging are comprehensive.

src/layout.rs (2)

80-204: Excellent BSP tree enhancements!

The implementation includes:

  • Proper recursive rectangle updates maintaining tree consistency
  • Clever spiral layout using alternating split directions by depth
  • Correct tree collapsing logic when removing windows
  • Efficient window containment checking

The alternating split pattern creates an intuitive i3/sway-like layout behavior.


246-268: LGTM!

The window management methods properly handle BSP tree lifecycle, including creation when needed and cleanup when empty.

src/macos/accessibility.rs (9)

139-145: Improved window ID generation addresses previous concerns.

The new hash-based approach using FNV-like multiplication and combining PID with element pointer is a significant improvement over the previous raw pointer casting. This reduces collision probability while maintaining reasonable performance.

However, as noted in previous reviews, this still has inherent limitations with pointer reuse and AX element lifecycle. Consider the web search recommendation about using _AXUIElementGetWindow for more stable CGWindowID extraction when feasible.


184-208: Well-structured window movement with proper error handling.

The refactored move_window method properly separates concerns by delegating to find_window_element and set_window_rect. The graceful handling of missing windows with debug logging instead of hard failures is appropriate for window management scenarios.


210-233: Proper resource management in window element lookup.

The find_window_element method correctly handles CFRetain/CFRelease for returned elements and implements a reasonable fallback strategy with cache refresh. This addresses the previous review concern about ignoring the window_id parameter.


235-305: Batch window movement logic addresses previous mapping concerns.

The new implementation correctly maintains window ID to layout mapping using a HashMap lookup rather than sequential application. The PID-based grouping and index-within-PID matching strategy is more robust than the previous approach.

The resource cleanup at lines 292-296 properly releases retained window elements to prevent leaks.


307-362: Robust window positioning with proper AXValue usage.

The set_window_rect method correctly uses AXValue creation for Core Graphics types and implements proper resource cleanup. The error handling captures both position and size operation results.


463-524: Comprehensive window enumeration with proper filtering.

The get_windows_for_app_by_pid method implements good practices:

  • Early return for invalid PIDs
  • Proper resource management with CFRetain for elements
  • Window tileability validation
  • Comprehensive cleanup

526-556: Window tileability check ensures movement compatibility.

The is_window_tileable method properly validates that windows have the required position and size attributes before attempting to manage them. The resource cleanup for temporary values is handled correctly.


762-773: Drop implementation properly releases cached resources.

The Drop implementation correctly iterates through cached window elements and releases them, preventing resource leaks when the AccessibilityManager is destroyed.


489-489:

❓ Verification inconclusive

Verify Core Foundation array API usage.

The code uses core_foundation::array::CFArrayRef and related functions, but I need to verify this is the correct way to access these APIs in the core_foundation crate.

Also applies to: 724-724


🏁 Script executed:

#!/bin/bash
# Check if the core_foundation crate exports array functions as used
rg -A 3 "CFArrayRef\|CFArrayGetCount\|CFArrayGetValueAtIndex" --type rust

Length of output: 75


Verify Core Foundation array API usage

Please confirm that the core_foundation crate you depend on exposes the raw CFArray APIs you’re casting to—namely CFArrayRef, CFArrayGetCount, and CFArrayGetValueAtIndex. A codebase search won’t surface definitions for external crates, so double-check against the crate’s documentation or your local core-foundation source.

Locations to verify:

  • src/macos/accessibility.rs: line 489
  • src/macos/accessibility.rs: line 724

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/layout.rs (1)

319-387: Complex synchronization logic needs careful review.

The enhanced compute_bsp_layout method includes sophisticated tree synchronization, but there are potential performance and correctness concerns:

  1. Performance: The current approach collects all windows, then removes missing ones, then adds new ones. This could be inefficient for large window sets.

  2. Edge case: If collect_all_windows_static is called on an empty tree, it might not handle all edge cases correctly.

Consider optimizing the synchronization approach:

-        // Sync the BSP tree with current windows
-        if let Some(ref mut root) = self.bsp_root {
-            // Remove windows that are no longer present
-            let mut to_remove = Vec::new();
-            Self::collect_all_windows_static(root, &mut to_remove);
-            for window_id in to_remove {
-                if !window_ids.contains(&window_id) {
-                    root.remove_window(window_id);
-                }
-            }
+        // More efficient: directly compare and sync
+        if let Some(ref mut root) = self.bsp_root {
+            // Remove windows that are no longer present
+            let mut current_windows = Vec::new();
+            Self::collect_all_windows_static(root, &mut current_windows);
+            for &window_id in &current_windows {
+                if !window_ids.contains(&window_id) {
+                    root.remove_window(window_id);
+                }
+            }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 602e4be and 9d754d4.

📒 Files selected for processing (13)
  • src/bin/skew-cli.rs (0 hunks)
  • src/config.rs (1 hunks)
  • src/focus.rs (1 hunks)
  • src/hotkeys.rs (8 hunks)
  • src/ipc.rs (2 hunks)
  • src/layout.rs (8 hunks)
  • src/macos/accessibility.rs (9 hunks)
  • src/macos/cgwindow.rs (8 hunks)
  • src/macos/window_system.rs (7 hunks)
  • src/main.rs (1 hunks)
  • src/plugins.rs (1 hunks)
  • src/snap.rs (1 hunks)
  • src/window_manager.rs (15 hunks)
💤 Files with no reviewable changes (1)
  • src/bin/skew-cli.rs
✅ Files skipped from review due to trivial changes (4)
  • src/main.rs
  • src/plugins.rs
  • src/config.rs
  • src/ipc.rs
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/hotkeys.rs
  • src/macos/cgwindow.rs
  • src/macos/window_system.rs
  • src/window_manager.rs
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/snap.rs (2)
src/lib.rs (1)
  • new (28-35)
src/window_manager.rs (1)
  • new (77-107)
🔇 Additional comments (15)
src/focus.rs (1)

341-344: Clean refactoring of the backward cycling logic!

The simplified approach with explicit edge case handling is more readable and maintainable than modulo arithmetic.

src/macos/accessibility.rs (3)

177-201: Excellent refactoring of the move_window method!

The implementation properly manages element lifecycles with retain/release and gracefully handles missing windows without failing the operation.


228-298: Well-designed batch window movement with proper window-to-layout mapping!

The implementation correctly addresses the window matching concern by:

  • Grouping windows by PID
  • Matching window elements to layout rectangles using PID-specific indexing
  • Properly managing element lifecycles with retain/release

This approach ensures each window gets its intended layout position.


300-357: Robust implementation of window rect setting!

The method properly creates and manages AXValue objects with correct cleanup on all paths, including error cases.

src/snap.rs (1)

295-332: Excellent use of conservative thresholds for snap detection!

The implementation prevents unintended snapping with:

  • 500ms minimum drag duration
  • 100px minimum drag distance
  • Additional 10px threshold to avoid redundant moves

This ensures snapping only occurs for clearly intentional window movements.

src/layout.rs (10)

5-5: Good addition for debugging support.

Adding the Debug derive trait will improve debugging capabilities for the LayoutType enum.


80-115: Solid rectangle update implementation.

The update_rect and update_child_rects methods correctly handle rectangle calculations for BSP tree nodes. The logic properly splits rectangles horizontally and vertically based on the split ratio.


159-171: Efficient window counting implementation.

The recursive window counting logic is correct and handles both leaf and container nodes appropriately.


221-233: Clean window containment check.

The contains_window method correctly traverses the tree to check for window existence.


276-298: Well-structured window management methods.

The add_window and remove_window methods for LayoutManager properly coordinate with the BSP tree, including cleanup when the tree becomes empty.


443-443: Correct float layout implementation.

Using w.rect directly for floating windows is the correct approach, as floating windows should maintain their current positions.


460-460: Good use of div_ceil for grid calculations.

The div_ceil method provides cleaner ceiling division logic for grid row calculations.


605-605: Excellent bounds checking for split ratio.

Using clamp(0.1, 0.9) ensures the split ratio stays within reasonable bounds, preventing edge cases where windows become too small.


117-157:

❌ Incorrect review comment

Review the spiral layout insertion logic.

The insertion logic implements alternating splits based on depth, but there may be a conceptual issue with the spiral pattern.

let should_split_horizontal = depth % 2 == 0;

This creates alternating splits (vertical, then horizontal), but the comment mentions "new windows go right, then down" which suggests a different pattern. Consider verifying that this depth-based alternating approach produces the intended spiral effect.


🏁 Script executed:

#!/bin/bash
# Description: Check if there are any tests or documentation for spiral layout behavior
# to verify the intended insertion pattern

rg -A 10 -B 5 "spiral.*layout|spiral.*pattern" --type rust

Length of output: 3572


🏁 Script executed:

#!/bin/bash
# Extract the full implementation of compute_spiral_layout for review
rg -A200 "fn compute_spiral_layout" --type rust

Length of output: 7346


Ignore insertion logic for Spiral layout — it isn’t used by compute_spiral_layout.

The insert_window_with_depth routine builds the BSP tree for BSP layouts only. The Spiral layout’s compute_spiral_layout function ignores that tree entirely, laying out windows directly from the windows slice and split_ratio. The depth-based split logic has no effect on Spiral, so this review comment can be dismissed.

Likely an incorrect or invalid review comment.


173-219:

❓ Verification inconclusive

Verify the newer Rust syntax compatibility.

The tree collapsing logic uses some newer Rust methods that may require verification:

.is_some_and(|left| left.remove_window(window_id))
.is_none_or(|n| n.count_windows() == 0)

These methods were introduced in recent Rust versions. Ensure the project's MSRV (Minimum Supported Rust Version) supports these methods.


🏁 Script executed:

#!/bin/bash
# Description: Check the project's Rust version requirements and verify method availability

# Check Cargo.toml for rust-version or edition
fd Cargo.toml --exec cat {} \; | grep -E "rust-version|edition"

# Check if is_some_and and is_none_or are available in the target Rust version
# is_some_and was stabilized in Rust 1.70.0
# is_none_or was stabilized in Rust 1.82.0
echo "Note: is_some_and requires Rust 1.70.0+, is_none_or requires Rust 1.82.0+"

Length of output: 242


Verify the project’s MSRV before using is_some_and and is_none_or

The code on src/layout.rs lines 173–219 uses:

.is_some_and(|left| left.remove_window(window_id))   // stabilised in Rust 1.70.0
.is_none_or(|n| n.count_windows() == 0)              // stabilised in Rust 1.82.0

Cargo.toml currently only declares:

edition = "2021"

Please ensure the Minimum Supported Rust Version (MSRV) is at least 1.82.0. You can either:

  • Add a rust-version = "1.82.0" field to Cargo.toml
  • Or, if you must support older toolchains, replace these with equivalent expressions, e.g.:
    .as_mut().map_or(false, |left| left.remove_window(window_id))
    .as_ref().map_or(true, |n| n.count_windows() == 0)

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

♻️ Duplicate comments (6)
src/snap.rs (2)

52-53: The snap_threshold parameter remains unused despite past feedback.

The snap_threshold field is stored but never used in the snapping logic, which still uses hardcoded values (100ms, 20px) in end_window_drag. This misleads users who pass this parameter expecting it to control snapping behavior.


83-283: The update_snap_zones method is still overly verbose and repetitive.

This method contains extensive code duplication for creating different snap zones. The repetitive patterns for bounds and snap rectangle creation make the code harder to maintain and modify.

src/macos/accessibility.rs (4)

2-2: Missing Core Foundation Array imports cause compilation errors.

The code uses core_foundation::array::CFArrayRef and related functions throughout but doesn't import the array module at the top of the file.


132-142: Improved window ID generation but still has collision risks.

The enhanced hash function with FNV-like prime multiplication reduces collisions compared to raw pointer casting, but still suffers from pointer address reuse and AX element instability across API calls.


422-457: Process enumeration still has infinite loop risk.

The get_all_running_pids method can potentially loop infinitely if proc_listpids consistently returns a value equal to the buffer size.


958-988: Good collision detection but fallback ID still has collision risk.

The collision detection is excellent defensive programming. However, the fallback ID generation using only timestamp and index could still cause collisions.

🧹 Nitpick comments (1)
src/snap.rs (1)

59-60: Unused fields with dead_code allowances suggest incomplete implementation.

The WindowDragState struct has multiple fields marked with #[allow(dead_code)], indicating either incomplete functionality or unnecessary data storage.

Consider removing unused fields or implementing the missing functionality that would use them. If they're for future features, add TODO comments explaining their intended purpose.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9d754d4 and 6ad7d81.

📒 Files selected for processing (6)
  • src/macos/accessibility.rs (9 hunks)
  • src/macos/cgwindow.rs (8 hunks)
  • src/macos/window_system.rs (7 hunks)
  • src/main.rs (2 hunks)
  • src/snap.rs (1 hunks)
  • src/window_manager.rs (17 hunks)
✅ Files skipped from review due to trivial changes (1)
  • src/main.rs
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/macos/cgwindow.rs
  • src/macos/window_system.rs
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/snap.rs (2)
src/window_manager.rs (1)
  • new (80-111)
src/lib.rs (1)
  • new (28-35)
🔇 Additional comments (5)
src/window_manager.rs (4)

19-19: LGTM: Well-integrated owner_pid field.

The addition of owner_pid to the Window struct aligns perfectly with the enhanced window identification in the macOS accessibility layer and supports more accurate window management.


69-69: LGTM: Smart defensive programming with programmatic move tracking.

The snap_manager integration and programmatically_moving set provide an elegant solution to prevent recursive snap handling when the manager itself moves windows.

Also applies to: 75-76


488-491: Good defensive programming: Marking all windows before batch move.

The pattern of marking all windows as programmatically_moving before the batch operation ensures consistent snap conflict prevention even if the batch operation partially fails.


496-556: Robust fallback strategy for batch window movements.

The implementation properly handles batch move failures by falling back to individual moves with retry logic. The detailed logging helps with debugging layout application issues.

src/macos/accessibility.rs (1)

244-314: Improved batch window movement with proper PID-based matching.

The new implementation correctly matches windows by PID and index rather than global ordering, addressing the previous window-to-layout mapping issue. The resource management with CFRetain/CFRelease is also properly handled.

@plyght
Copy link
Owner Author

plyght commented Jun 3, 2025

@coderabbitai resolve

@coderabbitai
Copy link

coderabbitai bot commented Jun 3, 2025

✅ Actions performed

Comments resolved.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (1)
src/window_manager.rs (1)

460-463: Workspace filtering acknowledgment is good, but consider a configuration option.

🧹 Nitpick comments (5)
src/snap.rs (3)

85-86: Consider making border_size and margin configurable parameters.

The hardcoded values for border_size (120.0) and margin (10.0) limit the flexibility of the snap zones. These should be configurable through the SnapManager constructor to allow users to customize their snapping experience.

 impl SnapManager {
-    pub fn new(screen_rect: Rect, snap_threshold: f64) -> Self {
+    pub fn new(screen_rect: Rect, snap_threshold: f64, border_size: f64, margin: f64) -> Self {
         let mut manager = Self {
             screen_rect,
             snap_zones: Vec::new(),
             snap_threshold,
+            border_size,
+            margin,
             window_drag_states: HashMap::new(),
         };

And update the struct to store these values:

 pub struct SnapManager {
     screen_rect: Rect,
     snap_zones: Vec<SnapZone>,
     snap_threshold: f64,
+    border_size: f64,
+    margin: f64,
     window_drag_states: HashMap<WindowId, WindowDragState>,
 }

160-160: Remove unused parameter to improve code clarity.

The _border_size parameter in create_zone_rect is marked as unused with an underscore prefix, but it's passed to every call. This suggests either the parameter should be used or removed entirely.

-    fn create_zone_rect(
-        &self,
-        screen_rect: Rect,
-        config: (f64, f64, f64, f64),
-        _border_size: f64,
-        margin: f64,
-    ) -> Rect {
+    fn create_zone_rect(
+        &self,
+        screen_rect: Rect,
+        config: (f64, f64, f64, f64),
+        margin: f64,
+    ) -> Rect {

And update the call site:

-            let bounds = self.create_zone_rect(screen_rect, bounds_config, border_size, margin);
-            let snap_rect = self.create_zone_rect(screen_rect, snap_config, border_size, margin);
+            let bounds = self.create_zone_rect(screen_rect, bounds_config, margin);
+            let snap_rect = self.create_zone_rect(screen_rect, snap_config, margin);

185-189: Complex margin calculation logic needs documentation.

The margin calculation logic in the width calculation is non-trivial and could benefit from a comment explaining when and why the margin adjustment is applied.

         let width = if w_config < 0.0 {
             -w_config
         } else if w_config <= 1.0 {
+            // Adjust for margin when zone starts at margin position to prevent overlap
             screen_rect.width * w_config
                 - if x_config == margin {
                     2.0 * margin
                 } else {
                     0.0
                 }
         } else {
             w_config
         };
src/window_manager.rs (2)

597-607: Potential improvement: Consolidate duplicate timer spawning logic.

The timer spawning logic for drag end checks is duplicated between the initial drag start and drag continuation cases. Consider extracting this into a helper method to reduce code duplication.

+    fn schedule_drag_end_check(&mut self, window_id: WindowId, rect: Rect, delay_ms: u64) {
+        let command_tx = self.command_tx.clone();
+        let handle = tokio::spawn(async move {
+            tokio::time::sleep(tokio::time::Duration::from_millis(delay_ms)).await;
+            info!("⏰ SENDING DRAG END CHECK for window {:?}", window_id);
+            if let Err(e) = command_tx
+                .send(Command::CheckSnapDragEnd(window_id, rect))
+                .await
+            {
+                warn!("Failed to send snap check command: {}", e);
+            }
+        });
+        self.pending_drag_end = Some(handle);
+    }

             if !self.snap_manager.is_window_dragging(window_id) {
                 info!("🚀 STARTING DRAG TRACKING for window {:?}", window_id);
                 self.snap_manager.start_window_drag(window_id, old_rect);
-                // Schedule check for drag end
-                let command_tx = self.command_tx.clone();
-                let window_id_copy = window_id;
-                let new_rect_copy = new_rect;
-
-                let handle = tokio::spawn(async move {
-                    tokio::time::sleep(tokio::time::Duration::from_millis(500)).await;
-                    info!("⏰ SENDING DRAG END CHECK for window {:?}", window_id_copy);
-                    if let Err(e) = command_tx
-                        .send(Command::CheckSnapDragEnd(window_id_copy, new_rect_copy))
-                        .await
-                    {
-                        warn!("Failed to send snap check command: {}", e);
-                    }
-                });
-                self.pending_drag_end = Some(handle);
+                self.schedule_drag_end_check(window_id, new_rect, 500);
             } else {
                 info!("⏳ CONTINUING DRAG for window {:?}", window_id);
                 self.snap_manager.update_window_drag(window_id, new_rect);
-                // Schedule a new drag end check
-                let command_tx = self.command_tx.clone();
-                let window_id_copy = window_id;
-                let new_rect_copy = new_rect;
-
-                let handle = tokio::spawn(async move {
-                    tokio::time::sleep(tokio::time::Duration::from_millis(400)).await;
-                    info!(
-                        "⏰ SENDING DRAG END CHECK (continuation) for window {:?}",
-                        window_id_copy
-                    );
-                    if let Err(e) = command_tx
-                        .send(Command::CheckSnapDragEnd(window_id_copy, new_rect_copy))
-                        .await
-                    {
-                        warn!("Failed to send snap check command: {}", e);
-                    }
-                });
-                self.pending_drag_end = Some(handle);
+                self.schedule_drag_end_check(window_id, new_rect, 400);
             }

Also applies to: 617-630


773-796: Dead code should be removed or implemented.

The update_layout_for_manual_move method is marked with #[allow(dead_code)] and contains placeholder logic. If this functionality isn't needed, consider removing it. If it will be implemented later, add a TODO comment with specific plans.

-    #[allow(dead_code)]
-    async fn update_layout_for_manual_move(
-        &mut self,
-        window_id: WindowId,
-        new_rect: Rect,
-    ) -> Result<()> {
-        // For now, we'll just apply the existing layout logic
-        // In a more sophisticated implementation, we might update the BSP tree
-        // to reflect the manual positioning
-        debug!(
-            "Window {:?} manually moved to {:?}, updating layout",
-            window_id, new_rect
-        );
-
-        // You could implement logic here to:
-        // 1. Remove the window from its current position in the BSP tree
-        // 2. Find where it should be placed based on its new position
-        // 3. Rebuild the tree structure accordingly
-
-        // For now, just ensure the layout is consistent
-        self.apply_layout().await?;
-
-        Ok(())
-    }
+    // TODO: Implement BSP tree update for manual window positioning
+    // This would allow preserving user-initiated window moves within the layout system
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6ad7d81 and 90659af.

📒 Files selected for processing (3)
  • src/macos/accessibility.rs (9 hunks)
  • src/snap.rs (1 hunks)
  • src/window_manager.rs (17 hunks)
🔇 Additional comments (20)
src/snap.rs (2)

249-250: Good implementation of configurable snap thresholds.

The snap threshold is now properly used for both time and distance calculations, addressing the previous issue where hardcoded values were used instead of the constructor parameter.


333-398: Well-structured snap target detection with clear priority.

The method correctly prioritizes corners over edges over center, which provides intuitive snapping behavior. The extensive debug logging will be helpful for troubleshooting snap behavior.

src/window_manager.rs (4)

130-136: Excellent documentation of refresh interval decisions.

The detailed comment explaining the different intervals (1000ms for refresh vs 200ms for monitoring) and suggesting future configurability addresses the previous concern about inconsistent intervals.


583-586: Good implementation of timer cancellation.

The addition of timer cancellation logic properly addresses the race condition issue identified in previous reviews. This ensures only the latest drag-end check runs.


496-558: Excellent batch window movement implementation with fallback.

The implementation properly attempts batch window moves first, then falls back to individual moves with retry logic if the batch operation fails. This provides both performance optimization and reliability.


686-687: Good threshold for avoiding redundant snap moves.

The 5-pixel threshold prevents unnecessary window movements when the snap position is very close to the current position, improving user experience.

src/macos/accessibility.rs (14)

2-13: LGTM - Import fixes resolve compilation issues.

The addition of core_foundation::array imports fixes the previous compilation errors, and the new proc_listpids function declaration is necessary for system-wide process enumeration functionality.


15-66: LGTM - Standard macOS API constants and structures.

These additions correctly define the necessary constants and structures for Core Foundation and Accessibility API integration.


134-141: Improved window ID generation approach.

The change from raw pointer casting to the generate_window_id method addresses previous stability concerns with window identification. The new approach incorporates PID and hash-based generation for more reliable window IDs.


174-214: Excellent robust window movement implementation.

The rewritten method properly handles cache refreshing, includes a fallback mechanism using CGWindow data for brute force searching, and has correct memory management with appropriate CFRelease calls. This addresses previous concerns about window finding reliability.


216-239: Well-implemented window element finder.

This method correctly implements window finding with proper cache management, memory safety through CFRetain calls, and appropriate fallback to cache refresh when windows aren't found.


241-311: Excellent batch window movement implementation.

This method properly addresses the previous window-to-layout mapping concerns by grouping windows by PID and matching by index within each PID. The memory management is correct with proper CFRelease calls, and the error handling is comprehensive.


313-370: Correct implementation of window positioning.

The method properly uses AXValue objects for setting window position and size, includes appropriate error checking, and handles memory management correctly with CFRelease calls for all created AXValue objects.


372-461: Comprehensive window enumeration implementation.

The process enumeration correctly implements buffer management with safety limits to prevent infinite loops. The approach of enumerating all running processes provides comprehensive window discovery capability.


463-487: Good deprecation practice.

The deprecated methods are properly marked with version information and provide clear migration guidance while maintaining backward compatibility.


489-581: Solid PID-based window enumeration.

The implementation properly filters system processes, checks window tileability through attribute verification, and handles memory management correctly with appropriate CFRetain/CFRelease calls.


677-729: Well-designed cache refresh mechanism.

The cache refresh methods properly handle memory cleanup, include throttling to prevent excessive refreshes, and provide appropriate fallback mechanisms. The 100ms throttling prevents performance issues while maintaining responsiveness.


731-886: Comprehensive window finding and rect extraction.

These methods properly implement window finding through multiple strategies and correctly extract actual values from AXValue objects, addressing previous concerns about incomplete value extraction. Memory management is handled appropriately throughout.


888-1042: Robust enumeration and caching with collision handling.

The implementation correctly handles window enumeration across all applications, includes collision detection with fallback ID generation, and properly manages memory with CFRetain for cached elements. The hash-based window ID generation incorporating PID and index provides good uniqueness.


1045-1055: Proper resource cleanup in Drop implementation.

The Drop implementation correctly releases all cached window elements and the system element, ensuring proper cleanup of Core Foundation resources and preventing memory leaks.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (2)
src/snap.rs (2)

82-230: Excellent refactoring addresses previous maintainability concerns!

The update_snap_zones method has been significantly improved from the previous version. The data-driven approach using zone configurations in an array eliminates the repetitive code patterns that were flagged in past reviews. The code is now much more maintainable and easier to understand.


307-309: Good fix for the hardcoded threshold issue!

The snap thresholds are now properly calculated from the configurable snap_threshold parameter, addressing the previous concern about ignoring constructor parameters. The scaling approach for distance threshold is well thought out.

🧹 Nitpick comments (6)
src/snap.rs (4)

58-59: Consider removing the dead_code attribute if the field is truly unused.

The window_id field in WindowDragState is marked with #[allow(dead_code)] but it's only used for initialization. If this field serves no purpose, consider removing it to simplify the struct.

 #[derive(Debug, Clone)]
 struct WindowDragState {
-    #[allow(dead_code)]
-    window_id: WindowId,
     initial_rect: Rect,
     is_dragging: bool,
     drag_start_time: std::time::Instant,
 }

And update the initialization:

 self.window_drag_states.insert(
     window_id,
     WindowDragState {
-        window_id,
         initial_rect: current_rect,
         is_dragging: true,
         drag_start_time: std::time::Instant::now(),
     },
 );

232-261: Consider adding documentation for the mixed coordinate system.

The create_absolute_zone_rect method handles both relative (0-1) and absolute coordinates, which could be confusing for maintainers. Consider adding a doc comment explaining the coordinate system convention.

+    /// Creates a rectangle from configuration that supports both relative and absolute coordinates.
+    /// Values <= 1.0 are treated as relative percentages of screen dimensions.
+    /// Values > 1.0 are treated as absolute pixel coordinates.
     fn create_absolute_zone_rect(&self, screen_rect: Rect, config: (f64, f64, f64, f64)) -> Rect {

350-351: Consider making position/size difference thresholds configurable.

The hardcoded 10.0 pixel threshold for determining if a window needs to snap could be made configurable for better user experience customization.

+    // Add to SnapManager struct
+    position_tolerance: f64,
-            if dx > 10.0 || dy > 10.0 || dw > 10.0 || dh > 10.0 {
+            let tolerance = self.position_tolerance;
+            if dx > tolerance || dy > tolerance || dw > tolerance || dh > tolerance {

390-455: Optimize zone lookup with early termination.

The find_snap_target method iterates through zones multiple times for prioritization. Consider creating a single prioritized iterator or caching the prioritized zones for better performance.

 pub fn find_snap_target(&self, window_rect: Rect) -> Option<Rect> {
     let center_x = window_rect.x + window_rect.width / 2.0;
     let center_y = window_rect.y + window_rect.height / 2.0;
 
+    // Create prioritized zones: corners first, then edges, then center
+    let prioritized_regions = [
+        &[SnapRegion::NorthWest, SnapRegion::NorthEast, SnapRegion::SouthWest, SnapRegion::SouthEast],
+        &[SnapRegion::North, SnapRegion::South, SnapRegion::East, SnapRegion::West],
+        &[SnapRegion::Center],
+    ];
+
+    for region_group in prioritized_regions {
         for zone in &self.snap_zones {
-            if matches!(
-                zone.region,
-                SnapRegion::NorthWest
-                    | SnapRegion::NorthEast
-                    | SnapRegion::SouthWest
-                    | SnapRegion::SouthEast
-            ) && self.point_in_rect(center_x, center_y, &zone.bounds)
-            {
-                debug!(
-                    "Window center ({}, {}) in {} zone",
-                    center_x,
-                    center_y,
-                    zone.region.name()
-                );
-                return Some(zone.snap_rect);
+            if region_group.contains(&zone.region) && self.point_in_rect(center_x, center_y, &zone.bounds) {
+                debug!("Window center ({}, {}) in {} zone", center_x, center_y, zone.region.name());
+                return Some(zone.snap_rect);
             }
         }
+    }
llms.txt (2)

9-9: Fix redundant acronym usage.

The phrase "CLI Interface" contains a redundancy since "CLI" already stands for "Command-Line Interface."

-- **CLI Interface** (`main.rs`): Command-line entry point
+- **CLI** (`main.rs`): Command-line entry point
🧰 Tools
🪛 LanguageTool

[style] ~9-~9: This phrase is redundant (‘I’ stands for ‘Interface’). Use simply “CLIInterface”.
Context: ...ssibility APIs. ## Key Components - CLI Interface (main.rs): Command-line entry point...

(ACRONYM_TAUTOLOGY)


56-56: Add missing article for better grammar.

The phrase "with macOS window system" is missing the definite article "the."

-- Integrates with macOS window system through Accessibility bindings
+- Integrates with the macOS window system through Accessibility bindings
🧰 Tools
🪛 LanguageTool

[uncategorized] ~56-~56: You might be missing the article “the” here.
Context: ...for window operations - Integrates with macOS window system through Accessibility bin...

(AI_EN_LECTOR_MISSING_DETERMINER_THE)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 90659af and 7ecc811.

📒 Files selected for processing (2)
  • llms.txt (1 hunks)
  • src/snap.rs (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/snap.rs (2)
src/window_manager.rs (1)
  • new (82-114)
src/lib.rs (1)
  • new (28-35)
🪛 LanguageTool
llms.txt

[style] ~9-~9: This phrase is redundant (‘I’ stands for ‘Interface’). Use simply “CLIInterface”.
Context: ...ssibility APIs. ## Key Components - CLI Interface (main.rs): Command-line entry point...

(ACRONYM_TAUTOLOGY)


[grammar] ~17-~17: The operating system from Apple is written “macOS”.
Context: ...ng integration - macOS Integration (macos/): Accessibility API bindings, Core Gr...

(MAC_OS)


[uncategorized] ~56-~56: You might be missing the article “the” here.
Context: ...for window operations - Integrates with macOS window system through Accessibility bin...

(AI_EN_LECTOR_MISSING_DETERMINER_THE)

🔇 Additional comments (1)
llms.txt (1)

1-81: Excellent comprehensive project documentation!

This documentation provides valuable context for the tiling window manager project, covering architecture, dependencies, development workflow, and important considerations. The structure is clear and will be very helpful for contributors and maintainers.

🧰 Tools
🪛 LanguageTool

[style] ~9-~9: This phrase is redundant (‘I’ stands for ‘Interface’). Use simply “CLIInterface”.
Context: ...ssibility APIs. ## Key Components - CLI Interface (main.rs): Command-line entry point...

(ACRONYM_TAUTOLOGY)


[grammar] ~17-~17: The operating system from Apple is written “macOS”.
Context: ...ng integration - macOS Integration (macos/): Accessibility API bindings, Core Gr...

(MAC_OS)


[uncategorized] ~56-~56: You might be missing the article “the” here.
Context: ...for window operations - Integrates with macOS window system through Accessibility bin...

(AI_EN_LECTOR_MISSING_DETERMINER_THE)

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (1)
src/window_manager.rs (1)

140-140: Refresh interval consistency has been addressed.

The comment clearly explains the rationale for different intervals between window refresh (1000ms) and monitoring (200ms), and suggests making them configurable.

🧹 Nitpick comments (4)
src/macos/window_notifications.rs (1)

269-289: Consider safer memory management pattern for stored callback data.

The current implementation uses a complex pattern with Box::from_raw and Box::into_raw to avoid dropping the data. While functional, this pattern is error-prone and could lead to memory leaks if not handled carefully.

Consider using a more straightforward approach with Rc or storing the data directly in a static variable:

-unsafe fn get_dragging_windows(observer: &Object) -> Option<Arc<Mutex<HashMap<WindowId, Rect>>>> {
-    let ptr: *const std::ffi::c_void = *observer.get_ivar("dragging_windows");
-    if ptr.is_null() {
-        return None;
-    }
-    let boxed = Box::from_raw(ptr as *mut Arc<Mutex<HashMap<WindowId, Rect>>>);
-    let result = Some((*boxed).clone());
-    let _ = Box::into_raw(boxed); // Don't drop it
-    result
-}
+unsafe fn get_dragging_windows(observer: &Object) -> Option<Arc<Mutex<HashMap<WindowId, Rect>>>> {
+    let ptr: *const std::ffi::c_void = *observer.get_ivar("dragging_windows");
+    if ptr.is_null() {
+        return None;
+    }
+    // Cast directly to Arc reference without taking ownership
+    let arc_ref = &*(ptr as *const Arc<Mutex<HashMap<WindowId, Rect>>>);
+    Some(arc_ref.clone())
+}

The same pattern should be applied to get_event_sender.

src/window_manager.rs (3)

189-221: Consider extracting drag detection constants and simplifying the logic.

The drag detection logic uses a hardcoded distance threshold of 5 pixels and has complex nested conditions. This could be made more maintainable.

+    const DRAG_END_THRESHOLD: f64 = 5.0;
+
+    fn calculate_distance(rect1: &Rect, rect2: &Rect) -> f64 {
+        ((rect2.x - rect1.x).powi(2) + (rect2.y - rect1.y).powi(2)).sqrt()
+    }
+
     // Check if this is the end of a user drag by comparing with previous position
     if let Some(&previous_rect) = self.previous_window_positions.get(&id) {
-        let distance = ((new_rect.x - previous_rect.x).powi(2) + (new_rect.y - previous_rect.y).powi(2)).sqrt();
+        let distance = Self::calculate_distance(&previous_rect, &new_rect);
         
         // If the window hasn't moved much, this might be the end of a drag
-        if distance < 5.0 {
+        if distance < Self::DRAG_END_THRESHOLD {

73-74: Remove or utilize the dead code field.

The command_tx field is marked with #[allow(dead_code)] but appears to be used in the codebase (e.g., passed to various managers).

If this field is actually being used, remove the #[allow(dead_code)] attribute:

-    #[allow(dead_code)]
     command_tx: mpsc::Sender<Command>,

890-913: Document the purpose of placeholder method or implement it.

The update_layout_for_manual_move method is marked as dead code and contains only placeholder logic.

The comment indicates this is for future BSP tree updates. Would you like me to:

  1. Implement the BSP tree update logic based on the new window position?
  2. Create a GitHub issue to track this TODO?
  3. Remove the method if it's not needed in the near future?
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7ecc811 and 8667112.

📒 Files selected for processing (4)
  • src/macos/mod.rs (1 hunks)
  • src/macos/window_notifications.rs (1 hunks)
  • src/snap.rs (1 hunks)
  • src/window_manager.rs (14 hunks)
✅ Files skipped from review due to trivial changes (1)
  • src/macos/mod.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/snap.rs

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (2)
src/macos/window_notifications.rs (1)

56-63: Consider safer memory management for cross-boundary data storage.

The current approach stores raw pointers to boxed data in Objective-C instance variables, which could lead to memory safety issues if not handled carefully. While the current implementation manually manages the lifecycle, this pattern is error-prone.

Consider using a safer approach with reference counting or storing the data in a static registry indexed by observer pointer:

// Alternative approach - use a static registry
use std::sync::RwLock;
use std::collections::HashMap;

static OBSERVER_DATA: RwLock<HashMap<*const Object, (Arc<Mutex<HashMap<WindowId, Rect>>>, mpsc::Sender<WindowDragEvent>)>> = RwLock::new(HashMap::new());

// In start_observing:
-(*observer).set_ivar(
-    "dragging_windows",
-    Box::into_raw(Box::new(dragging_windows)) as *const _ as *const std::ffi::c_void,
-);
-(*observer).set_ivar(
-    "event_sender",
-    Box::into_raw(Box::new(event_sender)) as *const _ as *const std::ffi::c_void,
-);
+OBSERVER_DATA.write().unwrap().insert(observer, (dragging_windows, event_sender));
src/window_manager.rs (1)

765-788: Placeholder method needs implementation guidance.

The update_layout_for_manual_move method is marked as dead code and only contains placeholder logic. Consider either implementing the BSP tree update logic or removing it if not needed.

The method outlines the intended functionality in comments. Would you like me to help implement the BSP tree update logic or should this method be removed if it's not currently needed?

+// If implementing BSP tree updates:
+// 1. Find the window's current position in the BSP tree
+// 2. Calculate the new logical position based on screen coordinates
+// 3. Restructure the tree to reflect the new layout
+// 4. Apply the updated layout to all affected windows
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8667112 and ad066c8.

📒 Files selected for processing (2)
  • src/macos/window_notifications.rs (1 hunks)
  • src/window_manager.rs (14 hunks)
🔇 Additional comments (6)
src/macos/window_notifications.rs (1)

260-277: PID extraction implementation looks correct.

The updated implementation properly extracts the process ID using the NSRunningApplication API, which addresses the previous concern about the hardcoded placeholder value.

The approach using msg_send![window, app] followed by msg_send![app, runningApplication] and then msg_send![running_app, processIdentifier] is the correct way to get the owner PID from an NSWindow.

src/window_manager.rs (5)

134-140: Well-documented refresh interval configuration.

The detailed comments explaining the refresh timer intervals and suggesting future configuration options provide excellent context for the design decisions. The 1000ms refresh interval is now properly justified in relation to the 200ms monitoring interval.


405-438: Effective workspace detection improves reliability.

The new get_effective_current_workspace method provides a robust fallback mechanism that prioritizes focused window workspace, then most common workspace, and finally the stored workspace. This addresses workspace detection reliability issues.

The implementation correctly handles edge cases and provides clear debug logging for workspace detection decisions.


577-640: Batch window movement with proper fallback.

The implementation attempts to use the new move_all_windows method for efficiency, with appropriate fallback to individual moves. The retry logic with exponential backoff is well-implemented.

The error handling gracefully degrades performance while maintaining functionality, and the logging provides good visibility into which approach was used.


646-763: Comprehensive drag end handling implementation.

The handle_drag_end method properly integrates with the snap manager and handles all drag result types (snap, swap, return, no action). The programmatic move tracking prevents recursive drag detection.

The implementation correctly:

  • Uses effective workspace for collision detection
  • Marks windows as programmatically moving to prevent recursive snapping
  • Updates internal state after moves
  • Clears drag state when complete

511-515: Window position tracking initialization.

Good practice to initialize position tracking for new windows during refresh. This ensures drag detection works correctly for all windows from the start.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
src/snap.rs (1)

457-459: Fix boundary overlap issue in point detection.

Using <= for both bounds checks means points exactly on the right/bottom edges belong to multiple zones, which can cause unexpected behavior with overlapping zones.

-    fn point_in_rect(&self, x: f64, y: f64, rect: &Rect) -> bool {
-        x >= rect.x && x <= rect.x + rect.width && y >= rect.y && y <= rect.y + rect.height
-    }
+    fn point_in_rect(&self, x: f64, y: f64, rect: &Rect) -> bool {
+        x >= rect.x && x < rect.x + rect.width && y >= rect.y && y < rect.y + rect.height
+    }
🧹 Nitpick comments (2)
src/snap.rs (2)

52-52: Unclear parameter naming: snap_threshold used for distance.

The field name snap_threshold is ambiguous since it's used for distance thresholds but could be confused with time thresholds. Consider renaming for clarity.

-    snap_threshold: f64,
+    snap_distance_threshold: f64,

350-350: Magic number threshold should be configurable.

The 10.0 pixel threshold for determining if a window is "already close to warp target" is hardcoded. Consider making this configurable or using a consistent threshold system.

+    const SNAP_PRECISION_THRESHOLD: f64 = 10.0;
+
     // In the method:
-            if dx > 10.0 || dy > 10.0 || dw > 10.0 || dh > 10.0 {
+            if dx > Self::SNAP_PRECISION_THRESHOLD || dy > Self::SNAP_PRECISION_THRESHOLD || 
+               dw > Self::SNAP_PRECISION_THRESHOLD || dh > Self::SNAP_PRECISION_THRESHOLD {
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ad066c8 and 24e06cc.

📒 Files selected for processing (2)
  • src/snap.rs (1 hunks)
  • src/window_manager.rs (15 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/window_manager.rs
🔇 Additional comments (6)
src/snap.rs (6)

1-4: Clean imports and dependencies.

The module imports are well-organized and include only necessary dependencies.


5-32: Well-designed SnapRegion enum with helpful utilities.

The enum covers all necessary screen regions for comprehensive window snapping, and the name() method provides useful string representations for debugging.


41-47: DragResult enum effectively models all drag outcomes.

The enum variants appropriately handle the different scenarios: snapping, swapping (with original rect preservation), returning to original position, and no action.


82-230: Well-structured zone configuration system.

The current implementation using a zones array with tuple configurations is much more maintainable than repetitive hardcoded zone creation. The helper method create_absolute_zone_rect provides good abstraction for coordinate handling.


497-521: Efficient window detection algorithm.

The find_window_under_drag method uses a simple center-point collision detection which is appropriate for the use case and efficiently excludes the dragged window itself.


523-537: Clean utility methods with appropriate interfaces.

The getter methods and state management functions provide a clean interface for external consumers while maintaining proper encapsulation.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (3)
src/window_manager.rs (3)

414-504: Complex but well-structured immediate positioning logic.

The implementation correctly handles:

  • Distance-based movement detection (20px threshold is reasonable)
  • Zone-based actions (center→swap, edges→snap, outside→return)
  • Redundant move prevention (10px tolerance)
  • Proper error handling with fallbacks

The complexity is warranted for the functionality, but consider adding inline comments for the zone logic to improve maintainability.


696-800: Excellent layout application improvements with robust error handling.

Key improvements:

  • Uses effective workspace detection for better reliability
  • Bulk window moves with individual move fallback
  • 3-attempt retry logic with 50ms delays for transient failures
  • Comprehensive logging for debugging
  • Proper programmatic move tracking

The retry logic effectively handles the window positioning challenges mentioned in the AI summary.

Consider making the retry count and delay configurable via the config file for different system performance characteristics.


969-992: Consider removing or better documenting this placeholder method.

The update_layout_for_manual_move method is marked as dead code and contains mostly TODO comments. Consider either:

  1. Remove it entirely if the functionality isn't planned for the near term
  2. Add a clear comment explaining when this will be implemented and its intended purpose
  3. Move it to a separate module for future BSP tree enhancements

This reduces code clutter and makes the current functionality clearer.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 24e06cc and 39cb029.

📒 Files selected for processing (2)
  • src/snap.rs (1 hunks)
  • src/window_manager.rs (15 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/snap.rs
🔇 Additional comments (12)
src/window_manager.rs (12)

5-5: LGTM! New imports and Window struct enhancement look good.

The additions integrate well with the overall architecture:

  • Window drag notifications import enables native macOS drag detection
  • Snap manager import provides the snapping functionality
  • The owner_pid field addition aligns with the enhanced window metadata extraction mentioned in the AI summary

Also applies to: 8-8, 20-20


69-88: Well-designed state tracking for drag management.

The new fields effectively separate concerns:

  • snap_manager handles snapping logic
  • drag_observer and drag_event_rx enable native drag detection
  • State tracking sets (programmatically_moving, user_dragging_windows) prevent feedback loops
  • previous_window_positions enables proper drag distance calculations

This design should prevent the race conditions mentioned in past reviews.


102-130: Solid initialization with proper error handling.

The setup correctly:

  • Initializes drag observer with error propagation
  • Sets reasonable snap threshold (50px)
  • Properly initializes all state tracking collections
  • Uses screen rect for snap manager bounds

141-171: Good improvements to startup and event loop.

Positive changes:

  • Initial layout application ensures consistent startup state
  • Drag event handling integrates cleanly into the main loop
  • The interval documentation addresses past review concerns

The TODO comment about making intervals configurable is helpful for future improvement.


196-214: Excellent move event handling with proper state management.

The logic correctly differentiates between:

  • Programmatic moves (cleanup and state update)
  • NSWindow-tracked user drags (state update only)
  • Other user moves (immediate snap processing)

This should eliminate the feedback loop issues mentioned in past reviews.


269-269: Consistent programmatic move tracking prevents feedback loops.

The pattern of marking windows as programmatically_moving before repositioning them is correctly applied across all relevant commands. This ensures these moves won't trigger unintended snapping behavior.

Also applies to: 291-293, 326-326, 350-352


384-412: Well-implemented drag event handling with good integration.

The method correctly:

  • Tracks user drag state via user_dragging_windows
  • Integrates with snap manager for drag tracking
  • Processes drag end only for managed windows
  • Includes helpful debug logging

The emoji logging adds good visual distinction for debugging drag operations.


506-559: Well-implemented helper methods with consistent patterns.

Both swap_windows and return_window_to_original correctly:

  • Mark windows as programmatically moving to prevent feedback
  • Handle errors gracefully with appropriate logging
  • Update internal state (previous_window_positions)
  • Follow the established error handling patterns

565-598: Excellent workspace detection with robust fallback strategy.

The multi-tier approach significantly improves reliability:

  1. Focused window's workspace (most reliable)
  2. Most common workspace among visible windows
  3. Stored current workspace (fallback)

This addresses the workspace filtering concerns from past reviews and provides much more reliable workspace awareness.


608-608: Consistent use of improved workspace detection.

The switch to get_effective_current_workspace() makes directional window navigation more reliable by using the enhanced workspace detection logic.

Also applies to: 613-613


652-676: Good improvements to window refresh with better workspace tracking.

The enhancements include:

  • Workspace change detection with proper logging
  • Initial position tracking for new windows (prevents missing position data)
  • Graceful error handling for workspace detection failures

These changes support the improved drag and positioning functionality.


806-967: Comprehensive drag end handling with excellent error recovery.

The method expertly handles all drag scenarios:

  • SnapToZone: Checks for redundant moves before snapping
  • SwapWithWindow: Uses bulk moves with individual move fallback
  • ReturnToOriginal: Restores previous position
  • NoAction: No operation needed

The comprehensive logging and proper state cleanup (clear_drag_state) ensure reliable operation and prevent state leaks.

@plyght
Copy link
Owner Author

plyght commented Jun 3, 2025

@coderabbitai resolve

@coderabbitai
Copy link

coderabbitai bot commented Jun 3, 2025

✅ Actions performed

Comments resolved.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (3)
src/window_manager.rs (3)

388-472: Consider decomposing the drag event handler.

This method handles two distinct event types and is quite long. Consider splitting it into separate methods:

 async fn handle_drag_event(&mut self, event: WindowDragEvent) -> Result<()> {
     match event {
-        WindowDragEvent::DragStarted { window_id, initial_rect, owner_pid } => {
-            // ... complex start logic
-        }
-        WindowDragEvent::DragEnded { window_id, final_rect, owner_pid } => {
-            // ... complex end logic  
-        }
+        WindowDragEvent::DragStarted { window_id, initial_rect, owner_pid } => {
+            self.handle_drag_started(window_id, initial_rect, owner_pid).await
+        }
+        WindowDragEvent::DragEnded { window_id, final_rect, owner_pid } => {
+            self.handle_drag_ended(window_id, final_rect, owner_pid).await
+        }
     }
-    Ok(())
 }

This would improve maintainability and make the code easier to test.


875-983: Extract retry logic for better maintainability.

The apply_layout method is comprehensive but quite long. Consider extracting the retry logic for individual window moves:

+async fn move_window_with_retry(&mut self, window_id: WindowId, rect: Rect, max_attempts: usize) -> Result<()> {
+    for attempt in 0..max_attempts {
+        match self.macos.move_window(window_id, rect).await {
+            Ok(_) => {
+                debug!("Successfully moved window {:?} on attempt {}", window_id, attempt + 1);
+                return Ok(());
+            }
+            Err(e) if attempt < max_attempts - 1 => {
+                debug!("Failed to move window {:?} on attempt {}: {}, retrying", window_id, attempt + 1, e);
+                tokio::time::sleep(Duration::from_millis(50)).await;
+            }
+            Err(e) => {
+                warn!("Failed to move window {:?} after {} attempts: {}", window_id, max_attempts, e);
+                return Err(e);
+            }
+        }
+    }
+    unreachable!()
+}

This would make the main layout logic cleaner and the retry logic reusable.


1-1188: Consider organizing the large file for better maintainability.

This file has grown quite large (1188 lines) with several long methods. Consider:

  1. Extract common patterns: Workspace filtering, window position updates, and programmatic move tracking appear in multiple places
  2. Consider module organization: Drag handling, layout application, and window swapping could be separate modules
  3. Helper methods: Extract common operations like "mark as programmatic move and update position"

For example, a helper for common window state updates:

fn update_window_state(&mut self, window_id: WindowId, new_rect: Rect, mark_programmatic: bool) {
    if mark_programmatic {
        self.programmatically_moving.insert(window_id);
    }
    if let Some(window) = self.windows.get_mut(&window_id) {
        window.rect = new_rect;
    }
    self.previous_window_positions.insert(window_id, new_rect);
}

This would reduce duplication and improve consistency.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 39cb029 and 00b6a28.

📒 Files selected for processing (2)
  • README.md (1 hunks)
  • src/window_manager.rs (15 hunks)
✅ Files skipped from review due to trivial changes (1)
  • README.md
🔇 Additional comments (2)
src/window_manager.rs (2)

20-20: Good addition for process-based window management.

The owner_pid field addition aligns well with the enhanced window enumeration and tracking capabilities mentioned in the PR summary.


745-778: Excellent workspace detection strategy.

The fallback approach using focused window → most common workspace → stored value is a robust solution to the workspace detection reliability issues mentioned in previous reviews. The comprehensive logging will also help debug any remaining workspace detection problems.

Comment on lines 196 to 218
WindowEvent::WindowMoved(id, new_rect) => {
if let Some(window) = self.windows.get_mut(&id) {
window.rect = new_rect;
// Handle programmatic move cleanup
if self.programmatically_moving.contains(&id) {
debug!("Ignoring programmatic move for window {:?}", id);
self.programmatically_moving.remove(&id);
if let Some(window) = self.windows.get_mut(&id) {
window.rect = new_rect;
}
// Update previous position tracking for programmatic moves
self.previous_window_positions.insert(id, new_rect);
} else if self.user_dragging_windows.contains(&id) {
// This is a user drag that NSWindow notifications already started tracking
debug!("Window {:?} moved during NSWindow drag to {:?}", id, new_rect);
if let Some(window) = self.windows.get_mut(&id) {
window.rect = new_rect;
}
// Update position tracking but don't trigger immediate positioning
self.previous_window_positions.insert(id, new_rect);
} else {
// This is a user move - process for potential snapping
debug!("Window {:?} moved to {:?}", id, new_rect);
self.handle_immediate_window_positioning(id, new_rect).await?;
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Refactor complex window move handling for better maintainability.

The window move handling has complex branching logic that could become brittle. Consider extracting each case into separate methods for better clarity and testing:

 WindowEvent::WindowMoved(id, new_rect) => {
-    // Handle programmatic move cleanup
-    if self.programmatically_moving.contains(&id) {
-        debug!("Ignoring programmatic move for window {:?}", id);
-        // ... rest of programmatic logic
-    } else if self.user_dragging_windows.contains(&id) {
-        // ... rest of user drag logic  
-    } else {
-        // ... rest of immediate positioning logic
-    }
+    if self.programmatically_moving.contains(&id) {
+        self.handle_programmatic_move(id, new_rect).await?;
+    } else if self.user_dragging_windows.contains(&id) {
+        self.handle_user_drag_move(id, new_rect).await?;
+    } else {
+        self.handle_immediate_window_positioning(id, new_rect).await?;
+    }
 }

This would improve readability, testability, and reduce the risk of state inconsistencies.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/window_manager.rs around lines 196 to 218, the window move event handling
contains complex branching logic that reduces readability and maintainability.
Refactor this by extracting each branch of the if-else statement (programmatic
move, user dragging, and user move) into separate dedicated methods. Each method
should encapsulate the specific logic for updating window state and positions,
improving clarity and making the code easier to test and maintain.

Comment on lines +986 to +1162
async fn handle_drag_end(
&mut self,
window_id: WindowId,
_initial_rect: Rect,
final_rect: Rect,
) -> Result<()> {
// Get current workspace from focused window for reliable workspace detection
let effective_workspace = self.get_effective_current_workspace();

// Get all windows for collision detection (filter by effective workspace)
let workspace_windows: Vec<&Window> = self
.windows
.values()
.filter(|w| w.workspace_id == effective_workspace && !w.is_minimized)
.collect();

info!(
"🔍 Found {} windows in workspace for collision detection",
workspace_windows.len()
);

// Check what should happen with this drag
let drag_result =
self.snap_manager
.end_window_drag(window_id, final_rect, &workspace_windows);

info!("🎯 Drag result: {:?}", drag_result);

match drag_result {
DragResult::SnapToZone(snap_rect) => {
// Check if window needs to be moved (avoid redundant moves)
let dx = (snap_rect.x - final_rect.x).abs();
let dy = (snap_rect.y - final_rect.y).abs();
let dw = (snap_rect.width - final_rect.width).abs();
let dh = (snap_rect.height - final_rect.height).abs();

if dx > 5.0 || dy > 5.0 || dw > 5.0 || dh > 5.0 {
info!(
"📍 Snapping window {:?} to zone at {:?}",
window_id, snap_rect
);

// Mark as programmatic move
self.programmatically_moving.insert(window_id);

// Move the window to snap position (no delay needed with proper notifications)
match self.macos.move_window(window_id, snap_rect).await {
Ok(_) => info!("✅ Successfully snapped window {:?} to zone", window_id),
Err(e) => warn!("❌ Failed to snap window {:?}: {}", window_id, e),
}

// Update our internal state
if let Some(window) = self.windows.get_mut(&window_id) {
window.rect = snap_rect;
}
}
}
DragResult::SwapWithWindow(target_window_id, original_rect) => {
info!(
"🔄 Swapping window {:?} with window {:?}",
window_id, target_window_id
);

// Get current window positions for accuracy
let current_windows = self.macos.get_windows().await?;
let target_window_current = current_windows.iter().find(|w| w.id == target_window_id);

if let Some(target_window) = target_window_current {
let target_rect = target_window.rect;

// Mark both windows as programmatic moves
self.programmatically_moving.insert(window_id);
self.programmatically_moving.insert(target_window_id);

// Create layouts for both windows in their swapped positions
let mut swap_layouts = HashMap::new();
swap_layouts.insert(window_id, target_rect);
swap_layouts.insert(target_window_id, original_rect);

// Get both window objects for the bulk move API
let both_windows: Vec<crate::Window> = [window_id, target_window_id]
.iter()
.filter_map(|id| current_windows.iter().find(|w| w.id == *id).cloned())
.collect();

info!("🔄 Executing swap: dragged window {:?} -> {:?}, target window {:?} -> {:?}",
window_id, target_rect, target_window_id, original_rect);

// Use the bulk move API which tends to be more reliable
match self.macos.move_all_windows(&swap_layouts, &both_windows).await {
Ok(_) => {
// Update our internal state
if let Some(window) = self.windows.get_mut(&window_id) {
window.rect = target_rect;
}
if let Some(window) = self.windows.get_mut(&target_window_id) {
window.rect = original_rect;
}

// Update position tracking
self.previous_window_positions.insert(window_id, target_rect);
self.previous_window_positions.insert(target_window_id, original_rect);

info!(
"✅ Successfully swapped windows {:?} and {:?}",
window_id, target_window_id
);
}
Err(e) => {
warn!("Bulk swap failed, trying individual moves: {}", e);

// Fallback to individual moves
match self.macos.move_window(window_id, target_rect).await {
Ok(_) => {
info!("✅ Moved dragged window to target position");
if let Some(window) = self.windows.get_mut(&window_id) {
window.rect = target_rect;
}
self.previous_window_positions.insert(window_id, target_rect);
}
Err(e) => warn!("❌ Failed to move dragged window: {}", e),
}

match self.macos.move_window(target_window_id, original_rect).await {
Ok(_) => {
info!("✅ Moved target window to original position");
if let Some(window) = self.windows.get_mut(&target_window_id) {
window.rect = original_rect;
}
self.previous_window_positions.insert(target_window_id, original_rect);
}
Err(e) => warn!("❌ Failed to move target window: {}", e),
}

info!(
"✅ Completed swap with individual moves: {:?} and {:?}",
window_id, target_window_id
);
}
}
} else {
warn!("❌ Target window {:?} not found in current windows", target_window_id);
}
}
DragResult::ReturnToOriginal(original_rect) => {
info!(
"↩️ Returning window {:?} to original position {:?}",
window_id, original_rect
);

// Mark as programmatic move
self.programmatically_moving.insert(window_id);

// Move the window back to its original position
match self.macos.move_window(window_id, original_rect).await {
Ok(_) => {
info!("✅ Successfully returned window {:?} to original position", window_id);
// Update our internal state
if let Some(window) = self.windows.get_mut(&window_id) {
window.rect = original_rect;
}
self.previous_window_positions.insert(window_id, original_rect);
}
Err(e) => warn!("❌ Failed to return window {:?} to original position: {}", window_id, e),
}
}
DragResult::NoAction => {
info!("❌ No action needed for window {:?}", window_id);
}
}

// Always clear the drag state when we're done
self.snap_manager.clear_drag_state(window_id);
info!("🧹 Cleared drag state for window {:?}", window_id);

Ok(())
}
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Clarify the purpose of this drag end handler.

This method appears to duplicate much of the drag end logic found in handle_drag_event. Consider:

  1. If this is redundant: Remove it to eliminate code duplication
  2. If it serves a different purpose: Add clear documentation explaining when each method should be used
  3. If both are needed: Extract common logic into shared helper methods

The method is also quite long and could benefit from decomposition into smaller, focused methods for each drag result type.


🏁 Script executed:

#!/bin/bash
# Search for calls to handle_drag_end to understand its usage
rg "handle_drag_end" -A 3 -B 3

Length of output: 299



🏁 Script executed:

#!/bin/bash
# Locate definition and usages of handle_drag_event
rg "async fn handle_drag_event" -A50 -B0 src/window_manager.rs

# Search for handle_drag_end references inside handle_drag_event
rg "handle_drag_end" -A5 -B5 src/window_manager.rs

Length of output: 3538


Consolidate drag-end handling and remove unused duplication

The handle_drag_end method (src/window_manager.rs:986–1162) duplicates the DragEnded arm in handle_drag_event but is never invoked. To clean this up:

  • Remove the unused handle_drag_end method, or
  • Refactor handle_drag_event to delegate its DragEnded logic to handle_drag_end and eliminate the inline duplication.
  • In either case, extract each DragResult branch (snap, swap, return, no-action) into small, focused helpers for readability and maintainability.
🤖 Prompt for AI Agents
In src/window_manager.rs from lines 986 to 1162, the handle_drag_end method
duplicates the drag end logic already present inline in the DragEnded arm of
handle_drag_event but is never called. To fix this, either remove the unused
handle_drag_end method entirely to eliminate dead code or refactor
handle_drag_event to delegate its DragEnded case to handle_drag_end, removing
the inline duplication. Additionally, extract each DragResult match branch
(SnapToZone, SwapWithWindow, ReturnToOriginal, NoAction) into separate small
helper functions to improve readability and maintainability.

Comment on lines +69 to +87
snap_manager: SnapManager,

event_rx: mpsc::Receiver<WindowEvent>,
command_rx: mpsc::Receiver<Command>,
#[allow(dead_code)]
command_tx: mpsc::Sender<Command>,

// Drag notification system
drag_observer: WindowDragNotificationObserver,
drag_event_rx: mpsc::Receiver<WindowDragEvent>,

// Track windows being moved programmatically to avoid snap conflicts
programmatically_moving: std::collections::HashSet<WindowId>,

// Track actual user drag state (via NSWindow notifications)
user_dragging_windows: std::collections::HashSet<WindowId>,

// Track window previous positions for immediate drag detection
previous_window_positions: std::collections::HashMap<WindowId, Rect>,
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Ensure proper cleanup of tracking collections.

The new tracking fields (programmatically_moving, user_dragging_windows, previous_window_positions) are well-structured for their purposes. However, ensure these collections are properly cleaned up when windows are destroyed to prevent memory leaks.

Consider adding cleanup logic in the WindowDestroyed event handler:

 WindowEvent::WindowDestroyed(id) => {
     if let Some(window) = self.windows.remove(&id) {
+        // Clean up tracking collections
+        self.programmatically_moving.remove(&id);
+        self.user_dragging_windows.remove(&id);
+        self.previous_window_positions.remove(&id);
+        self.snap_manager.clear_drag_state(id);
         self.apply_layout().await?;
         self.plugin_manager.on_window_destroyed(&window)?;
     }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
snap_manager: SnapManager,
event_rx: mpsc::Receiver<WindowEvent>,
command_rx: mpsc::Receiver<Command>,
#[allow(dead_code)]
command_tx: mpsc::Sender<Command>,
// Drag notification system
drag_observer: WindowDragNotificationObserver,
drag_event_rx: mpsc::Receiver<WindowDragEvent>,
// Track windows being moved programmatically to avoid snap conflicts
programmatically_moving: std::collections::HashSet<WindowId>,
// Track actual user drag state (via NSWindow notifications)
user_dragging_windows: std::collections::HashSet<WindowId>,
// Track window previous positions for immediate drag detection
previous_window_positions: std::collections::HashMap<WindowId, Rect>,
WindowEvent::WindowDestroyed(id) => {
if let Some(window) = self.windows.remove(&id) {
// Clean up tracking collections
self.programmatically_moving.remove(&id);
self.user_dragging_windows.remove(&id);
self.previous_window_positions.remove(&id);
self.snap_manager.clear_drag_state(id);
self.apply_layout().await?;
self.plugin_manager.on_window_destroyed(&window)?;
}
}
🤖 Prompt for AI Agents
In src/window_manager.rs around lines 69 to 87, the new tracking collections for
window states need proper cleanup to avoid memory leaks. Update the
WindowDestroyed event handler to remove the destroyed window's ID from
programmatically_moving, user_dragging_windows, and previous_window_positions
collections. This ensures that all references to the destroyed window are
cleared promptly.

@plyght plyght closed this Jun 6, 2025
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