Compare commits

..

1 Commits

Author SHA1 Message Date
vx-clutch
fd4922a980 chore: update todo 2026-03-19 23:58:03 -04:00
5 changed files with 513 additions and 248 deletions

View File

@@ -4,111 +4,7 @@ local symbol = require("lib.symbol")
local M = {} local M = {}
M.__index = M M.__index = M
local default_html_header = [[ function M.fes(header, footer)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
{{FAVICON}}
<title>{{TITLE}}</title>
<style>
:root { --bg:#f5f5f5; --text:#111827; --muted:#6b7280; --link:#1a0dab; --accent:#68a6ff; --highlight:#004d99; --note-bg:#fff; --panel-bg:#fff; --border:rgba(0,0,0,.1); --table-head:#f3f4f6; --code-color:#004d99; --blockquote-border:#1a73e8; --banner-bg:#fff; --footer-bg:#fff; --shadow:rgba(0,0,0,.08); }
@media (prefers-color-scheme: dark) { :root { --bg:#0f1113; --text:#e6eef3; --muted:#9aa6b1; --link:#68a6ff; --accent:#68a6ff; --highlight:#cde7ff; --note-bg:#1a1c20; --panel-bg:#1a1c20; --border:rgba(255,255,255,.06); --table-head:#1a1c20; --code-color:#cde7ff; --blockquote-border:#68a6ff; --banner-bg:#1a1c20; --footer-bg:#1a1c20; --shadow:rgba(0,0,0,.4); } }
html, body { min-height:100%; margin:0; padding:0; background:var(--bg); color:var(--text); font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif; line-height:1.5; -webkit-font-smoothing:antialiased; -moz-osx-font-smoothing:grayscale; }
body { padding:36px; }
.container { max-width:830px; margin:0 auto; }
.container>*:not(.banner) { margin:28px 0; }
h1,h2,h3,h4,h5,h6 { font-weight:600; margin:0 0 12px 0; }
h1 { font-size:40px; margin-bottom:20px; font-weight:700; }
h2 { font-size:32px; margin:26px 0 14px; }
h3 { font-size:26px; margin:22px 0 12px; }
h4 { font-size:20px; margin:18px 0 10px; }
h5 { font-size:16px; margin:16px 0 8px; }
h6 { font-size:14px; margin:14px 0 6px; color:var(--muted); }
p { margin:14px 0; }
a { color:var(--link); text-decoration:none; transition: color .15s ease, text-decoration-color .15s ease; }
.hidden { color:var(--text); text-decoration:none; }
a:hover { text-decoration:underline; }
summary { cursor:pointer; }
details { background:var(--panel-bg); border:1px solid var(--border); border-radius:4px; padding:14px 16px; margin:16px 0; }
details summary { list-style:none; font-weight:600; color:var(--text); display:flex; align-items:center; }
details summary::-webkit-details-marker { display:none; }
details summary::before { content:"▸"; margin-right:8px; transition:transform .15s ease; color:var(--accent); }
details[open] summary::before { transform:rotate(90deg); }
summary::after { content:"Expand"; margin-left:auto; font-size:13px; color:var(--muted); }
details[open] summary::after { content:"Collapse"; }
details>*:not(summary) { margin-top:12px; }
.note, pre, code { background:var(--note-bg); border:1px solid var(--border); }
.note { padding:20px; border-radius:4px; margin:28px 0; color:var(--text); }
.note strong { color:var(--text); }
.muted { color:var(--muted); }
.lead { font-size:15px; margin-top:8px; }
.callout { display:block; margin:12px 0; }
.small { font-size:13px; color:var(--muted); margin-top:6px; }
.highlight { font-weight:700; color:var(--highlight); }
ul, ol { margin:14px 0; padding-left:26px; }
.tl { display:grid; grid-template-columns:repeat(auto-fill,200px); gap:15px; list-style-type:none; padding:0; margin:0; justify-content:start; }
ul.tl li { padding:10px; width:fit-content; }
li { margin:6px 0; }
code { padding:3px 7px; border-radius:3px; font-family:"SF Mono",Monaco,"Cascadia Code","Roboto Mono",Consolas,"Courier New",monospace; font-size:.9em; color:var(--code-color); }
pre { padding:20px; border-radius:4px; margin:14px 0; overflow-x:auto; font-family:"SF Mono",Monaco,"Cascadia Code","Roboto Mono",Consolas,"Courier New",monospace; font-size:14px; line-height:1.6; }
pre code { background:none; border:none; padding:0; font-size:inherit; }
blockquote { border-left:3px solid var(--blockquote-border); padding-left:18px; margin:14px 0; color:var(--text); font-style:italic; }
hr { border:0; border-top:1px solid rgba(0,0,0,.08); margin:26px 0; }
@media (prefers-color-scheme: dark) { hr { border-top-color:rgba(255,255,255,.1); } }
img { max-width:100%; height:auto; border-radius:4px; margin:14px 0; }
table { width:100%; border-collapse:collapse; margin:14px 0; }
th, td { padding:12px 16px; text-align:left; border-bottom:1px solid var(--border); }
th { background:var(--table-head); font-weight:600; color:var(--text); }
tr:hover { background:rgba(0,0,0,0.02); }
@media (prefers-color-scheme: dark) { tr:hover { background:rgba(255,255,255,0.02); } }
.divider { margin:26px 0; height:1px; background:rgba(0,0,0,.08); }
@media (prefers-color-scheme: dark) { .divider { background:rgba(255,255,255,.1); } }
.section { margin-top:36px; }
.links { margin:12px 0; }
.links a { display:inline-block; margin:0 14px 6px 0; color:var(--link); }
strong, b { font-weight:600; color:var(--text); }
em, i { font-style:italic; }
.center { display:flex; justify-content:center; align-items:center; }
.banner { width:100%; box-sizing:border-box; text-align:center; background:var(--banner-bg); padding:20px; border:1px solid var(--border); border-bottom-right-radius:8px; border-bottom-left-radius:8px; color:var(--text); margin:-36px 0 28px 0; box-shadow:0 0.2em 0.6em var(--shadow); }
.nav { margin-left:auto; margin-right:auto; }
.nav a { color:var(--highlight); }
.footer { background:var(--footer-bg); padding:20px 0; border-top:1px solid rgba(0,0,0,.08); font-size:14px; color:var(--muted); display:flex; justify-content:center; align-items:center; gap:24px; margin-top:28px !important; margin-bottom:0; }
.left { text-align:left; float:left; }
.right { text-align:right; float:right; }
</style>
</head>
<body>
<div class="container">
]]
local default_html_footer = [[
<footer class="footer">
<a href="https://git.vxserver.dev/fSD/fes" target="_blank">Fes Powered</a>
<a href="https://www.lua.org/" target="_blank">Lua Powered</a>
<a href="https://git.vxserver.dev/fSD/fes/src/branch/main/LICENSE" target="_blank">ISC Licensed</a>
<p>{{COPYRIGHT}}</p>
</footer>
</div>
</body>
</html>
]]
local default_gemini_header = [[
]]
local default_gemini_footer = [[
=> https://git.vxserver.dev/fSD/fes Fes Powered
=> https://www.lua.org Lua Powered
=> https://git.vxserver.dev/fSD/fes/src/branch/main/LICENSE ISC Licensed
{{COPYRIGHT}}
]]
function M.fes(proto, header, footer)
proto = proto or "http"
std.proto = proto
local config = {} local config = {}
local site_config = {} local site_config = {}
local fes_mod = package.loaded.fes local fes_mod = package.loaded.fes
@@ -119,22 +15,8 @@ function M.fes(proto, header, footer)
end end
end end
if proto == "http" and site_config.favicon then if site_config.favicon then
site_config.favicon = site_config.favicon = '<link rel="icon" type="image/x-icon" href="' .. site_config.favicon .. '">'
'<link rel="icon" type="image/x-icon" href="'
.. site_config.favicon
.. '">'
end
local default_header
local default_footer
if proto == "http" then
default_header = default_html_header
default_footer = default_html_footer
elseif proto == "gemini" then
default_header = default_gemini_header
default_footer = default_gemini_footer
end end
local self = { local self = {
@@ -142,39 +24,349 @@ function M.fes(proto, header, footer)
title = site_config.title, title = site_config.title,
copyright = site_config.copyright, copyright = site_config.copyright,
favicon = site_config.favicon, favicon = site_config.favicon,
header = header or default_header, header = header or [[
footer = footer or default_footer, <!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
{{FAVICON}}
<title>{{TITLE}}</title>
<style>
:root {
--bg: #f5f5f5;
--text: #111827;
--muted: #6b7280;
--link: #1a0dab;
--accent: #68a6ff;
--highlight: #004d99;
--note-bg: #ffffff;
--panel-bg: #ffffff;
--border: rgba(0,0,0,.1);
--table-head: #f3f4f6;
--code-color: #004d99;
--blockquote-border: #1a73e8;
--banner-bg: #ffffff;
--footer-bg: #ffffff;
--shadow: rgba(0,0,0,.08);
}
@media (prefers-color-scheme: dark) {
:root {
--bg: #0f1113;
--text: #e6eef3;
--muted: #9aa6b1;
--link: #68a6ff;
--accent: #68a6ff;
--highlight: #cde7ff;
--note-bg: #1a1c20;
--panel-bg: #1a1c20;
--border: rgba(255,255,255,.06);
--table-head: #1a1c20;
--code-color: #cde7ff;
--blockquote-border: #68a6ff;
--banner-bg: #1a1c20;
--footer-bg: #1a1c20;
--shadow: rgba(0,0,0,.4);
}
}
html, body {
min-height: 100%;
margin: 0;
padding: 0;
background: var(--bg);
color: var(--text);
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
line-height: 1.5;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
body { padding: 36px; }
.container { max-width: 830px; margin: 0 auto; }
.container > *:not(.banner) { margin: 28px 0; }
h1, h2, h3, h4, h5, h6 { font-weight: 600; margin: 0 0 12px 0; }
h1 { font-size: 40px; margin-bottom: 20px; font-weight: 700; }
h2 { font-size: 32px; margin: 26px 0 14px; }
h3 { font-size: 26px; margin: 22px 0 12px; }
h4 { font-size: 20px; margin: 18px 0 10px; }
h5 { font-size: 16px; margin: 16px 0 8px; }
h6 { font-size: 14px; margin: 14px 0 6px; color: var(--muted); }
p { margin: 14px 0; }
a { color: var(--link); text-decoration: none; transition: color .15s ease, text-decoration-color .15s ease; }
.hidden { color: var(--text); text-decoration: none; }
a:hover { text-decoration: underline; }
summary { cursor: pointer; }
details {
background: var(--panel-bg);
border: 1px solid var(--border);
border-radius: 4px;
padding: 14px 16px;
margin: 16px 0;
}
details summary {
list-style: none;
font-weight: 600;
color: var(--text);
display: flex;
align-items: center;
}
details summary::-webkit-details-marker { display: none; }
details summary::before {
content: "▸";
margin-right: 8px;
transition: transform .15s ease;
color: var(--accent);
}
details[open] summary::before { transform: rotate(90deg); }
summary::after { content: "Expand"; margin-left: auto; font-size: 13px; color: var(--muted); }
details[open] summary::after { content: "Collapse"; }
details > *:not(summary) { margin-top: 12px; }
.note, pre, code {
background: var(--note-bg);
border: 1px solid var(--border);
}
.note {
padding: 20px;
border-radius: 4px;
background: var(--note-bg);
border: 1px solid var(--border);
margin: 28px 0;
color: var(--text);
}
.note strong { color: var(--text); }
.muted { color: var(--muted); }
.lead { font-size: 15px; margin-top: 8px; }
.callout { display: block; margin: 12px 0; }
.small { font-size: 13px; color: var(--muted); margin-top: 6px; }
.highlight { font-weight: 700; color: var(--highlight); }
ul, ol { margin: 14px 0; padding-left: 26px; }
.tl {
display: grid;
grid-template-columns: repeat(auto-fill, 200px);
gap: 15px;
list-style-type: none;
padding: 0;
margin: 0;
justify-content: start;
}
ul.tl li { padding: 10px; width: fit-content; }
li { margin: 6px 0; }
code {
padding: 3px 7px;
border-radius: 3px;
font-family: "SF Mono", Monaco, "Cascadia Code", "Roboto Mono", Consolas, "Courier New", monospace;
font-size: .9em;
color: var(--code-color);
}
pre {
padding: 20px;
border-radius: 4px;
margin: 14px 0;
overflow-x: auto;
font-family: "SF Mono", Monaco, "Cascadia Code", "Roboto Mono", Consolas, "Courier New", monospace;
font-size: 14px;
line-height: 1.6;
}
pre code { background: none; border: none; padding: 0; font-size: inherit; }
blockquote {
border-left: 3px solid var(--blockquote-border);
padding-left: 18px;
margin: 14px 0;
color: var(--text);
font-style: italic;
}
hr { border: 0; border-top: 1px solid rgba(0,0,0,.08); margin: 26px 0; }
@media (prefers-color-scheme: dark) {
hr { border-top-color: rgba(255,255,255,.1); }
}
img { max-width: 100%; height: auto; border-radius: 4px; margin: 14px 0; }
table { width: 100%; border-collapse: collapse; margin: 14px 0; }
th, td {
padding: 12px 16px;
text-align: left;
border-bottom: 1px solid var(--border);
}
th {
background: var(--table-head);
font-weight: 600;
color: var(--text);
}
tr:hover { background: rgba(0,0,0,0.02); }
@media (prefers-color-scheme: dark) {
tr:hover { background: rgba(255,255,255,0.02); }
}
.divider { margin: 26px 0; height: 1px; background: rgba(0,0,0,.08); }
@media (prefers-color-scheme: dark) {
.divider { background: rgba(255,255,255,.1); }
}
.section { margin-top: 36px; }
.links { margin: 12px 0; }
.links a { display: inline-block; margin: 0 14px 6px 0; color: var(--link); }
strong, b { font-weight: 600; color: var(--text); }
em, i { font-style: italic; }
.center { display: flex; justify-content: center; align-items: center; }
.banner {
width: 100%;
box-sizing: border-box;
text-align: center;
background: var(--banner-bg);
padding: 20px;
border: 1px solid var(--border);
border-bottom-right-radius: 8px;
border-bottom-left-radius: 8px;
color: var(--text);
margin: -36px 0 28px 0;
box-shadow: 0 0.2em 0.6em var(--shadow);
}
.nav { margin-left: auto; margin-right: auto; }
.nav a { color: var(--highlight); }
.footer {
background: var(--footer-bg);
padding: 20px 0;
border-top: 1px solid rgba(0,0,0,.08);
font-size: 14px;
color: var(--muted);
display: flex;
justify-content: center;
align-items: center;
gap: 24px;
margin-top: 28px !important;
margin-bottom: 0;
}
.left { text-align: left; float: left; }
.right { text-align: right; float: right; }
</style>
</head>
<body>
<div class="container">
]],
footer = footer or [[
<footer class="footer">
<a href="https://git.vxserver.dev/fSD/fes" target="_blank">Fes Powered</a>
<a href="https://www.lua.org/" target="_blank">Lua Powered</a>
<a href="https://git.vxserver.dev/fSD/fes/src/branch/master/COPYING" target="_blank">ISC Licensed</a>
<p>{{COPYRIGHT}}</p>
</footer>
</div>
</body>
</html>
]],
parts = {}, parts = {},
proto = proto,
} }
return setmetatable(self, M) return setmetatable(self, M)
end end
function M:raw(str) function M:g(str)
table.insert(self.parts, (str or "") .. "\n") 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 return self
end end
for name, func in pairs(std) do for name, func in pairs(std) do
if type(func) == "function" then if type(func) == "function" then
M[name] = function(self, ...) M[name] = function(self, ...)
table.insert(self.parts, func(...)) local result = func(...)
table.insert(self.parts, result)
return self return self
end end
end end
end end
function M:build() function M:build()
if self.proto == "http" then local header = self.header
local header = self.header:gsub("{{TITLE}}", self.title or "Document") header = header:gsub("{{TITLE}}", self.title or "Document")
local favicon_html = self.favicon or '<link rel="icon" href="data:image/svg+xml,<svg xmlns=%%22http://www.w3.org/2000/svg%%22 viewBox=%%220 0 100 100%%22><text y=%%22.9em%%22 font-size=%%2290%%22>🔥</text></svg>">' local favicon_html = self.favicon and ('<link rel="icon" type="image/x-icon" href="' .. self.favicon .. '">')
header = header:gsub("{{FAVICON}}", favicon_html) header = header:gsub(
local footer = self.footer:gsub("{{COPYRIGHT}}", self.copyright or symbol.legal.copyright .. "The Copyright Holder") "{{FAVICON}}",
favicon_html
or
[[<link rel="icon" href="data:image/svg+xml,<svg xmlns=%%22http://www.w3.org/2000/svg%%22 viewBox=%%220 0 100 100%%22><text y=%%22.9em%%22 font-size=%%2290%%22>🔥</text></svg>">]]
)
local footer = self.footer:gsub("{{COPYRIGHT}}",
self.copyright or symbol.legal.copyright .. "The Copyright Holder")
return header .. table.concat(self.parts, "\n") .. footer return header .. table.concat(self.parts, "\n") .. footer
end end
return table.concat(self.parts, "\n")
end
M.__tostring = function(self) M.__tostring = function(self)
return self:build() return self:build()

View File

@@ -1,130 +1,195 @@
local M = {} local M = {}
M.proto = "http" function M.element(tag, attrs, content)
local out = { "<", tag }
local function isHttp() if attrs then
return M.proto == "http" for k, v in pairs(attrs) do
end if v ~= false and v ~= nil then
if v == true then
local function isGemini() out[#out + 1] = " " .. k
return M.proto == "gemini"
end
local function esc(s)
s = s or ""
s = s:gsub("&", "&amp;")
s = s:gsub("<", "&lt;")
s = s:gsub(">", "&gt;")
return s
end
M.p = function(s)
s = s or ""
if isHttp() then
return "<p>" .. esc(s) .. "</p>"
elseif isGemini() then
return s
end
end
M.h = function(level, s)
level = tonumber(level) or 1
if level < 1 then level = 1 end
if level > 6 then level = 6 end
s = s or ""
if isHttp() then
return "<h" .. level .. ">" .. esc(s) .. "</h" .. level .. ">"
elseif isGemini() then
return string.rep("#", level) .. " " .. s
end
end
M.codeblock = function(s)
s = s or ""
if isHttp() then
return "<pre><code>" .. esc(s) .. "</code></pre>"
elseif isGemini() then
return "```\n" .. s .. "\n```"
end
end
M.inline = function(s)
s = s or ""
if isHttp() then
return "<code>" .. esc(s) .. "</code>"
elseif isGemini() then
return "`" .. s .. "`"
end
end
M.link = function(text, url)
text = text or ""
url = url or ""
if isHttp() then
return "<a href=\"" .. esc(url) .. "\">" .. esc(text) .. "</a>"
elseif isGemini() then
return "=> " .. url .. " " .. text
end
end
M.list = function(items, ordered)
items = items or {}
if isHttp() then
local tag = ordered and "ol" or "ul"
local out = "<" .. tag .. ">"
for _, v in ipairs(items) do
out = out .. "<li>" .. esc(v) .. "</li>"
end
out = out .. "</" .. tag .. ">"
return out
elseif isGemini() then
local out = {}
for i, v in ipairs(items) do
if ordered then
table.insert(out, i .. ". " .. v)
else else
table.insert(out, "* " .. v) out[#out + 1] = " " .. k .. "=\"" .. tostring(v) .. "\""
end end
end end
return table.concat(out, "\n")
end end
end end
M.blockquote = function(s) if content == nil then
s = s or "" out[#out + 1] = " />"
if isHttp() then return table.concat(out)
return "<blockquote>" .. esc(s) .. "</blockquote>"
elseif isGemini() then
return "> " .. s
end
end end
M.rule = function() out[#out + 1] = ">"
if isHttp() then out[#out + 1] = tostring(content)
return "<hr />" out[#out + 1] = "</"
elseif isGemini() then out[#out + 1] = tag
return "---" out[#out + 1] = ">"
end
return table.concat(out)
end end
M.image = function(alt, src) function M.a(link, str)
alt = alt or "" link = link or "https://example.com"
src = src or "" str = str or link
if isHttp() then return M.element("a", { href = link }, str)
return "<img src=\"" .. esc(src) .. "\" alt=\"" .. esc(alt) .. "\" />"
elseif isGemini() then
return "=> " .. src .. " " .. alt
end
end end
M.file = function(text, url) function M.download(link, str, downloadName)
text = text or "" link = link or "."
url = url or "" str = str or link
if isHttp() then return M.element("a", { href = link, download = downloadName }, str)
return "<a href=\"" .. esc(url) .. "\" download>" .. esc(text) .. "</a>"
elseif isGemini() then
return "=> " .. url .. " " .. text
end end
function M.ha(link, str)
link = link or "https://example.com"
str = str or link
return M.element("a", { href = link, class = "hidden" }, str)
end
function M.external(link, str)
return M.element("a", { href = link, target = "_blank" }, str)
end
function M.note(str)
return M.element("div", { class = "note" }, str)
end
function M.muted(str)
return M.element("div", { class = "muted" }, str)
end
function M.callout(str)
return M.element("div", { class = "callout" }, str)
end
function M.h1(str)
return M.element("h1", nil, str or "")
end
function M.h2(str)
return M.element("h2", nil, str or "")
end
function M.h3(str)
return M.element("h3", nil, str or "")
end
function M.h4(str)
return M.element("h4", nil, str or "")
end
function M.h5(str)
return M.element("h5", nil, str or "")
end
function M.h6(str)
return M.element("h6", nil, str or "")
end
function M.p(str)
return M.element("p", nil, str or "")
end
function M.pre(str)
return M.element("pre", nil, str or "")
end
function M.code(str)
return M.element("pre", nil, M.element("code", nil, str or ""))
end
function M.ul(items)
items = items or {}
local out = {}
for _, item in ipairs(items) do
out[#out + 1] = M.element("li", nil, item)
end
return M.element("ul", nil, table.concat(out))
end
function M.ol(items)
items = items or {}
local out = {}
for _, item in ipairs(items) do
out[#out + 1] = M.element("li", nil, item)
end
return M.element("ol", nil, table.concat(out))
end
function M.tl(items)
items = items or {}
local out = {}
for _, item in ipairs(items) do
out[#out + 1] = M.element("li", nil, item)
end
return M.element("ul", { class = "tl" }, table.concat(out))
end
function M.blockquote(str)
return M.element("blockquote", nil, str or "")
end
function M.hr()
return M.element("hr")
end
function M.img(src, alt)
return M.element("img", { src = src or "", alt = alt or "" })
end
function M.strong(str)
return M.element("strong", nil, str or "")
end
function M.em(str)
return M.element("em", nil, str or "")
end
function M.br()
return M.element("br")
end
function M.div(content, class)
return M.element("div", class and { class = class } or nil, content or "")
end
function M.span(content, class)
return M.element("span", class and { class = class } or nil, content or "")
end
function M.escape(str)
str = tostring(str or "")
str = str:gsub("&", "&amp;")
str = str:gsub("<", "&lt;")
str = str:gsub(">", "&gt;")
str = str:gsub('"', "&quot;")
str = str:gsub("'", "&#39;")
return str
end
function M.highlight(str)
return M.element("span", { class = "highlight" }, str or "")
end
function M.banner(str)
return M.element("div", { class = "banner" }, str or "")
end
function M.center(str)
return M.element("div", { class = "center" }, str or "")
end
function M.nav(link, str)
link = link or "example.com"
str = str or link
return M.element("a", { href = link, class = "nav" }, str)
end
function M.rl(r, l)
return
M.element("span", { class = "left" }, r or "") ..
M.element("span", { class = "right" }, l or "")
end end
return M return M

View File

@@ -1,9 +1,16 @@
local std = require("lib.std")
local symbol = require("lib.symbol")
local M = {} local M = {}
function M.cc(tbl, sep) function M.cc(tbl, sep)
return table.concat(tbl, sep or "") return table.concat(tbl, sep or "")
end end
function M.copyright(link, holder)
return symbol.legal.copyright .. " " .. std.external(link, holder)
end
function M.ls(dir) function M.ls(dir)
local p = io.popen('ls -A -1 -- ' .. string.format('%q', dir)) local p = io.popen('ls -A -1 -- ' .. string.format('%q', dir))
if not p then if not p then

View File

@@ -9,7 +9,7 @@ var gitCommit string = "devel"
const PROGRAM_NAME string = "fes" const PROGRAM_NAME string = "fes"
const PROGRAM_NAME_LONG string = "fes/fSD" const PROGRAM_NAME_LONG string = "fes/fSD"
const VERSION string = "2.0.0" const VERSION string = "1.2.0"
func Version() { func Version() {
fmt.Printf("%s version %s\n", PROGRAM_NAME_LONG, VERSION) fmt.Printf("%s version %s\n", PROGRAM_NAME_LONG, VERSION)

1
todo Normal file
View File

@@ -0,0 +1 @@
footer: update link to direct to a local page for ISC