diff --git a/kordophone2.xcodeproj/project.pbxproj b/kordophone2.xcodeproj/project.pbxproj index 764b51e..542da4d 100644 --- a/kordophone2.xcodeproj/project.pbxproj +++ b/kordophone2.xcodeproj/project.pbxproj @@ -6,6 +6,10 @@ objectVersion = 77; objects = { +/* Begin PBXBuildFile section */ + CD41F5D32E62431D00E0027B /* KeychainAccess in Frameworks */ = {isa = PBXBuildFile; productRef = CD41F5D22E62431D00E0027B /* KeychainAccess */; }; +/* End PBXBuildFile section */ + /* Begin PBXFileReference section */ CD41F5972E5B8E7300E0027B /* kordophone2.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = kordophone2.app; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ @@ -23,6 +27,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + CD41F5D32E62431D00E0027B /* KeychainAccess in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -65,6 +70,7 @@ ); name = kordophone2; packageProductDependencies = ( + CD41F5D22E62431D00E0027B /* KeychainAccess */, ); productName = kordophone2; productReference = CD41F5972E5B8E7300E0027B /* kordophone2.app */; @@ -94,6 +100,9 @@ ); mainGroup = CD41F58E2E5B8E7300E0027B; minimizedProjectReferenceProxies = 1; + packageReferences = ( + CD41F5D12E62431D00E0027B /* XCRemoteSwiftPackageReference "KeychainAccess" */, + ); preferredProjectObjectVersion = 77; productRefGroup = CD41F5982E5B8E7300E0027B /* Products */; projectDirPath = ""; @@ -321,6 +330,25 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + CD41F5D12E62431D00E0027B /* XCRemoteSwiftPackageReference "KeychainAccess" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/kishikawakatsumi/KeychainAccess.git"; + requirement = { + branch = master; + kind = branch; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + CD41F5D22E62431D00E0027B /* KeychainAccess */ = { + isa = XCSwiftPackageProductDependency; + package = CD41F5D12E62431D00E0027B /* XCRemoteSwiftPackageReference "KeychainAccess" */; + productName = KeychainAccess; + }; +/* End XCSwiftPackageProductDependency section */ }; rootObject = CD41F58F2E5B8E7300E0027B /* Project object */; } diff --git a/kordophone2/PreferencesView.swift b/kordophone2/PreferencesView.swift index 7bac6ac..24de68c 100644 --- a/kordophone2/PreferencesView.swift +++ b/kordophone2/PreferencesView.swift @@ -5,6 +5,7 @@ // Created by James Magahern on 8/24/25. // +import KeychainAccess import SwiftUI struct PreferencesView: View @@ -56,6 +57,7 @@ struct AccountSettings: View var password: String private let xpc = XPCClient() + private let keychain = Keychain(service: "net.buzzert.kordophonecd") init(serverURL: String = "", username: String = "", password: String = "") { self.serverURL = serverURL @@ -70,6 +72,7 @@ struct AccountSettings: View let settings = try await xpc.getSettings() self.serverURL = settings.serverUrl self.username = settings.username + self.password = keychain[settings.username] ?? "" } catch { print("Error getting settings: \(error)") } @@ -93,6 +96,8 @@ struct AccountSettings: View username: username )) } + + keychain[username] = password } catch { print("Error saving settings: \(error)") } diff --git a/kordophone2/Transcript/TranscriptDisplayItemViews.swift b/kordophone2/Transcript/TranscriptDisplayItemViews.swift index f26d191..2b5c1cd 100644 --- a/kordophone2/Transcript/TranscriptDisplayItemViews.swift +++ b/kordophone2/Transcript/TranscriptDisplayItemViews.swift @@ -71,16 +71,51 @@ struct ImageItemView: View @State private var img: NSImage? @Environment(\.xpcClient) var xpcClient + @State private var containerWidth: CGFloat? = nil + + var aspectRatio: CGFloat { + if let size = attachment.size, size.height > 0 { + return size.width / size.height + } else { + return 1.0 + } + } + + var preferredSize: CGSize { + return CGSize( + width: preferredWidth, + height: preferredWidth / aspectRatio + ) + } + + var preferredWidth: CGFloat { + if let containerWidth, let attachmentWidth = attachment.size?.width { + return CGFloat.minimum(containerWidth, attachmentWidth) + } else if let containerWidth { + return containerWidth + } else { + return 200.0 // fallback + } + } + var body: some View { BubbleView(isFromMe: isFromMe) { if let img { Image(nsImage: img) .resizable() .scaledToFit() + .frame( + maxWidth: containerWidth + ) } else { - ProgressView() + Rectangle() + .fill(.gray) + } } + .onGeometryChange(for: CGFloat.self, + of: { $0.size.width }, + action: { containerWidth = $0 }) .task { do { let handle = try await xpcClient.openAttachmentFileHandle(