Skip to content

Commit b197ec2

Browse files
committed
docs: actual docs page
1 parent dda51f5 commit b197ec2

File tree

7 files changed

+333
-21
lines changed

7 files changed

+333
-21
lines changed

.vitepress/config.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,23 @@ export default defineConfig({
77
title: "Rootbeer",
88
description: "Deterministically manage your dotfiles",
99
themeConfig: {
10-
// https://vitepress.dev/reference/default-theme-config
1110
nav: [
12-
{ text: 'Home', link: '/' },
11+
{ text: 'Guide', link: '/guide/getting-started' },
12+
{ text: 'Modules', link: '/modules/zsh' },
1313
],
1414

1515
sidebar: [
16+
{
17+
text: 'Guide',
18+
items: [
19+
{ text: 'Getting Started', link: '/guide/getting-started' },
20+
{ text: 'Core API', link: '/guide/core-api' },
21+
]
22+
},
1623
{
1724
text: 'Modules',
1825
items: [
19-
{ text: 'Zsh', link: '/modules/zsh' }
26+
{ text: 'Zsh', link: '/modules/zsh' },
2027
]
2128
}
2229
],

docs/guide/core-api.md

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
# Core API
2+
3+
The core API is available via `require("rootbeer")` and provides the
4+
primitives for writing files, creating symlinks, and querying system info.
5+
6+
```lua
7+
local rb = require("rootbeer")
8+
```
9+
10+
## Writing Files
11+
12+
Write content to any path. Parent directories are created automatically,
13+
and `~` expands to `$HOME`.
14+
15+
```lua
16+
rb.file("~/.config/starship.toml", [[
17+
[character]
18+
success_symbol = "[➜](bold green)"
19+
]])
20+
```
21+
22+
<!--@include: ../_generated/core.file.md-->
23+
24+
## Symlinking
25+
26+
Symlink files from your source directory into place. The source path is
27+
relative to `~/.local/share/rootbeer/source/`. Idempotent — existing
28+
correct links are skipped, stale links are replaced.
29+
30+
```lua
31+
rb.link_file("config/gitconfig", "~/.gitconfig")
32+
rb.link_file("config/nvim", "~/.config/nvim")
33+
```
34+
35+
<!--@include: ../_generated/core.link_file.md-->
36+
37+
## System Info
38+
39+
Query the current machine to write conditional configs that work across
40+
multiple systems.
41+
42+
```lua
43+
local d = rb.data()
44+
45+
if d.os == "Darwin" then
46+
rb.file("~/.config/homebrew/env", 'export HOMEBREW_PREFIX="/opt/homebrew"\n')
47+
end
48+
```
49+
50+
<!--@include: ../_generated/core.data.md-->
51+
52+
## JSON Serialization
53+
54+
Generate JSON config files for tools like VS Code or Alacritty.
55+
56+
```lua
57+
rb.file("~/.config/alacritty/alacritty.json", rb.to_json({
58+
terminal = { shell = "/bin/zsh" },
59+
font = { size = 14 },
60+
}))
61+
```
62+
63+
<!--@include: ../_generated/core.to_json.md-->
64+
65+
## Low-Level Primitives
66+
67+
These are building blocks used internally by modules. Prefer `rb.file()`
68+
with module renderers for most use cases.
69+
70+
<!--@include: ../_generated/core.line.md-->
71+
72+
<!--@include: ../_generated/core.emit.md-->
73+
74+
## Utilities
75+
76+
<!--@include: ../_generated/core.interpolate_table.md-->
77+
78+
<!--@include: ../_generated/core.register_module.md-->

docs/guide/getting-started.md

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
# Getting Started
2+
3+
Rootbeer is a dotfile manager that lets you define your system configuration
4+
in Lua scripts. Think [chezmoi](https://www.chezmoi.io/), but with the full
5+
power of a real scripting language instead of Go templates.
6+
7+
## Installation
8+
9+
Rootbeer is built from source using Meson and Ninja. LuaJIT and cJSON are
10+
vendored as subprojects — no extra dependencies required.
11+
12+
```bash
13+
meson setup build
14+
meson compile -C build
15+
```
16+
17+
The binary is built to `./build/src/cli/rb`.
18+
19+
If you have [mise](https://mise.jdx.dev/) installed:
20+
21+
```bash
22+
mise run build
23+
```
24+
25+
## Initializing
26+
27+
Set up a new source directory, or clone an existing dotfiles repo:
28+
29+
```bash
30+
# Create a fresh source directory with a starter manifest
31+
rb init
32+
33+
# Or clone from a GitHub repo
34+
rb init tale/dotfiles
35+
```
36+
37+
This creates `~/.local/share/rootbeer/source/` with a `rootbeer.lua` manifest
38+
file. This directory is your dotfiles repo — commit it, push it, clone it on
39+
another machine.
40+
41+
## Your First Config
42+
43+
Open `~/.local/share/rootbeer/source/rootbeer.lua`:
44+
45+
```lua
46+
local rb = require("rootbeer")
47+
local zsh = require("rootbeer.shells.zsh")
48+
49+
rb.file("~/.zshrc", zsh.config({
50+
env = {
51+
EDITOR = "nvim",
52+
LANG = "en_US.UTF-8",
53+
},
54+
aliases = {
55+
g = "git",
56+
v = "nvim",
57+
},
58+
extra = "autoload -Uz compinit && compinit",
59+
}))
60+
```
61+
62+
This writes a generated `.zshrc` to your home directory.
63+
64+
## Applying
65+
66+
```bash
67+
# Preview what would change
68+
rb apply -n
69+
70+
# Apply for real
71+
rb apply
72+
```
73+
74+
`rb apply` evaluates your manifest and writes/links files into place. Use
75+
`-n` (dry run) to see what would happen without touching the filesystem.
76+
77+
## Conditionals
78+
79+
Use `rb.data()` to branch on the current machine:
80+
81+
```lua
82+
local d = rb.data()
83+
84+
if d.os == "Darwin" then
85+
rb.file("~/.config/homebrew/env", 'export HOMEBREW_PREFIX="/opt/homebrew"\n')
86+
end
87+
88+
if d.hostname == "workstation" then
89+
-- work-specific config
90+
end
91+
```
92+
93+
`rb.data()` returns a table with:
94+
95+
| Field | Description |
96+
|-------|-------------|
97+
| `os` | Operating system name (e.g. `Linux`, `Darwin`) |
98+
| `arch` | CPU architecture (e.g. `x86_64`, `arm64`) |
99+
| `hostname` | Machine hostname |
100+
| `home` | Home directory path |
101+
| `username` | Current username |
102+
103+
## Symlinking
104+
105+
For files you want to edit directly (like a gitconfig), use `rb.link_file()`
106+
to symlink them from your source directory:
107+
108+
```lua
109+
-- Source path is relative to the source directory
110+
-- Target path supports ~ expansion
111+
rb.link_file("config/gitconfig", "~/.gitconfig")
112+
rb.link_file("config/nvim", "~/.config/nvim")
113+
```
114+
115+
Symlinks are idempotent — if the link already points to the right place,
116+
nothing happens. Stale links are replaced.
117+
118+
## Next Steps
119+
120+
- [Modules](/modules/zsh) — Declarative config generators for shells and tools
121+
- [Core API](/guide/core-api) — Full reference for `rb.*` functions

docs/index.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
---
2+
layout: home
3+
4+
hero:
5+
name: Rootbeer
6+
tagline: Manage your dotfiles with Lua
7+
actions:
8+
- theme: brand
9+
text: Get Started
10+
link: /guide/getting-started
11+
- theme: alt
12+
text: View on GitHub
13+
link: https://github.com/tale/rootbeer
14+
15+
features:
16+
- title: Real Scripting, No Templates
17+
details: Use Lua's loops, conditionals, and functions instead of learning a templating DSL. Your config is code.
18+
- title: Declarative Modules
19+
details: Describe what your shell config, git config, or SSH config should look like with simple Lua tables.
20+
- title: Cross-Platform
21+
details: Detect OS, architecture, and hostname at runtime. One config repo, multiple machines.
22+
---

lua/rootbeer/core.lua

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
--- @meta
2+
--- Stubs for the native rootbeer core API (implemented in C).
3+
--- This file is not loaded at runtime — it exists for LuaLS
4+
--- type checking and lua2md documentation generation.
5+
6+
local rb = {}
7+
8+
--- @class rb.DataInfo
9+
--- @field os string Operating system name (e.g. `"Linux"`, `"Darwin"`).
10+
--- @field arch string CPU architecture (e.g. `"x86_64"`, `"arm64"`).
11+
--- @field hostname string Machine hostname.
12+
--- @field home string Home directory path.
13+
--- @field username string Current username.
14+
15+
--- Writes content to a file path.
16+
--- The `~` character is expanded to `$HOME`. Parent directories are
17+
--- created automatically. In dry-run mode, prints what would be written.
18+
--- @param path string The destination file path (supports `~` expansion).
19+
--- @param content string The content to write.
20+
function rb.file(path, content) end
21+
22+
--- Creates a symlink from a source file to a destination path.
23+
--- The source is relative to the rootbeer source directory
24+
--- (`~/.local/share/rootbeer/source/`). The destination supports `~`
25+
--- expansion. Idempotent — existing correct links are left alone, stale
26+
--- links are replaced.
27+
--- @param src string Source path, relative to the source directory.
28+
--- @param dest string Destination path (supports `~` expansion).
29+
function rb.link_file(src, dest) end
30+
31+
--- Returns a table with information about the current machine.
32+
--- @return rb.DataInfo
33+
function rb.data() end
34+
35+
--- Serializes a Lua table to a JSON string.
36+
--- @param tbl table The table to serialize.
37+
--- @return string
38+
function rb.to_json(tbl) end
39+
40+
--- Appends a line (with trailing newline) to the internal output buffer.
41+
--- This is a low-level primitive — prefer `rb.file()` with module
42+
--- renderers like `zsh.config()` for most use cases.
43+
--- @param str string The line to append.
44+
function rb.line(str) end
45+
46+
--- Appends raw text to the internal output buffer without a trailing newline.
47+
--- @param str string The text to append.
48+
function rb.emit(str) end
49+
50+
--- Passes a table through a transform function and returns the result.
51+
--- @param tbl table The input table.
52+
--- @param fn fun(tbl: table): table The transform function.
53+
--- @return table
54+
function rb.interpolate_table(tbl, fn) end
55+
56+
--- Registers a native Lua module under the given name.
57+
--- Used internally by the plugin system.
58+
--- @param name string The module name to register.
59+
--- @param tbl table The module table.
60+
function rb.register_module(name, tbl) end
61+
62+
return rb

lua/rootbeer/init.lua

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
local M = {}
2+
3+
M.zsh = require("rootbeer.shells.zsh")
4+
5+
return M

scripts/lua2md.ts

Lines changed: 35 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,12 @@ interface Module {
5959

6060
// ── Parsing ────────────────────────────────────────────────────────
6161

62-
/** Match a type that may contain generics and union pipes: table<string, string>, string|string[] */
63-
const TYPE_RE = /(?:\S+<[^>]+>|\S+(?:\|(?:\S+<[^>]+>|\S+))*)/
62+
/** Match a LuaLS type annotation:
63+
* - fun(tbl: table): table — function signatures with parens/spaces
64+
* - table<string, string> — generics with angle brackets
65+
* - string|string[] — union types
66+
*/
67+
const TYPE_RE = /(?:fun\([^)]*\)(?:\s*:\s*\S+)?|\S+<[^>]+>|\S+(?:\|(?:\S+<[^>]+>|\S+))*)/
6468

6569
function parseFile(filepath: string, root: string): Module | null {
6670
const rel = relative(root, filepath)
@@ -205,29 +209,42 @@ function renderBlock(block: DocBlock, mod: Module): string {
205209
}
206210

207211
if (block.params.length) {
208-
for (const p of block.params) {
209-
const classDef = mod.classes.get(p.type)
210-
if (classDef) {
211-
out.push(`**\`${p.name}\`** \`${escapeCell(p.type)}\` — ${escapeCell(p.desc)}`)
212-
out.push("")
213-
renderFields(out, classDef.fields)
214-
out.push("")
215-
} else {
216-
out.push("**Parameters:**")
217-
out.push("")
218-
out.push("| Name | Type | Description |")
219-
out.push("|------|------|-------------|")
212+
// Split params into class-typed (expanded inline) and plain
213+
const classParams = block.params.filter((p) => mod.classes.has(p.type))
214+
const plainParams = block.params.filter((p) => !mod.classes.has(p.type))
215+
216+
if (plainParams.length) {
217+
out.push("**Parameters:**")
218+
out.push("")
219+
out.push("| Name | Type | Description |")
220+
out.push("|------|------|-------------|")
221+
for (const p of plainParams) {
220222
out.push(`| \`${p.name}\` | \`${escapeCell(p.type)}\` | ${escapeCell(p.desc)} |`)
221-
out.push("")
222223
}
224+
out.push("")
225+
}
226+
227+
for (const p of classParams) {
228+
const classDef = mod.classes.get(p.type)!
229+
out.push(`**\`${p.name}\`** \`${escapeCell(p.type)}\` — ${escapeCell(p.desc)}`)
230+
out.push("")
231+
renderFields(out, classDef.fields)
232+
out.push("")
223233
}
224234
}
225235

226236
if (block.returns.length) {
227-
out.push("**Returns:** ", "")
237+
out.push("**Returns:**", "")
228238
for (const r of block.returns) {
229-
const desc = r.desc ? ` — ${r.desc}` : ""
230-
out.push(`- \`${escapeCell(r.type)}\`${desc}`)
239+
const classDef = mod.classes.get(r.type)
240+
if (classDef) {
241+
out.push(`- \`${escapeCell(r.type)}\`${r.desc ? ` — ${r.desc}` : ""}`)
242+
out.push("")
243+
renderFields(out, classDef.fields)
244+
} else {
245+
const desc = r.desc ? ` — ${r.desc}` : ""
246+
out.push(`- \`${escapeCell(r.type)}\`${desc}`)
247+
}
231248
}
232249
out.push("")
233250
}

0 commit comments

Comments
 (0)