From 0a0b1fa8c3dfccc86f8a2ebb7472266e93dd4941 Mon Sep 17 00:00:00 2001 From: vx-clutch Date: Sun, 4 Jan 2026 16:27:39 -0500 Subject: [PATCH] large changes --- .gitignore | 1 + examples/extentions/Fes.toml | 5 + examples/extentions/README.md | 33 +++++++ examples/extentions/www/index.lua | 12 +++ lib/fes.lua | 28 +++++- lib/site.lua | 27 ++++++ lib/std.lua | 155 +++++++++++++----------------- lib/symbol.lua | 74 +++++++++++++- lib/util.lua | 29 +++++- main.go | 7 +- modules/new/new.go | 9 +- modules/ui/ui.go | 2 +- 12 files changed, 273 insertions(+), 109 deletions(-) create mode 100644 examples/extentions/Fes.toml create mode 100644 examples/extentions/README.md create mode 100644 examples/extentions/www/index.lua create mode 100644 lib/site.lua diff --git a/.gitignore b/.gitignore index 6718873..8bdbb2c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ fes *.tar.gz +/stuff/ diff --git a/examples/extentions/Fes.toml b/examples/extentions/Fes.toml new file mode 100644 index 0000000..7f3be7f --- /dev/null +++ b/examples/extentions/Fes.toml @@ -0,0 +1,5 @@ +[app] + +name = "extentions" +version = "0.0.1" +authors = ["vx-clutch"] \ No newline at end of file diff --git a/examples/extentions/README.md b/examples/extentions/README.md new file mode 100644 index 0000000..47477b6 --- /dev/null +++ b/examples/extentions/README.md @@ -0,0 +1,33 @@ +# extentions + +``` +fes new extentions +``` + +> **Know what you are doing?** Delete this file. Have fun! + +## Project Structure + +Inside your Fes project, you'll see the following directories and files: + +``` +. +├── Fes.toml +├── README.md +└── www + └── index.lua +``` + +Fes looks for `.lua` files in the `www/` directory. Each file is exposed as a route based on its file name. + +## Commands + +All commands are run from the root of the project, from a terminal: + +| Command | Action | +| :------------------------ | :----------------------------------------------- | +| `fes run .` | Runs the project at `.` | + +## What to learn more? + +Check out [Fes's docs](https://docs.vxserver.dev/static/fes.html). \ No newline at end of file diff --git a/examples/extentions/www/index.lua b/examples/extentions/www/index.lua new file mode 100644 index 0000000..a24c17b --- /dev/null +++ b/examples/extentions/www/index.lua @@ -0,0 +1,12 @@ +local fes = require("fes") +local site = fes.fes() + +site.copyright = fes.util.copyright("https://fsd.vxserver.dev/", "fSD") + +site:extend("myext", { + shout = function(self, str) return self:g(str:upper()) end +}) + +site.myext:shout("hello world") + +return site diff --git a/lib/fes.lua b/lib/fes.lua index 2933122..3b37930 100644 --- a/lib/fes.lua +++ b/lib/fes.lua @@ -1,4 +1,5 @@ local std = require("lib.std") +local symbol = require("lib.symbol") local M = {} M.__index = M @@ -318,11 +319,30 @@ em, i { font-style: italic; } return setmetatable(self, M) end -function M:custom(str) +function M:g(str) table.insert(self.parts, str) return self end +function M:extend(name, tbl) + if type(name) ~= "string" then + error("First argument to extend must be a string (namespace name)") + end + if type(tbl) ~= "table" then + error("Second argument to extend must be a table of functions") + end + self[name] = {} + for k, v in pairs(tbl) do + if type(v) ~= "function" then + error("Extension values must be functions, got " .. type(v) .. " for key " .. k) + end + self[name][k] = function(...) + return v(self, ...) + end + end + return self +end + for name, func in pairs(std) do if type(func) == "function" then M[name] = function(self, ...) @@ -340,9 +360,11 @@ function M:build() header = header:gsub( "{{FAVICON}}", favicon_html - or [[]] + or + [[]] ) - local footer = self.footer:gsub("{{COPYRIGHT}}", self.copyright or "© The Copyright Holder") + local footer = self.footer:gsub("{{COPYRIGHT}}", + self.copyright or symbol.legal.copyright .. "The Copyright Holder") return header .. table.concat(self.parts, "\n") .. footer end diff --git a/lib/site.lua b/lib/site.lua new file mode 100644 index 0000000..0be7eeb --- /dev/null +++ b/lib/site.lua @@ -0,0 +1,27 @@ +local M = {} + +function M.name() + local fes_mod = package.loaded.fes + if fes_mod and fes_mod.config and fes_mod.config.site and fes_mod.config.site.name then + return fes_mod.config.site.name + end + return "" +end + +function M.version() + local fes_mod = package.loaded.fes + if fes_mod and fes_mod.config and fes_mod.config.site and fes_mod.config.site.version then + return fes_mod.config.site.version + end + return "" +end + +function M.authors() + local fes_mod = package.loaded.fes + if fes_mod and fes_mod.config and fes_mod.config.site and fes_mod.config.site.authors then + return fes_mod.config.site.authors + end + return {} +end + +return M diff --git a/lib/std.lua b/lib/std.lua index 6611138..f7fb8a1 100644 --- a/lib/std.lua +++ b/lib/std.lua @@ -1,155 +1,157 @@ local M = {} -function M.fes_version() - local fes_mod = package.loaded.fes - if fes_mod and fes_mod.config and fes_mod.config.fes and fes_mod.config.fes.version then - return fes_mod.config.fes.version - end - return "" -end +function M.element(tag, attrs, content) + local out = { "<", tag } -function M.site_version() - local fes_mod = package.loaded.fes - if fes_mod and fes_mod.config and fes_mod.config.site and fes_mod.config.site.version then - return fes_mod.config.site.version + if attrs then + for k, v in pairs(attrs) do + if v ~= false and v ~= nil then + if v == true then + out[#out + 1] = " " .. k + else + out[#out + 1] = " " .. k .. "=\"" .. tostring(v) .. "\"" + end + end + end end - return "" + + if content == nil then + out[#out + 1] = " />" + return table.concat(out) + end + + out[#out + 1] = ">" + out[#out + 1] = tostring(content) + out[#out + 1] = "" + + return table.concat(out) end function M.a(link, str) link = link or "https://example.com" str = str or link - return '' .. str .. "" + return M.element("a", { href = link }, str) end function M.ha(link, str) link = link or "https://example.com" str = str or link - return '" + return M.element("a", { href = link, class = "hidden" }, str) end function M.external(link, str) - return '' .. str .. "" + return M.element("a", { href = link, target = "_blank" }, str) end function M.note(str) - return '
' .. str .. "
" + return M.element("div", { class = "note" }, str) end function M.muted(str) - return '
' .. str .. "
" + return M.element("div", { class = "muted" }, str) end function M.callout(str) - return '
' .. str .. "
" + return M.element("div", { class = "callout" }, str) end function M.h1(str) - return "

