Home

Handling Bookmarks In Neopoligen

Head's up: I'm actively overhauling my site builder right now. To wit:

  • Most images and lots of links are broken
  • Code blocks don't wrap and don't have syntax highlighting
  • Other miscellaneous formatting and functionality is off

Please excuse the mess while I put things back together

Introduction

I just read a post from Roma Komarov1 about managing and publishing bookmarks on a website2. Handling bookmarks is something I've been giving a lot of thought to as I work on my website builder (which is called Neopoligen3).

Bookmarks are interesting in their own right. They also make a great proxy for other categories of content. Quotes, reviews, and image galleries all come to mind.

Starting With Sections

Neopoligen (the website builder) is designed to work with files written in Neopolitan4 (a markdown replacement format). Each file consists of sections that start with tags that look like this:

-- section-name

This is where the section content goes

Section can have flags and key/value attributes. For example:

-- section-name
-- flag
-- key: value

This is where the section content goes

A Basic Bookmark

For a real-world example, here's a demo-bookmark-v1 section that's slated to ship with the first version Neopoligen:

-- demo-bookmark-v1
-- title: Example.com
-- url: https://www.example.com/

The go-to URL for when you need to point test 
links somewhere

Output for each section is controlled by template files (which will see a bit further below). Here's the live output from the demo template:

Bookmark

The go-to URL for when you need to point test links somewhere

Adding Details

Attributes are arbitrary. You can add as many as you like and just wire the template up to use them.

It's also possible to nest sections inside each other.

Here's another example that combines those two things: more attributes and a block quote nested inside the bookmark:

-- demo-bookmark-v1/
-- title: Example.com
-- subtitle: My favorite place to link to
-- url: https://www.example.com/
-- tag: web dev 
-- tag: neopoligen

The go-to URL for when you need to point test 
links somewhere. According to the page itself:

    -- blockquote/

    This domain is for use in illustrative 
    examples in documents. You may use this 
    domain in literature without prior 
    coordination or asking for permission.

    -- /blockquote

I wonder how much traffic that site gets 
every time I link to it.

-- /demo-bookmark-v1

Here's the output:

Bookmark
My favorite place to link to

The go-to URL for when you need to point test links somewhere. According to the page itself:

This domain is for use in illustrative examples in documents. You may use this domain in literature without prior coordination or asking for permission.

I wonder how much traffic that site gets every time I link to it.

tags: web dev, neopoligen

No JavaScript Required

Neopoligen doesn't use a JavaScript framework. It uses templates written in a modified flavor of Jinja5. Here's the complete template that rendered everything above. (Fair warning: syntax highlighting might not be working when you see this page. It makes looking at the sample a bit tough.)

[!- import "built-in/macros.neoj" as neo -!]

[# TYPE: basic #]
[# ATTRS_TO_HOIST: [class, id] #]

<section [@- neo.hoist_section_attrs() -@]>

  <div class="demo-bookmark-v1-header">
    Bookmark
  </div>

  <div class="demo-bookmark-v1-body">

    <div class="demo-bookmark-v1-title">
      <a href="[@- neo.output_attr("url") -@]">
        [@- neo.output_attr("title") -@]
      </a>
    </div>

    [!- if "subtitle" in section.attrs -!]
      <div class="demo-bookmark-v1-subtitle">
        [@- neo.output_attr("subtitle") -@]
      </div>
    [!- endif -!]

    <div class="demo-bookmark-v1-details">
      [@- neo.output_all_child_sections() -@]
    </div>

    [!- if "tag" in section.attrs -!]
      <div class="demo-bookmark-v1-tag-wrapper">
        tags:[@- ' ' -@]
          [!- for tag_item in section.attrs["tag"] -!]
            [@- neo.output_spans(tag_item.spans) -@]
            [!- if not loop.last -!]
              ,[@- ' ' -@]
            [!- endif -!]
          [!- endfor -!]
      </div>
    [!- endif -!]

    <div class="demo-bookmark-v1-url-wrapper">
      <a href="[@- neo.output_attr("url") -@]">
        [@- neo.output_attr("url") -@]
      </a>
    </div>

  </div>
</section>

[@- neo.output_end_section(section) -@]

I'll write up more about how the templates work in other posts. In the mean time, you can probably get a good idea of what's going on if you've worked with HTML before.

Neopoligen will come with enough basic templates that you'll be able to build a basic site without having to mess with them at all. But, everything is open and accessible to either modify or make entirely new sections and templates yourself.

A Database Of Files

One of my favorite things about working with Neopoligen is not having to spin up database software. The content files act like a database on their own. It's so nice to be a able to create ad-hoc types by simply naming a new section then making a corresponding template for it.

There's more in store along those lines. I'm working on querying capabilities for the content. They'll provide for making collections, links between the tags in the example, and all the other stuff you can do with collections.

You'll also be able to have files that don't end up as posts. The idea is to set a flag in them so they load their sections for querying without directly publishing the underlying page.

Bookmarks will be one of my main use cases for this. Specifically, it'll let you have a bunch of files with different categories of bookmarks that can either be output directly or via sets gathered from queries.

Wrapping Up

I've been working on Neopoligen (and Neopolitan) for two years. The journey has been longer than you might expected. Partially, that's because I decided to make it in Rust6 (which I'd never used before) with a parser combinator (which I'd never even heard of before).

More importantly, I wanted to make the experience feel right. I spent revision after revision working to reduce the friction between having an idea and getting it to show up on a web page.

It's been a long haul, but I couldn't be happier with where it's landing. The more chances I get to use Neopoligen instead of just working on it, the more enjoyment I'm getting out of it.

Right now, Neopoligen works on my computer. I'm close to publishing a beta version that other folks will be able to use too. The goal is to have it out by the end of the year.

I can't wait to share it with you.

- alan

~ fin ~

Footnotes

1 ⤴
Roma Komarov on Mastodon
2 ⤴
Managing Bookmarks, part 1 - by Roma Komarov
3 ⤴
Neopoligen - The Website Builder
4 ⤴
Neopolitan

I don't have a good link handy for the Neopolitan format at the moment. The details are on the Neopoligen page for now

5 ⤴
Jinja, MiniJinja, and NeoJinja

Neopoligen is written in Rust and uses the MiniJinja template engine (which is based off the original Jinja template engine). I've modified it a little to change the tokens from things like {{ }} to [@ @] which I find easier to read and track when working with the templates.

6 ⤴
The Rust Programming Language

The learning curve has been no joke, but the more I use Rust the more I like it.

7 ⤴
The nom parser cobinator

Written in rust and "eating data byte by byte". Another part of the process where the learning curve kicked my butt for a while. Now that I've got my head around it a bit, it's super powerful to work with.