Arc Browser: Building a plugin (Boost) with help from ChatGPT
Date: 2023-05-23 02:44:37
In this post I'll explain how I built a plugin for the Arc Browser with help from ChatGPT.

I've been using the https://arc.net/[Arc Browser^] for a couple of months now and one of my favourite things is the simplicity of the plugin (or as they call it, 'Boost') functionality.

I wanted to port over a Chrome bookmark that I use to capture the podcasts that I've listened to on https://player.fm/[Player.FM^].
In this blog post I'll show how ChatGPT helped me convert the bookmark code to an Arc Boost.

I started with this code that finds all the listened to podcasts and then writes them out to the console:

[source, javascript]
----
javascript:(()=>{  
    function decodeHtml(html) {      
        var txt = document.createElement("textarea");      
        txt.innerHTML = html;    
        return txt.value;  
    }    
    
    let rows = $("article[data-played-at]").map(function(idx, ele) {        
        let row = "* Listened to " + $(ele).attr('data-title') +   " by " + $(ele).find("div.title").text() + " - https://player.fm/series/" + $(ele).attr('data-series-id') + "/" + $(ele).attr('data-slug');        
        return decodeHtml(row);    
    }).get();    
    
    setTimeout(async()=>{    
        console.log(rows.join("\n"));    
        alert(rows.length + " podcasts copied")  
    }, 2000)}
)()
----

An example of the output is shown below:

[source, text]
----
* Listened to A groundbreaking new proof for Pythagoras’ Theorem? by More or Less: Behind the Stats - https://player.fm/series/1301260/a-groundbreaking-new-proof-for-pythagoras-theorem
* Listened to Twitter storm by Trending - https://player.fm/series/1301465/twitter-storm
* Listened to After Pulse: Suddenly Unemployed by Community Pulse - https://player.fm/series/1409158/after-pulse-suddenly-unemployed
----

I was relying on jQuery to do find the parts of the page, but that isn't available in an Arc Boost, so I need to use the built in APIs instead.
ChatGPT guided me towards the `document.querySelectorAll` function and converted this:

[source, javascript]
----
$("article[data-played-at]")
----

to:

[source, javascript]
----
document.querySelectorAll("article[data-played-at]")
----

But then I wasn't sure how to call the `map` function on that output, so I asked ChatGPT again.

image::{{<siteurl>}}/uploads/2023/04/chatgpt-opener.png[ChatGPT - Iterate over querySelectorAll]

So I did that and then needed to work out how to extract attributes from each element.
The function calls that ChatGPT told me to make didn't exist, so I had to fall back on the documentation (I know!). 
I eventually ended up with the following code to extract the rows:

[source, javascript]
----
let rows = Array.from(document.querySelectorAll("article[data-played-at]")).map(function(ele) {        
    let row = "* Listened to " + ele.attributes['data-title'].value + 
    " by " + ele.querySelector("div.title").textContent + 
    " - https://player.fm/series/" + ele.attributes['data-series-id'].value + 
    "/" + ele.attributes['data-slug'].value;

    return decodeHtml(row);    
});
----

Next I asked it to add a button to the page that I could click on to copy the listened episodes to the clipboard. 
It first suggested using `navigator.clipboard`, but that wasn't working for me at the time, so it suggested I used `document.execCommand('copy')` instead. 
The full code for the plugin looks like this:

[source, javascript]
----
function decodeHtml(html) {      
  var txt = document.createElement("textarea");      
  txt.innerHTML = html;    
  return txt.value;  
} 

// Create an image
const buttonDiv = document.createElement("div");
buttonDiv.style.display = "flex";
buttonDiv.style.justifyContent = "center";
buttonDiv.style.marginBottom = "16px";

const button = document.createElement("button");
button.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
  <path d="M11 0H3a2 2 0 0 0-2 2v9h2V2h8V2a2 2 0 0 0-2-2zm1 4H4v9h8V4z"/>
  </svg> Copy Podcasts`;

button.style.display = "flex";
button.style.alignItems = "center";
button.style.gap = "8px";
button.style.backgroundColor = "white";
button.style.border = "1px solid #ccc";
button.style.borderRadius = "4px";
button.style.padding = "8px";


button.onclick = function () {
    let rows = Array.from(document.querySelectorAll("article[data-played-at]")).map(function(ele) {        
        let row = "* Listened to " + ele.attributes['data-title'].value + 
        " by " + ele.querySelector("div.title").textContent + 
        " - https://player.fm/series/" + ele.attributes['data-series-id'].value + 
        "/" + ele.attributes['data-slug'].value;

        return decodeHtml(row);    
    });
 
    const textToCopy = rows.join("\n");

    const textArea = document.createElement('textarea');
    textArea.value = textToCopy;
    document.body.appendChild(textArea);

    textArea.select();
    document.execCommand('copy');
    document.body.removeChild(textArea);

    console.log(textToCopy);    
    alert(rows.length + " podcasts copied")  
};

buttonDiv.appendChild(button);

const preamble = document.querySelector(".preamble");
preamble.insertAdjacentElement("beforebegin", buttonDiv);
----

You can see the Boost in action below:

image::{{<siteurl>}}/uploads/2023/05/arc-plugin.gif[Animation of an Arc Boost copying listened episodes from Player.FM to the clipboard]