From 2d1b4642e26eb02ca7add49c0ec3e06532765a4e Mon Sep 17 00:00:00 2001 From: nesquena-hermes <[email protected]> Date: Mon, 1 Jun 2026 21:20:42 +0000 Subject: [PATCH] feat: syntax highlighting in workspace file preview (#3337, @mysoul12138) --- static/style.css | 1 + static/workspace.js | 38 +++++++++++++++++++++++++++++++++++++- 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/static/style.css b/static/style.css index d755b435..80911d8a 100644 --- a/static/style.css +++ b/static/style.css @@ -1957,6 +1957,7 @@ .preview-area.visible{display:flex;opacity:1;} .preview-path{font-size:11px;color:var(--muted);padding-bottom:8px;border-bottom:1px solid var(--border);flex-shrink:0;} .preview-code{font-family:"SF Mono","Fira Code",ui-monospace,monospace;font-size:12px;line-height:1.6;white-space:pre-wrap;word-break:break-word;color:var(--pre-text);} + .preview-code code[class*="language-"]{background:var(--code-bg) !important;} /* Image preview */ .preview-img-wrap{display:flex;align-items:center;justify-content:center;flex:1;padding:8px 0;min-height:0;} .preview-img{max-width:100%;max-height:100%;object-fit:contain;border-radius:6px;box-shadow:0 2px 12px rgba(0,0,0,.4);} diff --git a/static/workspace.js b/static/workspace.js index 5cc5378d..6c911ca9 100644 --- a/static/workspace.js +++ b/static/workspace.js @@ -573,6 +573,32 @@ function cancelEditMode(){ updateEditBtn(); } +// Map file extensions to Prism.js language identifiers. +// Prism autoloader fetches missing language components from CDN on demand. +const _PRISM_LANG_MAP={ + js:'javascript',mjs:'javascript',jsx:'jsx',ts:'typescript',tsx:'tsx', + py:'python',pyw:'python',pyi:'python', + rb:'ruby',go:'go',rs:'rust',java:'java',kt:'kotlin',kts:'kotlin', + c:'c',h:'c',cpp:'cpp',cxx:'cpp',hpp:'cpp',cc:'cpp', + cs:'csharp',swift:'swift',scala:'scala', + php:'php',pl:'perl',pm:'perl',r:'r',lua:'lua', + sh:'bash',bash:'bash',zsh:'bash',fish:'bash', + ps1:'powershell',psm1:'powershell', + sql:'sql',graphql:'graphql', + json:'json',yaml:'yaml',yml:'yaml',toml:'toml',xml:'xml', + html:'markup',htm:'markup',svg:'markup',vue:'markup', + css:'css',scss:'scss',sass:'sass',less:'less', + md:'markdown',markdown:'markdown', + dockerfile:'docker',makefile:'makefile',cmake:'cmake', + ini:'ini',cfg:'ini',conf:'ini',properties:'properties', + diff:'diff',patch:'diff', + txt:'',log:'',csv:'',tsv:'', +}; +function _prismLanguageForPath(path){ + const ext=fileExt(path).replace(/^\./,''); + return _PRISM_LANG_MAP[ext]!==undefined?_PRISM_LANG_MAP[ext]:'plaintext'; +} + async function openFile(path, opts={}){ if(!S.session)return; const ext=fileExt(path); @@ -659,7 +685,17 @@ async function openFile(path, opts={}){ return; } showPreview('code'); - $('previewCode').textContent=data.content; + // Syntax highlighting with Prism.js (already loaded on the page). + const codeEl=document.createElement('code'); + codeEl.textContent=data.content; + const lang=_prismLanguageForPath(path); + if(lang) codeEl.className='language-'+lang; + const pre=$('previewCode'); + pre.textContent=''; + pre.appendChild(codeEl); + if(typeof Prism!=='undefined'&&typeof Prism.highlightElement==='function'){ + Prism.highlightElement(codeEl); + } }catch(e){ // If it's a 400/too-large error, offer download instead downloadFile(path);