Skip to content

Commit f8f7e96

Browse files
committed
Move chat context menu to tab bar
1 parent 37ec23e commit f8f7e96

5 files changed

Lines changed: 101 additions & 71 deletions

File tree

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import AppKit
2+
import SharedUIComponents
3+
import SwiftUI
4+
5+
struct ChatContextMenu: View {
6+
let chat: ChatProvider
7+
@AppStorage(\.customCommands) var customCommands
8+
9+
var body: some View {
10+
Group {
11+
currentSystemPrompt
12+
currentExtraSystemPrompt
13+
resetPrompt
14+
15+
Divider()
16+
17+
customCommandMenu
18+
}
19+
}
20+
21+
@ViewBuilder
22+
var currentSystemPrompt: some View {
23+
Text("System Prompt:")
24+
Text({
25+
var text = chat.systemPrompt
26+
if text.isEmpty { text = "N/A" }
27+
if text.count > 30 { text = String(text.prefix(30)) + "..." }
28+
return text
29+
}() as String)
30+
}
31+
32+
@ViewBuilder
33+
var currentExtraSystemPrompt: some View {
34+
Text("Extra Prompt:")
35+
Text({
36+
var text = chat.extraSystemPrompt
37+
if text.isEmpty { text = "N/A" }
38+
if text.count > 30 { text = String(text.prefix(30)) + "..." }
39+
return text
40+
}() as String)
41+
}
42+
43+
var resetPrompt: some View {
44+
Button("Reset System Prompt") {
45+
chat.resetPrompt()
46+
}
47+
}
48+
49+
var customCommandMenu: some View {
50+
Menu("Custom Commands") {
51+
ForEach(
52+
customCommands.filter {
53+
switch $0.feature {
54+
case .chatWithSelection, .customChat: return true
55+
case .promptToCode: return false
56+
case .singleRoundDialog: return false
57+
}
58+
},
59+
id: \.name
60+
) { command in
61+
Button(action: {
62+
chat.triggerCustomCommand(command)
63+
}) {
64+
Text(command.name)
65+
}
66+
}
67+
}
68+
}
69+
}

