From 0318ac2bdfe764475938b25e9707d359c6107f09 Mon Sep 17 00:00:00 2001 From: Tnixc Date: Sun, 1 Feb 2026 20:50:12 -0500 Subject: [PATCH 1/3] feat: add text selection across multiple text views (MVP) --- .../Message Body/Markdown/MarkdownText.swift | 48 +++++ .../Markdown/SelectableMarkdownText.swift | 173 ++++++++++++++++++ .../Messages/Message Body/MessageBody.swift | 6 +- 3 files changed, 225 insertions(+), 2 deletions(-) create mode 100644 Paicord/Common/Chat/Messages/Message Body/Markdown/SelectableMarkdownText.swift diff --git a/Paicord/Common/Chat/Messages/Message Body/Markdown/MarkdownText.swift b/Paicord/Common/Chat/Messages/Message Body/Markdown/MarkdownText.swift index 8a8dc5da..3b63462a 100644 --- a/Paicord/Common/Chat/Messages/Message Body/Markdown/MarkdownText.swift +++ b/Paicord/Common/Chat/Messages/Message Body/Markdown/MarkdownText.swift @@ -21,6 +21,7 @@ struct MarkdownText: View, Equatable { @Environment(\.dynamicTypeSize) var dynamicType @ViewStorage var dynamicTypeSizeStorage: DynamicTypeSize = .xSmall @Environment(\.theme) var theme + @Environment(\.enableCrossBlockTextSelection) var enableCrossBlockTextSelection @State private var renderer: MarkdownRendererVM @State private var userPopover: PartialUser? @@ -68,12 +69,33 @@ struct MarkdownText: View, Equatable { Text(markdown: content) // Apple’s markdown fallback .opacity(0.6) } else { + #if os(macOS) + if enableCrossBlockTextSelection { + SelectableMarkdownText( + blocks: renderer.blocks.compactMap { block in + guard let attr = block.attributedContent else { return nil } + return SelectableMarkdownText.BlockInfo( + id: String(block.id), + attributedString: attr + ) + } + ) + } else { + ForEach(renderer.blocks) { block in + BlockView(block: block) + .equatable() + .debugRender() + .debugCompute() + } + } + #else ForEach(renderer.blocks) { block in BlockView(block: block) .equatable() .debugRender() .debugCompute() } + #endif } } .environment( @@ -1682,3 +1704,29 @@ final class EmojiAttachmentViewProvider: NSTextAttachmentViewProvider { container = nil } } + +// MARK: - Environment Key for Cross-Block Text Selection + +private struct CrossBlockTextSelectionKey: EnvironmentKey { + static let defaultValue: Bool = false +} + +extension EnvironmentValues { + var enableCrossBlockTextSelection: Bool { + get { self[CrossBlockTextSelectionKey.self] } + set { self[CrossBlockTextSelectionKey.self] = newValue } + } +} + +extension View { + /// Enables cross-block text selection for markdown text within this view + /// - Note: Only works on macOS. On iOS, this does nothing and the original + /// per-block rendering is used. + func enableCrossBlockTextSelection(_ enabled: Bool = true) -> some View { + #if os(macOS) + environment(\.enableCrossBlockTextSelection, enabled) + #else + self // No-op on iOS + #endif + } +} diff --git a/Paicord/Common/Chat/Messages/Message Body/Markdown/SelectableMarkdownText.swift b/Paicord/Common/Chat/Messages/Message Body/Markdown/SelectableMarkdownText.swift new file mode 100644 index 00000000..7d372ade --- /dev/null +++ b/Paicord/Common/Chat/Messages/Message Body/Markdown/SelectableMarkdownText.swift @@ -0,0 +1,173 @@ +// +// SelectableMarkdownText.swift +// Paicord +// +// Created by Tnixc on 01/02/2026. +// + +import SwiftUI +#if canImport(AppKit) +import AppKit +#endif + +/// MVP multi select +struct SelectableMarkdownText: View { + let blocks: [BlockInfo] + + struct BlockInfo: Identifiable { + let id: String + let attributedString: NSAttributedString + } + + init(blocks: [BlockInfo]) { + self.blocks = blocks + } + + var body: some View { + #if os(macOS) + VStack(alignment: .leading, spacing: 4) { + SelectableTextContainer(blocks: blocks) + } + #else + // unreachable + EmptyView() + #endif + } +} + +#if os(macOS) +private struct SelectableTextContainer: NSViewRepresentable { + let blocks: [SelectableMarkdownText.BlockInfo] + @Environment(\.openURL) private var openURL + + func makeNSView(context: Context) -> CombinedTextView { + let textView = CombinedTextView() + textView.isEditable = false + textView.isSelectable = true + textView.drawsBackground = false + textView.textContainerInset = .zero + textView.textContainer?.lineFragmentPadding = 0 + textView.textContainer?.widthTracksTextView = true + textView.isAutomaticLinkDetectionEnabled = false + textView.linkTextAttributes = [:] + textView.delegate = context.coordinator + textView.usesAdaptiveColorMappingForDarkAppearance = true + + updateTextStorage(textView: textView, blocks: blocks) + + return textView + } + + func updateNSView(_ nsView: CombinedTextView, context: Context) { + updateTextStorage(textView: nsView, blocks: blocks) + } + + private func updateTextStorage(textView: CombinedTextView, blocks: [SelectableMarkdownText.BlockInfo]) { + let combined = NSMutableAttributedString() + + for (index, block) in blocks.enumerated() { + combined.append(block.attributedString) + + // add line break between blocks (except after the last one) + if index < blocks.count - 1 { + combined.append(NSAttributedString(string: "\n")) + } + } + + if textView.attributedString() != combined { + textView.textStorage?.setAttributedString(combined) + } + } + + func sizeThatFits( + _ proposal: ProposedViewSize, + nsView: CombinedTextView, + context: Context + ) -> CGSize? { + let targetWidth = proposal.width ?? 400 + guard let layoutManager = nsView.layoutManager, + let textContainer = nsView.textContainer + else { return nil } + + textContainer.containerSize = CGSize( + width: targetWidth, + height: .greatestFiniteMagnitude + ) + layoutManager.ensureLayout(for: textContainer) + + let usedRect = layoutManager.usedRect(for: textContainer) + return CGSize(width: targetWidth, height: ceil(usedRect.height)) + } + + func makeCoordinator() -> Coordinator { + Coordinator(openURL: openURL) + } + + final class Coordinator: NSObject, NSTextViewDelegate { + let openURL: OpenURLAction + + init(openURL: OpenURLAction) { + self.openURL = openURL + } + + func textView( + _ textView: NSTextView, + clickedOnLink link: Any, + at charIndex: Int + ) -> Bool { + if let url = link as? URL { + openURL(url) + return true + } + return false + } + } +} + +final class CombinedTextView: NSTextView { + override func rightMouseDown(with event: NSEvent) { + nextResponder?.rightMouseDown(with: event) + } + + override func writeSelection( + to pboard: NSPasteboard, + type: NSPasteboard.PasteboardType + ) -> Bool { + let selected = attributedString().attributedSubstring(from: selectedRange()) + let copy = NSMutableAttributedString(attributedString: selected) + + copy.enumerateAttribute( + .rawContent, + in: NSRange(location: 0, length: copy.length), + options: .reverse + ) { value, range, _ in + if let customText = value as? String { + var r = NSRange() + let attrs = copy.attributes(at: range.location, effectiveRange: &r) + copy.replaceCharacters( + in: range, + with: NSAttributedString(string: customText, attributes: attrs) + ) + } + } + + pboard.clearContents() + pboard.writeObjects([copy.string as NSString]) + return true + } +} +#endif + +#Preview { + let block1 = SelectableMarkdownText.BlockInfo( + id: "1", + attributedString: NSAttributedString(string: "First paragraph with some text.") + ) + let block2 = SelectableMarkdownText.BlockInfo( + id: "2", + attributedString: NSAttributedString(string: "Second paragraph with more text to select.") + ) + + SelectableMarkdownText(blocks: [block1, block2]) + .padding() +} diff --git a/Paicord/Common/Chat/Messages/Message Body/MessageBody.swift b/Paicord/Common/Chat/Messages/Message Body/MessageBody.swift index 5ed4fa70..de00d5d5 100644 --- a/Paicord/Common/Chat/Messages/Message Body/MessageBody.swift +++ b/Paicord/Common/Chat/Messages/Message Body/MessageBody.swift @@ -34,7 +34,8 @@ extension MessageCell { var body: some View { VStack(alignment: .leading, spacing: 4) { // Content - Group { +// Group { +// totally breaks with Group idk why. if !messageContentHidden { let content = message.content ?? "" if message.flags?.contains(.isComponentsV2) == true { @@ -47,9 +48,10 @@ extension MessageCell { channelStore: channelStore ) .equatable() + .enableCrossBlockTextSelection(true) } } - } +// } // Attachments let attachments = message.attachments ?? [] From f78103049f3a3252ee61607c0633db376899fa2c Mon Sep 17 00:00:00 2001 From: Tnixc Date: Sun, 1 Feb 2026 20:54:37 -0500 Subject: [PATCH 2/3] chore: format --- .../Message Body/Markdown/MarkdownText.swift | 43 +-- .../Markdown/SelectableMarkdownText.swift | 252 +++++++++--------- .../Messages/Message Body/MessageBody.swift | 34 +-- 3 files changed, 170 insertions(+), 159 deletions(-) diff --git a/Paicord/Common/Chat/Messages/Message Body/Markdown/MarkdownText.swift b/Paicord/Common/Chat/Messages/Message Body/Markdown/MarkdownText.swift index 3b63462a..0ef90dd9 100644 --- a/Paicord/Common/Chat/Messages/Message Body/Markdown/MarkdownText.swift +++ b/Paicord/Common/Chat/Messages/Message Body/Markdown/MarkdownText.swift @@ -21,7 +21,8 @@ struct MarkdownText: View, Equatable { @Environment(\.dynamicTypeSize) var dynamicType @ViewStorage var dynamicTypeSizeStorage: DynamicTypeSize = .xSmall @Environment(\.theme) var theme - @Environment(\.enableCrossBlockTextSelection) var enableCrossBlockTextSelection + @Environment(\.enableCrossBlockTextSelection) + var enableCrossBlockTextSelection @State private var renderer: MarkdownRendererVM @State private var userPopover: PartialUser? @@ -70,31 +71,31 @@ struct MarkdownText: View, Equatable { .opacity(0.6) } else { #if os(macOS) - if enableCrossBlockTextSelection { - SelectableMarkdownText( - blocks: renderer.blocks.compactMap { block in - guard let attr = block.attributedContent else { return nil } - return SelectableMarkdownText.BlockInfo( - id: String(block.id), - attributedString: attr - ) + if enableCrossBlockTextSelection { + SelectableMarkdownText( + blocks: renderer.blocks.compactMap { block in + guard let attr = block.attributedContent else { return nil } + return SelectableMarkdownText.BlockInfo( + id: String(block.id), + attributedString: attr + ) + } + ) + } else { + ForEach(renderer.blocks) { block in + BlockView(block: block) + .equatable() + .debugRender() + .debugCompute() } - ) - } else { + } + #else ForEach(renderer.blocks) { block in BlockView(block: block) .equatable() .debugRender() .debugCompute() } - } - #else - ForEach(renderer.blocks) { block in - BlockView(block: block) - .equatable() - .debugRender() - .debugCompute() - } #endif } } @@ -1724,9 +1725,9 @@ extension View { /// per-block rendering is used. func enableCrossBlockTextSelection(_ enabled: Bool = true) -> some View { #if os(macOS) - environment(\.enableCrossBlockTextSelection, enabled) + environment(\.enableCrossBlockTextSelection, enabled) #else - self // No-op on iOS + self // No-op on iOS #endif } } diff --git a/Paicord/Common/Chat/Messages/Message Body/Markdown/SelectableMarkdownText.swift b/Paicord/Common/Chat/Messages/Message Body/Markdown/SelectableMarkdownText.swift index 7d372ade..be6df6b2 100644 --- a/Paicord/Common/Chat/Messages/Message Body/Markdown/SelectableMarkdownText.swift +++ b/Paicord/Common/Chat/Messages/Message Body/Markdown/SelectableMarkdownText.swift @@ -6,168 +6,178 @@ // import SwiftUI + #if canImport(AppKit) -import AppKit + import AppKit #endif /// MVP multi select struct SelectableMarkdownText: View { let blocks: [BlockInfo] - + struct BlockInfo: Identifiable { let id: String let attributedString: NSAttributedString } - + init(blocks: [BlockInfo]) { self.blocks = blocks } - + var body: some View { #if os(macOS) - VStack(alignment: .leading, spacing: 4) { - SelectableTextContainer(blocks: blocks) - } + VStack(alignment: .leading, spacing: 4) { + SelectableTextContainer(blocks: blocks) + } #else - // unreachable - EmptyView() + // unreachable + EmptyView() #endif } } #if os(macOS) -private struct SelectableTextContainer: NSViewRepresentable { - let blocks: [SelectableMarkdownText.BlockInfo] - @Environment(\.openURL) private var openURL - - func makeNSView(context: Context) -> CombinedTextView { - let textView = CombinedTextView() - textView.isEditable = false - textView.isSelectable = true - textView.drawsBackground = false - textView.textContainerInset = .zero - textView.textContainer?.lineFragmentPadding = 0 - textView.textContainer?.widthTracksTextView = true - textView.isAutomaticLinkDetectionEnabled = false - textView.linkTextAttributes = [:] - textView.delegate = context.coordinator - textView.usesAdaptiveColorMappingForDarkAppearance = true - - updateTextStorage(textView: textView, blocks: blocks) - - return textView - } - - func updateNSView(_ nsView: CombinedTextView, context: Context) { - updateTextStorage(textView: nsView, blocks: blocks) - } - - private func updateTextStorage(textView: CombinedTextView, blocks: [SelectableMarkdownText.BlockInfo]) { - let combined = NSMutableAttributedString() - - for (index, block) in blocks.enumerated() { - combined.append(block.attributedString) - - // add line break between blocks (except after the last one) - if index < blocks.count - 1 { - combined.append(NSAttributedString(string: "\n")) + private struct SelectableTextContainer: NSViewRepresentable { + let blocks: [SelectableMarkdownText.BlockInfo] + @Environment(\.openURL) private var openURL + + func makeNSView(context: Context) -> CombinedTextView { + let textView = CombinedTextView() + textView.isEditable = false + textView.isSelectable = true + textView.drawsBackground = false + textView.textContainerInset = .zero + textView.textContainer?.lineFragmentPadding = 0 + textView.textContainer?.widthTracksTextView = true + textView.isAutomaticLinkDetectionEnabled = false + textView.linkTextAttributes = [:] + textView.delegate = context.coordinator + textView.usesAdaptiveColorMappingForDarkAppearance = true + + updateTextStorage(textView: textView, blocks: blocks) + + return textView + } + + func updateNSView(_ nsView: CombinedTextView, context: Context) { + updateTextStorage(textView: nsView, blocks: blocks) + } + + private func updateTextStorage( + textView: CombinedTextView, + blocks: [SelectableMarkdownText.BlockInfo] + ) { + let combined = NSMutableAttributedString() + + for (index, block) in blocks.enumerated() { + combined.append(block.attributedString) + + // add line break between blocks (except after the last one) + if index < blocks.count - 1 { + combined.append(NSAttributedString(string: "\n")) + } + } + + if textView.attributedString() != combined { + textView.textStorage?.setAttributedString(combined) } } - - if textView.attributedString() != combined { - textView.textStorage?.setAttributedString(combined) + + func sizeThatFits( + _ proposal: ProposedViewSize, + nsView: CombinedTextView, + context: Context + ) -> CGSize? { + let targetWidth = proposal.width ?? 400 + guard let layoutManager = nsView.layoutManager, + let textContainer = nsView.textContainer + else { return nil } + + textContainer.containerSize = CGSize( + width: targetWidth, + height: .greatestFiniteMagnitude + ) + layoutManager.ensureLayout(for: textContainer) + + let usedRect = layoutManager.usedRect(for: textContainer) + return CGSize(width: targetWidth, height: ceil(usedRect.height)) } - } - - func sizeThatFits( - _ proposal: ProposedViewSize, - nsView: CombinedTextView, - context: Context - ) -> CGSize? { - let targetWidth = proposal.width ?? 400 - guard let layoutManager = nsView.layoutManager, - let textContainer = nsView.textContainer - else { return nil } - - textContainer.containerSize = CGSize( - width: targetWidth, - height: .greatestFiniteMagnitude - ) - layoutManager.ensureLayout(for: textContainer) - - let usedRect = layoutManager.usedRect(for: textContainer) - return CGSize(width: targetWidth, height: ceil(usedRect.height)) - } - - func makeCoordinator() -> Coordinator { - Coordinator(openURL: openURL) - } - - final class Coordinator: NSObject, NSTextViewDelegate { - let openURL: OpenURLAction - - init(openURL: OpenURLAction) { - self.openURL = openURL + + func makeCoordinator() -> Coordinator { + Coordinator(openURL: openURL) } - - func textView( - _ textView: NSTextView, - clickedOnLink link: Any, - at charIndex: Int - ) -> Bool { - if let url = link as? URL { - openURL(url) - return true + + final class Coordinator: NSObject, NSTextViewDelegate { + let openURL: OpenURLAction + + init(openURL: OpenURLAction) { + self.openURL = openURL + } + + func textView( + _ textView: NSTextView, + clickedOnLink link: Any, + at charIndex: Int + ) -> Bool { + if let url = link as? URL { + openURL(url) + return true + } + return false } - return false } } -} -final class CombinedTextView: NSTextView { - override func rightMouseDown(with event: NSEvent) { - nextResponder?.rightMouseDown(with: event) - } - - override func writeSelection( - to pboard: NSPasteboard, - type: NSPasteboard.PasteboardType - ) -> Bool { - let selected = attributedString().attributedSubstring(from: selectedRange()) - let copy = NSMutableAttributedString(attributedString: selected) - - copy.enumerateAttribute( - .rawContent, - in: NSRange(location: 0, length: copy.length), - options: .reverse - ) { value, range, _ in - if let customText = value as? String { - var r = NSRange() - let attrs = copy.attributes(at: range.location, effectiveRange: &r) - copy.replaceCharacters( - in: range, - with: NSAttributedString(string: customText, attributes: attrs) - ) + final class CombinedTextView: NSTextView { + override func rightMouseDown(with event: NSEvent) { + nextResponder?.rightMouseDown(with: event) + } + + override func writeSelection( + to pboard: NSPasteboard, + type: NSPasteboard.PasteboardType + ) -> Bool { + let selected = attributedString().attributedSubstring( + from: selectedRange() + ) + let copy = NSMutableAttributedString(attributedString: selected) + + copy.enumerateAttribute( + .rawContent, + in: NSRange(location: 0, length: copy.length), + options: .reverse + ) { value, range, _ in + if let customText = value as? String { + var r = NSRange() + let attrs = copy.attributes(at: range.location, effectiveRange: &r) + copy.replaceCharacters( + in: range, + with: NSAttributedString(string: customText, attributes: attrs) + ) + } } + + pboard.clearContents() + pboard.writeObjects([copy.string as NSString]) + return true } - - pboard.clearContents() - pboard.writeObjects([copy.string as NSString]) - return true } -} #endif #Preview { let block1 = SelectableMarkdownText.BlockInfo( id: "1", - attributedString: NSAttributedString(string: "First paragraph with some text.") + attributedString: NSAttributedString( + string: "First paragraph with some text." + ) ) let block2 = SelectableMarkdownText.BlockInfo( id: "2", - attributedString: NSAttributedString(string: "Second paragraph with more text to select.") + attributedString: NSAttributedString( + string: "Second paragraph with more text to select." + ) ) - + SelectableMarkdownText(blocks: [block1, block2]) .padding() } diff --git a/Paicord/Common/Chat/Messages/Message Body/MessageBody.swift b/Paicord/Common/Chat/Messages/Message Body/MessageBody.swift index de00d5d5..7fff97ed 100644 --- a/Paicord/Common/Chat/Messages/Message Body/MessageBody.swift +++ b/Paicord/Common/Chat/Messages/Message Body/MessageBody.swift @@ -34,24 +34,24 @@ extension MessageCell { var body: some View { VStack(alignment: .leading, spacing: 4) { // Content -// Group { -// totally breaks with Group idk why. - if !messageContentHidden { - let content = message.content ?? "" - if message.flags?.contains(.isComponentsV2) == true { - ComponentsV2View( /*components: message.components*/) - .equatable(by: message.components) - } else if !content.isEmpty { - MarkdownText( - content: content, - meta: message, - channelStore: channelStore - ) - .equatable() - .enableCrossBlockTextSelection(true) - } + // Group { + // totally breaks with Group idk why. + if !messageContentHidden { + let content = message.content ?? "" + if message.flags?.contains(.isComponentsV2) == true { + ComponentsV2View( /*components: message.components*/) + .equatable(by: message.components) + } else if !content.isEmpty { + MarkdownText( + content: content, + meta: message, + channelStore: channelStore + ) + .equatable() + .enableCrossBlockTextSelection(true) } -// } + } + // } // Attachments let attachments = message.attachments ?? [] From 900362b1f7736ec9e317aba3234ee1a15670fadb Mon Sep 17 00:00:00 2001 From: Tnixc Date: Sun, 1 Feb 2026 21:11:41 -0500 Subject: [PATCH 3/3] i think this should fix it --- Paicord.xcodeproj/project.pbxproj | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Paicord.xcodeproj/project.pbxproj b/Paicord.xcodeproj/project.pbxproj index c66a6feb..1c696eb7 100644 --- a/Paicord.xcodeproj/project.pbxproj +++ b/Paicord.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 391A9AD92F30311E00215754 /* SelectableMarkdownText.swift in Sources */ = {isa = PBXBuildFile; fileRef = 391A9AD82F30311E00215754 /* SelectableMarkdownText.swift */; }; 5724CD6F2E733D0E00931FEF /* MFASheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5724CD6E2E733D0900931FEF /* MFASheet.swift */; }; 572B694C2E7079FB00BCED00 /* PaicordLib in Frameworks */ = {isa = PBXBuildFile; productRef = 572B694B2E7079FB00BCED00 /* PaicordLib */; }; 5783AA902E6B8F6500F73F6D /* MeshGradient in Frameworks */ = {isa = PBXBuildFile; productRef = 5783AA8F2E6B8F6500F73F6D /* MeshGradient */; }; @@ -155,6 +156,7 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 391A9AD82F30311E00215754 /* SelectableMarkdownText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectableMarkdownText.swift; sourceTree = ""; }; 5724CD6E2E733D0900931FEF /* MFASheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MFASheet.swift; sourceTree = ""; }; 5783AA932E6B8FCD00F73F6D /* MeshGradient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeshGradient.swift; sourceTree = ""; }; 578E1CE02E67780B00C6DEA3 /* GradientText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GradientText.swift; sourceTree = ""; }; @@ -490,6 +492,7 @@ AA0C43D52E9EF91D000FA834 /* Markdown */ = { isa = PBXGroup; children = ( + 391A9AD82F30311E00215754 /* SelectableMarkdownText.swift */, 57A55C562E7560CC005C8226 /* MarkdownText.swift */, AA0C43D62E9F18AD000FA834 /* AttributedText.swift */, ); @@ -1131,6 +1134,7 @@ 57F5AF472E7CAD2500AD5674 /* Challenges.swift in Sources */, AA47AF242EDEF139008A50C9 /* ConnectionsSection.swift in Sources */, AA418C842F21AAFB00B51C18 /* UpdateCheck.swift in Sources */, + 391A9AD92F30311E00215754 /* SelectableMarkdownText.swift in Sources */, AA409ABF2EC74F5E00848045 /* ThemingSupport.swift in Sources */, AA13A1E32EA83766005A3613 /* TypingIndicatorBar.swift in Sources */, AA9D26B12EC95EE4006071FE /* FlowLayout.swift in Sources */, @@ -1307,10 +1311,11 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Paicord/Paicord.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEAD_CODE_STRIPPING = YES; - DEVELOPMENT_TEAM = 566RT33SA2; + DEVELOPMENT_TEAM = ""; ENABLE_APP_SANDBOX = YES; ENABLE_FILE_ACCESS_DOWNLOADS_FOLDER = readwrite; ENABLE_HARDENED_RUNTIME = YES; @@ -1370,10 +1375,11 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Paicord/Paicord.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEAD_CODE_STRIPPING = YES; - DEVELOPMENT_TEAM = 566RT33SA2; + DEVELOPMENT_TEAM = ""; ENABLE_APP_SANDBOX = YES; ENABLE_FILE_ACCESS_DOWNLOADS_FOLDER = readwrite; ENABLE_HARDENED_RUNTIME = YES;