Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions .github/workflows/build-release-artifacts.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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
Expand All @@ -175,5 +173,4 @@ jobs:
overwrite_files: true
files: |
${{ steps.release.outputs.dmgs }}
${{ steps.release.outputs.checksums }}
${{ steps.release.outputs.manifest }}
31 changes: 31 additions & 0 deletions Sources/ClipDock/ClipboardMonitoring.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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: #"<img\b"#, options: .regularExpression) != nil
else {
return false
}

if html.range(of: #"<a\b"#, options: .regularExpression) != nil {
return false
}

guard let htmlPlainText = readHTMLPlainText(from: pasteboard) else {
return true
}

return ClipboardLinkDetector().detectPureLink(in: htmlPlainText) == nil
}
}

@MainActor
Expand Down
8 changes: 4 additions & 4 deletions Sources/ClipDock/Resources/AppInfo.plist
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleShortVersionString</key>
<string>0.1.7</string>
<key>CFBundleVersion</key>
<string>6</string>
<key>CFBundleShortVersionString</key>
<string>0.1.11</string>
<key>CFBundleVersion</key>
<string>10</string>
</dict>
</plist>
41 changes: 41 additions & 0 deletions Tests/ClipboardPanelAppTests/ClipboardMonitoringTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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(#"<p><a href="\#(url)">\#(url)</a></p>"#, 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(#"<html><body><img src="\#(imageURL)" alt="logo"></body></html>"#, 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 {
Expand Down