diff --git a/public_html/admin/index.php b/public_html/admin/index.php index ce1e171..50f7d62 100644 --- a/public_html/admin/index.php +++ b/public_html/admin/index.php @@ -995,6 +995,17 @@ if ($action) { $raw = curl_exec($ch); curl_close($ch); j(json_decode($raw, true) ?: ['error'=>'Arc Reactor unreachable']); + + case 'vision_analyze': + $id = (int)($_GET['id'] ?? 0); if (!$id) bad('Missing id'); + $ch = curl_init('http://127.0.0.1:7474/job'); + curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER=>true, CURLOPT_POST=>true, CURLOPT_TIMEOUT=>5, + CURLOPT_POSTFIELDS=>json_encode(['type'=>'vision','payload'=>['screenshot_id'=>$id,'provider'=>'claude'],'priority'=>8,'created_by'=>'admin']), + CURLOPT_HTTPHEADER=>['Content-Type: application/json']]); + $raw = curl_exec($ch); curl_close($ch); + j(json_decode($raw, true) ?: ['error'=>'Arc Reactor unreachable']); + break; + case 'vision_purge': $ch = curl_init('http://127.0.0.1:7474/screenshots/purge'); curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER=>true, CURLOPT_TIMEOUT=>5, CURLOPT_CUSTOMREQUEST=>'DELETE']); @@ -3119,24 +3130,33 @@ async function loadVision() { } gallery.innerHTML = shots.map(s => { - const shotTs = ts(s.created_at); - const has = s.file_size > 0; - const meth = (s.method || 'unknown').toUpperCase(); - const dim = s.width && s.height ? `${s.width}×${s.height}` : ''; - const analysis = (s.vision_analysis || '').substring(0, 180); + const shotTs = ts(s.created_at); + const hasImg = (s.file_size || 0) > 0; + const meth = (s.method || 'unknown').toUpperCase(); + const rawAnalysis = s.vision_analysis || ''; + const isFailed = rawAnalysis.startsWith('Vision analysis unavailable'); + const analysis = isFailed ? '' : rawAnalysis.substring(0, 200); + const hasAnalysis = !isFailed && rawAnalysis.length > 0; return `
-
- ${esc(s.hostname||'unknown')} - ${meth} +
+ + ${esc(s.hostname||'unknown')} + ${meth} - + +
- ${has ? `
- ◈ ${dim ? dim + ' · ' : ''}${Math.round((s.file_size||0)/1024)}KB IMAGE -
` : '
TEXT SNAPSHOT ONLY
'} - ${analysis ? `
${esc(analysis)}${s.vision_analysis?.length>180?'…':''}
` : ''} -
${shotTs}
+ ${hasImg + ? `
+ 🖥 + ${Math.round((s.file_size||0)/1024)}KB  ·  click to view +
` + : '
TEXT SNAPSHOT ONLY
'} + ${hasAnalysis + ? `
${esc(analysis)}${rawAnalysis.length>200?'…':''}
` + : `
${isFailed?'Analysis failed — credits needed':'No analysis yet — click ◈ to analyze'}
`} +
${shotTs}
`; }).join(''); @@ -3155,12 +3175,26 @@ async function visionViewScreenshot(id) { METHOD: ${esc(d.method||'')} · ${d.width&&d.height?d.width+'×'+d.height+' · ':''} ${Math.round((d.file_size||0)/1024)}KB · ${ts(d.created_at)}
${imgHtml} - ${d.vision_analysis ? `
VISION ANALYSIS
-
${esc(d.vision_analysis)}
` : ''} + ${d.vision_analysis && !d.vision_analysis.startsWith('Vision analysis unavailable') ? ` +
◈ VISION ANALYSIS
+
${esc(d.vision_analysis)}
` : + `
No analysis — click ◈ in gallery to analyze when credits are available.
`} `, null, null); document.getElementById('modalSave').style.display = 'none'; } + +async function visionReanalyze(id) { + toast('Submitting vision analysis job...', 'ok'); + const d = await api('vision_analyze', {id}); + if (d && d.job_id) { + toast('Analysis job #' + d.job_id + ' queued', 'ok'); + setTimeout(() => loadVision(), 8000); + } else { + toast((d && d.error) || 'Failed — Arc offline or no credits', 'err'); + } +} + async function visionRunScreenshot() { let agents = _visionAgents.length ? _visionAgents : null; if (!agents) { @@ -4548,7 +4582,8 @@ async function loadArc() { document.getElementById('arc-done-val').textContent = s?.jobs_done ?? s?.stats?.done ?? '—'; document.getElementById('arc-fail-val').textContent = s?.jobs_failed ?? s?.stats?.failed ?? '—'; document.getElementById('arc-hb-val').textContent = s?.last_heartbeat ? ts(s.last_heartbeat) : (online ? 'ALIVE' : '—'); - document.getElementById('arc-caps-val').textContent = Array.isArray(s?.capabilities) ? s.capabilities.join(' · ') : (s?.capabilities || '—'); + const caps = s?.capabilities || s?.handlers; + document.getElementById('arc-caps-val').textContent = Array.isArray(caps) ? caps.join(' · ') : (caps || '—'); const list = Array.isArray(jobs) ? jobs : (jobs?.jobs || []); if (!list.length) {