Created
May 4, 2025 19:31
-
-
Save dreness/7ec9799cb4fac068f7fe010dd3febf01 to your computer and use it in GitHub Desktop.
WIP Roo Code transcript viewer
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="utf-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1"> | |
<title>Roo Code Transcript Viewer</title> | |
<link rel="stylesheet" | |
href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css"> | |
<style> | |
.block { padding:1rem; border-radius:5px; } | |
.block-header { font-weight:600; text-transform:uppercase; font-size:.9rem; margin-bottom:.5rem; } | |
.block-content { white-space:pre-wrap; word-wrap:break-word; } | |
.user-block { background:#e7f3ff; border-left:4px solid #0d6efd; } | |
.user-block .block-header { color:#0d6efd; } | |
.assistant-block { background:#efffed; border-left:4px solid #198754; } | |
.assistant-block .block-header { color:#198754; } | |
.system-block { background:#f8f9fa; border-left:4px solid #6c757d; } | |
.system-block .block-header { color:#6c757d; } | |
.system-block .block-content { font-family:monospace; font-size:.95rem; } | |
.system-block h2, | |
.system-block h3, | |
.system-block h4 { font-size:1rem; margin:.5rem 0 .3rem; } | |
.task-block { background:#f3edff; border-left:4px solid #6f42c1; } | |
.task-block .block-header { color:#6f42c1; } | |
/* Error and file sub‑blocks */ | |
.error-sub { background:#fdecea; border-left:4px solid #dc3545; padding:.5rem .75rem; margin:.5rem 0; border-radius:4px; } | |
.file-sub { background:#f5f5f5; border-left:4px solid #6c757d; padding:.5rem .75rem; margin:.5rem 0; border-radius:4px; font-family:monospace; white-space:pre; } | |
.thinking { font-style:italic; color:#6c757d; } | |
ol { margin:0 0 .75rem 1.25rem; } | |
pre, code { background:#f5f5f5; border-radius:4px; } | |
pre { padding:1rem; overflow:auto; } | |
code { padding:.15rem .35rem; } | |
</style> | |
</head> | |
<body> | |
<div class="container my-4"> | |
<h1 class="mb-4">Roo Code Transcript Viewer</h1> | |
<div class="mb-3"> | |
<label for="fileInput" class="form-label"><strong>Transcript file</strong></label> | |
<input type="file" id="fileInput" class="form-control" accept=".md"> | |
</div> | |
<div class="mb-3"> | |
<strong>Show / hide</strong> | |
<div class="form-check form-check-inline"> | |
<input class="form-check-input" type="checkbox" id="toggleUser" checked> | |
<label class="form-check-label" for="toggleUser">User prompts</label> | |
</div> | |
<div class="form-check form-check-inline"> | |
<input class="form-check-input" type="checkbox" id="toggleAssistant" checked> | |
<label class="form-check-label" for="toggleAssistant">Assistant responses</label> | |
</div> | |
<div class="form-check form-check-inline"> | |
<input class="form-check-input" type="checkbox" id="toggleSystem" checked> | |
<label class="form-check-label" for="toggleSystem">System info</label> | |
</div> | |
<div class="form-check form-check-inline"> | |
<input class="form-check-input" type="checkbox" id="toggleTask" checked> | |
<label class="form-check-label" for="toggleTask">Tasks</label> | |
</div> | |
</div> | |
<div id="transcriptContainer"></div> | |
</div> | |
<script> | |
/* ---------- Helpers ---------- */ | |
const esc=s=>s.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">"); | |
function cleanGarbage(t){return t.replace(/’/g,"'").replace(/“|�/g,'"') | |
.replace(/–/g,"–").replace(/—/g,"—").replace(/…/g,"…") | |
.replace(/Â /g,' ').replace(/Â/g,'').replace(/Ã/g,'').replace(/€(?!\d)/g,'€');} | |
const roleEmoji={ask:'❓',orchestrator:'🪃',edit:'✏️',architect:'🏗️',code:'💻'}; | |
const tagEmoji={'[new_task':'🆕','[read_file':'📖'}; | |
/* ---------- Format content ---------- */ | |
function formatContent(lines){ | |
let html='',inCode=false,inList=false,inError=false,inFile=false; | |
const endList=()=>{if(inList){html+='</ol>';inList=false;}}; | |
const openError=()=>{if(!inError){endList();html+='<div class="error-sub">';inError=true;}}; | |
const closeError=()=>{if(inError){html+='</div>';inError=false;}}; | |
const openFile=()=>{if(!inFile){endList();html+='<div class="file-sub"><pre><code>';inFile=true;}}; | |
const closeFile=()=>{if(inFile){html+='</code></pre></div>';inFile=false;}}; | |
for(let raw of lines){ | |
raw=cleanGarbage(raw); | |
/* file block tags */ | |
if(/^\s*<write_(to_)?file.*?>/i.test(raw)){openFile();continue;} | |
if(/^\s*<\/write_(to_)?file>/i.test(raw)){closeFile();continue;} | |
/* error tags */ | |
if(/^\s*<error>/i.test(raw)){openError();continue;} | |
if(/^\s*<\/error>/i.test(raw)){closeError();continue;} | |
/* other structural tags hidden */ | |
if(/^\s*<\/?(new_task|environment_details|message)[^>]*>/i.test(raw))continue; | |
if(inFile){html+=esc(raw)+'\n';continue;} /* raw inside file block */ | |
raw=raw.replace(/\\n/g,'<br>'); | |
const ltrim=raw.trimStart(); | |
/* code fences */ | |
if(ltrim.startsWith('```')){ | |
if(!inCode){endList();html+='<pre><code>';inCode=true;} | |
else{html+='</code></pre>';inCode=false;} | |
continue; | |
} | |
if(inCode){html+=esc(raw)+'\n';continue;} | |
/* headings (#, ##, ###) */ | |
if(/^#{1,3}\s+/.test(ltrim)){ | |
endList(); | |
const lvl=ltrim.match(/^#+/)[0].length; | |
const tag=['h2','h3','h4'][lvl-1]; | |
html+=`<${tag}>${esc(ltrim.replace(/^#{1,3}\s+/,''))}</${tag}>`; | |
continue; | |
} | |
/* numbered lists */ | |
if(/^\s*\d+\.\s+/.test(ltrim)){ | |
if(!inList){html+='<ol>';inList=true;} | |
html+='<li>'+esc(ltrim.replace(/^\s*\d+\.\s+/,''))+'</li>'; | |
continue; | |
}else{endList();} | |
/* bold **text** */ | |
let lineHTML=esc(raw).replace(/\*\*(.+?)\*\*/g,'<strong>$1</strong>'); | |
/* emoji replacements for common tags */ | |
for(const tag in tagEmoji){ | |
lineHTML=lineHTML.replace(new RegExp('\\'+tag+'.*?\\]','g'),tagEmoji[tag]); | |
} | |
/* <mode>role</mode> emoji */ | |
const modeMatch=ltrim.match(/^<mode>(.*?)<\/mode>$/i); | |
if(modeMatch){ | |
const role=modeMatch[1].toLowerCase(); | |
lineHTML=`<strong>${roleEmoji[role]||'🔧'} ${role.charAt(0).toUpperCase()+role.slice(1)}</strong>`; | |
} | |
html+=lineHTML+'\n'; | |
} | |
endList(); closeError(); closeFile(); | |
if(inCode)html+='</code></pre>'; | |
return html.replace(/<thinking>/g,'<span class="thinking">') | |
.replace(/<\/thinking>/g,'</span>'); | |
} | |
/* ---------- Parse ---------- */ | |
function parseTranscript(text){ | |
const lines=text.split(/\r?\n/),blocks=[];let i=0; | |
const gather=stop=>{const a=[];while(i<lines.length&&!stop(lines[i]))a.push(lines[i++]);return a;}; | |
while(i<lines.length){ | |
const ln=lines[i]; | |
if(ln.trim()===''||ln.trim()==='---'){i++;continue;} | |
if(ln.startsWith('<environment_details>')){ | |
const content=gather(l=>l.startsWith('</environment_details>'));if(i<lines.length)i++; | |
blocks.push({type:'system',content});continue; | |
} | |
if(ln.startsWith('<new_task')){ | |
const content=gather(l=>l.startsWith('</new_task>'));if(i<lines.length)i++; | |
blocks.push({type:'task',content});continue; | |
} | |
if(ln.startsWith('**User:**')){ | |
i++;const content=gather(l=>/^\*\*(User|Assistant):\*\*/.test(l) | |
||l.startsWith('<environment_details>') | |
||l.startsWith('<new_task')||l.trim()==='---'); | |
blocks.push({type:'user',content});continue; | |
} | |
if(ln.startsWith('**Assistant:**')){ | |
i++;const content=gather(l=>/^\*\*(User|Assistant):\*\*/.test(l) | |
||l.startsWith('<environment_details>') | |
||l.startsWith('<new_task')||l.trim()==='---'); | |
blocks.push({type:'assistant',content});continue; | |
} | |
i++; | |
} | |
return blocks; | |
} | |
/* ---------- Render ---------- */ | |
function renderTranscript(blocks){ | |
const C=document.getElementById('transcriptContainer');C.innerHTML=''; | |
blocks.forEach(b=>{ | |
const wrap=document.createElement('div'); | |
wrap.className=`block ${b.type}-block mb-4`; | |
const hdr=document.createElement('div'); | |
hdr.className='block-header'; | |
hdr.textContent={user:'User prompt',assistant:'Assistant response', | |
system:'System info',task:'Task'}[b.type]||'Block'; | |
const cont=document.createElement('div'); | |
cont.className='block-content'; | |
cont.innerHTML=formatContent(b.content); | |
wrap.append(hdr,cont);C.appendChild(wrap); | |
}); | |
} | |
/* ---------- File input ---------- */ | |
document.getElementById('fileInput').addEventListener('change',e=>{ | |
const f=e.target.files[0];if(!f)return; | |
const r=new FileReader(); | |
r.onload=ev=>renderTranscript(parseTranscript(ev.target.result)); | |
r.readAsText(f); | |
}); | |
/* ---------- Toggles ---------- */ | |
const toggleMap={toggleUser:'user-block',toggleAssistant:'assistant-block', | |
toggleSystem:'system-block',toggleTask:'task-block'}; | |
for(const id in toggleMap){ | |
document.getElementById(id).addEventListener('change',function(){ | |
document.querySelectorAll('.'+toggleMap[id]) | |
.forEach(el=>el.style.display=this.checked?'':'none'); | |
}); | |
} | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment