Skip to content

Commit

Permalink
Pre-release 0.27.92
Browse files Browse the repository at this point in the history
  • Loading branch information
actions-user committed Oct 31, 2024
1 parent a9ad824 commit 3404070
Show file tree
Hide file tree
Showing 24 changed files with 310 additions and 66 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/devm33/CGEventOverride",
"state" : {
"revision" : "571d36d63e68fac30e4a350600cd186697936f74",
"version" : "1.2.3"
"branch" : "devm33/fix-stale-AXIsProcessTrusted",
"revision" : "06a9bf1f8f8d47cca221344101cc0274f04cc513"
}
},
{
Expand Down
3 changes: 2 additions & 1 deletion Core/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ let package = Package(
// quick hack to support custom UserDefaults
// https://github.com/sindresorhus/KeyboardShortcuts
.package(url: "https://github.com/devm33/KeyboardShortcuts", branch: "main"),
.package(url: "https://github.com/devm33/CGEventOverride", from: "1.2.1"),
.package(url: "https://github.com/devm33/CGEventOverride", branch: "devm33/fix-stale-AXIsProcessTrusted"),
.package(url: "https://github.com/devm33/Highlightr", branch: "master"),
],
targets: [
Expand Down Expand Up @@ -83,6 +83,7 @@ let package = Package(
.product(name: "UserDefaultsObserver", package: "Tool"),
.product(name: "AppMonitoring", package: "Tool"),
.product(name: "SuggestionBasic", package: "Tool"),
.product(name: "Status", package: "Tool"),
.product(name: "ChatTab", package: "Tool"),
.product(name: "Logger", package: "Tool"),
.product(name: "ChatAPIService", package: "Tool"),
Expand Down
7 changes: 4 additions & 3 deletions Core/Sources/HostApp/General.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import Client
import ComposableArchitecture
import Foundation
import LaunchAgentManager
import Status
import SwiftUI
import XPCShared
import Logger
Expand All @@ -11,7 +12,7 @@ struct General {
@ObservableState
struct State: Equatable {
var xpcServiceVersion: String?
var isAccessibilityPermissionGranted: Bool?
var isAccessibilityPermissionGranted: ObservedAXStatus = .unknown
var isReloading = false
}

Expand All @@ -20,7 +21,7 @@ struct General {
case setupLaunchAgentIfNeeded
case openExtensionManager
case reloadStatus
case finishReloading(xpcServiceVersion: String, permissionGranted: Bool)
case finishReloading(xpcServiceVersion: String, permissionGranted: ObservedAXStatus)
case failedReloading
case retryReloading
}
Expand All @@ -35,7 +36,7 @@ struct General {
case .appear:
return .run { send in
await send(.setupLaunchAgentIfNeeded)
for await _ in DistributedNotificationCenter.default().notifications(named: NSNotification.Name("com.apple.accessibility.api")) {
for await _ in DistributedNotificationCenter.default().notifications(named: .serviceStatusDidChange) {
await send(.reloadStatus)
}
}
Expand Down
20 changes: 3 additions & 17 deletions Core/Sources/HostApp/GeneralSettings/CopilotConnectionView.swift
Original file line number Diff line number Diff line change
@@ -1,20 +1,6 @@
import ComposableArchitecture
import SwiftUI

struct ActivityIndicatorView: NSViewRepresentable {
func makeNSView(context _: Context) -> NSProgressIndicator {
let progressIndicator = NSProgressIndicator()
progressIndicator.style = .spinning
progressIndicator.controlSize = .small
progressIndicator.startAnimation(nil)
return progressIndicator
}

func updateNSView(_: NSProgressIndicator, context _: Context) {
// No-op
}
}

struct CopilotConnectionView: View {
@AppStorage("username") var username: String = ""
@Environment(\.toast) var toast
Expand All @@ -38,6 +24,9 @@ struct CopilotConnectionView: View {
title: "GitHub Account Status Permissions",
subtitle: "GitHub Connection: \(viewModel.status?.description ?? "Loading...")"
) {
if viewModel.isRunningAction || waitingForSignIn {
ProgressView().controlSize(.small)
}
Button("Refresh Connection") {
viewModel.checkStatus()
}
Expand Down Expand Up @@ -72,9 +61,6 @@ struct CopilotConnectionView: View {
viewModel.isSignInAlertPresented = false
}
}
if viewModel.isRunningAction || waitingForSignIn {
ActivityIndicatorView()
}
}
}

Expand Down
10 changes: 8 additions & 2 deletions Core/Sources/HostApp/GeneralSettings/GeneralSettingsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,14 @@ struct GeneralSettingsView: View {
let store: StoreOf<General>

var accessibilityPermissionSubtitle: String {
guard let granted = store.isAccessibilityPermissionGranted else { return "Loading..." }
return granted ? "Granted" : "Not Granted. Required to run. Click to open System Preferences."
switch store.isAccessibilityPermissionGranted {
case .granted:
return "Granted"
case .notGranted:
return "Not Granted. Required to run. Click to open System Preferences."
case .unknown:
return ""
}
}

var body: some View {
Expand Down
3 changes: 3 additions & 0 deletions Core/Sources/Service/RealtimeSuggestionController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import Combine
import Foundation
import Logger
import Preferences
import Status
import QuartzCore
import Workspace
import XcodeInspector
Expand Down Expand Up @@ -124,10 +125,12 @@ public actor RealtimeSuggestionController {
do {
try await XcodeInspector.shared.safe.latestActiveXcode?
.triggerCopilotCommand(name: "Sync Text Settings")
await Status.shared.updateExtensionStatus(.succeeded)
} catch {
if filespace.codeMetadata.uti?.isEmpty ?? true {
filespace.codeMetadata.uti = nil
}
await Status.shared.updateExtensionStatus(.failed)
}
}
}
Expand Down
1 change: 1 addition & 0 deletions Core/Sources/Service/Service.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ public final class Service {
let globalShortcutManager: GlobalShortcutManager
let keyBindingManager: KeyBindingManager
let xcodeThemeController: XcodeThemeController = .init()
public var markAsProcessing: (Bool) -> Void = { _ in }

@Dependency(\.toast) var toast
var cancellable = Set<AnyCancellable>()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ struct PresentInWindowSuggestionPresenter {
Task { @MainActor in
let controller = Service.shared.guiController.widgetController
controller.markAsProcessing(isProcessing)
Service.shared.markAsProcessing(isProcessing)
}
}

Expand Down
7 changes: 5 additions & 2 deletions Core/Sources/Service/XPCService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import GitHubCopilotService
import LanguageServerProtocol
import Logger
import Preferences
import Status
import XPCShared

public class XPCService: NSObject, XPCServiceProtocol {
Expand All @@ -16,8 +17,10 @@ public class XPCService: NSObject, XPCServiceProtocol {
)
}

public func getXPCServiceAccessibilityPermission(withReply reply: @escaping (Bool) -> Void) {
reply(AXIsProcessTrusted())
public func getXPCServiceAccessibilityPermission(withReply reply: @escaping (ObservedAXStatus) -> Void) {
Task {
reply(await Status.shared.getAXStatus())
}
}

// MARK: - Suggestion
Expand Down
34 changes: 16 additions & 18 deletions ExtensionService/AppDelegate+Menu.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import AppKit
import Foundation
import Preferences
import Status
import SuggestionBasic
import XcodeInspector
import Logger
Expand All @@ -14,10 +15,6 @@ extension AppDelegate {
.init("xcodeInspectorDebugMenu")
}

fileprivate var accessibilityAPIPermissionMenuItemIdentifier: NSUserInterfaceItemIdentifier {
.init("accessibilityAPIPermissionMenuItem")
}

fileprivate var sourceEditorDebugMenu: NSUserInterfaceItemIdentifier {
.init("sourceEditorDebugMenu")
}
Expand Down Expand Up @@ -72,12 +69,12 @@ extension AppDelegate {
xcodeInspectorDebug.submenu = xcodeInspectorDebugMenu
xcodeInspectorDebug.isHidden = false

let accessibilityAPIPermission = NSMenuItem(
title: "Accessibility Permission: N/A",
action: nil,
statusMenuItem = NSMenuItem(
title: "",
action: #selector(openStatusLink),
keyEquivalent: ""
)
accessibilityAPIPermission.identifier = accessibilityAPIPermissionMenuItemIdentifier
statusMenuItem.isHidden = true

let quitItem = NSMenuItem(
title: "Quit",
Expand Down Expand Up @@ -126,7 +123,7 @@ extension AppDelegate {
statusBarMenu.addItem(toggleIgnoreLanguage)
statusBarMenu.addItem(.separator())
statusBarMenu.addItem(copilotStatus)
statusBarMenu.addItem(accessibilityAPIPermission)
statusBarMenu.addItem(statusMenuItem)
statusBarMenu.addItem(.separator())
statusBarMenu.addItem(openDocs)
statusBarMenu.addItem(openForum)
Expand Down Expand Up @@ -160,22 +157,14 @@ extension AppDelegate: NSMenuDelegate {
item.identifier == toggleIgnoreLanguageMenuItemIdentifier
}) {
if let lang = DisabledLanguageList.shared.activeDocumentLanguage {
toggleLanguage.title = "\(DisabledLanguageList.shared.isEnabled(lang) ? "Disable" : "Enable") Completions For \(lang.rawValue)"
toggleLanguage.title = "\(DisabledLanguageList.shared.isEnabled(lang) ? "Disable" : "Enable") Completions for \(lang.rawValue)"
toggleLanguage.action = #selector(toggleIgnoreLanguage)
} else {
toggleLanguage.title = "No Active Document"
toggleLanguage.action = nil
}
}

if let accessibilityAPIPermission = menu.items.first(where: { item in
item.identifier == accessibilityAPIPermissionMenuItemIdentifier
}) {
AXIsProcessTrusted()
accessibilityAPIPermission.title =
"Accessibility Permission: \(AXIsProcessTrusted() ? "Granted" : "Not Granted")"
}

statusChecker.updateStatusInBackground(notify: { (status: String, isOk: Bool) in
if let statusItem = menu.items.first(where: { item in
item.identifier == self.copilotStatusMenuItemIdentifier
Expand Down Expand Up @@ -321,6 +310,15 @@ private extension AppDelegate {
}
}
}

@objc func openStatusLink() {
Task {
let status = await Status.shared.getStatus()
if let s = status.url, let url = URL(string: s) {
NSWorkspace.shared.open(url)
}
}
}
}

private extension NSMenuItem {
Expand Down
85 changes: 75 additions & 10 deletions ExtensionService/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import Logger
import Preferences
import Service
import ServiceManagement
import Status
import SwiftUI
import UpdateChecker
import UserDefaultsObserver
Expand All @@ -30,6 +31,7 @@ class ExtensionUpdateCheckerDelegate: UpdateCheckerDelegate {
class AppDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate {
let service = Service.shared
var statusBarItem: NSStatusItem!
var statusMenuItem: NSMenuItem!
var xpcController: XPCController?
let updateChecker =
UpdateChecker(
Expand All @@ -39,21 +41,29 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate {
let statusChecker: AuthStatusChecker = AuthStatusChecker()
var xpcExtensionService: XPCExtensionService?
private var cancellables = Set<AnyCancellable>()
private var progressView: NSProgressIndicator?
private var idleIcon = NSImage(named: "MenuBarIcon")

func applicationDidFinishLaunching(_: Notification) {
if ProcessInfo.processInfo.environment["IS_UNIT_TEST"] == "YES" { return }
_ = XcodeInspector.shared
service.markAsProcessing = { [weak self] in
guard let self = self else { return }
self.markAsProcessing($0)
}
service.start()
AXIsProcessTrustedWithOptions([
kAXTrustedCheckOptionPrompt.takeRetainedValue() as NSString: true,
] as CFDictionary)
setupQuitOnUpdate()
setupQuitOnUserTerminated()
setupQuitOnFeatureFlag()
xpcController = .init()
Logger.service.info("XPC Service started.")
NSApp.setActivationPolicy(.accessory)
buildStatusBarMenu()
watchServiceStatus()
watchAXStatus()
updateStatusBarItem() // set the initial status
}

@objc func quit() {
Expand Down Expand Up @@ -132,15 +142,6 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate {
}
}

func setupQuitOnFeatureFlag() {
FeatureFlagNotifierImpl.shared.featureFlagsDidChange.sink { [weak self] (flags) in
if flags.x != true {
Logger.service.info("Xcode feature flag not granted, quitting.")
self?.quit()
}
}.store(in: &cancellables)
}

func requestAccessoryAPIPermission() {
AXIsProcessTrustedWithOptions([
kAXTrustedCheckOptionPrompt.takeRetainedValue() as NSString: true,
Expand All @@ -161,6 +162,70 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate {
xpcExtensionService = service
return service
}

func watchServiceStatus() {
let notifications = NotificationCenter.default.notifications(named: .serviceStatusDidChange)
Task { [weak self] in
for await _ in notifications {
guard let self else { return }
self.updateStatusBarItem()
}
}
}

func watchAXStatus() {
let osNotifications = DistributedNotificationCenter.default().notifications(named: NSNotification.Name("com.apple.accessibility.api"))
Task { [weak self] in
for await _ in osNotifications {
guard let self else { return }
self.updateStatusBarItem()
}
}
}

func updateStatusBarItem() {
Task { @MainActor in
let status = await Status.shared.getStatus()
let image = if status.system {
NSImage(systemSymbolName: status.icon, accessibilityDescription: nil)
} else {
NSImage(named: status.icon)
}
idleIcon = image
self.statusBarItem.button?.image = image
if let message = status.message {
// TODO switch to attributedTitle to enable line breaks and color.
self.statusMenuItem.title = message
self.statusMenuItem.isHidden = false
self.statusMenuItem.isEnabled = status.url != nil
} else {
self.statusMenuItem.isHidden = true
}
}
}

func markAsProcessing(_ isProcessing: Bool) {
if !isProcessing {
// No longer in progress
progressView?.removeFromSuperview()
progressView = nil
statusBarItem.button?.image = idleIcon
return
}
if progressView != nil {
// Already in progress
return
}
let progress = NSProgressIndicator()
progress.style = .spinning
progress.sizeToFit()
progress.frame = statusBarItem.button?.bounds ?? .zero
progress.isIndeterminate = true
progress.startAnimation(nil)
statusBarItem.button?.addSubview(progress)
statusBarItem.button?.image = nil
progressView = progress
}
}

extension NSRunningApplication {
Expand Down
Loading

0 comments on commit 3404070

Please sign in to comment.