Existing ASP.NET API with vanilla JS SPA, WindowWatcher, Chrome extension, and MCP server. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
92 lines
3.6 KiB
JavaScript
92 lines
3.6 KiB
JavaScript
import * as api from '../api.js';
|
|
|
|
const el = () => document.getElementById('page-context');
|
|
|
|
export async function initContext() {
|
|
el().innerHTML = `
|
|
<h1 class="page-title">Context</h1>
|
|
<div class="section-title">App Summary (8 hours)</div>
|
|
<div id="ctx-summary" class="card mb-16"></div>
|
|
<div class="flex-between mb-8">
|
|
<div class="section-title" style="margin-bottom:0">Recent Events</div>
|
|
<select class="form-select" id="ctx-minutes" style="width:auto">
|
|
<option value="15">Last 15 min</option>
|
|
<option value="30" selected>Last 30 min</option>
|
|
<option value="60">Last hour</option>
|
|
<option value="120">Last 2 hours</option>
|
|
<option value="480">Last 8 hours</option>
|
|
</select>
|
|
</div>
|
|
<div id="ctx-events" class="card table-wrap"></div>`;
|
|
|
|
document.getElementById('ctx-minutes').addEventListener('change', loadEvents);
|
|
await Promise.all([loadSummary(), loadEvents()]);
|
|
}
|
|
|
|
async function loadSummary() {
|
|
try {
|
|
const summary = await api.context.summary();
|
|
const container = document.getElementById('ctx-summary');
|
|
if (!summary || !summary.length) {
|
|
container.innerHTML = `<div class="empty-state">No activity recorded</div>`;
|
|
return;
|
|
}
|
|
container.innerHTML = `
|
|
<table>
|
|
<thead><tr><th>Application</th><th>Category</th><th>Events</th><th>First Seen</th><th>Last Seen</th></tr></thead>
|
|
<tbody>
|
|
${summary.map(s => `
|
|
<tr>
|
|
<td>${esc(s.appName)}</td>
|
|
<td>${esc(s.category)}</td>
|
|
<td>${s.eventCount}</td>
|
|
<td>${formatTime(s.firstSeen)}</td>
|
|
<td>${formatTime(s.lastSeen)}</td>
|
|
</tr>`).join('')}
|
|
</tbody>
|
|
</table>`;
|
|
} catch (e) {
|
|
document.getElementById('ctx-summary').innerHTML = `<div class="empty-state">Failed to load summary</div>`;
|
|
}
|
|
}
|
|
|
|
async function loadEvents() {
|
|
const minutes = parseInt(document.getElementById('ctx-minutes').value);
|
|
try {
|
|
const events = await api.context.recent(minutes);
|
|
const container = document.getElementById('ctx-events');
|
|
if (!events || !events.length) {
|
|
container.innerHTML = `<div class="empty-state">No recent events</div>`;
|
|
return;
|
|
}
|
|
container.innerHTML = `
|
|
<table>
|
|
<thead><tr><th>Source</th><th>App</th><th>Window Title</th><th>URL</th><th>Time</th></tr></thead>
|
|
<tbody>
|
|
${events.map(e => `
|
|
<tr>
|
|
<td>${esc(e.source)}</td>
|
|
<td>${esc(e.appName)}</td>
|
|
<td class="truncate">${esc(e.windowTitle)}</td>
|
|
<td class="truncate">${e.url ? esc(e.url) : '-'}</td>
|
|
<td>${formatTime(e.timestamp)}</td>
|
|
</tr>`).join('')}
|
|
</tbody>
|
|
</table>`;
|
|
} catch (e) {
|
|
document.getElementById('ctx-events').innerHTML = `<div class="empty-state">Failed to load events</div>`;
|
|
}
|
|
}
|
|
|
|
function formatTime(iso) {
|
|
const d = new Date(iso);
|
|
return d.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit' });
|
|
}
|
|
|
|
function esc(str) {
|
|
if (!str) return '';
|
|
const d = document.createElement('div');
|
|
d.textContent = str;
|
|
return d.innerHTML;
|
|
}
|