" .. (str or "") .. "

" + return M.element("h1", nil, str or "") end function M.h2(str) - return "

" .. (str or "") .. "

" + return M.element("h2", nil, str or "") end function M.h3(str) - return "

" .. (str or "") .. "

" + return M.element("h3", nil, str or "") end + function M.h4(str) - return "

" .. (str or "") .. "

" + return M.element("h4", nil, str or "") end function M.h5(str) - return "
" .. (str or "") .. "
" + return M.element("h5", nil, str or "") end function M.h6(str) - return "
" .. (str or "") .. "
" + return M.element("h6", nil, str or "") end function M.p(str) - return "

" .. (str or "") .. "

" + return M.element("p", nil, str or "") end function M.pre(str) - return "
" .. (str or "") .. "
" + return M.element("pre", nil, str or "") end function M.code(str) - return "
" .. (str or "") .. "
" + return M.element("pre", nil, M.element("code", nil, str or "")) end function M.ul(items) items = items or {} - local html = "" - return html + return M.element("ul", nil, table.concat(out)) end function M.ol(items) items = items or {} - local html = "
    " + local out = {} for _, item in ipairs(items) do - html = html .. "
  1. " .. tostring(item) .. "
  2. " + out[#out + 1] = M.element("li", nil, item) end - html = html .. "
