3 Commits
rich-ui ... doc

Author SHA1 Message Date
f37c3e0e53 fix: util.cc concat with \n 2025-12-14 20:29:45 -05:00
1427d0d780 doc: first 2025-12-14 19:40:55 -05:00
3cfc9b4aed Merge pull request 'rich-ui' (#3) from rich-ui into main
Reviewed-on: #3
2025-12-14 11:55:27 -05:00
6 changed files with 508 additions and 46 deletions

View File

@@ -186,46 +186,6 @@ function M.site_authors()
return {} return {}
end 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 = "<table><thead><tr>"
for _, header in ipairs(headers) do
html = html .. "<th>" .. tostring(header) .. "</th>"
end
html = html .. "</tr></thead><tbody>"
for _, row in ipairs(rows) do
html = html .. "<tr>"
for _, cell in ipairs(row) do
html = html .. "<td>" .. tostring(cell) .. "</td>"
end
html = html .. "</tr>"
end
html = html .. "</tbody></table>"
return html
end
function M.highlight(str) function M.highlight(str)
return '<span class="highlight">' .. (str or "") .. "</span>" return '<span class="highlight">' .. (str or "") .. "</span>"
end end

View File

@@ -4,7 +4,7 @@ local symbol = require("core.symbol")
local M = {} local M = {}
function M.cc(tbl) function M.cc(tbl)
return table.concat(tbl) return table.concat(tbl, "\n")
end end
function M.copyright(link, holder) function M.copyright(link, holder)

500
index.html Normal file
View File

@@ -0,0 +1,500 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Documentation Title</title>
<style>
html, body {
min-height: 100%;
margin: 0;
padding: 0;
background: #0f1113;
color: #e6eef3;
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;
}
main {
max-width: 830px;
margin: 0 auto;
padding: 36px;
}
header {
margin-bottom: 36px;
}
h1 {
font-size: 40px;
font-weight: 700;
margin: 0 0 20px 0;
}
h2 {
font-size: 32px;
margin: 26px 0 14px;
border-bottom: 1px solid rgba(255,255,255,.1);
padding-bottom: 6px;
}
h3 {
font-size: 26px;
margin: 22px 0 12px;
}
p {
margin: 14px 0;
}
header p {
color: #9aa6b1;
}
nav {
margin: 28px 0;
padding: 20px;
background: #1a1c20;
border: 1px solid rgba(255,255,255,.06);
border-radius: 4px;
}
nav h2 {
font-size: 20px;
margin: 0 0 12px 0;
border: none;
padding: 0;
}
nav ul {
list-style: none;
padding: 0;
margin: 0;
}
nav li {
margin: 6px 0;
}
a {
color: #68a6ff;
text-decoration: none;
transition: color .15s ease;
}
a:hover {
text-decoration: underline;
}
section {
margin-top: 36px;
}
ul, ol {
margin: 14px 0;
padding-left: 26px;
}
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: #cde7ff;
background: #1a1c20;
border: 1px solid rgba(255,255,255,.06);
}
pre {
padding: 20px;
border-radius: 4px;
margin: 14px 0;
overflow-x: auto;
background: #1a1c20;
border: 1px solid rgba(255,255,255,.06);
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;
color: inherit;
}
blockquote {
border-left: 3px solid #68a6ff;
padding-left: 18px;
margin: 14px 0;
color: #dfe9ee;
font-style: italic;
}
table {
width: 100%;
border-collapse: collapse;
margin: 14px 0;
}
th, td {
padding: 12px 16px;
text-align: left;
border-bottom: 1px solid rgba(255,255,255,.06);
}
th {
background: #1a1c20;
font-weight: 600;
color: #f0f6f8;
}
tr:hover {
background: rgba(255,255,255,.02);
}
footer {
margin-top: 48px;
padding-top: 20px;
border-top: 1px solid rgba(255,255,255,.1);
color: #9aa6b1;
font-size: 14px;
}
</style>
</head>
<body>
<main>
<header>
<h1>Documentation</h1>
<p>Fes: A lightweight, static, and opinionated microframework.</p>
</header>
<nav>
<h2>Contents</h2>
<ul>
<li><a href="#introduction">Introduction</a></li>
<li><a href="#installation">Installation</a></li>
<li><a href="#usage">Usage</a></li>
<li><a href="#cli-reference">Cli Reference</a></li>
<li><a href="#reference">Reference</a></li>
</ul>
</nav>
<section id="introduction">
<h2>Introduction</h2>
<p>Fes, or Free Easy Site, is a microframework used for small static sites. It is not designed for complex web applications and that is why it is good. Yes, I hate modern web and that is the reason this exists.</p>
</section>
<section id="installation">
<h2>Installation</h2>
<pre><code>git clone https://git.vxserver.dev/fSD/fes</code></pre>
<pre><code>cd fes</code></pre>
<pre><code>go install fes</code></pre>
</section>
<section id="usage">
<h2>Usage</h2>
<p>Typical workflows and examples.</p>
<ul>
<li>Creating project</li>
<li>Hosting websites</li>
<li>Generating websites</li>
</ul>
</section>
<section id="cli-reference">
<h2>Cli Reference</h2>
<table> <thead>
<tr>
<th>Name</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>--help</code></td>
<td>Display help information</td>
</tr>
<tr>
<td><code>--no-color</code></td>
<td>Disable color output</td>
</tr>
<tr>
<td><code>-p &lt;port&gt;</code></td>
<td>Set the server port</td>
</tr>
<tr>
<td><code>new &lt;project&gt;</code></td>
<td>Create a new projet called &lt;project&gt;</td>
</tr>
<tr>
<td><code>doc</code></td>
<td>Open this documention page</td>
</tr>
<tr>
<td><code>run &lt;project&gt;</code></td>
<td>Run the projet called &lt;project&gt;</td>
</tr>
</tbody>
</table>
</section>
<section id="reference">
<h2>Reference</h2>
All <code>std</code> functions have binding for the site and can be used like so: <code>site:h1("Hello, World!")</code>, where site is the site object.
<h3>Builtin</h3>
<table>
<thead>
<tr>
<th>Name</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>fes()</code></td>
<td>Generate a site object</td>
</tr>
<tr>
<td><code>:custom()</code></td>
<td>Add a custom string to the site body</td>
</tr>
</tbody>
</table>
<h3>Std</h3>
<table>
<thead>
<tr>
<th>Name</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>std.fes_version()</code></td>
<td>Get the current version of fes.</td>
</tr>
<tr>
<td><code>std.site_version()</code></td>
<td>Get the current version of the site, defined in <code>Fes.toml</code>.</td>
</tr>
<tr>
<td><code>std.site_name()</code></td>
<td>Get the current name of the site, defined in <code>Fes.toml</code>.</td>
</tr>
<tr>
<td><code>std.site_title()</code></td>
<td>Get the current name of the site, defined in <code>Fes.toml</code>.</td>
</tr>
<tr>
<td><code>std.site_authors()</code></td>
<td>Get a table of the authors of the site, defined in <code>Fes.toml</code>.</td>
</tr>
<tr>
<td><code>std.join</code></td>
<td>Get a table of the authors of the site, defined in <code>Fes.toml</code>.</td>
</tr>
<tr>
<td><code>std.a(link: string, str: string)</code></td>
<td>Returns an anchor tag.</td>
</tr>
<tr>
<td><code>std.ha(link: string, str: string)</code></td>
<td>Returns an anchor tag with sytiling to make it hidden.</td>
</tr>
<tr>
<td><code>std.external(link: string, str: string)</code></td>
<td>Returns an anchor tag that opens up in a new tab.</td>
</tr>
<tr>
<td><code>std.note(str: string)</code></td>
<td>Returns a note object.</td>
</tr>
<tr>
<td><code>std.muted(str: string)</code></td>
<td>Returns a muted object.</td>
</tr>
<tr>
<td><code>std.callout(str: string)</code></td>
<td>Returns a callout object.</td>
</tr>
<tr>
<td><code>std.h1(str: string)</code></td>
<td>Returns a header level 1 object.</td>
</tr>
<tr>
<td><code>std.h2(str: string)</code></td>
<td>Returns a header level 2 object.</td>
</tr>
<tr>
<td><code>std.h3(str: string)</code></td>
<td>Returns a header level 3 object.</td>
</tr>
<tr>
<td><code>std.h4(str: string)</code></td>
<td>Returns a header level 4 object.</td>
</tr>
<tr>
<td><code>std.h5(str: string)</code></td>
<td>Returns a header level 5 object.</td>
</tr>
<tr>
<td><code>std.h6(str: string)</code></td>
<td>Returns a header level 6 object.</td>
</tr>
<tr>
<td><code>std.p(str: string)</code></td>
<td>Returns a paragraph object.</td>
</tr>
<tr>
<td><code>std.pre(str: string)</code></td>
<td>Returns preformated text.</td>
</tr>
<tr>
<td><code>std.code(str: string)</code></td>
<td>Returns a codeblock.</td>
</tr>
<tr>
<td><code>std.ul(items: {...strings})</code></td>
<td>Generates an unordered list from a table of strings.</td>
</tr>
<tr>
<td><code>std.ol(items: {...strings})</code></td>
<td>Generates an ordered list from a table of strings.</td>
</tr>
<tr>
<td><code>std.tl(items: {...strings})</code></td>
<td>Generates a table from a table of strings.</td>
</tr>
<tr>
<td><code>std.blockquote(str: string)</code></td>
<td>Returns a blockquote.</td>
</tr>
<tr>
<td><code>std.hr()</code></td>
<td>Returns a horizonal rule.</td>
</tr>
<tr>
<td><code>std.img(src: string, alt: string)</code></td>
<td>Returns an image at location source with given alternative text.</td>
</tr>
<tr>
<td><code>std.strong(str: string)</code></td>
<td>Returns bolded text.</td>
</tr>
<tr>
<td><code>std.em(str: string)</code></td>
<td>Returns italicized text.</td>
</tr>
<tr>
<td><code>std.br()</code></td>
<td>Returns a break.</td>
</tr>
<tr>
<td><code>std.div(content: string, class: string)</code></td>
<td>Returns a div of class <code>class</code> with content of <code>content</code>.</td>
</tr>
<tr>
<td><code>std.spa(content: string, class: string)</code></td>
<td>Returns a span of class <code>class</code> with content of <code>content</code>.</td>
</tr>
<tr>
<td><code>std.escape(str: string)</code></td>
<td>Returns an html escaped string.</td>
</tr>
<tr>
<td><code>std.highlight(str: string)</code></td>
<td>Returns highlighted text.</td>
</tr>
<tr>
<td><code>std.banner(str: string)</code></td>
<td>Returns a banner that is attached to the top of the site.</td>
</tr>
<tr>
<td><code>std.center(str: string)</code></td>
<td>Returns centered text.</td>
</tr>
<tr>
<td><code>std.nav(link: string, str: string)</code></td>
<td>Returns a speical navigation link, used for in-site traversal.</td>
</tr>
<tr>
<td><code>std.rl(r: string, l: string)</code></td>
<td>Right and light alight content.</td>
</tr>
</tbody>
</table>
<h3>Symbol</h3>
<table> <thead>
<tr>
<th>Name</th>
<th>Symbol</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>symbol.copyright</code></td>
<td>&#169;</td> </tr>
<tr>
<td><code>Registered Trademark</code></td>
<td>&#174;</td>
</tr>
<tr>
<td><code>Trademark</code></td>
<td>&#8482;</td>
</tr>
</tbody>
</table>
<h3>Util</h3>
<table> <thead>
<tr>
<th>Name</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>util.cc(tbl: {...strings})</code></td>
<td>Concatenate a table of strings into a single string.</td>
</tr>
<tr>
<td><code>util.copyright(link: string, holder: string)</code></td>
<td>Used when setting the website copyright holder.</td>
</tr>
</tbody>
</table>
<h3>Dkjson</h3>
This is a third party object and is best documented <a href="https://dkolf.de/dkjson-lua/documentation" target="_blank">here</a>
<table> <thead>
<tr>
<th>Name</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>dkjson.encode(obj: {...any})</code></td>
<td>Serilize a Lua table into JSON.</td>
</tr>
<tr>
<td><code>dkjson.decode(obj: {...any})</code></td>
<td>Deserilize JSON into a Lua table.</td>
</tr>
</tbody>
</table>
</section>
<footer>
<p>Last updated: 2025-12-14</p>
</footer>
</main>
</body>
</html>

