Scripts to export Safari tabs
May 13, 2025 in ProjectsThis is a collection (ok, a couple) of Apple automation scripts to extract the tabs out of the topmost Safari window and send them elsewhere.
Pull just the URLs
I use this in a Shortcut to send the URLs to Reeder’s Read it Later.
on run {input, parameters}
tell application "Safari"
set tabURLs to {}
set tabCount to 0
repeat with t in tabs of window 0
if name of t is not "Untitled" then
set tabURLs to tabURLs & (URL of t)
set tabCount to tabCount + 1
end if
end repeat
end tell
return tabURLs
end run
Convert tabs into Obsidian
This one uses my pbcopy-chromium tool to construct nodes pastable into Obsidian Canvas.
It’s self-contained, so you can run it with osascript -l JavaScript nodes_to_canvas.js
, or embed into a Shortcut.
const PBCOPY_CHROMIUM_PATH = "/usr/bin/pbcopy-chromium";
function simpleHash(str) {
var hash = 0,
i,
chr;
if (str.length === 0) return hash;
for (i = 0; i < str.length; i++) {
chr = str.charCodeAt(i);
hash = (hash << 5) - hash + chr;
hash |= 0; // Convert to 32bit integer
}
return Math.abs(hash);
}
function getSafariTabs() {
var safari = Application("Safari");
var win = safari.windows[0];
var tabs = win.tabs();
var tabData = [];
for (var i = 0; i < tabs.length; i++) {
var tab = tabs[i];
if (tab.name() !== "Untitled") {
tabData.push({
name: tab.name(),
url: tab.url(),
});
}
}
return tabData;
}
function makeCanvasNodes(tabData) {
const NODE_GAP = 30;
const NODE_WIDTH = 400;
const NODE_HEIGHT = 100;
var nodes = [];
for (var i = 0; i < tabData.length; i++) {
var tab = tabData[i];
nodes.push({
// Use a stable node ID so in theory you can run this multiple times
// and avoid duplicates. In theory.
id: "saved-tab-" + simpleHash(tab.name + tab.url),
type: "text",
x: 0,
y: i * (NODE_HEIGHT + NODE_GAP),
width: NODE_WIDTH,
height: NODE_HEIGHT,
text: `[${tab.name}](${tab.url})`,
});
}
return nodes;
}
function writeToTempFile(app, json) {
// Write JSON to a temporary file
var tempFile = app.doShellScript("mktemp");
// https://stackoverflow.com/a/29133840/6678
// Apparently normal writing to file has problems with UTF-8
// This is using the so-called "ObjC bridge"
str = $.NSString.alloc.initWithUTF8String(json);
str.writeToFileAtomicallyEncodingError(tempFile, true, $.NSUTF8StringEncoding, null);
return tempFile;
}
function run(input, parameters) {
var tabData = getSafariTabs();
var nodes = makeCanvasNodes(tabData);
var result = {
nodes: nodes,
edges: [],
center: { x: 0, y: 0 },
};
var json = JSON.stringify(result);
var app = Application.currentApplication();
app.includeStandardAdditions = true;
var tempFilePath = writeToTempFile(app, json);
// Use pbcopy-chromium with the temp file
app.doShellScript(PBCOPY_CHROMIUM_PATH + " --type=obsidian/canvas < " + tempFilePath);
// Remove the temp file
app.doShellScript("rm " + tempFilePath);
return `${result.nodes.length} Obsidian Canvas nodes copied to clipboard`;
}
Liked the post? Treat me to a coffee