Skip to content

feat: dynamic per-tab settings window sizing#330

Closed
datlechin wants to merge 4 commits intomainfrom
worktree-settings-window-sizing
Closed

feat: dynamic per-tab settings window sizing#330
datlechin wants to merge 4 commits intomainfrom
worktree-settings-window-sizing

Conversation

@datlechin
Copy link
Copy Markdown
Collaborator

@datlechin datlechin commented Mar 15, 2026

Summary

  • Replace the fixed 720×500 settings window frame with per-tab dynamic sizing
  • Add SettingsWindowResizer (NSViewRepresentable) that animates the NSWindow size on tab change, keeping the top-left corner pinned (standard macOS preferences behavior)
  • Form-based tabs (General, Editor, History, etc.) shrink to ~450px wide; HSplitView tabs (Appearance, Plugins) get the width they need

Test plan

  • Open Settings (Cmd+,), click through each tab — window should animate smoothly to different sizes
  • Verify top-left corner stays pinned during resize
  • Close and reopen Settings — should open at correct size for the remembered tab
  • Verify Appearance and Plugins HSplitView content is not clipped
  • Verify compact form tabs (General, Editor, License) are not wastefully wide

Summary by CodeRabbit

New Features

  • Settings window now dynamically resizes based on the selected tab, with smooth transitions and optimized layouts for each settings section.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 15, 2026

Warning

Rate limit exceeded

@datlechin has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 8 minutes and 40 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: bae79a97-5543-4289-b478-1a60edc56375

📥 Commits

Reviewing files that changed from the base of the PR and between 6b5f3f4 and 01203e9.

📒 Files selected for processing (2)
  • TablePro/Views/Settings/SettingsView.swift
  • TablePro/Views/Settings/SettingsWindowResizer.swift
📝 Walkthrough

Walkthrough

The PR introduces dynamic window sizing for the Settings window based on selected tabs. A new preferredSize property on the SettingsTab enum specifies dimensions per tab. The new SettingsWindowResizer (NSViewRepresentable) applies these sizes to the window with smooth transitions while preserving the top-left anchor.

Changes

Cohort / File(s) Summary
Settings View Enhancements
TablePro/Views/Settings/SettingsView.swift
Added preferredSize computed property to SettingsTab enum mapping tabs to their preferred window dimensions. Introduced currentTab computed property to track the selected tab with fallback to .general. Replaced static frame and background with dynamic sizing driven by currentTab.preferredSize and SettingsWindowResizer.
Window Resizing Integration
TablePro/Views/Settings/SettingsWindowResizer.swift
New NSViewRepresentable struct that handles dynamic Settings window resizing. Computes adjusted window frames, applies smooth animations, and constrains min/max sizes. Includes private _SettingsWindowSizeView helper class to trigger layout on attachment.

Sequence Diagram

sequenceDiagram
    participant User
    participant SettingsView as SettingsView
    participant SettingsTab as SettingsTab
    participant SettingsWindowResizer as SettingsWindowResizer
    participant NSWindow as NSWindow / AppKit

    User->>SettingsView: Select different tab
    SettingsView->>SettingsView: Update selectedTab state
    SettingsView->>SettingsTab: Resolve currentTab
    SettingsTab-->>SettingsView: Return SettingsTab enum value
    SettingsView->>SettingsTab: Read preferredSize property
    SettingsTab-->>SettingsView: Return CGSize for tab
    SettingsView->>SettingsWindowResizer: Provide size parameter
    SettingsWindowResizer->>SettingsWindowResizer: updateNSView called
    SettingsWindowResizer->>NSWindow: Calculate new frame (top-left anchor preserved)
    SettingsWindowResizer->>NSWindow: Apply smooth animation (if visible)
    SettingsWindowResizer->>NSWindow: Update minSize & maxSize constraints
    NSWindow-->>User: Window resizes to preferred dimensions
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 The bunny hops through tabs with glee,
Each window size, a perfect spree!
Smooth resizing, frame by frame,
Top-left anchored, never the same!
Dynamic settings, hopping with care,

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: dynamic per-tab settings window sizing' directly and specifically describes the main change: implementing dynamic, per-tab sizing for the settings window instead of a fixed size.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch worktree-settings-window-sizing
📝 Coding Plan
  • Generate coding plan for human review comments

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

