Skip to content

Commit 12dbf3c

Browse files
committed
Add new custom command one-time dialog
1 parent 61500a9 commit 12dbf3c

File tree

7 files changed

+375
-264
lines changed

7 files changed

+375
-264
lines changed

Core/Sources/ChatService/ChatService.swift

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,14 @@ public final class ChatService: ObservableObject {
8585
isReceivingMessage = false
8686
}
8787
}
88+
89+
public func sendAndWait(content: String) async throws -> String {
90+
try await send(content: content)
91+
if let reply = await memory.history.last(where: { $0.role == .assistant })?.content {
92+
return reply
93+
}
94+
return ""
95+
}
8896

8997
public func stopReceivingMessage() async {
9098
await pluginController.stopResponding()
@@ -175,8 +183,8 @@ public final class ChatService: ObservableObject {
175183
sendingMessageImmediately: prompt,
176184
name: command.name
177185
)
178-
case .promptToCode:
179-
return nil
186+
case .promptToCode: return nil
187+
case .oneTimeDialog: return nil
180188
}
181189
}()
182190

Core/Sources/ChatTab/ChatGPT/ChatPanel.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -463,6 +463,7 @@ struct ChatContextMenu: View {
463463
switch $0.feature {
464464
case .chatWithSelection, .customChat: return true
465465
case .promptToCode: return false
466+
case .oneTimeDialog: return false
466467
}
467468
},
468469
id: \.name

Core/Sources/HostApp/CustomCommandView.swift

