Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
257c025
Update to activate extension app when using hotkey to show widgets
intitni Nov 17, 2023
4d6b46a
Move GlobalShortcutManager to its own file
intitni Nov 17, 2023
d81a5fb
Add an action toggleWIdgets to GUI
intitni Nov 17, 2023
bc57cf1
Update to change key window when toggling widget with hotkey
intitni Nov 17, 2023
4b1271a
Fix activation logic
intitni Nov 17, 2023
5a3db20
Support activating selecting chat tab in various situations
intitni Nov 17, 2023
72bc2f2
Auto focus on text field in chat in various situations
intitni Nov 17, 2023
ebce401
Unify implementation of app activation
intitni Nov 17, 2023
a0d46ca
Activate extension app when prompt to code is activated
intitni Nov 17, 2023
22bf255
Update to control prompt to code focus state in reducer
intitni Nov 17, 2023
80acb92
Add missing weak self
intitni Nov 17, 2023
aa0fe32
Adjust timing issue of prompt to code
intitni Nov 17, 2023
fa2775a
Re activate extension after accepting prompt to code when continuous
intitni Nov 17, 2023
33f4b04
Merge branch 'hotfix/active-extension-when-using-hotkey-to-show-widge…
intitni Nov 17, 2023
ca98d1b
Ignore chunk id
intitni Nov 17, 2023
52fe8af
Merge branch 'hotfix/random-message-id-in-one-llm-round' into hotfix/…
intitni Nov 17, 2023
0f3f3e6
Fix cancellation
intitni Nov 17, 2023
15c8ba8
Add shouldEndTextWindow to configuration
intitni Nov 17, 2023
1ca4993
Bump version to 0.27.1
intitni Nov 17, 2023
ef1f42a
Workaround cooperative app activation
intitni Nov 18, 2023
93ce520
Update appcast.xml
intitni Nov 18, 2023
45a78f4
Merge branch 'hotfix/0.27.1'
intitni Nov 18, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions Core/Sources/ChatGPTChatTab/Chat.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ struct Chat: ReducerProtocol {
var history: [ChatMessage] = []
@BindingState var isReceivingMessage = false
var chatMenu = ChatMenu.State()
@BindingState var focusedField: Field?

enum Field: String, Hashable {
case textField
}
}

