| <!doctype html> |
| <html lang="ja"> |
| <head> |
| <meta charset="utf-8"/> |
| <title>AVH-Math Phase18 UI</title> |
| <style> |
| body { font-family: sans-serif; margin:0; display:flex; flex-direction:column; height:100vh; } |
| header { background:#222; color:white; padding:10px 20px; display:flex; justify-content:space-between; align-items:center; } |
| main { display:flex; flex:1; overflow:hidden; } |
| #left { width:380px; border-right:1px solid #ccc; padding:15px; overflow-y:auto; background:#fcfcfc; } |
| #center { flex:1; display:flex; flex-direction:column; border-right:1px solid #ccc; } |
| #right { width:400px; padding:15px; overflow-y:auto; background:#f4f4f4; } |
| .search-box { margin-bottom:15px; display:flex; gap:5px; } |
| .item { padding:10px; border-bottom:1px solid #eee; cursor:pointer; } |
| .item:hover { background:#eef; } |
| #cy { flex:1; background:white; } |
| pre { background:#eee; padding:10px; border-radius:4px; white-space:pre-wrap; font-size:12px; } |
| .pill { display:inline-block; padding:2px 8px; border-radius:12px; background:#ddd; font-size:11px; margin-right:5px; } |
| .btn-group { margin-top:10px; display:flex; flex-wrap:wrap; gap:5px; } |
| button { padding:5px 10px; cursor:pointer; } |
| </style> |
| </head> |
| <body> |
| <header> |
| <strong>AVH-Math Phase18: Integrated Navigator</strong> |
| <div id="stats">Loading...</div> |
| </header> |
| <main> |
| <section id="left"> |
| <div class="search-box"> |
| <input type="text" id="q" placeholder="Search or Query"> |
| <button onclick="search()">Search</button> |
| <button onclick="navigate()">Nav</button> |
| </div> |
| <div id="results"></div> |
| <h4>Navigation Report</h4> |
| <pre id="nav-report">No report yet.</pre> |
| </section> |
| <section id="center"> |
| <div id="cy"></div> |
| </section> |
| <section id="right"> |
| <h3>Entry Detail</h3> |
| <div id="detail">Select an item.</div> |
| </section> |
| </main> |
| <script src="/static/vendor/cytoscape.min.js"></script> |
| <script> |
| let cy; |
| async function api(path, opts={}) { |
| const res = await fetch(path, opts); |
| return res.json(); |
| } |
| |
| async function search() { |
| const q = document.getElementById('q').value; |
| const data = await api(`/api/search?q=${encodeURIComponent(q)}`); |
| const list = document.getElementById('results'); |
| list.innerHTML = ''; |
| data.candidates.forEach(([id, score]) => { |
| const d = document.createElement('div'); |
| d.className = 'item'; |
| d.innerText = `${id} (${score})`; |
| d.onclick = () => loadEntry(id); |
| list.appendChild(d); |
| }); |
| } |
| |
| async function navigate() { |
| const q = document.getElementById('q').value; |
| const data = await api(`/api/navigate?q=${encodeURIComponent(q)}`); |
| document.getElementById('nav-report').innerText = JSON.stringify(data.result || data.raw, null, 2); |
| } |
| |
| async function loadEntry(id) { |
| const e = await api(`/api/entry/${encodeURIComponent(id)}`); |
| const detail = document.getElementById('detail'); |
| detail.innerHTML = ` |
| <h4>${e.id}</h4> |
| <p><strong>${e.title}</strong></p> |
| <p>${e.statement}</p> |
| <h5>Refutation</h5> |
| <pre>${e.refutation || 'None'}</pre> |
| <div class="btn-group"> |
| <button onclick="sendFeedback('${id}', 'good')">👍 Good</button> |
| <button onclick="sendFeedback('${id}', 'weak')">⚠️ Weak</button> |
| <button onclick="sendFeedback('${id}', 'wrong')">❌ Wrong</button> |
| </div> |
| `; |
| if(cy) { |
| cy.elements().removeClass('highlighted'); |
| cy.getElementById(id).addClass('highlighted'); |
| } |
| } |
| |
| async function sendFeedback(id, verdict) { |
| await api('/api/feedback', { |
| method: 'POST', |
| headers: {'Content-Type': 'application/json'}, |
| body: JSON.stringify({id, verdict}) |
| }); |
| alert('Feedback sent!'); |
| } |
| |
| async function initGraph() { |
| const g = await api('/api/graph'); |
| cy = cytoscape({ |
| container: document.getElementById('cy'), |
| elements: g.nodes.map(n => ({data: n})), |
| style: [ |
| {selector: 'node', style: {'label': 'data(label)', 'width': 'mapData(size, 0, 100, 10, 50)', 'height': 'mapData(size, 0, 100, 10, 50)', 'font-size': '8px'}}, |
| {selector: '.highlighted', style: {'background-color': '#f00', 'line-color': '#f00'}} |
| ], |
| layout: {name: 'cose'} |
| }); |
| } |
| |
| initGraph(); |
| api('/api/stats').then(s => { document.getElementById('stats').innerText = `KB: ${s.kb_ids} | Nodes: ${s.graph_nodes}`; }); |
| </script> |
| </body> |
| </html> |
|
|