Booking.com · VS Code Extension

TMPL Preview

Render .inc and .tmpl affiliate templates locally inside VS Code — with real mock variables, injected page bundles, multi-language support, and AI-assisted mock generation. No server, no deployment.

Published Extension HTML::Template BHC dialect affiliate_data · mesgen · book · extranet

Get started

1 · Install the extension

Search for TMPL Preview in the VS Code Extensions panel (publisher: saabbas) and click Install.

2 · Open a template

Open any .inc or .tmpl file from affiliate_data, then run TMPL: Open Preview to the Side from the command palette (⌘⇧P).

3 · Configure code root (optional but recommended)

Open the TMPL Preview settings panel (gear icon in the sidebar) and set Code root to your ~/git_tree folder. Then tick the repos to search — this unlocks role detection and AI-assisted mocks.

What it does

A full rendering pipeline — in your editor, offline.

🖼️

Live template render

Tokenizes and evaluates TMPL_VAR, TMPL_IF, TMPL_LOOP, TMPL_FOR, TMPL_CASE, TMPL_WITH, tilde-trim, and more — the full BHC dialect your templates actually use.

🎨

Full page CSS injection

Walks template_specific_stylesheets.inc → resolves .concat.css bundles → renders TMPL inside CSS → injects BUI tokens as --bui_color_* variables. What you see matches production styling.

🎭

Mock variable sidebar

Three-tab panel: Values (variables, loops, functions), Translations, Assets. Edit any value and the preview re-renders live. Values persist per-file in .tmpl-preview/.

AI-assisted mock generation

Click Fill with AI — the extension sends the template + its action file + any test fixtures to Claude and populates realistic values. Scope-aware: loop-local variables go into the right loop, not the root.

🔍

Role & context detection

Heuristically detects the role (book, mesgen, extranet, app). For .inc fragments, shows which parent templates include it and lets you pick the render context.

🌍

45-locale translations

Switch between en-gb, de, ja, ar, and 41 other locales. Translation cache reloads instantly; missing keys render as humanised fallbacks instead of raw [[key_name]] noise.

In action

review_invite_email_no_photo.inc — Preview
TMPL PREVIEW
role unknown standalone
reviewmg_invitation.tmpl:1
0 CSS · 0 JS
Values 46/48 Translations
b_hotel_name
b_checkin_localized
csp_nonce
Booking.com
Hi

How was your stay at

Test Hotel

4 nights Amsterdam

Jan 09 — Jan 11

How was your stay at Test Hotel?

😞
poor
😐
fair
🙂
good
😁
excellent

Preview renders the full template with mock values. The sidebar shows all variables, their current values, and flags anything missing.

Missing variables banner

Missing: 4 vars, 0 fns
Vars: X-BME-Format, X-BME-PlainText-URL, X-BME-Recipient-Email, X-BME-ReplyTo-Email

Banners appear above the preview for unresolved includes, empty output, script load failures, and missing variables. Nothing is silent.

AI fill

Click Fill with AI next to any missing variable cluster and Claude generates contextually accurate values — hotel names, dates, URLs — matching the template's role and structure.

Import from test fixture

No API key? Click Import from test to pull values from a .t test file in the handler repo. Often more accurate than AI for state-heavy pages.

Settings at a glance

Gear icon in the TMPL Preview sidebar header — everything in one place, no settings.json hunting.

Integration

Code root

Parent folder containing your repos (e.g. ~/git_tree). Sub-directories appear as searchable repos.

Repositories to search

Pill multi-select. Toggle which repos feed role detection and AI mock generation.

Role segment depth

How many path segments below the repo root form the role label. Default: 1.

AI Assistance

Use Claude CLI as fallback

When role/parent detection fails, asks your local claude CLI. Off by default — prompts and results stay local.

Allowed actions

Granular opt-in: find role · suggest mock values · describe template. Each is independently toggleable.

Preview Defaults

Auto-inject sibling .css / .js

Master toggle. Per-role override table lets you e.g. enable CSS but skip JS for mesgen.

Translation language

45-locale dropdown. Change applies on next render; translation cache is per-language.

Static URL prefix (CDN)

Prefix for resolving CDN URLs in the rendered output. Default: https://t-cf.bstatic.com.

Capabilities & limits

