aboutsummaryrefslogtreecommitdiff
path: root/scripts/theme-studio/previews.js
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/theme-studio/previews.js')
-rw-r--r--scripts/theme-studio/previews.js89
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','&lt;')+os(a,'markdown-html-tag-name-face','kbd')+os(a,'markdown-html-tag-delimiter-face','&gt;')+'Ctrl-C'+os(a,'markdown-html-tag-delimiter-face','&lt;/')+os(a,'markdown-html-tag-name-face','kbd')+os(a,'markdown-html-tag-delimiter-face','&gt;'));
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>';}