dogeared

I spend a lot of time reading and watching things on the internet. Sometimes these things are long / broken into parts across many different pages and I don't have a good way to keep track of my progress without either juggling a lot of bookmarks, writing down where I left off elsewhere, or eternally keeping a tab open.

This browser extension tries to solve this problem by providing a way to save the current page as your place in a "series", load it, and overwrite the saved page with some other page.

Until validation is implemented, in practice, what I'm referring to as a "series" does not have to be an actual series, but is simply "a named container for a URL". For many use cases, validation would involve parsing the actual web page or providing a table of contents, which is unnecessary. Still, I plan to implement basic validation using the current domain, or a user-provided prefix.

features in order of development

permalink to "features in order of development"

Browser APIs:

Third-party dependencies:

External Information needed

Series Information JSON
{
    "series": [
        {
            "id": "1e8ed227-7c10-4000-afa0-669f184576fc",
            "name": "Grant Abbitt - Blender 3 - Complete Beginners Guide",
            "marks": [
                {
                    "title": "Blender 3 - Complete Beginners Guide - Part 2 - Materials & Rendering",
                    "url": "https://www.youtube.com/watch?v=g5lHlUB66r0&list=PLn3ukorJv4vuU3ILv3g3xnUyEGOQR-D8J&index=2",
                    "creation_time" : 1704668925
                },
                {
                    "title": "Blender 3 - Complete Beginners Guide - Part 3 - The Old Man",
                    "url": "https://www.youtube.com/watch?v=zt2ldQ23uOE&list=PLn3ukorJv4vuU3ILv3g3xnUyEGOQR-D8J&index=3",
                    "creation_time": 1704928125
                } // the page displayed is always the last one
            ],
            "validation_prefix": "https://youtube.com",
            "favicon": `icon_${id}.png`, // or store separately
            "last_access_time": 1704928125, // update, load
            "creation_time": 1704928125
        },
        {
            "id": "047b0f7b-2da2-4b5c-8bb3-36078a2c1b08",
            "name": "Crafting Interpreters",
            "marks": [
                {
                    "title": "Scanning - Crafting Interpreters",
                    "url": "http://craftinginterpreters.com/scanning.html#lexemes-and-tokens",
                    "creation_time": 1704668925
                }
            ],
            "validation_prefix": "http://craftinginterpreters.com",
            "favicon": "",
            "last_access_time": 1704928125,
            "creation_time": 1704928125
        },
        {
            "id": "c4df7368-747c-4c88-ba3c-2256355bead4",
            "name": "Misc Cat Videos",
            "marks": [
                {
                    "title": "Nyan Cat! [Official]",
                    "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
                    "creation_time": 1704668925
                },
                {
                    "title": "ABBA - Happy New Year (cover by Bongo Cat)",
                    "url": "https://www.youtube.com/watch?v=hQdVkcB6e9U",
                    "creation_time": 1704668925
                }
            ],
            "validation_prefix": "https://youtube.com",
            "favicon": "",
            "last_access_time": 1704928125,
            "creation_time": 1704928125
        }
    ]
}
Options JSON
{
    "default_sort": "LAST_UPDATED", // LAST_ACCESSED, LAST_CREATED, CUSTOM(?)
    "default_theme": "light", // dark, auto
    "show_favicons": False,
    "max_history_size": 10, // -1 for infinite
    "validate_urls": True
}

Went through the MDN popup extension tutorial and then tried to make a simple popup where you can click a button and populate it with the current tab's title and URL. Success!

The thing I most struggled with was the development workflow. The manual way of adding a temporary addon, inspecting it, reloading the addon after changing it, was definitely not the way to go.

After some trial and error and hunting around documentation I finally figured out a web-ext command to open the Firefox developer edition with the extension installed, to a specific page, with dev tools for the popup itself open (you can't inspect the popup itself with the normal inspector, and you can't disable the popup auto-hide either).

web-ext run \
    --firefox=/Applications/Firefox\ Developer\ Edition.app/Contents/MacOS/firefox \
    --devtools --firefox-profile=dev-edition-default \
    --start-url http://google.com

I also added the --firefox-profile option so I could access uBlock and TreeStyleTabs in the test browser environment.

Even with this, I still have to manually rearrange my windows and disable the popup auto-hide every time I run web-ext. Wish there was a flag or setting that I could check.

Since this is just a browser extension, I didn't want to use any build tools nor any frameworks. I looked into vanilla JS reactive app patterns and tried using web components for the first time and while I did get something working (see my web-components branch on github) I didn't have a clear understanding of how to hook the data (essentially a json blob) into separate components. Do you just store the whole object in the component itself? How could I export the data afterwards? I think if I had experience using React or other similar frameworks, it would have been more intuitive.

I looked at the TodoMVC implementation in Vanilla JS by the folks at Frontend Masters and read through the code, which made a lot of sense to me (the way the data layer was separated and so on) compared to whatever I was doing with web components. It also taught me about event delegation and using events with the storage layer to trigger re-rendering. I rewrote the extension to follow the simplest approach and got the series list rendering, as well as saving and loading data from localstorage.

Got the MVP of the extension working with overwrite, load, create/remove series functionality, and installed it persistently on my regular browser.

Here are some demos! (After recording I added a "Remove Series" button.)

dogeared demo save and load

dogeared demo create series