diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index e242d77..542a461 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -28,8 +28,16 @@ jobs: bun-version: latest - run: bun install working-directory: docs - - run: bun run build + - name: Build docs (fail on render errors) working-directory: docs + run: | + # vitepress prints SSR render errors but still exits 0, leaving blank pages + set -o pipefail + bun run build 2>&1 | tee build.log + if grep -qE "(TypeError|ReferenceError|SyntaxError|\[vitepress\].*error)" build.log; then + echo "::error::Docs build logged render errors (see above)." + exit 1 + fi - name: Verify build output run: | if [ ! -d "docs/.vitepress/dist" ] || [ -z "$(ls -A docs/.vitepress/dist)" ]; then diff --git a/CHANGELOG.md b/CHANGELOG.md index b13e392..469d475 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Docs +- Fix blank Template Authoring page (inline `{{ }}` parsed as Vue), derive the + template count from `templates/`, WCAG-compliant brand colors in light mode, + favicon and header logo, and fail the docs CI build on render errors (#138). + ### Fixed - Findings from a Cora scan (#136): `--stdin`/`--json` temp input is now unique, `O_EXCL`-created and removed afterwards (was a fixed `/tmp` name); diff --git a/README.md b/README.md index 54669f7..c6ec6fa 100644 --- a/README.md +++ b/README.md @@ -105,7 +105,7 @@ templates/ ├── twitter-quote/ │ ├── template.svg │ └── schema.json -└── ... # 18 templates included +└── ... # 150+ templates included ``` ## Documentation diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts index dca1bef..a49f2ff 100644 --- a/docs/.vitepress/config.ts +++ b/docs/.vitepress/config.ts @@ -1,5 +1,11 @@ +import { readdirSync } from 'node:fs' +import { fileURLToPath } from 'node:url' import { defineConfig } from 'vitepress' +// Single source of truth: one directory per bundled template. +const templatesDir = fileURLToPath(new URL('../../templates', import.meta.url)) +const templateCount = readdirSync(templatesDir, { withFileTypes: true }).filter((e) => e.isDirectory()).length + export default defineConfig({ title: 'Cosy', description: 'Template-based image generation in Rust', @@ -9,15 +15,36 @@ export default defineConfig({ lastUpdated: true, ignoreDeadLinks: true, + markdown: { + config: (md) => { + // Inline code like `{{ brand.brand_name }}` is minijinja syntax, not Vue. + const inline = md.renderer.rules.code_inline! + md.renderer.rules.code_inline = (tokens, idx, options, env, self) => + inline(tokens, idx, options, env, self).replace(' {{ brand.brand_name }} {{ brand.bg_color }} @@ -156,13 +156,13 @@ The SVG file uses [minijinja](https://docs.rs/minijinja) templating syntax for d Use the `default()` filter to provide fallback values: -```svg +```xml ``` ### Conditionals -```svg +```xml {% if slide.source %} {{ slide.source }} {% endif %} @@ -172,7 +172,7 @@ Use the `default()` filter to provide fallback values: When a text field has `wrap_width` set, Cosy pre-computes wrapped lines and provides them as `{field_name}_lines`: -```svg +```xml {% for line in stat_label_lines %} {{ line }} {% endfor %} @@ -182,7 +182,7 @@ When a text field has `wrap_width` set, Cosy pre-computes wrapped lines and prov Most templates use a two-stop linear gradient for the background: -```svg +```xml @@ -194,7 +194,7 @@ Most templates use a two-stop linear gradient for the background: ### Accent Gradient (for numbers, headings) -```svg +```xml @@ -205,7 +205,7 @@ Most templates use a two-stop linear gradient for the background: ### Radial Glow Effect -```svg +```xml @@ -217,7 +217,7 @@ Most templates use a two-stop linear gradient for the background: ### Dots Pattern (subtle texture) -```svg +```xml @@ -228,7 +228,7 @@ Most templates use a two-stop linear gradient for the background: ### Decorative Circle (top-right accent) -```svg +```xml ``` @@ -263,7 +263,7 @@ A background texture or photo rendered behind the gradient overlay. **SVG pattern:** -```svg +```xml {% if bg_image_data_uri %} @@ -323,7 +323,7 @@ Cosy bundles these fonts (embedded at compile time — no system fonts required) **SVG usage:** -```svg +```xml font-family="Inter,sans-serif" font-family="Space Grotesk,sans-serif" font-family="JetBrains Mono,monospace" @@ -366,7 +366,7 @@ mkdir templates/my-template ### 3. Write template.svg -```svg +```xml @@ -496,7 +496,7 @@ curl -o output.png -X POST http://localhost:3000/api/render \ **Avoid arithmetic in `{% set %}` within loops** — it doesn't work reliably. Instead, use `loop.index`, `loop.first`, `loop.cycle()`: -```svg +```xml {# Good: use loop helpers #} {% for line in title_lines %} {{ line }} @@ -535,7 +535,7 @@ The default Cosy color scheme: All templates must render the brand watermark with an identical spec: -```svg +```xml {% if brand.show_brand %} {{ brand.brand_name | default('') }} {% endif %} @@ -575,7 +575,7 @@ Black, so `font-style="italic"` and `font-weight="800"` resolve to real faces. ## Hashtag Block Pattern -```svg +```xml {% if slide.hashtag %} diff --git a/docs/templates/index.md b/docs/templates/index.md index 883e96d..63cf4cc 100644 --- a/docs/templates/index.md +++ b/docs/templates/index.md @@ -2,9 +2,14 @@ aside: false --- + + # Templates -Cosy ships with **148 templates** out of the box. Browse the gallery below, or use the sidebar to filter by category. +Cosy ships with **{{ theme.templateCount }} templates** out of the box. Browse the gallery below, or use the sidebar to filter by category.