mirror of
https://github.com/MustangYM/OSXChatGpt.git
synced 2025-11-25 03:15:08 +08:00
add cloud more Data
This commit is contained in:
@@ -29,6 +29,7 @@
|
||||
CB0F5A6029D059C4005B71D2 /* SplashCodeSyntaxHighlighter.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB0F5A5E29D059C4005B71D2 /* SplashCodeSyntaxHighlighter.swift */; };
|
||||
CB1DCAC629B4F09F00B1D4E1 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = CB1DCAC529B4F09F00B1D4E1 /* Assets.xcassets */; };
|
||||
CB1DCAC929B4F09F00B1D4E1 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = CB1DCAC829B4F09F00B1D4E1 /* Preview Assets.xcassets */; };
|
||||
CB1F1DD029DDBA0B008CFD0B /* AIPromptPopView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB1F1DCF29DDBA0B008CFD0B /* AIPromptPopView.swift */; };
|
||||
CB2449F829D721F3006EE829 /* SystemManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB2449F729D721F3006EE829 /* SystemManager.swift */; };
|
||||
CB2449FA29D7FE38006EE829 /* ServerManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB2449F929D7FE38006EE829 /* ServerManager.swift */; };
|
||||
CB27655C29D1C12C00897E0E /* MarkdownView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB27655B29D1C12C00897E0E /* MarkdownView.swift */; };
|
||||
@@ -72,6 +73,7 @@
|
||||
CB1DCAC529B4F09F00B1D4E1 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
CB1DCAC829B4F09F00B1D4E1 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
|
||||
CB1DCACA29B4F09F00B1D4E1 /* OSXChatGPT.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = OSXChatGPT.entitlements; sourceTree = "<group>"; };
|
||||
CB1F1DCF29DDBA0B008CFD0B /* AIPromptPopView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AIPromptPopView.swift; sourceTree = "<group>"; };
|
||||
CB228EA129CD4949006B3559 /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; };
|
||||
CB2449F729D721F3006EE829 /* SystemManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SystemManager.swift; sourceTree = "<group>"; };
|
||||
CB2449F929D7FE38006EE829 /* ServerManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerManager.swift; sourceTree = "<group>"; };
|
||||
@@ -136,6 +138,7 @@
|
||||
CB2F972129CED6AE004EBD96 /* ChatRoomInputView.swift */,
|
||||
182B436429BC5C8700F06778 /* UserInitializeView.swift */,
|
||||
CB27656529D1DA9800897E0E /* AIPromptView.swift */,
|
||||
CB1F1DCF29DDBA0B008CFD0B /* AIPromptPopView.swift */,
|
||||
CB27657429D33D7A00897E0E /* AIPromptInputView.swift */,
|
||||
182B436129BC5C8700F06778 /* View.swift */,
|
||||
182B437A29BC5FBE00F06778 /* EnterAPIView.swift */,
|
||||
@@ -348,6 +351,7 @@
|
||||
CB2F972829CEFB65004EBD96 /* ChatRoomToolBar.swift in Sources */,
|
||||
CBC4B12329B8D28D00650296 /* Message+CoreDataClass.swift in Sources */,
|
||||
CB27657329D30F1400897E0E /* AIPromptViewMdoel.swift in Sources */,
|
||||
CB1F1DD029DDBA0B008CFD0B /* AIPromptPopView.swift in Sources */,
|
||||
CB27655C29D1C12C00897E0E /* MarkdownView.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
||||
@@ -13,11 +13,13 @@ class AIPromptSessionViewMdoel: ObservableObject {
|
||||
@Published var selectedItem :Prompt?
|
||||
|
||||
func fetchAllPrompts(session: Conversation) {
|
||||
let completedDateSort = NSSortDescriptor(keyPath: \Prompt.createdDate, ascending: false)
|
||||
var aa: [Prompt] = CoreDataManager.shared.fetch("Prompt", sorting: [completedDateSort])
|
||||
if (aa.count == 0) {
|
||||
aa = AIPromptSessionViewMdoel.createDefaultPrompt()
|
||||
}
|
||||
let request: NSFetchRequest<Prompt> = Prompt.fetchRequest()
|
||||
request.predicate = NSPredicate(format: "type == %d", 3)
|
||||
let timestampSortDescriptor = NSSortDescriptor(key: "createdDate", ascending: false)
|
||||
request.sortDescriptors = [timestampSortDescriptor]
|
||||
|
||||
var aa: [Prompt] = CoreDataManager.shared.fetch(request: request)
|
||||
|
||||
if let prompt = session.prompt {
|
||||
if aa.contains(where: {$0.id == prompt.id && $0.type != 1}) {
|
||||
selectedItem = prompt
|
||||
@@ -35,88 +37,31 @@ class AIPromptSessionViewMdoel: ObservableObject {
|
||||
}
|
||||
CoreDataManager.shared.saveData()
|
||||
allPrompts = aa
|
||||
|
||||
// 获取云端数据
|
||||
HTTPClient.getPrompt { [weak self] datas, err in
|
||||
print("获取云端数据成功")
|
||||
guard let self = self else { return }
|
||||
var temp: [Prompt] = []
|
||||
datas.forEach { json in
|
||||
if let jsonData = json as? [String: Any] {
|
||||
if let idString = jsonData["idString"] as? String {
|
||||
if !self.allPrompts.contains(where: {$0.idString == idString}) {
|
||||
let p = Prompt(context: CoreDataManager.shared.container.viewContext)
|
||||
p.id = UUID(uuidString: idString)
|
||||
p.createdDate = Date()
|
||||
p.author = jsonData["author"] as? String
|
||||
p.title = jsonData["title"] as? String
|
||||
p.prompt = jsonData["prompt"] as? String
|
||||
p.hexColor = NSColor.randomColor().toHexString()
|
||||
CoreDataManager.shared.saveData()
|
||||
temp.append(p)
|
||||
}
|
||||
}else {
|
||||
let p = Prompt(context: CoreDataManager.shared.container.viewContext)
|
||||
p.id = UUID()
|
||||
p.createdDate = Date()
|
||||
p.author = jsonData["author"] as? String
|
||||
p.title = jsonData["title"] as? String
|
||||
p.prompt = jsonData["prompt"] as? String
|
||||
p.hexColor = NSColor.randomColor().toHexString()
|
||||
CoreDataManager.shared.saveData()
|
||||
temp.append(p)
|
||||
}
|
||||
}
|
||||
}
|
||||
if temp.count > 0 {
|
||||
DispatchQueue.main.async {
|
||||
self.allPrompts += temp
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func deletePrompt(prompt: Prompt) {
|
||||
if prompt.type == 1 {
|
||||
return
|
||||
}
|
||||
allPrompts.removeAll(where: {$0.id == prompt.id})
|
||||
prompt.type = 2
|
||||
if let idx = allPrompts.firstIndex(where: {$0.id == prompt.id}) {
|
||||
allPrompts[idx].type = 2
|
||||
}
|
||||
allPrompts.removeAll(where: {$0.type == 2 })
|
||||
if prompt.id == selectedItem?.id {
|
||||
selectedItem = allPrompts.first
|
||||
}
|
||||
CoreDataManager.shared.delete(object: prompt)
|
||||
}
|
||||
|
||||
static func createDefaultPrompt() -> [Prompt] {
|
||||
var temp: [Prompt] = []
|
||||
let prompt1 = Prompt(context: CoreDataManager.shared.container.viewContext)
|
||||
prompt1.id = UUID()
|
||||
prompt1.createdDate = Date()
|
||||
prompt1.title = "翻译"
|
||||
prompt1.prompt = "翻译我说的任何中文或英文。只返回翻译结果,不解释它"
|
||||
prompt1.hexColor = NSColor.randomColor().toHexString()
|
||||
temp.append(prompt1)
|
||||
|
||||
|
||||
CoreDataManager.shared.saveData()
|
||||
return temp
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class AIPromptViewMdoel: ObservableObject {
|
||||
let isSession: Bool
|
||||
@Published var allPrompts: [Prompt] = []
|
||||
@Published var prompts: [Prompt] = []
|
||||
|
||||
init(isSession: Bool) {
|
||||
self.isSession = isSession
|
||||
if isSession {
|
||||
fetchPrompts()
|
||||
}else {
|
||||
fetchAllPrompts()
|
||||
}
|
||||
init() {
|
||||
fetchAllPrompts()
|
||||
}
|
||||
|
||||
static func randomColor() -> Color {
|
||||
@@ -134,20 +79,111 @@ class AIPromptViewMdoel: ObservableObject {
|
||||
prompt.author = author
|
||||
prompt.id = UUID()
|
||||
prompt.createdDate = Date()
|
||||
prompt.type = 2
|
||||
allPrompts.insert(prompt, at: 0)
|
||||
CoreDataManager.shared.saveData()
|
||||
if isToggleOn {
|
||||
HTTPClient.uploadPrompt(prompt: prompt)
|
||||
}
|
||||
|
||||
}
|
||||
func updatePrompt(prompt: Prompt, isToggleOn: Bool) {
|
||||
if let idx = allPrompts.firstIndex(where: {$0.id == prompt.id}) {
|
||||
allPrompts[idx] = prompt
|
||||
}else {
|
||||
allPrompts.append(prompt)
|
||||
}
|
||||
|
||||
CoreDataManager.shared.saveData()
|
||||
if isToggleOn {
|
||||
HTTPClient.uploadPrompt(prompt: prompt)
|
||||
}
|
||||
|
||||
}
|
||||
func deletePrompt(prompt: Prompt) {
|
||||
allPrompts.removeAll(where: {$0.id == prompt.id})
|
||||
CoreDataManager.shared.delete(object: prompt)
|
||||
CoreDataManager.shared.saveData()
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
extension AIPromptViewMdoel {
|
||||
private func fetchAllPrompts() {
|
||||
let completedDateSort = NSSortDescriptor(keyPath: \Prompt.serial, ascending: false)
|
||||
let aa: [Prompt] = CoreDataManager.shared.fetch("Prompt", sorting: [completedDateSort])
|
||||
//删除云端数据
|
||||
let deleteRequest: NSFetchRequest<Prompt> = Prompt.fetchRequest()
|
||||
deleteRequest.predicate = NSPredicate(format: "type == 0")
|
||||
CoreDataManager.shared.delete(request: deleteRequest)
|
||||
|
||||
//检索本地自定义数据
|
||||
let request: NSFetchRequest<Prompt> = Prompt.fetchRequest()
|
||||
let type2Predicate = NSPredicate(format: "type == %d", 2)
|
||||
let type3Predicate = NSPredicate(format: "type == %d", 3)
|
||||
let compoundPredicate = NSCompoundPredicate(orPredicateWithSubpredicates: [type2Predicate, type3Predicate])
|
||||
request.predicate = compoundPredicate
|
||||
|
||||
|
||||
let typeSortDescriptor = NSSortDescriptor(key: "type", ascending: false)
|
||||
let timestampSortDescriptor = NSSortDescriptor(key: "createdDate", ascending: false)
|
||||
request.sortDescriptors = [typeSortDescriptor, timestampSortDescriptor]
|
||||
var aa: [Prompt] = CoreDataManager.shared.fetch(request: request)
|
||||
|
||||
//移除无效数据
|
||||
let remove = aa.filter { $0.title == nil || $0.prompt == nil || $0.id == nil}
|
||||
CoreDataManager.shared.delete(objects: remove)
|
||||
aa.removeAll { $0.title == nil || $0.prompt == nil || $0.id == nil }
|
||||
|
||||
//赋值
|
||||
allPrompts = aa
|
||||
|
||||
|
||||
// 获取云端数据
|
||||
HTTPClient.getPrompt { [weak self] datas, err in
|
||||
print("云端数据请求成功")
|
||||
guard let self = self else { return }
|
||||
var temp: [Prompt] = []
|
||||
datas.forEach { json in
|
||||
if let jsonData = json as? [String: Any] {
|
||||
if let idString = jsonData["idString"] as? String {
|
||||
if !self.allPrompts.contains(where: {$0.idString == idString}) {
|
||||
let p = Prompt(context: CoreDataManager.shared.container.viewContext)
|
||||
p.id = UUID(uuidString: idString)
|
||||
p.type = 0
|
||||
p.createdDate = Date()
|
||||
p.author = jsonData["author"] as? String
|
||||
p.title = jsonData["title"] as? String
|
||||
p.prompt = jsonData["prompt"] as? String
|
||||
p.hexColor = NSColor.randomColor().toHexString()
|
||||
p.cloudId = jsonData["cloudId"] as? Int32 ?? 0
|
||||
CoreDataManager.shared.saveData()
|
||||
temp.append(p)
|
||||
}
|
||||
}else {
|
||||
let p = Prompt(context: CoreDataManager.shared.container.viewContext)
|
||||
p.id = UUID()
|
||||
p.type = 0
|
||||
p.createdDate = Date()
|
||||
p.author = jsonData["author"] as? String
|
||||
p.title = jsonData["title"] as? String
|
||||
p.prompt = jsonData["prompt"] as? String
|
||||
p.cloudId = jsonData["cloudId"] as? Int32 ?? 0
|
||||
p.hexColor = NSColor.randomColor().toHexString()
|
||||
CoreDataManager.shared.saveData()
|
||||
temp.append(p)
|
||||
}
|
||||
}
|
||||
}
|
||||
if temp.count > 0 {
|
||||
self.allPrompts.forEach { p in
|
||||
temp.removeAll(where: {$0.cloudId == p.cloudId})
|
||||
}
|
||||
DispatchQueue.main.async {
|
||||
self.allPrompts += temp
|
||||
}
|
||||
}else {
|
||||
print("获取云端数据失败!!!需要更新token")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func fetchPrompts() {
|
||||
|
||||
@@ -427,9 +427,9 @@ extension ViewModel {
|
||||
func fetchConversations() {
|
||||
let completedDateSort = NSSortDescriptor(keyPath: \Conversation.updateData, ascending: false)
|
||||
var aa: [Conversation] = CoreDataManager.shared.fetch("Conversation", sorting: [completedDateSort])
|
||||
let remove = aa.filter { $0.lastMessage == nil}
|
||||
let remove = aa.filter { $0.lastMessage == nil && $0.prompt == nil }
|
||||
CoreDataManager.shared.delete(objects: remove)
|
||||
aa.removeAll { $0.lastMessage == nil}
|
||||
aa.removeAll { $0.lastMessage == nil && $0.prompt == nil}
|
||||
conversations = aa
|
||||
}
|
||||
private func fetchConversation(sesstionId: String) -> Conversation? {
|
||||
|
||||
@@ -26,4 +26,5 @@ public class Prompt: NSManagedObject {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -10,6 +10,8 @@ import Foundation
|
||||
import CoreData
|
||||
|
||||
|
||||
|
||||
|
||||
extension Prompt {
|
||||
|
||||
@nonobjc public class func fetchRequest() -> NSFetchRequest<Prompt> {
|
||||
@@ -25,6 +27,7 @@ extension Prompt {
|
||||
@NSManaged public var type: Int16
|
||||
@NSManaged public var hexColor: String?
|
||||
@NSManaged public var sesstion: NSSet?
|
||||
@NSManaged public var cloudId: Int32
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -285,19 +285,22 @@ extension HTTPClient {
|
||||
code = response.statusCode
|
||||
}
|
||||
if let data = data,
|
||||
let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any],
|
||||
let base64Str = json["content"] as? String {
|
||||
let base64String = base64Str.replacingOccurrences(of: "\n", with: "")
|
||||
if let da = NSData(base64Encoded: base64String, options: NSData.Base64DecodingOptions.init(rawValue: 0)),
|
||||
let dataString = String(data: da as Data, encoding: .utf8),
|
||||
let jsonData = dataString.data(using: .utf8),
|
||||
let jsonObject = try? JSONSerialization.jsonObject(with: jsonData, options: []) as? [Any] {
|
||||
callback(jsonObject, nil)
|
||||
let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any] {
|
||||
if let base64Str = json["content"] as? String {
|
||||
let base64String = base64Str.replacingOccurrences(of: "\n", with: "")
|
||||
if let da = NSData(base64Encoded: base64String, options: NSData.Base64DecodingOptions.init(rawValue: 0)),
|
||||
let dataString = String(data: da as Data, encoding: .utf8),
|
||||
let jsonData = dataString.data(using: .utf8),
|
||||
let jsonObject = try? JSONSerialization.jsonObject(with: jsonData, options: []) as? [Any] {
|
||||
callback(jsonObject, nil)
|
||||
}else {
|
||||
callback([], "data error code:\(code)")
|
||||
}
|
||||
}else {
|
||||
//获取不到数据,需要更新token
|
||||
callback([], "data error code:\(code)")
|
||||
|
||||
}
|
||||
}else {
|
||||
callback([], "data error code:\(code)")
|
||||
}
|
||||
}
|
||||
task.resume()
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
</entity>
|
||||
<entity name="Prompt" representedClassName=".Prompt" syncable="YES">
|
||||
<attribute name="author" optional="YES" attributeType="String"/>
|
||||
<attribute name="cloudId" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="createdDate" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="hexColor" optional="YES" attributeType="String"/>
|
||||
<attribute name="id" optional="YES" attributeType="UUID" usesScalarValueType="NO"/>
|
||||
|
||||
@@ -8,10 +8,10 @@
|
||||
import SwiftUI
|
||||
|
||||
struct AIPromptInputView: View {
|
||||
@StateObject var viewModel = AIPromptViewMdoel(isSession: true)
|
||||
@StateObject var viewModel: AIPromptViewMdoel
|
||||
@Binding var isPresented: Bool
|
||||
@State private var title: String = ""
|
||||
@State private var prompt: String = ""
|
||||
@State private var content: String = ""
|
||||
@State private var author: String = ""
|
||||
@State private var isToggleOn: Bool = true
|
||||
@Environment(\.colorScheme) private var colorScheme
|
||||
@@ -22,7 +22,7 @@ struct AIPromptInputView: View {
|
||||
VStack {
|
||||
VStack {
|
||||
Spacer()
|
||||
Text("自定义提示")
|
||||
Text("自定义添加")
|
||||
.font(.title3)
|
||||
.foregroundColor((colorScheme == .dark) ? .white.opacity(0.9) :.black.opacity(0.9))
|
||||
Spacer()
|
||||
@@ -73,7 +73,7 @@ struct AIPromptInputView: View {
|
||||
}.padding(.top, 5)
|
||||
|
||||
HStack {
|
||||
TextEditor(text: $prompt)
|
||||
TextEditor(text: $content)
|
||||
.font(Font.system(size: 13))
|
||||
.padding(8)
|
||||
.background((colorScheme == .dark) ? .gray.opacity(0.1) :.white.opacity(0.9))
|
||||
@@ -90,12 +90,12 @@ struct AIPromptInputView: View {
|
||||
.padding(.leading, 20)
|
||||
.padding(.bottom, 0)
|
||||
.frame(height: 18)
|
||||
Text("(选填)")
|
||||
.font(Font.system(size: 11))
|
||||
.padding(.top, 5)
|
||||
.foregroundColor(.gray.opacity(0.6))
|
||||
.frame(height: 18)
|
||||
Text("后台审核通过后可分享给其他人")
|
||||
// Text("(选填)")
|
||||
// .font(Font.system(size: 11))
|
||||
// .padding(.top, 5)
|
||||
// .foregroundColor(.gray.opacity(0.6))
|
||||
// .frame(height: 18)
|
||||
Text("分享您的修饰语到词库")
|
||||
.font(Font.system(size: 10))
|
||||
.padding(.top, 5)
|
||||
.foregroundColor(.gray.opacity(0.6))
|
||||
@@ -119,7 +119,7 @@ struct AIPromptInputView: View {
|
||||
|
||||
VStack {
|
||||
HStack {
|
||||
Toggle("是否上传至云端", isOn: $isToggleOn)
|
||||
Toggle("是否分享", isOn: $isToggleOn)
|
||||
.padding()
|
||||
.foregroundColor(.gray)
|
||||
.font(Font.system(size: 13))
|
||||
@@ -132,7 +132,11 @@ struct AIPromptInputView: View {
|
||||
}
|
||||
|
||||
Button {
|
||||
viewModel.addPrompt(title: title, content: prompt, author: author, isToggleOn: isToggleOn)
|
||||
if title.isEmpty || content.isEmpty {
|
||||
self.isPresented = false
|
||||
return
|
||||
}
|
||||
viewModel.addPrompt(title: title, content: content, author: author, isToggleOn: isToggleOn)
|
||||
self.isPresented = false
|
||||
|
||||
} label: {
|
||||
|
||||
154
OSXChatGPT/OSXChatGPT/WindowView/AIPromptPopView.swift
Normal file
154
OSXChatGPT/OSXChatGPT/WindowView/AIPromptPopView.swift
Normal file
@@ -0,0 +1,154 @@
|
||||
//
|
||||
// AIPromptPopView.swift
|
||||
// OSXChatGPT
|
||||
//
|
||||
// Created by CoderChan on 2023/4/5.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct AIPromptPopView: View {
|
||||
@Environment(\.colorScheme) private var colorScheme
|
||||
@EnvironmentObject var viewModel: ViewModel
|
||||
@StateObject var data = AIPromptSessionViewMdoel()
|
||||
@Binding var showInputView: Bool
|
||||
@Binding var showPopover: Bool
|
||||
@State private var isPresented = false
|
||||
|
||||
var titleColor: Color {
|
||||
switch colorScheme {
|
||||
case .dark:
|
||||
return Color.white.opacity(0.9)
|
||||
default:
|
||||
return Color.black.opacity(0.9)
|
||||
}
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
ZStack {
|
||||
VStack {
|
||||
Spacer()
|
||||
Text("选择提示")
|
||||
.font(.title3)
|
||||
.foregroundColor(titleColor)
|
||||
Spacer()
|
||||
|
||||
}
|
||||
HStack {
|
||||
Button {
|
||||
if let prompt = data.selectedItem {
|
||||
viewModel.updateConversation(sesstionId: viewModel.currentConversation!.sesstionId, prompt: prompt)
|
||||
}
|
||||
self.showPopover = false
|
||||
} label: {
|
||||
Text("确定")
|
||||
}.padding(10)
|
||||
Spacer()
|
||||
Button {
|
||||
self.showPopover = false
|
||||
withAnimation {
|
||||
viewModel.currentConversation = nil
|
||||
viewModel.showAIPrompt = true
|
||||
}
|
||||
} label: {
|
||||
Text("词库")
|
||||
}
|
||||
.padding(10)
|
||||
}
|
||||
}
|
||||
List(data.allPrompts) { item in
|
||||
AIPromptPopCellView(item: item, isSelected: data.selectedItem == item)
|
||||
.onTapGesture {
|
||||
data.selectedItem = item
|
||||
}
|
||||
.contextMenu {
|
||||
Button(action: {
|
||||
withAnimation {
|
||||
data.deletePrompt(prompt: item)
|
||||
}
|
||||
}) {
|
||||
Text("移除快捷方式")
|
||||
}
|
||||
}
|
||||
}.frame(width: 560, height: 380)
|
||||
.onAppear {
|
||||
if let conversation = viewModel.currentConversation {
|
||||
data.fetchAllPrompts(session: conversation)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct AIPromptPopCellView: View {
|
||||
@Environment(\.colorScheme) private var colorScheme
|
||||
let item: Prompt
|
||||
let isSelected: Bool
|
||||
var body: some View {
|
||||
HStack {
|
||||
if self.isSelected {
|
||||
Image(systemName: "checkmark.square.fill")
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.frame(width: 20, height: 20)
|
||||
.foregroundColor((colorScheme == .dark) ? .white.opacity(0.8) : .white)
|
||||
.background(Color.blue)
|
||||
.clipShape(Circle())
|
||||
.padding(5)
|
||||
|
||||
} else {
|
||||
Circle()
|
||||
.stroke(Color.blue, lineWidth: 1)
|
||||
.frame(width: 20, height: 20)
|
||||
.padding(5)
|
||||
}
|
||||
if item.type == 1 {
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
HStack {
|
||||
Text("【默认无修饰语】")
|
||||
.font(Font.system(size: 15))
|
||||
.foregroundColor((colorScheme == .dark) ? .white.opacity(0.8) : .white)
|
||||
.padding(.trailing, 6)
|
||||
.padding(.bottom, 6)
|
||||
Text("当前选中的修饰语")
|
||||
.font(Font.system(size: 14))
|
||||
.foregroundColor((colorScheme == .dark) ? .white.opacity(0.8) : .white)
|
||||
.padding(.bottom, 6)
|
||||
Spacer()
|
||||
Text("(没有修饰语请到词库添加)")
|
||||
.font(Font.system(size: 11))
|
||||
.foregroundColor((colorScheme == .dark) ? .white.opacity(0.8) : .white)
|
||||
.padding(.bottom, 6)
|
||||
}
|
||||
Text("每个会话只能选择一个修饰语, 也可以自定义添加修饰语")
|
||||
.font(.subheadline)
|
||||
.foregroundColor((colorScheme == .dark) ? .white.opacity(0.6) : .white)
|
||||
}.padding(.leading, 2)
|
||||
}else {
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
Text(item.title ?? "")
|
||||
.font(.headline)
|
||||
.foregroundColor((colorScheme == .dark) ? .white.opacity(0.8) : .white)
|
||||
.foregroundColor(.primary)
|
||||
.padding(.bottom, 6)
|
||||
Text(item.prompt ?? "")
|
||||
.font(.subheadline)
|
||||
.foregroundColor((colorScheme == .dark) ? .white.opacity(0.6) : .white)
|
||||
}.padding(.leading, 2)
|
||||
}
|
||||
|
||||
Spacer()
|
||||
}
|
||||
.frame(height: 60)
|
||||
.padding(.vertical, 5)
|
||||
.padding(.horizontal, 10)
|
||||
.background(
|
||||
item.color.brightness((self.colorScheme == .dark) ? -0.5 : -0.2)
|
||||
)
|
||||
.cornerRadius(6)
|
||||
}
|
||||
}
|
||||
//struct AIPromptPopView_Previews: PreviewProvider {
|
||||
// static var previews: some View {
|
||||
// AIPromptPopView()
|
||||
// }
|
||||
//}
|
||||
@@ -7,24 +7,144 @@
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct AIPromptDetailView: View {
|
||||
let prompt: Prompt
|
||||
@StateObject var data: AIPromptViewMdoel
|
||||
@Environment(\.presentationMode) var presentationMode
|
||||
@Environment(\.colorScheme) private var colorScheme
|
||||
var body: some View {
|
||||
GeometryReader { geometry in
|
||||
ZStack {
|
||||
VStack(alignment: .leading) {
|
||||
HStack {
|
||||
VStack(alignment: .leading) {
|
||||
Text(prompt.title ?? "")
|
||||
.font(.headline)
|
||||
.foregroundColor((colorScheme == .dark) ? .white.opacity(0.8) : .white)
|
||||
.padding(.bottom, 6)
|
||||
HStack {
|
||||
Button {
|
||||
if prompt.type == 3 {
|
||||
//移除,
|
||||
prompt.type = 2
|
||||
data.updatePrompt(prompt: prompt, isToggleOn: false)
|
||||
presentationMode.wrappedValue.dismiss()
|
||||
}else {
|
||||
//添加,
|
||||
prompt.type = 3
|
||||
data.updatePrompt(prompt: prompt, isToggleOn: false)
|
||||
presentationMode.wrappedValue.dismiss()
|
||||
}
|
||||
} label: {
|
||||
HStack {
|
||||
if prompt.type == 3 {
|
||||
Image(systemName: "minus.square")
|
||||
.resizable()
|
||||
.frame(width: 10, height: 10)
|
||||
.foregroundColor(.white)
|
||||
.padding(0)
|
||||
Text("移除快捷方式")
|
||||
.padding(0)
|
||||
.foregroundColor(.white)
|
||||
}else {
|
||||
HStack {
|
||||
Image(systemName: "plus.app")
|
||||
.resizable()
|
||||
.frame(width: 10, height: 10)
|
||||
.foregroundColor(.white)
|
||||
.padding(0)
|
||||
Text("添加快捷方式")
|
||||
.padding(0)
|
||||
.foregroundColor(.white)
|
||||
}
|
||||
}
|
||||
}
|
||||
.padding(EdgeInsets(top: 6, leading: 10, bottom: 6, trailing: 10))
|
||||
|
||||
}
|
||||
.buttonStyle(LinkButtonStyle())
|
||||
.background(
|
||||
RoundedRectangle(cornerRadius: 20)
|
||||
.stroke(Color.white, lineWidth: 0.5)
|
||||
)
|
||||
|
||||
if prompt.type == 2 {
|
||||
Button {
|
||||
data.deletePrompt(prompt: prompt)
|
||||
} label: {
|
||||
HStack {
|
||||
Image(systemName: "trash.slash")
|
||||
.resizable()
|
||||
.frame(width: 10, height: 10)
|
||||
.foregroundColor(.white)
|
||||
.padding(0)
|
||||
Text("删除数据")
|
||||
.padding(0)
|
||||
.foregroundColor(.white)
|
||||
}
|
||||
.padding(EdgeInsets(top: 6, leading: 10, bottom: 6, trailing: 10))
|
||||
|
||||
}.buttonStyle(LinkButtonStyle())
|
||||
.background(
|
||||
RoundedRectangle(cornerRadius: 20)
|
||||
.stroke(Color.white, lineWidth: 0.5)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}.padding(.leading, 6)
|
||||
Spacer()
|
||||
}.padding(.bottom, 10)
|
||||
ScrollView {
|
||||
HStack() {
|
||||
Text(prompt.prompt ?? "")
|
||||
.font(.subheadline)
|
||||
.foregroundColor((colorScheme == .dark) ? .white.opacity(0.6) : .white)
|
||||
.padding(10)
|
||||
Spacer()
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
.frame(width: geometry.size.width - 30, height: geometry.size.height - 120)
|
||||
.background(.white.opacity(0.1))
|
||||
.cornerRadius(5)
|
||||
}
|
||||
|
||||
if let author = prompt.author {
|
||||
VStack {
|
||||
HStack {
|
||||
Spacer()
|
||||
Text("\(author)")
|
||||
.foregroundColor((colorScheme == .dark) ? .white.opacity(0.8) : .white)
|
||||
.font(.system(size: 11))
|
||||
.padding(.top, 5)
|
||||
}
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
.frame(width: geometry.size.width, height: geometry.size.height)
|
||||
.cornerRadius(6)
|
||||
.background(
|
||||
prompt.color.brightness((self.colorScheme == .dark) ? -0.5 : -0.2)
|
||||
)
|
||||
}
|
||||
.frame(width: 500, height: 350)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
struct AIPromptView: View {
|
||||
let sesstionId: String? //有则表示在会话中打开
|
||||
|
||||
var body: some View {
|
||||
Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/)
|
||||
}
|
||||
}
|
||||
|
||||
struct AIPromptPopView: View {
|
||||
@Environment(\.colorScheme) private var colorScheme
|
||||
@EnvironmentObject var viewModel: ViewModel
|
||||
@StateObject var data = AIPromptSessionViewMdoel()
|
||||
@Binding var showInputView: Bool
|
||||
@Binding var showPopover: Bool
|
||||
@StateObject var data = AIPromptViewMdoel()
|
||||
@State private var isPresented = false
|
||||
@State private var showInputView = false
|
||||
|
||||
var titleColor: Color {
|
||||
switch colorScheme {
|
||||
@@ -36,109 +156,102 @@ struct AIPromptPopView: View {
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
ZStack {
|
||||
VStack {
|
||||
Spacer()
|
||||
Text("选择提示")
|
||||
.font(.title3)
|
||||
.foregroundColor(titleColor)
|
||||
Spacer()
|
||||
|
||||
}
|
||||
HStack {
|
||||
Spacer()
|
||||
Button {
|
||||
self.showPopover = false
|
||||
self.showInputView = true
|
||||
} label: {
|
||||
Text("自定义")
|
||||
GeometryReader { geometry in
|
||||
if data.allPrompts.count > 0 {
|
||||
ScrollView {
|
||||
LazyVStack(spacing: 8) {
|
||||
ForEach(data.allPrompts) { (item) in
|
||||
AIPromptCellView(data: data, item: item)
|
||||
}
|
||||
}
|
||||
.background(Color.clear)
|
||||
}
|
||||
.padding(10)
|
||||
.padding(.leading, 16)
|
||||
.padding(.trailing, 16)
|
||||
.frame(width: geometry.size.width, height: geometry.size.height - 50)
|
||||
|
||||
.onAppear {
|
||||
|
||||
}
|
||||
}else {
|
||||
VStack(alignment: .center) {
|
||||
Text("请点击右上角自定义添加新的修饰语")
|
||||
}.frame(width: geometry.size.width, height: geometry.size.height - 50)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
.padding(.top, 1)
|
||||
.toolbar {
|
||||
Spacer()
|
||||
Button(action: {
|
||||
showInputView.toggle()
|
||||
}) {
|
||||
Image(systemName: "plus")
|
||||
}
|
||||
}
|
||||
List(data.allPrompts) { item in
|
||||
AIPromptPopCellView(item: item, isSelected: data.selectedItem == item) {
|
||||
data.selectedItem = item
|
||||
}.contextMenu {
|
||||
Button(action: {
|
||||
data.deletePrompt(prompt: item)
|
||||
}) {
|
||||
Text("删除")
|
||||
}
|
||||
// Button(action: {
|
||||
//
|
||||
// }) {
|
||||
// Text("编辑")
|
||||
// }
|
||||
}
|
||||
}.frame(width: 560, height: 380)
|
||||
.onAppear {
|
||||
if let conversation = viewModel.currentConversation {
|
||||
data.fetchAllPrompts(session: conversation)
|
||||
}
|
||||
}
|
||||
.onDisappear {
|
||||
viewModel.updateConversation(sesstionId: viewModel.currentConversation!.sesstionId, prompt: data.selectedItem!)
|
||||
|
||||
}
|
||||
.sheet(isPresented: $showInputView) {
|
||||
AIPromptInputView(viewModel: data, isPresented: $showInputView)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
struct AIPromptPopCellView: View {
|
||||
struct AIPromptCellView: View {
|
||||
@Environment(\.colorScheme) private var colorScheme
|
||||
let item: Prompt
|
||||
let isSelected: Bool
|
||||
let action: () -> Void
|
||||
|
||||
@StateObject var data: AIPromptViewMdoel
|
||||
@State private var isTapped = false
|
||||
@State var item: Prompt
|
||||
var body: some View {
|
||||
HStack {
|
||||
if self.isSelected {
|
||||
Image(systemName: "checkmark.square.fill")
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.frame(width: 20, height: 20)
|
||||
.foregroundColor((colorScheme == .dark) ? .white.opacity(0.8) : .white)
|
||||
.background(Color.blue)
|
||||
.clipShape(Circle())
|
||||
.padding(5)
|
||||
|
||||
} else {
|
||||
Circle()
|
||||
.stroke(Color.blue, lineWidth: 1)
|
||||
.frame(width: 20, height: 20)
|
||||
.padding(5)
|
||||
}
|
||||
if item.type == 1 {
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
HStack {
|
||||
Text("【默认无修饰语】")
|
||||
.font(Font.system(size: 15))
|
||||
.foregroundColor((colorScheme == .dark) ? .white.opacity(0.8) : .white)
|
||||
.padding(.trailing, 6)
|
||||
.padding(.bottom, 6)
|
||||
Text("当前选中的修饰语")
|
||||
.font(Font.system(size: 14))
|
||||
.foregroundColor((colorScheme == .dark) ? .white.opacity(0.8) : .white)
|
||||
.padding(.bottom, 6)
|
||||
}
|
||||
Text("每个会话只能选择一个修饰语, 也可以自定义添加修饰语")
|
||||
.font(.subheadline)
|
||||
.foregroundColor((colorScheme == .dark) ? .white.opacity(0.6) : .white)
|
||||
}.padding(.leading, 2)
|
||||
}else {
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
Text(item.title ?? "")
|
||||
.font(.headline)
|
||||
.foregroundColor((colorScheme == .dark) ? .white.opacity(0.8) : .white)
|
||||
.foregroundColor(.primary)
|
||||
.padding(.bottom, 6)
|
||||
Text(item.prompt ?? "")
|
||||
.font(.subheadline)
|
||||
.foregroundColor((colorScheme == .dark) ? .white.opacity(0.6) : .white)
|
||||
}.padding(.leading, 2)
|
||||
ZStack {
|
||||
VStack(alignment: .leading) {
|
||||
HStack {
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
HStack {
|
||||
if item.type == 3 {
|
||||
Image(systemName: "checkmark.square")
|
||||
.resizable()
|
||||
.frame(width: 10, height: 10)
|
||||
.foregroundColor(.white)
|
||||
.padding(.trailing, 0)
|
||||
}else {
|
||||
Image(systemName: "plus.app")
|
||||
.resizable()
|
||||
.frame(width: 10, height: 10)
|
||||
.foregroundColor(.white)
|
||||
.padding(.trailing, 0)
|
||||
}
|
||||
|
||||
Text(item.title ?? "")
|
||||
.font(.headline)
|
||||
.foregroundColor((colorScheme == .dark) ? .white.opacity(0.8) : .white)
|
||||
.padding(.leading, 0)
|
||||
|
||||
}.padding(.bottom, 6)
|
||||
Text(item.prompt ?? "")
|
||||
.font(.subheadline)
|
||||
.foregroundColor((colorScheme == .dark) ? .white.opacity(0.6) : .white)
|
||||
}.padding(.leading, 2)
|
||||
Spacer()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Spacer()
|
||||
if let author = item.author {
|
||||
VStack {
|
||||
HStack {
|
||||
Spacer()
|
||||
Text("\(author)")
|
||||
.foregroundColor((colorScheme == .dark) ? .white.opacity(0.8) : .white)
|
||||
.font(.system(size: 11))
|
||||
.padding(.top, 5)
|
||||
}
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
.frame(height: 60)
|
||||
.padding(.vertical, 5)
|
||||
@@ -148,13 +261,23 @@ struct AIPromptPopCellView: View {
|
||||
)
|
||||
.cornerRadius(6)
|
||||
.onTapGesture {
|
||||
self.action()
|
||||
self.isTapped.toggle()
|
||||
}
|
||||
.sheet(isPresented: $isTapped) {
|
||||
AIPromptDetailView(prompt: item, data: data)
|
||||
.onTapGesture {
|
||||
isTapped = false
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
struct AIPromptView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
AIPromptView(sesstionId: nil)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//struct AIPromptView_Previews: PreviewProvider {
|
||||
// static var previews: some View {
|
||||
// AIPromptView(sesstionId: nil)
|
||||
// }
|
||||
//}
|
||||
|
||||
@@ -36,6 +36,12 @@ struct ChatRoomToolBar: View {
|
||||
AIPromptPopView(showInputView: $showInputView, showPopover: $showPopover).environmentObject(viewModel)
|
||||
}
|
||||
|
||||
Button("清空消息") {
|
||||
viewModel.messages.removeAll()
|
||||
viewModel.deleteAllMessage(sesstionId: viewModel.currentConversation?.sesstionId ?? "")
|
||||
viewModel.updateConversation(sesstionId: viewModel.currentConversation?.sesstionId ?? "", message: nil)
|
||||
}
|
||||
|
||||
Spacer()
|
||||
if viewModel.showStopAnswerBtn {
|
||||
Button("停止生成") {
|
||||
@@ -47,10 +53,6 @@ struct ChatRoomToolBar: View {
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
.sheet(isPresented: $showInputView) {
|
||||
// 在 sheet 中呈现表格
|
||||
AIPromptInputView(isPresented: $showInputView)
|
||||
}
|
||||
.padding(.leading, 12)
|
||||
.background(Color.clear)
|
||||
|
||||
@@ -21,6 +21,9 @@ struct SessionsView: View {
|
||||
}.buttonStyle(BorderlessButtonStyle())
|
||||
Spacer()
|
||||
Button(action: {
|
||||
viewModel.showUserInitialize = false
|
||||
viewModel.showAIPrompt = false
|
||||
|
||||
// 点击 New Chat 按钮的操作
|
||||
viewModel.currentConversation = viewModel.addNewConversation()
|
||||
viewModel.createNewChat = true
|
||||
@@ -37,13 +40,16 @@ struct SessionsView: View {
|
||||
}
|
||||
.buttonStyle(BorderlessButtonStyle())
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.padding(.leading,10)
|
||||
.padding(.leading,0)
|
||||
|
||||
NavigationLink(destination: UserInitializeView().environmentObject(viewModel), isActive: $viewModel.showUserInitialize) {
|
||||
Button(action: {
|
||||
// 点击右边按钮的操作
|
||||
viewModel.currentConversation = nil//先取消会话
|
||||
// viewModel.showAIPrompt = false
|
||||
if viewModel.currentConversation != nil {
|
||||
viewModel.currentConversation = nil//先取消会话
|
||||
}
|
||||
viewModel.createNewChat = false
|
||||
viewModel.showAIPrompt = false
|
||||
viewModel.showUserInitialize = true
|
||||
KeyboardMonitor.shared.stopKeyMonitor()
|
||||
KeyboardMonitor.shared.stopMonitorPasteboard()
|
||||
@@ -55,27 +61,30 @@ struct SessionsView: View {
|
||||
.cornerRadius(5)
|
||||
}
|
||||
.buttonStyle(PlainButtonStyle())
|
||||
.padding(.trailing, 20)
|
||||
.padding(.trailing, 0)
|
||||
}.buttonStyle(PlainButtonStyle())
|
||||
|
||||
// NavigationLink(destination: AIPromptView(sesstionId: nil), isActive: $viewModel.showAIPrompt) {
|
||||
// Button(action: {
|
||||
// // 点击右边按钮的操作
|
||||
// viewModel.currentConversation = nil//先取消会话
|
||||
// viewModel.showUserInitialize = false
|
||||
// viewModel.showAIPrompt = true
|
||||
// KeyboardMonitor.shared.stopKeyMonitor()
|
||||
// KeyboardMonitor.shared.stopMonitorPasteboard()
|
||||
// }) {
|
||||
// Image(systemName: "swift")
|
||||
// .padding(10)
|
||||
// .foregroundColor(.white)
|
||||
// .background(Color.gray)
|
||||
// .cornerRadius(5)
|
||||
// }
|
||||
// .buttonStyle(PlainButtonStyle())
|
||||
// .padding(.trailing, 10)
|
||||
// }.buttonStyle(PlainButtonStyle())
|
||||
NavigationLink(destination: AIPromptView().environmentObject(viewModel), isActive: $viewModel.showAIPrompt) {
|
||||
Button(action: {
|
||||
// 点击右边按钮的操作
|
||||
if viewModel.currentConversation != nil {
|
||||
viewModel.currentConversation = nil//先取消会话
|
||||
}
|
||||
viewModel.createNewChat = false
|
||||
viewModel.showUserInitialize = false
|
||||
viewModel.showAIPrompt = true
|
||||
KeyboardMonitor.shared.stopKeyMonitor()
|
||||
KeyboardMonitor.shared.stopMonitorPasteboard()
|
||||
}) {
|
||||
Image(systemName: "swift")
|
||||
.padding(10)
|
||||
.foregroundColor(.white)
|
||||
.background(Color.gray)
|
||||
.cornerRadius(5)
|
||||
}
|
||||
.buttonStyle(PlainButtonStyle())
|
||||
.padding(.trailing, 10)
|
||||
}.buttonStyle(PlainButtonStyle())
|
||||
|
||||
|
||||
})
|
||||
@@ -99,6 +108,9 @@ struct SessionsView: View {
|
||||
.leftSessionContentSize()
|
||||
|
||||
}
|
||||
.onAppear {
|
||||
let _ = ServerManager.shared.checkToken()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user