- Rust 57.8%
- Handlebars 21.9%
- TypeScript 10.4%
- CSS 4.7%
- HTML 3.4%
- Other 1.8%
| +content | ||
| +devops | ||
| src | ||
| .gitignore | ||
| AGENTS.md | ||
| bun.lock | ||
| Cargo.lock | ||
| Cargo.toml | ||
| dev.ts | ||
| flake.lock | ||
| flake.nix | ||
| package.json | ||
| README.md | ||
| schema.yaml | ||
| thp-docs.toml | ||
THP docs
Custom CMS built for THP.
Usage
This program does these things:
- Read config file
thp-docs.toml
The config file declares the content_folder (e.g. content_folder = "content").
It also requires a narac field which must point to the Nara compiler executable (e.g. narac = "/usr/bin/narac").
Inside the content folder, the program expects:
- A
+staticfolder for static assets. - A
+routesfolder for the page hierarchy.
Then, based on those 2 things:
- In dev mode, sets up hot reloading on
+routesand serves the routes as full html pages. On each request fully processes the file. - In build mode, walks
+routes& generates a full, static html page for each route it encounters.
Routes
Inside the routes folder is where the actual pages go. Each file
(with exceptions) inside matches to a route. For instance, these files:
- +content/
- +routes/
- index.html
- install.md
- support/
- forum.hbs
would create 3 routes: /, /install and /support/forum
If a directory is requested (e.g., /support/), the program will look for an index file with any of the supported extensions inside that directory.
Supported extensions
The program supports html, markdown, handlebars & yaml files.
Nara Code Blocks in Markdown
In markdown files, you can use nara code blocks. If you add a :compile flag to the language tag, the CMS will attempt to compile the code using the narac compiler specified in the config.
```nara:compile
// This code will be compiled to bytecode
let x = 10;
print(x);
```
The compiled bytecode is then passed to the interactive playground component. If compilation fails, the error message will be shown in the console and the build will continue with an error message in the bytecode field.
+layout files
Each folder can have a single +layout file. Such file works as a "shell"
for the rest of routes, and its descendants.
- +content/
- +routes
- support/
- +layout.hbs
- forum.hbs
- stack_overflow.hbs
- self_managed/
- phone.hbs
In the example above, the +layout.hbs file will be used as a shell for all
files in the folder (forum, stack_overflow), AND for self_managed/phone.
Layouts are nested. If self_managed/ also had a +layout.hbs, then phone.hbs
would be wrapped by self_managed/+layout.hbs, which would then be wrapped by
support/+layout.hbs. This works from the innermost file out to the root of the
+routes folder.
There can be multiple +layout files, but only one per folder.
The layout file MUST be HBS, and it MUST have a {{{children}}} tag
inside it. This tag is where the content of the child pages will be rendered.
File Tree in Layouts
Layout files have access to a file_tree variable containing all routes
in the layout's directory and its subdirectories. This is useful for rendering
navigation menus.
Each entry in file_tree has:
name: Display name (e.g., "Getting Started")path: URL path (e.g., "/learn/getting-started")is_dir: Whether the entry is a directorychildren: Child entries (for directories)
Example usage in a layout:
<nav>
<ul>
{{#each file_tree}}
<li>
<a href="{{this.path}}">{{this.name}}</a>
{{#if this.is_dir}}
<ul>
{{#each this.children}}
<li><a href="{{this.path}}">{{this.name}}</a></li>
{{/each}}
</ul>
{{/if}}
</li>
{{/each}}
</ul>
</nav>
+order.yml files
By default, the file tree is sorted alphabetically with auto-prettified names
(e.g., getting-started.md becomes "Getting Started"). To control the order
and display names, create a +order.yml file in the directory.
- path: index
name: Introduction
- path: variables
name: Variables & Data Types
- path: functions
name: Functions
- path: advanced
name: Advanced Topics
Each entry has:
path: The file or folder name without extension (e.g.,indexforindex.md)name: The display name to show in navigation
Behavior:
- If
+order.ymlexists, only listed entries appear in the nav (unlisted files are hidden) - If
+order.ymldoes not exist, all files appear alphabetically - Each directory can have its own
+order.yml(nested, not inherited) - Missing entries (listed in manifest but file doesn't exist) produce a warning
API Documentation
The program supports generating API documentation from YAML files.
YAML Schema
Files with the .yaml extension are treated as API documentation. They must follow a specific schema:
title: std
description: Core language features.
internals: |
This module is implemented in C++ and exposed via the Nara runtime.
It uses the `libuv` library for asynchronous I/O.
refs:
- id: FileDescriptor
name: FileDescriptor
description: A handle to an open file.
link: "https://en.wikipedia.org/wiki/File_descriptor"
functions:
- name: open
description: Opens a file.
parameters:
- name: path
type: string
description: The filesystem path.
return:
type: "$ref:FileDescriptor"
description: The opened file handle.
example: |
const fd = std.open("/etc/passwd");
console.log(fd);
+api_layout files
Unlike regular routes, YAML files require a +api_layout.hbs file to be present in the same folder or any parent folder.
The +api_layout.hbs file is a Handlebars template that receives the parsed YAML data in a api map. You can access fields like {{api.namespace}}, {{api.description}}, {{api.types}}, and {{api.functions}} directly.
Note that +api_layout.hbs files are also inherited from parent folders, similar to +layout.hbs files, but they are only used for rendering YAML files.
When a YAML file is rendered, it is first processed by the +api_layout.hbs template. The resulting HTML is then wrapped by any applicable +layout.hbs files, just like regular routes.
Type References
In the YAML schema, types can be plain strings or references to types defined in the types section by prefixing them with $ref:.
When rendered in the template, these are passed as strings (e.g., "$ref:FileDescriptor").
Static assets
The +static folder is where the static assets go. These files are served
as-is, without any processing, at the root of the site. For example,
+content/+static/tw-out.css is served at /tw-out.css.