View File

@@ -18,10 +18,14 @@ import (
//go:embed core/* //go:embed core/*
var core embed.FS var core embed.FS
//go:embed index.html
var documentation string
func init() { func init() {
config.Port = flag.Int("p", 3000, "Set the server port") config.Port = flag.Int("p", 3000, "Set the server port")
config.Color = flag.Bool("no-color", false, "Disable color output") config.Color = flag.Bool("no-color", false, "Disable color output")
config.Core = core config.Core = core
config.Doc = documentation
} }
func main() { func main() {

View File

@@ -6,6 +6,7 @@ import (
) )
var Core embed.FS var Core embed.FS
var Doc string
var Port *int var Port *int
var Color *bool var Color *bool

View File

@@ -1,6 +1,7 @@
package doc package doc
import ( import (
"fes/src/config"
"fmt" "fmt"
"os" "os"
"path/filepath" "path/filepath"
@@ -12,12 +13,8 @@ func Open() error {
fmt.Println("Opening documentation in browser") fmt.Println("Opening documentation in browser")
tmpFile := filepath.Join(os.TempDir(), "doc.html") tmpFile := filepath.Join(os.TempDir(), "doc.html")
content := `<html><body><pre>
This feature is not implemented yet. It will be once the doc site
is up and running, for now read through the core/ files and examples.
</pre></body></html>`
if err := os.WriteFile(tmpFile, []byte(content), 0644); err != nil { if err := os.WriteFile(tmpFile, []byte(config.Doc), 0644); err != nil {
return err return err
} }