Skip to content

Commit

Permalink
Merge branch 'hotfix/0.27.1'
Browse files Browse the repository at this point in the history
  • Loading branch information
intitni committed Nov 18, 2023
2 parents fa7dc18 + 93ce520 commit 45a78f4
Show file tree
Hide file tree
Showing 30 changed files with 431 additions and 184 deletions.
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

0 comments on commit 45a78f4

Please sign in to comment.