"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getExample = getExample;
const EXAMPLES = {
manifest_basic: `# Basic manifest.json
A minimal manifest with static pages only:
\`\`\`json
{
"pages": [
{
"path": "/",
"file": "pages/index.html",
"title": "Home",
"editable": true
},
{
"path": "/about",
"file": "pages/about.html",
"title": "About Us",
"editable": true
},
{
"path": "/contact",
"file": "pages/contact.html",
"title": "Contact",
"editable": true
}
],
"defaultHeadHtml": "<link href='https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap' rel='stylesheet'>"
}
\`\`\`
**Key points:**
- Each page has a unique \`path\` (the URL)
- \`file\` points to the HTML file in pages/ folder
- \`editable: true\` allows inline editing in the CMS
- \`defaultHeadHtml\` is injected into all pages`,
manifest_custom_paths: `# manifest.json with CMS Collections
Configure templates for your CMS collections:
\`\`\`json
{
"pages": [
{
"path": "/",
"file": "pages/index.html",
"title": "Home",
"editable": true
},
{
"path": "/about",
"file": "pages/about.html",
"title": "About",
"editable": true
}
],
"cmsTemplates": {
"postsIndex": "templates/posts_index.html",
"postsIndexPath": "/blog",
"postsDetail": "templates/posts_detail.html",
"postsDetailPath": "/blog",
"teamIndex": "templates/team.html",
"teamIndexPath": "/team",
"servicesIndex": "templates/services.html",
"servicesIndexPath": "/services",
"servicesDetail": "templates/service-detail.html",
"servicesDetailPath": "/services"
}
}
\`\`\`
**Path Configuration Pattern:**
- \`{collectionSlug}Index\` - Template file for listing page
- \`{collectionSlug}IndexPath\` - URL for listing page
- \`{collectionSlug}Detail\` - Template file for detail page
- \`{collectionSlug}DetailPath\` - URL base for detail pages`,
manifest_minimal_with_ui: `# Minimal manifest.json (Configure Templates via Settings UI)
When you want to upload pages first and configure CMS templates later via the Settings UI:
\`\`\`json
{
"pages": [
{
"path": "/",
"file": "pages/index.html",
"title": "Home",
"editable": true
},
{
"path": "/about",
"file": "pages/about.html",
"title": "About Us",
"editable": true
},
{
"path": "/blog",
"file": "pages/blog.html",
"title": "Blog",
"editable": true
},
{
"path": "/blog-post",
"file": "pages/blog-post.html",
"title": "Blog Post Template",
"editable": true
}
]
}
\`\`\`
**After uploading this package:**
1. Go to Dashboard → Settings → CMS Templates
2. Configure template mappings for your collections
3. Set URL paths
**Benefits of this approach:**
- Faster initial upload - no need to configure cmsTemplates in JSON
- Visual UI makes it easier to understand the configuration
- Can change template mappings without re-uploading the package`,
blog_index_template: `# Blog/Posts Index Template
Lists all posts with featured section (using a "posts" collection):
\`\`\`html
<main class="blog-page">
<header class="page-header">
<h1 data-edit-key="blog-page-title">Our Blog</h1>
<p data-edit-key="blog-page-intro">Latest articles and insights</p>
</header>
<!-- Featured Post (first featured article) -->
{{#each posts featured=true limit=1}}
<article class="featured-post">
{{#if image}}
<img src="{{image}}" alt="{{name}}" class="featured-image">
{{/if}}
<div class="featured-content">
<h2><a href="{{url}}">{{name}}</a></h2>
<p class="excerpt">{{summary}}</p>
<div class="meta">
{{#if author}}
<span class="author">By {{author.name}}</span>
{{/if}}
<time>{{publishedAt}}</time>
</div>
</div>
</article>
{{/each}}
<!-- All Posts Grid -->
<div class="posts-grid">
{{#each posts limit=12 sort="publishedAt" order="desc"}}
<article class="post-card">
{{#if thumbnail}}
<img src="{{thumbnail}}" alt="{{name}}">
{{else}}
{{#if image}}
<img src="{{image}}" alt="{{name}}">
{{/if}}
{{/if}}
<div class="card-content">
<h3><a href="{{url}}">{{name}}</a></h3>
<p>{{summary}}</p>
{{#if author}}
<span class="byline">{{author.name}}</span>
{{/if}}
</div>
</article>
{{/each}}
</div>
{{#unless posts}}
<p>No posts yet. Check back soon!</p>
{{/unless}}
</main>
\`\`\`
**Key patterns:**
- \`{{#each posts featured=true limit=1}}\` for featured post
- \`{{#each posts limit=12 sort="publishedAt" order="desc"}}\` for post grid
- Always wrap images in \`{{#if image}}\`
- Use \`{{url}}\` for links to post detail page`,
blog_post_template: `# Blog Post Detail Template
Single post page with author information (using a "posts" collection with author relation):
\`\`\`html
<article class="blog-post">
{{#if image}}
<img src="{{image}}" alt="{{name}}" class="hero-image">
{{/if}}
<header class="post-header">
<h1>{{name}}</h1>
<div class="post-meta">
{{#if author}}
<div class="author-info">
{{#if author.photo}}
<img src="{{author.photo}}" alt="{{author.name}}" class="author-avatar">
{{/if}}
<a href="{{author.url}}" class="author-name">{{author.name}}</a>
</div>
{{/if}}
<time datetime="{{publishedAt}}">{{publishedAt}}</time>
</div>
</header>
{{#if summary}}
<p class="lead">{{summary}}</p>
{{/if}}
<div class="post-content">
{{{body}}}
</div>
{{#if author}}
<aside class="author-box">
{{#if author.photo}}
<img src="{{author.photo}}" alt="{{author.name}}">
{{/if}}
<div class="author-details">
<h3>About <a href="{{author.url}}">{{author.name}}</a></h3>
{{{author.bio}}}
</div>
</aside>
{{/if}}
</article>
\`\`\`
**IMPORTANT:**
- \`{{{body}}}\` uses TRIPLE braces (contains HTML)
- \`{{{author.bio}}}\` uses TRIPLE braces (contains HTML)
- Everything else uses double braces`,
team_template: `# Team Members Template
Using a "team" collection with name, role, photo, bio, order fields:
\`\`\`html
<section class="team-section">
<header class="section-header">
<h1 data-edit-key="team-page-title">Our Team</h1>
<p data-edit-key="team-page-intro">Meet the people behind our success</p>
</header>
<div class="team-grid">
{{#each team sort="order" order="asc"}}
<div class="team-member">
{{#if photo}}
<img src="{{photo}}" alt="{{name}}" class="member-photo">
{{/if}}
<h3>{{name}}</h3>
{{#if role}}
<p class="role">{{role}}</p>
{{/if}}
{{#if bio}}
<div class="bio">{{{bio}}}</div>
{{/if}}
{{#if email}}
<a href="mailto:{{email}}" class="email">{{email}}</a>
{{/if}}
</div>
{{/each}}
</div>
</section>
\`\`\`
**Key points:**
- \`sort="order" order="asc"\` maintains manual ordering
- \`{{{bio}}}\` uses triple braces for HTML content`,
downloads_template: `# Downloads/Resources Template
Using a "downloads" collection with name, description, fileUrl, category fields:
\`\`\`html
<section class="downloads-section">
<header class="section-header">
<h1 data-edit-key="downloads-title">Resources</h1>
<p data-edit-key="downloads-intro">Download our guides and materials</p>
</header>
<div class="downloads-list">
{{#each downloads sort="order" order="asc"}}
<div class="download-item">
<div class="download-info">
<h3>{{name}}</h3>
{{#if description}}
<p>{{description}}</p>
{{/if}}
{{#if category}}
<span class="category">{{category}}</span>
{{/if}}
</div>
<a href="{{fileUrl}}" class="download-btn" download>
Download
</a>
</div>
{{/each}}
</div>
</section>
\`\`\``,
authors_template: `# Authors Index Template
Using an "authors" collection:
\`\`\`html
<section class="authors-section">
<header class="section-header">
<h1 data-edit-key="authors-title">Our Authors</h1>
</header>
<div class="authors-grid">
{{#each authors}}
<a href="{{url}}" class="author-card">
{{#if photo}}
<img src="{{photo}}" alt="{{name}}" class="author-photo">
{{/if}}
<h3>{{name}}</h3>
{{#if bio}}
<p class="bio-preview">{{{bio}}}</p>
{{/if}}
</a>
{{/each}}
</div>
</section>
\`\`\``,
author_detail_template: `# Author Detail Template
Using an "authors" collection with detail pages:
\`\`\`html
<article class="author-detail">
<header class="author-header">
{{#if photo}}
<img src="{{photo}}" alt="{{name}}" class="author-photo-large">
{{/if}}
<h1>{{name}}</h1>
<div class="social-links">
{{#if email}}
<a href="mailto:{{email}}">Email</a>
{{/if}}
{{#if twitter}}
<a href="{{twitter}}">Twitter</a>
{{/if}}
{{#if linkedin}}
<a href="{{linkedin}}">LinkedIn</a>
{{/if}}
</div>
</header>
{{#if bio}}
<div class="author-bio">{{{bio}}}</div>
{{/if}}
<!-- Show posts by this author (if you have a posts collection with author relation) -->
<section class="author-articles">
<h2>Articles by {{name}}</h2>
<div class="articles-grid">
{{#each posts}}
{{#if (eq author.name ../name)}}
<a href="{{url}}" class="article-card">
{{#if thumbnail}}
<img src="{{thumbnail}}" alt="{{name}}">
{{/if}}
<h3>{{name}}</h3>
<p>{{summary}}</p>
</a>
{{/if}}
{{/each}}
</div>
</section>
</article>
\`\`\`
**Pattern:** \`{{#if (eq author.name ../name)}}\` filters posts by this author`,
custom_collection_template: `# Custom Collection Template
For any user-defined collection. All collections have built-in \`name\`, \`slug\`, \`url\`, and date fields.
## Index Template
\`\`\`html
<!-- Index Template: templates/products_index.html -->
<section class="products-section">
<h1 data-edit-key="products-title">Our Products</h1>
<div class="products-grid">
{{#each products sort="publishedAt" order="desc"}}
<article class="product-card">
{{#if image}}
<img src="{{image}}" alt="{{name}}">
{{/if}}
<h3><a href="{{url}}">{{name}}</a></h3>
{{#if price}}
<span class="price">\${{price}}</span>
{{/if}}
{{#if description}}
<p>{{description}}</p>
{{/if}}
<time class="date">{{publishedAt}}</time>
</article>
{{/each}}
</div>
{{#unless products}}
<p>No products yet.</p>
{{/unless}}
</section>
\`\`\`
## Detail Template
\`\`\`html
<!-- Detail Template: templates/products_detail.html -->
<article class="product-detail">
{{#if image}}
<img src="{{image}}" alt="{{name}}" class="hero-image">
{{/if}}
<h1>{{name}}</h1>
<time class="date">{{publishedAt}}</time>
{{#if price}}
<span class="price">\${{price}}</span>
{{/if}}
{{#if description}}
<div class="description">{{{description}}}</div>
{{/if}}
</article>
\`\`\`
## Built-in Tokens (Available on ALL items)
- \`{{name}}\` - Item name/title
- \`{{slug}}\` - URL slug
- \`{{url}}\` - Full URL to detail page
- \`{{publishedAt}}\` - Publish date
- \`{{createdAt}}\` - Creation date
- \`{{updatedAt}}\` - Last modified date
## In manifest.json
\`\`\`json
{
"cmsTemplates": {
"productsIndex": "templates/products_index.html",
"productsIndexPath": "/shop",
"productsDetail": "templates/products_detail.html",
"productsDetailPath": "/products"
}
}
\`\`\``,
form_handling: `# Form Handling
Forms are automatically captured by the CMS using the \`data-form\` attribute:
\`\`\`html
<form data-form="contact" class="contact-form">
<div class="form-group">
<label for="name">Name</label>
<input type="text" name="name" id="name" required>
</div>
<div class="form-group">
<label for="email">Email</label>
<input type="email" name="email" id="email" required>
</div>
<div class="form-group">
<label for="message">Message</label>
<textarea name="message" id="message" required></textarea>
</div>
<button type="submit">Send Message</button>
</form>
\`\`\`
## Form Handler Script
Add this script to handle form submissions:
\`\`\`javascript
// Handle all forms with data-form attribute
document.querySelectorAll('form[data-form]').forEach(form => {
form.addEventListener('submit', async (e) => {
e.preventDefault();
const submitBtn = form.querySelector('button[type="submit"]');
const originalText = submitBtn?.textContent || 'Submit';
if (submitBtn) {
submitBtn.disabled = true;
submitBtn.textContent = 'Sending...';
}
try {
const formName = form.dataset.form || 'general';
const formData = new FormData(form);
const data = Object.fromEntries(formData);
// Endpoint is /_forms/{formName}
const response = await fetch('/_forms/' + formName, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
if (response.ok) {
// Option 1: Redirect to thank you page
// window.location.href = '/thank-you';
// Option 2: Show success message
form.reset();
alert(form.dataset.successMessage || 'Thank you! Your message has been sent.');
} else {
throw new Error('Form submission failed');
}
} catch (error) {
console.error('Form error:', error);
alert('There was an error. Please try again.');
} finally {
if (submitBtn) {
submitBtn.disabled = false;
submitBtn.textContent = originalText;
}
}
});
});
\`\`\`
**Key points:**
- Add \`data-form="formname"\` to identify the form (e.g., \`data-form="contact"\`)
- Endpoint is \`/_forms/{formName}\` (e.g., \`/_forms/contact\`)
- All inputs must have \`name\` attributes to be captured
- Add a submit button for the form to work
**Note:** The legacy \`data-form-name\` attribute is deprecated. Use \`data-form\` instead.`,
asset_paths: `# Asset Path Rules
**ALL asset paths must use /public/ prefix:**
\`\`\`html
<!-- CSS -->
<link rel="stylesheet" href="/public/css/style.css">
<link rel="stylesheet" href="/public/css/components/header.css">
<!-- JavaScript -->
<script src="/public/js/main.js"></script>
<script src="/public/js/vendor/swiper.min.js"></script>
<!-- Images -->
<img src="/public/images/logo.png" alt="Logo">
<img src="/public/images/team/john.jpg" alt="John">
<!-- Favicon -->
<link rel="icon" href="/public/images/favicon.ico">
\`\`\`
**In CSS files:**
\`\`\`css
/* Correct */
background-image: url('/public/images/hero-bg.jpg');
/* Wrong - will break */
background-image: url('../images/hero-bg.jpg');
background-image: url('images/hero-bg.jpg');
\`\`\`
**Common conversions:**
- \`href="css/style.css"\` → \`href="/public/css/style.css"\`
- \`src="../images/logo.png"\` → \`src="/public/images/logo.png"\`
- \`url('../fonts/custom.woff')\` → \`url('/public/fonts/custom.woff')\``,
image_handling: `# Image Handling in Templates
## Two Types of Images
### 1. Static/UI Images (Keep as /public/ paths)
Logos, icons, decorative backgrounds, UI elements - these are bundled with the site:
\`\`\`html
<!-- KEEP these as static paths -->
<img src="/public/images/logo.png" alt="Company Logo">
<img src="/public/images/icons/arrow.svg" alt="">
<link rel="icon" href="/public/images/favicon.ico">
<div style="background-image: url('/public/images/hero-pattern.svg')">
\`\`\`
### 2. Content Images (Use CMS Tokens)
Post images, team photos, product images - these are managed through the CMS:
\`\`\`html
<!-- USE CMS tokens for content -->
<img src="{{image}}" alt="{{name}}">
<img src="{{thumbnail}}" alt="{{name}}">
<img src="{{photo}}" alt="{{name}}">
\`\`\`
---
## Always Wrap Content Images in Conditionals
\`\`\`html
{{#if image}}
<img src="{{image}}" alt="{{name}}" class="hero-image">
{{/if}}
\`\`\`
Or provide a CSS fallback:
\`\`\`html
{{#if image}}
<img src="{{image}}" alt="{{name}}">
{{else}}
<div class="placeholder-image"></div>
{{/if}}
\`\`\`
---
## Common Mistakes
1. **Replacing logos with CMS tokens** - Keep static UI images as \`/public/\` paths
2. **Hardcoding example content images** - Use \`{{#each}}\` loops with CMS tokens
3. **Missing conditionals** - Always wrap optional content images in \`{{#if}}\``,
relation_fields: `# Relation Fields - Linking Collections
Relation fields let you link items from one collection to another.
---
## Using Relation Fields in Templates
When a relation field is set, access the related item's data using dot notation:
\`\`\`html
{{#each projects}}
<article class="project-card">
<h2><a href="{{url}}">{{name}}</a></h2>
{{#if category}}
<!-- Access any field from the related category -->
<span class="category-badge">{{category.name}}</span>
<a href="{{category.url}}" class="category-link">
View all {{category.name}} projects
</a>
{{/if}}
</article>
{{/each}}
\`\`\`
---
## Available Relation Tokens
| Token | Description |
|-------|-------------|
| \`{{relationField.name}}\` | Related item's name |
| \`{{relationField.slug}}\` | Related item's URL slug |
| \`{{relationField.url}}\` | Full URL to related item's detail page |
| \`{{relationField.anyField}}\` | Any field from the related collection |
---
## Filter by Relation on Detail Pages
On a category detail page, show only items with that category:
\`\`\`html
<h1>{{name}}</h1>
<p>Projects in this category:</p>
<div class="projects-grid">
{{#each projects}}
{{#if (eq category.slug ../slug)}}
<article class="project-card">
<h3>{{name}}</h3>
<a href="{{url}}">View Project</a>
</article>
{{/if}}
{{/each}}
</div>
\`\`\`
---
## Best Practices
1. **Always wrap in {{#if}}** - The relation may not be set for all items
2. **Use for categorization** - Tags, categories, content types
3. **Link related content** - Featured author, related service, parent category`,
data_edit_keys: `# Inline Editing with data-edit-key
Make text editable in the CMS visual editor:
\`\`\`html
<!-- Unique, descriptive keys -->
<h1 data-edit-key="home-hero-title">Welcome to Our Company</h1>
<p data-edit-key="home-hero-subtitle">We provide excellent services</p>
<!-- Hierarchical naming for sections -->
<section class="about">
<h2 data-edit-key="about-section-title">About Us</h2>
<p data-edit-key="about-section-paragraph-1">First paragraph...</p>
<p data-edit-key="about-section-paragraph-2">Second paragraph...</p>
</section>
<!-- For different pages, prefix with page name -->
<h1 data-edit-key="contact-page-title">Contact Us</h1>
<p data-edit-key="contact-intro">Get in touch with our team</p>
\`\`\`
**Naming conventions:**
- \`{page}-{section}-{element}\`
- Examples: \`home-hero-title\`, \`about-team-heading\`, \`contact-form-intro\`
- Must be unique across the entire site
- Use lowercase with hyphens`,
each_loop: `# {{#each}} Loop Syntax
**Basic loop:**
\`\`\`html
{{#each posts}}
<article>
<h2>{{name}}</h2>
<p>{{summary}}</p>
</article>
{{/each}}
\`\`\`
**With limit:**
\`\`\`html
{{#each posts limit=6}}
...
{{/each}}
\`\`\`
**Featured only (if collection has boolean "featured" field):**
\`\`\`html
{{#each posts featured=true}}
...
{{/each}}
\`\`\`
**Combined:**
\`\`\`html
{{#each posts featured=true limit=3}}
...
{{/each}}
\`\`\`
**With sorting:**
\`\`\`html
{{#each team sort="order" order="asc"}}
...
{{/each}}
\`\`\`
**Loop variables:**
\`\`\`html
{{#each posts limit=5}}
<article class="{{#if @first}}featured{{/if}} {{#if @last}}last{{/if}}">
<span class="position">Item {{@index}}</span>
<h2>{{name}}</h2>
</article>
{{/each}}
\`\`\`
- \`{{@first}}\` - true for first item
- \`{{@last}}\` - true for last item
- \`{{@index}}\` - zero-based index (0, 1, 2...)`,
conditional_if: `# Conditional Rendering
**{{#if}} - Render if truthy:**
\`\`\`html
{{#if image}}
<img src="{{image}}" alt="{{name}}">
{{/if}}
\`\`\`
**{{#if}} with {{else}}:**
\`\`\`html
{{#if thumbnail}}
<img src="{{thumbnail}}" alt="{{name}}">
{{else}}
<div class="placeholder">No image</div>
{{/if}}
\`\`\`
**{{#unless}} - Render if falsy:**
\`\`\`html
{{#unless featured}}
<span class="regular-post">Regular</span>
{{/unless}}
\`\`\`
**Nested conditionals:**
\`\`\`html
{{#if author}}
<div class="author-info">
{{#if author.photo}}
<img src="{{author.photo}}" alt="{{author.name}}">
{{/if}}
<span>{{author.name}}</span>
</div>
{{/if}}
\`\`\`
**Multiple conditions (check both):**
\`\`\`html
{{#if image}}
{{#if featured}}
<img src="{{image}}" class="featured-image">
{{/if}}
{{/if}}
\`\`\``,
nested_fields: `# Nested Field Access (Relation Fields)
Access related item fields using dot notation:
\`\`\`html
{{#each posts}}
<article>
<h2>{{name}}</h2>
{{#if author}}
<div class="author">
{{#if author.photo}}
<img src="{{author.photo}}" alt="{{author.name}}">
{{/if}}
<span>By {{author.name}}</span>
{{#if author.bio}}
<p class="bio">{{{author.bio}}}</p>
{{/if}}
</div>
{{/if}}
</article>
{{/each}}
\`\`\`
**Available nested tokens (for a relation field named "author"):**
- \`{{author.name}}\`
- \`{{author.slug}}\`
- \`{{author.url}}\`
- \`{{{author.bio}}}\` (triple braces for richText!)
- Any other field from the related collection
**Always wrap in {{#if author}}** to handle items without the relation set`,
featured_posts: `# Featured Posts Section
**Homepage featured posts (3 most recent):**
\`\`\`html
<section class="featured-posts">
<h2 data-edit-key="home-blog-title">Latest News</h2>
{{#each posts featured=true limit=3}}
<article class="featured-card {{#if @first}}large{{/if}}">
{{#if image}}
<img src="{{image}}" alt="{{name}}">
{{/if}}
<div class="card-content">
<h3><a href="{{url}}">{{name}}</a></h3>
<p>{{summary}}</p>
{{#if author}}
<span class="byline">By {{author.name}}</span>
{{/if}}
<time>{{publishedAt}}</time>
</div>
</article>
{{/each}}
<a href="/blog" class="view-all">View All Posts</a>
</section>
\`\`\`
**Note:** Requires a boolean "featured" field on your collection`,
parent_context: `# Parent Context References (\`../\`)
Inside loops, access the **parent scope** (the page's current item) using \`../\`:
**Use Case: Author Detail Page - Show Only This Author's Posts**
\`\`\`html
<article class="author-detail">
<h1>{{name}}</h1>
<p class="bio">{{{bio}}}</p>
<section class="author-articles">
<h2>Posts by {{name}}</h2>
{{#each posts}}
{{#if (eq author.name ../name)}}
<article class="post-card">
<h3><a href="{{url}}">{{name}}</a></h3>
<p>{{summary}}</p>
<time>{{publishedAt}}</time>
</article>
{{/if}}
{{/each}}
</section>
</article>
\`\`\`
**How It Works:**
- Inside \`{{#each posts}}\`, the context is each post
- \`author.name\` = the post's author
- \`../name\` = the parent context (the author being displayed on the page)
- Only posts where author.name matches ../name are shown
**Use Case: Category Page - Highlight Current Category**
\`\`\`html
<nav class="category-nav">
{{#each categories}}
<a href="{{url}}" class="{{#if (eq slug ../slug)}}active{{/if}}">
{{name}}
</a>
{{/each}}
</nav>
\`\`\`
**Available Parent Fields:**
- \`../name\` - Parent item's name
- \`../slug\` - Parent item's slug
- \`../fieldName\` - Any field from the parent item`,
equality_comparison: `# Equality Comparisons
Compare two values using \`(eq field1 field2)\` helper:
## Show When Equal ({{#if (eq ...)}})
\`\`\`html
{{#if (eq author.slug ../slug)}}
<span class="current-author-badge">Your Post</span>
{{/if}}
\`\`\`
## Show When NOT Equal ({{#unless (eq ...)}}) - IMPORTANT!
The most common pattern for "Related Posts" or "Other Items" sections:
\`\`\`html
<!-- On a post page, show other posts EXCEPT the current one -->
<h3>Related Posts</h3>
{{#each posts limit=3}}
{{#unless (eq slug ../slug)}}
<article>
<a href="{{url}}">{{name}}</a>
<p>{{summary}}</p>
</article>
{{/unless}}
{{/each}}
\`\`\`
**How it works:**
- \`../slug\` accesses the current page's slug (parent context)
- \`slug\` is the loop item's slug
- \`{{#unless (eq slug ../slug)}}\` shows content when they're NOT equal
- This excludes the current post from the "related" list
## Compare Field to Literal String ({{#eq}})
\`\`\`html
{{#eq status "published"}}
<span class="badge badge-success">Published</span>
{{/eq}}
{{#eq category "news"}}
<span class="news-icon">News</span>
{{/eq}}
\`\`\`
## Summary Table
| Syntax | Shows content when... |
|--------|----------------------|
| \`{{#if (eq a b)}}\` | a equals b |
| \`{{#unless (eq a b)}}\` | a does NOT equal b |
| \`{{#eq field "value"}}\` | field equals "value" |`,
comparison_helpers: `# Comparison Helpers
Use comparison helpers for numeric and string comparisons in conditionals.
## Available Helpers
| Helper | Meaning | Example |
|--------|---------|---------|
| \`lt\` | Less than | \`{{#if (lt @index 4)}}\` |
| \`gt\` | Greater than | \`{{#if (gt @index 0)}}\` |
| \`lte\` | Less than or equal | \`{{#if (lte @index 4)}}\` |
| \`gte\` | Greater than or equal | \`{{#if (gte @index 2)}}\` |
| \`ne\` | Not equal | \`{{#if (ne status "draft")}}\` |
| \`eq\` | Equal | \`{{#if (eq category.slug ../slug)}}\` |
---
## Loop Index Comparisons
### Show First N Items Differently
\`\`\`html
{{#each posts}}
{{#if (lt @index 3)}}
<!-- First 3 items get special styling -->
<article class="featured-card">{{name}}</article>
{{else}}
<!-- Remaining items -->
<article class="standard-card">{{name}}</article>
{{/if}}
{{/each}}
\`\`\`
### Skip First Item
\`\`\`html
{{#each posts}}
{{#if (gt @index 0)}}
<article>{{name}}</article>
{{/if}}
{{/each}}
\`\`\`
### Show Items 2-5 Only
\`\`\`html
{{#each posts}}
{{#if (gte @index 1)}}
{{#if (lte @index 4)}}
<article>Item {{@index}}: {{name}}</article>
{{/if}}
{{/if}}
{{/each}}
\`\`\`
---
## Field Value Comparisons
### Filter by Status
\`\`\`html
{{#each posts}}
{{#if (ne status "draft")}}
<article>{{name}}</article>
{{/if}}
{{/each}}
\`\`\`
### Numeric Field Comparison
\`\`\`html
{{#each products}}
{{#if (gte price 100)}}
<span class="premium-badge">Premium</span>
{{/if}}
{{#if (lt stock 5)}}
<span class="low-stock">Low Stock!</span>
{{/if}}
{{/each}}
\`\`\`
---
## With {{#unless}}
The opposite of \`{{#if}}\`:
\`\`\`html
{{#each posts}}
{{#unless (lt @index 3)}}
<!-- Show for items 4 and beyond -->
<article class="archive-card">{{name}}</article>
{{/unless}}
{{/each}}
\`\`\`
---
## Common Patterns
### Hero + Grid Layout
\`\`\`html
{{#each posts}}
{{#if (lt @index 1)}}
<!-- First item is hero -->
<div class="hero-post">
<h1>{{name}}</h1>
{{{body}}}
</div>
{{else}}
{{#if (lt @index 4)}}
<!-- Items 2-4 in featured grid -->
<div class="featured-grid-item">{{name}}</div>
{{else}}
<!-- Rest in list -->
<div class="list-item">{{name}}</div>
{{/if}}
{{/if}}
{{/each}}
\`\`\`
### Pagination Preview
\`\`\`html
<!-- Show first 6 items, with "view more" after -->
{{#each posts}}
{{#if (lt @index 6)}}
<article>{{name}}</article>
{{/if}}
{{/each}}
{{#if (gt @length 6)}}
<a href="/blog">View all posts</a>
{{/if}}
\`\`\`
---
## Summary
| Syntax | Shows content when... |
|--------|----------------------|
| \`{{#if (lt @index 4)}}\` | index < 4 (first 4 items) |
| \`{{#if (gt @index 0)}}\` | index > 0 (skip first) |
| \`{{#if (lte @index 4)}}\` | index <= 4 (first 5 items) |
| \`{{#if (gte @index 2)}}\` | index >= 2 (skip first 2) |
| \`{{#if (ne field "value")}}\` | field != "value" |
| \`{{#unless (lt @index 3)}}\` | index >= 3 (opposite) |`,
youtube_embed: `# YouTube Video Embeds
**IMPORTANT:** YouTube iframes require specific attributes to work correctly. Missing attributes will cause Error 150/153.
## Correct YouTube Embed Format
\`\`\`html
<iframe
src="{{videoUrl}}"
title="{{name}}"
frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
referrerpolicy="strict-origin-when-cross-origin"
allowfullscreen
></iframe>
\`\`\`
## Required Attributes
| Attribute | Required | Why |
|-----------|----------|-----|
| \`referrerpolicy="strict-origin-when-cross-origin"\` | **YES** | YouTube blocks embeds without this |
| \`allowfullscreen\` | Recommended | Enables fullscreen button |
| \`allow="..."\` | Recommended | Enables player features |
| \`frameborder="0"\` | Recommended | Removes border |
| \`title="..."\` | Recommended | Accessibility for screen readers |
## Video URL Format
Store YouTube URLs in embed format:
\`\`\`
https://www.youtube.com/embed/VIDEO_ID
\`\`\`
**NOT** watch format:
\`\`\`
https://www.youtube.com/watch?v=VIDEO_ID
\`\`\`
## Full Example with Conditional
\`\`\`html
<div class="video-container">
{{#if videoUrl}}
<iframe
src="{{videoUrl}}"
title="{{name}}"
frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
referrerpolicy="strict-origin-when-cross-origin"
allowfullscreen
></iframe>
{{else}}
<div class="video-placeholder">
<p>Video coming soon</p>
</div>
{{/if}}
</div>
\`\`\``,
nested_collection_loop: `# Nested Collection Loops
For hierarchical content like documentation categories with pages, product categories with products, or authors with their posts.
**Works on ALL page types:** static pages, index pages, AND detail pages.
## The Pattern
\`\`\`html
{{#each outer_collection}}
<div class="category-section">
<h3>{{name}}</h3>
<ul>
{{#each inner_collection where="relation_field.slug:{{slug}}"}}
<li><a href="{{url}}">{{name}}</a></li>
{{/each}}
</ul>
</div>
{{/each}}
\`\`\`
**How it works:**
- The outer loop iterates through parent items (e.g., categories)
- \`{{slug}}\` in the \`where\` clause references the current outer item's slug
- The inner loop filters to only items matching that parent
---
## Using @root. Prefix
Use \`@root.\` to explicitly access collections from the root context:
\`\`\`html
{{#each doc_categories}}
<h3>{{name}}</h3>
{{#each @root.doc_pages where="category.slug:{{slug}}"}}
<a href="{{url}}">{{name}}</a>
{{/each}}
{{/each}}
\`\`\`
Both syntaxes work identically:
- \`{{#each doc_pages}}\` - Standard syntax
- \`{{#each @root.doc_pages}}\` - Explicit root reference
---
## Documentation Sidebar Example
**Collections needed:**
- \`doc_categories\` (name, slug, order)
- \`doc_pages\` (name, slug, content, category → relation to doc_categories)
\`\`\`html
<nav class="docs-sidebar">
{{#each doc_categories sort="order" order="asc"}}
<div class="sidebar-section">
<h4 class="section-title">{{name}}</h4>
<ul class="section-links">
{{#each @root.doc_pages where="category.slug:{{slug}}" sort="order" order="asc"}}
<li>
<a href="{{url}}" class="doc-link">{{name}}</a>
</li>
{{/each}}
</ul>
</div>
{{/each}}
</nav>
\`\`\`
This sidebar pattern can be included on:
- **Static pages** (about, contact, etc.)
- **Index pages** (docs listing page)
- **Detail pages** (individual doc pages)
---
## Blog Categories with Posts Example
\`\`\`html
<section class="categorized-posts">
{{#each categories}}
<div class="category-group">
<h2><a href="{{url}}">{{name}}</a></h2>
<div class="posts-grid">
{{#each posts where="category.id:{{id}}" limit=3 sort="publishedAt" order="desc"}}
<article class="post-card">
{{#if image}}
<img src="{{image}}" alt="{{name}}">
{{/if}}
<h3><a href="{{url}}">{{name}}</a></h3>
<p>{{summary}}</p>
</article>
{{/each}}
</div>
<a href="{{url}}" class="view-all">View all {{name}}</a>
</div>
{{/each}}
</section>
\`\`\`
---
## Important Notes
1. **Works on ALL page types** - Static pages, index pages, AND detail pages
2. **The \`where\` clause uses parent context** - \`{{slug}}\` or \`{{id}}\` comes from the outer loop's current item
3. **@root. is optional** - Use it when you want to be explicit about root context
4. **Supports all modifiers** - \`limit\`, \`sort\`, \`order\` work on inner loops
5. **Relation field format** - Use \`relation_field.slug:{{slug}}\` or \`relation_field.id:{{id}}\`
---
## Manifest Configuration
\`\`\`json
{
"cmsTemplates": {
"doc_categoriesIndex": "templates/docs_index.html",
"doc_categoriesIndexPath": "/docs",
"doc_pagesDetail": "templates/doc_page.html",
"doc_pagesDetailPath": "/docs"
}
}
\`\`\`
This gives you URLs like:
- \`/docs\` - Documentation index showing all categories with their pages
- \`/docs/quick-start-guide\` - Individual doc page`,
loop_variables: `# Loop Variables
Special variables available inside \`{{#each}}\` loops.
## Available Variables
| Variable | Description |
|----------|-------------|
| \`{{@index}}\` | Zero-based index (0, 1, 2...) |
| \`{{@first}}\` | True for first item only |
| \`{{@last}}\` | True for last item only |
| \`{{@length}}\` | Total number of items |
---
## Conditional Usage
Use loop variables in conditionals for styling:
\`\`\`html
{{#each team_members}}
{{#if @first}}
<div class="featured-member">{{name}} - Team Lead</div>
{{else}}
<div class="member">{{name}}</div>
{{/if}}
{{/each}}
\`\`\`
---
## Common Patterns
### Add separator between items (not before first):
\`\`\`html
{{#each tags}}
{{#unless @first}} | {{/unless}}
<span>{{name}}</span>
{{/each}}
\`\`\`
Output: \`Tag1 | Tag2 | Tag3\`
### Comma-separated list (no comma after last):
\`\`\`html
{{#each authors}}
<span>{{name}}</span>{{#unless @last}}, {{/unless}}
{{/each}}
\`\`\`
Output: \`Alice, Bob, Charlie\`
### Style first and last items:
\`\`\`html
{{#each posts}}
<article class="{{#if @first}}first{{/if}} {{#if @last}}last{{/if}}">
{{name}}
</article>
{{/each}}
\`\`\`
### Show position number:
\`\`\`html
{{#each leaderboard}}
<div class="rank-{{@index}}">
#{{@index}}: {{name}} - {{score}} points
</div>
{{/each}}
\`\`\`
---
## Important Notes
- Loop variables **only work inside \`{{#each}}\`** blocks
- Using them outside a loop will not work and will log a warning
- \`{{#unless @first}}\` is the opposite of \`{{#if @first}}\`
- \`{{#unless @last}}\` is the opposite of \`{{#if @last}}\``,
common_mistakes: `# Common AI Mistakes - Quick Reference
This example shows the WRONG and CORRECT patterns side-by-side for quick reference.
---
## Manifest Format
**WRONG:**
\`\`\`json
{
"collections": {
"posts": {
"indexPath": "/blog",
"indexFile": "collections/posts/index.html"
}
}
}
\`\`\`
**CORRECT:**
\`\`\`json
{
"cmsTemplates": {
"postsIndex": "templates/posts_index.html",
"postsDetail": "templates/posts_detail.html",
"postsIndexPath": "/blog",
"postsDetailPath": "/blog"
}
}
\`\`\`
---
## Folder Structure
**WRONG:**
\`\`\`
assets/css/style.css ← Won't load
collections/posts/ ← Wrong folder
\`\`\`
**CORRECT:**
\`\`\`
public/css/style.css ← Loads at /css/style.css
templates/posts_index.html
templates/posts_detail.html
\`\`\`
---
## Template Naming
**WRONG:**
\`\`\`
post-detail.html
postDetail.html
collections/posts/detail.html
\`\`\`
**CORRECT:**
\`\`\`
posts_detail.html
posts_index.html
authors_detail.html
\`\`\`
Pattern: \`{collection_slug}_{type}.html\`
---
## Inline Editing
**WRONG (no editing possible):**
\`\`\`html
<h1>{{headline}}</h1>
<p>{{description}}</p>
\`\`\`
**CORRECT (enables visual editing):**
\`\`\`html
<h1 data-edit-key="headline">{{headline}}</h1>
<p data-edit-key="description">{{description}}</p>
\`\`\`
---
## Links in Templates
**WRONG (hardcoded path):**
\`\`\`html
<a href="/posts/{{slug}}">Read more</a>
\`\`\`
**CORRECT (uses manifest path):**
\`\`\`html
<a href="{{url}}">Read more</a>
\`\`\`
The \`{{url}}\` token automatically uses the path from manifest.json.
---
## Asset References in HTML
**WRONG:**
\`\`\`html
<link href="/assets/css/style.css">
<script src="assets/js/main.js">
\`\`\`
**CORRECT:**
\`\`\`html
<link href="/css/style.css">
<script src="/js/main.js">
\`\`\`
Assets in \`public/\` are served without the \`public/\` prefix in the URL.
---
## Field Slugs
**WRONG (camelCase):**
\`\`\`
heroImage
authorBio
publishedDate
\`\`\`
**CORRECT (snake_case):**
\`\`\`
hero_image
author_bio
published_date
\`\`\`
---
## Rich Text Fields
**WRONG (double braces):**
\`\`\`html
<div>{{body}}</div> ← HTML will be escaped!
\`\`\`
**CORRECT (triple braces):**
\`\`\`html
<div>{{{body}}}</div> ← HTML renders correctly
\`\`\`
Use triple braces for rich text fields to render HTML formatting.
---
## Complete Example Package Structure
\`\`\`
my-site/
├── manifest.json
├── pages/
│ ├── index.html
│ ├── about.html
│ └── contact.html
├── templates/
│ ├── posts_index.html
│ ├── posts_detail.html
│ ├── authors_index.html
│ └── authors_detail.html
└── public/
├── css/
│ └── style.css
├── js/
│ └── main.js
└── images/
└── logo.svg
\`\`\`
This structure will work every time!`,
};
/**
* Returns example code for a specific pattern
*/
async function getExample(exampleType) {
return EXAMPLES[exampleType] || `Example not found: ${exampleType}`;
}