A => .hgignore +1 -0
@@ 0,0 1,1 @@ 
+_build/
  No newline at end of file

          
A => README.md +2 -0
@@ 0,0 1,2 @@ 
+# notes
+

          
A => content/assets/style.css +11 -0
@@ 0,0 1,11 @@ 
+html {font-family: Menlo, Consolas, Monaco, Liberation Mono, Lucida Console, monospace;font-size: 14px;background: #eee;}
+body {margin: 0 auto;padding:0 2ch;max-width: 76ch;min-height: 100vh;}
+body > header {position: absolute;top: 0;width: 76ch;display: flex;justify-content: space-between;}
+h1,h2,h3,h4,h5,h6 {margin:0;font-size:1.2rem;}
+main > header {margin: 4rem 0 2rem;}
+main > time {color:gray;}
+#blog-list ul li:hover {background-color: antiquewhite;}
+#blog-list ul {padding-left: 10ch;}
+#blog-list ul li {padding-left: 2ch;}
+#blog-list ul li::marker {content: attr(data-date);color: gray;}
+#changelog summary {font-weight: bold;margin-top: 2rem;}

          
A => content/index.html +11 -0
@@ 0,0 1,11 @@ 
+<h1>home</h1>
+
+<p>The quick brown fox jumps over the lazy dog.</p>
+
+<p><a class="btn" href="/whoami">whoami</a></p>
+
+<section id="notes">
+  <h2 id="notes">notes</h2>
+  <div id="blog-list"></div>
+</section>
+

          
A => content/whoami.html +17 -0
@@ 0,0 1,17 @@ 
+<h1>whoami</h1>
+
+<p>Lorem ipsum dolor sit amet.</p>
+
+<section id="projects">
+  <h2>projects</h2>
+  <p>Here I'm currently working on:</p>
+  <ul>
+    <li><a href="https://kamusurban.com">kamusurban</a>, like urbandictionary but for indonesian</li>
+    <li><a href="/alitpub">alitpub</a>, small, single-user activitypub server written in OCaml. this is project for learning ocml and activitypub</li>
+    <li><a href="https://komodo.social">komodo.social</a> or ksocial in short, a small indonesian comunity.</li>  
+  </ul>
+
+  <p>You can find more on <a href="https://github.com/siarie?tab=repositories&q=topic%3Ashowcase">github</a>,
+    <a href="https://gitlab.com/siarie">gitlab</a></p>
+</section>
+

          
A => helpers/first_update.sh +7 -0
@@ 0,0 1,7 @@ 
+#!/bin/sh
+
+TARGET_FILE=$1
+OUTPUT=$(hg log -r: -l1 -T '{date(date, "%Y-%m-%d %H:%M:%S")}' "$TARGET_FILE")
+
+[ -z "$OUTPUT" ] && OUTPUT=$(date +"%F %T")
+echo "<strong>Published: </strong><time id="pubdate" datetime=\"$OUTPUT\">$OUTPUT</time>"

          
A => helpers/log-index.lua +136 -0
@@ 0,0 1,136 @@ 
+if not config["index_template"] then
+  Plugin.fail("Please define the index_template option")
+end
+
+if not config["index_selector"] then
+  Plugin.fail("Please define the index_selector option")
+end
+
+tag_path = "tags"
+if config["tag_path"] then
+  tag_path = config["tag_path"]
+end
+
+local count = size(site_index)
+
+-- Render entries on the blog page
+local entries = {}
+local i = 1
+while (i<=count) do
+  entry = site_index[i]
+  entry["date"] = Date.reformat(entry["date"], {"%Y-%m-%d %H:%M:%S"}, "%Y-%m-%d")
+  entries[i] = entry
+  i = i + 1
+end
+
+env = {}
+env["entries"] = entries
+posts = HTML.parse(String.render_template(config["index_template"], env))
+container = HTML.select_one(page, config["index_selector"])
+HTML.append_child(container, posts)
+
+-- Create new pages for each tag
+
+tag_path = Sys.join_path(Sys.dirname(page_file), tag_path)
+
+-- Find all existing tags first
+all_tags = {}
+local i = 1
+while (i <= count) do
+    entry = entries[i]
+    local k = 1
+    if not entry["tags"] then
+        -- This entry has no tags, skip it
+        i = i + 1
+    else
+        tag_count = size(entry["tags"])
+        while (k <= tag_count) do
+            all_tags[entry["tags"][k]] = 1
+            k = k + 1
+        end
+    end
+    i = i + 1
+end
+
+all_tags = Table.keys(all_tags)
+
+function find_entries_with_tag(entries, tag)
+    local es = {}
+    local i = 1
+    Log.debug("so far")
+    local count = size(entries)
+    local k = 1
+    while (i <= count) do
+        entry = entries[i]
+        if not (Value.is_table(entry["tags"])) then
+            -- No tags in this entry, so it definitely does not match
+            i = i + 1
+        else
+            if Table.has_value(entry["tags"], tag) then
+                es[k] = entry
+                k = k + 1
+            end
+            i = i + 1
+        end
+    end
+    return es
+end
+
+function build_tag_page(entries, tag)
+    local matching_entries = find_entries_with_tag(entries, tag)
+    local template = "<h1>Posts tagged \"{{tag}}\"</h1>" .. 
+      "<div id=\"blog-list\">" .. config["index_template"] .. "</div>"
+    local env = {}
+    env["tag"] = tag
+    env["entries"] = matching_entries
+    posts = String.render_template(template, env)
+    return posts
+end
+
+pages = {}
+
+local i = 1
+local tag_count = size(all_tags)
+while (i <= tag_count) do
+    tag = all_tags[i]
+    Log.info(format("Generating a page for tag \"%s\"", tag))
+
+    tag_page = {}
+    tag_page["page_file"] = Sys.join_path(tag_path, format("%s.html", tag))
+    tag_page["page_content"] = build_tag_page(entries, tag)
+
+    pages[i] = tag_page
+
+    i = i + 1
+end
+
+-- Finally, generate a page with a list of all tags
+local tag_links = {}
+local i = 1
+local tag_count = size(all_tags)
+while (i <= tag_count) do
+    local tag = all_tags[i]
+    local tag_link = {}
+    tag_link["url"] = tag
+    tag_link["title"] = tag
+    tag_links[i] = tag_link
+
+    i = i + 1
+end
+
+local template = [[
+  <h1>Tags</h1>
+  <ul>
+  {% for t in tag_links %}
+    <li> <a href="{{t.url}}">{{t.title}}</a> </li>
+  {% endfor %}
+  </ul>
+  ]]
+
+local env = {}
+env["tag_links"] = tag_links
+local all_tags_page = {}
+all_tags_page["page_file"] = Sys.join_path(tag_path, "index.html")
+all_tags_page["page_content"] = String.render_template(template, env)
+
+pages[size(pages) + 1] = all_tags_page

          
A => plugins/atom.lua +105 -0
@@ 0,0 1,105 @@ 
+-- Atom feed generator
+
+Plugin.require_version("4.0.0")
+
+data = {}
+
+date_input_formats = soupault_config["index"]["date_formats"]
+
+feed_file = config["feed_file"]
+
+custom_options = soupault_config["custom_options"]
+
+if not Table.has_key(custom_options, "site_url") then
+  Plugin.exit([[Atom feed generation is not enabled in the config. If you want to enable it, set custom_options.atom_feeds = true]])
+end
+
+if not Table.has_key(custom_options, "site_url") then
+  Plugin.fail([[custom_options["site_url"] option is required when feed generation is enabled]])
+end
+
+data["site_url"] = custom_options["site_url"]
+data["feed_id"] = Sys.join_path(custom_options["site_url"], feed_file)
+
+data["soupault_version"] = Plugin.soupault_version()
+
+data["feed_author"] = custom_options["site_author"]
+data["feed_author_email"] = custom_options["site_author_email"]
+data["feed_title"] = custom_options["site_title"]
+data["feed_subtitle"] = custom_options["site_subtitle"]
+data["feed_logo"] = custom_options["site_logo"]
+
+
+function in_section(entry)
+  return (entry["nav_path"][1] == config["use_section"])
+end
+
+function tags_match(entry)
+  if config["use_tag"] then
+    return (Regex.match(entry["tags"], format("\\b%s\\b", config["use_tag"])))
+  else
+    return 1
+  end
+end
+
+entries = {}
+
+-- Original, unfiltered entries inded
+local n = 1
+
+-- Index of the new array of entries we are building
+local m = 1
+
+local count = size(site_index)
+while (n <= count) do
+  entry = site_index[n]
+  if (in_section(entry) and tags_match(entry)) then
+    if entry["date"] then
+      entry["date"] = Date.reformat(entry["date"], date_input_formats, "%Y-%m-%dT%H:%M:%S%:z")
+    end
+    entries[m] = entry
+    m = m + 1
+  end
+  n = n + 1
+end
+
+if (soupault_config["index"]["sort_descending"] or
+   (not Table.has_key(soupault_config["index"], "sort_descending")))
+then
+  data["feed_last_updated"] = entries[1]["date"]
+else
+  data["feed_last_updated"] = entries[size(entries)]["date"]
+end
+
+data["entries"] = entries
+
+feed_template = [[
+<?xml version='1.0' encoding='UTF-8'?>
+<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
+  <id>{{feed_id}}</id>
+  <updated>{{feed_last_updated}}</updated>
+  <title>{{feed_title}}</title>
+  {%- if feed_subtitle -%} <subtitle>{{feed_subtitle}}</subtitle> {%- endif -%}
+  {%- if feed_logo -%} <logo>{{feed_logo}}</logo> {%- endif -%}
+  <author>
+    <name>{{feed_author}}</name>
+    {%- if feed_author_email -%}<email>{{feed_author_email}}</email> {%- endif -%}
+  </author>
+  <generator uri="https://soupault.app" version="{{soupault_version}}">soupault</generator>
+  {%- for e in entries %}
+  <entry>
+    <id>{{site_url}}{{e.url}}</id>
+    <title>{{e.title}}</title>
+    <updated>{{e.date}}</updated>
+    <content type="html">
+    {{e.excerpt | escape}}
+    </content>
+    <link href="{{site_url}}{{e.url}}" rel="alternate"/>
+  </entry>
+  {% endfor %}
+</feed>
+]]
+
+feed = String.render_template(feed_template, data)
+
+Sys.write_file(Sys.join_path(target_dir, feed_file), String.trim(feed))

          
A => plugins/changelog.lua +29 -0
@@ 0,0 1,29 @@ 
+Plugin.require_version("4.0.0")
+
+prefix_url = "https://hg.sr.ht/~siarie/siarie.cc/rev"
+command = "hg log --template '<li><a href=\"" .. prefix_url .. "/{node}\">{node|short}</a> {desc|strip|firstline}</li>' " .. page_file
+
+command_output = Sys.get_program_output(command)
+if not command_output then
+   Plugin.exit("Nothing to do")
+end
+
+changelogs = HTML.parse(command_output)
+
+-- echo "<details id=\"changelog\">"
+-- echo "<summary>Changelog</summary>"
+-- echo "<ul>"
+-- echo $LOGS
+-- echo "</ul>"
+-- echo "</details>"
+
+details = HTML.create_element("details")
+HTML.set_attribute(details, "id", "changelog")
+HTML.append_child(details, HTML.create_element("summary", "Changelog"))
+
+ul = HTML.create_element("ul")
+HTML.append_child(ul, changelogs)
+HTML.append_child(details, ul)
+
+HTML.insert_after(HTML.select_one(page, "main"), details)
+

          
A => plugins/tags.lua +34 -0
@@ 0,0 1,34 @@ 
+Plugin.require_version("1.10")
+
+elements = HTML.select(page, "tag")
+local count = size(elements)
+if count ~= 0 then
+   tags = HTML.create_element("div")
+   HTML.set_attribute(tags, "id", "tags")
+
+   key = HTML.create_element("strong")
+   HTML.append_child(key, HTML.create_text("Tags: "))
+
+   HTML.append_child(tags, key)
+
+   local idx = 1
+   while elements[idx] do
+      el = elements[idx]
+      data = HTML.strip_tags(el)
+      tag_link = HTML.create_element("a")
+      HTML.set_attribute(tag_link, "href", "/tags/" .. data)
+      HTML.set_attribute(tag_link, "class", "tag")
+
+      if idx ~= 1 then
+         HTML.append_child(tags, HTML.create_text(", "))
+      end
+
+      HTML.append_child(tag_link, HTML.create_text(data))
+      HTML.append_child(tags, tag_link)
+      
+      HTML.delete_element(el)
+      idx = idx+1
+   end
+
+   HTML.append_child(HTML.select_one(page, "main"), tags)
+end

          
A => soupault.toml +123 -0
@@ 0,0 1,123 @@ 
+[custom_options]
+site_url = "https://siarie.me/"
+site_author = "Sri Aspari"
+site_author_email = "mail@siarie.me"
+site_title = "siarie's notes"
+# site_logo = "https://example.com/~jrandomhacker/favicon.png"
+
+[settings]
+soupault_version = "4.10.0"
+strict = true
+verbose = false
+debug = true
+site_dir = "content"
+build_dir = "_build"
+
+page_file_extensions = ["htm", "html", "md", "adoc"]
+
+clean_urls = true
+
+keep_extensions = ["html", "htm"]
+default_extension = "html"
+
+ignore_extensions = ["draft"]
+generator_mode = true
+complete_page_selector = "html"
+default_template_file = "templates/main.html"
+default_content_selector = "main"
+default_content_action = "append_child"
+keep_doctype = true
+doctype = "<!DOCTYPE html>"
+pretty_print_html = false
+plugin_discovery = true
+plugin_dirs = ["plugins"]
+
+caching = false
+cache_dir = ".soupault-cache"
+
+page_character_encoding = "utf-8"
+
+
+# It is possible to store pages in any format if you have a program
+# that converts it to HTML and writes it to standard output.
+# Example:
+#[preprocessors]
+#  md = "cmark --unsafe --smart"
+#  adoc = "asciidoctor -o -"
+
+[index]
+index = true
+extract_after_widgets = ['insert-publish-date']
+sort_by = "date"
+sort_descending = true
+sort_type = "calendar"
+date_formats = ["%Y-%m-%d"]
+strict_sort = false
+
+
+[index.fields]
+title = { selector = ["h1"] }
+tags = { selector = ".tag", select_all = true }
+date = { selector = "time#pubdate", extract_attribute = "datetime", fallback_to_content = true }
+
+[index.views.blog-index]
+index_selector = "#blog-list"
+section = "notes"
+include_subsections = true
+index_template = """
+<ul>
+{% for e in entries %}
+<li data-date="{{e.date}}"><a href="{{e.url}}">{{e.title}}</a></td></li>
+{% endfor %}
+</ul>
+"""
+file = "helpers/log-index.lua"
+
+[widgets.page-title]
+widget = "title"
+selector = "h1"
+default = "siarie's notes"
+force = false
+
+[widgets.generator-meta]
+widget = "insert_html"
+html = '<meta name="generator" content="soupault">'
+selector = "head"
+
+[widgets.insert-changelog]
+widget = "changelog"
+# selector = "main"
+# section = "notes"
+# action = "insert_after"
+after = "insert-publish-date"
+# exclude_path_regex = ["(.*)/index(.*)", "tags/(.*)"]
+# command = "./helpers/changelog.sh $PAGE_FILE"
+
+[widgets.insert-publish-date]
+widget = "exec"
+selector = "main"
+section = "notes"
+action = "append_child"
+# exclude_path_regex = ["(.*)/index(.*)", "tags/(.*)", "(.*)/whoami(.*)"]
+command = "helpers/first_update.sh $PAGE_FILE"
+
+[widgets.wrap-header]
+widget = "wrap"
+wrapper = "<header>"
+selector = "main>h1"
+
+[widgets.make-tags]
+widget = "tags"
+before = "insert-publish-date"
+
+[widgets.back-to-home]
+widget = "insert_html"
+selector = "main"
+action = "insert_after"
+html = '<p><a href="/">&lhard; back to home</a></p>'
+
+[widgets.make-feed]
+widget = "atom"
+page = "index.html"
+use_section = "notes"
+feed_file = "index.xml"

          
A => templates/main.html +14 -0
@@ 0,0 1,14 @@ 
+
+<html lang="en">
+  <head>
+    <meta charset="utf-8">
+    <title></title>
+    <meta name="viewport" content="width=device-width, initial-scale=1">
+    <link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text x='50' y='50' font-size='70' text-anchor='middle' dominant-baseline='middle'>🌏</text></svg>" />
+    <link rel="stylesheet" href="/assets/style.css">
+  </head>
+  <body>
+    <header></header>
+    <main></main>
+  </body>
+</html>