commit 522cbdece8753cd05a0d005e17aaaa7825fc2a6d
parent 3cfc9b4aed10ae5365b0856102bf94fe5049d10a
Author: vx-clutch <[email protected]>
Date: Sun, 14 Dec 2025 19:41:43 -0500
Merge pull request 'doc: first' (#4) from doc into main
Reviewed-on: https://git.vxserver.dev/fSD/fes/pulls/4
Diffstat:
5 files changed, 507 insertions(+), 45 deletions(-)
diff --git a/core/std.lua b/core/std.lua
@@ -186,46 +186,6 @@ function M.site_authors()
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 = "<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)
return '<span class="highlight">' .. (str or "") .. "</span>"
end
diff --git a/index.html b/index.html
@@ -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 <port></code></td>
+ <td>Set the server port</td>
+ </tr>
+ <tr>
+ <td><code>new <project></code></td>
+ <td>Create a new projet called <project></td>
+ </tr>
+ <tr>
+ <td><code>doc</code></td>
+ <td>Open this documention page</td>
+ </tr>
+ <tr>
+ <td><code>run <project></code></td>
+ <td>Run the projet called <project></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>©</td> </tr>
+ <tr>
+ <td><code>Registered Trademark</code></td>
+ <td>®</td>
+ </tr>
+ <tr>
+ <td><code>Trademark</code></td>
+ <td>™</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>
diff --git a/main.go b/main.go
@@ -18,10 +18,14 @@ import (
//go:embed core/*
var core embed.FS
+//go:embed index.html
+var documentation string
+
func init() {
config.Port = flag.Int("p", 3000, "Set the server port")
config.Color = flag.Bool("no-color", false, "Disable color output")
config.Core = core
+ config.Doc = documentation
}
func main() {
diff --git a/src/config/config.go b/src/config/config.go
@@ -6,6 +6,7 @@ import (
)
var Core embed.FS
+var Doc string
var Port *int
var Color *bool
diff --git a/src/doc/doc.go b/src/doc/doc.go
@@ -1,6 +1,7 @@
package doc
import (
+ "fes/src/config"
"fmt"
"os"
"path/filepath"
@@ -12,12 +13,8 @@ func Open() error {
fmt.Println("Opening documentation in browser")
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
}