Bengal 0.2.6
Double-buffered dev server, i18n gettext workflow, Jinja2 removal, navigation and validation performance, new output generators
Key additions: Double-buffered dev server that eliminates flash-of-unstyled-content during rebuilds, a gettext PO/MO workflow for i18n, Jinja2 engine removal (Kida is now the only template engine), O(n²)→O(1) page navigation, new output format generators, and orchestration improvements for build metrics and diagnostics.
Highlights
Double-Buffered Dev Server
The dev server now uses a double-buffered output strategy to eliminate FOUC (flash of unstyled content) during rapid rebuilds. Instead of writing directly to the output directory while the server is serving it, the build writes to an alternate buffer directory and atomically swaps it in when complete.
- BufferManager: New component manages two output directories and provides atomic swap
- ASGI integration: The ASGI app accepts a callable that returns the current active directory, so each request sees a consistent snapshot
- Zero-disk reactive path: Content-only edits can skip full disk writes entirely
- In-process builds: Dev server builds now run in-process for cleaner Ctrl+C shutdown (no zombie subprocesses)
i18n Gettext Workflow (Phase 1A)
Newbengal i18ncommand group for gettext PO/MO translation workflows:
bengal i18n extract— Scan templates fort()calls and generate.pottemplate filesbengal i18n compile— Compile.pofiles to.mobinary formatbengal i18n status— Show translation coverage per locale with color-coded percentages
The underlyingi18n/catalog.pyprovides PO/MO catalog management and coverage computation. Exception handling in i18n commands uses narrow, specific catches.
Jinja2 Engine Removed
Jinja2 is no longer a built-in template engine. Kida is now the canonical (and only) engine.
- Removed
jinja.pyengine and adapter - Removed Jinja2 from
detect_adapter_type()logic - If you were using
template_engine: jinja2in your config, switch totemplate_engine: kida. Kida uses Jinja2-compatible syntax, so most templates work without changes. See the Kida migration guide for details.
O(n²)→O(1) Page Navigation
Previous/next page lookups usedlist.index(), scanning the full page list for every call. On a site with N pages this was O(n²) total work.
Navigation now uses lazily-built dicts keyed by page identity. The index is constructed once on first access and invalidated when the page list grows (e.g., as taxonomy pages are appended). Each lookup is O(1).
Applies to:page.next, page.prev, page.next_in_section, page.prev_in_section.
Faster Cross-Reference Validation
The health validator previously recomputed line numbers by slicing the content string on every match. Newline positions are now precomputed once and binary-searched withbisect_left, reducing validation from O(M×N) to O(N + M log N) per page.
New Output Generators
The postprocess pipeline gains several new output format generators:
agent_manifest_generator— Machine-readable manifest for AI agent consumptionchangelog_generator— Generates changelog pages from structured release datallms_txt_generator— Produces llms.txt output for LLM discoveryrobots.txtgenerator — Configurable robots.txt with sitemap references
Orchestration Improvements
- Block cache in WaveScheduler: Per-page rendering now leverages the block cache for faster incremental builds
- Explicit manifest context: Asset manifest context is propagated explicitly through the orchestration pipeline (before falling back to ContextVar)
- Aggregated fallback diagnostics: Instead of per-asset warnings, asset manifest fallbacks are sampled and summarized at phase end — reduces log noise on large sites
- Build metrics: Parsed/rendered cache hit rates now appear in the build summary
Other Changes
- Content Signals: New
visibility.ai_trainandvisibility.ai_inputpage properties for controlling AI training and RAG/grounding signals, with site-level defaults viacontent_signalsconfig - Tag normalization:
Frontmatter.from_dict()andPage.__post_init__()normalize malformed tag input (e.g., nested lists, None values) - Menu field rename:
url→hrefinMenuItem.to_dict();active/active_trailremoved (templates compute active state via URL comparison for cache stability) - Excerpt index: New
get_excerpt_for_path()in the cache layer provides lightweight excerpt lookups for PageProxy without triggering a full page load - list-table directive: Fixed options mapping; previously skipped tests re-enabled
- CLI output: Stale process and port-in-use messages now use
CLIOutputfor consistent formatting - RTL support: New
direction()template function for right-to-left language detection - Template validation: New
--templates-contextflag for Kida context validation to catch missing variables before render - Subdirectory detection:
bengal serveandbengal buildnow scan all subdirectories for Bengal site markers instead of hardcoding directory names - Dead code removal: Three unused methods removed from
PageOperationsMixin - Dependency bumps: kida-templates ≥0.2.8, patitas ≥0.3.5, bengal-pounce ≥0.3.0
Breaking Changes
- Jinja2 removed: If your config specifies
template_engine: jinja2, change it tokida. See the Kida migration guide. - Menu dict shape:
urlrenamed tohref;activeandactive_trailfields removed. Templates that readitem.urlshould useitem.href. Templates that relied onitem.activeshould compareitem.hrefagainst the current page URL instead.
Upgrading
uv pip install --upgrade bengal
# or
pip install --upgrade bengal
# or use the self-update command
bengal upgrade