Copy name and URL of all the opened tabs of the frontmost Arc Browser window

Hi, I would like to create a macro that will copy all of the Name and URL of the frontmost Arc Browser window, so I can paste the results in Apple Notes or another software

I was able to write this script to get the name and url of the active tab of the Arc front window

tell application "Arc"
	
	set theTitle to title of active tab of front window
	
	
	set theUrl to URL of active tab of front window
	
	set the clipboard to "[" & theTitle & "]
(" & theUrl & ")"
end tell

Unfortunately, with my really limited Applescript knowledge I can't go any further.
How can I modify this script to get the names and url of all the tabs of the Arc frontmost window, skipping the pinned tabs?

To test in Script Editor, set the language selector drop-down at top left
to JavaScript (rather than AppleScript):

Expand disclosure triangle to view JS source
(() => {
    "use strict";

    ObjC.import("AppKit");

    // main IO()
    const main = () => {
        const allTabs = Application("Arc").windows.tabs;

        return copyText(
            zipWith(
                title => url => `[${title}](${url})`
                // title => url => [title, url]
            )(
                allTabs.title()
            )(
                allTabs.url()
            )
            .join("\n")
        );
    };

    // --------------------- GENERIC ---------------------

    // zipWith :: (a -> a -> b) -> [a] -> [b]
    const zipWith = f =>
        // A list with the length of the shorter of
        // xs and ys, defined by zipping with a
        // custom function, rather than with the
        // default tuple constructor.
        xs => ys => xs.slice(
            0, Math.min(xs.length, ys.length)
        )
        .map((x, i) => f(x)(ys[i]));


    // copyText :: String -> IO String
    const copyText = s => {
        const pb = $.NSPasteboard.generalPasteboard;

        return (
            pb.clearContents,
            pb.setStringForType(
                $(s),
                $.NSPasteboardTypeString
            ),
            s
        );
    };

    return main();
})();

Copies a Markdown link list, with labels and urls, to the plain text clipboard.

1 Like

For a version which copies in tab-delimited (rather than Markdown) format:

edit the function applied by zipWith to:

title => url => `${title}\t${url})`
1 Like

Or to restrict the harvest to the tabs of the front window:

Expand disclosure triangle to view JS source
(() => {
    "use strict";

    ObjC.import("AppKit");

    // main IO()
    const main = () => {
        const
            allTabs = Application("Arc")
            .windows.at(0).tabs;

        return copyText(
            zipWith(
                title => url => `[${title}](${url})`
                // title => url => [title, url]
            )(
                allTabs.title()
            )(
                allTabs.url()
            )
            .join("\n")
        );
    };

    // --------------------- GENERIC ---------------------

    // zipWith :: (a -> a -> b) -> [a] -> [b]
    const zipWith = f =>
        // A list with the length of the shorter of
        // xs and ys, defined by zipping with a
        // custom function, rather than with the
        // default tuple constructor.
        xs => ys => xs.slice(
            0, Math.min(xs.length, ys.length)
        )
        .map((x, i) => f(x)(ys[i]));


    // copyText :: String -> IO String
    const copyText = s => {
        const pb = $.NSPasteboard.generalPasteboard;

        return (
            pb.clearContents,
            pb.setStringForType(
                $(s),
                $.NSPasteboardTypeString
            ),
            s
        );
    };

    return main();
})();
1 Like

Thanks a lot @ComplexPoint !
Your last example is exactly what I need, I just wold like to divide the Page Title and the Url with a line break to have better clarity when I paste the text.

PS to restrict your harvest to unpinned tabs on the front window you would need a .where or .whose clause:

Expand disclosure triangle to view JS source
(() => {
    "use strict";

    const main = () => {
        const
            frontWindowUnpinnedTabs = Application("Arc")
            .windows.at(0)
            .tabs.where({location: "unpinned"});

        return zipWith(
            title => url => `${title}\t${url}`
        )(
            frontWindowUnpinnedTabs.title()
        )(
            frontWindowUnpinnedTabs.url()
        )
        .join("\n");
    };

    // --------------------- GENERIC ---------------------

    // zipWith :: (a -> a -> b) -> [a] -> [b]
    const zipWith = f =>
    // A list with the length of the shorter of
    // xs and ys, defined by zipping with a
    // custom function, rather than with the
    // default tuple constructor.
        xs => ys => xs.slice(
            0, Math.min(xs.length, ys.length)
        )
        .map((x, i) => f(x)(ys[i]));

    return main();
})();
1 Like

Thanks again @ComplexPoint , but this time when I run the script the result doesn't get copied on the clipboard like it was before

If you look at the earlier versions, you will see how to wrap the expression in copyText

Thanks a lot @ComplexPoint with your help I was able to get two scripts, one copies all the tabs and the other only the unpinned ones and there's a carriage return between tab name and URL

1 Like

Please share the complete code at the end. Thank you.

1 Like

Hey there,
I am trying to replicate this as this would be really useful for me but I am not sure how to choose JavaScript for the macro.
(Sorry real newbie here)

What Action is this? Execure JS for Automation?

Thanks!

I'm using an Execute AppleScript Action. I have previously written and saved the code as an AppleScript document

Sorry for replying many months later, I noticed your request just now when reading the today reply, Here's the complete code:

(() => {
    "use strict";

    ObjC.import("AppKit");

    // main IO()
    const main = () => {
        const
            frontWindowUnpinnedTabs = Application("Arc")
			.windows.at(0)
			.tabs.where({location: "unpinned"});

        return copyText(
            zipWith(
                title => url => `[${title}]\n(${url})`
                // title => url => [title, url]
            )(
                frontWindowUnpinnedTabs.title()
            )(
                frontWindowUnpinnedTabs.url()
            )
            .join("\n")
        );
    };

    // --------------------- GENERIC ---------------------

    // zipWith :: (a -> a -> b) -> [a] -> [b]
    const zipWith = f =>
        // A list with the length of the shorter of
        // xs and ys, defined by zipping with a
        // custom function, rather than with the
        // default tuple constructor.
        xs => ys => xs.slice(
            0, Math.min(xs.length, ys.length)
        )
        .map((x, i) => f(x)(ys[i]));


    // copyText :: String -> IO String
    const copyText = s => {
        const pb = $.NSPasteboard.generalPasteboard;

        return (
            pb.clearContents,
            pb.setStringForType(
                $(s),
                $.NSPasteboardTypeString
            ),
            s
        );
    };

    return main();
})();
1 Like

Thank you for the replay, Project. I am not sure what am I doing wrong but this is not returning anything.


.

Hello Adela (@Adela_Fiala):wave:

Welcome to the forum. This is a very great and helpful and friendly Community.

In your case you chose the wrong Action. Just switch to the Execute JavaScript for Automation Action. If the script doesn’t use the modern syntax you’re good to go but if not there is a setting for it in the action on the left side of the Textbox for the Script (KM 11.0+)

Greetings from Germany :de:

Tobias

1 Like

Hi Tobias @Nr.5-need_input! Thank you for your suggestion - I've tried but still no results. Is there anything else I could try?

Cheers from Czechia! :slight_smile:

Hello Adela (@Adela_Fiala):wave:

Did you see this ?!

There is the setting for modern syntax that I mentioned.

Did you tried using it disabled and enabled ?!

I am not able to tell you what syntax version the code is written in but normally one of these both settings should work if you have v11

Greetings from Germany :de:

Tobias

2 Likes

Hi Tobias! Great, this one worked, I had to toggle it to the xModern Syntax.

Thank you for your patience with newbie with zero JavaScript knowledge :smiling_face_with_tear: :upside_down_face:

1 Like