diff --git a/supacode/Clients/Repositories/RepositoryIconDetectorScanner.swift b/supacode/Clients/Repositories/RepositoryIconDetectorScanner.swift index 20912432..9b578753 100644 --- a/supacode/Clients/Repositories/RepositoryIconDetectorScanner.swift +++ b/supacode/Clients/Repositories/RepositoryIconDetectorScanner.swift @@ -184,7 +184,8 @@ extension RepositoryIconDetector { /// won't turn into the missing-file placeholder. The declared /// canvas must be finite and within the same pixel ceiling as /// rasters — vectors have no quality floor, so only the upper - /// bound applies. + /// bound applies. A final rasterization pass rejects SVGs that + /// load fine but draw nothing. private func decodableSVGSize(at url: URL) -> CGSize? { guard let prefix = boundedContents(of: url, limit: Scanner.maxImageBytes), let head = String(data: prefix.prefix(4096), encoding: .utf8), @@ -201,7 +202,45 @@ extension RepositoryIconDetector { else { return nil } - return size + return rendersVisiblePixels(image) ? size : nil + } + + /// CoreSVG ignores embedded `"# + return Data(svg.utf8) + } + private struct ManifestEntry { var filename: String var size: String @@ -568,6 +578,33 @@ struct RepositoryIconDetectorTests { } } + @Test func invisiblyRenderingSVGFaviconFallsBackToRasterFavicon() async throws { + try await withTemporaryProjectDirectory( + entries: [], + contents: [ + "package.json": Data(#"{"name":"site"}"#.utf8), + "public/favicon.svg": styleColoredSVGData, + "public/favicon.png": pngData(width: 32, height: 32), + ] + ) { root in + let candidate = await RepositoryIconDetector.detect(at: root) + #expect(candidate?.evidence == .webAsset) + #expect(candidate?.imageURL.lastPathComponent == "favicon.png") + } + } + + @Test func invisiblyRenderingSVGAloneYieldsNothing() async throws { + try await withTemporaryProjectDirectory( + entries: [], + contents: [ + "package.json": Data(#"{"name":"site"}"#.utf8), + "public/favicon.svg": styleColoredSVGData, + ] + ) { root in + #expect(await RepositoryIconDetector.detect(at: root) == nil) + } + } + @Test func oversizedSVGCanvasIsRejected() async throws { let hugeSVG = #""#