Skip to content

Fix tooltip tracking lifetime and shortcut lag#1043

Merged
austinywang merged 2 commits intomainfrom
issue-1036-tooltip-tracking-area-crash
Mar 7, 2026
Merged

Fix tooltip tracking lifetime and shortcut lag#1043
austinywang merged 2 commits intomainfrom
issue-1036-tooltip-tracking-area-crash

Conversation

@austinywang
Copy link
Copy Markdown
Contributor

@austinywang austinywang commented Mar 7, 2026

Summary

  • What changed?
  • Why?

Testing

  • How did you test this change?
  • What did you verify manually?

Demo Video

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

  • Video URL or attachment:

Review Trigger (Copy/Paste as PR comment)

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

Checklist

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

Summary by cubic

Fixes tooltip crashes by cleaning up tracking areas and switching all tooltips to safeHelp. Also reduces shortcut lag by focusing the right window only when needed.

  • Bug Fixes

    • Remove tracking areas in deinit and clear divider cursors to prevent dangling pointers (browser/terminal host views, web view, Ghostty view).
    • Replace .help with .safeHelp across UI (search overlays, browser toolbar, sidebar, notifications, update pill/titlebar); update Bonsplit submodule.
    • Addresses the tooltip tracking area crash in issue-1036.
  • Performance

    • Add prepareWindowForSyntheticInput and use it in simulate_shortcut and activateApp to avoid repeated app activation/order-front.
    • Keep timestamps stable and reuse existing shortcut handling to reduce perceived lag.

Written for commit 01e9a05. Summary will update on new commits.

Summary by CodeRabbit

  • Bug Fixes

    • Improved help text and tooltip behavior across the UI by switching to a safer tooltip mechanism.
    • Enhanced resource cleanup during view teardown to avoid leftover tracking state and cursor issues.
    • Made synthetic keyboard input more reliable by ensuring target windows are prepared before dispatch.
  • Chores

    • Updated a bundled third‑party component reference.

@vercel
Copy link
Copy Markdown

vercel bot commented Mar 7, 2026

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

Project Deployment Actions Updated (UTC)
cmux Ready Ready Preview, Comment Mar 7, 2026 11:07am

@chatgpt-codex-connector
Copy link
Copy Markdown

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

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 7, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7de67d12-3b61-40cd-a23b-37c5f9ff7262

📥 Commits

Reviewing files that changed from the base of the PR and between 9e8a401 and 01e9a05.

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

📝 Walkthrough

Walkthrough

Migrates .help to .safeHelp across multiple views, adds deinitializers to remove NSTrackingArea and clear divider cursor state in several host views, refactors synthetic input preparation in TerminalController via a new helper, and updates the Bonsplit submodule pointer.

Changes

