Temporary stuff.
This commit is contained in:
parent
bcfb41917a
commit
9dde163a3c
@ -231,7 +231,6 @@
|
|||||||
);
|
);
|
||||||
mainGroup = 4F772AF82A4706F600D3266B;
|
mainGroup = 4F772AF82A4706F600D3266B;
|
||||||
packageReferences = (
|
packageReferences = (
|
||||||
4F50EF3F2A49CA5E009BD94E /* XCRemoteSwiftPackageReference "Down" */,
|
|
||||||
4F50EF422A49CE31009BD94E /* XCRemoteSwiftPackageReference "MarkdownView" */,
|
4F50EF422A49CE31009BD94E /* XCRemoteSwiftPackageReference "MarkdownView" */,
|
||||||
4F50EF452A49D012009BD94E /* XCRemoteSwiftPackageReference "Highlightr" */,
|
4F50EF452A49D012009BD94E /* XCRemoteSwiftPackageReference "Highlightr" */,
|
||||||
);
|
);
|
||||||
@ -444,6 +443,7 @@
|
|||||||
ENABLE_PREVIEWS = YES;
|
ENABLE_PREVIEWS = YES;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
INFOPLIST_FILE = ChatMasterMind/Info.plist;
|
INFOPLIST_FILE = ChatMasterMind/Info.plist;
|
||||||
|
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities";
|
||||||
"INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES;
|
"INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES;
|
||||||
"INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES;
|
"INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES;
|
||||||
"INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES;
|
"INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES;
|
||||||
@ -485,6 +485,7 @@
|
|||||||
ENABLE_PREVIEWS = YES;
|
ENABLE_PREVIEWS = YES;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
INFOPLIST_FILE = ChatMasterMind/Info.plist;
|
INFOPLIST_FILE = ChatMasterMind/Info.plist;
|
||||||
|
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities";
|
||||||
"INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES;
|
"INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES;
|
||||||
"INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES;
|
"INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES;
|
||||||
"INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES;
|
"INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES;
|
||||||
@ -644,14 +645,6 @@
|
|||||||
/* End XCConfigurationList section */
|
/* End XCConfigurationList section */
|
||||||
|
|
||||||
/* Begin XCRemoteSwiftPackageReference section */
|
/* Begin XCRemoteSwiftPackageReference section */
|
||||||
4F50EF3F2A49CA5E009BD94E /* XCRemoteSwiftPackageReference "Down" */ = {
|
|
||||||
isa = XCRemoteSwiftPackageReference;
|
|
||||||
repositoryURL = "https://github.com/iwasrobbed/Down";
|
|
||||||
requirement = {
|
|
||||||
branch = master;
|
|
||||||
kind = branch;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
4F50EF422A49CE31009BD94E /* XCRemoteSwiftPackageReference "MarkdownView" */ = {
|
4F50EF422A49CE31009BD94E /* XCRemoteSwiftPackageReference "MarkdownView" */ = {
|
||||||
isa = XCRemoteSwiftPackageReference;
|
isa = XCRemoteSwiftPackageReference;
|
||||||
repositoryURL = "https://github.com/LiYanan2004/MarkdownView.git";
|
repositoryURL = "https://github.com/LiYanan2004/MarkdownView.git";
|
||||||
|
|||||||
@ -1,14 +1,5 @@
|
|||||||
{
|
{
|
||||||
"pins" : [
|
"pins" : [
|
||||||
{
|
|
||||||
"identity" : "down",
|
|
||||||
"kind" : "remoteSourceControl",
|
|
||||||
"location" : "https://github.com/iwasrobbed/Down",
|
|
||||||
"state" : {
|
|
||||||
"branch" : "master",
|
|
||||||
"revision" : "e754ab1c80920dd51a8e08290c912ac1c2ac8b58"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"identity" : "highlightr",
|
"identity" : "highlightr",
|
||||||
"kind" : "remoteSourceControl",
|
"kind" : "remoteSourceControl",
|
||||||
|
|||||||
Binary file not shown.
@ -9,30 +9,26 @@ import Foundation
|
|||||||
import SwiftData
|
import SwiftData
|
||||||
|
|
||||||
@Model
|
@Model
|
||||||
final class ChatPair: Identifiable {
|
final class ChatPair {
|
||||||
let id: UUID
|
|
||||||
var timestamp: Date
|
var timestamp: Date
|
||||||
var question: String
|
var question: String
|
||||||
var answer: String?
|
var answer: String?
|
||||||
var previousVersions: [ChatPair]
|
var disabled: Bool
|
||||||
|
|
||||||
init(question: String, answer: String? = nil, timestamp: Date = Date(), previousVersions: [ChatPair] = []) {
|
init(question: String, answer: String? = nil, timestamp: Date = Date(), disabled: Bool = false) {
|
||||||
self.id = UUID()
|
|
||||||
self.question = question
|
self.question = question
|
||||||
self.answer = answer
|
self.answer = answer
|
||||||
self.timestamp = timestamp
|
self.timestamp = timestamp
|
||||||
self.previousVersions = previousVersions
|
self.disabled = disabled
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Model
|
@Model
|
||||||
final class ChatHistory: Identifiable {
|
final class ChatHistory {
|
||||||
let id: UUID
|
|
||||||
var name: String
|
var name: String
|
||||||
var chatPairs: [ChatPair]
|
var chatPairs: [ChatPair]
|
||||||
|
|
||||||
init(name: String, chatPairs: [ChatPair] = []) {
|
init(name: String, chatPairs: [ChatPair] = []) {
|
||||||
self.id = UUID()
|
|
||||||
self.name = name
|
self.name = name
|
||||||
self.chatPairs = chatPairs
|
self.chatPairs = chatPairs
|
||||||
}
|
}
|
||||||
@ -42,7 +38,7 @@ final class ChatHistory: Identifiable {
|
|||||||
chatPairs.append(newPair)
|
chatPairs.append(newPair)
|
||||||
}
|
}
|
||||||
|
|
||||||
func editChatPair(withId id: UUID, question: String? = nil, answer: String? = nil) {
|
func editChatPair(withId id: PersistentIdentifier, question: String? = nil, answer: String? = nil) {
|
||||||
guard let index = chatPairs.firstIndex(where: { $0.id == id }) else { return }
|
guard let index = chatPairs.firstIndex(where: { $0.id == id }) else { return }
|
||||||
let newChatPair = chatPairs[index]
|
let newChatPair = chatPairs[index]
|
||||||
newChatPair.previousVersions.append(chatPairs[index])
|
newChatPair.previousVersions.append(chatPairs[index])
|
||||||
@ -55,4 +51,8 @@ final class ChatHistory: Identifiable {
|
|||||||
newChatPair.timestamp = Date()
|
newChatPair.timestamp = Date()
|
||||||
chatPairs[index] = newChatPair
|
chatPairs[index] = newChatPair
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func moveChatPair(from source: IndexSet, to destination: Int) {
|
||||||
|
chatPairs.move(fromOffsets: source, toOffset: destination)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -55,9 +55,73 @@ struct ContentView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct ChatPairView: View {
|
||||||
|
let chatPair: ChatPair
|
||||||
|
let editAction: () -> Void
|
||||||
|
let toggleAction: (Bool) -> Void
|
||||||
|
let dateFormatter: DateFormatter
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
GroupBox {
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
MarkdownView(text: chatPair.question)
|
||||||
|
Divider()
|
||||||
|
if let answer = chatPair.answer {
|
||||||
|
MarkdownView(text: answer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.onTapGesture {
|
||||||
|
editAction()
|
||||||
|
}
|
||||||
|
.opacity(chatPair.disabled ? 0.5 : 1)
|
||||||
|
HStack {
|
||||||
|
Toggle("", isOn: Binding(
|
||||||
|
get: { !chatPair.disabled },
|
||||||
|
set: { toggleAction($0) }))
|
||||||
|
.toggleStyle(CheckboxToggleStyle())
|
||||||
|
.labelsHidden()
|
||||||
|
Text("\(chatPair.timestamp, formatter: dateFormatter)")
|
||||||
|
.foregroundColor(.secondary)
|
||||||
|
.font(.footnote)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.padding(.vertical)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ChatPairEditor: View {
|
||||||
|
@Binding var chatPair: ChatPair?
|
||||||
|
@Binding var question: String
|
||||||
|
@Binding var answer: String
|
||||||
|
let saveAction: () -> Void
|
||||||
|
let cancelAction: () -> Void
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
VStack {
|
||||||
|
TextEditor(text: $question)
|
||||||
|
.overlay(RoundedRectangle(cornerRadius: 5).stroke(Color.gray))
|
||||||
|
.frame(maxHeight: .infinity)
|
||||||
|
TextEditor(text: $answer)
|
||||||
|
.overlay(RoundedRectangle(cornerRadius: 5).stroke(Color.gray))
|
||||||
|
.frame(maxHeight: .infinity)
|
||||||
|
HStack {
|
||||||
|
Button(action: cancelAction) {
|
||||||
|
Image(systemName: "xmark.circle.fill")
|
||||||
|
}
|
||||||
|
Button(action: saveAction) {
|
||||||
|
Image(systemName: "checkmark.circle.fill")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.padding()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct ChatHistoryDetailView: View {
|
struct ChatHistoryDetailView: View {
|
||||||
@Environment(\.modelContext) private var modelContext
|
@Environment(\.modelContext) private var modelContext
|
||||||
var chatHistory: ChatHistory
|
@State var chatHistory: ChatHistory
|
||||||
@State private var newQuestion: String = ""
|
@State private var newQuestion: String = ""
|
||||||
@State private var pairToEdit: ChatPair? = nil
|
@State private var pairToEdit: ChatPair? = nil
|
||||||
@State private var newAnswer: String = ""
|
@State private var newAnswer: String = ""
|
||||||
@ -65,59 +129,75 @@ struct ChatHistoryDetailView: View {
|
|||||||
var body: some View {
|
var body: some View {
|
||||||
VStack {
|
VStack {
|
||||||
List {
|
List {
|
||||||
ForEach(chatHistory.chatPairs) { chatPair in
|
ForEach(Array(chatHistory.chatPairs.enumerated()), id: \.element) { index, chatPair in
|
||||||
VStack(alignment: .leading) {
|
ChatPairView(chatPair: chatPair,
|
||||||
MarkdownView(text: chatPair.question)
|
editAction: {
|
||||||
if let answer = chatPair.answer {
|
pairToEdit = chatPair
|
||||||
MarkdownView(text: answer)
|
newQuestion = chatPair.question
|
||||||
.tint(.secondary)
|
newAnswer = chatPair.answer ?? ""
|
||||||
}
|
},
|
||||||
Button(action: {
|
toggleAction: { isEnabled in
|
||||||
pairToEdit = chatPair
|
chatPair.disabled = !isEnabled
|
||||||
newAnswer = chatPair.answer ?? ""
|
saveContext()
|
||||||
}) {
|
},
|
||||||
Text("Edit")
|
dateFormatter: itemFormatter)
|
||||||
.foregroundColor(.blue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Text("Timestamp: \(chatPair.timestamp)")
|
|
||||||
.foregroundColor(.secondary)
|
|
||||||
.font(.footnote)
|
|
||||||
}
|
}
|
||||||
|
.onDelete(perform: deleteChatPair)
|
||||||
|
.onMove(perform: moveChatPair)
|
||||||
}
|
}
|
||||||
HStack {
|
HStack {
|
||||||
TextEditor(text: $newQuestion)
|
TextEditor(text: $newQuestion)
|
||||||
.frame(height: 100)
|
.frame(height: 50)
|
||||||
.overlay(RoundedRectangle(cornerRadius: 5).stroke(Color.gray))
|
.overlay(RoundedRectangle(cornerRadius: 5).stroke(Color.gray))
|
||||||
Button(action: {
|
Button(action: {
|
||||||
addChatPair()
|
addChatPair()
|
||||||
}) {
|
}) {
|
||||||
Text("Add")
|
Image(systemName: "plus.circle.fill")
|
||||||
}
|
|
||||||
Button(action: {
|
|
||||||
cancelEdit()
|
|
||||||
}) {
|
|
||||||
Text("Cancel")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.padding()
|
.padding()
|
||||||
}
|
}
|
||||||
.navigationTitle(chatHistory.name)
|
.navigationTitle(chatHistory.name)
|
||||||
.sheet(item: $pairToEdit) { pairToEdit in
|
.sheet(item: $pairToEdit) { pairToEdit in
|
||||||
VStack {
|
ChatPairEditor(chatPair: $pairToEdit,
|
||||||
Text(pairToEdit.question)
|
question: $newQuestion,
|
||||||
TextEditor(text: $newAnswer)
|
answer: $newAnswer,
|
||||||
.overlay(RoundedRectangle(cornerRadius: 5).stroke(Color.gray))
|
saveAction: { editChatPair(pairToEdit) },
|
||||||
.frame(maxHeight: .infinity)
|
cancelAction: cancelEdit)
|
||||||
Button(action: {
|
|
||||||
editChatPair(pairToEdit)
|
|
||||||
}) {
|
|
||||||
Text("Save")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.padding()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func saveContext() {
|
||||||
|
do {
|
||||||
|
try modelContext.save()
|
||||||
|
} catch {
|
||||||
|
print("Error saving model context: \(error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func deleteChatPair(at offsets: IndexSet) {
|
||||||
|
withAnimation {
|
||||||
|
offsets.forEach { index in
|
||||||
|
let chatPair = chatHistory.chatPairs[index]
|
||||||
|
modelContext.delete(chatPair)
|
||||||
|
}
|
||||||
|
saveContext()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func moveChatPair(from source: IndexSet, to destination: Int) {
|
||||||
|
withAnimation {
|
||||||
|
chatHistory.moveChatPair(from: source, to: destination)
|
||||||
|
saveContext()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var itemFormatter: DateFormatter {
|
||||||
|
let formatter = DateFormatter()
|
||||||
|
formatter.dateStyle = .medium
|
||||||
|
formatter.timeStyle = .short
|
||||||
|
return formatter
|
||||||
|
}
|
||||||
|
|
||||||
private func addChatPair() {
|
private func addChatPair() {
|
||||||
guard !newQuestion.isEmpty else { return }
|
guard !newQuestion.isEmpty else { return }
|
||||||
@ -125,34 +205,41 @@ struct ChatHistoryDetailView: View {
|
|||||||
let newPair = ChatPair(question: newQuestion)
|
let newPair = ChatPair(question: newQuestion)
|
||||||
chatHistory.chatPairs.append(newPair)
|
chatHistory.chatPairs.append(newPair)
|
||||||
newQuestion = ""
|
newQuestion = ""
|
||||||
do {
|
saveContext()
|
||||||
try modelContext.save()
|
|
||||||
} catch {
|
|
||||||
print("Error saving model context: \(error)")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func editChatPair(_ chatPair: ChatPair) {
|
private func editChatPair(_ chatPair: ChatPair) {
|
||||||
guard !newAnswer.isEmpty else { return }
|
guard !newAnswer.isEmpty else { return }
|
||||||
withAnimation {
|
withAnimation {
|
||||||
chatHistory.editChatPair(withId: chatPair.id, question: nil, answer: newAnswer)
|
chatHistory.editChatPair(withId: chatPair.id, question: newQuestion, answer: newAnswer)
|
||||||
newAnswer = ""
|
newAnswer = ""
|
||||||
pairToEdit = nil
|
pairToEdit = nil
|
||||||
do {
|
saveContext()
|
||||||
try modelContext.save()
|
|
||||||
} catch {
|
|
||||||
print("Error saving model context: \(error)")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func cancelEdit() {
|
private func cancelEdit() {
|
||||||
|
newQuestion = ""
|
||||||
newAnswer = ""
|
newAnswer = ""
|
||||||
pairToEdit = nil
|
pairToEdit = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
struct CheckboxToggleStyle: ToggleStyle {
|
||||||
|
func makeBody(configuration: Configuration) -> some View {
|
||||||
|
HStack {
|
||||||
|
configuration.label
|
||||||
|
Spacer()
|
||||||
|
Image(systemName: configuration.isOn ? "eye" : "eye.slash")
|
||||||
|
.foregroundColor(configuration.isOn ? .blue : .gray)
|
||||||
|
.onTapGesture { configuration.isOn.toggle() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#Preview {
|
#Preview {
|
||||||
ContentView()
|
ContentView()
|
||||||
.modelContainer(for: ChatHistory.self, inMemory: true)
|
.modelContainer(for: ChatHistory.self, inMemory: true)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user