Copy link
Copy Markdown

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

🧹 Nitpick comments (2)
TablePro/Views/Settings/SettingsWindowResizer.swift (2)

12-13: Add explicit access control on the new type (and keep size immutable).

Line 12 introduces a new type without an explicit access modifier. Also, size does not need to be mutable.

♻️ Proposed update
-struct SettingsWindowResizer: NSViewRepresentable {
-    var size: CGSize
+internal struct SettingsWindowResizer: NSViewRepresentable {
+    let size: CGSize

As per coding guidelines, "Always specify access control explicitly (private, internal, public) on extensions and types. Specify on the extension itself, not on individual members."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@TablePro/Views/Settings/SettingsWindowResizer.swift` around lines 12 - 13,
Add an explicit access control modifier to the new SettingsWindowResizer type
(e.g., make the struct internal or private per the module visibility
requirements) and make its size property immutable by changing var size to let
size; update any callers if needed to match the chosen access level and ensure
the type-level access modifier is used on the struct declaration (not on
individual members).

20-31: Avoid redundant frame updates when size constraints are already satisfied.

updateNSView currently re-applies frame and constraints every update pass. Add a fast-path return to avoid unnecessary AppKit work.

⚡ Proposed update
     func updateNSView(_ nsView: NSView, context: Context) {
         guard let window = nsView.window else { return }
         let contentSize = size
         let newFrameSize = window.frameRect(forContentRect: NSRect(origin: .zero, size: contentSize)).size
+        if window.frame.size == newFrameSize,
+           window.minSize == newFrameSize,
+           window.maxSize == newFrameSize {
+            return
+        }
         var frame = window.frame
         // Pin top-left corner
         frame.origin.y += frame.size.height - newFrameSize.height
         frame.size = newFrameSize
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@TablePro/Views/Settings/SettingsWindowResizer.swift` around lines 20 - 31,
updateNSView is re-setting the window frame and min/max sizes on every call; add
a fast-path that returns early when no change is required by comparing the
computed newFrameSize and current window.frame.size and current constraints: if
newFrameSize equals window.frame.size (or window.frameRect(forContentRect:)
would result in the same outer frame) and window.minSize == newFrameSize and
window.maxSize == newFrameSize then exit the method without calling
window.setFrame or re-assigning min/max sizes; keep the existing top-left
pinning logic but only perform it when a real size change is needed (refer to
updateNSView, newFrameSize, frame, window.minSize and window.maxSize).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@TablePro/Views/Settings/SettingsWindowResizer.swift`:
- Around line 12-13: Add an explicit access control modifier to the new
SettingsWindowResizer type (e.g., make the struct internal or private per the
module visibility requirements) and make its size property immutable by changing
var size to let size; update any callers if needed to match the chosen access
level and ensure the type-level access modifier is used on the struct
declaration (not on individual members).
- Around line 20-31: updateNSView is re-setting the window frame and min/max
sizes on every call; add a fast-path that returns early when no change is
required by comparing the computed newFrameSize and current window.frame.size
and current constraints: if newFrameSize equals window.frame.size (or
window.frameRect(forContentRect:) would result in the same outer frame) and
window.minSize == newFrameSize and window.maxSize == newFrameSize then exit the
method without calling window.setFrame or re-assigning min/max sizes; keep the
existing top-left pinning logic but only perform it when a real size change is
needed (refer to updateNSView, newFrameSize, frame, window.minSize and
window.maxSize).

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 22b17cc0-09ae-43aa-b2d2-5f7dc11c4199

📥 Commits

Reviewing files that changed from the base of the PR and between 49f3d7b and 6b5f3f4.

📒 Files selected for processing (2)
  • TablePro/Views/Settings/SettingsView.swift
  • TablePro/Views/Settings/SettingsWindowResizer.swift

@datlechin datlechin closed this Mar 15, 2026
@datlechin datlechin deleted the worktree-settings-window-sizing branch March 15, 2026 10:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant