diff --git a/.emmyrc.json b/.emmyrc.json index 8371d7e..d897994 100644 --- a/.emmyrc.json +++ b/.emmyrc.json @@ -1,7 +1,6 @@ { "$schema": "https://raw.githubusercontent.com/EmmyLuaLs/emmylua-analyzer-rust/refs/heads/main/crates/emmylua_code_analysis/resources/schema.json", "runtime": { - "version": "LuaJIT", "requireLikeFunction": [ "maivi.mod" ] @@ -9,33 +8,6 @@ "workspace": { "workspaceRoots": [ "./lua" - ], - "library": [ - "./src/runtime" - ], - "checkThirdParty": "Disable" - }, - "diagnostics": { - "groupFileStatus": { - "strict": "Any", - "strong": "Any" - }, - "groupSeverity": { - "strong": "Warning", - "strict": "Warning" - }, - "unusedLocalExclude": [ - "_*" ] - }, - "format": { - "externalTool": { - "program": "stylua", - "args": [ - "-", - "--stdin-filepath", - "${file}" - ] - } } } diff --git a/.luarc.json b/.luarc.json new file mode 100644 index 0000000..4e16790 --- /dev/null +++ b/.luarc.json @@ -0,0 +1,35 @@ +{ + "$schema": "https://raw.githubusercontent.com/sumneko/vscode-lua/master/setting/schema.json", + "runtime": { + "version": "LuaJIT" + }, + "workspace": { + "library": [ + "./src/runtime" + ], + "checkThirdParty": "Disable" + }, + "diagnostics": { + "groupFileStatus": { + "strict": "Any", + "strong": "Any" + }, + "groupSeverity": { + "strong": "Warning", + "strict": "Warning" + }, + "unusedLocalExclude": [ + "_*" + ] + }, + "format": { + "externalTool": { + "program": "stylua", + "args": [ + "-", + "--stdin-filepath", + "${file}" + ] + } + } +} diff --git a/lua/components/meta/desc.lua b/lua/components/meta/desc.lua index 57c0ac9..ba81f42 100644 --- a/lua/components/meta/desc.lua +++ b/lua/components/meta/desc.lua @@ -1,6 +1,6 @@ return maivi.component(function(_, children) local desc = children[1] - return desc and h.div({ + return desc and h.span({ class = "description", h.em(desc), }) or nil diff --git a/lua/components/taglist.lua b/lua/components/taglist.lua deleted file mode 100644 index e76664d..0000000 --- a/lua/components/taglist.lua +++ /dev/null @@ -1,19 +0,0 @@ -return maivi.component(function(_, children) - return h.ul({ - role = "list", - class = "tag-list", - - h(Iter.lst(children) - :map(function(_, tag) - return h.li({ - h.a({ - class = "tag", - internal = true, - href = string.format("/tag/%s", tag), - string.format("#%s", tag), - }), - }) - end) - :to()), - }) -end) diff --git a/lua/components/tags/list.lua b/lua/components/tags/list.lua new file mode 100644 index 0000000..11d0671 --- /dev/null +++ b/lua/components/tags/list.lua @@ -0,0 +1,14 @@ +local tag = maivi.mod("components/tags/tag") + +return maivi.component(function(_, children) + return h.ul({ + role = "list", + class = "tags", + + h(Iter.lst(children) + :map(function(_, tagname) + return h.li(tag(tagname)) + end) + :to()), + }) +end) diff --git a/lua/components/tags/tag.lua b/lua/components/tags/tag.lua new file mode 100644 index 0000000..92a40ce --- /dev/null +++ b/lua/components/tags/tag.lua @@ -0,0 +1,10 @@ +return maivi.component(function(_, children) + local tagname = children[1] + + return h.a({ + class = "tag", + internal = true, + href = string.format("/tag/%s", tagname), + string.format("#%s", tagname), + }) +end) diff --git a/lua/init.lua b/lua/init.lua index c26de0a..cc3e60f 100644 --- a/lua/init.lua +++ b/lua/init.lua @@ -66,6 +66,8 @@ return function() end end + -- transform from `{ path: data }` to `{ url: data }`, where data has + -- `data.path` and `data.url` maivi.data.posts = Iter.tbl(this.data):mapattrs(selectmd("^data/blog/")):to() maivi.data.notes = Iter.tbl(this.data):mapattrs(selectmd("^data/notes/")):to() maivi.data.tools = Iter.tbl(this.data):mapattrs(selectmd("^data/tools/")):to() diff --git a/lua/layouts/base.lua b/lua/layouts/base.lua index adbe354..37f63b8 100644 --- a/lua/layouts/base.lua +++ b/lua/layouts/base.lua @@ -100,6 +100,7 @@ return maivi.layout(function(slots) -- styles ========================================================== h.style( + ---@language css [[ @layer reset, theme, base, layout, components, partials, page, media; ]] ), -- reset diff --git a/lua/pages/404.lua b/lua/pages/404.lua index 491dd69..43a6420 100644 --- a/lua/pages/404.lua +++ b/lua/pages/404.lua @@ -13,7 +13,7 @@ return base(function(props, _) left = h({ h.div({ class = "sidebar-search", maivi.mod("partials/search") }), maivi.mod("partials/nav"), - maivi.mod("partials/bloglist"), + maivi.mod("partials/posts/section"), }), } end) diff --git a/lua/pages/blog.lua b/lua/pages/blog.lua index b766db1..d9f5804 100644 --- a/lua/pages/blog.lua +++ b/lua/pages/blog.lua @@ -1,25 +1,15 @@ local base = maivi.mod("layouts/base") -local postpageitem = maivi.mod("components/postpageitem") +local postslist = maivi.mod("partials/posts/list") return base(function(props, _) - local posts = Iter.tbl(maivi.data.posts) - :map(function(_, page) - return postpageitem(page) - end) - :to() - return { main = h.section({ id = "main", class = "page", props.content, - h.ul({ - class = "blogpost-list", - - h(posts), - }), + postslist(), }), } end) diff --git a/lua/pages/index.lua b/lua/pages/index.lua index 4da9f50..1858e66 100644 --- a/lua/pages/index.lua +++ b/lua/pages/index.lua @@ -23,7 +23,7 @@ return base(function(props, _) maivi.mod("partials/search"), }), maivi.mod("partials/nav"), - maivi.mod("partials/bloglist"), + maivi.mod("partials/posts/section"), }), right = h({ h.div({ diff --git a/lua/pages/post.lua b/lua/pages/post.lua index 52eb4eb..eeaef5c 100644 --- a/lua/pages/post.lua +++ b/lua/pages/post.lua @@ -1,5 +1,5 @@ local base = maivi.mod("layouts/base") -local taglist = maivi.mod("components/taglist") +local taglist = maivi.mod("components/tags/list") return base(function(props, _) return { @@ -32,7 +32,7 @@ return base(function(props, _) string.format("%d min", props.reading_time), }) or nil, }), - additionalHeader = taglist(props.tags), + additionalPageheader = taglist(props.tags), content = h.pre({ props.content }), } end) diff --git a/lua/partials/breadcrumbs.lua b/lua/partials/breadcrumbs.lua index 2ce0e65..520c81b 100644 --- a/lua/partials/breadcrumbs.lua +++ b/lua/partials/breadcrumbs.lua @@ -5,7 +5,7 @@ return function() h.code({ h.pre({ class = "subtle", - this.ctx().path, + this.ctx().url, }), }), }) diff --git a/lua/partials/footer.lua b/lua/partials/footer.lua index 930bf24..bb6ed1e 100644 --- a/lua/partials/footer.lua +++ b/lua/partials/footer.lua @@ -89,7 +89,9 @@ return function() }), h.div({ id = "device-details" }), - h.script([[ + h.script( + ---@language javascript + [[ function setDeviceDetails(e) { const { width, height } = e.currentTarget.screen; document.querySelector("#device-details").innerText = @@ -97,7 +99,8 @@ function setDeviceDetails(e) { } window.addEventListener("load", setDeviceDetails); window.addEventListener("resize", setDeviceDetails); -]]), +]] + ), }), }) end diff --git a/lua/components/postpageitem.lua b/lua/partials/posts/item.lua similarity index 78% rename from lua/components/postpageitem.lua rename to lua/partials/posts/item.lua index d27dc67..38e77ad 100644 --- a/lua/components/postpageitem.lua +++ b/lua/partials/posts/item.lua @@ -1,20 +1,17 @@ local metadate = maivi.mod("components/meta/date") local metadesc = maivi.mod("components/meta/desc") -local taglist = maivi.mod("components/taglist") +local taglist = maivi.mod("components/tags/list") return function(props) - return h.li({ - class = "blogpost", + return h.div({ h.a({ class = "blogpost-info", href = props.url, metadate(props.date), h.h2({ class = "title", props.title }), - metadesc(props.desc), }), - taglist(props.tags), }) end diff --git a/lua/partials/posts/list.lua b/lua/partials/posts/list.lua new file mode 100644 index 0000000..20fcf99 --- /dev/null +++ b/lua/partials/posts/list.lua @@ -0,0 +1,22 @@ +local item = maivi.mod("partials/posts/item") + +return function() + return h.ul({ + role = "list", + class = "posts", + + h(Iter.tbl(maivi.data.posts) + :map(function(k, v) + return v + end) + :sort(function(a, b) + return a.date > b.date + end) + :map(function(_, page) + return h.li({ + item(page), + }) + end) + :to()), + }) +end diff --git a/lua/partials/bloglist.lua b/lua/partials/posts/section.lua similarity index 68% rename from lua/partials/bloglist.lua rename to lua/partials/posts/section.lua index 962de97..cc23cd7 100644 --- a/lua/partials/bloglist.lua +++ b/lua/partials/posts/section.lua @@ -1,13 +1,19 @@ -local metadate = require("components/meta/date") +local metadate = maivi.mod("components/meta/date") return maivi.component(function() local posts = Iter.tbl(maivi.data.posts) + :map(function(k, v) + return { k = k, v = v } + end) + :sort(function(a, b) + return a.v.date > b.v.date + end) :take(3) - :map(function(url, page) + :map(function(_, kv) + local url, page = kv.k, kv.v return h.li({ - class = "blogitem", h.a({ href = url, h.span({ class = "title", page.title }) }), - h.div({ + h.span({ class = "subtitle", metadate(page.date), }), @@ -16,12 +22,11 @@ return maivi.component(function() :to() return h.div({ - class = "section blog-section", + class = "section posts", h.h3([[blog]]), h.ul({ role = "list", - class = "bloglist", h(posts), diff --git a/src/lua.zig b/src/lua.zig index 4c294b9..79f4d7c 100644 --- a/src/lua.zig +++ b/src/lua.zig @@ -14,6 +14,9 @@ const helpers = maivi.helpers; pub var lua: *Lua = undefined; +// var arena: std.heap.ArenaAllocator = undefined; +// var chunk_allocator: std.mem.Allocator = undefined; +// var packages: std.StringHashMap([]const u8) = undefined; const embed = @import("embed.zig"); const lua_display = @import("lua_display.zig"); @@ -21,6 +24,10 @@ const lua_display = @import("lua_display.zig"); pub fn init() !void { fern.trace().ctx("lua.init").log(); + // arena = .init(maivi.allocator); + // errdefer arena.deinit(); + // chunk_allocator = arena.allocator(); + // packages = .init(chunk_allocator); lua = try lua_init(); } @@ -52,6 +59,7 @@ pub fn lua_init() !*Lua { // global fn ============================================================== l.register("print", zlua.wrap(lua_print)); + // l.register("require", zlua.wrap(lua_require)); l.register("read", zlua.wrap(lua_read)); // maivi ====================================================================== @@ -104,6 +112,7 @@ pub fn lua_init() !*Lua { pub fn deinit() void { lua.deinit(); + // arena.deinit(); } /// * Pops from Stack: `0` @@ -216,3 +225,58 @@ fn lua_read(l: *Lua) !i32 { return 1; } +// fn loadchunk(l: *Lua, modname: []const u8) !void { +// const buffer = packages.get(modname) orelse return error.PackageNotFound; +// +// const modnameZ = try maivi.allocator.dupeZ(u8, modname); +// defer maivi.allocator.free(modnameZ); +// +// fern.debug().ctx("lua.loadchunk") +// .msg("loading buffer") +// .str("modname", modname).log(); +// try l.loadBuffer(buffer, modnameZ); +// try pcall(l, .{ .args = 0, .results = 1 }); +// } +// +// fn loadfile(modname: []const u8) !void { +// const dir = try std.Io.Dir.openDirAbsolute(maivi.io, maivi.config.luaroot, .{}); +// const contents = helpers.read_file(chunk_allocator, dir, modname) catch |err| switch (err) { +// error.IsDirectory => blk: { +// const new_path = try std.mem.concat(maivi.allocator, u8, &.{ modname, "/init.lua" }); +// defer maivi.allocator.free(new_path); +// break :blk try helpers.read_file(chunk_allocator, dir, new_path); +// }, +// error.FileNotFound => blk: { +// const new_path = try std.mem.concat(maivi.allocator, u8, &.{ modname, ".lua" }); +// defer maivi.allocator.free(new_path); +// break :blk try helpers.read_file(chunk_allocator, dir, new_path); +// }, +// else => return err, +// }; +// errdefer chunk_allocator.free(contents); +// +// fern.debug().ctx("lua.loadfile") +// .msg("saving contents") +// .str("modname", modname).log(); +// const modnameZ = try maivi.allocator.dupeZ(u8, modname); +// defer maivi.allocator.free(modnameZ); +// +// if (packages.contains(modname)) return error.Clobber; +// try packages.put(modname, contents); +// } +// +// pub fn lua_require(l: *Lua) !i32 { +// const modname = try l.toString(-1); +// l.pop(1); +// fern.debug().ctx("lua.require").str("modname", modname).log(); +// +// loadchunk(l, modname) catch |err| switch (err) { +// error.PackageNotFound => { +// try loadfile(modname); +// try loadchunk(l, modname); +// }, +// else => return err, +// }; +// +// return 1; +// } diff --git a/src/runtime/_meta.lua b/src/runtime/_meta.lua index 6c822ff..c789382 100644 --- a/src/runtime/_meta.lua +++ b/src/runtime/_meta.lua @@ -16,5 +16,22 @@ _G.h = _G.h or ... ---@class maivi.router ---@field add fun(self, path: string, f: Component, props: table) +_G.maivi = ... + ---@type maivi.router _G.maivi.router = ... + +_G.maivi.display = ... + +---@param v any +---@return string +function _G.maivi.display.elem(v) end + +---@param v any +---@return string +function _G.maivi.display.attr(v) end + +---@param modname string +---@return any +---@nodiscard +function _G.maivi.mod(modname) end diff --git a/src/runtime/iter.lua b/src/runtime/iter.lua index 1a6c9ef..f304011 100644 --- a/src/runtime/iter.lua +++ b/src/runtime/iter.lua @@ -61,8 +61,8 @@ function Iter:each(f) end end ----@generic K, V, R ----@param f fun(k: K, v: V): R +---@generic K, V +---@param f fun(k: K, v: V): K, V function Iter:mapattrs(f) local new = {} @@ -76,6 +76,20 @@ function Iter:mapattrs(f) return Iter.new(self.n, new, self.i) end +---@generic V +---@param f fun(a: V, b: V): boolean returns true when the first item is less +--- than the second +function Iter:sort(f) + local new = {} + for k, v in self.n, self.t, self.i do + new[k] = v + end + + table.sort(new, f) + + return Iter.new(self.n, new, self.i) +end + ---@param n integer function Iter:skip(n) self.i = (self.i or 1) + n diff --git a/src/runtime/maivi.lua b/src/runtime/maivi.lua index cd903be..9b54c2e 100644 --- a/src/runtime/maivi.lua +++ b/src/runtime/maivi.lua @@ -1,4 +1,3 @@ ----@type any -- selene: allow(unscoped_variables, global_usage) _G.maivi = _G.maivi or {} --[[@as table]] @@ -192,7 +191,6 @@ end -- modules ==================================================================== ----@type fun(modname: string): any maivi.mod = setmetatable({ graph = {}, }, { diff --git a/styles/components/page.css b/styles/components/page.css index 94a1ff1..17b4856 100644 --- a/styles/components/page.css +++ b/styles/components/page.css @@ -4,7 +4,7 @@ } /* unset list item padding for tag list */ - ul.tag-list li { + ul[role=list].tags li { display: inline; padding: 0; } diff --git a/styles/partials/blog.css b/styles/partials/blog.css index 0e934d5..067bd02 100644 --- a/styles/partials/blog.css +++ b/styles/partials/blog.css @@ -1,5 +1,5 @@ @layer partials { - ul.blogpost-list { + ul[role=list].posts { display: flex; flex-direction: column; @@ -31,11 +31,10 @@ color: var(--skye); } - li.blogpost { + >li div { display: grid; grid-template: auto auto / 100%; - list-style: none; padding: 1rem 0; gap: 1rem; @@ -82,9 +81,15 @@ } } - ul.tag-list li { + /* force tags to look like text */ + ul[role=list].tags li { + /* to replace padding */ + margin-inline: 0 .5rem; + a { color: var(--subtext1); + background-color: transparent; + padding: 0; } } } diff --git a/styles/partials/bloglist.css b/styles/partials/bloglist.css index 5b9bc8c..667b20a 100644 --- a/styles/partials/bloglist.css +++ b/styles/partials/bloglist.css @@ -1,5 +1,5 @@ @layer partials { - .bloglist { + .section.posts ul[role=list] { display: flex; flex-direction: column; gap: 1rem; @@ -9,10 +9,6 @@ grid-template-rows: 1fr auto; gap: .2rem; - h2 { - font-size: 1rem; - } - .subtitle { color: var(--subtext0); font-size: .8rem;