From 372b27540b5369020229fd6f038d5074d7ff7d76 Mon Sep 17 00:00:00 2001 From: enricobuehler Date: Mon, 29 Jun 2026 12:34:19 +0200 Subject: [PATCH] fix(apple): render Acknowledgements notices in lazy chunks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit THIRD-PARTY-NOTICES.txt is ~885 KB / 16k lines; rendering it in a single SwiftUI Text overshot the text-rendering height limit — it laid out for ages and drew blank below the cutoff (only the small punktfunk licenses above it showed). Split the notices into ~80 line-chunks (<=200 lines / <=18 KB each, computed once as Licenses.thirdPartyNoticesChunks) and render them in a top-level LazyVStack so only on-screen chunks lay out and no chunk is tall enough to clip. Chunking is lossless — rejoining the chunks reproduces the original byte-for-byte, so no notice text is dropped. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../AcknowledgementsView.swift | 60 +++++++++++-------- .../apple/Sources/PunktfunkKit/Licenses.swift | 14 +++++ 2 files changed, 50 insertions(+), 24 deletions(-) diff --git a/clients/apple/Sources/PunktfunkClient/AcknowledgementsView.swift b/clients/apple/Sources/PunktfunkClient/AcknowledgementsView.swift index b03f152..cc19374 100644 --- a/clients/apple/Sources/PunktfunkClient/AcknowledgementsView.swift +++ b/clients/apple/Sources/PunktfunkClient/AcknowledgementsView.swift @@ -10,32 +10,44 @@ struct AcknowledgementsView: View { var body: some View { ScrollView { - VStack(alignment: .leading, spacing: 18) { - Text("punktfunk") - .font(.title2).bold() - if let version { - Text("Version \(version)") - .font(.caption) - .foregroundStyle(.secondary) + // Top-level LazyVStack so the third-party-notices chunks (Licenses.thirdPartyNoticesChunks, + // ~885 KB total) load lazily as they scroll into view — a single Text that large overshoots + // the text-rendering height limit (blank below the limit + very slow). spacing 0 keeps the + // notice chunks visually continuous; the header block carries its own spacing + bottom pad. + LazyVStack(alignment: .leading, spacing: 0) { + VStack(alignment: .leading, spacing: 18) { + Text("punktfunk") + .font(.title2).bold() + if let version { + Text("Version \(version)") + .font(.caption) + .foregroundStyle(.secondary) + } + Text(Licenses.appLicense) + .font(.caption.monospaced()) + .modifier(SelectableText()) + + Divider() + + Text("Third-party software") + .font(.headline) + Text( + "punktfunk uses the open-source components below, each under its own license. " + + "On some platforms FFmpeg is additionally bundled under the LGPL v2.1+ " + + "(dynamically linked, replaceable)." + ) + .font(.caption) + .foregroundStyle(.secondary) } - Text(Licenses.appLicense) - .font(.caption.monospaced()) - .modifier(SelectableText()) + .frame(maxWidth: .infinity, alignment: .leading) + .padding(.bottom, 18) - Divider() - - Text("Third-party software") - .font(.headline) - Text( - "punktfunk uses the open-source components below, each under its own license. " - + "On some platforms FFmpeg is additionally bundled under the LGPL v2.1+ " - + "(dynamically linked, replaceable)." - ) - .font(.caption) - .foregroundStyle(.secondary) - Text(Licenses.thirdPartyNotices) - .font(.caption2.monospaced()) - .modifier(SelectableText()) + ForEach(Licenses.thirdPartyNoticesChunks.indices, id: \.self) { i in + Text(Licenses.thirdPartyNoticesChunks[i]) + .font(.caption2.monospaced()) + .frame(maxWidth: .infinity, alignment: .leading) + .modifier(SelectableText()) + } } .frame(maxWidth: 900, alignment: .leading) .frame(maxWidth: .infinity, alignment: .leading) diff --git a/clients/apple/Sources/PunktfunkKit/Licenses.swift b/clients/apple/Sources/PunktfunkKit/Licenses.swift index 34bb035..a0b514a 100644 --- a/clients/apple/Sources/PunktfunkKit/Licenses.swift +++ b/clients/apple/Sources/PunktfunkKit/Licenses.swift @@ -33,4 +33,18 @@ public enum Licenses { let text = resource("THIRD-PARTY-NOTICES") return text.isEmpty ? "Third-party notices unavailable." : text } + + /// `thirdPartyNotices` pre-split into render-sized line chunks. The full notices are ~885 KB / + /// 16k lines; a single SwiftUI `Text` that large overshoots CoreText/CoreAnimation's max + /// renderable height — it lays out for ages and draws blank past the limit — so the + /// Acknowledgements screen renders these chunks in a `LazyVStack` (only on-screen chunks lay + /// out, and no chunk is tall enough to clip). Split at line boundaries and joined with "\n"; + /// the inter-chunk break is the `LazyVStack` row boundary, so no text is lost. Computed once. + public static let thirdPartyNoticesChunks: [String] = { + let lines = thirdPartyNotices.split(separator: "\n", omittingEmptySubsequences: false) + let chunkSize = 200 + return stride(from: 0, to: lines.count, by: chunkSize).map { start in + lines[start..