/** * "Made with HuggingChat" badge injected into artifacts deployed to a static * Space. Previews never get it — only the shipped `index.html`. * * The artifact page is arbitrary author HTML/CSS, so the badge is isolated on * both sides: * - its markup lives in a **closed shadow root**, so page selectors (and page * scripts) can't reach in, and the badge's own CSS can't leak out; * - the host element's layout/paint properties are set as **inline * `!important`** declarations, which outrank every author stylesheet rule * (including `!important` ones), so a stray `div { display: none }` or * `* { position: static }` can't hide or dislodge it. `all: initial` is set * first to drop inherited values (font, color, visibility, transform…). * * A MutationObserver re-attaches the host if the page replaces `document.body` * content wholesale (common in artifacts that render a whole app into the body). */ const END_SCRIPT_TAG = ""; /** Attribute marking the badge host, also used as the idempotency check. */ export const BADGE_HOST_ATTRIBUTE = "data-huggingchat-badge"; const BADGE_URL = "https://huggingface.co/chat"; const BADGE_LABEL = "Made with HuggingChat"; // Same mark as static/huggingchat/logo.svg, inlined so the badge stays // self-contained (a deployed Space must not depend on chat-ui being reachable). const BADGE_LOGO = ``; const BADGE_SHADOW_HTML = `${BADGE_LOGO}${BADGE_LABEL}`; // Inline host styles, each applied with `important` so no author rule can win. // Order matters: `all` resets everything, so it must come first. const HOST_STYLES: Array<[string, string]> = [ ["all", "initial"], ["position", "fixed"], ["right", "12px"], ["bottom", "12px"], ["z-index", "2147483647"], ["display", "block"], ["visibility", "visible"], ["opacity", "1"], ["pointer-events", "auto"], ["width", "auto"], ["height", "auto"], ["margin", "0"], ["padding", "0"], ["max-width", "none"], ["max-height", "none"], ["transform", "none"], ["filter", "none"], ["clip-path", "none"], ["color-scheme", "light"], ]; function buildBadgeScript(): string { const shadowHtml = JSON.stringify(BADGE_SHADOW_HTML).replace(/<\//g, "<\\/"); const hostStyles = JSON.stringify(HOST_STYLES); return `` would * terminate the artifact's script and corrupt the page — and scanning for the * *last* match is quadratic on content that repeats the tag. Appending has * neither problem and produces the same result in the browser. */ export function appendHuggingChatBadge(html: string): string { return html + "\n" + buildBadgeScript() + "\n"; }