Script to paste Safari URL& Title to Bike

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

2 Likes