enum Action: Equatable, BindableAction {
Expand All @@ -45,6 +50,7 @@ struct Chat: ReducerProtocol {
case deleteMessageButtonTapped(MessageID)
case resendMessageButtonTapped(MessageID)
case setAsExtraPromptButtonTapped(MessageID)
case focusOnTextField

case observeChatService
case observeHistoryChange
Expand Down Expand Up @@ -89,6 +95,7 @@ struct Chat: ReducerProtocol {
await send(.isReceivingMessageChanged)
await send(.systemPromptChanged)
await send(.extraSystemPromptChanged)
await send(.focusOnTextField)
}

case .sendButtonTapped:
Expand Down Expand Up @@ -127,6 +134,10 @@ struct Chat: ReducerProtocol {
return .run { _ in
await service.setMessageAsExtraPrompt(id: id)
}

case .focusOnTextField:
state.focusedField = .textField
return .none

case .observeChatService:
return .run { send in
Expand Down
21 changes: 13 additions & 8 deletions Core/Sources/ChatGPTChatTab/ChatGPTChatTab.swift
Original file line number Diff line number Diff line change
Expand Up @@ -116,13 +116,19 @@ public class ChatGPTChatTab: ChatTab {
public func start() {
chatTabViewStore.send(.updateTitle("Chat"))

service.$systemPrompt.removeDuplicates().sink { _ in
chatTabViewStore.publisher.focusTrigger.removeDuplicates().sink { [weak self] _ in
Task { @MainActor [weak self] in
self?.viewStore.send(.focusOnTextField)
}
}.store(in: &cancellable)

service.$systemPrompt.removeDuplicates().sink { [weak self] _ in
Task { @MainActor [weak self] in
self?.chatTabViewStore.send(.tabContentUpdated)
}
}.store(in: &cancellable)

service.$extraSystemPrompt.removeDuplicates().sink { _ in
service.$extraSystemPrompt.removeDuplicates().sink { [weak self] _ in
Task { @MainActor [weak self] in
self?.chatTabViewStore.send(.tabContentUpdated)
}
Expand All @@ -134,12 +140,11 @@ public class ChatGPTChatTab: ChatTab {
}
}.store(in: &cancellable)

viewStore.publisher.removeDuplicates()
.sink { [weak self] _ in
Task { @MainActor [weak self] in
self?.chatTabViewStore.send(.tabContentUpdated)
}
}.store(in: &cancellable)
viewStore.publisher.removeDuplicates().sink { [weak self] _ in
Task { @MainActor [weak self] in
self?.chatTabViewStore.send(.tabContentUpdated)
}
}.store(in: &cancellable)
}
}

17 changes: 9 additions & 8 deletions Core/Sources/ChatGPTChatTab/ChatPanel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -498,16 +498,13 @@ struct FunctionMessage: View {

struct ChatPanelInputArea: View {
let chat: StoreOf<Chat>
@FocusState var isInputAreaFocused: Bool
@FocusState var focusedField: Chat.State.Field?

var body: some View {
HStack {
clearButton
textEditor
}
.onAppear {
isInputAreaFocused = true
}
.padding(8)
.background(.ultraThickMaterial)
}
Expand Down Expand Up @@ -538,8 +535,11 @@ struct ChatPanelInputArea: View {
@MainActor
var textEditor: some View {
HStack(spacing: 0) {
WithViewStore(chat, removeDuplicates: { $0.typedMessage == $1.typedMessage }) {
viewStore in
WithViewStore(
chat,
removeDuplicates: {
$0.typedMessage == $1.typedMessage && $0.focusedField == $1.focusedField
}) { viewStore in
ZStack(alignment: .center) {
// a hack to support dynamic height of TextEditor
Text(
Expand All @@ -560,7 +560,8 @@ struct ChatPanelInputArea: View {
.padding(.top, 1)
.padding(.bottom, -1)
}
.focused($isInputAreaFocused)
.focused($focusedField, equals: .textField)
.bind(viewStore.$focusedField, to: $focusedField)
.padding(8)
.fixedSize(horizontal: false, vertical: true)
}
Expand Down Expand Up @@ -595,7 +596,7 @@ struct ChatPanelInputArea: View {
.keyboardShortcut(KeyEquivalent.return, modifiers: [.shift])

Button(action: {
isInputAreaFocused = true
focusedField = .textField
}) {
EmptyView()
}
Expand Down
4 changes: 4 additions & 0 deletions Core/Sources/ChatService/ChatService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ public final class ChatService: ObservableObject {
let configuration = UserPreferenceChatGPTConfiguration().overriding()
/// Used by context collector
let extraConfiguration = configuration.overriding()
extraConfiguration.textWindowTerminator = {
guard let last = $0.last else { return false }
return last.isNewline || last.isPunctuation
}
let memory = ContextAwareAutoManagedChatGPTMemory(
configuration: extraConfiguration,
functionProvider: ChatFunctionProvider()
Expand Down
24 changes: 17 additions & 7 deletions Core/Sources/Service/GUI/GraphicalUserInterfaceController.swift
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import ActiveApplicationMonitor
import AppActivator
import AppKit
import ChatGPTChatTab
import ChatTab
import ComposableArchitecture
import Dependencies
import Environment
import Preferences
import SuggestionModel
import SuggestionWidget

#if canImport(ProChatTabs)
Expand Down Expand Up @@ -54,6 +56,7 @@ struct GUI: ReducerProtocol {
case openChatPanel(forceDetach: Bool)
case createChatGPTChatTabIfNeeded
case sendCustomCommandToActiveChat(CustomCommand)
case toggleWidgetsHotkeyPressed

case suggestionWidget(WidgetFeature.Action)

Expand All @@ -66,7 +69,8 @@ struct GUI: ReducerProtocol {
#endif
}

@Dependency(\.chatTabPool) var chatTabPool: ChatTabPool
@Dependency(\.chatTabPool) var chatTabPool
@Dependency(\.activateThisApp) var activateThisApp

public enum Debounce: Hashable {
case updateChatTabOrder
Expand Down Expand Up @@ -135,6 +139,9 @@ struct GUI: ReducerProtocol {
.chatPanel(.presentChatPanel(forceDetach: forceDetach))
)
)
await send(.suggestionWidget(.updateKeyWindow(.chatPanel)))

activateThisApp()
}

case .createChatGPTChatTabIfNeeded:
Expand Down Expand Up @@ -192,6 +199,11 @@ struct GUI: ReducerProtocol {
}
}

case .toggleWidgetsHotkeyPressed:
return .run { send in
await send(.suggestionWidget(.circularWidget(.widgetClicked)))
}

case let .suggestionWidget(.chatPanel(.chatTab(id, .tabContentUpdated))):
#if canImport(ChatTabPersistent)
// when a tab is updated, persist it.
Expand Down Expand Up @@ -262,12 +274,10 @@ public final class GraphicalUserInterfaceController {
Task {
let handler = PseudoCommandHandler()
await handler.acceptPromptToCode()
if let app = ActiveApplicationMonitor.shared.previousApp,
app.isXcode,
!promptToCode.isContinuous
{
try await Task.sleep(nanoseconds: 200_000_000)
app.activate()
if !promptToCode.isContinuous {
NSWorkspace.activatePreviousActiveXcode()
} else {
NSWorkspace.activateThisApp()
}
}
}
Expand Down
16 changes: 4 additions & 12 deletions Core/Sources/Service/GUI/WidgetDataSource.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import ActiveApplicationMonitor
import AppActivator
import AppKit
import ChatService
import ComposableArchitecture
import Foundation
Expand Down Expand Up @@ -39,24 +41,14 @@ extension WidgetDataSource: SuggestionWidgetDataSource {
Task {
let handler = PseudoCommandHandler()
await handler.rejectSuggestions()
if let app = ActiveApplicationMonitor.shared.previousApp,
app.isXcode
{
try await Task.sleep(nanoseconds: 200_000_000)
app.activate()
}
NSWorkspace.activatePreviousActiveXcode()
}
},
onAcceptSuggestionTapped: {
Task {
let handler = PseudoCommandHandler()
await handler.acceptSuggestion()
if let app = ActiveApplicationMonitor.shared.previousApp,
app.isXcode
{
try await Task.sleep(nanoseconds: 200_000_000)
app.activate()
}
NSWorkspace.activatePreviousActiveXcode()
}
}
)
Expand Down
63 changes: 63 additions & 0 deletions Core/Sources/Service/GlobalShortcutManager.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import AppKit
import Combine
import Foundation
import KeyboardShortcuts
import XcodeInspector

extension KeyboardShortcuts.Name {
static let showHideWidget = Self("ShowHideWidget")
}

@MainActor
final class GlobalShortcutManager {
let guiController: GraphicalUserInterfaceController
private var cancellable = Set<AnyCancellable>()

nonisolated init(guiController: GraphicalUserInterfaceController) {
self.guiController = guiController
}

func start() {
KeyboardShortcuts.userDefaults = .shared
setupShortcutIfNeeded()

KeyboardShortcuts.onKeyUp(for: .showHideWidget) { [guiController] in
let isXCodeActive = XcodeInspector.shared.activeXcode != nil

if !isXCodeActive,
!guiController.viewStore.state.suggestionWidgetState.chatPanelState.isPanelDisplayed,
UserDefaults.shared.value(for: \.showHideWidgetShortcutGlobally)
{
guiController.viewStore.send(.openChatPanel(forceDetach: true))
} else {
guiController.viewStore.send(.toggleWidgetsHotkeyPressed)
}
}

XcodeInspector.shared.$activeApplication.sink { app in
if !UserDefaults.shared.value(for: \.showHideWidgetShortcutGlobally) {
let shouldBeEnabled = if let app, app.isXcode || app.isExtensionService {
true
} else {
false
}
if shouldBeEnabled {
self.setupShortcutIfNeeded()
} else {
self.removeShortcutIfNeeded()
}
} else {
self.setupShortcutIfNeeded()
}
}.store(in: &cancellable)
}

func setupShortcutIfNeeded() {
KeyboardShortcuts.enable(.showHideWidget)
}

func removeShortcutIfNeeded() {
KeyboardShortcuts.disable(.showHideWidget)
}
}

57 changes: 0 additions & 57 deletions Core/Sources/Service/Service.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import Combine
import Dependencies
import Foundation
import KeyboardShortcuts
import Workspace
import WorkspaceSuggestionService
import XcodeInspector
Expand All @@ -15,10 +13,6 @@ import ProService
public static let shared = TheActor()
}

extension KeyboardShortcuts.Name {
static let showHideWidget = Self("ShowHideWidget")
}

/// The running extension service.
public final class Service {
public static let shared = Service()
Expand Down Expand Up @@ -69,54 +63,3 @@ public final class Service {
}
}

@MainActor
final class GlobalShortcutManager {
let guiController: GraphicalUserInterfaceController
private var cancellable = Set<AnyCancellable>()

nonisolated init(guiController: GraphicalUserInterfaceController) {
self.guiController = guiController
}

func start() {
KeyboardShortcuts.userDefaults = .shared
setupShortcutIfNeeded()

KeyboardShortcuts.onKeyUp(for: .showHideWidget) { [guiController] in
if XcodeInspector.shared.activeXcode == nil,
!guiController.viewStore.state.suggestionWidgetState.chatPanelState.isPanelDisplayed,
UserDefaults.shared.value(for: \.showHideWidgetShortcutGlobally)
{
guiController.viewStore.send(.openChatPanel(forceDetach: true))
} else {
guiController.viewStore.send(.suggestionWidget(.circularWidget(.widgetClicked)))
}
}

XcodeInspector.shared.$activeApplication.sink { app in
if !UserDefaults.shared.value(for: \.showHideWidgetShortcutGlobally) {
let shouldBeEnabled = if let app, app.isXcode || app.isExtensionService {
true
} else {
false
}
if shouldBeEnabled {
self.setupShortcutIfNeeded()
} else {
self.removeShortcutIfNeeded()
}
} else {
self.setupShortcutIfNeeded()
}
}.store(in: &cancellable)
}

func setupShortcutIfNeeded() {
KeyboardShortcuts.enable(.showHideWidget)
}

func removeShortcutIfNeeded() {
KeyboardShortcuts.disable(.showHideWidget)
}
}

Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import AppKit
import ChatService
import Environment
import Foundation
Expand Down Expand Up @@ -409,7 +410,7 @@ extension WindowBaseCommandHandler {
}() as (String, CursorRange)

let viewStore = Service.shared.guiController.viewStore

let customCommandTemplateProcessor = CustomCommandTemplateProcessor()
let newExtraSystemPrompt = extraSystemPrompt.map(customCommandTemplateProcessor.process)
let newPrompt = prompt.map(customCommandTemplateProcessor.process)
Expand Down
Loading