Can do

  • Render any .inc or .tmpl file from affiliate_data with full CSS/JS injection
  • Evaluate TMPL_VAR, TMPL_IF/UNLESS, TMPL_LOOP, TMPL_FOR, TMPL_CASE/WHEN, TMPL_WITH, TMPL_SETVAR, TMPL_INLINE
  • Resolve and TMPL-render CSS/JS bundle chains (.concat.css) including BUI design tokens
  • Scope-aware mock inference — loop-local variables appear inside the correct loop, not at root
  • AI mock generation via Anthropic API or local Claude CLI
  • Import mock values from Perl test fixtures (.t files) — no API key needed
  • 45-locale translation with humanised fallbacks for missing keys
  • Context chip strip — preview an .inc under any of its parent templates' CSS/JS context
  • Open in browser with full asset chain injected via file:// paths (macOS)
  • Banners for parse errors, unresolved includes, missing variables, script failures — nothing is silent

Current limits

  • Interactive scripts run only in the browser. jQuery, Prototype, and other runtime libs can't execute inside the VS Code webview (CSP). Use Open in browser for interactive testing.
  • Exotic BHC helpers are approximated. The TS renderer covers ~95% of the dialect. Helpers like TMPL_FRAGMENT, complex action-supplied vars, and locale-aware translation require the real Perl engine (Perl bridge scaffolded, not yet wired).
  • Role detection is heuristic only. Path-based + filename-based inference covers most cases. Authoritative repo-grep backend (workstream 1) is scaffolded but not yet running — role can be manually overridden per-file.
  • Foundation bundles are hardcoded per role. Conditional CSS loaded under specific b_action branches (iPhone, RTL, mlt, cancellation_tpi…) are NOT auto-injected. Toggle them manually in the Assets tab.
  • Missing translations render as humanised text. A key like ugcc_review_form_header__property_name shows as "Property name" — readable but not the real translated string.
  • No dev-menu in the browser view yet. Editing mock variables requires going back to the VS Code sidebar (browser dev-menu is on the roadmap, workstream 6).
  • BUI libs: vendor paths unresolved. Files like libs:bui/9.9.8/… aren't vendored in affiliate_data — 3 bundles show a small gap in low-level BUI base styles.
What TMPL Preview is not: it does not replace a staging environment, a Perl test runner, or production rendering. It's a fast-feedback local tool — get a visual sense of your template, iterate on mock data, and catch obvious rendering bugs before pushing.

Roadmap

What's shipping next — roughly in order.

Scaffolded

Perl bridge — real BHC rendering

Option-B Bazel bridge (render.pl + bridge.ts) scaffolded, awaiting wiring into the extension. Will add a Re-render with Perl button for 100%-faithful output.

Backend pending

Authoritative role detection (workstream 1)

Async repo-grep that searches codeRoot repos for the template basename in handler files. Settings UI is done; the grep + role-map cache backend is the remaining work.

Planned

Dev-menu in browser view (workstream 6)

Floating overlay in the exported HTML: edit mock variables, hit Apply, preview re-renders in the browser without going back to VS Code. Local HTTP server + poll-based refresh.

Planned

Patch-protocol sidebar updates

Diff-based DOM updates instead of full HTML replacement. Eliminates the flash on fast typing in the variable inputs.

Common questions

The preview looks unstyled. What's wrong?

Check that Code root and at least one repo (usually affiliate_data) are configured in Settings. The bundle discovery pipeline needs to locate affiliates/bookings2/…concat.css. Watch View → Output → TMPL Preview for discovery logs.

Variables I set in the sidebar aren't changing the preview.

The variable may be a loop-local rather than a root variable. Check the scope chip next to the input — if it says loop: b_xyz, the value lives inside the loop array. Make sure the loop has at least one item in it (the Values tab shows loop arrays as expandable rows).

The AI mock generation is empty / refuses to run.

Set your Anthropic API key via the command palette: TMPL: Set API Key. Key is stored in VS Code's secure secret storage, never in settings files. Alternatively use Import from test — it pulls values directly from .t test files with no API key required.

Can I use this for non-affiliate_data templates?

Yes, but CSS bundle discovery is tuned to the affiliates/bookings2/ structure. Standalone templates (no include chain) will render immediately; the bundle injection just won't find anything to inject.

Why does my template show untranslated keys like "Num nights city"?

That's the humanised fallback for a translation key the extension can't resolve. Real translations come from _translations.json — if that file isn't present in the workspace, keys are displayed as readable human text instead of raw [[key_name]] noise.

The role pill says "role unknown". How do I fix it?

Click the role pill and pick from the QuickPick — the manual override persists per-file. The heuristic covers most files but may mis-classify templates that don't follow naming conventions. Authoritative repo-grep backend is coming (workstream 1).