Three-tier responsive system for the navigation bar:
Structure:
- Each label+select pair wrapped in .ctl-group (inline-flex,
white-space:nowrap) so they never split across rows.
- Separators gain flex-shrink:0;align-self:center for stability.
Breakpoints:
- Default: single row, all controls + hint visible, compact 8px gap
- ≤900px: hint text hidden (first sacrifice for space)
- ≤700px: separators hidden, tighter gaps/padding, smaller fonts
- ≤500px: ultra-compact — min select widths, smaller brand
CSS changes:
- height → min-height so bar grows vertically when wrapping
- flex-wrap:wrap enables natural multi-row flow
- row-gap:4px keeps wrapped rows visually connected
- Separators hidden on narrow screens (gap provides spacing)
Co-Authored-By: Claude <noreply@anthropic.com>
- Delete tools/server/ (Node.js, juice, inliner.mjs, server.mjs, node_modules)
- Create tools/preview/server.go — pure Go dev server with SSE + file polling
- Rewrite tools/cli/dev.go — start Go server in-process, no Node.js required
- Simplify preview/index.html — View dropdown (Modern/Source), remove Client
Simulation panel, remove Gmail/Outlook logic, WebSocket → SSE
- Update all 14 documentation files (README, CONTRIBUTING, AGENTS + translations)
to reflect pure Go dev server and simplified preview
Co-Authored-By: Claude <noreply@anthropic.com>
Replaced neutral-gray scrollbar with muted-steel-blue
rgba(107,125,153,...) — the page's --muted colour at varying
opacities. Thumb blends with the navy-blue background palette
(#0b1120 / #151d2e / #1e2d45) rather than standing out as
an unrelated grey element.
Page-level + iframe-injected scrollbar: unified palette.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Page-level scrollbar:
- WebKit: ::-webkit-scrollbar with 3px width, transparent track,
6% white thumb → 18% on hover
- Firefox: scrollbar-width:thin + scrollbar-color on #preview-area, #panel
- Legacy Edge/IE: -ms-overflow-style: -ms-autohiding-scrollbar
- Chrome 108+: overflow:overlay via @supports (scrollbar floats,
no layout shift)
Iframe content (email preview):
- Same cross-browser treatment injected into every rendered template
- Dark email themes get dark scrollbar (rgba(0,0,0,.08))
- Light email themes also covered (6% black is subtle on any bg)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
When rendered is null, render() was setting loading.style.display='flex'
before returning — overriding the hide already done in the init error
branch. Now render() silently returns, leaving init to handle the error.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Global scrollbar: 3px wide, transparent track, 6% white thumb
that brightens to 15% on hover. Firefox thin scrollbar via
scrollbar-width:thin. Practically invisible unless needed.
- Iframe content: inject matching scrollbar CSS into every
rendered email template so the email preview itself has
the same subtle scrollbar treatment.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The _patch.cjs script accidentally stripped </script></body></html>
when replacing the WebSocket section, causing the inline JavaScript
to never terminate — the browser parsed everything as script content
and all functions were undefined.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Remove preview/rendered.js from .gitignore so the project works
out-of-the-box after clone (no Go required for static preview).
- Hide loading spinner when rendered.js is not found; previously
the spinner stayed visible forever because render() returned
early when 'rendered' was null.
- Regenerate rendered.js with all current template fixes.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- transform(): only Raw mode does HTML escaping. Modern, Gmail, Outlook
all show the same rendered HTML — in dev mode Juice handles CSS
inlining server-side; in static mode the notice bar warns.
- static-notice: change display from '' to 'block' to properly
override CSS rule #static-notice{display:none}. The empty string
did not override the stylesheet rule in static file:// mode.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Add static-notice banner: 'Gmail/Outlook simulation is approximate.
Use dev mode for accurate rendering.' Hidden in dev mode via
WebSocket try/catch, auto-shown in file:// mode on WS failure.
- Simplify transform(): Gmail/Outlook modes now return same HTML as
Modern — the dev server with Juice provides actual client sim.
- Add markSafeHTML(): converts RenderedNote/Body/RenderedContent
from plain string to template.HTML so markdown-rendered HTML
content is not escaped by Go's html/template engine.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Split monolithic build-preview.go into modular packages:
tools.go (entry), cli/ (list/create/delete/preview commands),
config/ (JSON loading), preview/ (rendering engine, funcs, locale)
- Extract all template metadata into data/templates_config.json
as single source of truth — no hardcoded template data in Go
- Replace hand-rolled CLI parsing with github.com/urfave/cli/v2
- Add sensible defaults for --folder (../themes) and --config
(./data/templates_config.json) — most commands now run bare
- Add type-coercing gt/lt/ge/le template funcs for JSON float64
- Fix AppUrl to gitea.com matching official template convention
- rendered.js now exports __REGISTRY__ and __PARAMS__ alongside
__RENDERED__ — preview/index.html reads all from single source
- Cross-validate all 11 template data contexts against official
Gitea source (services/mailer/*.go + templates/mail/*.tmpl)
- Update all docs (README, AGENTS, CONTRIBUTING, 5 languages)
to reflect new CLI workflow with create scaffolding command
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>