Copying The Current Safari Tab as a Clickable Link
After switching from Chrome to Safari, I started missing the excellent TabCopy extension that allowed me to easily copy the current tab’s URL to the clipboard, formatted as a clickable link displaying the page title. I often use this to reference web pages in emails, to-do lists, documentation, Slack, etc. Instead of dealing with potentially long URLs, I prefer pasting a human-readable link with the URL hidden behind it.
This post outlines how I managed to implement this specific requirement for Safari. I’m not focussing on TabCopy’s other features as they weren’t relevant for my workflow.
TL;DR: Here’s the finished script which retrieves the URL and title of the active Safari tab, constructs an HTML link, and copies it to the macOS clipboard. It also includes a plain-text representation in Markdown syntax.
You can use an automation tool such as Alfred to bind this script to a hot key local to Safari and show a notification when done:
The Script Explained
Fetching the Title
Using AppleScript, we fetch the URL and the title displayed in the frontmost Safari window.
Massaging the Title
Starting with version 17, Safari introduced support for profiles. The profile name will be prefixed to the document title that is retrieved using AppleScript. We detect it based on the long dash (—), which Safari uses to separate the profile name from the web page title, and strip that string using sed
.
You can extend this function to do further processing if needed.
Preparing The HTML String
Adding the <meta charset='utf-8'>
ensures that the string is treated as UTF-8 when adding it to the clipboard. Without this, special characters in the page title will break.
I’m also adding a style tag to use the default sans-serif font when pasting the link, which I usually prefer. Your mileage may vary.
hexdump -ve '1/1 "%.2x"'
will convert a string into a stream of two-digit hexadecimal numbers, without skipping any repeated lines. This seems to be required to get the string over into the AppleScript world, which I learned in this StackOverflow post.
Preparing a Plain Text Version
Some apps, such as Slack, are picky and won’t paste the HTML string when no plain text version is present (details in the next section below). I’m therefore adding plain text representation in Markdown syntax which includes both the page title and the link.
Setting the Clipboard
Finally, I’m writing both strings to the clipboard, again using AppleScript.
Getting It To Work In Slack
At first glance, all seemed to work, as the link would paste correctly in native macOS apps, such as Mail, TextEdit, or OmniOutliner. However, at some point I noticed that it wasn’t working in Slack. Performing the paste action would do nothing, not even giving me the title without the link. Maybe this is an issue with Electron apps in general, which I haven’t verified.
I was puzzled and decided to take a deep dive into the inner workings of the macOS clipboard. I knew that copying a link from a webpage in Chrome or Safari and pasting it into Slack worked correctly, so I wanted to figure out how the raw data would look differently in the clipboard.
Apple provides a Clipboard Viewer utility as part of the Additional Tools for Xcode, which was exactly what I needed to get to the bottom of this. Despite having an icon that you’d never expect from an Apple app shipped today, it gets its job done, no frills.
Manually copying a link from a web page in Safari would write the following data in the clipboard:
More than I expected. There are multiple HTML and RTF representations, including a com.apple.webarchive data type, and various plain text representations in both UTF-8 and UTF-16.
In contrast, my script would only write two data types, both being the HTML representation:
After some trial and error, I found out that the plain text version is key. Slack expects a plain text to be included, even though it won’t be using it at all. As long as a plain text is included, it will happily paste the HTML version.
So I updated my script to include the plain text version:
plain=$(echo "[${title}](${url})" | sed 's/"/\\"/g')
osascript <<EOF
set the clipboard to {«class HTML»:«data HTML${html}», string:"${plain}"}
EOF
For some reason, this adds a bunch of other plain text representations. But it does the trick! The link can now be pasted into Slack.
To make this more useful, I decided to use the Markdown syntax here.
Credits
- Header artwork generated with ChatGPT and DALL·E.
- https://stackoverflow.com/a/11089226/1387396