Docs, ticks, working app

This commit is contained in:
Hugh Kaznowski
2026-09-05 15:05:39 +01:00
parent 41362aa3d8
commit 51e577f01e
41 changed files with 1039 additions and 683 deletions
+24 -24
View File
@@ -155,14 +155,14 @@ export function Metrics({ range }: { range: TimeRange | null }) {
const groups = groupByLabels(bucketValues)
return (
<div className="border-b border-neutral-800 p-3 text-sm">
<div className="mb-2 text-xs text-neutral-500">Metrics</div>
<div className="border-b border-border p-3 text-sm">
<div className="mb-2 text-xs text-text-subtle">Metrics</div>
<div className="flex gap-4">
<form onSubmit={handleSubmit} className="w-80 shrink-0 space-y-2">
<div>
<label className="mb-1 block text-xs text-neutral-500">Name</label>
<label className="mb-1 block text-xs text-text-subtle">Name</label>
<input
className="w-full rounded border border-neutral-700 bg-neutral-900 px-2 py-1 text-sm text-neutral-100 placeholder:text-neutral-500 focus:border-neutral-500 focus:outline-none"
className="w-full rounded border border-border-soft bg-surface-1 px-2 py-1 text-sm text-text placeholder:text-text-subtle focus:border-border-strong focus:outline-none"
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="requests"
@@ -170,9 +170,9 @@ export function Metrics({ range }: { range: TimeRange | null }) {
/>
</div>
<div>
<label className="mb-1 block text-xs text-neutral-500">Message match regex</label>
<label className="mb-1 block text-xs text-text-subtle">Message match regex</label>
<input
className="w-full rounded border border-neutral-700 bg-neutral-900 px-2 py-1 font-mono text-sm text-neutral-100 placeholder:text-neutral-500 focus:border-neutral-500 focus:outline-none"
className="w-full rounded border border-border-soft bg-surface-1 px-2 py-1 font-mono text-sm text-text placeholder:text-text-subtle focus:border-border-strong focus:outline-none"
value={regex}
onChange={(e) => setRegex(e.target.value)}
placeholder={String.raw`request path=(?P<path>\S+) duration=(?P<duration>\d+)`}
@@ -180,12 +180,12 @@ export function Metrics({ range }: { range: TimeRange | null }) {
/>
</div>
<div>
<div className="mb-1 flex items-center justify-between text-xs text-neutral-500">
<div className="mb-1 flex items-center justify-between text-xs text-text-subtle">
<span>Params</span>
<button
type="button"
onClick={addMatcher}
className="rounded border border-neutral-700 px-2 py-0.5 text-neutral-300 hover:bg-neutral-800"
className="rounded border border-border-soft px-2 py-0.5 text-text-secondary hover:bg-surface-2"
>
+ Add
</button>
@@ -194,19 +194,19 @@ export function Metrics({ range }: { range: TimeRange | null }) {
{matchers.map((matcher, i) => (
<div key={i} className="flex items-center gap-1">
<input
className="w-24 min-w-0 flex-1 rounded border border-neutral-700 bg-neutral-900 px-1.5 py-1 text-xs text-neutral-100 placeholder:text-neutral-500 focus:border-neutral-500 focus:outline-none"
className="w-24 min-w-0 flex-1 rounded border border-border-soft bg-surface-1 px-1.5 py-1 text-xs text-text placeholder:text-text-subtle focus:border-border-strong focus:outline-none"
value={matcher.param_name}
onChange={(e) => updateMatcher(i, { param_name: e.target.value })}
placeholder="param_name"
/>
<input
className="w-24 min-w-0 flex-1 rounded border border-neutral-700 bg-neutral-900 px-1.5 py-1 text-xs text-neutral-100 placeholder:text-neutral-500 focus:border-neutral-500 focus:outline-none"
className="w-24 min-w-0 flex-1 rounded border border-border-soft bg-surface-1 px-1.5 py-1 text-xs text-text placeholder:text-text-subtle focus:border-border-strong focus:outline-none"
value={matcher.capture_group}
onChange={(e) => updateMatcher(i, { capture_group: e.target.value })}
placeholder="capture_group"
/>
<select
className="rounded border border-neutral-700 bg-neutral-900 px-1 py-1 text-xs text-neutral-100 focus:border-neutral-500 focus:outline-none"
className="rounded border border-border-soft bg-surface-1 px-1 py-1 text-xs text-text focus:border-border-strong focus:outline-none"
value={matcher.kind}
onChange={(e) => updateMatcher(i, { kind: e.target.value as MetricParamMatcher['kind'] })}
>
@@ -218,7 +218,7 @@ export function Metrics({ range }: { range: TimeRange | null }) {
type="button"
onClick={() => removeMatcher(i)}
disabled={matchers.length === 1}
className="rounded border border-neutral-700 px-1.5 py-1 text-xs text-neutral-400 hover:bg-neutral-800 disabled:opacity-30"
className="rounded border border-border-soft px-1.5 py-1 text-xs text-text-muted hover:bg-surface-2 disabled:opacity-30"
>
×
</button>
@@ -226,28 +226,28 @@ export function Metrics({ range }: { range: TimeRange | null }) {
))}
</div>
</div>
{submitError && <div className="rounded border border-red-900 bg-red-950/50 px-2 py-1 text-xs text-red-400">{submitError}</div>}
{submitError && <div className="rounded border border-danger-border bg-danger-bg/50 px-2 py-1 text-xs text-danger-text">{submitError}</div>}
<button
type="submit"
disabled={submitting}
className="rounded border border-neutral-700 px-3 py-1 text-sm text-neutral-300 hover:bg-neutral-800 disabled:opacity-50"
className="rounded border border-border-soft px-3 py-1 text-sm text-text-secondary hover:bg-surface-2 disabled:opacity-50"
>
{submitting ? 'Declaring…' : 'Declare metric'}
</button>
</form>
<div className="min-w-0 flex-1">
<div className="mb-1 text-xs text-neutral-500">Declared metrics</div>
<div className="mb-1 text-xs text-text-subtle">Declared metrics</div>
<div className="mb-3 flex flex-wrap gap-1">
{metrics.length === 0 && <span className="text-xs text-neutral-600">No metrics declared yet.</span>}
{metrics.length === 0 && <span className="text-xs text-text-faint">No metrics declared yet.</span>}
{metrics.map((m) => (
<button
key={m.name}
onClick={() => setSelected(m.name)}
className={`rounded border px-2 py-1 text-xs ${
selected === m.name
? 'border-sky-500 bg-sky-500/10 text-sky-300'
: 'border-neutral-700 text-neutral-300 hover:bg-neutral-800'
? 'border-accent bg-accent/10 text-accent-text'
: 'border-border-soft text-text-secondary hover:bg-surface-2'
}`}
title={m.message_match_regex}
>
@@ -258,15 +258,15 @@ export function Metrics({ range }: { range: TimeRange | null }) {
{selected && (
<div>
<div className="mb-1 text-xs text-neutral-500">
<div className="mb-1 text-xs text-text-subtle">
{selected} — {bucketValues.length} occurrence{bucketValues.length === 1 ? '' : 's'} in range
</div>
{groups.length === 0 ? (
<div className="text-xs text-neutral-600">No matching values in the active range.</div>
<div className="text-xs text-text-faint">No matching values in the active range.</div>
) : (
<div className="max-h-56 overflow-auto rounded border border-neutral-800">
<div className="max-h-56 overflow-auto rounded border border-border">
<table className="w-full border-collapse text-xs">
<thead className="sticky top-0 bg-neutral-900 text-neutral-500">
<thead className="sticky top-0 bg-surface-1 text-text-subtle">
<tr>
<th className="px-2 py-1 text-left font-normal">Labels</th>
<th className="px-2 py-1 text-right font-normal">Count</th>
@@ -275,14 +275,14 @@ export function Metrics({ range }: { range: TimeRange | null }) {
</thead>
<tbody>
{groups.map((group) => (
<tr key={group.key} className="border-t border-neutral-800 text-neutral-300">
<tr key={group.key} className="border-t border-border text-text-secondary">
<td className="px-2 py-1 font-mono">
{Object.entries(group.labels)
.map(([k, v]) => `${k}=${v}`)
.join(', ') || '(no labels)'}
</td>
<td className="px-2 py-1 text-right">{group.count}</td>
<td className="px-2 py-1 font-mono text-sky-400">{sparkline(group.values, range)}</td>
<td className="px-2 py-1 font-mono text-accent-text">{sparkline(group.values, range)}</td>
</tr>
))}
</tbody>