Core/Sources/ChatGPTChatTab/ChatGPTChatTab.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ public class ChatGPTChatTab: ChatTab {
1313
public func buildView() -> any View {
1414
ChatPanel(chat: provider)
1515
}
16+
17+
public func buildMenu() -> any View {
18+
ChatContextMenu(chat: provider)
19+
}
1620

1721
public init(service: ChatService = .init()) {
1822
self.service = service

Core/Sources/ChatGPTChatTab/ChatPanel.swift

Lines changed: 0 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -302,9 +302,6 @@ struct ChatPanelInputArea: View {
302302
}
303303
.padding(8)
304304
.background(.ultraThickMaterial)
305-
.contextMenu {
306-
ChatContextMenu(chat: chat)
307-
}
308305
}
309306

310307
var clearButton: some View {
@@ -412,72 +409,6 @@ struct ChatPanelInputArea: View {
412409
}
413410
}
414411

415-
struct ChatContextMenu: View {
416-
let chat: ChatProvider
417-
@AppStorage(\.customCommands) var customCommands
418-
419-
var body: some View {
420-
Group {
421-
currentSystemPrompt
422-
currentExtraSystemPrompt
423-
resetPrompt
424-
425-
Divider()
426-
427-
customCommandMenu
428-
}
429-
}
430-
431-
@ViewBuilder
432-
var currentSystemPrompt: some View {
433-
Text("System Prompt:")
434-
Text({
435-
var text = chat.systemPrompt
436-
if text.isEmpty { text = "N/A" }
437-
if text.count > 30 { text = String(text.prefix(30)) + "..." }
438-
return text
439-
}() as String)
440-
}
441-
442-
@ViewBuilder
443-
var currentExtraSystemPrompt: some View {
444-
Text("Extra Prompt:")
445-
Text({
446-
var text = chat.extraSystemPrompt
447-
if text.isEmpty { text = "N/A" }
448-
if text.count > 30 { text = String(text.prefix(30)) + "..." }
449-
return text
450-
}() as String)
451-
}
452-
453-
var resetPrompt: some View {
454-
Button("Reset System Prompt") {
455-
chat.resetPrompt()
456-
}
457-
}
458-
459-
var customCommandMenu: some View {
460-
Menu("Custom Commands") {
461-
ForEach(
462-
customCommands.filter {
463-
switch $0.feature {
464-
case .chatWithSelection, .customChat: return true
465-
case .promptToCode: return false
466-
case .singleRoundDialog: return false
467-
}
468-
},
469-
id: \.name
470-
) { command in
471-
Button(action: {
472-
chat.triggerCustomCommand(command)
473-
}) {
474-
Text(command.name)
475-
}
476-
}
477-
}
478-
}
479-
}
480-
481412
struct RoundedCorners: Shape {
482413
var tl: CGFloat = 0.0
483414
var tr: CGFloat = 0.0

Core/Sources/SuggestionWidget/ChatWindowView.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ struct ChatTabBar: View {
7373
let store: StoreOf<ChatPanelFeature>
7474

7575
struct TabBarState: Equatable {
76+
var tabs: [BaseChatTab]
7677
var tabInfo: [ChatTabInfo]
7778
var selectedTabId: String
7879
}
@@ -81,6 +82,7 @@ struct ChatTabBar: View {
8182
WithViewStore(
8283
store,
8384
observe: { TabBarState(
85+
tabs: $0.chatTapGroup.tabs,
8486
tabInfo: $0.chatTapGroup.tabInfo,
8587
selectedTabId: $0.chatTapGroup.selectedTabId
8688
?? $0.chatTapGroup.tabInfo.first?.id ?? ""
@@ -95,6 +97,13 @@ struct ChatTabBar: View {
9597
info: info,
9698
isSelected: info.id == viewStore.state.selectedTabId
9799
)
100+
.contextMenu {
101+
if let tab = viewStore.state.tabs
102+
.first(where: { $0.id == info.id })
103+
{
104+
tab.menu
105+
}
106+
}
98107
}
99108
}
100109
}
@@ -194,6 +203,12 @@ struct ChatTabContainer: View {
194203

195204
struct ChatWindowView_Previews: PreviewProvider {
196205
class FakeChatTab: ChatTab {
206+
func buildMenu() -> any View {
207+
Text("Menu Item")
208+
Text("Menu Item")
209+
Text("Menu Item")
210+
}
211+
197212
func buildView() -> any View {
198213
ChatPanel(
199214
chat: .init(

Tool/Sources/ChatTab/ChatTab.swift

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ open class BaseChatTab: Equatable {
6262

6363
@ViewBuilder
6464
public var body: some View {
65-
let id = "BaseChatTab\(info.id)"
65+
let id = "ChatTabBody\(info.id)"
6666
if let tab = self as? ChatTabType {
6767
ContentView(info: info, buildView: tab.buildView).id(id)
6868
} else {
@@ -72,7 +72,12 @@ open class BaseChatTab: Equatable {
7272

7373
@ViewBuilder
7474
public var menu: some View {
75-
EmptyView()
75+
let id = "ChatTabMenu\(info.id)"
76+
if let tab = self as? ChatTabType {
77+
ContentView(info: info, buildView: tab.buildMenu).id(id)
78+
} else {
79+
EmptyView().id(id)
80+
}
7681
}
7782

7883
public static func == (lhs: BaseChatTab, rhs: BaseChatTab) -> Bool {
@@ -83,6 +88,8 @@ open class BaseChatTab: Equatable {
8388
public protocol ChatTabType {
8489
@ViewBuilder
8590
func buildView() -> any View
91+
@ViewBuilder
92+
func buildMenu() -> any View
8693
}
8794

8895
public class EmptyChatTab: ChatTab {
@@ -92,6 +99,10 @@ public class EmptyChatTab: ChatTab {
9299
}
93100
.background(Color.blue)
94101
}
102+
103+
public func buildMenu() -> any View {
104+
EmptyView()
105+
}
95106

96107
public init(id: String = UUID().uuidString) {
97108
super.init(id: id, title: "Empty")

0 commit comments

Comments
 (0)