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.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 end return "" end function M.a(link, str) return "" .. str .. "" end function M.external(link, str) return "" .. str .. "" end function M.note(str) return '
' .. str .. '
' end function M.muted(str) return '
' .. str .. '
' end function M.h1(str) return "

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

" end function M.h2(str) return "

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

" end function M.h3(str) return "

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

" end function M.h4(str) return "

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

" end function M.h5(str) return "
" .. (str or "") .. "
" end function M.h6(str) return "
" .. (str or "") .. "
" end function M.p(str) return "

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

" end function M.code(str) return "" .. (str or "") .. "" end function M.pre(str) return "
" .. (str or "") .. "
" end function M.ul(items) items = items or {} local html = "" return html end function M.ol(items) items = items or {} local html = "
    " for _, item in ipairs(items) do html = html .. "
  1. " .. tostring(item) .. "
  2. " end html = html .. "
" return html end function M.blockquote(str) return "
" .. (str or "") .. "
" end function M.hr() return "
" end function M.img(src, alt) src = src or "" alt = alt or "" return '' .. alt .. '' end function M.strong(str) return "" .. (str or "") .. "" end function M.em(str) return "" .. (str or "") .. "" end function M.br() return "
" end function M.div(content, class) content = content or "" class = class or "" local class_attr = class ~= "" and (' class="' .. class .. '"') or "" return "" .. content .. "" end function M.span(content, class) content = content or "" class = class or "" local class_attr = class ~= "" and (' class="' .. class .. '"') or "" return "" .. content .. "" end -- HTML escaping utility function M.escape(str) str = tostring(str or "") str = str:gsub("&", "&") str = str:gsub("<", "<") str = str:gsub(">", ">") str = str:gsub('"', """) str = str:gsub("'", "'") 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 -- Join array with separator function M.join(arr, sep) arr = arr or {} sep = sep or ", " local result = {} for _, v in ipairs(arr) do table.insert(result, tostring(v)) end return table.concat(result, sep) end -- Trim whitespace function M.trim(str) str = tostring(str or "") return str:match("^%s*(.-)%s*$") end -- Table HTML generator function M.table(headers, rows) headers = headers or {} rows = rows or {} local html = "" for _, header in ipairs(headers) do html = html .. "" end html = html .. "" for _, row in ipairs(rows) do html = html .. "" for _, cell in ipairs(row) do html = html .. "" end html = html .. "" end html = html .. "
" .. tostring(header) .. "
" .. tostring(cell) .. "
" return html end return M