Files
knox/internal/server/layout.html

300 lines
13 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{{define "head"}}<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Knox{{if .title}} · {{.title}}{{end}}</title>
<script src="https://unpkg.com/htmx.org@2.0.4"></script>
<script src="https://cdn.tailwindcss.com"></script>
<style>body{font-family:system-ui,sans-serif}</style>
</head>
<body class="bg-gray-950 text-gray-100 min-h-screen">
<header class="border-b border-gray-800 p-4 flex items-center justify-between">
<h1 class="text-xl font-bold text-emerald-400"><a href="/">Knox</a>{{if .title}} <span class="text-gray-400 text-sm font-normal">/ {{.title}}</span>{{end}}</h1>
<nav class="flex gap-4 text-sm">
<a href="/" class="text-gray-300 hover:text-white">Dashboard</a>
<a href="/threads" class="text-gray-300 hover:text-white">Threads</a>
</nav>
</header>
{{end}}
{{define "foot"}}</body>
</html>
{{end}}
{{define "not_found"}}
{{template "head" .}}
<main class="max-w-4xl mx-auto p-6 text-center">
<div class="text-gray-500 text-lg">Page not found</div>
<a href="/" class="mt-6 inline-block text-emerald-400 hover:text-emerald-300 text-sm">Back to Dashboard</a>
</main>
{{template "foot" .}}
{{end}}
{{define "dashboard"}}
{{template "head" .}}
<main class="max-w-6xl mx-auto p-6 space-y-6">
{{if .golden}}
<div class="bg-emerald-900/30 border border-emerald-800 rounded-lg p-4">
<div class="text-xs text-emerald-400 uppercase tracking-wider">Golden Thread</div>
<div class="text-lg font-semibold mt-1">{{.golden.Title}}</div>
<div class="text-sm text-gray-400">{{.golden.Motivation}}</div>
</div>
{{end}}
<div class="flex gap-4">
<input type="text" name="q" placeholder="Search knowledge index..."
class="flex-1 bg-gray-900 border border-gray-700 rounded px-4 py-2 text-sm focus:outline-none focus:border-emerald-500"
hx-get="/search" hx-trigger="keyup changed delay:300ms" hx-target="#results" hx-indicator="#search-indicator">
<button class="bg-emerald-700 hover:bg-emerald-600 px-4 py-2 rounded text-sm font-medium"
hx-get="/search" hx-include="[name=q]" hx-target="#results" hx-indicator="#search-indicator">Search</button>
</div>
<div id="results" class="text-sm text-gray-500"></div>
<div id="search-indicator" class="htmx-indicator text-xs text-emerald-400">Searching...</div>
<div hx-get="/stats" hx-trigger="load, every 30s" hx-swap="innerHTML">
<div class="text-gray-500">Loading stats...</div>
</div>
<section>
<h2 class="text-sm font-semibold text-gray-500 uppercase tracking-wider mb-2">Issues</h2>
{{if .gitea}}
<div class="space-y-1 mb-4">
{{range .gitea}}
<a href="/entries/{{.Fingerprint}}" class="block bg-gray-900 hover:bg-gray-800 rounded px-3 py-2 text-sm">
<span class="text-emerald-300">{{trunc .Title 80}}</span>
<span class="ml-2 text-xs text-gray-600">{{displayTime .}}</span>
</a>
{{end}}
</div>
{{end}}
</section>
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
<section>
<h3 class="text-sm font-semibold text-gray-500 uppercase tracking-wider mb-2">Notes</h3>
{{if .obsidian}}
<div class="space-y-1">
{{range .obsidian}}
<a href="/entries/{{.Fingerprint}}" class="block bg-gray-900 hover:bg-gray-800 rounded px-3 py-2 text-sm">
<span>{{trunc .Title 50}}</span>
<span class="ml-2 text-xs text-gray-600">{{displayTime .}}</span>
</a>
{{end}}
</div>
{{else}}
<div class="text-gray-500 text-xs">No notes indexed.</div>
{{end}}
</section>
<section>
<h3 class="text-sm font-semibold text-gray-500 uppercase tracking-wider mb-2">Sessions</h3>
{{if .session}}
<div class="space-y-1">
{{range .session}}
<a href="/entries/{{.Fingerprint}}" class="block bg-gray-900 hover:bg-gray-800 rounded px-3 py-2 text-sm">
<span>{{trunc .Title 50}}</span>
<span class="ml-2 text-xs text-gray-600">{{displayTime .}}</span>
</a>
{{end}}
</div>
{{else}}
<div class="text-gray-500 text-xs">No sessions tracked.</div>
{{end}}
</section>
<section>
<h3 class="text-sm font-semibold text-gray-500 uppercase tracking-wider mb-2">Skills</h3>
{{if .skills}}
<div class="space-y-1">
{{range .skills}}
<a href="/entries/{{.Fingerprint}}" class="block bg-gray-900 hover:bg-gray-800 rounded px-3 py-2 text-sm">
<span>{{trunc .Title 50}}</span>
<span class="ml-2 text-xs text-gray-600">{{displayTime .}}</span>
</a>
{{end}}
</div>
{{else}}
<div class="text-gray-500 text-xs">No skills indexed.</div>
{{end}}
</section>
</div>
<section>
<h3 class="text-sm font-semibold text-gray-500 uppercase tracking-wider mb-2">Feed</h3>
<div class="space-y-1">
{{range .recent}}
<a href="/entries/{{.Fingerprint}}" class="block bg-gray-900 hover:bg-gray-800 rounded px-3 py-2 text-sm">
<span class="font-mono text-xs text-gray-500">{{shortFP .Fingerprint}}</span>
<span class="ml-2">{{trunc .Title 60}}</span>
<span class="ml-2 text-xs px-1.5 py-0.5 rounded bg-gray-800 text-gray-400">{{.SourceID}}</span>
<span class="ml-2 text-xs text-gray-600">{{displayTime .}}</span>
</a>
{{else}}
<div class="text-gray-500 text-sm">No entries yet.</div>
{{end}}
</div>
</section>
</main>
{{template "foot" .}}
{{end}}
{{define "search_results"}}
{{if .results}}
<div class="space-y-1">
<div class="text-xs text-gray-500 mb-2">{{len .results}} results for "{{.query}}"</div>
{{range .results}}
<a href="/entries/{{.Fingerprint}}" class="block bg-gray-900 hover:bg-gray-800 rounded px-3 py-2 text-sm">
<div>
<span class="font-mono text-xs text-gray-500">{{shortFP .Fingerprint}}</span>
<span class="ml-2 font-medium">{{trunc .Title 70}}</span>
<span class="ml-2 text-xs px-1.5 py-0.5 rounded bg-gray-800 text-gray-400">{{.SourceID}}</span>
</div>
{{if .Summary}}<div class="text-gray-500 text-xs mt-0.5 ml-12">{{trunc .Summary 120}}</div>{{end}}
</a>
{{end}}
</div>
{{else}}
<p class="text-gray-500 text-sm">No results for "{{.query}}".</p>
{{end}}
{{end}}
{{define "stats"}}
<div class="grid grid-cols-2 md:grid-cols-4 gap-4">
{{range .stats}}
<div class="bg-gray-900 rounded-lg p-3 border border-gray-800">
<div class="text-xs text-gray-500 uppercase tracking-wider">{{.Label}}</div>
<div class="text-xl font-semibold mt-1">{{.Value}}</div>
</div>
{{end}}
</div>
{{end}}
{{define "thread_list"}}
{{template "head" .}}
<main class="max-w-4xl mx-auto p-6 space-y-4">
<div class="flex gap-2 mb-4">
<a href="/threads" class="px-3 py-1 text-sm rounded {{if eq .status ""}}bg-emerald-700{{else}}bg-gray-800 hover:bg-gray-700{{end}}">All</a>
<a href="/threads?status=active" class="px-3 py-1 text-sm rounded {{if eq .status "active"}}bg-emerald-700{{else}}bg-gray-800 hover:bg-gray-700{{end}}">Active</a>
<a href="/threads?status=stalled" class="px-3 py-1 text-sm rounded {{if eq .status "stalled"}}bg-emerald-700{{else}}bg-gray-800 hover:bg-gray-700{{end}}">Stalled</a>
<a href="/threads?status=resolved" class="px-3 py-1 text-sm rounded {{if eq .status "resolved"}}bg-emerald-700{{else}}bg-gray-800 hover:bg-gray-700{{end}}">Resolved</a>
</div>
{{range .threads}}
<a href="/threads/{{.ID}}" class="block bg-gray-900 rounded-lg p-4 border border-gray-800 hover:border-gray-600">
<div class="flex items-center gap-2">
<span class="text-lg font-semibold">{{.Title}}</span>
<span class="text-xs px-1.5 py-0.5 rounded {{if eq .Status "active"}}bg-emerald-900 text-emerald-300{{else if eq .Status "resolved"}}bg-blue-900 text-blue-300{{else}}bg-gray-800 text-gray-400{{end}}">{{.Status}}</span>
<span class="text-xs text-gray-500">{{.Priority}}</span>
</div>
{{if .Motivation}}<div class="text-sm text-gray-400 mt-1">{{.Motivation}}</div>{{end}}
<div class="text-xs text-gray-600 mt-1">{{.EntryCount}} observations · {{humanTime .UpdatedAt}}</div>
</a>
{{else}}
<div class="text-gray-500 text-sm">No threads found.</div>
{{end}}
</main>
{{template "foot" .}}
{{end}}
{{define "thread_detail"}}
{{template "head" .}}
<main class="max-w-4xl mx-auto p-6 space-y-6">
<div class="bg-gray-900 rounded-lg p-4 border border-gray-800">
<div class="flex items-center gap-2">
<h2 class="text-xl font-bold">{{.thread.Title}}</h2>
{{if .isGolden}}<span class="text-xs bg-emerald-900 text-emerald-300 px-2 py-0.5 rounded">GOLDEN</span>{{end}}
</div>
<div class="flex gap-4 mt-2 text-sm text-gray-400">
<span>{{.thread.Status}}</span>
<span>{{.thread.Priority}}</span>
<span>Created {{humanTime .thread.CreatedAt}}</span>
</div>
{{if .thread.Motivation}}<div class="mt-3 text-gray-300"><strong>Motivation:</strong> {{.thread.Motivation}}</div>{{end}}
{{if ne .thread.Provenance "{}"}}<div class="mt-1 text-xs text-gray-500"><strong>Provenance:</strong> {{.thread.Provenance}}</div>{{end}}
</div>
{{if .provenance}}
<section>
<h3 class="text-sm font-semibold text-gray-400 uppercase tracking-wider mb-2">Provenance Graph</h3>
<div class="bg-gray-900 rounded-lg p-3 border border-gray-800 text-sm font-mono">
{{range .provenance}}
<div class="text-gray-400">└─ <a href="{{fpURL .Fingerprint}}" class="text-emerald-400 hover:text-emerald-300">{{shortFP .Fingerprint}}</a> <span class="text-gray-600">[{{.Relation}}]</span></div>
{{end}}
</div>
</section>
{{end}}
{{if .obs}}
<section>
<h3 class="text-sm font-semibold text-gray-400 uppercase tracking-wider mb-2">Observations ({{len .obs}})</h3>
<div class="space-y-1">
{{range .obs}}
<div class="bg-gray-900 rounded px-3 py-2 text-sm flex items-center gap-3">
<span class="font-mono text-xs text-gray-500">#{{.ID}}</span>
<span class="flex-1">{{trunc .Title 50}}</span>
<span class="text-xs text-gray-500">{{.SourceID}}</span>
{{if gt .Count 1}}<span class="text-xs text-amber-500" title="seen {{.Count}} times">×{{.Count}}</span>{{end}}
<span class="text-xs text-gray-600">{{humanTime .CollectedAt}}</span>
</div>
{{end}}
</div>
</section>
{{end}}
{{if .notes}}
<section>
<h3 class="text-sm font-semibold text-gray-400 uppercase tracking-wider mb-2">Notes</h3>
<div class="space-y-2">
{{range .notes}}
<div class="bg-gray-900 rounded p-3 border border-gray-800">
<div class="text-xs text-gray-500">{{humanTime .CreatedAt}}</div>
<div class="text-sm mt-1">{{.Note}}</div>
</div>
{{end}}
</div>
</section>
{{end}}
</main>
{{template "foot" .}}
{{end}}
{{define "entry_detail"}}
{{template "head" .}}
<main class="max-w-4xl mx-auto p-6 space-y-4">
<div class="bg-gray-900 rounded-lg p-4 border border-gray-800">
<div class="grid grid-cols-2 gap-4 text-sm">
<div><span class="text-gray-500">Fingerprint</span><br><span class="font-mono text-xs">{{.entry.Fingerprint}}</span></div>
<div><span class="text-gray-500">Source</span><br>{{.entry.SourceID}}</div>
<div><span class="text-gray-500">Title</span><br><span class="font-medium">{{.entry.Title}}</span></div>
<div><span class="text-gray-500">Project</span><br>{{.entry.Project}}</div>
<div><span class="text-gray-500">Path</span><br><span class="font-mono text-xs">{{.entry.SourcePath}}</span></div>
<div><span class="text-gray-500">Type</span><br>{{.entry.ContentType}}</div>
<div><span class="text-gray-500">Signal Time</span><br>{{if .entry.CreatedAt}}{{humanTime .entry.CreatedAt}}{{else}}<span class="text-gray-500">—</span>{{end}}</div>
<div><span class="text-gray-500">Ingested</span><br>{{humanTime .entry.LastSeen}}</div>
<div><span class="text-gray-500">Ref Count</span><br>{{.entry.RefCount}}</div>
<div><span class="text-gray-500">Confidence</span><br>{{printf "%.1f" .entry.LastConfidence}}</div>
</div>
{{if .entry.Summary}}<div class="mt-4 p-3 bg-gray-800 rounded text-sm">{{.entry.Summary}}</div>{{end}}
</div>
{{if .obs}}
<section>
<h3 class="text-sm font-semibold text-gray-400 uppercase tracking-wider mb-2">Observation History ({{len .obs}})</h3>
<div class="space-y-1">
{{range .obs}}
<div class="bg-gray-900 rounded px-3 py-2 text-sm flex items-center gap-3">
<span class="font-mono text-xs text-gray-500">#{{.ID}}</span>
<span class="text-xs text-gray-500">{{humanTime .CollectedAt}}</span>
{{if gt .Count 1}}<span class="text-xs text-amber-500" title="seen {{.Count}} times, unchanged">×{{.Count}}</span>{{end}}
<span class="text-xs px-1.5 py-0.5 rounded bg-gray-800 text-gray-400">{{.Trigger}}</span>
<span class="text-xs text-gray-500">{{.IngesterVersion}}</span>
<span class="text-xs text-gray-500">conf:{{printf "%.1f" .Confidence}}</span>
</div>
{{end}}
</div>
</section>
{{end}}
</main>
{{template "foot" .}}
{{end}}