Lines changed: 2 additions & 256 deletions
Original file line numberDiff line numberDiff line change
@@ -15,22 +15,6 @@ extension List {
1515
struct CustomCommandView: View {
1616
final class Settings: ObservableObject {
1717
@AppStorage(\.customCommands) var customCommands
18-
var illegalNames: [String] {
19-
let existed = customCommands.map(\.name)
20-
let builtin: [String] = [
21-
"Get Suggestions",
22-
"Accept Suggestion",
23-
"Reject Suggestion",
24-
"Next Suggestion",
25-
"Previous Suggestion",
26-
"Real-time Suggestions",
27-
"Prefetch Suggestions",
28-
"Open Chat",
29-
"Prompt to Code",
30-
]
31-
32-
return existed + builtin
33-
}
3418

3519
init(customCommands: AppStorage<[CustomCommand]>? = nil) {
3620
if let list = customCommands {
@@ -67,6 +51,8 @@ struct CustomCommandView: View {
6751
Text("Custom Chat")
6852
case .promptToCode:
6953
Text("Prompt to Code")
54+
case .oneTimeDialog:
55+
Text("One-time Dialog")
7056
}
7157
}
7258
.font(.caption)
@@ -146,224 +132,6 @@ struct CustomCommandView: View {
146132
}
147133
}
148134

149-
struct EditCustomCommandView: View {
150-
@Environment(\.toast) var toast
151-
@Binding var editingCommand: CustomCommandView.EditingCommand?
152-
var settings: CustomCommandView.Settings
153-
let originalName: String
154-
@State var commandType: CommandType
155-
156-
@State var name: String
157-
@State var prompt: String
158-
@State var systemPrompt: String
159-
@State var usePrompt: Bool
160-
@State var continuousMode: Bool
161-
@State var editingContentInFullScreen: Binding<String>?
162-
@State var generatingPromptToCodeDescription: Bool
163-
164-
enum CommandType: Int, CaseIterable {
165-
case chatWithSelection
166-
case promptToCode
167-
case customChat
168-
}
169-
170-
init(
171-
editingCommand: Binding<CustomCommandView.EditingCommand?>,
172-
settings: CustomCommandView.Settings
173-
) {
174-
_editingCommand = editingCommand
175-
self.settings = settings
176-
originalName = editingCommand.wrappedValue?.command.name ?? ""
177-
name = originalName
178-
switch editingCommand.wrappedValue?.command.feature {
179-
case let .chatWithSelection(extraSystemPrompt, prompt, useExtraSystemPrompt):
180-
commandType = .chatWithSelection
181-
self.prompt = prompt ?? ""
182-
systemPrompt = extraSystemPrompt ?? ""
183-
usePrompt = useExtraSystemPrompt ?? true
184-
continuousMode = false
185-
generatingPromptToCodeDescription = true
186-
case let .customChat(systemPrompt, prompt):
187-
commandType = .customChat
188-
self.systemPrompt = systemPrompt ?? ""
189-
self.prompt = prompt ?? ""
190-
usePrompt = false
191-
continuousMode = false
192-
generatingPromptToCodeDescription = true
193-
case let .promptToCode(extraSystemPrompt, prompt, continuousMode, generateDescription):
194-
commandType = .promptToCode
195-
self.prompt = prompt ?? ""
196-
systemPrompt = extraSystemPrompt ?? ""
197-
usePrompt = false
198-
self.continuousMode = continuousMode ?? false
199-
generatingPromptToCodeDescription = generateDescription ?? true
200-
case .none:
201-
commandType = .chatWithSelection
202-
prompt = ""
203-
systemPrompt = ""
204-
continuousMode = false
205-
usePrompt = true
206-
generatingPromptToCodeDescription = true
207-
}
208-
}
209-
210-
var body: some View {
211-
ScrollView {
212-
Form {
213-
TextField("Name", text: $name)
214-
215-
Picker("Command Type", selection: $commandType) {
216-
ForEach(CommandType.allCases, id: \.rawValue) { commandType in
217-
Text({
218-
switch commandType {
219-
case .chatWithSelection:
220-
return "Send Message"
221-
case .promptToCode:
222-
return "Prompt to Code"
223-
case .customChat:
224-
return "Custom Chat"
225-
}
226-
}() as String).tag(commandType)
227-
}
228-
}
229-
230-
switch commandType {
231-
case .chatWithSelection:
232-
systemPromptTextField(title: "Extra System Prompt", hasToggle: true)
233-
promptTextField
234-
case .promptToCode:
235-
continuousModeToggle
236-
generateDescriptionToggle
237-
systemPromptTextField(title: "Extra System Prompt", hasToggle: false)
238-
promptTextField
239-
case .customChat:
240-
systemPromptTextField(hasToggle: false)
241-
promptTextField
242-
}
243-
}.padding()
244-
}.safeAreaInset(edge: .bottom) {
245-
VStack {
246-
Divider()
247-
248-
VStack {
249-
Text(
250-
"After renaming or adding a custom command, please restart Xcode to refresh the menu."
251-
)
252-
.foregroundStyle(.secondary)
253-
254-
HStack {
255-
Spacer()
256-
Button("Close") {
257-
editingCommand = nil
258-
}
259-
260-
lazy var newCommand = CustomCommand(
261-
commandId: editingCommand?.command.id ?? UUID().uuidString,
262-
name: name,
263-
feature: {
264-
switch commandType {
265-
case .chatWithSelection:
266-
return .chatWithSelection(
267-
extraSystemPrompt: systemPrompt,
268-
prompt: prompt,
269-
useExtraSystemPrompt: usePrompt
270-
)
271-
case .promptToCode:
272-
return .promptToCode(
273-
extraSystemPrompt: systemPrompt,
274-
prompt: prompt,
275-
continuousMode: continuousMode,
276-
generateDescription: generatingPromptToCodeDescription
277-
)
278-
case .customChat:
279-
return .customChat(
280-
systemPrompt: systemPrompt,
281-
prompt: prompt
282-
)
283-
}
284-
}()
285-
)
286-
287-
if editingCommand?.isNew ?? true {
288-
Button("Add") {
289-
guard !settings.illegalNames.contains(newCommand.name) else {
290-
toast(Text("Command name is illegal."), .error)
291-
return
292-
}
293-
guard !newCommand.name.isEmpty else {
294-
toast(Text("Command name cannot be empty."), .error)
295-
return
296-
}
297-
settings.customCommands.append(newCommand)
298-
editingCommand?.isNew = false
299-
editingCommand?.command = newCommand
300-
301-
toast(Text("The command is created."), .info)
302-
}
303-
} else {
304-
Button("Save") {
305-
guard !settings.illegalNames.contains(newCommand.name)
306-
|| newCommand.name == originalName
307-
else {
308-
toast(Text("Command name is illegal."), .error)
309-
return
310-
}
311-
guard !newCommand.name.isEmpty else {
312-
toast(Text("Command name cannot be empty."), .error)
313-
return
314-
}
315-
316-
if let index = settings.customCommands.firstIndex(where: {
317-
$0.id == newCommand.id
318-
}) {
319-
settings.customCommands[index] = newCommand
320-
} else {
321-
settings.customCommands.append(newCommand)
322-
}
323-
324-
toast(Text("The command is updated."), .info)
325-
}
326-
}
327-
}
328-
}
329-
.padding(.horizontal)
330-
}
331-
.padding(.bottom)
332-
.background(.regularMaterial)
333-
}
334-
}
335-
336-
@ViewBuilder
337-
var promptTextField: some View {
338-
VStack(alignment: .leading, spacing: 4) {
339-
Text("Prompt")
340-
EditableText(text: $prompt)
341-
}
342-
.padding(.vertical, 4)
343-
}
344-
345-
@ViewBuilder
346-
func systemPromptTextField(title: String? = nil, hasToggle: Bool) -> some View {
347-
VStack(alignment: .leading, spacing: 4) {
348-
if hasToggle {
349-
Toggle(title ?? "System Prompt", isOn: $usePrompt)
350-
} else {
351-
Text(title ?? "System Prompt")
352-
}
353-
EditableText(text: $systemPrompt)
354-
}
355-
.padding(.vertical, 4)
356-
}
357-
358-
var continuousModeToggle: some View {
359-
Toggle("Continuous Mode", isOn: $continuousMode)
360-
}
361-
362-
var generateDescriptionToggle: some View {
363-
Toggle("Generate Description", isOn: $generatingPromptToCodeDescription)
364-
}
365-
}
366-
367135
// MARK: - Previews
368136

369137
struct CustomCommandView_Preview: PreviewProvider {
@@ -403,25 +171,3 @@ struct CustomCommandView_Preview: PreviewProvider {
403171
}
404172
}
405173

406-
struct EditCustomCommandView_Preview: PreviewProvider {
407-
static var previews: some View {
408-
EditCustomCommandView(
409-
editingCommand: .constant(CustomCommandView.EditingCommand(
410-
isNew: false,
411-
command: .init(
412-
commandId: "4",
413-
name: "Explain Code",
414-
feature: .promptToCode(
415-
extraSystemPrompt: nil,
416-
prompt: "Hello",
417-
continuousMode: false,
418-
generateDescription: true
419-
)
420-
)
421-
)),
422-
settings: .init(customCommands: .init(wrappedValue: [], "CustomCommandView_Preview"))
423-
)
424-
.frame(width: 800)
425-
}
426-
}
427-

0 commit comments

Comments
 (0)