" - return html + return M.element("ol", nil, table.concat(out)) end function M.tl(items) items = items or {} - local html = '" - return html + return M.element("ul", { class = "tl" }, table.concat(out)) end function M.blockquote(str) - return "
" .. (str or "") .. "
" + return M.element("blockquote", nil, str or "") end function M.hr() - return "
" + return M.element("hr") end function M.img(src, alt) - src = src or "" - alt = alt or "" - return '' .. alt .. '' + return M.element("img", { src = src or "", alt = alt or "" }) end function M.strong(str) - return "" .. (str or "") .. "" + return M.element("strong", nil, str or "") end function M.em(str) - return "" .. (str or "") .. "" + return M.element("em", nil, str or "") end function M.br() - return "
" + return M.element("br") end function M.div(content, class) - content = content or "" - class = class or "" - local class_attr = class ~= "" and (' class="' .. class .. '"') or "" - return "" .. content .. "" + return M.element("div", class and { class = class } or nil, content or "") end function M.span(content, class) - content = content or "" - class = class or "" - local class_attr = class ~= "" and (' class="' .. class .. '"') or "" - return "" .. content .. "" + return M.element("span", class and { class = class } or nil, content or "") end --- HTML escaping utility function M.escape(str) str = tostring(str or "") str = str:gsub("&", "&") @@ -160,55 +162,28 @@ function M.escape(str) return str end --- Get site name from config -function M.site_name() - local fes_mod = package.loaded.fes - if fes_mod and fes_mod.config and fes_mod.config.site and fes_mod.config.site.name then - return fes_mod.config.site.name - end - return "" -end - --- Get site title from config -function M.site_title() - local fes_mod = package.loaded.fes - if fes_mod and fes_mod.config and fes_mod.config.site and fes_mod.config.site.title then - return fes_mod.config.site.title - end - return "" -end - --- Get site authors from config -function M.site_authors() - local fes_mod = package.loaded.fes - if fes_mod and fes_mod.config and fes_mod.config.site and fes_mod.config.site.authors then - return fes_mod.config.site.authors - end - return {} -end - function M.highlight(str) - return '' .. (str or "") .. "" + return M.element("span", { class = "highlight" }, str or "") end function M.banner(str) - return '" + return M.element("div", { class = "banner" }, str or "") end function M.center(str) - return '
' .. (str or "") .. "
" + return M.element("div", { class = "center" }, str or "") end function M.nav(link, str) link = link or "example.com" str = str or link - return '' .. str .. "" + return M.element("a", { href = link, class = "nav" }, str) end function M.rl(r, l) - r = r or "" - l = l or "" - return string.format('%s%s', r, l) + return + M.element("span", { class = "left" }, r or "") .. + M.element("span", { class = "right" }, l or "") end return M diff --git a/lib/symbol.lua b/lib/symbol.lua index b3fac61..978e08a 100644 --- a/lib/symbol.lua +++ b/lib/symbol.lua @@ -1,7 +1,75 @@ local M = {} -M.copyright = "©" -M.registered_trademark = "®" -M.trademark = "™" +local function get(s) + return "&" .. (s or "") .. ";" +end + +M.legal = { + copyright = get("copy"), + registered_trademark = get("reg"), + trademark = get("trade"), +} + +M.currency = { + euro = get("euro"), + pound = get("pound"), + yen = get("yen"), + cent = get("cent"), + dollar = "$", +} + +M.math = { + plus_minus = get("plusmn"), + multiply = get("times"), + divide = get("divide"), + not_equal = get("ne"), + less_equal = get("le"), + greater_equal = get("ge"), + infinity = get("infin"), + approx = get("asymp"), +} + +M.arrows = { + left = get("larr"), + right = get("rarr"), + up = get("uarr"), + down = get("darr"), + left_right = get("harr"), +} + +M.punctuation = { + left_double_quote = get("ldquo"), + right_double_quote = get("rdquo"), + left_single_quote = get("lsquo"), + right_single_quote = get("rsquo"), + ellipsis = get("hellip"), + em_dash = get("mdash"), + en_dash = get("ndash"), +} + +M.whitespace = { + non_breaking = get("nbsp"), + thin = get("thinsp"), +} + +M.symbols = { + degree = get("deg"), + micro = get("micro"), + section = get("sect"), + paragraph = get("para"), + check = get("check"), + cross = get("cross"), + bullet = get("bull"), + middle_dot = get("middot"), + broken_bar = get("brvbar"), +} + +M.html = { + less_than = get("lt"), + greater_than = get("gt"), + ampersand = get("amp"), + double_quote = get("quot"), + single_quote = get("apos"), +} return M diff --git a/lib/util.lua b/lib/util.lua index 2fb2733..4bfde52 100644 --- a/lib/util.lua +++ b/lib/util.lua @@ -3,12 +3,33 @@ local symbol = require("lib.symbol") local M = {} -function M.cc(tbl) - return table.concat(tbl) +function M.cc(tbl, sep) + return table.concat(tbl, sep or "") end -function M.copyright(link, holder) - return symbol.copyright .. " " .. std.external(link, holder) +function M.year(y) + return y or os.date("%Y") +end + +function M.copyright(link, holder, year) + return symbol.legal.copyright .. " " .. M.year(year) .. " " .. std.external(link, holder) +end + +function M.license(name) + return symbol.legal.registered .. " " .. name +end + +function M.ls(dir) + local p = io.popen('ls -A -1 -- ' .. string.format('%q', dir)) + if not p then + return nil + end + local t = {} + for line in p:lines() do + t[#t + 1] = line + end + p:close() + return t end return M diff --git a/main.go b/main.go index 7c080ea..30941bf 100644 --- a/main.go +++ b/main.go @@ -65,10 +65,6 @@ func main() { color.NoColor = true } - if *config.Port == 3000 { - ui.WARNING("Using default port, this may lead to conflicts with other services") - } - args := flag.Args() if len(args) < 1 { flag.Usage() @@ -98,6 +94,9 @@ func main() { os.Exit(1) } case "run": + if *config.Port == 3000 { + ui.WARNING("Using default port, this may lead to conflicts with other services") + } ui.Log("Fes is starting") ui.Log("Fes version=%s, commit=%s, just started", version.VERSION, version.GetCommit()) diff --git a/modules/new/new.go b/modules/new/new.go index 564a487..caf0dae 100644 --- a/modules/new/new.go +++ b/modules/new/new.go @@ -118,11 +118,12 @@ Check out [Fes's docs](https://docs.vxserver.dev/static/fes.html).`, "$$", "`"), ui.Hint("you can run this with `fes run %s`", dir) - fmt.Println("Created new Fes project at", func() string { - if res, err := filepath.Abs(dir); err == nil { - return res + fmt.Println("Created new Fes project at", func () string { + if cwd, err := os.Getwd(); err != nil { + return dir + } else { + return cwd } - return dir }()) return nil diff --git a/modules/ui/ui.go b/modules/ui/ui.go index 4a35df8..1c39fa8 100644 --- a/modules/ui/ui.go +++ b/modules/ui/ui.go @@ -72,7 +72,7 @@ func Hint(msg string, args ...any) { g = (hex >> 8) & 0xFF b = hex & 0xFF return - }(hintColor)).Printf("%s * hint: %s\n", formatTimestamp(), formatted) + }(hintColor)).Printf("hint: %s\n", formatted) } // Path logging: prints route and status