diff --git a/.github/workflows/build-release-artifacts.yml b/.github/workflows/build-release-artifacts.yml index 4f45045..376129d 100644 --- a/.github/workflows/build-release-artifacts.yml +++ b/.github/workflows/build-release-artifacts.yml @@ -90,7 +90,6 @@ jobs: echo "tag=$tag" echo "artifact_dir=$artifact_dir" echo "dmgs=$artifact_dir/ClipDock-$version-*.dmg" - echo "checksums=$artifact_dir/SHA256SUMS" echo "manifest=$artifact_dir/ClipDock-release-manifest.txt" echo "release_notes=$artifact_dir/ClipDock-release-notes.md" } >> "$GITHUB_OUTPUT" @@ -157,7 +156,6 @@ jobs: name: ClipDock-${{ steps.release.outputs.version }}-macos path: | ${{ steps.release.outputs.dmgs }} - ${{ steps.release.outputs.checksums }} ${{ steps.release.outputs.manifest }} if-no-files-found: error retention-days: 30 @@ -175,5 +173,4 @@ jobs: overwrite_files: true files: | ${{ steps.release.outputs.dmgs }} - ${{ steps.release.outputs.checksums }} ${{ steps.release.outputs.manifest }} diff --git a/Sources/ClipDock/ClipboardMonitoring.swift b/Sources/ClipDock/ClipboardMonitoring.swift index e96404e..b6c4c86 100644 --- a/Sources/ClipDock/ClipboardMonitoring.swift +++ b/Sources/ClipDock/ClipboardMonitoring.swift @@ -41,6 +41,9 @@ struct ClipboardPayloadReader: ClipboardContentReading { let image = CapturedClipboardImage.read(from: pasteboard, skipFileURLCheck: true) if let image { + if let linkText = plainLinkTextOverride(from: pasteboard) { + return .text(linkText) + } return .image(image) } @@ -71,6 +74,16 @@ struct ClipboardPayloadReader: ClipboardContentReading { return nil } + private func plainLinkTextOverride(from pasteboard: NSPasteboard) -> String? { + guard let text = normalizedPlainText(from: pasteboard.string(forType: .string)), + ClipboardLinkDetector().detectPureLink(in: text) != nil, + !htmlAppearsToDescribeImageOnlyPayload(from: pasteboard) + else { + return nil + } + return text + } + private func readBestRichText( from pasteboard: NSPasteboard, htmlBacked: Bool @@ -305,6 +318,24 @@ struct ClipboardPayloadReader: ClipboardContentReading { } return nil } + + private func htmlAppearsToDescribeImageOnlyPayload(from pasteboard: NSPasteboard) -> Bool { + guard let html = htmlString(from: pasteboard)?.lowercased(), + html.range(of: #" - CFBundleShortVersionString - 0.1.7 - CFBundleVersion - 6 + CFBundleShortVersionString + 0.1.11 + CFBundleVersion + 10 diff --git a/Tests/ClipboardPanelAppTests/ClipboardMonitoringTests.swift b/Tests/ClipboardPanelAppTests/ClipboardMonitoringTests.swift index 388eb50..7a31967 100644 --- a/Tests/ClipboardPanelAppTests/ClipboardMonitoringTests.swift +++ b/Tests/ClipboardPanelAppTests/ClipboardMonitoringTests.swift @@ -474,6 +474,47 @@ struct ClipboardMonitoringTests { } } + @Test + @MainActor + func pureURLTextWinsOverBitmapSidecarFromRichEditors() throws { + let url = "https://english.news.cn/20260609/9e32edbd6ad94d4e8d6a42441cf14d1b/c.html" + let pngData = try makePNGData(width: 32, height: 20) + let pasteboard = NSPasteboard(name: NSPasteboard.Name(UUID().uuidString)) + pasteboard.clearContents() + _ = pasteboard.declareTypes([.png, .html, .string], owner: nil) + #expect(pasteboard.setData(pngData, forType: .png)) + #expect(pasteboard.setString(#"

\#(url)

"#, forType: .html)) + #expect(pasteboard.setString(url, forType: .string)) + + let snapshot = ClipboardPayloadReader().readContent(from: pasteboard) + guard case .text(let text, let displayRichText) = snapshot else { + Issue.record("Pure URL text from rich editors should not be captured as an image sidecar") + return + } + + #expect(text == url) + #expect(displayRichText == nil) + } + + @Test + @MainActor + func imageOnlyHTMLWithURLStringMetadataRemainsImage() throws { + let imageURL = "https://example.com/logo.png" + let pngData = try makePNGData(width: 32, height: 20) + let pasteboard = NSPasteboard(name: NSPasteboard.Name(UUID().uuidString)) + pasteboard.clearContents() + _ = pasteboard.declareTypes([.png, .html, .string], owner: nil) + #expect(pasteboard.setData(pngData, forType: .png)) + #expect(pasteboard.setString(#"logo"#, forType: .html)) + #expect(pasteboard.setString(imageURL, forType: .string)) + + let snapshot = ClipboardPayloadReader().readContent(from: pasteboard) + guard case .image = snapshot else { + Issue.record("Image-only HTML plus URL metadata should remain an image payload") + return + } + } + @Test @MainActor func fileEvidenceWinsOverRTF() throws {