summaryrefslogtreecommitdiff
path: root/web/templates/agent-context.html
diff options
context:
space:
mode:
authorPeter Stone <thepeterstone@gmail.com>2026-01-28 23:32:26 -1000
committerPeter Stone <thepeterstone@gmail.com>2026-01-28 23:32:26 -1000
commitd39220eac03fbc5b714bde989665ed1c92dd24a5 (patch)
tree03e1985745043b9af6e7442ac21fdcd5d843146f /web/templates/agent-context.html
parent05b1930e04ac222d73ffb2f45c1b1febb69f893d (diff)
Expand agent context API with completed log and calendar view
- Add completed_tasks table to log task completions with title, due date, and completion timestamp - Extend agent context date range: 7 days back to 14 days forward - Add completed_log to API response (last 50 completed tasks) - Add day_section field to timeline items (overdue/today/tomorrow/later) - Add calendar-style view for today's schedule (6am-10pm hourly grid) - Add tabbed interface for Timeline vs Completed Log in HTML view Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Diffstat (limited to 'web/templates/agent-context.html')
-rw-r--r--web/templates/agent-context.html258
1 files changed, 214 insertions, 44 deletions
diff --git a/web/templates/agent-context.html b/web/templates/agent-context.html
index 3a4778a..db618ba 100644
--- a/web/templates/agent-context.html
+++ b/web/templates/agent-context.html
@@ -5,16 +5,34 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Agent Context - {{.AgentName}}</title>
<style>
- body { font-family: system-ui, sans-serif; max-width: 800px; margin: 40px auto; padding: 20px; background: #1a1a2e; color: #eee; }
+ body { font-family: system-ui, sans-serif; max-width: 1000px; margin: 40px auto; padding: 20px; background: #1a1a2e; color: #eee; }
.card { background: #16213e; border-radius: 8px; padding: 24px; margin-bottom: 20px; }
h1 { color: #e94560; margin-top: 0; }
- h2 { color: #0f3460; font-size: 1.2em; margin-top: 24px; }
+ h2 { color: #4da6ff; font-size: 1.2em; margin-top: 24px; margin-bottom: 16px; }
.label { color: #888; font-size: 0.9em; margin-bottom: 4px; }
.value { font-family: monospace; background: #0f0f23; padding: 8px 12px; border-radius: 4px; word-break: break-all; margin-bottom: 16px; }
- .summary { display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 12px; margin-bottom: 20px; }
+ .summary { display: grid; grid-template-columns: repeat(auto-fit, minmax(100px, 1fr)); gap: 12px; margin-bottom: 20px; }
.summary-item { background: #0f3460; padding: 12px; border-radius: 6px; text-align: center; }
.summary-value { font-size: 1.5em; font-weight: bold; color: #e94560; }
.summary-label { font-size: 0.8em; color: #888; }
+
+ /* Calendar styles */
+ .calendar { position: relative; border-left: 2px solid #333; margin-left: 60px; min-height: 640px; }
+ .calendar-hour { position: relative; height: 40px; border-bottom: 1px solid #222; }
+ .calendar-hour-label { position: absolute; left: -60px; top: -8px; font-size: 0.75em; color: #666; width: 50px; text-align: right; }
+ .calendar-item { position: absolute; left: 10px; right: 10px; background: #0f3460; border-radius: 4px; padding: 6px 10px; font-size: 0.85em; border-left: 3px solid; overflow: hidden; z-index: 1; }
+ .calendar-item:hover { z-index: 10; background: #1a4a80; }
+ .calendar-item.source-todoist { border-color: #e44332; }
+ .calendar-item.source-trello { border-color: #0079bf; }
+ .calendar-item.source-plantoeat { border-color: #5cb85c; }
+ .calendar-item.source-calendar { border-color: #9b59b6; }
+ .calendar-item.source-gtasks { border-color: #f39c12; }
+ .calendar-item-time { font-size: 0.75em; color: #888; }
+ .calendar-item-title { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
+ .all-day { background: #1a1a2e; padding: 8px; margin-bottom: 10px; border-radius: 4px; }
+ .all-day-item { display: inline-block; background: #0f3460; padding: 4px 8px; border-radius: 4px; margin: 2px; font-size: 0.85em; border-left: 3px solid #5cb85c; }
+
+ /* Table styles */
table { width: 100%; border-collapse: collapse; margin-top: 16px; }
th, td { padding: 10px; text-align: left; border-bottom: 1px solid #333; }
th { background: #0f3460; color: #fff; }
@@ -23,36 +41,61 @@
.source-todoist { background: #e44332; color: #fff; }
.source-trello { background: #0079bf; color: #fff; }
.source-plantoeat { background: #5cb85c; color: #fff; }
- .due-overdue { color: #dc3545; }
- .due-today { color: #ffc107; }
- .due-future { color: #28a745; }
+ .source-calendar { background: #9b59b6; color: #fff; }
+ .source-gtasks { background: #f39c12; color: #fff; }
+ .section-overdue { color: #dc3545; }
+ .section-today { color: #ffc107; }
+ .section-tomorrow { color: #28a745; }
+ .section-later { color: #6c757d; }
a { color: #4da6ff; }
+ .completed-item { opacity: 0.7; }
+ .tabs { display: flex; gap: 4px; margin-bottom: 16px; }
+ .tab { padding: 8px 16px; background: #0f3460; border: none; color: #fff; cursor: pointer; border-radius: 4px 4px 0 0; }
+ .tab.active { background: #16213e; }
+ .tab-content { display: none; }
+ .tab-content.active { display: block; }
</style>
</head>
<body>
<script type="application/json" id="agent-data">
{
"generated_at": "{{.GeneratedAt}}",
+ "today": "{{.Today}}",
"range": {
"start": "{{.RangeStart}}",
"end": "{{.RangeEnd}}"
},
"summary": {
"total_items": {{with .Summary}}{{.total_items}}{{else}}0{{end}},
- "overdue": {{with .Summary}}{{.overdue}}{{else}}0{{end}},
- "today": {{with .Summary}}{{.today}}{{else}}0{{end}}
+ "by_section": {{with .Summary}}{{if .by_section}}{"overdue": {{if index .by_section "overdue"}}{{index .by_section "overdue"}}{{else}}0{{end}}, "today": {{if index .by_section "today"}}{{index .by_section "today"}}{{else}}0{{end}}, "tomorrow": {{if index .by_section "tomorrow"}}{{index .by_section "tomorrow"}}{{else}}0{{end}}}{{else}}{}{{end}}{{else}}{}{{end}}
},
+ "today_items": [{{range $i, $item := .TodayItems}}{{if $i}},{{end}}
+ {
+ "id": "{{$item.ID}}",
+ "source": "{{$item.Source}}",
+ "type": "{{$item.Type}}",
+ "title": "{{$item.Title}}",
+ "due": {{if $item.Due}}"{{$item.Due.Format "2006-01-02T15:04:05Z07:00"}}"{{else}}null{{end}},
+ "completable": {{$item.Completable}}
+ }{{end}}
+ ],
"timeline": [{{range $i, $item := .Timeline}}{{if $i}},{{end}}
{
"id": "{{$item.ID}}",
"source": "{{$item.Source}}",
"type": "{{$item.Type}}",
"title": "{{$item.Title}}",
- "description": "{{$item.Description}}",
"due": {{if $item.Due}}"{{$item.Due.Format "2006-01-02T15:04:05Z07:00"}}"{{else}}null{{end}},
- "priority": {{$item.Priority}},
- "completable": {{$item.Completable}},
- "url": "{{$item.URL}}"
+ "day_section": "{{$item.DaySection}}",
+ "completable": {{$item.Completable}}
+ }{{end}}
+ ],
+ "completed_log": [{{range $i, $item := .CompletedLog}}{{if $i}},{{end}}
+ {
+ "source": "{{$item.Source}}",
+ "title": "{{$item.Title}}",
+ "due_date": {{if $item.DueDate}}"{{$item.DueDate.Format "2006-01-02T15:04:05Z07:00"}}"{{else}}null{{end}},
+ "completed_at": "{{$item.CompletedAt.Format "2006-01-02T15:04:05Z07:00"}}"
}{{end}}
]
}
@@ -60,7 +103,7 @@
<div class="card">
<h1>Agent Context</h1>
- <p>Timeline data for <strong>{{.AgentName}}</strong></p>
+ <p>Full timeline data for <strong>{{.AgentName}}</strong></p>
<div class="label">Generated At</div>
<div class="value">{{.GeneratedAt}}</div>
@@ -76,46 +119,173 @@
<div class="summary-value">{{with .Summary}}{{.total_items}}{{else}}0{{end}}</div>
<div class="summary-label">Total Items</div>
</div>
+ {{with .Summary}}{{with .by_section}}
<div class="summary-item">
- <div class="summary-value">{{with .Summary}}{{.overdue}}{{else}}0{{end}}</div>
+ <div class="summary-value section-overdue">{{if index . "overdue"}}{{index . "overdue"}}{{else}}0{{end}}</div>
<div class="summary-label">Overdue</div>
</div>
<div class="summary-item">
- <div class="summary-value">{{with .Summary}}{{.today}}{{else}}0{{end}}</div>
- <div class="summary-label">Due Today</div>
+ <div class="summary-value section-today">{{if index . "today"}}{{index . "today"}}{{else}}0{{end}}</div>
+ <div class="summary-label">Today</div>
+ </div>
+ <div class="summary-item">
+ <div class="summary-value section-tomorrow">{{if index . "tomorrow"}}{{index . "tomorrow"}}{{else}}0{{end}}</div>
+ <div class="summary-label">Tomorrow</div>
+ </div>
+ {{end}}{{end}}
+ <div class="summary-item">
+ <div class="summary-value">{{len .CompletedLog}}</div>
+ <div class="summary-label">Recently Done</div>
</div>
</div>
</div>
<div class="card">
- <h2>Timeline</h2>
- {{if .Timeline}}
- <table>
- <thead>
- <tr>
- <th>Source</th>
- <th>Title</th>
- <th>Due</th>
- <th>Type</th>
- </tr>
- </thead>
- <tbody>
- {{range .Timeline}}
- <tr>
- <td><span class="source source-{{.Source}}">{{.Source}}</span></td>
- <td>
- {{if .URL}}<a href="{{.URL}}" target="_blank">{{.Title}}</a>{{else}}{{.Title}}{{end}}
- {{if .Description}}<br><small style="color: #888;">{{.Description}}</small>{{end}}
- </td>
- <td>{{if .Due}}{{.Due.Format "Jan 2, 3:04 PM"}}{{else}}-{{end}}</td>
- <td>{{.Type}}</td>
- </tr>
- {{end}}
- </tbody>
- </table>
- {{else}}
- <p>No items in the timeline for this date range.</p>
- {{end}}
+ <h2>Today's Schedule ({{.Today}})</h2>
+ <div class="all-day" id="all-day-items"></div>
+ <div class="calendar" id="today-calendar"></div>
+ <script>
+ (function() {
+ const data = document.getElementById('agent-data');
+ const items = data ? JSON.parse(data.textContent).today_items || [] : [];
+ const calendar = document.getElementById('today-calendar');
+ const allDay = document.getElementById('all-day-items');
+ const startHour = 6;
+ const endHour = 22;
+ const hourHeight = 40;
+
+ // Generate hour rows
+ for (let hour = startHour; hour <= endHour; hour++) {
+ const row = document.createElement('div');
+ row.className = 'calendar-hour';
+ row.dataset.hour = hour;
+ const label = document.createElement('span');
+ label.className = 'calendar-hour-label';
+ if (hour < 12) label.textContent = hour + 'am';
+ else if (hour === 12) label.textContent = '12pm';
+ else label.textContent = (hour - 12) + 'pm';
+ row.appendChild(label);
+ calendar.appendChild(row);
+ }
+
+ if (items.length === 0) {
+ calendar.innerHTML = '<p style="color: #888; padding: 20px;">No items scheduled for today.</p>';
+ allDay.style.display = 'none';
+ return;
+ }
+
+ items.forEach(item => {
+ if (!item.due) return;
+ const due = new Date(item.due);
+ const hour = due.getHours();
+ const minutes = due.getMinutes();
+
+ // All-day items (midnight) go in separate section
+ if (hour === 0 && minutes === 0) {
+ const el = document.createElement('span');
+ el.className = 'all-day-item source-' + item.source;
+ el.textContent = item.title;
+ allDay.appendChild(el);
+ return;
+ }
+
+ // Skip items outside 6am-10pm range
+ if (hour < startHour || hour > endHour) return;
+
+ const el = document.createElement('div');
+ el.className = 'calendar-item source-' + item.source;
+ const top = (hour - startHour) * hourHeight + (minutes / 60) * hourHeight;
+ el.style.top = top + 'px';
+ el.style.height = '34px';
+ el.innerHTML = '<div class="calendar-item-time">' + due.toLocaleTimeString('en-US', {hour: 'numeric', minute: '2-digit'}) + '</div><div class="calendar-item-title">' + item.title + '</div>';
+ if (item.url) {
+ el.style.cursor = 'pointer';
+ el.onclick = () => window.open(item.url, '_blank');
+ }
+ calendar.appendChild(el);
+ });
+
+ // Hide all-day section if empty
+ if (allDay.children.length === 0) {
+ allDay.style.display = 'none';
+ }
+ })();
+ </script>
</div>
+
+ <div class="card">
+ <div class="tabs">
+ <button class="tab active" onclick="showTab('timeline')">Timeline</button>
+ <button class="tab" onclick="showTab('completed')">Completed Log</button>
+ </div>
+
+ <div id="timeline" class="tab-content active">
+ <h2>Upcoming &amp; Overdue</h2>
+ {{if .Timeline}}
+ <table>
+ <thead>
+ <tr>
+ <th>Source</th>
+ <th>Title</th>
+ <th>Due</th>
+ <th>Section</th>
+ </tr>
+ </thead>
+ <tbody>
+ {{range .Timeline}}
+ <tr>
+ <td><span class="source source-{{.Source}}">{{.Source}}</span></td>
+ <td>
+ {{if .URL}}<a href="{{.URL}}" target="_blank">{{.Title}}</a>{{else}}{{.Title}}{{end}}
+ {{if .Description}}<br><small style="color: #888;">{{.Description}}</small>{{end}}
+ </td>
+ <td>{{if .Due}}{{.Due.Format "Jan 2, 3:04 PM"}}{{else}}-{{end}}</td>
+ <td><span class="section-{{.DaySection}}">{{.DaySection}}</span></td>
+ </tr>
+ {{end}}
+ </tbody>
+ </table>
+ {{else}}
+ <p style="color: #888;">No upcoming or overdue items.</p>
+ {{end}}
+ </div>
+
+ <div id="completed" class="tab-content">
+ <h2>Recently Completed</h2>
+ {{if .CompletedLog}}
+ <table>
+ <thead>
+ <tr>
+ <th>Source</th>
+ <th>Title</th>
+ <th>Due Date</th>
+ <th>Completed</th>
+ </tr>
+ </thead>
+ <tbody>
+ {{range .CompletedLog}}
+ <tr class="completed-item">
+ <td><span class="source source-{{.Source}}">{{.Source}}</span></td>
+ <td>{{.Title}}</td>
+ <td>{{if .DueDate}}{{.DueDate.Format "Jan 2"}}{{else}}-{{end}}</td>
+ <td>{{.CompletedAt.Format "Jan 2, 3:04 PM"}}</td>
+ </tr>
+ {{end}}
+ </tbody>
+ </table>
+ {{else}}
+ <p style="color: #888;">No completed tasks logged yet.</p>
+ {{end}}
+ </div>
+ </div>
+
+ <script>
+ function showTab(tabId) {
+ document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
+ document.querySelectorAll('.tab-content').forEach(c => c.classList.remove('active'));
+ document.querySelector('.tab[onclick*="' + tabId + '"]').classList.add('active');
+ document.getElementById(tabId).classList.add('active');
+ }
+ </script>
</body>
</html>