559 lines
12 KiB
Markdown
559 lines
12 KiB
Markdown
---
|
|
version: "1.0"
|
|
date: "2025-01-15"
|
|
author: "DWS Foldsite Team"
|
|
title: "Template Discovery System"
|
|
description: "Understanding how Foldsite finds and chooses templates"
|
|
summary: "Deep dive into Foldsite's hierarchical template discovery algorithm - learn exactly how templates are matched to content."
|
|
quick_tips:
|
|
- "Templates are searched from most specific to most general"
|
|
- "First match wins - more specific templates override general ones"
|
|
- "Templates cascade down through directory hierarchies"
|
|
---
|
|
|
|
# Template Discovery System
|
|
|
|
Understanding how Foldsite discovers and chooses templates is key to building sophisticated sites. This guide explains the complete template resolution algorithm.
|
|
|
|
## The Discovery Algorithm
|
|
|
|
When Foldsite renders a page, it follows a **hierarchical search pattern** to find the best template.
|
|
|
|
### Core Principle
|
|
|
|
**Specificity wins.** More specific templates override general ones.
|
|
|
|
Think of it like CSS specificity:
|
|
```
|
|
#specific-id (most specific)
|
|
.class-name
|
|
element (least specific)
|
|
```
|
|
|
|
In Foldsite:
|
|
```
|
|
my-post.html (most specific - exact file)
|
|
__file.md.html (category + extension)
|
|
__file.document.html (category only)
|
|
__file.html (least specific - any file)
|
|
```
|
|
|
|
## File Type Detection
|
|
|
|
Before template discovery, Foldsite determines the content type:
|
|
|
|
### For Files
|
|
|
|
**Step 1:** Extract extension
|
|
```
|
|
blog/my-post.md → extension: "md"
|
|
```
|
|
|
|
**Step 2:** Map to categories
|
|
```
|
|
"md" → categories: ["document", "md"]
|
|
```
|
|
|
|
**Category mapping:**
|
|
```python
|
|
GENERIC_FILE_MAPPING = {
|
|
"document": ["md", "txt", "html"],
|
|
"image": ["png", "jpg", "jpeg", "gif", "svg"],
|
|
"multimedia": ["mp4", "mp3", "webm"],
|
|
"other": [...] # Everything else
|
|
}
|
|
```
|
|
|
|
### For Folders
|
|
|
|
**Step 1:** Count file types in folder
|
|
```
|
|
photos/vacation/
|
|
IMG_001.jpg → jpg: 1
|
|
IMG_002.jpg → jpg: 2
|
|
IMG_003.jpg → jpg: 3
|
|
notes.txt → txt: 1
|
|
```
|
|
|
|
**Step 2:** Most common type wins
|
|
```
|
|
Most common: jpg (3 files)
|
|
→ categories: ["image", "jpg"]
|
|
```
|
|
|
|
## Template Search Order
|
|
|
|
### For Files: `blog/my-post.md`
|
|
|
|
**Given:**
|
|
- Path: `blog/my-post.md`
|
|
- Type: `file`
|
|
- Categories: `["document", "md"]`
|
|
- Extension: `md`
|
|
|
|
**Search order:**
|
|
|
|
1. **Exact file match** in current directory
|
|
```
|
|
templates/blog/my-post.html
|
|
```
|
|
|
|
2. **Type + extension** in current directory
|
|
```
|
|
templates/blog/__file.md.html
|
|
```
|
|
|
|
3. **Type + category** in current directory (most specific category first)
|
|
```
|
|
templates/blog/__file.document.html
|
|
```
|
|
|
|
4. **Generic type** in current directory
|
|
```
|
|
templates/blog/__file.html
|
|
```
|
|
|
|
5. **Move up one directory**, repeat steps 2-4
|
|
```
|
|
templates/__file.md.html
|
|
templates/__file.document.html
|
|
templates/__file.html
|
|
```
|
|
|
|
6. **Default template** (if exists)
|
|
```
|
|
templates/default.html
|
|
```
|
|
|
|
**First match wins!**
|
|
|
|
### For Folders: `photos/vacation/`
|
|
|
|
**Given:**
|
|
- Path: `photos/vacation/`
|
|
- Type: `folder`
|
|
- Categories: `["image"]` (most files are images)
|
|
|
|
**Search order:**
|
|
|
|
1. **Exact folder match**
|
|
```
|
|
templates/photos/vacation/__folder.html
|
|
```
|
|
|
|
2. **Type + category** in current directory
|
|
```
|
|
templates/photos/__folder.image.html
|
|
```
|
|
|
|
3. **Generic folder** in current directory
|
|
```
|
|
templates/photos/__folder.html
|
|
```
|
|
|
|
4. **Move up one directory**, repeat steps 2-3
|
|
```
|
|
templates/__folder.image.html
|
|
templates/__folder.html
|
|
```
|
|
|
|
5. **Default template**
|
|
```
|
|
templates/default.html
|
|
```
|
|
|
|
## Practical Examples
|
|
|
|
### Example 1: Blog Post
|
|
|
|
**Content:** `content/blog/posts/2024-my-post.md`
|
|
|
|
**Template search:**
|
|
```
|
|
1. templates/blog/posts/2024-my-post.html ✗ Not found
|
|
2. templates/blog/posts/__file.md.html ✗ Not found
|
|
3. templates/blog/posts/__file.document.html ✗ Not found
|
|
4. templates/blog/posts/__file.html ✗ Not found
|
|
5. templates/blog/__file.md.html ✓ FOUND!
|
|
```
|
|
|
|
**Result:** Uses `templates/blog/__file.md.html`
|
|
|
|
**Why:** Most specific template found in the hierarchy.
|
|
|
|
### Example 2: Homepage
|
|
|
|
**Content:** `content/index.md`
|
|
|
|
**Template search:**
|
|
```
|
|
1. templates/index.html ✓ FOUND!
|
|
```
|
|
|
|
**Result:** Uses `templates/index.html`
|
|
|
|
**Why:** Exact file match (most specific possible).
|
|
|
|
### Example 3: Photo Gallery
|
|
|
|
**Content:** `content/photos/vacation/` (folder with 50 JPG files)
|
|
|
|
**Categories:** `["image"]`
|
|
|
|
**Template search:**
|
|
```
|
|
1. templates/photos/vacation/__folder.html ✗ Not found
|
|
2. templates/photos/__folder.image.html ✓ FOUND!
|
|
```
|
|
|
|
**Result:** Uses `templates/photos/__folder.image.html`
|
|
|
|
**Why:** Type + category match in parent directory.
|
|
|
|
### Example 4: Deep Nesting
|
|
|
|
**Content:** `content/docs/guides/advanced/testing.md`
|
|
|
|
**Template search:**
|
|
```
|
|
1. templates/docs/guides/advanced/testing.html ✗ Not found
|
|
2. templates/docs/guides/advanced/__file.md.html ✗ Not found
|
|
3. templates/docs/guides/__file.md.html ✗ Not found
|
|
4. templates/docs/__file.md.html ✓ FOUND!
|
|
```
|
|
|
|
**Result:** Uses `templates/docs/__file.md.html`
|
|
|
|
**Why:** Climbs directory tree until finding a match.
|
|
|
|
## Template Cascade
|
|
|
|
Templates **cascade down** through directories:
|
|
|
|
```
|
|
templates/
|
|
├── __file.md.html ← Default for ALL markdown
|
|
└── blog/
|
|
└── __file.md.html ← Override for blog/ only
|
|
```
|
|
|
|
**Rendering `content/about.md`:**
|
|
- Uses `templates/__file.md.html`
|
|
|
|
**Rendering `content/blog/post.md`:**
|
|
- Uses `templates/blog/__file.md.html` (more specific)
|
|
|
|
### Multi-Level Cascade
|
|
|
|
```
|
|
templates/
|
|
├── __file.md.html ← Level 1: Site default
|
|
├── blog/
|
|
│ └── __file.md.html ← Level 2: Blog default
|
|
└── blog/
|
|
└── tutorials/
|
|
└── __file.md.html ← Level 3: Tutorial-specific
|
|
```
|
|
|
|
**Effect:**
|
|
- `content/about.md` → Level 1 template
|
|
- `content/blog/news.md` → Level 2 template
|
|
- `content/blog/tutorials/python.md` → Level 3 template
|
|
|
|
## Category Priority
|
|
|
|
Files can belong to multiple categories. **More specific categories come first.**
|
|
|
|
### Category Hierarchy
|
|
|
|
For `my-post.md`:
|
|
```
|
|
Categories: ["md", "document"]
|
|
↑ ↑
|
|
specific general
|
|
```
|
|
|
|
**Search order:**
|
|
```
|
|
1. __file.md.html ← Most specific
|
|
2. __file.document.html ← More general
|
|
3. __file.html ← Most general
|
|
```
|
|
|
|
## Debug Mode
|
|
|
|
Enable debug mode to see template discovery in action:
|
|
|
|
```toml
|
|
# config.toml
|
|
[server]
|
|
debug = true
|
|
```
|
|
|
|
**Console output when visiting a page:**
|
|
```
|
|
[DEBUG] Template discovery for: blog/my-post.md
|
|
[DEBUG] Content type: file
|
|
[DEBUG] Categories: ['md', 'document']
|
|
[DEBUG] Extension: md
|
|
[DEBUG]
|
|
[DEBUG] Searching templates:
|
|
[DEBUG] 1. templates/blog/my-post.html - NOT FOUND
|
|
[DEBUG] 2. templates/blog/__file.md.html - FOUND!
|
|
[DEBUG]
|
|
[DEBUG] Using template: templates/blog/__file.md.html
|
|
```
|
|
|
|
### Debug Test Page
|
|
|
|
Create a test page to understand discovery:
|
|
|
|
```markdown
|
|
---
|
|
title: "Template Discovery Test"
|
|
---
|
|
|
|
# Current Template Info
|
|
|
|
**Current Path:** {{ currentPath }}
|
|
|
|
**Styles Loaded:**
|
|
{% for style in styles %}
|
|
- {{ style }}
|
|
{% endfor %}
|
|
|
|
**Metadata:**
|
|
{{ metadata }}
|
|
```
|
|
|
|
Visit this page and check the console to see which template was used.
|
|
|
|
## Edge Cases
|
|
|
|
### No Template Found
|
|
|
|
If no template matches:
|
|
|
|
**For files:**
|
|
- Foldsite serves the file directly (download)
|
|
- Useful for PDFs, images, etc.
|
|
|
|
**For folders:**
|
|
- Returns 404 error
|
|
- Need at least one `__folder.html` template
|
|
|
|
### Hidden Files
|
|
|
|
Files/folders starting with `___` are ignored:
|
|
|
|
```
|
|
content/
|
|
├── post.md ✓ Will be rendered
|
|
└── ___draft.md ✗ Ignored completely
|
|
```
|
|
|
|
No template search is performed for hidden content.
|
|
|
|
### Multiple Extensions
|
|
|
|
Only the last extension is considered:
|
|
|
|
```
|
|
my-file.tar.gz → extension: "gz"
|
|
```
|
|
|
|
Not: `tar.gz` or `tar`
|
|
|
|
## Style Discovery
|
|
|
|
Styles follow **similar logic** to templates but accumulate instead of first-match:
|
|
|
|
### Style Search for `blog/post.md`
|
|
|
|
**All matching styles are loaded (in order):**
|
|
```
|
|
1. /base.css ← Always loaded
|
|
2. /blog/__file.md.css ← If exists
|
|
3. /blog/__file.document.css ← If exists
|
|
4. /__file.md.css ← If exists (from root)
|
|
5. /blog/post.md.css ← If exists (specific file)
|
|
```
|
|
|
|
**All found styles are included** (not just first match).
|
|
|
|
**Rendering:**
|
|
```html
|
|
<link rel="stylesheet" href="/styles/base.css">
|
|
<link rel="stylesheet" href="/styles/blog/__file.md.css">
|
|
<link rel="stylesheet" href="/styles/__file.md.css">
|
|
```
|
|
|
|
## Optimization Tips
|
|
|
|
### 1. Keep Templates at Appropriate Levels
|
|
|
|
**Good:**
|
|
```
|
|
templates/
|
|
├── __file.md.html ← General template
|
|
└── blog/
|
|
└── __file.md.html ← Blog-specific customization
|
|
```
|
|
|
|
**Avoid:**
|
|
```
|
|
templates/
|
|
├── post1.html
|
|
├── post2.html
|
|
├── post3.html ← Repetitive!
|
|
└── ...
|
|
```
|
|
|
|
### 2. Use Categories Effectively
|
|
|
|
**Good:**
|
|
```
|
|
templates/
|
|
├── __file.md.html ← For markdown
|
|
├── __folder.image.html ← For galleries
|
|
└── __file.document.html ← For all documents
|
|
```
|
|
|
|
**Avoid:**
|
|
```
|
|
templates/
|
|
├── __file.html ← Too generic
|
|
└── (nothing else)
|
|
```
|
|
|
|
### 3. Understand the Cascade
|
|
|
|
Place templates where they make sense:
|
|
|
|
**Project structure:**
|
|
```
|
|
content/
|
|
├── blog/ → Frequent posts, custom layout
|
|
├── docs/ → Technical docs, different layout
|
|
└── about.md → One-off pages
|
|
```
|
|
|
|
**Template structure:**
|
|
```
|
|
templates/
|
|
├── __file.md.html ← Default for one-off pages
|
|
├── blog/
|
|
│ └── __file.md.html ← Blog-specific
|
|
└── docs/
|
|
└── __file.md.html ← Docs-specific
|
|
```
|
|
|
|
## Common Patterns
|
|
|
|
### Pattern: Section-Specific Layouts
|
|
|
|
Different sections need different layouts:
|
|
|
|
```
|
|
templates/
|
|
├── base.html ← Shared wrapper
|
|
├── __file.md.html ← Default content
|
|
├── blog/
|
|
│ ├── __file.md.html ← Blog posts
|
|
│ └── __folder.md.html ← Blog index
|
|
├── portfolio/
|
|
│ ├── __file.md.html ← Project pages
|
|
│ └── __folder.md.html ← Portfolio grid
|
|
└── docs/
|
|
├── __file.md.html ← Documentation pages
|
|
└── __folder.md.html ← Docs index
|
|
```
|
|
|
|
### Pattern: Gradual Specialization
|
|
|
|
Start general, add specificity as needed:
|
|
|
|
**Phase 1: MVP**
|
|
```
|
|
templates/
|
|
├── base.html
|
|
└── __file.md.html
|
|
```
|
|
|
|
**Phase 2: Add Gallery**
|
|
```
|
|
templates/
|
|
├── base.html
|
|
├── __file.md.html
|
|
└── __folder.image.html ← New!
|
|
```
|
|
|
|
**Phase 3: Custom Blog**
|
|
```
|
|
templates/
|
|
├── base.html
|
|
├── __file.md.html
|
|
├── __folder.image.html
|
|
└── blog/
|
|
└── __file.md.html ← New!
|
|
```
|
|
|
|
### Pattern: Override Single Page
|
|
|
|
Override one specific page:
|
|
|
|
```
|
|
templates/
|
|
├── __file.md.html ← All markdown
|
|
└── index.html ← Special homepage
|
|
```
|
|
|
|
## Troubleshooting
|
|
|
|
### Template Not Being Used
|
|
|
|
**Check:**
|
|
1. **File name** - Is it exactly right?
|
|
2. **Location** - Is it in the right directory?
|
|
3. **Extension** - `.html`, not `.htm` or `.jinja`
|
|
4. **Debug mode** - What does the console say?
|
|
|
|
**Debug:**
|
|
```bash
|
|
# Enable debug
|
|
[server]
|
|
debug = true
|
|
|
|
# Restart and check console output
|
|
```
|
|
|
|
### Wrong Template Used
|
|
|
|
**Likely cause:** More specific template exists
|
|
|
|
**Example:**
|
|
```
|
|
Want: templates/__file.md.html
|
|
Using: templates/blog/__file.md.html ← More specific!
|
|
```
|
|
|
|
**Solution:** Update the more specific template, or remove it.
|
|
|
|
### Styles Not Loading
|
|
|
|
**Check:**
|
|
1. **Style file exists** in `styles/` directory
|
|
2. **Path matches** template expectations
|
|
3. **Browser dev tools** - Are styles being requested?
|
|
|
|
**Remember:** Unlike templates, **all matching styles load**.
|
|
|
|
## Next Steps
|
|
|
|
- **[Template System Overview](index.md)** - Basics of templates
|
|
- **[Template Helpers](template-helpers.md)** - Functions available in templates
|
|
- **[Recipes](../recipes/)** - Working examples
|
|
- **[Styles Guide](../styles/)** - CSS cascade system
|
|
|
|
Understanding template discovery gives you complete control over your site's presentation. Use it wisely!
|