diff --git a/Packages/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite.swift b/Packages/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite.swift index 1333cfafe42..c7440d2dde1 100644 --- a/Packages/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite.swift +++ b/Packages/CmuxMobileShell/Sources/CmuxMobileShell/MobileShellComposite.swift @@ -344,7 +344,9 @@ public final class MobileShellComposite: MobileTerminalOutputSinking { /// Per-image encoded-bytes cap. An add whose single image exceeds this is /// rejected outright (the view bounds the encode below this, but the store /// re-enforces it as the single source of truth). - public static let maxPendingAttachmentImageBytes = 8 * 1024 * 1024 + // nonisolated so the composer's off-main (nonisolated) image-encode path can + // read the authoritative per-image cap; it is an immutable Sendable literal. + public nonisolated static let maxPendingAttachmentImageBytes = 8 * 1024 * 1024 /// GLOBAL encoded-bytes budget summed across EVERY terminal's staged set, not /// just the target's. The per-terminal cap bounds one draft, but each live /// terminal carries its own per-terminal budget, so staging photos across many diff --git a/Packages/CmuxMobileShellUI/Sources/CmuxMobileShellUI/TerminalComposerView.swift b/Packages/CmuxMobileShellUI/Sources/CmuxMobileShellUI/TerminalComposerView.swift index 2ee76e0e77a..299fbdad58c 100644 --- a/Packages/CmuxMobileShellUI/Sources/CmuxMobileShellUI/TerminalComposerView.swift +++ b/Packages/CmuxMobileShellUI/Sources/CmuxMobileShellUI/TerminalComposerView.swift @@ -114,7 +114,7 @@ struct TerminalComposerView: View { /// clipboard paste path and keep the bounded encode under ~8 MB. The store /// re-enforces this as the authoritative per-image cap; this constant only /// bounds the encode loop below it. - private static let maxImageBytes = CMUXMobileShellStore.maxPendingAttachmentImageBytes + private nonisolated static let maxImageBytes = CMUXMobileShellStore.maxPendingAttachmentImageBytes /// Cap how many images one message may carry, mirrored from the store so the /// picker's `maxSelectionCount` matches the store's authoritative count cap. @@ -137,14 +137,14 @@ struct TerminalComposerView: View { /// Max pixel dimension of the cached chip thumbnail. The chip renders at 56pt; /// 3x covers Retina without holding the full-resolution image. - private static let thumbnailMaxPixelSize = 168 + private nonisolated static let thumbnailMaxPixelSize = 168 /// Max pixel dimension of the SEND payload. ImageIO downsamples the picked /// item to fit this longest edge before re-encoding, so a panorama or a /// 48-megapixel HEIC never materializes as a full-resolution raster. 2048 px /// keeps screenshot text legible for an agent while bounding the bytes well /// under the per-image cap. - private static let sendMaxPixelSize = 2048 + private nonisolated static let sendMaxPixelSize = 2048 var body: some View { composerSurface @@ -542,7 +542,7 @@ struct TerminalComposerView: View { /// 3. Progressively smaller dimensions as a last resort. /// Returns the encoded bytes + a lowercase format hint, or `nil` if the source /// is undecodable. The cap is also re-enforced authoritatively in the store. - private static func boundedSendPayload(from source: CGImageSource) -> (data: Data, format: String)? { + private nonisolated static func boundedSendPayload(from source: CGImageSource) -> (data: Data, format: String)? { // PNG at the bounded send size: lossless, and for a typical screenshot it // lands well under the cap. if let png = downsampledImageData( @@ -599,7 +599,7 @@ struct TerminalComposerView: View { /// - maxPixelSize: The longest-edge cap, in pixels, for the downsample. /// - type: The destination UTType identifier (`"public.png"`/`"public.jpeg"`). /// - jpegQuality: The JPEG compression quality (0...1); `nil` for PNG. - private static func downsampledImageData( + private nonisolated static func downsampledImageData( from source: CGImageSource, maxPixelSize: Int, type: String,