Status bars, collapsible thoughts, clickable choices — without a desktop browser

The tavern community's most vibrant craft is beautification: status bars, themed chat skins, interactive choice menus, all built from regex scripts and HTML. That ecosystem grew up inside desktop browsers. Here is how it renders on a phone in Foreverse: the marker-to-regex-to-WebView pipeline, the sandbox rules that break desktop habits, and why beautify code costs zero context tokens.

Hand-drawn ink illustration: a plain speech bubble passes through an ornate cinnabar frame and emerges gilded with ribbons, a nib pen and brush resting below

This post exists because of a bug report from a beautify author during testing: “my status bar is alive on desktop and renders as a lump of source code on your app — why?” The diagnosis took five minutes (the marker was shaped like an HTML tag, and import sanitization stripped it), but it exposed something bigger: the craft's entire body of folk knowledge assumes a desktop browser. On a phone, the rules need restating. This is that restatement.

Why beautification deserves engineering respect

Beautification is not decoration. A status bar externalizes hidden state — affection, time, location — so the player stops bookkeeping in their head. Collapsible reasoning gives long replies a table of contents. Themed skins give a card its own visual identity. The Chinese tavern community has pushed this craft remarkably far; a polished skin pack earns thousands of likes on social platforms. Those creations deserve full rendering on a phone, not a downgrade to plain text.

The three-stage pipeline and its rules

StageWhat happensThe rule
Model outputs markersEach reply carries plain-text markers like [status|happy|85] or :::thinkMarkers must be plain text, never <tag>-shaped (import strips raw HTML)
Regex replacementThe card's beautify regex turns markers into themed HTMLScope to AI output + display-only, so replacements never pollute fed-back history
WebView renderingThe product renders in a sandboxed WebView, styled as the author intendedSelf-contained HTML+CSS; one isolated scope per fence; height auto-reported to size the bubble

The display-only flag in stage two is the token story. The model's history stays clean markers forever; your HTML lives purely in the display layer. The older habit of pasting HTML straight into greetings burns context every single turn — the regex pipeline costs nothing extra.

The mobile sandbox: which desktop habits break

Three prohibitions and one safety net. No external resources: CDN libraries, remote fonts, and hotlinked images simply never load; inline everything. No viewport-filling styles: min-height: 100vh fights the auto-height mechanism and inflates bubbles absurdly. No local file access. The safety net: if a regex replaces content into emptiness, the original text renders instead — your bug can disable the beautify, but it cannot swallow the prose.

On the capability side: inline CSS and JS both run; the host API subset covers triggerSlash, getChatMessages, getWorldbook, and a real generate(); each html fence gets an isolated scope, so two components' styles never collide.

Two protocols for people who refuse to write regex

Besides the full pipeline there are two low-floor options. fv-card puts JSON in a fence and renders native mini-cards (notes, countdowns, mood cards) with no WebView at all. And [[sticker:name]] pulls a same-named image from the character's sticker folder. Anniversary reminders and companion sticker packs take about five minutes with these.

Closing note

The value of this ecosystem belongs to its creators; the platform's job is stable rendering semantics and clearly written rules. This covers the 90% you hit daily; the remaining 10% — capture-group limits, macro expansion timing, the full host API table — is itemized in the beautify spec. If a card renders differently on the phone than on desktop, send the card and screenshots to [email protected]; rendering parity is this feature's acceptance bar, so it gets P1 treatment.

FAQ

Do beautified cards tuned on desktop SillyTavern work directly on the phone?

Behavioral parity is the goal: when a card carries HTML beautify regex, import auto-enables rich rendering for it, and the marker-to-regex-to-HTML chain mirrors desktop semantics. What breaks are two desktop habits: external resources (CDN jQuery, remote fonts, hotlinked images) never load inside the mobile sandbox, and scripts that reach into the parent page's DOM have no equivalent. Self-contained HTML+CSS cards port cleanly.

Does beautify code eat my context tokens?

No, and that is the point of the pipeline. The model outputs only short plain-text markers each turn — a few dozen tokens. The hundreds of lines of HTML live in the regex replacement string and apply at display time only. With the display-only flag set, replacements never enter the history fed back to the model: it always sees clean markers, and your HTML costs zero tokens.

Why can't my marker look like an HTML tag?

Import sanitization. Raw HTML tags inside card text fields are stripped at import (standard hygiene for untrusted card content), so a marker shaped like <status> never survives to rendering. The rule: markers are plain-text symbols (:::think, [status|...]), and HTML exists only inside regex replacement strings.

Can interactive beautification work on mobile?

Yes. The WebView exposes a working subset of the host API: triggerSlash runs commands like /setinput, /send and /trigger; getChatMessages reads the transcript; generate() genuinely triggers a second generation. Clickable choice menus and buttons work, and buttons suppress text selection so tapping an option never accidentally selects prose.

Questions or ideas? Join our Discord →

How Character-Card Beautification Survives on a Phone · Foreverse · Xinmeng