diff --git a/XRPC.xcworkspace/xcuserdata/llsc12.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/XRPC.xcworkspace/xcuserdata/llsc12.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist
index 083d04e..7923f8b 100644
--- a/XRPC.xcworkspace/xcuserdata/llsc12.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist
+++ b/XRPC.xcworkspace/xcuserdata/llsc12.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist
@@ -3,4 +3,22 @@
uuid = "56FD24EC-8B81-45C4-A37F-8A8B759FA931"
type = "0"
version = "2.0">
+
+
+
+
+
+
diff --git a/XRPC/AXScrape.swift b/XRPC/AXScrape.swift
index ab79f53..a292d97 100644
--- a/XRPC/AXScrape.swift
+++ b/XRPC/AXScrape.swift
@@ -52,18 +52,53 @@ class AXScrape: ObservableObject {
let doc: URL? = docFilePath == nil ? nil : URL(fileURLWithPath: docFilePath!.replacingOccurrences(of: "file://", with: ""))
let currentSessionDate: Date? = {
- switch presenceState {
- case .working(let xcodeState):
+ if case .working(let xcodeState) = presenceState {
return xcodeState.sessionDate
- default: return nil
}
+ return nil
}()
+ var warnings: Int, errors: Int, issues: Int
+ warnings = 0
+ errors = 0
+ issues = 0
+
+ // we need to get the ui elements for the header bar of xcode since thats where the warns and errors are displayed
+ if
+ let focusedWindow,
+ let sourceEditor = filterElements(of: focusedWindow, filter: {
+ (try? $0.role()) ?? .unknown == .textArea &&
+ (try? $0.attribute(.description) ?? "") == "Source Editor"
+ }).first
+
+ {
+ // editor shows errors, runtime warnings and warnings etc in "Line Annotation" id'd elements
+ let annotations = filterElements(of: sourceEditor, filter: {
+ (try? $0.role()) ?? .unknown == .button &&
+ (try? $0.attribute(.identifier)) == "Line Annotation"
+ })
+
+ for annotation in annotations {
+ issues += 1
+ let line = ((try? annotation.attribute(.description) ?? "") ?? "")
+ if line.starts(with: "Warning") && line.contains("Runtime Issue") {
+ warnings += 1
+ } else if line.starts(with: "Warning") {
+ warnings += 1
+ } else if line.starts(with: "Error") {
+ errors += 1
+ }
+ }
+ }
+
let xcState = XcodeState(
workspace: workspace,
editorFile: doc,
isEditingFile: isEditing,
- sessionDate: currentSessionDate ?? xcodeProcess?.launchDate ?? .now /// preserve xcode last date or make new date, used for timings
+ sessionDate: currentSessionDate ?? xcodeProcess?.launchDate ?? .now, /// preserve xcode last date or make new date, used for timings
+ errors: errors,
+ warnings: warnings,
+ totalIssues: issues
)
@@ -86,6 +121,10 @@ struct XcodeState: Equatable {
var sessionDate: Date?
+ var errors: Int
+ var warnings: Int
+ var totalIssues: Int
+
/// Is true if xcode has no file open (sitting in xcodeproj or xcworkspace)
var isIdle: Bool {
editorFile?.lastPathComponent.contains("xcodeproj") ?? true || editorFile?.lastPathComponent.contains("xcworkspace") ?? true
@@ -115,3 +154,73 @@ fileprivate extension String {
self.components(separatedBy: string).count - 1
}
}
+
+
+func traverseHierarchy(of element: UIElement, level: Int = 0) {
+ let indent = String(repeating: " ", count: level)
+
+ // Log the current (parent) element's details
+ do {
+ let role = try element.attribute(.role) as String? ?? "Unknown"
+ let title = try element.attribute(.title) as String? ?? "No Title"
+ let help = try element.attribute(.help) as String? ?? "No Help"
+ let desc = try element.attribute(.description) as String? ?? "No Description"
+ let vald = try element.attribute(.valueDescription) as String? ?? "No Value Description"
+
+ // print("\(indent)- [\(role) name] \(title)")
+ // print("\(indent)- [\(role) help] \(help)")
+ // print("\(indent)- [\(role) desc] \(desc)")
+ // print("\(indent)- [\(role) vald] \(vald)")
+ print("\(indent)- [\(role)] \(try! element.getMultipleAttributes(element.attributes()).map {($0.key.rawValue, $0.value)})")
+ print("")
+
+// Any as String
+ } catch {
+ print("\(indent)- Error accessing attributes for element: \(error)")
+ }
+
+ // Get the children of the current element
+ if let children = try? element.children() {
+ for child in children {
+ // Recursive call to traverse and log children
+ traverseHierarchy(of: child, level: level + 1)
+ }
+ }
+}
+
+extension UIElement {
+ func children() throws -> [UIElement] {
+ let a: [AXUIElement] = (try attribute(.children)) ?? [AXUIElement]()
+ return a.map { UIElement($0) }
+ }
+}
+
+/// Recursively traverses the accessibility hierarchy and applies a closure to filter elements.
+///
+/// - Parameters:
+/// - element: The root `UIElement` to start the traversal from.
+/// - level: The current depth in the hierarchy (used for indentation/debugging purposes).
+/// - filter: A closure that takes a `UIElement` and returns a `Bool`. If the closure returns `true`, the element is considered a match.
+/// - Returns: A list of `UIElement` objects that match the filter condition.
+func filterElements(
+ of element: UIElement,
+ level: Int = 0,
+ filter: (UIElement) -> Bool
+) -> [UIElement] {
+ var matchingElements = [UIElement]()
+
+ // Check if the current element matches the filter
+ if filter(element) {
+ matchingElements.append(element)
+ }
+
+ // Recursively traverse the children of the element
+ if let children = try? element.children() {
+ for child in children {
+ let childMatches = filterElements(of: child, level: level + 1, filter: filter)
+ matchingElements.append(contentsOf: childMatches)
+ }
+ }
+
+ return matchingElements
+}
diff --git a/XRPC/AppDelegate.swift b/XRPC/AppDelegate.swift
index cf393a1..ce1164e 100644
--- a/XRPC/AppDelegate.swift
+++ b/XRPC/AppDelegate.swift
@@ -59,11 +59,12 @@ class AppDelegate: NSObject, NSApplicationDelegate {
// self.window?.standardWindowButton(.closeButton)?.isHidden = true
self.window?.standardWindowButton(.miniaturizeButton)?.isHidden = true
self.window?.standardWindowButton(.zoomButton)?.isHidden = true
- let titlebar = self.window?.standardWindowButton(.closeButton)?.superview
+// let titlebar = self.window?.standardWindowButton(.closeButton)?.superview
// self.window?.titlebarAppearsTransparent = true
self.window?.makeKeyAndOrderFront(self)
SetupVM.shared.setupWindowClose = { self.window?.close(); self.window = nil}
}
+
}
class KillOnCloseViewController: NSViewController {
diff --git a/XRPC/Base.lproj/Main.storyboard b/XRPC/Base.lproj/Main.storyboard
index 25f575d..5349c69 100644
--- a/XRPC/Base.lproj/Main.storyboard
+++ b/XRPC/Base.lproj/Main.storyboard
@@ -1,7 +1,8 @@
-
+
-
+
+
diff --git a/XRPC/RPC.swift b/XRPC/RPC.swift
index 1fae885..0934f1e 100644
--- a/XRPC/RPC.swift
+++ b/XRPC/RPC.swift
@@ -78,14 +78,24 @@ class RPC: ObservableObject, SwordRPCDelegate {
presence.details = "In \(ws)"
}
+ // if issues != 0
+ var issuesString = ""
+ if xcodeState.totalIssues != 0 {
+ if xcodeState.errors != 0 {
+ issuesString = "⛔️\(xcodeState.errors) "
+ } else if xcodeState.warnings != 0 {
+ issuesString = "⚠️\(xcodeState.totalIssues - xcodeState.errors) "
+ }
+ }
+
if xcodeState.isIdle {
presence.state = "Idling in Xcode"
} else {
if let filename = xcodeState.fileName {
if xcodeState.isEditingFile {
- presence.state = "Editing \(filename)"
+ presence.state = "Editing \(filename) \(issuesString)"
} else {
- presence.state = "Viewing \(filename)"
+ presence.state = "Viewing \(filename) \(issuesString)"
}
}
}