diff options
Diffstat (limited to 'citrine.lua')
-rw-r--r-- | citrine.lua | 172 |
1 files changed, 172 insertions, 0 deletions
diff --git a/citrine.lua b/citrine.lua new file mode 100644 index 0000000..3f9b41a --- /dev/null +++ b/citrine.lua @@ -0,0 +1,172 @@ +local h = require 'html' + +local M = {} + +local site_sections = { + default = "account", +} + +local function navbar(links) + h.nav({class = "site-nav"}, function() + for _, link in ipairs(links) do + h.a({ + href = link[2], + class = link[2] == links.selected and "selected" or nil + }, link[1]) + h.text " " + end + end) +end + +function M.page(param) + return h.render_doc(function() + h.meta {charset = "utf-8"} + h.title(param.title and + param.title .. " — auth.citrons.xyz" or "auth.citrons.xyz") + h.meta { + name = "viewport", + content = "width=device-width, initial-scale=0.75" + } + h.link {rel = "stylesheet", href = "/static/citrine.css"} + h.div({id = "page-grid"}, function() + h.header({class = "site-header"}, function() + param.section = param.section or "default" + h.img({ + src = "/static/headers/"..param.section..".png", + alt = site_sections[param.section] + }) + end) + h.div({class = "content"}, function() + h.main(param[1]) + h.footer({class = "site-footer"}, function() + h.p(function() + h.a({href = + "https://citrons.xyz/git/auth.git/" + }, "© 2023 citrons") + end) + end) + end) + h.div({class = "header-icons"}, function() + h.a({class = "help", href = "/account"}, function() + h.img { + src = "/static/gear.png", alt = "account settings", + class = "help" + } + end) + h.a({class = "help", href = "/"}, function() + h.img { + src = "/static/qmark.png", alt = "about", + class = "help" + } + end) + end) + end) + end) +end + +local function class_article(e) + return function(...) + h[e]({class = "article"}, ...) + end +end + +M.h1 = class_article "h1" +M.h2 = class_article "h2" +M.h3 = class_article "h3" +M.h4 = class_article "h4" +M.h5 = class_article "h5" +M.h6 = class_article "h6" + +function M.quote(quote, attrib) + h.figure({class = attrib and "quote attrib" or "quote"}, function() + h.blockquote(function() + h.p({}, quote) + end) + if attrib then + h.figcaption({}, attrib) + end + end) +end + +function M.dyk(content) + h.aside({class = "dyk"}, function() + h.h2 "did you know?" + if type(content) == "string" then + html.p(content) + else + content() + end + end) +end + +function M.code_block(content) + h.pre({class = "code-block"}, content) +end + +function M.apiobutton() + h.a({href = "https://citrons.xyz/a/memetic-apioform-page", + class="apiopage"}, function() + h.img {src = "https://citrons.xyz/a/static/apiopage.png", + alt = "memetic apiopage: click to view"} + end) +end + +function M.redact(s) + h.del({class = "redaction", title = "redacted text"}, function() + h.span({}, s:gsub(utf8.charpattern, function(c) + if c ~= " " then return "X" end + end)) + end) +end + +function M.errmsg(e) + h.div({class = "errmsg"}, function() + h.p({}, e) + end) +end + +local statuses = { + [400] = "400 Bad Request", + [401] = "401 Unauthorized", + [403] = "403 Forbidden", + [404] = "404 Not Found", + [405] = "405 Method Not Allowed", + [410] = "410 Gone", + [411] = "411 Length Required", + [413] = "413 Payload Too Large", + [429] = "429 Too Many Requests", + [500] = "500 Internal Server Error", + [501] = "501 Not Implemented", + [502] = "502 Bad Gateway", + [503] = "503 Service Unavailable", + [504] = "504 Gateway Timeout" +} + +local messages = { + [400] = "your software has sent an invalid request to the server.", + [401] = "permission denied.", + [403] = "permission denied.", + [404] = "the page you attempted to access was not found.", + [405] = "your software has sent a request with an invalid method.", + [410] = "this content is no longer available.", + [411] = "your software failed to provide a length for the request body.", + [413] = "your software sent too much data to the server.", + [429] = "you are doing this too often. try again later.", + [500] = "a temporary internal error has occurred. this is my fault.", + [501] = "the requested feature is not implemented.", +} + +function M.error(code, message) + return { + content_type = 'text/html', status = statuses[code] or code + }, M.page { + section = "error", + function() + M.h1((statuses[code] or ("error "..code)):lower()) + h.p(message or messages[code] or + messages[code // 100 * 100] or "an error has occurred") + end + } +end + +return M |