feat(web/diagnostics): default to recent 500, move time window to Advanced
test-web / test (push) Successful in 39s

The diagnostics view already defaulted to the most recent 500 events (no
window); make that the obvious path. Device/Kind stay primary; the
start/end window + row cap move into a collapsed "Advanced filters"
disclosure (auto-opens when a window is active) with a "Reset to recent
500" action. Caption now states whether you're seeing the recent default
or a windowed slice.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K55iTxn95BtshocgdE1shW
This commit is contained in:
2026-06-30 12:42:25 -04:00
parent 782f152d37
commit 96b15b75e6
+110 -71
View File
@@ -36,13 +36,26 @@
} }
} }
// Default view = the most recent N events, no time window. The
// start/end window is an Advanced affordance.
const DEFAULT_LIMIT = 500;
// Filter state. // Filter state.
let accountId = $state(''); let accountId = $state('');
let clientId = $state(''); let clientId = $state('');
let kind = $state(''); let kind = $state('');
let fromLocal = $state(''); let fromLocal = $state('');
let toLocal = $state(''); let toLocal = $state('');
let limit = $state(500); let limit = $state(DEFAULT_LIMIT);
// True when an explicit time window is set (drives the caption wording).
const hasWindow = $derived(Boolean(fromLocal || toLocal));
function resetWindow() {
fromLocal = '';
toLocal = '';
limit = DEFAULT_LIMIT;
}
// datetime-local (browser-local, no tz) → RFC3339 UTC the API accepts. // datetime-local (browser-local, no tz) → RFC3339 UTC the API accepts.
function toRfc(v: string): string | undefined { function toRfc(v: string): string | undefined {
@@ -191,77 +204,97 @@
</div> </div>
</section> </section>
<!-- Filters --> <!-- Filters. Default view = most recent {DEFAULT_LIMIT} events, no time
<section class="flex flex-wrap items-end gap-3"> window; the start/end window lives under Advanced. -->
<label class="block text-xs text-text-secondary"> <section class="space-y-3">
Device <div class="flex flex-wrap items-end gap-3">
<select <label class="block text-xs text-text-secondary">
bind:value={clientId} Device
class="mt-1 block w-48 rounded border border-border bg-surface px-2 py-1.5 text-sm text-text-primary" <select
> bind:value={clientId}
<option value="">All devices</option> class="mt-1 block w-48 rounded border border-border bg-surface px-2 py-1.5 text-sm text-text-primary"
{#each devices as d (d.client_id)} >
<option value={d.client_id}>{d.client_id.slice(0, 12)} · {d.event_count}</option> <option value="">All devices</option>
{/each} {#each devices as d (d.client_id)}
</select> <option value={d.client_id}>{d.client_id.slice(0, 12)} · {d.event_count}</option>
</label> {/each}
<label class="block text-xs text-text-secondary"> </select>
Kind </label>
<select <label class="block text-xs text-text-secondary">
bind:value={kind} Kind
class="mt-1 block w-40 rounded border border-border bg-surface px-2 py-1.5 text-sm text-text-primary" <select
> bind:value={kind}
<option value="">All kinds</option> class="mt-1 block w-40 rounded border border-border bg-surface px-2 py-1.5 text-sm text-text-primary"
{#each KINDS as k (k)} >
<option value={k}>{kindLabel(k)}</option> <option value="">All kinds</option>
{/each} {#each KINDS as k (k)}
</select> <option value={k}>{kindLabel(k)}</option>
</label> {/each}
<label class="block text-xs text-text-secondary"> </select>
From </label>
<input
type="datetime-local"
bind:value={fromLocal}
class="mt-1 block rounded border border-border bg-surface px-2 py-1.5 text-sm text-text-primary"
/>
</label>
<label class="block text-xs text-text-secondary">
To
<input
type="datetime-local"
bind:value={toLocal}
class="mt-1 block rounded border border-border bg-surface px-2 py-1.5 text-sm text-text-primary"
/>
</label>
<label class="block text-xs text-text-secondary">
Limit
<input
type="number"
min="1"
max="5000"
bind:value={limit}
class="mt-1 block w-24 rounded border border-border bg-surface px-2 py-1.5 text-sm text-text-primary"
/>
</label>
<div class="ml-auto flex gap-2"> <div class="ml-auto flex gap-2">
<button <button
type="button" type="button"
onclick={onCopyJson} onclick={onCopyJson}
disabled={rows.length === 0} disabled={rows.length === 0}
class="inline-flex items-center gap-1.5 rounded border border-border px-3 py-2 text-sm text-text-primary hover:bg-surface-hover disabled:opacity-50" class="inline-flex items-center gap-1.5 rounded border border-border px-3 py-2 text-sm text-text-primary hover:bg-surface-hover disabled:opacity-50"
> >
<Copy size={15} /> Copy JSON <Copy size={15} /> Copy JSON
</button> </button>
<button <button
type="button" type="button"
onclick={onDownloadNdjson} onclick={onDownloadNdjson}
disabled={rows.length === 0} disabled={rows.length === 0}
class="inline-flex items-center gap-1.5 rounded border border-border px-3 py-2 text-sm text-text-primary hover:bg-surface-hover disabled:opacity-50" class="inline-flex items-center gap-1.5 rounded border border-border px-3 py-2 text-sm text-text-primary hover:bg-surface-hover disabled:opacity-50"
> >
<Download size={15} /> Download <Download size={15} /> Download
</button> </button>
</div>
</div> </div>
<!-- Advanced: explicit start/end window + row cap. Collapsed by
default so the common case (recent N) needs no interaction. -->
<details class="rounded-md border border-border" open={hasWindow}>
<summary class="cursor-pointer px-3 py-2 text-xs text-text-secondary">
Advanced filters{hasWindow ? ' · window active' : ''}
</summary>
<div class="flex flex-wrap items-end gap-3 border-t border-border px-3 py-3">
<label class="block text-xs text-text-secondary">
From
<input
type="datetime-local"
bind:value={fromLocal}
class="mt-1 block rounded border border-border bg-surface px-2 py-1.5 text-sm text-text-primary"
/>
</label>
<label class="block text-xs text-text-secondary">
To
<input
type="datetime-local"
bind:value={toLocal}
class="mt-1 block rounded border border-border bg-surface px-2 py-1.5 text-sm text-text-primary"
/>
</label>
<label class="block text-xs text-text-secondary">
Limit
<input
type="number"
min="1"
max="5000"
bind:value={limit}
class="mt-1 block w-24 rounded border border-border bg-surface px-2 py-1.5 text-sm text-text-primary"
/>
</label>
<button
type="button"
onclick={resetWindow}
class="rounded border border-border px-3 py-2 text-sm text-text-secondary hover:bg-surface-hover"
>
Reset to recent {DEFAULT_LIMIT}
</button>
</div>
</details>
</section> </section>
<!-- Timeline --> <!-- Timeline -->
@@ -276,7 +309,13 @@
</p> </p>
{:else} {:else}
<div class="flex items-center justify-between"> <div class="flex items-center justify-between">
<span class="text-xs text-text-muted">{rows.length} events (oldest first)</span> <span class="text-xs text-text-muted">
{#if hasWindow}
{rows.length} events in window (oldest first)
{:else}
Most recent {rows.length} events (oldest first) · set a time window under Advanced
{/if}
</span>
</div> </div>
<ul class="divide-y divide-border rounded-md border border-border font-mono text-xs"> <ul class="divide-y divide-border rounded-md border border-border font-mono text-xs">
{#each rows as r (r.id)} {#each rows as r (r.id)}