Cohort / File(s) Summary
Help Text Modifier Migration
Sources/ContentView.swift, Sources/Find/BrowserSearchOverlay.swift, Sources/Find/SurfaceSearchOverlay.swift, Sources/NotificationsPage.swift, Sources/Panels/BrowserPanelView.swift, Sources/Update/UpdatePill.swift, Sources/Update/UpdateTitlebarAccessory.swift
Replaced .help(...) with .safeHelp(...) across UI elements; no control-flow or logic changes.
Resource Cleanup Deinitializers
Sources/BrowserWindowPortal.swift, Sources/GhosttyTerminalView.swift, Sources/Panels/BrowserPanelView.swift, Sources/TerminalWindowPortal.swift
Added deinit implementations that remove NSTrackingArea if present and call clearActiveDividerCursor(restoreArrow: false) to ensure tracking/cursor cleanup on deallocation.
Synthetic Input Handling Optimization
Sources/TerminalController.swift
Added prepareWindowForSyntheticInput(_:) and refactored simulateShortcut/typing flows to centralize window activation/visibility handling before dispatching synthetic events.
Dependency Update
vendor/bonsplit
Updated Bonsplit submodule reference (commit moved); no code changes within the submodule.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰 I hopped through code at break of dawn,
Swapped old help leaves for a safer one,
Cleared tracking trails the moment they part,
Polished cursors, primed each window's heart,
Now inputs bounce true — a tidy start. ✨

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description uses the required template structure but lacks substantive details in all key sections including Summary, Testing, and Demo Video. Fill in the Summary section with what changed and why, add Testing details about manual verification, and include a demo video link or note if UI changes are present.
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 (1 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the two main changes in the PR: fixing tooltip tracking lifetime issues and reducing shortcut lag.

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch issue-1036-tooltip-tracking-area-crash

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

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 7, 2026

Greptile Summary

This PR addresses two distinct bugs: orphaned NSTrackingArea instances causing tooltip lifecycle issues, and unnecessary per-keystroke window activation overhead introducing shortcut simulation lag.

Tooltip tracking lifetime fix:

  • Adds deinit to three NSView subclasses (WindowBrowserHostView, WindowTerminalHostView, and the inner WebViewRepresentable host view) to explicitly call removeTrackingArea before deallocation. Without this, a tracking area whose owner is a deallocated view can continue to receive mouse events and send them to freed memory.
  • Migrates all .help(...) SwiftUI modifier calls (20+ sites across 9 files) to the new .safeHelp(...) modifier introduced in the updated bonsplit submodule. This companion fix addresses the same tracking-area lifetime problem at the SwiftUI layer, where the standard .help() modifier may register tracking areas that outlive their host view.

Shortcut simulation lag fix:

  • Extracts prepareWindowForSyntheticInput(_:) helper in TerminalController, which conditionally calls NSApp.activate and makeKeyAndOrderFront only when the app is not already active or the window is not already the key window. The previous code called both unconditionally on every synthetic key event, which introduced perceptible lag during automated shortcut replay.

Key changes by file:

  • BrowserWindowPortal.swift / TerminalWindowPortal.swift / BrowserPanelView.swiftdeinit tracking area cleanup
  • GhosttyTerminalView.swift — tracking area removal added to existing deinit
  • TerminalController.swiftprepareWindowForSyntheticInput refactor (also fixes mixed-tab indentation)
  • ContentView.swift, NotificationsPage.swift, UpdatePill.swift, UpdateTitlebarAccessory.swift, BrowserSearchOverlay.swift, SurfaceSearchOverlay.swift.safeHelp(...) migration
  • vendor/bonsplit — submodule bump to commit introducing safeHelp

Confidence Score: 4/5

  • Safe to merge — the changes are targeted cleanups with no functional regressions expected, but the PR description is entirely unfilled and the safeHelp implementation is only visible through the submodule bump.
  • All three changed areas (tracking area deinit, safeHelp migration, prepareWindowForSyntheticInput) are defensive improvements with clear intent. The deinit additions follow the same pattern as existing cleanup code in updateTrackingAreas. The prepareWindowForSyntheticInput refactor preserves the exact previous semantics while skipping redundant AppKit calls. The only deduction from a perfect score is the empty PR description (no testing details, no demo video) and the fact that the safeHelp implementation is in an opaque submodule bump, making independent verification of the fix harder.
  • No files require special attention, but reviewers should verify the vendor/bonsplit submodule diff independently to confirm the safeHelp implementation is correct.

Important Files Changed

Filename Overview
Sources/BrowserWindowPortal.swift Adds deinit to WindowBrowserHostView to remove the NSTrackingArea and clear the active divider cursor on deallocation, preventing orphaned tracking areas from sending events to a deallocated view.
Sources/GhosttyTerminalView.swift Adds removeTrackingArea(trackingArea) in deinit of GhosttyNSView, mirroring the pattern already used in updateTrackingAreas, to ensure the tracking area is torn down when the view is deallocated.
Sources/TerminalWindowPortal.swift Adds deinit to WindowTerminalHostView to remove the tracking area and clear the divider cursor, consistent with the same fix applied to the browser-side portal view.
Sources/Panels/BrowserPanelView.swift Adds deinit to the inner WebViewRepresentable coordinator/host NSView (same tracking area cleanup pattern) and migrates all .help(...) calls to .safeHelp(...).
Sources/TerminalController.swift Extracts prepareWindowForSyntheticInput helper to conditionally call NSApp.activate and makeKeyAndOrderFront only when truly needed, reducing per-keystroke activation overhead and latency in simulateShortcut and simulateType. Also fixes mixed-tab indentation.
Sources/ContentView.swift Migrates 8 .help(...) calls to .safeHelp(...) throughout ContentView to adopt the new Bonsplit-provided modifier with proper tooltip tracking lifetime management.
vendor/bonsplit Bumps the bonsplit submodule from 89a4fd1 to c5b3dd4, which is presumed to introduce the safeHelp View modifier and its tooltip tracking lifetime fix.

Sequence Diagram

sequenceDiagram
    participant ST as Socket Thread
    participant Main as Main Thread (DispatchQueue.main.sync)
    participant App as NSApp
    participant Win as NSWindow

    Note over ST,Win: simulateShortcut / simulateType

    ST->>Main: DispatchQueue.main.sync { ... }
    Main->>Main: Resolve targetWindow
    Main->>Main: prepareWindowForSyntheticInput(targetWindow)
    alt App not active
        Main->>App: NSApp.activate(ignoringOtherApps: true)
    end
    alt Window not key OR not visible
        Main->>Win: window.makeKeyAndOrderFront(nil)
    end
    Main->>App: NSApp.sendEvent(keyDownEvent)
    Main->>App: NSApp.sendEvent(keyUpEvent)
    ST-->>ST: result = "OK"

    Note over ST,Win: Before this PR: activate + makeKeyAndOrderFront called unconditionally every time
    Note over ST,Win: After this PR: called only when truly needed → reduced lag
Loading

Last reviewed commit: 9e8a401

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.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
Sources/Find/BrowserSearchOverlay.swift (1)

76-98: ⚠️ Potential issue | 🟡 Minor

Localize the new tooltip strings.

Lines 76, 87, and 98 add user-visible help text as bare literals. Please wrap these in String(localized:..., defaultValue:...) before passing them to .safeHelp(...).

Suggested fix
-                .safeHelp("Next match (Return)")
+                .safeHelp(String(localized: "browserSearchOverlay.nextMatch.help", defaultValue: "Next match (Return)"))-                .safeHelp("Previous match (Shift+Return)")
+                .safeHelp(String(localized: "browserSearchOverlay.previousMatch.help", defaultValue: "Previous match (Shift+Return)"))-                .safeHelp("Close (Esc)")
+                .safeHelp(String(localized: "browserSearchOverlay.close.help", defaultValue: "Close (Esc)"))

As per coding guidelines "All user-facing strings must be localized using String(localized: "key.name", defaultValue: "English text") for every string shown in the UI" and "Never use bare string literals in SwiftUI Text(), Button(), alert titles, or other user-visible UI elements; all must use the String(localized:) API".

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

In `@Sources/Find/BrowserSearchOverlay.swift` around lines 76 - 98, Replace the
three bare help-string literals passed to .safeHelp(...) with localized variants
using String(localized:..., defaultValue:...), e.g. for the .safeHelp("Next
match (Return)"), .safeHelp("Previous match (Shift+Return)"), and
.safeHelp("Close (Esc)") calls, call String(localized: "find.nextMatch.help",
defaultValue: "Next match (Return)"), String(localized:
"find.previousMatch.help", defaultValue: "Previous match (Shift+Return)"), and
String(localized: "find.close.help", defaultValue: "Close (Esc)") respectively
so the help text shown by Button / .safeHelp uses the String(localized:...,
defaultValue:...) API.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@Sources/Panels/BrowserPanelView.swift`:
- Line 627: Replace the hard-coded tooltip string passed to .safeHelp(...) with
a localized string key; e.g. use String(localized: "browser.theme.tooltip",
defaultValue: "Browser Theme: %@").formatted(browserThemeMode.displayName) so
the wrapper text is localized while still including
browserThemeMode.displayName. Update the .safeHelp call to use that
localized-formatted string (referencing browserThemeMode.displayName and the
.safeHelp modifier in BrowserPanelView) and add the "browser.theme.tooltip" key
to localization files.

In `@Sources/TerminalController.swift`:
- Around line 10254-10325: Add a DEBUG-only dlog() call in simulateShortcut
after preparing the targetWindow and creating keyDownEvent/keyUpEvent (and
before returning) to record the synthetic shortcut activity: include
parsed.characters, parsed.charactersIgnoringModifiers, parsed.keyCode,
parsed.modifierFlags, requestTimestamp, windowNumber
(targetWindow?.windowNumber), and whether
AppDelegate.shared?.debugHandleCustomShortcut handled the event; place the dlog
invocation near the block that checks AppDelegate.debugHandleCustomShortcut and
before NSApp.sendEvent so both handled and dispatched cases are logged; use the
existing symbols simulateShortcut, prepareWindowForSyntheticInput, keyDownEvent,
keyUpEvent, and AppDelegate.debugHandleCustomShortcut to locate where to insert
the call.
- Around line 10241-10252: prepareWindowForSyntheticInput currently activates
the app and raises the window which steals macOS focus; change it to not mutate
focus: in prepareWindowForSyntheticInput(_ window: NSWindow?) (the helper used
by simulate_shortcut and simulate_type) remove the
NSApp.activate(ignoringOtherApps:) and window.makeKeyAndOrderFront(nil) calls
and instead simply check guard let window else { return } plus require the app
to already be active and the window to be key and visible—if not, return/fail
early (or log/throw) so synthetic input does not force activation; ensure
callers (simulate_shortcut/simulate_type) rely on an explicit activate_app
command when focus is intended.

---

Outside diff comments:
In `@Sources/Find/BrowserSearchOverlay.swift`:
- Around line 76-98: Replace the three bare help-string literals passed to
.safeHelp(...) with localized variants using String(localized:...,
defaultValue:...), e.g. for the .safeHelp("Next match (Return)"),
.safeHelp("Previous match (Shift+Return)"), and .safeHelp("Close (Esc)") calls,
call String(localized: "find.nextMatch.help", defaultValue: "Next match
(Return)"), String(localized: "find.previousMatch.help", defaultValue: "Previous
match (Shift+Return)"), and String(localized: "find.close.help", defaultValue:
"Close (Esc)") respectively so the help text shown by Button / .safeHelp uses
the String(localized:..., defaultValue:...) API.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 66ae2e88-205d-4551-91d3-d737e4f0bc60

📥 Commits

Reviewing files that changed from the base of the PR and between 58bcc92 and 9e8a401.

📒 Files selected for processing (12)
  • Sources/BrowserWindowPortal.swift
  • Sources/ContentView.swift
  • Sources/Find/BrowserSearchOverlay.swift
  • Sources/Find/SurfaceSearchOverlay.swift
  • Sources/GhosttyTerminalView.swift
  • Sources/NotificationsPage.swift
  • Sources/Panels/BrowserPanelView.swift
  • Sources/TerminalController.swift
  • Sources/TerminalWindowPortal.swift
  • Sources/Update/UpdatePill.swift
  • Sources/Update/UpdateTitlebarAccessory.swift
  • vendor/bonsplit

browserThemeModePopover
}
.help("Browser Theme: \(browserThemeMode.displayName)")
.safeHelp("Browser Theme: \(browserThemeMode.displayName)")
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Localize the browser theme tooltip wrapper string.

"Browser Theme: ..." is still a user-visible literal, so this tooltip stays English-only even if browserThemeMode.displayName is localized.

Suggested fix
-        .safeHelp("Browser Theme: \(browserThemeMode.displayName)")
+        .safeHelp(
+            String(
+                localized: "browser.theme.tooltip",
+                defaultValue: "Browser Theme: \(browserThemeMode.displayName)"
+            )
+        )

As per coding guidelines, "All user-facing strings must be localized using String(localized: "key.name", defaultValue: "English text")" and "Never use bare string literals in SwiftUI Text(), Button(), alert titles, or other user-visible UI elements; all must use the String(localized:) API".

📝 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
.safeHelp("Browser Theme: \(browserThemeMode.displayName)")
.safeHelp(
String(
localized: "browser.theme.tooltip",
defaultValue: "Browser Theme: \(browserThemeMode.displayName)"
)
)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Sources/Panels/BrowserPanelView.swift` at line 627, Replace the hard-coded
tooltip string passed to .safeHelp(...) with a localized string key; e.g. use
String(localized: "browser.theme.tooltip", defaultValue: "Browser Theme:
%@").formatted(browserThemeMode.displayName) so the wrapper text is localized
while still including browserThemeMode.displayName. Update the .safeHelp call to
use that localized-formatted string (referencing browserThemeMode.displayName
and the .safeHelp modifier in BrowserPanelView) and add the
"browser.theme.tooltip" key to localization files.

Comment on lines +10241 to +10252
private func prepareWindowForSyntheticInput(_ window: NSWindow?) {
guard let window else { return }

// Stamp at socket-handler arrival so event.timestamp includes any wait
// before the main-thread event dispatch.
let requestTimestamp = ProcessInfo.processInfo.systemUptime

var result = "ERROR: Failed to create event"
DispatchQueue.main.sync {
// Prefer the current active-tab-manager window so shortcut simulation stays
// scoped to the intended window even when NSApp.keyWindow is stale.
let targetWindow: NSWindow? = {
if let activeTabManager = self.tabManager,
let windowId = AppDelegate.shared?.windowId(for: activeTabManager),
let window = AppDelegate.shared?.mainWindow(for: windowId) {
return window
}
return NSApp.keyWindow
?? NSApp.mainWindow
?? NSApp.windows.first(where: { $0.isVisible })
?? NSApp.windows.first
}()
if let targetWindow {
NSApp.activate(ignoringOtherApps: true)
targetWindow.makeKeyAndOrderFront(nil)
}
let windowNumber = targetWindow?.windowNumber ?? 0
guard let keyDownEvent = NSEvent.keyEvent(
with: .keyDown,
location: .zero,
modifierFlags: parsed.modifierFlags,
timestamp: requestTimestamp,
windowNumber: windowNumber,
context: nil,
characters: parsed.characters,
charactersIgnoringModifiers: parsed.charactersIgnoringModifiers,
isARepeat: false,
keyCode: parsed.keyCode
) else {
result = "ERROR: NSEvent.keyEvent returned nil"
return
}
let keyUpEvent = NSEvent.keyEvent(
with: .keyUp,
location: .zero,
modifierFlags: parsed.modifierFlags,
timestamp: requestTimestamp + 0.0001,
windowNumber: windowNumber,
context: nil,
characters: parsed.characters,
charactersIgnoringModifiers: parsed.charactersIgnoringModifiers,
isARepeat: false,
keyCode: parsed.keyCode
)
// Socket-driven shortcut simulation should reuse the exact same matching logic as the
// app-level shortcut monitor (so tests are hermetic), while still falling back to the
// normal responder chain for plain typing.
if let delegate = AppDelegate.shared, delegate.debugHandleCustomShortcut(event: keyDownEvent) {
result = "OK"
return
}
NSApp.sendEvent(keyDownEvent)
if let keyUpEvent {
NSApp.sendEvent(keyUpEvent)
}
result = "OK"
}
return result
}
// Keep socket-driven input simulation focused on the intended window without
// paying repeated activation/order-front costs for every synthetic key event.
if !NSApp.isActive {
NSApp.activate(ignoringOtherApps: true)
}
if !window.isKeyWindow || !window.isVisible {
window.makeKeyAndOrderFront(nil)
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Don't activate or raise the app from this helper.

prepareWindowForSyntheticInput now calls NSApp.activate and window.makeKeyAndOrderFront, so both simulate_shortcut and the Line 10370 simulate_type path will steal macOS/app focus even though these are not explicit focus-intent socket commands. Prefer failing when the target window is not already ready, or require tests to call the explicit activate_app command first.

As per coding guidelines, "Socket/CLI commands must not steal macOS app focus (no app activation/window raising side effects)" and "Only explicit focus-intent commands may mutate in-app focus/selection (window.focus, workspace.select/next/previous/last, surface.focus, pane.focus/last, browser focus commands, and v1 focus equivalents)`."

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

In `@Sources/TerminalController.swift` around lines 10241 - 10252,
prepareWindowForSyntheticInput currently activates the app and raises the window
which steals macOS focus; change it to not mutate focus: in
prepareWindowForSyntheticInput(_ window: NSWindow?) (the helper used by
simulate_shortcut and simulate_type) remove the
NSApp.activate(ignoringOtherApps:) and window.makeKeyAndOrderFront(nil) calls
and instead simply check guard let window else { return } plus require the app
to already be active and the window to be key and visible—if not, return/fail
early (or log/throw) so synthetic input does not force activation; ensure
callers (simulate_shortcut/simulate_type) rely on an explicit activate_app
command when focus is intended.

Comment on lines +10254 to +10325
private func simulateShortcut(_ args: String) -> String {
let combo = args.trimmingCharacters(in: .whitespacesAndNewlines)
guard !combo.isEmpty else {
return "ERROR: Usage: simulate_shortcut <combo>"
}
guard let parsed = parseShortcutCombo(combo) else {
return "ERROR: Invalid combo. Example: cmd+ctrl+h"
}

// Stamp at socket-handler arrival so event.timestamp includes any wait
// before the main-thread event dispatch.
let requestTimestamp = ProcessInfo.processInfo.systemUptime

var result = "ERROR: Failed to create event"
DispatchQueue.main.sync {
// Prefer the current active-tab-manager window so shortcut simulation stays
// scoped to the intended window even when NSApp.keyWindow is stale.
let targetWindow: NSWindow? = {
if let activeTabManager = self.tabManager,
let windowId = AppDelegate.shared?.windowId(for: activeTabManager),
let window = AppDelegate.shared?.mainWindow(for: windowId) {
return window
}
return NSApp.keyWindow
?? NSApp.mainWindow
?? NSApp.windows.first(where: { $0.isVisible })
?? NSApp.windows.first
}()
prepareWindowForSyntheticInput(targetWindow)
let windowNumber = targetWindow?.windowNumber ?? 0
guard let keyDownEvent = NSEvent.keyEvent(
with: .keyDown,
location: .zero,
modifierFlags: parsed.modifierFlags,
timestamp: requestTimestamp,
windowNumber: windowNumber,
context: nil,
characters: parsed.characters,
charactersIgnoringModifiers: parsed.charactersIgnoringModifiers,
isARepeat: false,
keyCode: parsed.keyCode
) else {
result = "ERROR: NSEvent.keyEvent returned nil"
return
}
let keyUpEvent = NSEvent.keyEvent(
with: .keyUp,
location: .zero,
modifierFlags: parsed.modifierFlags,
timestamp: requestTimestamp + 0.0001,
windowNumber: windowNumber,
context: nil,
characters: parsed.characters,
charactersIgnoringModifiers: parsed.charactersIgnoringModifiers,
isARepeat: false,
keyCode: parsed.keyCode
)
// Socket-driven shortcut simulation should reuse the exact same matching logic as the
// app-level shortcut monitor (so tests are hermetic), while still falling back to the
// normal responder chain for plain typing.
if let delegate = AppDelegate.shared, delegate.debugHandleCustomShortcut(event: keyDownEvent) {
result = "OK"
return
}
NSApp.sendEvent(keyDownEvent)
if let keyUpEvent {
NSApp.sendEvent(keyUpEvent)
}
result = "OK"
}
return result
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Add a dlog() entry for the synthetic shortcut path.

This code now prepares window focus state and injects synthetic keyDown/keyUp events, but it still emits no unified debug-event log entry for the actual key/focus activity. That makes shortcut-lag regressions much harder to trace in DEBUG builds.

As per coding guidelines, "All debug events (keys, mouse, focus, splits, tabs) must be logged to the unified debug event log in DEBUG builds using the dlog() free function."

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

In `@Sources/TerminalController.swift` around lines 10254 - 10325, Add a
DEBUG-only dlog() call in simulateShortcut after preparing the targetWindow and
creating keyDownEvent/keyUpEvent (and before returning) to record the synthetic
shortcut activity: include parsed.characters,
parsed.charactersIgnoringModifiers, parsed.keyCode, parsed.modifierFlags,
requestTimestamp, windowNumber (targetWindow?.windowNumber), and whether
AppDelegate.shared?.debugHandleCustomShortcut handled the event; place the dlog
invocation near the block that checks AppDelegate.debugHandleCustomShortcut and
before NSApp.sendEvent so both handled and dispatched cases are logged; use the
existing symbols simulateShortcut, prepareWindowForSyntheticInput, keyDownEvent,
keyUpEvent, and AppDelegate.debugHandleCustomShortcut to locate where to insert
the call.

Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

2 issues found across 12 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="Sources/Find/BrowserSearchOverlay.swift">

<violation number="1" location="Sources/Find/BrowserSearchOverlay.swift:76">
P3: Localize this tooltip string instead of using a raw literal so it can be translated in non-English locales.</violation>
</file>

<file name="Sources/Panels/BrowserPanelView.swift">

<violation number="1" location="Sources/Panels/BrowserPanelView.swift:627">
P3: Localize this tooltip string instead of hardcoding English text.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

}
.buttonStyle(SearchButtonStyle())
.help("Next match (Return)")
.safeHelp("Next match (Return)")
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot Mar 7, 2026

Choose a reason for hiding this comment

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

P3: Localize this tooltip string instead of using a raw literal so it can be translated in non-English locales.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At Sources/Find/BrowserSearchOverlay.swift, line 76:

<comment>Localize this tooltip string instead of using a raw literal so it can be translated in non-English locales.</comment>

<file context>
@@ -73,7 +73,7 @@ struct BrowserSearchOverlay: View {
                 }
                 .buttonStyle(SearchButtonStyle())
-                .help("Next match (Return)")
+                .safeHelp("Next match (Return)")
 
                 Button(action: {
</file context>
Suggested change
.safeHelp("Next match (Return)")
.safeHelp(String(localized: "search.nextMatch.help", defaultValue: "Next match (Return)"))
Fix with Cubic

browserThemeModePopover
}
.help("Browser Theme: \(browserThemeMode.displayName)")
.safeHelp("Browser Theme: \(browserThemeMode.displayName)")
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot Mar 7, 2026

Choose a reason for hiding this comment

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

P3: Localize this tooltip string instead of hardcoding English text.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At Sources/Panels/BrowserPanelView.swift, line 627:

<comment>Localize this tooltip string instead of hardcoding English text.</comment>

<file context>
@@ -624,7 +624,7 @@ struct BrowserPanelView: View {
             browserThemeModePopover
         }
-        .help("Browser Theme: \(browserThemeMode.displayName)")
+        .safeHelp("Browser Theme: \(browserThemeMode.displayName)")
         .accessibilityIdentifier("BrowserThemeModeButton")
     }
</file context>
Fix with Cubic

…acking-area-crash

# Conflicts:
#	Sources/ContentView.swift
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