From 9c7010ebe2041ae73195745d76403568237c2905 Mon Sep 17 00:00:00 2001 From: Craig Jennings Date: Thu, 2 Jul 2026 13:35:28 -0400 Subject: feat(theme-studio): web-mode scene covering all 81 faces + HTML sample The web-mode preview is one mixed document: markup with every tag/attr variant, an inline CSS part, a generic template block (engine-agnostic on purpose), and a script part carrying a JSON island, JSX depths, nested template literals, SQL-in-a-string, a PHP preprocessor island, and JSDoc annotations. The realism gate now covers it, so all 81 faces are exercised. SAMPLES gains an HTML language, which also enriches the syntax and auto-dim previews. --- scripts/theme-studio/previews.js | 47 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) (limited to 'scripts/theme-studio/previews.js') diff --git a/scripts/theme-studio/previews.js b/scripts/theme-studio/previews.js index 4c9474fe..0cd4d86a 100644 --- a/scripts/theme-studio/previews.js +++ b/scripts/theme-studio/previews.js @@ -592,6 +592,53 @@ function renderRainbowDelimitersPreview(){const a='rainbow-delimiters',L=[]; L.push(os(a,'rainbow-delimiters-base-face','base-face')+' underlies every depth; ' +os(a,'rainbow-delimiters-base-error-face','base-error-face')+' underlies both error faces'); return previewLines(L);} +function renderWebModePreview(){const a='web-mode',L=[],o=(f,t)=>os(a,'web-mode-'+f,t); + // One mixed HTML document exercising all 81 faces: markup, an inline CSS + // part, a JS part with a JSON island / JSX / template literals / SQL-in-a- + // string, a generic {{...}}/{%...%} template block (engine-agnostic on + // purpose), and a JSDoc-style annotation comment. + const B=(t)=>o('html-tag-bracket-face',t); + L.push(o('doctype-face','')); + L.push(o('comment-face','')); + L.push(B('<')+o('html-tag-face','html')+' '+o('html-attr-name-face','lang')+o('html-attr-equal-face','=')+o('html-attr-value-face','"en"')+B('>')); + L.push(''); + L.push(B('<')+o('html-tag-face','style')+B('>')+o('style-face',' /* the css part rides web-mode-style-face */')); + L.push(' '+o('css-comment-face','/* layout */')); + L.push(' '+o('css-at-rule-face','@media')+' (min-width: 40rem) {'); + L.push(' '+o('css-selector-tag-face','body')+' '+o('css-selector-face','#page')+' '+o('css-selector-class-face','.card')+o('css-pseudo-class-face',':hover')+' {'); + L.push(' '+o('css-property-name-face','color')+': '+o('css-color-face','#67809c')+' '+o('css-priority-face','!important')+';'); + L.push(' '+o('css-property-name-face','width')+': '+o('css-function-face','calc')+'(100% - '+o('css-variable-face','--gutter')+');'); + L.push(' '+o('css-property-name-face','font-family')+': '+o('css-string-face','"Berkeley Mono"')+'; } }'); + L.push(B('')); + L.push(''); + L.push(B('<')+o('html-tag-face','body')+' '+o('html-attr-custom-face','data-theme')+o('html-attr-equal-face','=')+o('html-attr-value-face','"dupre"')+B('>')); + L.push(' '+o('current-element-highlight-face',B('<')+o('html-tag-face','main')+B('>'))+' '+o('comment-face','')); + L.push(' '+B('<')+o('html-tag-custom-face','cart-widget')+' '+o('html-attr-engine-face',':items')+o('html-attr-equal-face','=')+o('html-attr-value-face','"cart.lines"')+B('/>')); + L.push(' '+B('<')+o('html-tag-namespaced-face','svg:rect')+' '+o('html-attr-name-face','width')+o('html-attr-equal-face','=')+o('html-attr-value-face','"12"')+B('/>')); + L.push(' '+B('<')+o('html-tag-face','p')+B('>')+'Fish '+o('html-entity-face','&')+' chips '+B('<')+o('html-tag-face','b')+B('>')+o('bold-face','bold')+B('')+' '+B('<')+o('html-tag-face','i')+B('>')+o('italic-face','italic')+B('')+' '+B('<')+o('html-tag-face','u')+B('>')+o('underline-face','underlined')+B('')+B('')); + L.push(' '+B('<')+o('html-tag-unclosed-face','li')+B('>')+'this li never closes '+o('error-face','')+' '+o('warning-face','duplicate id')+' '+o('whitespace-face','···')+' '+o('folded-face','[details folded…]')+' '+o('inlay-face',' 3 items ')+' '+o('current-column-highlight-face','│')); + L.push(''); + L.push(' '+o('block-delimiter-face','{%')+' '+o('block-control-face','if')+' '+o('variable-name-face','user')+o('block-face','.signed_in')+' '+o('block-delimiter-face','%}')+' '+o('block-comment-face','{# template block, any engine #}')); + L.push(' '+o('block-delimiter-face','{{')+' '+o('variable-name-face','user')+o('block-face','.name')+' '+o('filter-face','| capitalize')+' '+o('block-delimiter-face','}}')); + L.push(' '+o('block-delimiter-face','{%')+' '+o('block-control-face','include')+' '+o('block-string-face','"badge.html"')+' '+o('block-attr-name-face','with')+' '+o('block-attr-value-face','tier=gold')+' '+o('block-delimiter-face','%}')); + L.push(' '+o('block-delimiter-face','{%')+' '+o('block-control-face','endif')+' '+o('block-delimiter-face','%}')); + L.push(''); + L.push(B('<')+o('html-tag-face','script')+B('>')+o('script-face',' // the js part rides web-mode-script-face')+o('part-face',' (embedded parts share web-mode-part-face)')); + L.push(' '+o('comment-face','/**')); + L.push(' '+o('annotation-face',' * Render one cart row.')); + L.push(' '+o('annotation-face',' * ')+o('annotation-tag-face','@param')+' '+o('annotation-type-face','{Line}')+' '+o('annotation-value-face','line')+' '+o('annotation-face','the row, e.g. ')+o('annotation-html-face','line.sku')); + L.push(' '+o('annotation-face',' */')); + L.push(' '+o('keyword-face','const')+' '+o('constant-face','VAT')+' = '+o('variable-name-face','rate')+' ?? 0.21; '+o('javascript-comment-face','// js comment')+' '+o('part-comment-face','/* part comment */')); + L.push(' '+o('keyword-face','function')+' '+o('function-name-face','renderLine')+'('+o('param-name-face','line')+', '+o('param-name-face','fmt')+') {'); + L.push(' '+o('keyword-face','const')+' '+o('type-face','Money')+' '+o('variable-name-face','total')+' = '+o('function-call-face','round')+'('+o('variable-name-face','line')+'.'+o('symbol-face','qty')+' * '+o('builtin-face','Number')+'('+o('variable-name-face','line')+'.price));'); + L.push(' '+o('keyword-face','return')+' '+o('javascript-string-face','`')+o('interpolate-color1-face','${')+o('interpolate-color2-face','fmt(')+o('interpolate-color3-face','${')+o('interpolate-color4-face','total')+o('interpolate-color3-face','}')+o('interpolate-color2-face',')')+o('interpolate-color1-face','}')+o('javascript-string-face',' EUR`')+'; }'); + L.push(' '+o('keyword-face','const')+' '+o('variable-name-face','q')+' = '+o('part-string-face','"')+o('sql-keyword-face','SELECT')+o('part-string-face',' sku ')+o('sql-keyword-face','FROM')+o('part-string-face',' lines ')+o('sql-keyword-face','WHERE')+o('part-string-face',' qty > 0"')+';'); + L.push(' '+o('keyword-face','const')+' '+o('variable-name-face','cfg')+' = '+o('json-context-face','{ ')+o('json-key-face','"currency"')+': '+o('json-string-face','"EUR"')+o('json-context-face',', ')+o('json-comment-face','/* json island */')+o('json-context-face',' }')+';'); + L.push(' '+o('keyword-face','return')+' ('+o('jsx-depth-1-face','')+o('jsx-depth-2-face','')+o('jsx-depth-3-face','')+o('jsx-depth-4-face','')+o('jsx-depth-5-face','{n}')+o('jsx-depth-4-face','')+o('jsx-depth-3-face','')+o('jsx-depth-2-face','')+o('jsx-depth-1-face','')+');'); + L.push(' '+o('preprocessor-face','')); + L.push(B('')); + L.push(B('')); + return previewLines(L);} function renderAiTermPreview(){const a='ai-term',L=[]; // What these faces actually paint: the Claude Code TUI inside an agent // terminal. The banner is the fixed accent (every session); each /color -- cgit v1.2.3