feat(OQ-30): browser-panel + /docs/ proxy-fetch i opax.html

This commit is contained in:
chrischristiansen-glitch 2026-06-13 05:30:21 +02:00
parent 8f0829f6b3
commit 7bd1d3fb50

View File

@ -283,6 +283,17 @@
.roi-sub{font-size:12px;color:var(--text-dim);margin-top:4px;line-height:1.5} .roi-sub{font-size:12px;color:var(--text-dim);margin-top:4px;line-height:1.5}
.roi-divider{width:1px;height:60px;background:var(--border);flex-shrink:0} .roi-divider{width:1px;height:60px;background:var(--border);flex-shrink:0}
@media(max-width:600px){.roi-divider{display:none}.roi-box{gap:16px}} @media(max-width:600px){.roi-divider{display:none}.roi-box{gap:16px}}
/* BROWSER PANEL */
.browser-bar{display:flex;align-items:center;gap:8px;padding:10px 16px;border-bottom:1px solid var(--border);background:var(--bg-2);flex-shrink:0}
.browser-url-input{flex:1;background:var(--bg-3);border:1px solid var(--border);border-radius:8px;padding:6px 12px;font-family:var(--mono);font-size:12px;color:var(--text);outline:none;transition:border-color .2s}
.browser-url-input:focus{border-color:var(--accent)}
.browser-url-input::placeholder{color:var(--text-faint)}
.browser-go-btn{padding:6px 14px;border-radius:8px;background:var(--accent);color:#fff;font-size:12px;font-weight:600;transition:background .14s;white-space:nowrap}
.browser-go-btn:hover{background:#6a4cff}
.browser-frame{flex:1;border:none;width:100%;height:100%;background:var(--bg)}
.browser-error{display:none;flex:1;align-items:center;justify-content:center;flex-direction:column;gap:12px;color:var(--text-faint);font-family:var(--mono);font-size:13px}
.browser-error.show{display:flex}
</style> </style>
<script src="https://cdn.jsdelivr.net/npm/chart.js@4/dist/chart.umd.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/chart.js@4/dist/chart.umd.min.js"></script>
</head> </head>
@ -332,6 +343,9 @@
<div class="nav-item" onclick="go('md')" id="nav-md"> <div class="nav-item" onclick="go('md')" id="nav-md">
<span class="nav-icon">📄</span> MD <span class="nav-icon">📄</span> MD
</div> </div>
<div class="nav-item" onclick="go('browser')" id="nav-browser">
<span class="nav-icon">🌐</span> Browser
</div>
<div class="side-foot"> <div class="side-foot">
<div class="me-card"> <div class="me-card">
@ -565,6 +579,30 @@
</div> </div>
</div> </div>
<!-- BROWSER -->
<div class="panel" id="panel-browser">
<div class="panel-head">
<div class="p-avatar" style="background:linear-gradient(135deg,#5c8fff,#7c5cff)">🌐</div>
<div>
<div class="p-title">Browser</div>
<div class="p-sub">Intern dokumentasjonsleser · /docs/ proxy</div>
</div>
<div class="p-actions">
<button class="hbtn" onclick="browserRefresh()">↻ Refresh</button>
</div>
</div>
<div class="browser-bar">
<input id="browser-url" class="browser-url-input" type="text" placeholder="https://... eller /docs/MASTERPLAN.md" onkeydown="if(event.key==='Enter')browserNav()">
<button class="browser-go-btn" onclick="browserNav()">Gå →</button>
</div>
<div id="browser-error" class="browser-error">
<span style="font-size:32px">⚠️</span>
<span id="browser-error-msg">Kunne ikke laste siden</span>
</div>
<iframe id="browser-frame" class="browser-frame" src="" sandbox="allow-scripts allow-same-origin allow-forms allow-popups"
onload="browserFrameLoad()" onerror="browserFrameError()"></iframe>
</div>
</div><!-- /panels --> </div><!-- /panels -->
</div><!-- /main --> </div><!-- /main -->
</div><!-- /shell --> </div><!-- /shell -->
@ -575,7 +613,6 @@ const AGENT_URL = ''
const USER_ID = 'chris' const USER_ID = 'chris'
const STORAGE_KEY = 'jason_history_v2' const STORAGE_KEY = 'jason_history_v2'
const SESSION_KEY = 'jason_session_v2' const SESSION_KEY = 'jason_session_v2'
const GH_RAW = 'https://raw.githubusercontent.com/vauco-saas/OSVauco/main/'
const GH_BLOB = 'https://github.com/vauco-saas/OSVauco/blob/main/' const GH_BLOB = 'https://github.com/vauco-saas/OSVauco/blob/main/'
const MD_DOCS = [ const MD_DOCS = [
@ -598,6 +635,7 @@ const PANEL_META = {
sites: ['Sites & Moduler', 'Alle Vauco-flater'], sites: ['Sites & Moduler', 'Alle Vauco-flater'],
terminal: ['Terminal', 'OPAX agent · Cloud Run'], terminal: ['Terminal', 'OPAX agent · Cloud Run'],
md: ['MD — Dokumenter', 'Kjerndoks · vauco-saas/OSVauco'], md: ['MD — Dokumenter', 'Kjerndoks · vauco-saas/OSVauco'],
browser: ['Browser', 'Intern dokumentasjonsleser · /docs/ proxy'],
} }
const SITES_DATA = [ const SITES_DATA = [
@ -648,7 +686,6 @@ function updatePricing(val){
renderTierGrid(spend) renderTierGrid(spend)
// ROI: 15% of spend saved via CostGuard optimisation
const savings = spend * 0.15 const savings = spend * 0.15
const net_monthly = savings - tier.price const net_monthly = savings - tier.price
const net_annual = net_monthly * 12 const net_annual = net_monthly * 12
@ -706,6 +743,26 @@ function initPricing(){
updatePricing(slider.value) updatePricing(slider.value)
} }
// BROWSER PANEL
function browserNav(){
const raw = document.getElementById('browser-url').value.trim()
if(!raw) return
const url = raw.startsWith('http') ? raw : '/docs/'+raw.replace(/^\/docs\//,'')
document.getElementById('browser-error').classList.remove('show')
document.getElementById('browser-frame').src = url
}
function browserRefresh(){
const f = document.getElementById('browser-frame')
if(f.src) f.src = f.src
}
function browserFrameLoad(){
document.getElementById('browser-error').classList.remove('show')
}
function browserFrameError(){
document.getElementById('browser-error-msg').textContent = 'Siden kunne ikke lastes — sjekk URL eller /docs/-proxy'
document.getElementById('browser-error').classList.add('show')
}
// BUILD STATUS POLL // BUILD STATUS POLL
async function pollBuild(){ async function pollBuild(){
try{ try{
@ -893,7 +950,10 @@ async function openMd(path, name){
const viewer=document.getElementById('md-viewer') const viewer=document.getElementById('md-viewer')
viewer.innerHTML='<div class="md-viewer-loading"><span style="animation:pulse 1s infinite"></span> Laster '+esc(path)+'...</div>' viewer.innerHTML='<div class="md-viewer-loading"><span style="animation:pulse 1s infinite"></span> Laster '+esc(path)+'...</div>'
try{ try{
const raw=await fetch(GH_RAW+path+'?t='+Date.now()).then(r=>{ if(!r.ok) throw new Error('HTTP '+r.status); return r.text() }) // 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) viewer.innerHTML=renderMd(raw)
}catch(e){ }catch(e){
viewer.innerHTML=`<div class="md-viewer-loading">❌ Kunne ikke laste: ${esc(e.message)}</div>` viewer.innerHTML=`<div class="md-viewer-loading">❌ Kunne ikke laste: ${esc(e.message)}</div>`
@ -1047,7 +1107,6 @@ function exportHistory(){
} }
;(function(){ const h=lh();if(!h.length)return;h.forEach(m=>appendMsg(m.role,m.text,m.ts));document.getElementById('messages').scrollTop=99999 })() ;(function(){ const h=lh();if(!h.length)return;h.forEach(m=>appendMsg(m.role,m.text,m.ts));document.getElementById('messages').scrollTop=99999 })()
inp.focus() inp.focus()
// Init pricing on load so slider is ready if user navigates directly
renderTierGrid(5000) renderTierGrid(5000)
updatePricing(5000) updatePricing(5000)
</script> </script>