From af32a36a9191e7343f01637fe4596f8b67e03250 Mon Sep 17 00:00:00 2001 From: TZGyn Date: Wed, 12 Aug 2026 16:44:56 +0800 Subject: [PATCH] Add Copy Diagnostics menu item for multi-display debugging MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Prints each NSScreen's frame/backing scale/CG display ID and each SCDisplay's frame/point size, and copies it to the clipboard, so secondary-display capture failures can be debugged from the menu bar. 💘 Generated with Crush Assisted-by: Crush:deepseek-v4-flash-0731 --- Sources/Kapture/App.swift | 7 ++++++ Sources/Kapture/CaptureController.swift | 32 +++++++++++++++++++++++++ build-app.sh | 4 ++-- 3 files changed, 41 insertions(+), 2 deletions(-) diff --git a/Sources/Kapture/App.swift b/Sources/Kapture/App.swift index 0e94f23..c336588 100644 --- a/Sources/Kapture/App.swift +++ b/Sources/Kapture/App.swift @@ -48,6 +48,13 @@ struct MenuView: View { Divider() + Button("Copy Diagnostics") { + let diag = CaptureController.shared.diagnostics() + let pasteboard = NSPasteboard.general + pasteboard.clearContents() + pasteboard.setString(diag, forType: .string) + } + Button(LoginItem.isEnabled ? "Disable Launch at Login" : "Launch at Login") { if !LoginItem.toggle() { let alert = NSAlert() diff --git a/Sources/Kapture/CaptureController.swift b/Sources/Kapture/CaptureController.swift index de16215..6db5f73 100644 --- a/Sources/Kapture/CaptureController.swift +++ b/Sources/Kapture/CaptureController.swift @@ -10,6 +10,38 @@ final class CaptureController: NSObject { private var previousApp: NSRunningApplication? private var overlayIDs: [Int] = [] + /// Diagnostic snapshot for "Copy Diagnostics" menu item. + func diagnostics() -> String { + var lines: [String] = [] + lines.append("Kapture diagnostics") + for (i, s) in NSScreen.screens.enumerated() { + let scale = s.backingScaleFactor + let cgID = (s.deviceDescription[NSDeviceDescriptionKey("NSScreenNumber")] as? NSNumber)?.uint32Value ?? 0 + lines.append("screen[\(i)]: frame=\(s.frame) scale=\(scale) backingSize=\(s.frame.width*scale)x\(s.frame.height*scale) cgID=\(cgID)") + } + if #available(macOS 14.0, *) { + Task { + if let content = try? await SCShareableContent.current { + for (i, d) in content.displays.enumerated() { + lines.append("SCDisplay[\(i)]: frame=\(d.frame) width=\(d.width)x\(d.height) id=\(d.displayID)") + } + } else { + lines.append("SCShareableContent: unavailable (no Screen Recording permission?)") + } + let text = lines.joined(separator: "\n") + let pb = NSPasteboard.general + pb.clearContents() + pb.setString(text, forType: .string) + } + } else { + let text = lines.joined(separator: "\n") + let pb = NSPasteboard.general + pb.clearContents() + pb.setString(text, forType: .string) + } + return lines.joined(separator: "\n") + } + func begin() { guard !isSelecting else { return } guard PermissionManager.isGranted else { diff --git a/build-app.sh b/build-app.sh index 93a1f8a..3180131 100755 --- a/build-app.sh +++ b/build-app.sh @@ -108,9 +108,9 @@ cat > "$APP/Contents/Info.plist" <<'PLIST' CFBundleIconFile Icon CFBundleShortVersionString - 1.1.5 + 1.1.6 CFBundleVersion - 15 + 16 LSUIElement NSHighResolutionCapable