Skip to content

Add Writing / Shortcuts / Apps panes to redesigned Settings#366

Merged
FuJacob merged 1 commit into
mainfrom
feat/settings-redesign-writing-shortcuts-apps
May 28, 2026
Merged

Add Writing / Shortcuts / Apps panes to redesigned Settings#366
FuJacob merged 1 commit into
mainfrom
feat/settings-redesign-writing-shortcuts-apps

Conversation

@FuJacob
Copy link
Copy Markdown
Owner

@FuJacob FuJacob commented May 28, 2026

Re-opened after base branch deletion. Rebased onto current main (includes tooltip removal #362), cotabbyHelp calls stripped. Build/lint clean locally.

Greptile Summary

This PR promotes the Writing, Shortcuts, and Apps settings panes from placeholder stubs to fully implemented views, completing three legs of the redesigned Settings window. Each pane is lifted from the corresponding section of the legacy SettingsView so behavior is preserved, and SettingsContainerView simply swaps the three PlaceholderPaneView cases for the real implementations.

  • WritingPaneView and AppsPaneView are clean lifts: word-count picker, profile fields, language/rules editors, and the disabled-app list (with NSOpenPanel picker) all match the legacy code with no behavioral changes.
  • ShortcutsPaneView introduces two independent @State recording booleans. If both "Change" buttons are activated without completing the first recording, both KeyRecorderView instances install NSEvent local monitors simultaneously; LIFO ordering means the second-installed monitor captures the next key, potentially recording the wrong binding. Additionally, the KeybindRow.clearHelp parameter is accepted at every call site but never rendered anywhere in the view.

Confidence Score: 3/5

Safe to merge for most users, but the shortcut recorder can silently assign a key to the wrong binding if both Change buttons are tapped without completing the first recording.

The Writing and Apps panes are faithful, straightforward lifts with nothing new to break. The Shortcuts pane has two independent recording-state booleans with no mutual-exclusion logic; when both are true at once, the two NSEvent local monitors compete in LIFO order, and the user's intended key gets recorded by whichever row most recently started recording — not necessarily the one they were targeting. The first recorder then silently stays open until another key is pressed. This is a present, reproducible mis-assignment of a persisted user setting rather than a hypothetical edge case.

Cotabby/UI/Settings/Panes/ShortcutsPaneView.swift — specifically the dual isRecording state and the unused clearHelp parameter in KeybindRow.

Important Files Changed

Filename Overview
Cotabby/UI/Settings/Panes/ShortcutsPaneView.swift New pane for keyboard shortcut configuration; has two independent recording-state booleans that can both be true simultaneously, allowing dual NSEvent monitors to fire on the same keypress and capture the wrong binding. Also carries an unused clearHelp parameter.
Cotabby/UI/Settings/Panes/AppsPaneView.swift New pane listing and managing per-app disable rules; lifted faithfully from the legacy settings section. Icon resolution, file picker, and rule removal logic look correct.
Cotabby/UI/Settings/Panes/WritingPaneView.swift New pane for writing preferences (length, name, languages, custom rules); straightforward lift from the legacy section with no logic changes.
Cotabby/UI/Settings/SettingsContainerView.swift Replaces three PlaceholderPaneView cases with the real pane views; change is minimal and correct.
Cotabby.xcodeproj/project.pbxproj Adds the three new Swift source files to the Xcode project; straightforward build-file and file-reference additions.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    SCV[SettingsContainerView\ndetailPane switch] --> WPV[WritingPaneView\nLength · Name · Languages · Rules]
    SCV --> SPV[ShortcutsPaneView\nAccept Word · Accept Entire]
    SCV --> APV[AppsPaneView\nDisabled App Rules]

    SPV --> KR1[KeybindRow\nAccept Word]
    SPV --> KR2[KeybindRow\nAccept Entire Suggestion]

    KR1 -->|isRecording=true| KRV1[KeyRecorderView\nNSEvent monitor 1]
    KR2 -->|isRecording=true| KRV2[KeyRecorderView\nNSEvent monitor 2]

    KRV1 -.->|LIFO: fires after monitor 2| CONFLICT((⚠️ Both active\nLIFO conflict))
    KRV2 -.->|LIFO: fires first| CONFLICT

    APV --> FP[NSOpenPanel\nApp File Picker]
    APV --> RL[Disabled Rules List\nwith remove button]
Loading

Fix All in Codex Fix All in Claude Code

Reviews (1): Last reviewed commit: "Add Writing / Shortcuts / Apps panes to ..." | Re-trigger Greptile

Greptile also left 2 inline comments on this PR.

@FuJacob FuJacob merged commit 1515e2c into main May 28, 2026
4 checks passed
@FuJacob FuJacob deleted the feat/settings-redesign-writing-shortcuts-apps branch May 28, 2026 10:38
Comment on lines +11 to +12
@State private var isRecordingKeybind = false
@State private var isRecordingFullAcceptKeybind = false
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 Dual active recorders can fire on the same keypress

isRecordingKeybind and isRecordingFullAcceptKeybind are independent, so both can be true simultaneously. When that happens, both KeyRecorderView instances call NSEvent.addLocalMonitorForEvents. Local monitors fire in LIFO order, so whichever "Change" button was clicked second will capture the next key — even if the user intended it for the first row. The first KeyRecorderView then stays in "Press a key…" state until a further key is pressed, silently recording a second shortcut the user didn't intend. Setting the other flag to false (or using a single activeRecorder enum) when one starts recording would prevent this.

Fix in Codex Fix in Claude Code

Comment on lines +76 to +85
private struct KeybindRow: View {
let label: String
let keyCode: CGKeyCode
let modifiers: ShortcutModifierMask
let defaultKeyCode: CGKeyCode
@Binding var isRecording: Bool
let onRecord: (CGKeyCode, ShortcutModifierMask, String) -> Void
let onReset: () -> Void
let onClear: () -> Void
let clearHelp: String
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Unused clearHelp parameter

clearHelp: String is declared in KeybindRow's stored properties and passed by both call sites, but it is never referenced anywhere in body. The PR description notes tooltip removal happened in #362, so this was almost certainly meant to appear as .help(clearHelp) on the Clear button. As-is, the parameter is dead weight and the Clear button is left with no help text for VoiceOver or on-hover display.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Fix in Codex Fix in Claude Code

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