From 71ecfa7cd8f1a5c3e5fbe26ea605e611c4b8a309 Mon Sep 17 00:00:00 2001 From: robin Date: Wed, 27 May 2026 18:53:55 +0000 Subject: [PATCH] rss: init --- justfile | 2 +- lua/init.luau | 4 +++- lua/layouts/base.luau | 10 +++++++--- lua/pages/rss.luau | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ runtime/h.luau | 1 + src/gen.zig | 31 +++++++++++++++++++++---------- 6 file(s) changed, 85 insertion(s)(+), 15 deletion(s)(-) diff --git a/justfile b/justfile --- a/justfile +++ b/justfile @@ -6,7 +6,7 @@ run: zig build run |& hl -P --config=hl.toml -gen: +gen $MAIVI_LOG="warn": zig build run -- build |& hl -P --config=hl.toml build: diff --git a/lua/init.luau b/lua/init.luau --- a/lua/init.luau +++ b/lua/init.luau @@ -24,10 +24,12 @@ title = "badges", desc = "collection of cool badges", }) - site.router:add("/start.robinwobin.dev", h.pages.start, { + site.router:add("/start", h.pages.start, { title = "start 🌱", desc = "cool start page:)", }) + + site.router:add("/rss.xml", h.pages.rss, {}) -- data ======================================================================= diff --git a/lua/layouts/base.luau b/lua/layouts/base.luau --- a/lua/layouts/base.luau +++ b/lua/layouts/base.luau @@ -22,7 +22,7 @@ media = "(prefers-color-scheme: light)", }), h.title(title), - h.meta({ name = "author", content = "Robin" }), + h.meta({ name = "author", content = "robinwobin" }), h.meta({ name = "description", content = description }), h.link({ rel = "canonical", href = this.base_url }), h.link({ rel = "expect", href = "#below-the-fold", blocking = "render" }), @@ -32,16 +32,20 @@ h.meta({ name = "twitter:image", content = "/me.webp" }), h.meta({ name = "twitter:card", content = description }), h.meta({ property = "og:title", content = title }), + h.meta({ property = "og:url", content = this.base_url }), + h.meta({ property = "og:site_name", content = "robinwobin.dev" }), h.meta({ property = "og:description", content = description }), h.meta({ property = "og:type", content = "website" }), - h.meta({ property = "og:image", content = "/me.webp" }), + h.meta({ property = "og:image:url", content = "/me.webp" }), + + h.meta({ name = "theme-color", content = "#cbe3b3" }), h.link({ rel = "alternate", type = "application/rss+xml", href = "/rss.xml" }), h.link({ rel = "alternate", type = "application/atom+xml", href = "/atom.xml" }), h.link({ type = "text/plain", rel = "author", href = "/humans.txt" }), h.link({ rel = "icon", href = "/favicon.svg" }), - h.link({ rel = "mask-icon", href = "/favicon.svg", color = "#b2c98f" }), + h.link({ rel = "mask-icon", href = "/favicon.svg", color = "#cbe3b3" }), h.style([[ @layer reset, theme, base, layout, components, partials, page, media; ]]), -- reset diff --git a/lua/pages/rss.luau b/lua/pages/rss.luau new file mode 100644 --- /dev/null +++ b/lua/pages/rss.luau @@ -0,0 +1,52 @@ +function item(url, props) + return h.elem("item")({ + h.elem("title")(props.title), + h.elem("pubDate")(os.date("%a, %d %b %Y %H:%M:%S %z", props.date)), + h.elem("author")([[robinwobin]]), + h.elem("link")(props.url), + h.elem("guid")(props.url), + h.elem("description")({ + ["xml:base"] = props.url, + props.desc, + }), + }) +end + +return function(_) + local items = h.Iter.tbl(h.data.posts):map(item):to() + + return h({ + [[]], + h.elem("rss")({ + ["xmlns:atom"] = "http://www.w3.org/2005/Atom", + version = "2.0", + h.elem("channel")({ + h.elem("title")([[robinwobin's blog]]), + h.elem("description")([[my little garden of stupid stuff]]), + h.elem("link")([[https://robinwobin.dev]]), + h.elem("generator")([[maivi]]), + this.rev ~= "dev" and h({ + h.elem("websiteHash")({ + this.rev, + }), + h.elem("websiteHashUrl")("https://codeberg.org/comfysage/site/commit/" .. this.rev), + }) or nil, + h.elem("atom:link", { empty = true })({ + href = string.format("%s/rss.xml", this.base_url), + rel = "self", + type = "application/rss+xml", + }), + h.elem("footerLinks")({ + h.elem("row")({ + h.elem("left")({ + h.elem("text")([[source]]), + h.elem("url")([[https://codeberg.org/comfysage/site]]), + h.elem("icon")([[mdi:source-fork]]), + }), + }), + }), + h(items), + }), + }), + }) +end diff --git a/runtime/h.luau b/runtime/h.luau --- a/runtime/h.luau +++ b/runtime/h.luau @@ -20,6 +20,7 @@ end local base = {} +base.elem = core.elem base.document = core.elem("!DOCTYPE html", { close = false }) base.meta = core.elem("meta", { empty = true }) base.link = core.elem("link", { empty = true }) diff --git a/src/gen.zig b/src/gen.zig --- a/src/gen.zig +++ b/src/gen.zig @@ -8,10 +8,17 @@ const zlua = @import("zlua"); const lua = @import("lua.zig"); +var arena: std.heap.ArenaAllocator = undefined; +var allocator: std.mem.Allocator = undefined; + pub fn generate() !void { fern.info().ctx("gen.generate") .msg("generating") .str("path", maivi.config.buildroot).log(); + + arena = .init(maivi.allocator); + allocator = arena.allocator(); + Io.Dir.createDirAbsolute(maivi.io, maivi.config.buildroot, .default_dir) catch |err| switch (err) { error.PathAlreadyExists => {}, else => return err, @@ -39,30 +46,33 @@ _ = lua.lua.getGlobal("site"); if (lua.lua.isNil(-1)) { return error.Unexpected; } - for (keys.items) |item| writepath(lua.lua, buildroot, item) catch |err| { + for (keys.items) |item| genpath(lua.lua, buildroot, item) catch |err| { fern.err().ctx("gen.generate") - .msg("could not write path") + .msg("could not generate path") .str("path", item) .err(err) .log(); continue; }; + + arena.deinit(); } -fn writepath(l: *zlua.Lua, dir: Io.Dir, path: []const u8) !void { +fn genpath(l: *zlua.Lua, dir: Io.Dir, path: []const u8) !void { _ = l.getField(-1, "get"); if (!l.isFunction(-1)) { return error.Unexpected; } _ = l.getGlobal("site"); _ = l.pushString(path); - lua.pcall(l, .{ .args = 2, .results = 1 }) catch return error.ReadFailed; + try lua.pcall(l, .{ .args = 2, .results = 1 }); if (l.isNil(-1)) { fern.err().ctx("gen.writepath").msg("got nil").log(); return error.FileNotFound; } if (!l.isString(-1)) { - fern.err().ctx("gen.writepath").msg("got non string type").log(); + const tname = l.typeNameIndex(-1); + fern.err().ctx("gen.writepath").msg("got non string type").str("T", tname).log(); return error.ReadFailed; } const str = blk: { @@ -72,15 +82,16 @@ }; l.pop(1); const outpath_base = if (path.len == 1) "." else path[1..]; - const outpath = try std.mem.join(maivi.allocator, "/", &.{ outpath_base, "index.html" }); - defer maivi.allocator.free(outpath); + var it = std.mem.splitBackwardsScalar(u8, path, '/'); + const outpath = if (!std.mem.containsAtLeast(u8, it.first(), 1, ".")) + try std.mem.join(allocator, "/", &.{ outpath_base, "index.html" }) + else + outpath_base; fern.info().ctx("gen.writepath") .str("path", path) .str("outpath", outpath).log(); - const outpath_parts = std.mem.cutScalarLast(u8, outpath, '/') orelse return error.Unexpected; - const parent = outpath_parts[0]; - try mkdir(dir, parent); + if (std.mem.cutScalarLast(u8, outpath, '/')) |outpath_parts| try mkdir(dir, outpath_parts[0]); const file = try dir.createFile(maivi.io, outpath, .{ .lock = .exclusive }); try file.writeStreamingAll(maivi.io, str); } -- tangled.sh