fix(opax): MD-panel — bytt renderMd() med marked.js (Fix 2)
This commit is contained in:
parent
d25caf114d
commit
39f714a621
|
|
@ -243,6 +243,7 @@
|
|||
.md-viewer p{margin-bottom:12px;color:var(--text-dim)}
|
||||
.md-viewer code{font-family:var(--mono);font-size:12px;background:rgba(255,255,255,.06);padding:2px 6px;border-radius:4px}
|
||||
.md-viewer pre{background:var(--bg-2);border:1px solid var(--border);padding:14px 16px;border-radius:10px;overflow-x:auto;font-family:var(--mono);font-size:12px;margin:12px 0;line-height:1.6}
|
||||
.md-viewer pre code{background:none;padding:0}
|
||||
.md-viewer table{width:100%;border-collapse:collapse;margin:14px 0;font-size:13px}
|
||||
.md-viewer th{text-align:left;padding:8px 12px;border-bottom:1px solid var(--border);font-size:11px;text-transform:uppercase;letter-spacing:0.05em;color:var(--text-faint)}
|
||||
.md-viewer td{padding:9px 12px;border-bottom:1px solid var(--border);color:var(--text-dim)}
|
||||
|
|
@ -251,6 +252,8 @@
|
|||
.md-viewer li{margin-bottom:4px}
|
||||
.md-viewer blockquote{border-left:3px solid var(--accent);padding:8px 16px;margin:12px 0;background:rgba(124,92,255,.06);border-radius:0 8px 8px 0;color:var(--text-dim)}
|
||||
.md-viewer hr{border:none;border-top:1px solid var(--border);margin:20px 0}
|
||||
.md-viewer a{color:var(--accent);text-decoration:none}
|
||||
.md-viewer a:hover{text-decoration:underline}
|
||||
.md-viewer-loading{display:flex;align-items:center;justify-content:center;height:200px;color:var(--text-faint);font-family:var(--mono);font-size:13px;gap:10px}
|
||||
|
||||
/* PRICING PANEL (CG4d) */
|
||||
|
|
@ -296,6 +299,7 @@
|
|||
.browser-error.show{display:flex}
|
||||
</style>
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js@4/dist/chart.umd.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/marked@12/marked.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="shell">
|
||||
|
|
@ -615,6 +619,9 @@ const STORAGE_KEY = 'jason_history_v2'
|
|||
const SESSION_KEY = 'jason_session_v2'
|
||||
const GH_BLOB = 'https://github.com/vauco-saas/OSVauco/blob/main/'
|
||||
|
||||
// Configure marked.js once
|
||||
marked.setOptions({ gfm: true, breaks: true })
|
||||
|
||||
const MD_DOCS = [
|
||||
{ icon:'🏛️', name:'MASTERPLAN', path:'docs/MASTERPLAN.md', desc:'Autoritativ sannhet — vinner alltid ved konflikt', badge:'autoritativ', badgeCls:'auth' },
|
||||
{ icon:'🗺️', name:'ROADMAP', path:'docs/ROADMAP.md', desc:'Hva som skjer akkurat nå — prioriteringer per sprint', badge:'aktiv', badgeCls:'' },
|
||||
|
|
@ -767,7 +774,9 @@ function browserFrameError(){
|
|||
async function pollBuild(){
|
||||
try{
|
||||
const d = await fetch('/opax/build-status').then(r=>r.json())
|
||||
const st = (d.status||'').toUpperCase()
|
||||
const builds = d.builds || []
|
||||
const latest = builds[0] || {}
|
||||
const st = (latest.status || d.status || '').toUpperCase()
|
||||
const cls = st==='SUCCESS'?'ok':['FAILURE','TIMEOUT','CANCELLED'].includes(st)?'fail':st?'running':''
|
||||
;['sb-build-dot','tb-build-dot'].forEach(id=>{ document.getElementById(id).className=(id==='sb-build-dot'?'build-dot ':'build-pill-dot ')+cls })
|
||||
document.getElementById('tb-build-txt').textContent = 'Build: '+(st||'ukjent')
|
||||
|
|
@ -803,7 +812,7 @@ async function loadBilling(){
|
|||
h+='<div class="sec-title">⚠️ Anomalier</div><div class="anomaly-list">'
|
||||
anoms.slice(0,5).forEach(a=>{
|
||||
const sev=(a.severity||'').toLowerCase()
|
||||
h+=`<div class="anom"><div class="anom-icon">${sev==='high'?'🔴':'🟡'}</div><div class="anom-info"><div class="anom-title">${esc(a.service||a.type||'Ukjent')}</div><div class="anom-desc">${esc(a.description||a.message||'')}</div></div><div class="anom-badge ${sev==='high'?'high':''}"">${esc(a.severity||'medium')}</div></div>`
|
||||
h+=`<div class="anom"><div class="anom-icon">${sev==='high'?'🔴':'🟡'}</div><div class="anom-info"><div class="anom-title">${esc(a.service||a.type||'Ukjent')}</div><div class="anom-desc">${esc(a.description||a.message||'')}</div></div><div class="anom-badge ${sev==='high'?'high':''}">${esc(a.severity||'medium')}</div></div>`
|
||||
})
|
||||
h+='</div>'
|
||||
}else{ h+='<div class="sec-title" style="color:var(--green)">✅ Ingen anomalier</div>' }
|
||||
|
|
@ -870,16 +879,20 @@ async function loadBuild(){
|
|||
el.innerHTML='<div class="empty-state"><div class="empty-icon">⏳</div><div class="empty-title">Laster builds...</div></div>'
|
||||
const d=await pollBuild()
|
||||
if(!d){el.innerHTML='<div class="empty-state"><div class="empty-icon">❌</div><div class="empty-title">Kunne ikke hente build</div></div>';return}
|
||||
const st=(d.status||'').toUpperCase()
|
||||
const ico=st==='SUCCESS'?'✅':['FAILURE','TIMEOUT'].includes(st)?'❌':'⏳'
|
||||
document.getElementById('build-panel-sub').innerHTML=`<span style="width:6px;height:6px;border-radius:50%;background:${st==='SUCCESS'?'var(--green)':['FAILURE','TIMEOUT'].includes(st)?'var(--danger)':'var(--amber)'};display:inline-block;margin-right:5px"></span>${st}`
|
||||
let h=`<div class="build-list"><div class="build-item"><div class="build-ico">${ico}</div><div class="build-info"><div class="build-id">${esc(d.build_id||d.id||'—')}</div><div class="build-branch">${esc(d.branch||d.trigger||'main')}</div><div class="build-time">${esc(d.created||d.timestamp||'—')}</div></div></div></div>`
|
||||
const logs=d.log_lines||d.logs||[]
|
||||
if(logs.length){
|
||||
h+='<div class="log-box">'
|
||||
logs.forEach(l=>{ const c=/error|fail/i.test(l)?'ll-fail':/success|done/i.test(l)?'ll-ok':''; h+=`<div class="${c}">${esc(l)}</div>` })
|
||||
h+='</div>'
|
||||
const builds = d.builds || []
|
||||
if(!builds.length){
|
||||
el.innerHTML='<div class="empty-state"><div class="empty-icon">🔨</div><div class="empty-title">Ingen builds funnet</div></div>'
|
||||
return
|
||||
}
|
||||
let h='<div class="build-list">'
|
||||
builds.forEach(b=>{
|
||||
const st=(b.status||'').toUpperCase()
|
||||
const ico=st==='SUCCESS'?'✅':['FAILURE','TIMEOUT'].includes(st)?'❌':'⏳'
|
||||
h+=`<div class="build-item"><div class="build-ico">${ico}</div><div class="build-info"><div class="build-id">${esc(b.id||'—')}</div><div class="build-branch">${esc(b.branch||'main')}</div><div class="build-time">${esc(b.createTime||'—')}</div></div></div>`
|
||||
})
|
||||
h+='</div>'
|
||||
const latest=builds[0]
|
||||
document.getElementById('build-panel-sub').innerHTML=`<span style="width:6px;height:6px;border-radius:50%;background:${latest.status==='SUCCESS'?'var(--green)':['FAILURE','TIMEOUT'].includes(latest.status)?'var(--danger)':'var(--amber)'};display:inline-block;margin-right:5px"></span>${latest.status||'ukjent'}`
|
||||
el.innerHTML=h
|
||||
}
|
||||
|
||||
|
|
@ -916,7 +929,7 @@ function openInline(url, name){
|
|||
document.getElementById('tb-sub').textContent=url
|
||||
}
|
||||
|
||||
// MD MODULE
|
||||
// MD MODULE — Fix 2: marked.js rendering
|
||||
function renderMdGrid(){
|
||||
const g=document.getElementById('md-grid')
|
||||
if(g.children.length) return
|
||||
|
|
@ -950,11 +963,11 @@ async function openMd(path, name){
|
|||
const viewer=document.getElementById('md-viewer')
|
||||
viewer.innerHTML='<div class="md-viewer-loading"><span style="animation:pulse 1s infinite">⏳</span> Laster '+esc(path)+'...</div>'
|
||||
try{
|
||||
// OQ-30: bruk /docs/ proxy i stedet for GH_RAW
|
||||
const res = await fetch('/docs/'+path+'?t='+Date.now())
|
||||
if(!res.ok) throw new Error('HTTP '+res.status)
|
||||
const raw = await res.text()
|
||||
viewer.innerHTML=renderMd(raw)
|
||||
// Fix 2: bruk marked.js i stedet for hjemmelaget renderMd()
|
||||
viewer.innerHTML = marked.parse(raw)
|
||||
}catch(e){
|
||||
viewer.innerHTML=`<div class="md-viewer-loading">❌ Kunne ikke laste: ${esc(e.message)}</div>`
|
||||
}
|
||||
|
|
@ -969,36 +982,6 @@ function mdShowGrid(){
|
|||
document.getElementById('tb-sub').textContent='Kjerndoks · vauco-saas/OSVauco'
|
||||
}
|
||||
|
||||
function renderMd(text){
|
||||
let s=esc(text)
|
||||
s=s.replace(/```[^\n]*\n([\s\S]*?)```/g,'<pre><code>$1</code></pre>')
|
||||
s=s.replace(/`([^`]+)`/g,'<code>$1</code>')
|
||||
s=s.replace(/^### (.+)$/gm,'<h3>$1</h3>')
|
||||
s=s.replace(/^## (.+)$/gm,'<h2>$1</h2>')
|
||||
s=s.replace(/^# (.+)$/gm,'<h1>$1</h1>')
|
||||
s=s.replace(/\*\*(.+?)\*\*/g,'<strong>$1</strong>')
|
||||
s=s.replace(/^---$/gm,'<hr>')
|
||||
s=s.replace(/^> (.+)$/gm,'<blockquote>$1</blockquote>')
|
||||
s=s.replace(/((?:^\|.+\|\n)+)/gm, tbl=>{
|
||||
const rows=tbl.trim().split('\n').filter(r=>!/^\|[-| :]+\|$/.test(r))
|
||||
if(!rows.length) return tbl
|
||||
let out='<table>'
|
||||
rows.forEach((r,i)=>{
|
||||
const cells=r.split('|').slice(1,-1)
|
||||
out+=i===0?'<thead><tr>':'<tr>'
|
||||
cells.forEach(c=>{ out+=i===0?`<th>${c.trim()}</th>`:`<td>${c.trim()}</td>` })
|
||||
out+=i===0?'</tr></thead><tbody>':'</tr>'
|
||||
})
|
||||
out+='</tbody></table>'
|
||||
return out
|
||||
})
|
||||
s=s.replace(/^[-*] (.+)$/gm,'<li>$1</li>')
|
||||
s=s.replace(/(<li>.*<\/li>)/s, m=>'<ul>'+m+'</ul>')
|
||||
s=s.replace(/^\d+\. (.+)$/gm,'<li>$1</li>')
|
||||
s=s.replace(/^(?!<[htublp|]).+$/gm, line=>line.trim()?`<p>${line}</p>`:'')
|
||||
return s
|
||||
}
|
||||
|
||||
// TERMINAL
|
||||
let termInited=false
|
||||
function initTerminal(){
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user