309 lines
9.0 KiB
Plaintext
309 lines
9.0 KiB
Plaintext
-- allium: 1
|
|
-- bDS Liquid Template Context Specification
|
|
-- Scope: core (Wave 4 — rendering parity)
|
|
-- Distilled from: ../bDS/src/main/engine/GenerationRouteRendererFactory.ts,
|
|
-- PageRenderer.ts, BlogGenerationEngine.ts
|
|
--
|
|
-- This document specifies the exact data structure passed to Liquid templates
|
|
-- during rendering. It is the contract between the generation engine and
|
|
-- template authors.
|
|
|
|
-- ============================================================================
|
|
-- GLOBAL TEMPLATE VARIABLES
|
|
-- ============================================================================
|
|
|
|
surface TemplateRenderingSurface {
|
|
facing _: RenderPipeline
|
|
|
|
provides:
|
|
RenderPostPageRequested(post, language)
|
|
RenderListPageRequested(posts, pagination, archive_context)
|
|
RenderNotFoundPageRequested()
|
|
}
|
|
|
|
surface RenderContextSurface {
|
|
context context: RenderContext
|
|
|
|
exposes:
|
|
context.language
|
|
context.language_prefix
|
|
context.page_title
|
|
context.pico_stylesheet_href
|
|
context.blog_languages
|
|
context.alternate_links
|
|
context.menu_items
|
|
context.post
|
|
context.day_blocks
|
|
context.archive_context
|
|
context.is_list_page
|
|
context.prev_page_href
|
|
context.next_page_href
|
|
context.not_found_message
|
|
}
|
|
|
|
surface PaginationContextSurface {
|
|
context pagination: PaginationContext
|
|
|
|
exposes:
|
|
pagination.is_first_page
|
|
pagination.is_last_page
|
|
pagination.has_prev_page
|
|
pagination.has_next_page
|
|
pagination.prev_page_href
|
|
pagination.next_page_href
|
|
pagination.current_page
|
|
pagination.total_pages
|
|
pagination.total_items
|
|
pagination.items_per_page
|
|
}
|
|
|
|
value RenderContext {
|
|
-- Top-level variables available in all templates
|
|
language: String -- Current language code
|
|
language_prefix: String? -- "/de" or "" depending on language
|
|
html_theme_attribute: String -- Theme class for <html> element
|
|
page_title: String -- Page title for <title> tag
|
|
pico_stylesheet_href: String -- Path to Pico CSS theme
|
|
blog_languages: List<BlogLanguage>
|
|
alternate_links: List<AlternateLink>
|
|
menu_items: List<MenuItem>
|
|
calendar_initial_year: Integer
|
|
calendar_initial_month: Integer
|
|
post: PostContext? -- Present on single post pages
|
|
post_categories: List<Category>
|
|
post_tags: List<Tag>
|
|
tag_color_by_name: Map<String, String>
|
|
backlinks: List<Backlink>
|
|
day_blocks: List<DayBlock>
|
|
archive_context: ArchiveContext?
|
|
show_archive_range_heading: Boolean
|
|
min_date: Timestamp?
|
|
max_date: Timestamp?
|
|
is_list_page: Boolean
|
|
is_first_page: Boolean
|
|
is_last_page: Boolean
|
|
has_prev_page: Boolean
|
|
has_next_page: Boolean
|
|
prev_page_href: String?
|
|
next_page_href: String?
|
|
not_found_message: String?
|
|
not_found_back_label: String?
|
|
|
|
-- Lookup maps for macro expansion
|
|
canonical_post_path_by_slug: Map<String, String>
|
|
canonical_media_path_by_source_path: Map<String, String>
|
|
post_data_json_by_id: Map<String, PostDataJson>
|
|
}
|
|
|
|
value BlogLanguage {
|
|
is_current: Boolean
|
|
code: String
|
|
flag: String
|
|
href: String
|
|
href_prefix: String
|
|
}
|
|
|
|
value AlternateLink {
|
|
href: String
|
|
hreflang: String
|
|
}
|
|
|
|
value MenuItem {
|
|
href: String
|
|
title: String
|
|
has_children: Boolean
|
|
children: List<MenuItem>?
|
|
}
|
|
|
|
-- ============================================================================
|
|
-- POST CONTEXT (Single Post Pages)
|
|
-- ============================================================================
|
|
|
|
value PostContext {
|
|
id: String
|
|
title: String
|
|
content: String -- Rendered HTML (after markdown + macros)
|
|
slug: String
|
|
excerpt: String?
|
|
author: String?
|
|
language: String?
|
|
show_title: Boolean -- Always true for post pages
|
|
published_at: Timestamp
|
|
created_at: Timestamp
|
|
updated_at: Timestamp
|
|
tags: List<String>
|
|
categories: List<String>
|
|
template_slug: String?
|
|
do_not_translate: Boolean
|
|
linked_media: List<MediaContext>
|
|
outgoing_links: List<LinkContext>
|
|
incoming_links: List<LinkContext>
|
|
}
|
|
|
|
value PostDataJson {
|
|
-- Serialized post data for Liquid template access
|
|
id: String
|
|
title: String
|
|
slug: String
|
|
excerpt: String?
|
|
author: String?
|
|
language: String?
|
|
published_at: Timestamp
|
|
created_at: Timestamp
|
|
updated_at: Timestamp
|
|
tags: List<String>
|
|
categories: List<String>
|
|
}
|
|
|
|
value MediaContext {
|
|
id: String
|
|
filename: String
|
|
original_name: String
|
|
mime_type: String
|
|
title: String?
|
|
alt: String?
|
|
caption: String?
|
|
author: String?
|
|
width: Integer?
|
|
height: Integer?
|
|
file_path: String
|
|
}
|
|
|
|
value LinkContext {
|
|
href: String
|
|
title: String
|
|
display_slug: String
|
|
}
|
|
|
|
-- ============================================================================
|
|
-- ARCHIVE CONTEXT (List Pages)
|
|
-- ============================================================================
|
|
|
|
value ArchiveContext {
|
|
kind: category | tag | date
|
|
name: String?
|
|
month: Integer?
|
|
year: Integer?
|
|
day: Integer?
|
|
}
|
|
|
|
value DayBlock {
|
|
show_date_marker: Boolean
|
|
date_label: String
|
|
posts: List<PostContext>
|
|
show_separator: Boolean
|
|
}
|
|
|
|
value Category {
|
|
name: String
|
|
slug: String
|
|
post_count: Integer
|
|
}
|
|
|
|
value Tag {
|
|
name: String
|
|
slug: String
|
|
color: String?
|
|
post_count: Integer
|
|
}
|
|
|
|
value Backlink {
|
|
path: String
|
|
display_slug: String
|
|
title: String
|
|
}
|
|
|
|
-- ============================================================================
|
|
-- PAGINATION CONTEXT
|
|
-- ============================================================================
|
|
|
|
value PaginationContext {
|
|
is_list_page: Boolean
|
|
is_first_page: Boolean
|
|
is_last_page: Boolean
|
|
has_prev_page: Boolean
|
|
has_next_page: Boolean
|
|
prev_page_href: String?
|
|
next_page_href: String?
|
|
current_page: Integer
|
|
total_pages: Integer
|
|
total_items: Integer
|
|
items_per_page: Integer
|
|
}
|
|
|
|
-- ============================================================================
|
|
-- LIQUID FILTER SPECIFICATION
|
|
-- Note: Allium does not have a 'filter' keyword. Filters are documented here
|
|
-- for reference but are implemented in the template engine, not in Allium specs.
|
|
-- ============================================================================
|
|
|
|
-- Built-in filters:
|
|
-- default: {{ value | default: "fallback" }}
|
|
-- escape: {{ text | escape }}
|
|
-- url_encode: {{ text | url_encode }}
|
|
-- append: {{ text | append: suffix }}
|
|
--
|
|
-- Custom filters:
|
|
-- i18n: {{ "key" | i18n: language }} - translation lookup
|
|
-- markdown: {{ content | markdown: ... }} - markdown rendering with macro expansion
|
|
|
|
-- ============================================================================
|
|
-- BUILT-IN MACROS
|
|
-- Note: Allium does not have a 'macro' keyword. Macros are documented here
|
|
-- for reference but are implemented by the rendering subsystem.
|
|
-- ============================================================================
|
|
|
|
-- Built-in macros:
|
|
-- gallery: [[gallery images=post.linked_media columns=3]]
|
|
-- youtube: [[youtube id=dQw4w9WgXcQ]]
|
|
-- vimeo: [[vimeo id=123456789]]
|
|
-- photo_archive: [[photo_archive media=project.media]]
|
|
-- tag_cloud: [[tag_cloud tags=Tags]]
|
|
|
|
-- ============================================================================
|
|
-- TEMPLATE LOOKUP RULES
|
|
-- ============================================================================
|
|
|
|
invariant TemplateLookupPriority {
|
|
-- Templates are resolved in this order:
|
|
-- 1. Post-specific template (post.template_slug)
|
|
-- 2. Tag-specific template (tag.post_template_slug)
|
|
-- 3. Category-specific template (category.post_template_slug)
|
|
-- 4. Default "post" template
|
|
--
|
|
-- List pages use "list" template
|
|
-- 404 pages use "not_found" template
|
|
-- Partials are referenced via {% render 'partial' %}
|
|
}
|
|
|
|
invariant PartialResolution {
|
|
-- Partials are looked up in templates/ directory
|
|
-- Usage: {% render 'partials/header', context_var: value %}
|
|
-- Partial files: templates/partials/{name}.liquid
|
|
}
|
|
|
|
-- ============================================================================
|
|
-- RENDERING RULES
|
|
-- ============================================================================
|
|
|
|
rule RenderPostPage {
|
|
when: RenderPostPageRequested(post, language)
|
|
let template = resolve_template(post)
|
|
let context = BuildPostContext(post, language)
|
|
ensures: RenderedHtml = liquid_render(template.content, context)
|
|
}
|
|
|
|
rule RenderListPage {
|
|
when: RenderListPageRequested(posts, pagination, archive_context)
|
|
let template = first (t in Templates where t.kind = "list" and t.enabled)
|
|
let context = BuildListContext(posts, pagination, archive_context)
|
|
ensures: RenderedHtml = liquid_render(template.content, context)
|
|
}
|
|
|
|
rule RenderNotFoundPage {
|
|
when: RenderNotFoundPageRequested()
|
|
let template = first (t in Templates where t.kind = "not_found" and t.enabled)
|
|
let context = BuildNotFoundContext
|
|
ensures: RenderedHtml = liquid_render(template.content, context)
|
|
}
|