Redoing a bunch of stuff, came up with better ideas.
This commit is contained in:
parent
5bf5a19953
commit
e35e507381
19 changed files with 339 additions and 432 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,2 +1 @@
|
|||
deps
|
||||
napkin.ini
|
||||
|
|
6
.luarc.json
Normal file
6
.luarc.json
Normal file
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"$schema": "https://raw.githubusercontent.com/LuaLS/vscode-lua/master/setting/schema.json",
|
||||
"runtime.version": "LuaJIT",
|
||||
"addonManager.enable": true,
|
||||
"workspace.userThirdParty": ["luv"]
|
||||
}
|
18
LICENSE
18
LICENSE
|
@ -1,18 +0,0 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2025 Nelson "n" Lopez
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
|
||||
associated documentation files (the "Software"), to deal in the Software without restriction,
|
||||
including without limitation the rights to use, copy, modify, merge, publish, distribute,
|
||||
sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or
|
||||
substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
|
||||
NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
11
README.md
11
README.md
|
@ -1,11 +0,0 @@
|
|||

|
||||
|
||||
an experimental blogging platform
|
||||
|
||||
## running
|
||||
|
||||
you will need to install luvit and it's adjacent utilities from [here](https://luvit.io)
|
||||
```
|
||||
git clone git@kitsunes.dev:n/napkin.git
|
||||
luvi .
|
||||
```
|
88
config.lua
88
config.lua
|
@ -1,88 +0,0 @@
|
|||
getmetatable("").__mod = function(s, tab)
|
||||
return (s:gsub('($%b{})', function(w) return tab[w:sub(3, -2)] or w end))
|
||||
end
|
||||
|
||||
local setup = function()
|
||||
local template = [[
|
||||
[server]
|
||||
domain = ${domain}
|
||||
port = ${port}
|
||||
local_only = false
|
||||
|
||||
[database]
|
||||
type = sqlite
|
||||
file = ${database}
|
||||
|
||||
[static]
|
||||
from = ${static}
|
||||
|
||||
[mappings]
|
||||
; devlog-1 = @myuser/adding-cheese-pt1
|
||||
]]
|
||||
|
||||
local read = function(...)
|
||||
local a
|
||||
repeat
|
||||
a = io.read(...)
|
||||
until a
|
||||
return a
|
||||
end
|
||||
|
||||
local with_defaults = function(question, default)
|
||||
io.write(question .. " (" .. default .. ") > ")
|
||||
io.flush()
|
||||
local answer = read()
|
||||
if #answer == 0 then
|
||||
return default
|
||||
end
|
||||
return question
|
||||
end
|
||||
|
||||
print
|
||||
"hey there! i can see that you haven't set napkin up yet, so we will create an ini file to set napkin up.\n"
|
||||
|
||||
local domain = ""
|
||||
repeat
|
||||
io.write("what's your domain? > ")
|
||||
io.flush()
|
||||
domain = read()
|
||||
until #domain >= 4
|
||||
|
||||
local port = ""
|
||||
repeat
|
||||
io.write("what's the desired port? > ")
|
||||
io.flush()
|
||||
port = read()
|
||||
until tonumber(port)
|
||||
|
||||
local database = with_defaults("sqlite database?", "./napkin.sqlite")
|
||||
local static = with_defaults("static serve folder?", "./static/")
|
||||
if true then
|
||||
local populate = ""
|
||||
repeat
|
||||
io.write("the static serve folder is empty, populate it? [Y,n] > ")
|
||||
io.flush()
|
||||
populate = read()
|
||||
until populate == "y" or populate == "n" or populate == ""
|
||||
|
||||
if populate ~= "n" then
|
||||
print("everything going!")
|
||||
end
|
||||
end
|
||||
|
||||
print "\nlastly, where napkin's config file should go, the default (./napkin.ini) will be automatically loaded whenever napkin is called with no --config argument, otherwise you will need said argument. you can also cancel this whole process by pressing CTRL+C\n"
|
||||
local config = with_defaults("where should the config file go? ", "./napkin.ini")
|
||||
|
||||
print "\nand just like that, it's all done! be sure to have sqlite, ffmpeg and imagemagick installed, then run napkin again.\n\nhave a nice day!!! :)"
|
||||
|
||||
local file = assert(io.open(config, "w"), "cannot open file!")
|
||||
file:write(template % {
|
||||
domain = domain,
|
||||
port = port,
|
||||
database = database,
|
||||
static = static
|
||||
})
|
||||
file:close()
|
||||
|
||||
os.exit()
|
||||
end
|
|
@ -1 +0,0 @@
|
|||
local config = require "config"
|
13
database/blogs/supercool.json
Normal file
13
database/blogs/supercool.json
Normal file
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"owner": 8675309,
|
||||
"name": "Super Cool Blog",
|
||||
"biography": "Hey there! Name's Mike, but everyone calls me Supreme Mike around these parts. I'm just a regular dude trying to make it big in this crazy world. I love long walks on the beach, feeding ducks at the park, and occasionally BASE jumping off the local Walmart -- you know, normal stuff! My philosophy in life is to take everything in stri- WAIT A SECOND, IS THAT PIZZA I SMELL??? OH MY ABSOLUTE FREAKING GOD I CANNOT STAND PIZZA!!1! Do you know what PIZZA has DONE to our SOCIETY?!? The AUDACITY of those flat circular MONSTROSITIES to call themselves FOOD! Every time I see someone eating pizza I just want to SCREAM into the VOID because HOW CAN PEOPLE BE SO BLIND?! It's just sauce and cheese ON BREAD! Wake up SHEEPLE! This is why I've dedicated my entire existence to DESTROYING every pizza I see. I will not REST until every LAST SLICE is ERADICATED from this earth! ...anyway, I also enjoy gardening and collecting vintage stamps.",
|
||||
|
||||
"index": [
|
||||
{
|
||||
"title": "Why I hate pizza.",
|
||||
"url": "why-i-hate-pizza",
|
||||
"creation": 1674933627
|
||||
}
|
||||
]
|
||||
}
|
0
database/blogs/supercool/why-i-hate-pizza.json
Normal file
0
database/blogs/supercool/why-i-hate-pizza.json
Normal file
86
ini.lua
86
ini.lua
|
@ -1,86 +0,0 @@
|
|||
local function unescape(str)
|
||||
local escape_sequences = {
|
||||
["n"] = "\n",
|
||||
["t"] = "\t",
|
||||
["r"] = "\r",
|
||||
["b"] = "\b",
|
||||
["f"] = "\f",
|
||||
["v"] = "\v",
|
||||
["\\"] = "\\",
|
||||
["\""] = "\"",
|
||||
["'"] = "'"
|
||||
}
|
||||
|
||||
return str:gsub("\\(.)", escape_sequences)
|
||||
end
|
||||
|
||||
---@param s string
|
||||
local function parse(s)
|
||||
local function parse_string(input)
|
||||
local str
|
||||
local first = input:sub(1, 1)
|
||||
local terminator
|
||||
if first == "'" or first == '"' then
|
||||
terminator = first
|
||||
end
|
||||
|
||||
return str, false
|
||||
end
|
||||
|
||||
local current_line = 0
|
||||
|
||||
-- For each line
|
||||
for line in s:gmatch("[^\r\n]+") do
|
||||
-- Count up
|
||||
current_line = current_line + 1
|
||||
|
||||
-- Find a possible comment
|
||||
local p = line:find(";")
|
||||
|
||||
-- If it exists and has not been escaped (\;)
|
||||
if p and line:sub(p - 1, 1) ~= "\\" then
|
||||
line = line:sub(1, p - 1) -- Trim!
|
||||
end
|
||||
|
||||
-- Trim whitespace, we don't need it.
|
||||
line = line:gsub('%s+', '')
|
||||
|
||||
-- If by now, the line is fully empty, then ignore.
|
||||
-- (Usually this means that the line was only whitespace or a comment)
|
||||
if #line == 0 then
|
||||
goto continue
|
||||
end
|
||||
|
||||
-- If line starts with [, then
|
||||
if line:sub(1, 1) == "[" then
|
||||
-- Check if it ends with ] (if not, fail)
|
||||
if line:sub(#line) ~= "]" then
|
||||
return false, current_line .. ": Malformed label! Misplaced/missing ']'"
|
||||
end
|
||||
|
||||
-- Then assume it's a label, and extract the name
|
||||
local label = line:sub(2, #line - 1)
|
||||
|
||||
-- We "unescape" all the stuff (ex. \n to newline)
|
||||
print(unescape(label))
|
||||
|
||||
goto continue
|
||||
end
|
||||
|
||||
-- Otherwise, line MUST be a statement!
|
||||
-- Matches for (value name = my stuff)
|
||||
local proto_key, proto_value = line:match("^(.-)%s*=%s*(.-)$")
|
||||
if not proto_key then
|
||||
return nil, current_line .. ": Malformed assign!"
|
||||
end
|
||||
|
||||
::continue::
|
||||
end
|
||||
end
|
||||
|
||||
local data, err = parse [[
|
||||
[label]
|
||||
packman = sjsj
|
||||
]]
|
||||
|
||||
print(err)
|
|
@ -1,4 +0,0 @@
|
|||
here are some libraries i came up with, for some reason.
|
||||
|
||||
yeah i just didnt see anything quite like it on the lua
|
||||
landscape and i had to do it myself 🔥
|
|
@ -1,107 +0,0 @@
|
|||
local function unescape(str)
|
||||
local escape_sequences = {
|
||||
["n"] = "\n",
|
||||
["t"] = "\t",
|
||||
["r"] = "\r",
|
||||
["b"] = "\b",
|
||||
["f"] = "\f",
|
||||
["v"] = "\v",
|
||||
["\\"] = "\\",
|
||||
["\""] = "\"",
|
||||
["'"] = "'"
|
||||
}
|
||||
|
||||
return str:gsub("\\(.)", escape_sequences)
|
||||
end
|
||||
|
||||
---@param s string
|
||||
local function parse(s)
|
||||
local out = {}
|
||||
local section = out
|
||||
local current_line = 0
|
||||
|
||||
-- For each line
|
||||
for line in s:gmatch("[^\r\n]+") do
|
||||
-- Count up
|
||||
current_line = current_line + 1
|
||||
|
||||
-- Find a possible comment
|
||||
local p = line:find(";")
|
||||
|
||||
-- If it exists and has not been escaped (\;)
|
||||
if p and line:sub(p - 1, 1) ~= "\\" then
|
||||
line = line:sub(1, p - 1) -- Trim!
|
||||
end
|
||||
|
||||
-- Trim whitespace, we don't need it.
|
||||
line = line:gsub('%s+', '')
|
||||
|
||||
-- If by now, the line is fully empty, then ignore.
|
||||
-- (Usually this means that the line was only whitespace or a comment)
|
||||
if #line == 0 then
|
||||
goto continue
|
||||
end
|
||||
|
||||
-- If line starts with [, then
|
||||
if line:sub(1, 1) == "[" then
|
||||
-- Check if it ends with ] (if not, fail)
|
||||
if line:sub(#line) ~= "]" then
|
||||
return false, current_line .. ": Malformed label! Misplaced/missing ']'"
|
||||
end
|
||||
|
||||
-- Then assume it's a label, and extract the name
|
||||
local label = line:sub(2, #line - 1)
|
||||
|
||||
out[label] = out[label] or {}
|
||||
section = out[label]
|
||||
|
||||
goto continue
|
||||
end
|
||||
|
||||
-- Otherwise, line MUST be a statement!
|
||||
-- Matches for (value name = my stuff)
|
||||
local proto_key, proto_value = line:match("^(.-)%s*=%s*(.-)$")
|
||||
if not proto_key then
|
||||
return nil, current_line .. ": Malformed assign!"
|
||||
end
|
||||
|
||||
-- Get start of value, to check whether we have to parse a "string" or not
|
||||
local start = proto_value:sub(1, 1)
|
||||
|
||||
if proto_value == "none" then -- Handle none
|
||||
section[proto_key] = nil
|
||||
elseif start == "'" or start == '"' then -- Handle strings
|
||||
local last = proto_value:find(start, 2)
|
||||
|
||||
while last and proto_value:sub(last - 1, last - 1) == "\\" do
|
||||
last = proto_value:find(start, last + 1)
|
||||
end
|
||||
|
||||
if not last then
|
||||
return nil, current_line .. ": Unterminated string!"
|
||||
end
|
||||
|
||||
section[proto_key] = unescape(proto_value:sub(2, last - 1))
|
||||
elseif proto_value == "true" then -- Handle true
|
||||
section[proto_key] = true
|
||||
elseif proto_value == "false" then -- Handle false
|
||||
section[proto_key] = false
|
||||
else -- Handle
|
||||
section[proto_key] =
|
||||
tonumber(proto_value) or -- Numbers and
|
||||
unescape(proto_value) -- Regular values!
|
||||
end
|
||||
|
||||
::continue::
|
||||
end
|
||||
|
||||
return out
|
||||
end
|
||||
|
||||
-- TODO!
|
||||
local function encode()
|
||||
end
|
||||
|
||||
return {
|
||||
parse = parse
|
||||
}
|
|
@ -1,97 +0,0 @@
|
|||
-- io operations for prompt utilities
|
||||
local write = io.write
|
||||
local read = io.read
|
||||
|
||||
-- handle luvit's different io behavior
|
||||
if package.loaded.luvibundle then
|
||||
read = function()
|
||||
local o
|
||||
repeat
|
||||
o = io.read()
|
||||
until o
|
||||
return o
|
||||
end
|
||||
end
|
||||
|
||||
-- basic prompt for text input
|
||||
local prompt = function(question, default)
|
||||
local r
|
||||
repeat
|
||||
write(question)
|
||||
if default then
|
||||
write(" (", default, ")")
|
||||
end
|
||||
write(" > ")
|
||||
r = read()
|
||||
if r == "" then
|
||||
r = default
|
||||
end
|
||||
until r ~= nil
|
||||
return r
|
||||
end
|
||||
|
||||
-- yes/no prompt returning boolean
|
||||
local prompt_binary = function(question, default)
|
||||
local p = " [y/n]"
|
||||
|
||||
if default == "y" then
|
||||
p = " [Y/n]"
|
||||
elseif default == "n" then
|
||||
p = " [y/N]"
|
||||
else
|
||||
default = nil
|
||||
end
|
||||
|
||||
local r
|
||||
repeat
|
||||
write(question, p)
|
||||
write(" > ")
|
||||
r = read()
|
||||
if r == "" then
|
||||
r = default and "y"
|
||||
end
|
||||
until r == "y" or r == "n"
|
||||
|
||||
return r == "y"
|
||||
end
|
||||
|
||||
-- number prompt with optional min/max bounds
|
||||
local prompt_number = function(question, default, min, max)
|
||||
local num
|
||||
while true do
|
||||
write(question)
|
||||
|
||||
if default then
|
||||
write(" (", default, ")")
|
||||
end
|
||||
|
||||
if max and min then
|
||||
write(" [", min, "..", max, "]")
|
||||
elseif max then
|
||||
write(" [..", max, "]")
|
||||
elseif min then
|
||||
write(" [", min, "..]")
|
||||
end
|
||||
|
||||
write(" > ")
|
||||
|
||||
local r = read()
|
||||
if r == "" then
|
||||
r = default
|
||||
end
|
||||
|
||||
num = tonumber(r)
|
||||
if num and
|
||||
((not min) or (num >= min)) and
|
||||
((not max) or (num <= max)) then
|
||||
break
|
||||
end
|
||||
end
|
||||
return num
|
||||
end
|
||||
|
||||
return {
|
||||
prompt = prompt,
|
||||
binary = prompt_binary,
|
||||
number = prompt_number
|
||||
}
|
1
logo.svg
1
logo.svg
|
@ -1 +0,0 @@
|
|||
<svg viewBox="0 0 158.8 39.7" xmlns="http://www.w3.org/2000/svg"><path d="M107.8 0 99 13.3 98.4.6l-8 1.8L88.7 37l10.2 1.3-.9-14.4 14.2 13.6L116 28l-11-9 10-14zm21 1.8 1.2 35.3 9.1 1.3-.5-10.5L141 19l4-6.4 2.9-.4 2.6 3.8v22l8.3-.3-1.4-29.4-4.3-6.2h-8.3L139.4 9l-2 5-.3-12.3zm-87.5.4-9.6 2.9 3 7.6 7.7-2.7 5.3 1.1.7 5.3-4 2-7-1.5-3.8 4.4-2.5 7-.3 6 6.2 2.8 7-.3 4-2.3 1.5 4L59 37l-3.3-5.6 3.7-5.9V16l-2.1-9.8-5.9-3zm35.7.3-11.7.6-3.7 4.8 1.5 30.6 8-.2-.6-12h9l6.4-4.1.8-8.6-1.4-7.2zM0 3.1l1.2 35.3 9.1 1.3-.6-10.5 2.5-8.9 3.9-6.4 3-.4 2.6 3.8v22.1l8.3-.3-1.5-29.4-4.2-6.2H16l-5.4 6.9-2 5-.3-12.3zm125.8.2-7.1.7-2 32.3 8.1 1.7zm-56 6.4 9.8 4.4-8 4zM38.6 24.9h6.9l-6.6 6Z" stroke-width="5.3" stroke-linecap="square" paint-order="markers stroke fill" stroke="#fff"/></svg>
|
Before Width: | Height: | Size: 767 B |
30
main.lua
30
main.lua
|
@ -1,15 +1,37 @@
|
|||
local weblit = require('weblit')
|
||||
local config = require('config')
|
||||
local database = require('database')
|
||||
local radon = require("./radon")
|
||||
local fs = require("fs")
|
||||
|
||||
weblit.app
|
||||
.bind({ host = "0.0.0.0", port = 1337 })
|
||||
.bind({ host = "127.0.0.1", port = 1337 })
|
||||
|
||||
-- Configure weblit server
|
||||
.use(weblit.logger)
|
||||
.use(weblit.autoHeaders)
|
||||
|
||||
.use(weblit.static("static"))
|
||||
.route(
|
||||
{ path = "/static/:path:" },
|
||||
weblit.static("static")
|
||||
)
|
||||
|
||||
.route({ path = "/p/:post" }, function()
|
||||
|
||||
end)
|
||||
|
||||
.route({ path = "/@:blog" }, function(req, res)
|
||||
local data = dofile("template/blog.lua")
|
||||
res.body = radon.compile(data)
|
||||
res.code = 200
|
||||
res.headers["Content-Type"] = "text/html"
|
||||
end)
|
||||
|
||||
.route({ path = "/@:blog/:post" }, function(req, res)
|
||||
|
||||
end)
|
||||
|
||||
.route({ path = "/" }, function(req, res)
|
||||
|
||||
end)
|
||||
|
||||
-- Start the server
|
||||
.start()
|
||||
|
|
15
package.lua
15
package.lua
|
@ -1,19 +1,16 @@
|
|||
return {
|
||||
name = "napkin",
|
||||
name = "radon",
|
||||
version = "0.0.1",
|
||||
description = "an experimental blogging platform",
|
||||
tags = { "lua", "lit", "luvit", "fediverse" },
|
||||
description = "A simple description of my little package.",
|
||||
tags = { "lua", "lit", "luvit" },
|
||||
license = "MIT",
|
||||
author = { name = "Nelson Lopez", email = "darltrash@icloud.com" },
|
||||
homepage = "n.cyrneko.eu/napkin",
|
||||
author = { name = "n", email = "darltrash@icloud.com" },
|
||||
homepage = "https://github.com/radontest",
|
||||
dependencies = {
|
||||
"creationix/weblit",
|
||||
"TohruMKDM/cli"
|
||||
|
||||
"creationix/weblit"
|
||||
},
|
||||
files = {
|
||||
"**.lua",
|
||||
"static/",
|
||||
"!test*"
|
||||
}
|
||||
}
|
||||
|
|
158
radon.lua
Normal file
158
radon.lua
Normal file
|
@ -0,0 +1,158 @@
|
|||
local tag = function(name)
|
||||
return function(content)
|
||||
local data = ""
|
||||
local t = type(content)
|
||||
|
||||
if t == "function" then
|
||||
local o = {}
|
||||
local e = content(o)
|
||||
if e then table.insert(o, e) end
|
||||
content = o
|
||||
t = "table"
|
||||
end
|
||||
|
||||
if t == "table" then
|
||||
for key, value in pairs(content) do
|
||||
if type(key) == "string" then
|
||||
data = data .. ' ' .. key .. '="' .. value .. '"'
|
||||
end
|
||||
end
|
||||
|
||||
content = table.concat(content, "")
|
||||
end
|
||||
|
||||
if t == "nil" then
|
||||
content = ""
|
||||
end
|
||||
|
||||
return ("<%s%s>%s</%s>"):format(name, data, content, name)
|
||||
end
|
||||
end
|
||||
|
||||
local radon = {
|
||||
p = tag("p"),
|
||||
h1 = tag("h1"),
|
||||
h2 = tag("h2"),
|
||||
h3 = tag("h3"),
|
||||
h4 = tag("h4"),
|
||||
h5 = tag("h5"),
|
||||
h6 = tag("h6"),
|
||||
div = tag("div"),
|
||||
span = tag("span"),
|
||||
a = tag("a"),
|
||||
img = tag("img"),
|
||||
br = tag("br"),
|
||||
hr = tag("hr"),
|
||||
table = tag("table"),
|
||||
tr = tag("tr"),
|
||||
td = tag("td"),
|
||||
th = tag("th"),
|
||||
thead = tag("thead"),
|
||||
tbody = tag("tbody"),
|
||||
tfoot = tag("tfoot"),
|
||||
ul = tag("ul"),
|
||||
ol = tag("ol"),
|
||||
li = tag("li"),
|
||||
dl = tag("dl"),
|
||||
dt = tag("dt"),
|
||||
dd = tag("dd"),
|
||||
form = tag("form"),
|
||||
input = tag("input"),
|
||||
textarea = tag("textarea"),
|
||||
select = tag("select"),
|
||||
option = tag("option"),
|
||||
button = tag("button"),
|
||||
label = tag("label"),
|
||||
script = tag("script"),
|
||||
link = tag("link"),
|
||||
meta = tag("meta"),
|
||||
title = tag("title"),
|
||||
head = tag("head"),
|
||||
body = tag("body"),
|
||||
html = tag("html"),
|
||||
nav = tag("nav"),
|
||||
header = tag("header"),
|
||||
footer = tag("footer"),
|
||||
main = tag("main"),
|
||||
figure = tag("figure"),
|
||||
figcaption = tag("figcaption"),
|
||||
blockquote = tag("blockquote"),
|
||||
pre = tag("pre"),
|
||||
code = tag("code"),
|
||||
em = tag("em"),
|
||||
strong = tag("strong"),
|
||||
i = tag("i"),
|
||||
b = tag("b"),
|
||||
small = tag("small"),
|
||||
sub = tag("sub"),
|
||||
sup = tag("sup"),
|
||||
time = tag("time"),
|
||||
progress = tag("progress"),
|
||||
audio = tag("audio"),
|
||||
video = tag("video"),
|
||||
source = tag("source"),
|
||||
iframe = tag("iframe"),
|
||||
canvas = tag("canvas"),
|
||||
svg = tag("svg"),
|
||||
section = tag("section"),
|
||||
article = tag("article"),
|
||||
aside = tag("aside"),
|
||||
details = tag("details"),
|
||||
summary = tag("summary"),
|
||||
dialog = tag("dialog"),
|
||||
menu = tag("menu"),
|
||||
menuitem = tag("menuitem"),
|
||||
meter = tag("meter"),
|
||||
abbr = tag("abbr"),
|
||||
address = tag("address"),
|
||||
bdi = tag("bdi"),
|
||||
bdo = tag("bdo"),
|
||||
cite = tag("cite"),
|
||||
datalist = tag("datalist"),
|
||||
dfn = tag("dfn"),
|
||||
ins = tag("ins"),
|
||||
del = tag("del"),
|
||||
kbd = tag("kbd"),
|
||||
mark = tag("mark"),
|
||||
noscript = tag("noscript"),
|
||||
output = tag("output"),
|
||||
picture = tag("picture"),
|
||||
ruby = tag("ruby"),
|
||||
rt = tag("rt"),
|
||||
rp = tag("rp"),
|
||||
samp = tag("samp"),
|
||||
template = tag("template"),
|
||||
var = tag("var"),
|
||||
wbr = tag("wbr"),
|
||||
fieldset = tag("fieldset"),
|
||||
legend = tag("legend"),
|
||||
optgroup = tag("optgroup"),
|
||||
track = tag("track"),
|
||||
colgroup = tag("colgroup"),
|
||||
col = tag("col"),
|
||||
caption = tag("caption"),
|
||||
object = tag("object"),
|
||||
param = tag("param"),
|
||||
map = tag("map"),
|
||||
area = tag("area"),
|
||||
style = tag("style"),
|
||||
}
|
||||
|
||||
radon.compile = function(func, ...)
|
||||
local p = _G.p
|
||||
_G.p = nil
|
||||
setmetatable(_G, { __index = radon })
|
||||
local r = func(...)
|
||||
setmetatable(_G, nil)
|
||||
_G.p = p
|
||||
|
||||
return r
|
||||
end
|
||||
|
||||
setmetatable(radon, {
|
||||
__call = function(_, func, ...)
|
||||
return radon.compile(func, ...)
|
||||
end
|
||||
})
|
||||
|
||||
return radon
|
|
@ -1,5 +1,50 @@
|
|||
/*
|
||||
please do not modify this, it's a crucial part of Napkin's UI normalization
|
||||
body {
|
||||
max-width: 600px;
|
||||
margin: auto;
|
||||
padding: 20px;
|
||||
|
||||
if you want to customize your Napkin instance, please modify instance.css!
|
||||
*/
|
||||
font-family:
|
||||
system-ui,
|
||||
-apple-system,
|
||||
BlinkMacSystemFont,
|
||||
"Segoe UI",
|
||||
"Roboto",
|
||||
"Oxygen",
|
||||
"Inter",
|
||||
"Ubuntu",
|
||||
"Cantarell",
|
||||
"Fira Sans",
|
||||
"Droid Sans",
|
||||
"Helvetica Neue",
|
||||
sans-serif;
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.post {
|
||||
border: 1px solid #ccc;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.post-list {
|
||||
}
|
||||
|
||||
.biography {
|
||||
border: 1px solid #ccc;
|
||||
padding: 10px;
|
||||
text-align: justify;
|
||||
}
|
||||
|
||||
section {
|
||||
margin-bottom: 40px;
|
||||
gap: 15px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
<h1>penia!</h1>
|
81
template/blog.lua
Normal file
81
template/blog.lua
Normal file
|
@ -0,0 +1,81 @@
|
|||
---@diagnostic disable:undefined-global
|
||||
|
||||
return function()
|
||||
local user = {
|
||||
handle = "@supercool",
|
||||
name = "Super Cool",
|
||||
|
||||
biography =
|
||||
"i am a super cool person, hello, welcome to my thing, uhhh, that thing where you write a thing and then you're like \"okay now what\" and write some more things because you can't think of anything else to write, and then you add some funny emojis like 🌙 🎂 🍋 okay anyway the point is i'm doing that thing where i just keep writing more and more stuff because i'm supposed to make it longer and more boring and not actually say anything",
|
||||
|
||||
posts = {
|
||||
{
|
||||
title = "Do not ever consume pizza.",
|
||||
timestamp = os.time()
|
||||
},
|
||||
|
||||
{
|
||||
title = "Why I don't believe in aliens.",
|
||||
timestamp = os.time() - 90000
|
||||
},
|
||||
|
||||
{
|
||||
title = "Pizza SUCKS, here's why.",
|
||||
timestamp = os.time() - 90000 * 4,
|
||||
pinned = true
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
return html {
|
||||
head {
|
||||
meta { charset = "UTF-8" },
|
||||
meta { ["http-equiv"] = "X-UA-Compatible", content = "IE=edge" },
|
||||
meta { name = "viewport", content = "width=device-width, initial-scale=1.0" },
|
||||
title "Document",
|
||||
|
||||
link { href = "static/baseline.css", rel = "stylesheet" },
|
||||
},
|
||||
|
||||
body {
|
||||
h1(user.name),
|
||||
p({
|
||||
a { href = "/", "instance.com/" },
|
||||
" ",
|
||||
a { href = "/" .. user.handle, user.handle .. "/" }
|
||||
}),
|
||||
|
||||
br(),
|
||||
|
||||
section({ class = "biography", user.biography }),
|
||||
|
||||
section(function(o)
|
||||
o.class = "post-list pinned-list"
|
||||
table.insert(o, h2("Pinned:"))
|
||||
|
||||
for _, post in ipairs(user.posts) do
|
||||
if post.pinned then
|
||||
table.insert(o, div {
|
||||
class = "post",
|
||||
h2("📌 " .. post.title),
|
||||
p(os.date("%Y-%m-%d %H:%M:%S", post.timestamp))
|
||||
})
|
||||
end
|
||||
end
|
||||
end),
|
||||
|
||||
section(function(o)
|
||||
o.class = "post-list"
|
||||
table.insert(o, h2("Posts:"))
|
||||
|
||||
for _, post in ipairs(user.posts) do
|
||||
table.insert(o, div {
|
||||
class = "post",
|
||||
h2(post.title),
|
||||
p(os.date("%Y-%m-%d %H:%M:%S", post.timestamp))
|
||||
})
|
||||
end
|
||||
end)
|
||||
}
|
||||
}
|
||||
end
|
Loading…
Add table
Add a link
Reference in a new issue