diff options
Diffstat (limited to 'scripts/theme-studio/previews.js')
| -rw-r--r-- | scripts/theme-studio/previews.js | 89 |
1 files changed, 73 insertions, 16 deletions
diff --git a/scripts/theme-studio/previews.js b/scripts/theme-studio/previews.js index cb9d5babe..afba6b0c9 100644 --- a/scripts/theme-studio/previews.js +++ b/scripts/theme-studio/previews.js @@ -24,9 +24,26 @@ function previewSpan(owner,face,text){ return `<span data-owner-app="${owner}" data-face="${face}"${cls} title="${title}" style="${style}">${text}</span>`; } function os(app,face,txt){return previewSpan(app,face,txt);} -// Shared wrapper for the line-based package previews: a monospace pre block. +// Preview font stack: the embedded @font-face (family "ThemeStudioNerd", +// Symbols Nerd Font Mono inlined as a data: URI in styles.css) supplies the nerd +// glyphs; monospace supplies everything else. The family name is deliberately +// custom, NOT the real "Symbols Nerd Font Mono": when the @font-face name matches +// a font the user has installed system-wide, Chrome resolves the family to the +// local copy instead of our embedded one and the glyphs render as tofu (the +// embedded font only wins in environments without that system font, e.g. headless +// CI). A unique family name forces the embedded font. "ThemeStudioNerd" carries +// only icon glyphs, so plain text falls through to monospace and the layout is +// unchanged — only the nerd codepoints pull from the embedded font. +// NOTE: the family name is UNQUOTED here on purpose. PREVIEW_FONT is interpolated +// into inline style="..." attributes (previewLines, genericPreview, the mock +// frame), and a double-quoted family name inside a double-quoted attribute +// terminates the attribute early, silently dropping the font-family (the glyphs +// then fall back to monospace = tofu). A no-space identifier needs no quotes, so +// keep ThemeStudioNerd quote-free and never reintroduce a spaced/quoted name here. +const PREVIEW_FONT='ThemeStudioNerd,monospace'; +// Shared wrapper for the line-based package previews: a nerd-font pre block. // Each renderer builds its own L array of os(...) lines and returns previewLines(L). -function previewLines(L){return `<div style="padding:12px 16px;font:12pt/1.7 monospace;white-space:pre">${L.join('\n')}</div>`;} +function previewLines(L){return `<div style="padding:12px 16px;font:12pt/1.7 ${PREVIEW_FONT};white-space:pre">${L.join('\n')}</div>`;} function renderOrgPreview(){const a='org-mode',L=[]; L.push(os(a,'org-document-info-keyword','#+TITLE:')+' '+os(a,'org-document-title','Project Notes')); L.push(os(a,'org-document-info-keyword','#+AUTHOR:')+' '+os(a,'org-document-info','Craig Jennings')); @@ -153,21 +170,21 @@ function renderDashboardPreview(){const a='dashboard',L=[]; L.push(''); L.push(''); L.push(os(a,'dashboard-heading','Projects:')); - L.push(' ~/'); - L.push(' ~/.emacs.d/'); - L.push(' ~/projects/work/'); - L.push(' ~/org/roam/'); - L.push(' ~/projects/home/'); + L.push(os(a,'dashboard-items-face',' ~/')); + L.push(os(a,'dashboard-items-face',' ~/.emacs.d/')); + L.push(os(a,'dashboard-items-face',' ~/projects/work/')); + L.push(os(a,'dashboard-items-face',' ~/org/roam/')); + L.push(os(a,'dashboard-items-face',' ~/projects/home/')); L.push(''); L.push(os(a,'dashboard-heading','Bookmarks')); - L.push(' Cesar Aira, The Little Buddhist Monk & the Proof'); - L.push(' Edward Abbey, The Fool’s Progress: An Honest Novel'); - L.push(' Agatha Christie, The A.B.C. Murders'); + L.push(os(a,'dashboard-items-face',' Cesar Aira, The Little Buddhist Monk & the Proof')); + L.push(os(a,'dashboard-items-face',' Edward Abbey, The Fool’s Progress: An Honest Novel')); + L.push(os(a,'dashboard-items-face',' Agatha Christie, The A.B.C. Murders')); L.push(''); L.push(os(a,'dashboard-heading','Recent Files:')); - L.push(' theme-theme.el'); - L.push(' todo.org'); - L.push(' theme-studio-palette-generator-spec.org'); + L.push(os(a,'dashboard-items-face',' theme-theme.el')); + L.push(os(a,'dashboard-items-face',' todo.org')); + L.push(os(a,'dashboard-items-face',' theme-studio-palette-generator-spec.org')); return previewLines(L);} function renderMu4ePreview(){const a='mu4e',L=[]; const pad=(s,n)=>{s=String(s);return s.length>=n?s.slice(0,n):s+' '.repeat(n-s.length);}; @@ -276,6 +293,18 @@ function renderGitGutterPreview(){const a='git-gutter',L=[]; L.push(os(a,'git-gutter:deleted','_')+os(a,'git-gutter:separator','|')+' (deleted lines marker)'); L.push(os(a,'git-gutter:unchanged',' ')+os(a,'git-gutter:separator','|')+' '+os(a,'git-gutter:unchanged','unchanged line of code')); return previewLines(L);} +function renderEatPreview(){const a='eat',L=[]; + L.push(os(a,'eat-shell-prompt-annotation-success','✔')+' ~/projects $ ls --color'); + L.push(os(a,'eat-term-color-blue','build/')+' '+os(a,'eat-term-color-blue','src/')+' '+os(a,'eat-term-color-green','run.sh')+' README.md'); + L.push(''); + L.push('palette '+os(a,'eat-term-color-black','■')+os(a,'eat-term-color-red','■')+os(a,'eat-term-color-green','■')+os(a,'eat-term-color-yellow','■')+os(a,'eat-term-color-blue','■')+os(a,'eat-term-color-magenta','■')+os(a,'eat-term-color-cyan','■')+os(a,'eat-term-color-white','■')+' normal'); + L.push(' '+os(a,'eat-term-color-bright-black','■')+os(a,'eat-term-color-bright-red','■')+os(a,'eat-term-color-bright-green','■')+os(a,'eat-term-color-bright-yellow','■')+os(a,'eat-term-color-bright-blue','■')+os(a,'eat-term-color-bright-magenta','■')+os(a,'eat-term-color-bright-cyan','■')+os(a,'eat-term-color-bright-white','■')+' bright'); + L.push(''); + L.push(os(a,'eat-term-bold','bold')+' '+os(a,'eat-term-faint','faint')+' '+os(a,'eat-term-italic','italic')+' '+os(a,'eat-term-slow-blink','slow-blink')+' '+os(a,'eat-term-fast-blink','fast-blink')); + L.push(''); + L.push(os(a,'eat-shell-prompt-annotation-running','…')+' running tests'); + L.push(os(a,'eat-shell-prompt-annotation-failure','✘')+' build failed'); + return previewLines(L);} function renderFlycheckPreview(){const a='flycheck',L=[]; L.push(os(a,'flycheck-fringe-error','E')+os(a,'flycheck-fringe-warning','W')+os(a,'flycheck-fringe-info','I')+' x = '+os(a,'flycheck-error','undefined_name')+'('+os(a,'flycheck-warning','unused_arg')+') '+os(a,'flycheck-info','# note')); L.push(' '+os(a,'flycheck-error-delimiter','[')+os(a,'flycheck-delimited-error','err')+os(a,'flycheck-error-delimiter',']')); @@ -402,7 +431,7 @@ function renderTelegaPreview(){const a='telega',L=[]; L.push(os(a,'telega-link-preview-sitename','example.com')+' '+os(a,'telega-link-preview-title','Link preview title')); L.push('Webpage '+os(a,'telega-webpage-title','Title')+' '+os(a,'telega-webpage-subtitle','Subtitle')+' '+os(a,'telega-webpage-header','Header')+' '+os(a,'telega-webpage-subheader','Subheader')+' '+os(a,'telega-webpage-outline','outline')+' '+os(a,'telega-webpage-fixed','fixed')+' '+os(a,'telega-webpage-preformatted','pre')+' '+os(a,'telega-webpage-marked','marked')+' '+os(a,'telega-webpage-strike-through','strike')+' '+os(a,'telega-webpage-chat-link','chat-link')); return previewLines(L);} -function genericPreview(app){let h='<div style="padding:10px 14px;font:12pt/1.8 monospace">';for(const [face,label] of APPS[app].faces)h+=`<div data-face="${face}" style="${ofs(app,face)}">${esc(label)}</div>`;return h+'</div>';} +function genericPreview(app){let h='<div style="padding:10px 14px;font:12pt/1.8 '+PREVIEW_FONT+'">';for(const [face,label] of APPS[app].faces)h+=`<div data-face="${face}" style="${ofs(app,face)}">${esc(label)}</div>`;return h+'</div>';} // Bespoke split preview: a focused window beside its auto-dimmed twin, both // showing the language selected at the top of the page (kept in sync via the // langsel onchange, which re-runs buildPkgPreview). The left pane carries the @@ -432,8 +461,8 @@ function renderAutodimPreview(){ const accent=uf('cursor').bg||'#67809c'; const pane=(label,body,bg,focused)=> `<div style="flex:1;min-width:20ch;border:${focused?'2px solid '+accent:'1px solid #2a2a2a'};border-radius:4px;overflow:hidden">` - +`<div style="text-align:center;font:bold 10pt monospace;padding:4px;color:${focused?'#cdced1':'#8a8a8a'};background:${focused?'#1a1a1a':'#0a0a0a'};border-bottom:1px solid #2a2a2a">${label}</div>` - +`<div style="padding:10px 12px;font:12pt/1.6 monospace;white-space:pre;background:${bg}">${body}</div></div>`; + +`<div style="text-align:center;font:bold 10pt ${PREVIEW_FONT};padding:4px;color:${focused?'#cdced1':'#8a8a8a'};background:${focused?'#1a1a1a':'#0a0a0a'};border-bottom:1px solid #2a2a2a">${label}</div>` + +`<div style="padding:10px 12px;font:12pt/1.6 ${PREVIEW_FONT};white-space:pre;background:${bg}">${body}</div></div>`; const litBody=lit+'\n'+`<span style="color:#5e6770">${esc(foldText)}</span>`; const dimBody=`<span data-face="auto-dim-other-buffers" style="color:${dimFg}">${dim}</span>\n` +`<span data-face="auto-dim-other-buffers-hide" style="color:${hideFg};background:${hideBg}">${esc(foldText)}</span>`; @@ -481,3 +510,31 @@ function renderMarkdownPreview(){const a='markdown-mode',L=[]; L.push(os(a,'markdown-html-tag-delimiter-face','<')+os(a,'markdown-html-tag-name-face','kbd')+os(a,'markdown-html-tag-delimiter-face','>')+'Ctrl-C'+os(a,'markdown-html-tag-delimiter-face','</')+os(a,'markdown-html-tag-name-face','kbd')+os(a,'markdown-html-tag-delimiter-face','>')); L.push(os(a,'markdown-footnote-marker-face','[^1]:')+' '+os(a,'markdown-footnote-text-face','the footnote text.')); return previewLines(L);} +// nerd-icons gallery grid: the full colored catalog. Every distinct face-bearing +// nerd-icons glyph (APPS['nerd-icons'].gallery, captured by build-nerd-icons-legend.el), +// one row per color face, the rows ordered by hue so families cluster (blues +// together, reds together). Each cell draws the glyph in its face color with the +// icon's nerd-font name beneath. SIZEPT (points, default 14) sizes the glyphs so +// the designer can view the grid at different buffer sizes via the preview-pane +// dropdown; the cell width scales with it. Recoloring a face repaints its swatch +// and every glyph in its row because os() reads the live registry. Falls back to +// the generic preview if the gallery is missing (the bespoke app registers with a +// valid legend, so that path is defensive). +function renderNerdIconsPreview(sizePt){ + const a='nerd-icons',groups=(APPS[a]&&APPS[a].gallery)||[]; + if(!groups.length)return genericPreview(a); + const pt=sizePt||14,cellW=Math.round(pt*4.6+24); + let h=`<div class="ni-gallery" style="padding:10px 14px;font:10pt/1.4 ${PREVIEW_FONT}">`; + for(const g of groups){ + h+='<div class="ni-row" style="margin:0 0 10px;border-top:1px solid #2a2a2a;padding-top:6px">' + +`<div class="ni-row-head" style="color:#8a8a8a;padding:0 0 5px">` + +os(a,g.face,'■')+' '+esc(g.face)+' ('+g.glyphs.length+')</div>' + +'<div class="ni-cells">'; + for(const e of g.glyphs) + h+=`<span class="ni-cell" style="display:inline-block;width:${cellW}px;text-align:center;vertical-align:top;margin:3px 1px">` + +`<span style="font-size:${pt}pt;line-height:1.3">`+os(a,g.face,e.glyph)+'</span><br>' + +`<span style="font-size:7.5pt;color:#9a9a9a;word-break:break-all;line-height:1.2">`+esc(e.name)+'</span>' + +'</span>'; + h+='</div></div>'; + } + return h+'</div>';} |
