Here’s a vanilla version (for testing in Script Editor, set the language at top left to JavaScript
)
It:
- copies the page name and url of the front Safari document both as Bike rich text and as MD plain text, and
- creates a rich text version of the link at the top of the front Bike document, if one is open.
Expand disclosure triangle to view JS source
(() => {
"use strict";
ObjC.import("AppKit");
// Copy Bike and MD versions of a link to the
// front Safari page,
// and add rich text link to top of
// front Bike document.
// RobTrew @2023
// Ver 0.01
const main = () => {
const page = Application("Safari").documents.at(0);
return either(
alert("Safari page name and url to Bike")
)(
mdVersion => mdVersion
)(
bindLR(
page.exists() ? (() => {
const
label = page.name(),
url = page.url(),
md = `[${label}](${url})`,
xml = linkFromBike(url)(label);
return (
setClipOfTextType(true)(
"com.hogbaysoftware.bike.xml"
)(xml),
setClipOfTextType(false)(
"public.utf8-plain-text"
)(md),
Right([md, xml])
);
})() : Left("No pages open in Safari.")
)(
([md, xml]) => {
const
bike = Application("Bike"),
doc = bike.documents.at(0);
return doc.exists() ? (
doc.import({
from: xml,
as: "bike format"
}),
Right(md)
) : Left("No documents open in Bike.");
}
)
);
};
// linkFromBike :: URL String -> String -> XML String
const linkFromBike = url =>
label => (
`<?xml version="1.0" encoding="UTF-8"?>
<html>
<head><meta charset="utf-8"/></head>
<body><ul><li>
<p><a href="${url}">${label}</a></p>
</li></ul></body>
</html>`
);
// ----------------------- JXA -----------------------
// alert :: String => String -> IO String
const alert = title =>
s => {
const sa = Object.assign(
Application("System Events"), {
includeStandardAdditions: true
});
return (
sa.activate(),
sa.displayDialog(s, {
withTitle: title,
buttons: ["OK"],
defaultButton: "OK"
}),
s
);
};
// setClipOfTextType :: Bool -> String ->
// String -> IO String
const setClipOfTextType = clear =>
utiOrBundleID =>
txt => {
const pb = $.NSPasteboard.generalPasteboard;
return (
clear && pb.clearContents,
pb.setStringForType(
$(txt),
utiOrBundleID
),
txt
);
};
// --------------------- GENERIC ---------------------
// Left :: a -> Either a b
const Left = x => ({
type: "Either",
Left: x
});
// Right :: b -> Either a b
const Right = x => ({
type: "Either",
Right: x
});
// bindLR (>>=) :: Either a ->
// (a -> Either b) -> Either b
const bindLR = m =>
mf => m.Left ? (
m
) : mf(m.Right);
// either :: (a -> c) -> (b -> c) -> Either a b -> c
const either = fl =>
// Application of the function fl to the
// contents of any Left value in e, or
// the application of fr to its Right value.
fr => e => e.Left ? (
fl(e.Left)
) : fr(e.Right);
// fmapLR (<$>) :: (b -> c) -> Either a b -> Either a c
const fmapLR = f =>
// Either f mapped into the contents of any Right
// value in e, or e unchanged if is a Left value.
e => "Left" in e ? (
e
) : Right(f(e.Right));
return main();
})();
See: Using Scripts - Bike