diff options
Diffstat (limited to 'web')
| -rw-r--r-- | web/templates/agent-context.html | 258 |
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 & 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> |
