HGRC Quarry Fossil Service
Simple tools for lasting software

Infrastructure as a Service / Nemoptera

A solid foundation to fork, tweak, and enhance.

Public | Owner: Infrastructure as a Service | Default branch: trunk | Last update: 2026-11-08 16:13

Artifact 54fcec26569aa09d18bc7fa72867b08fcd700a02fc67596ca74700d78cdba77e:


(defmodule nemoptera_ui
  (export all))

(defun render (page-title current mode logged-in? main)
  (let* ((site (nemoptera_site:all))
         (name (maps:get 'site_name site "Nemoptera"))
         (tagline (maps:get 'site_tagline site "")))
    (lists:flatten
     (list
      "<!DOCTYPE html><html lang=\"en\"><head><meta charset=\"utf-8\">"
      "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">"
      "<title>" (escape page-title) "</title><style>" (css) "</style></head><body>"
      "<h1 class=\"site\">" (escape name) "</h1>"
      (if (=:= "" tagline) "" (list "<p class=\"tagline\">" (escape tagline) "</p>"))
      "<p class=\"recent\">Recent changes: <a href=\"/timeline\">" (escape (recent-label)) "</a>"
      " | <a href=\"/rss\">RSS</a> | <a href=\"/atom\">Atom</a></p><hr class=\"rule\">"
      "<div class=\"frame\"><nav class=\"shortcuts\"><h2>Shortcuts</h2>"
      (shortcuts current mode logged-in?)
      "</nav><div class=\"bar\"></div><main class=\"main\">"
      main
      "</main></div></body></html>"))))

(defun recent-label ()
  (if (nemoptera_setup:installed?)
      (nemoptera_site:recent-stamp)
      "no changes yet"))

(defun shortcuts (current mode logged-in?)
  (let ((items (if (=:= mode 'admin)
                   (admin-links logged-in?)
                   (public-links logged-in?))))
    (lists:map (lambda (item) (render-shortcut item current)) items)))

(defun render-shortcut (item current)
  (case item
    (`#(,key ,href ,label ,target)
     (list "<a href=\"" (escape href) "\""
           (if (=:= target 'blank) " target=\"_blank\" rel=\"noopener\"" "")
           ">"
           (if (=:= key current) (list "<strong>" (escape label) "</strong>") (escape label))
           "</a>"))
    (`#(,key ,href ,label)
     (list "<a href=\"" (escape href) "\">"
           (if (=:= key current) (list "<strong>" (escape label) "</strong>") (escape label))
           "</a>"))))

(defun public-links (logged-in?)
  (lists:append
   (list (tuple 'home "/" "Home")
         (tuple 'repos "/#repositories" "Repositories")
         (tuple 'orgs "/#organizations" "Organizations")
         (tuple 'timeline "/timeline" "Timeline"))
   (lists:append
    (nemoptera_site:custom-shortcuts)
    (if logged-in?
        (list (tuple 'admin "/admin" "Admin") (tuple 'logout "/logout" "Logout"))
        (list (tuple 'login "/login" "Login"))))))

(defun admin-links (_logged-in?)
  (lists:append
   (list (tuple 'home "/admin" "Home")
         (tuple 'repos "/admin/repos" "Repositories")
         (tuple 'orgs "/admin/orgs" "Organizations")
         (tuple 'settings "/admin/settings" "Settings")
         (tuple 'public "/" "Public site"))
   (lists:append
    (nemoptera_site:custom-shortcuts)
    (list (tuple 'logout "/logout" "Logout")))))

(defun escape (value)
  (let ((text (nemoptera_text:to-str value)))
    (string:replace
     (string:replace
      (string:replace
       (string:replace
        (string:replace text "&" "&amp;" 'all)
        "<" "&lt;" 'all)
       ">" "&gt;" 'all)
      "\"" "&quot;" 'all)
     "'" "&#39;" 'all)))

(defun css ()
  "body{margin:0;background:#fff;color:#111;font:15px/1.45 Verdana,Geneva,sans-serif;}
h1.site{margin:26px 12px 0;text-align:center;font:700 40px/1.1 Georgia,'Times New Roman',Times,serif;letter-spacing:-.4px;}
.tagline{margin:8px 16px 0;text-align:center;}
.recent{margin:14px 12px 10px;text-align:center;}
a:link,a:visited{color:#00e;}
hr.rule{border:0;border-top:1px solid #000;margin:0;}
.frame{display:flex;align-items:stretch;max-width:1040px;margin:18px auto 56px;padding:0 12px;}
.shortcuts{width:148px;flex:0 0 148px;padding:2px 14px 0 4px;}
.shortcuts h2{margin:0 0 10px;font-style:italic;font-weight:normal;font-size:16px;}
.shortcuts a{display:block;margin:3px 0;}
.bar{width:1px;background:#000;flex:0 0 1px;}
.main{flex:1;min-width:0;padding:0 4px 0 22px;}
.cats{columns:2 280px;column-gap:42px;}
.cat{break-inside:avoid;margin:0 0 22px;}
.cat h2{margin:0 0 4px;font-size:20px;}
.cat h2 a{text-decoration:underline;}
.blurb,.repoline{margin:0 0 6px;}
h1.page{margin:0 0 10px;font-size:22px;}
p{margin:8px 0;}
table{border-collapse:collapse;width:100%;}
th,td{text-align:left;vertical-align:top;padding:5px 12px 5px 0;border-bottom:1px solid #ddd;}
th{border-bottom:1px solid #000;font-weight:normal;}
label{display:block;margin:12px 0 3px;}
input[type=text],input[type=password],input[type=url],textarea,select{font:inherit;width:min(34rem,100%);border:1px solid #777;padding:3px 6px;background:#fff;}
textarea{min-height:5.5rem;}
button,.button{font:inherit;background:#fff;border:1px solid #777;padding:3px 12px;color:#00e;cursor:pointer;}
.note{color:#333;}
.error{color:#900;}
.actions{margin-top:14px;}
.inline{display:inline;}
.check{margin-top:10px;}
")

Clone

You can clone this repository with the following command:

fossil clone https://hgrc.dev/IaaS/Nemoptera Nemoptera.fossil

Contribute

The best way to contribute is through e-mail. See the project documentation for more details.