feat(docs): create sukr documentation site
Self-documenting docs site built with sukr itself (dogfooding): - docs/site.toml with sukr.io base URL - docs-specific templates with sidebar navigation - Dark theme CSS, responsive layout - Documentation: getting-started, configuration, features Also: improved error chaining for better template debugging
This commit is contained in:
35
docs/content/_index.md
Normal file
35
docs/content/_index.md
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
---
|
||||||
|
title: sukr
|
||||||
|
description: Minimal static site compiler — suckless, Rust, zero JS
|
||||||
|
---
|
||||||
|
|
||||||
|
# Welcome to sukr
|
||||||
|
|
||||||
|
**sukr** transforms Markdown into high-performance static HTML. No bloated runtimes, no client-side JavaScript, just clean output.
|
||||||
|
|
||||||
|
## Why sukr?
|
||||||
|
|
||||||
|
- **Fast builds** — Single Rust binary, parallel processing
|
||||||
|
- **Zero JS** — Syntax highlighting at build time via Tree-sitter
|
||||||
|
- **Flexible templates** — Runtime Tera templates, no recompilation
|
||||||
|
- **Monorepo-ready** — Multiple sites via `-c` config flag
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Install
|
||||||
|
cargo install sukr
|
||||||
|
|
||||||
|
# Create site structure
|
||||||
|
mkdir -p content templates static
|
||||||
|
echo 'title = "My Site"' > site.toml
|
||||||
|
echo 'author = "Me"' >> site.toml
|
||||||
|
echo 'base_url = "https://example.com"' >> site.toml
|
||||||
|
|
||||||
|
# Build
|
||||||
|
sukr
|
||||||
|
```
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
Browse the sidebar for detailed documentation on all features.
|
||||||
78
docs/content/configuration.md
Normal file
78
docs/content/configuration.md
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
---
|
||||||
|
title: Configuration
|
||||||
|
description: Complete reference for site.toml configuration
|
||||||
|
weight: 2
|
||||||
|
---
|
||||||
|
|
||||||
|
# Configuration
|
||||||
|
|
||||||
|
sukr is configured via `site.toml`. All settings have sensible defaults.
|
||||||
|
|
||||||
|
## Basic Configuration
|
||||||
|
|
||||||
|
```toml
|
||||||
|
title = "My Site"
|
||||||
|
author = "Your Name"
|
||||||
|
base_url = "https://example.com"
|
||||||
|
```
|
||||||
|
|
||||||
|
| Field | Required | Description |
|
||||||
|
| ---------- | -------- | -------------------------------- |
|
||||||
|
| `title` | Yes | Site title (used in page titles) |
|
||||||
|
| `author` | Yes | Author name (used in feeds) |
|
||||||
|
| `base_url` | Yes | Canonical URL for the site |
|
||||||
|
|
||||||
|
## Path Configuration
|
||||||
|
|
||||||
|
All paths are optional. Default values shown:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[paths]
|
||||||
|
content = "content" # Markdown source files
|
||||||
|
output = "public" # Generated HTML output
|
||||||
|
static = "static" # Static assets (copied as-is)
|
||||||
|
templates = "templates" # Tera template files
|
||||||
|
```
|
||||||
|
|
||||||
|
Paths are resolved **relative to the config file location**. This enables monorepo setups:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Build site from subdirectory
|
||||||
|
sukr -c sites/blog/site.toml
|
||||||
|
# Paths resolve relative to sites/blog/
|
||||||
|
```
|
||||||
|
|
||||||
|
## CLI Options
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sukr # Use ./site.toml
|
||||||
|
sukr -c path/to/site.toml # Custom config
|
||||||
|
sukr --config path/to/site.toml
|
||||||
|
sukr -h, --help # Show help
|
||||||
|
```
|
||||||
|
|
||||||
|
## Frontmatter
|
||||||
|
|
||||||
|
Each Markdown file can have YAML frontmatter:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
---
|
||||||
|
title: Page Title
|
||||||
|
description: Optional description
|
||||||
|
date: 2024-01-15 # For blog posts
|
||||||
|
weight: 10 # Sort order (lower = first)
|
||||||
|
nav_label: Short Name # Override nav display
|
||||||
|
section_type: blog # Override section template
|
||||||
|
template: custom # Override page template
|
||||||
|
---
|
||||||
|
```
|
||||||
|
|
||||||
|
### Section Types
|
||||||
|
|
||||||
|
The `section_type` field determines which template is used for section indexes:
|
||||||
|
|
||||||
|
- `blog` → `templates/section/blog.html`
|
||||||
|
- `projects` → `templates/section/projects.html`
|
||||||
|
- _(any other)_ → `templates/section/default.html`
|
||||||
|
|
||||||
|
If not specified, sukr uses the directory name as the section type.
|
||||||
8
docs/content/features/_index.md
Normal file
8
docs/content/features/_index.md
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
---
|
||||||
|
title: Features
|
||||||
|
description: Explore sukr's capabilities
|
||||||
|
section_type: features
|
||||||
|
weight: 3
|
||||||
|
---
|
||||||
|
|
||||||
|
sukr provides a focused set of features for building fast, minimal static sites.
|
||||||
71
docs/content/features/sections.md
Normal file
71
docs/content/features/sections.md
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
---
|
||||||
|
title: Sections
|
||||||
|
description: Automatic section discovery and processing
|
||||||
|
weight: 2
|
||||||
|
---
|
||||||
|
|
||||||
|
# Sections
|
||||||
|
|
||||||
|
sukr automatically discovers sections from your content directory structure.
|
||||||
|
|
||||||
|
## What is a Section?
|
||||||
|
|
||||||
|
A section is any directory under `content/` that contains an `_index.md` file:
|
||||||
|
|
||||||
|
```
|
||||||
|
content/
|
||||||
|
├── _index.md # Homepage (not a section)
|
||||||
|
├── about.md # Standalone page
|
||||||
|
├── blog/ # ← This is a section
|
||||||
|
│ ├── _index.md # Section index
|
||||||
|
│ └── my-post.md # Section content
|
||||||
|
└── projects/ # ← This is also a section
|
||||||
|
├── _index.md
|
||||||
|
└── project-a.md
|
||||||
|
```
|
||||||
|
|
||||||
|
## Section Discovery
|
||||||
|
|
||||||
|
sukr automatically:
|
||||||
|
|
||||||
|
1. Scans `content/` for directories with `_index.md`
|
||||||
|
2. Collects all `.md` files in that directory (excluding `_index.md`)
|
||||||
|
3. Renders the section index template with the items
|
||||||
|
4. Renders individual content pages (for blog-type sections)
|
||||||
|
|
||||||
|
## Section Types
|
||||||
|
|
||||||
|
The section type determines which template is used. It's resolved in order:
|
||||||
|
|
||||||
|
1. **Frontmatter override**: `section_type: blog` in `_index.md`
|
||||||
|
2. **Directory name**: `content/blog/` → type `blog`
|
||||||
|
|
||||||
|
### Built-in Section Types
|
||||||
|
|
||||||
|
| Type | Behavior |
|
||||||
|
| ---------- | ------------------------------------------------------ |
|
||||||
|
| `blog` | Sorts by date (newest first), renders individual posts |
|
||||||
|
| `projects` | Sorts by weight, card-style listing |
|
||||||
|
| _(other)_ | Sorts by weight, uses default template |
|
||||||
|
|
||||||
|
## Section Frontmatter
|
||||||
|
|
||||||
|
In `_index.md`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
---
|
||||||
|
title: My Blog
|
||||||
|
description: Thoughts and tutorials
|
||||||
|
section_type: blog # Optional, defaults to directory name
|
||||||
|
weight: 1 # Nav order
|
||||||
|
---
|
||||||
|
```
|
||||||
|
|
||||||
|
## Adding a New Section
|
||||||
|
|
||||||
|
1. Create directory: `content/recipes/`
|
||||||
|
2. Create index: `content/recipes/_index.md`
|
||||||
|
3. Add content: `content/recipes/pasta.md`
|
||||||
|
4. Optionally create template: `templates/section/recipes.html`
|
||||||
|
|
||||||
|
That's it. sukr handles the rest.
|
||||||
87
docs/content/features/syntax-highlighting.md
Normal file
87
docs/content/features/syntax-highlighting.md
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
---
|
||||||
|
title: Syntax Highlighting
|
||||||
|
description: Build-time code highlighting with Tree-sitter
|
||||||
|
weight: 3
|
||||||
|
---
|
||||||
|
|
||||||
|
# Syntax Highlighting
|
||||||
|
|
||||||
|
sukr highlights code blocks at build time using Tree-sitter. No client-side JavaScript required.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Use fenced code blocks with a language identifier:
|
||||||
|
|
||||||
|
````markdown
|
||||||
|
```rust
|
||||||
|
fn main() {
|
||||||
|
println!("Hello, world!");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
````
|
||||||
|
|
||||||
|
## Supported Languages
|
||||||
|
|
||||||
|
| Language | Identifier |
|
||||||
|
| ---------- | --------------------- |
|
||||||
|
| Rust | `rust`, `rs` |
|
||||||
|
| Python | `python`, `py` |
|
||||||
|
| JavaScript | `javascript`, `js` |
|
||||||
|
| TypeScript | `typescript`, `ts` |
|
||||||
|
| Go | `go`, `golang` |
|
||||||
|
| Bash | `bash`, `sh`, `shell` |
|
||||||
|
| Nix | `nix` |
|
||||||
|
| TOML | `toml` |
|
||||||
|
| YAML | `yaml`, `yml` |
|
||||||
|
| JSON | `json` |
|
||||||
|
| HTML | `html` |
|
||||||
|
| CSS | `css` |
|
||||||
|
| Markdown | `markdown`, `md` |
|
||||||
|
|
||||||
|
## How It Works
|
||||||
|
|
||||||
|
1. During Markdown parsing, code blocks are intercepted
|
||||||
|
2. Tree-sitter parses the code and generates a syntax tree
|
||||||
|
3. Spans are generated with semantic CSS classes
|
||||||
|
4. All work happens at build time
|
||||||
|
|
||||||
|
## Styling
|
||||||
|
|
||||||
|
Highlighted code uses semantic CSS classes:
|
||||||
|
|
||||||
|
```css
|
||||||
|
.keyword {
|
||||||
|
color: #ff79c6;
|
||||||
|
}
|
||||||
|
.string {
|
||||||
|
color: #f1fa8c;
|
||||||
|
}
|
||||||
|
.function {
|
||||||
|
color: #50fa7b;
|
||||||
|
}
|
||||||
|
.comment {
|
||||||
|
color: #6272a4;
|
||||||
|
}
|
||||||
|
.number {
|
||||||
|
color: #bd93f9;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The exact classes depend on the language grammar.
|
||||||
|
|
||||||
|
## Nix Language Support
|
||||||
|
|
||||||
|
sukr includes full Nix highlighting with injection support. Bash code inside `buildPhase` and similar attributes is highlighted correctly:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
stdenv.mkDerivation {
|
||||||
|
buildPhase = ''
|
||||||
|
echo "Building..."
|
||||||
|
make -j$NIX_BUILD_CORES
|
||||||
|
'';
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Fallback
|
||||||
|
|
||||||
|
Unknown languages fall back to plain `<code>` blocks without highlighting.
|
||||||
91
docs/content/features/templates.md
Normal file
91
docs/content/features/templates.md
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
---
|
||||||
|
title: Tera Templates
|
||||||
|
description: Customizable templates without recompilation
|
||||||
|
weight: 1
|
||||||
|
---
|
||||||
|
|
||||||
|
# Tera Templates
|
||||||
|
|
||||||
|
sukr uses [Tera](https://tera.netlify.app/), a Jinja2-like templating engine. Templates are loaded at runtime, so you can modify them without recompiling sukr.
|
||||||
|
|
||||||
|
## Template Directory Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
templates/
|
||||||
|
├── base.html # Shared layout (required)
|
||||||
|
├── page.html # Standalone pages
|
||||||
|
├── homepage.html # Site homepage
|
||||||
|
├── section/
|
||||||
|
│ ├── default.html # Fallback section index
|
||||||
|
│ ├── blog.html # Blog section index
|
||||||
|
│ └── projects.html # Projects section index
|
||||||
|
└── content/
|
||||||
|
├── default.html # Fallback content page
|
||||||
|
└── post.html # Blog post
|
||||||
|
```
|
||||||
|
|
||||||
|
## Template Inheritance
|
||||||
|
|
||||||
|
All templates extend `base.html`:
|
||||||
|
|
||||||
|
```html
|
||||||
|
{% extends "base.html" %} {% block content %}
|
||||||
|
<article>
|
||||||
|
<h1>{{ page.title }}</h1>
|
||||||
|
{{ content | safe }}
|
||||||
|
</article>
|
||||||
|
{% endblock content %}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Available Context Variables
|
||||||
|
|
||||||
|
### All Templates
|
||||||
|
|
||||||
|
| Variable | Description |
|
||||||
|
| --------------- | ------------------------------- |
|
||||||
|
| `config.title` | Site title |
|
||||||
|
| `config.author` | Site author |
|
||||||
|
| `nav` | Navigation items |
|
||||||
|
| `page_path` | Current page path |
|
||||||
|
| `prefix` | Relative path prefix for assets |
|
||||||
|
| `base_url` | Canonical base URL |
|
||||||
|
| `title` | Current page title |
|
||||||
|
|
||||||
|
### Page Templates
|
||||||
|
|
||||||
|
| Variable | Description |
|
||||||
|
| ------------------ | --------------------- |
|
||||||
|
| `page.title` | Page title |
|
||||||
|
| `page.description` | Page description |
|
||||||
|
| `content` | Rendered HTML content |
|
||||||
|
|
||||||
|
### Section Templates
|
||||||
|
|
||||||
|
| Variable | Description |
|
||||||
|
| --------------------- | --------------------------------- |
|
||||||
|
| `section.title` | Section title |
|
||||||
|
| `section.description` | Section description |
|
||||||
|
| `items` | Array of content items in section |
|
||||||
|
|
||||||
|
### Content Item Fields (in `items`)
|
||||||
|
|
||||||
|
| Variable | Description |
|
||||||
|
| ------------------ | ------------------- |
|
||||||
|
| `item.title` | Content title |
|
||||||
|
| `item.description` | Content description |
|
||||||
|
| `item.date` | Publication date |
|
||||||
|
| `item.path` | URL path |
|
||||||
|
| `item.slug` | URL slug |
|
||||||
|
|
||||||
|
## Template Override
|
||||||
|
|
||||||
|
Set `template` in frontmatter to use a custom template:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
---
|
||||||
|
title: Special Page
|
||||||
|
template: special
|
||||||
|
---
|
||||||
|
```
|
||||||
|
|
||||||
|
This uses `templates/page/special.html` instead of the default.
|
||||||
77
docs/content/getting-started.md
Normal file
77
docs/content/getting-started.md
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
---
|
||||||
|
title: Getting Started
|
||||||
|
description: Install sukr and build your first site
|
||||||
|
weight: 1
|
||||||
|
---
|
||||||
|
|
||||||
|
# Getting Started
|
||||||
|
|
||||||
|
This guide walks you through installing sukr and creating your first static site.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
### From source (recommended)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/nrdxp/sukr
|
||||||
|
cd sukr
|
||||||
|
cargo install --path .
|
||||||
|
```
|
||||||
|
|
||||||
|
### With Nix
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nix build github:nrdxp/sukr
|
||||||
|
./result/bin/sukr --help
|
||||||
|
```
|
||||||
|
|
||||||
|
## Create Your First Site
|
||||||
|
|
||||||
|
### 1. Create directory structure
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mkdir my-site && cd my-site
|
||||||
|
mkdir -p content templates static
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Create configuration
|
||||||
|
|
||||||
|
Create `site.toml`:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
title = "My Site"
|
||||||
|
author = "Your Name"
|
||||||
|
base_url = "https://example.com"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Create homepage
|
||||||
|
|
||||||
|
Create `content/_index.md`:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
---
|
||||||
|
title: Welcome
|
||||||
|
description: My awesome site
|
||||||
|
---
|
||||||
|
|
||||||
|
# Hello, World!
|
||||||
|
|
||||||
|
This is my site built with sukr.
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Create templates
|
||||||
|
|
||||||
|
Copy the default templates from the sukr repository, or create your own Tera templates.
|
||||||
|
|
||||||
|
### 5. Build
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sukr
|
||||||
|
```
|
||||||
|
|
||||||
|
Your site is now in `public/`.
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
|
||||||
|
- Learn about [Configuration](configuration.html)
|
||||||
|
- Explore [Features](features/index.html)
|
||||||
9
docs/site.toml
Normal file
9
docs/site.toml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
author = "Sukr Contributors"
|
||||||
|
base_url = "https://sukr.io"
|
||||||
|
title = "Sukr Documentation"
|
||||||
|
|
||||||
|
[paths]
|
||||||
|
content = "content"
|
||||||
|
output = "public"
|
||||||
|
static = "static"
|
||||||
|
templates = "templates"
|
||||||
229
docs/static/style.css
vendored
Normal file
229
docs/static/style.css
vendored
Normal file
@@ -0,0 +1,229 @@
|
|||||||
|
/* sukr docs — clean documentation theme */
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--bg: #0d1117;
|
||||||
|
--bg-sidebar: #161b22;
|
||||||
|
--fg: #c9d1d9;
|
||||||
|
--fg-muted: #8b949e;
|
||||||
|
--accent: #58a6ff;
|
||||||
|
--accent-hover: #79b8ff;
|
||||||
|
--border: #30363d;
|
||||||
|
--code-bg: #1f2428;
|
||||||
|
--success: #3fb950;
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
|
||||||
|
background: var(--bg);
|
||||||
|
color: var(--fg);
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 260px 1fr;
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sidebar */
|
||||||
|
.sidebar {
|
||||||
|
background: var(--bg-sidebar);
|
||||||
|
border-right: 1px solid var(--border);
|
||||||
|
padding: 2rem 1.5rem;
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
height: 100vh;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar header {
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar .logo {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--accent);
|
||||||
|
text-decoration: none;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar .tagline {
|
||||||
|
display: block;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
color: var(--fg-muted);
|
||||||
|
margin-top: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar nav {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar nav a {
|
||||||
|
color: var(--fg);
|
||||||
|
text-decoration: none;
|
||||||
|
padding: 0.5rem 0.75rem;
|
||||||
|
border-radius: 6px;
|
||||||
|
transition: background 0.15s, color 0.15s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar nav a:hover {
|
||||||
|
background: var(--border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar nav a.active {
|
||||||
|
background: var(--accent);
|
||||||
|
color: var(--bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Main content */
|
||||||
|
main {
|
||||||
|
padding: 3rem 4rem;
|
||||||
|
max-width: 900px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 2.25rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
color: var(--fg);
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
margin: 2rem 0 1rem;
|
||||||
|
padding-bottom: 0.5rem;
|
||||||
|
border-bottom: 1px solid var(--border);
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
font-size: 1.25rem;
|
||||||
|
margin: 1.5rem 0 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lead {
|
||||||
|
font-size: 1.125rem;
|
||||||
|
color: var(--fg-muted);
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
color: var(--accent-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Code */
|
||||||
|
code {
|
||||||
|
font-family: ui-monospace, "Cascadia Code", "Source Code Pro", Menlo, monospace;
|
||||||
|
font-size: 0.875em;
|
||||||
|
background: var(--code-bg);
|
||||||
|
padding: 0.2em 0.4em;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
background: var(--code-bg);
|
||||||
|
padding: 1rem;
|
||||||
|
border-radius: 8px;
|
||||||
|
overflow-x: auto;
|
||||||
|
margin: 1rem 0;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
}
|
||||||
|
|
||||||
|
pre code {
|
||||||
|
background: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Section navigation */
|
||||||
|
.section-nav {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.75rem;
|
||||||
|
margin-top: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-link {
|
||||||
|
display: block;
|
||||||
|
padding: 1rem;
|
||||||
|
background: var(--bg-sidebar);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 8px;
|
||||||
|
text-decoration: none;
|
||||||
|
transition: border-color 0.15s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-link:hover {
|
||||||
|
border-color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-link strong {
|
||||||
|
display: block;
|
||||||
|
color: var(--fg);
|
||||||
|
margin-bottom: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-link span {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
color: var(--fg-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Lists */
|
||||||
|
ul, ol {
|
||||||
|
margin: 1rem 0;
|
||||||
|
padding-left: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
li {
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tables */
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
margin: 1rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
th, td {
|
||||||
|
padding: 0.75rem;
|
||||||
|
text-align: left;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
}
|
||||||
|
|
||||||
|
th {
|
||||||
|
background: var(--bg-sidebar);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responsive */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
body {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar {
|
||||||
|
position: static;
|
||||||
|
height: auto;
|
||||||
|
border-right: none;
|
||||||
|
border-bottom: 1px solid var(--border);
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
padding: 2rem 1.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
37
docs/templates/base.html
vendored
Normal file
37
docs/templates/base.html
vendored
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<title>{{ title }} | {{ config.title }}</title>
|
||||||
|
<link rel="canonical" href="{{ base_url }}{{ page_path }}" />
|
||||||
|
<link rel="stylesheet" href="{{ prefix }}/style.css" />
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<aside class="sidebar">
|
||||||
|
<header>
|
||||||
|
<a href="{{ prefix }}/index.html" class="logo">sukr</a>
|
||||||
|
<span class="tagline">suckless static sites</span>
|
||||||
|
</header>
|
||||||
|
<nav>
|
||||||
|
{% for item in nav %}
|
||||||
|
<a
|
||||||
|
href="{{ prefix }}{{ item.path }}"
|
||||||
|
{%
|
||||||
|
if
|
||||||
|
page_path=""
|
||||||
|
="item.path"
|
||||||
|
%}
|
||||||
|
class="active"
|
||||||
|
{%
|
||||||
|
endif
|
||||||
|
%}
|
||||||
|
>{{ item.label }}</a
|
||||||
|
>
|
||||||
|
{% endfor %}
|
||||||
|
</nav>
|
||||||
|
</aside>
|
||||||
|
<main>{% block content %}{% endblock content %}</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
9
docs/templates/content/default.html
vendored
Normal file
9
docs/templates/content/default.html
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{% extends "base.html" %} {% block content %}
|
||||||
|
<article class="doc-page">
|
||||||
|
<h1>{{ page.title }}</h1>
|
||||||
|
{% if page.description %}
|
||||||
|
<p class="lead">{{ page.description }}</p>
|
||||||
|
{% endif %}
|
||||||
|
<section class="content">{{ content | safe }}</section>
|
||||||
|
</article>
|
||||||
|
{% endblock content %}
|
||||||
9
docs/templates/homepage.html
vendored
Normal file
9
docs/templates/homepage.html
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{% extends "base.html" %} {% block content %}
|
||||||
|
<article class="homepage">
|
||||||
|
<h1>{{ page.title }}</h1>
|
||||||
|
{% if page.description %}
|
||||||
|
<p class="lead">{{ page.description }}</p>
|
||||||
|
{% endif %}
|
||||||
|
<section class="content">{{ content | safe }}</section>
|
||||||
|
</article>
|
||||||
|
{% endblock content %}
|
||||||
9
docs/templates/page.html
vendored
Normal file
9
docs/templates/page.html
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{% extends "base.html" %} {% block content %}
|
||||||
|
<article class="page">
|
||||||
|
<h1>{{ page.title }}</h1>
|
||||||
|
{% if page.description %}
|
||||||
|
<p class="lead">{{ page.description }}</p>
|
||||||
|
{% endif %}
|
||||||
|
<section class="content">{{ content | safe }}</section>
|
||||||
|
</article>
|
||||||
|
{% endblock content %}
|
||||||
18
docs/templates/section/default.html
vendored
Normal file
18
docs/templates/section/default.html
vendored
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
{% extends "base.html" %} {% block content %}
|
||||||
|
<article class="section-index">
|
||||||
|
<h1>{{ section.title }}</h1>
|
||||||
|
{% if section.description %}
|
||||||
|
<p class="lead">{{ section.description }}</p>
|
||||||
|
{% endif %}
|
||||||
|
<nav class="section-nav">
|
||||||
|
{% for item in items %}
|
||||||
|
<a href="{{ prefix }}{{ item.path }}" class="section-link">
|
||||||
|
<strong>{{ item.frontmatter.title }}</strong>
|
||||||
|
{% if item.frontmatter.description %}
|
||||||
|
<span>{{ item.frontmatter.description }}</span>
|
||||||
|
{% endif %}
|
||||||
|
</a>
|
||||||
|
{% endfor %}
|
||||||
|
</nav>
|
||||||
|
</article>
|
||||||
|
{% endblock content %}
|
||||||
18
docs/templates/section/features.html
vendored
Normal file
18
docs/templates/section/features.html
vendored
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
{% extends "base.html" %} {% block content %}
|
||||||
|
<article class="section-index">
|
||||||
|
<h1>{{ section.title }}</h1>
|
||||||
|
{% if section.description %}
|
||||||
|
<p class="lead">{{ section.description }}</p>
|
||||||
|
{% endif %}
|
||||||
|
<nav class="section-nav">
|
||||||
|
{% for item in items %}
|
||||||
|
<a href="{{ prefix }}{{ item.path }}" class="section-link">
|
||||||
|
<strong>{{ item.frontmatter.title }}</strong>
|
||||||
|
{% if item.frontmatter.description %}
|
||||||
|
<span>{{ item.frontmatter.description }}</span>
|
||||||
|
{% endif %}
|
||||||
|
</a>
|
||||||
|
{% endfor %}
|
||||||
|
</nav>
|
||||||
|
</article>
|
||||||
|
{% endblock content %}
|
||||||
12
src/error.rs
12
src/error.rs
@@ -42,12 +42,16 @@ pub enum Error {
|
|||||||
Config { path: PathBuf, message: String },
|
Config { path: PathBuf, message: String },
|
||||||
|
|
||||||
/// Failed to load templates.
|
/// Failed to load templates.
|
||||||
#[error("failed to load templates: {message}")]
|
#[error("failed to load templates: {0}")]
|
||||||
TemplateLoad { message: String },
|
TemplateLoad(#[source] tera::Error),
|
||||||
|
|
||||||
/// Failed to render template.
|
/// Failed to render template.
|
||||||
#[error("failed to render template '{template}': {message}")]
|
#[error("failed to render template '{template}'")]
|
||||||
TemplateRender { template: String, message: String },
|
TemplateRender {
|
||||||
|
template: String,
|
||||||
|
#[source]
|
||||||
|
source: tera::Error,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Result type alias for compiler operations.
|
/// Result type alias for compiler operations.
|
||||||
|
|||||||
@@ -35,6 +35,12 @@ fn main() {
|
|||||||
Ok(Some(config_path)) => {
|
Ok(Some(config_path)) => {
|
||||||
if let Err(e) = run(&config_path) {
|
if let Err(e) = run(&config_path) {
|
||||||
eprintln!("error: {e}");
|
eprintln!("error: {e}");
|
||||||
|
// Print full error chain
|
||||||
|
let mut source = std::error::Error::source(&e);
|
||||||
|
while let Some(cause) = source {
|
||||||
|
eprintln!(" caused by: {cause}");
|
||||||
|
source = std::error::Error::source(cause);
|
||||||
|
}
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,9 +18,7 @@ impl TemplateEngine {
|
|||||||
/// Load templates from a directory (glob pattern: `templates/**/*`).
|
/// Load templates from a directory (glob pattern: `templates/**/*`).
|
||||||
pub fn new(template_dir: &Path) -> Result<Self> {
|
pub fn new(template_dir: &Path) -> Result<Self> {
|
||||||
let pattern = template_dir.join("**/*").display().to_string();
|
let pattern = template_dir.join("**/*").display().to_string();
|
||||||
let tera = Tera::new(&pattern).map_err(|e| Error::TemplateLoad {
|
let tera = Tera::new(&pattern).map_err(Error::TemplateLoad)?;
|
||||||
message: e.to_string(),
|
|
||||||
})?;
|
|
||||||
Ok(Self { tera })
|
Ok(Self { tera })
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,7 +28,7 @@ impl TemplateEngine {
|
|||||||
.render(template_name, context)
|
.render(template_name, context)
|
||||||
.map_err(|e| Error::TemplateRender {
|
.map_err(|e| Error::TemplateRender {
|
||||||
template: template_name.to_string(),
|
template: template_name.to_string(),
|
||||||
message: e.to_string(),
|
source: e,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user