Trim comments across Rust and webapp source to direct one-liners (ticket 030)
Pass over src/*.rs, webapp/src/**, and the in-app "?" docs prose: condensed multi-paragraph doc comments to single lines that state the non-obvious why, deleted comments that only restated adjacent code, and tightened the Docs.tsx help-panel wording. No logic, structure, or code formatting changed — verified via cargo test (143 passed) and tsc -b && vite build. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XFUNZwCvz3kcod3352ZR2o
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
3cef6dc8ae
commit
00c21c8032
@@ -1,9 +1,7 @@
|
||||
import { useEffect, useRef, useState, type ReactNode } from 'react'
|
||||
import type { DocsTarget } from '../lib/docsNav'
|
||||
|
||||
/// One accordion section: a title plus its body, rendered as plain JSX so
|
||||
/// each page can mix prose, lists, and inline code without pulling in a
|
||||
/// markdown renderer for what is currently five static pages.
|
||||
/// One accordion section: title plus body as plain JSX, avoiding a markdown renderer for these static pages.
|
||||
interface DocSection {
|
||||
id: string
|
||||
title: string
|
||||
@@ -21,16 +19,15 @@ const SECTIONS: DocSection[] = [
|
||||
body: (
|
||||
<div className="flex flex-col gap-2">
|
||||
<p>
|
||||
ottyl turns a CLI program's stdout into a Grafana-like dashboard: pipe a program's output into it, and it
|
||||
indexes each line in memory as it arrives, then serves a webapp dashboard over the same process.
|
||||
ottyl turns a CLI program's stdout into a Grafana-like dashboard. Pipe a program's output into it, and it
|
||||
indexes each line in memory as it arrives while serving a dashboard from the same process.
|
||||
</p>
|
||||
<p>
|
||||
<Code>{'cli_command --verbose | ottyl'}</Code>
|
||||
</p>
|
||||
<p>
|
||||
Everything — ingested log lines, declared params, declared metrics, and derived buckets — lives in one
|
||||
in-memory index for the life of the process. Nothing is written to disk unless you explicitly save a{' '}
|
||||
<em>profile</em> or change a <em>setting</em>.
|
||||
Everything — log lines, params, metrics, and derived buckets — lives in one in-memory index for the
|
||||
process's lifetime. Nothing touches disk unless you save a <em>profile</em> or change a <em>setting</em>.
|
||||
</p>
|
||||
<p className="font-semibold text-text">CLI arguments</p>
|
||||
<ul className="list-disc pl-5">
|
||||
@@ -47,19 +44,17 @@ const SECTIONS: DocSection[] = [
|
||||
</ul>
|
||||
<p className="font-semibold text-text">Settings</p>
|
||||
<p>
|
||||
There is exactly one <em>settings</em> file per machine, holding the colour scheme, any imported/linked CSS,
|
||||
and which profile is active. Settings "owns" profiles in that sense — it only tracks pointers (known
|
||||
profile names, which one is active); the profiles themselves (params, metrics, dashboards) are stored
|
||||
separately, next to the settings file by default.
|
||||
There is one <em>settings</em> file per machine, holding the colour scheme, any imported/linked CSS, and
|
||||
which profile is active. Settings only tracks profile pointers — the profiles themselves (params, metrics,
|
||||
dashboards) are stored separately, next to the settings file by default.
|
||||
</p>
|
||||
<p className="font-semibold text-text">Profiles</p>
|
||||
<p>
|
||||
A profile is a named bundle of parsing/indexing rules for a particular CLI app — which params and metrics
|
||||
to derive from its log output, plus any saved dashboards — so restarting ottyl against the same program
|
||||
doesn't lose your setup. There is always a <Code>Default</Code> profile, and it can't be deleted. Creating
|
||||
a new profile copies the currently active one (or starts empty if none is active), so variations build on
|
||||
what you already have rather than starting from scratch. Switching profiles hot-swaps the running engine's
|
||||
declared params/metrics immediately, with no restart needed.
|
||||
A profile bundles the parsing/indexing rules for one CLI app — its params and metrics, plus any saved
|
||||
dashboards — so restarting ottyl against the same program doesn't lose your setup. A <Code>Default</Code>{' '}
|
||||
profile always exists and can't be deleted. Creating a new profile copies the active one (or starts empty
|
||||
if none is active), so variations build on what you already have. Switching profiles hot-swaps the running
|
||||
engine's declared params/metrics immediately, with no restart needed.
|
||||
</p>
|
||||
</div>
|
||||
),
|
||||
@@ -70,16 +65,16 @@ const SECTIONS: DocSection[] = [
|
||||
body: (
|
||||
<div className="flex flex-col gap-2">
|
||||
<p>
|
||||
The filter bar above the timeline takes a syntax-aware query combining regex search over raw log text with
|
||||
comparisons against declared metrics, joined by <Code>and</Code>/<Code>or</Code>:
|
||||
The filter bar takes a query combining regex search over raw log text with comparisons against declared
|
||||
metrics, joined by <Code>and</Code>/<Code>or</Code>:
|
||||
</p>
|
||||
<p>
|
||||
<Code>{'regex("error") and sequence<5 or timestamp>12:00'}</Code>
|
||||
</p>
|
||||
<p>
|
||||
<Code>{'metric_name[param]<5'}</Code> compares a metric's captured param value against a number, string, or
|
||||
timestamp. <Code>sequence</Code> and <Code>timestamp</Code> are reserved metric names — every ingested log
|
||||
line has both, so they're always available to filter on without declaring anything.
|
||||
<Code>{'metric_name[param]<5'}</Code> compares a metric's captured param against a number, string, or
|
||||
timestamp. <Code>sequence</Code> and <Code>timestamp</Code> are reserved metric names present on every log
|
||||
line, so they're always filterable without declaring anything.
|
||||
</p>
|
||||
<p className="font-semibold text-text">Date/time formats</p>
|
||||
<p>Timestamp comparisons accept several forms:</p>
|
||||
@@ -91,9 +86,9 @@ const SECTIONS: DocSection[] = [
|
||||
<li>full dates</li>
|
||||
</ul>
|
||||
<p>
|
||||
The same filter string is sent to both <Code>GET /api/logs</Code> and <Code>GET /api/timeline</Code> as the{' '}
|
||||
<Code>filter</Code> query param and evaluated on the server; a parse error comes back from the server and
|
||||
surfaces as inline text under the filter bar rather than crashing the page.
|
||||
The same filter string goes to both <Code>GET /api/logs</Code> and <Code>GET /api/timeline</Code> as the{' '}
|
||||
<Code>filter</Code> query param and is evaluated on the server; a parse error surfaces as inline text under
|
||||
the filter bar rather than crashing the page.
|
||||
</p>
|
||||
</div>
|
||||
),
|
||||
@@ -104,21 +99,20 @@ const SECTIONS: DocSection[] = [
|
||||
body: (
|
||||
<div className="flex flex-col gap-2">
|
||||
<p>
|
||||
A parameter declaration tells ottyl how to derive a named value from a raw log line: a regex that must
|
||||
match the message, plus a capture group within it, plus a type (<Code>string</Code>, <Code>number</Code>,
|
||||
or <Code>timestamp</Code>). Once declared, every already-ingested line gets retroactively indexed for that
|
||||
param, and every new line is checked against it as it arrives.
|
||||
A parameter declaration tells ottyl how to derive a named value from a log line: a regex the message must
|
||||
match, a capture group, and a type (<Code>string</Code>, <Code>number</Code>, or <Code>timestamp</Code>).
|
||||
Declaring one retroactively indexes every already-ingested line and checks every new line as it arrives.
|
||||
</p>
|
||||
<p>
|
||||
Params are the building block metrics are made of (a metric declaration is really a message-match regex
|
||||
plus a set of param matchers), and they're also what drives filtering and the log preview's column picker —
|
||||
declaring a <Code>duration</Code> param, for instance, makes <Code>duration</Code> available as a column in
|
||||
the log table and as a comparable value in the filter bar.
|
||||
Params are the building blocks metrics are made of (a metric is really a message-match regex plus a set of
|
||||
param matchers), and they drive filtering and the log preview's column picker — declaring a{' '}
|
||||
<Code>duration</Code> param, for instance, makes it available as both a log-table column and a comparable
|
||||
value in the filter bar.
|
||||
</p>
|
||||
<p>
|
||||
The Params panel declares a bare param directly (<Code>POST /api/params</Code>) — the same request the
|
||||
Metrics panel's declaration form makes per param matcher. Declaring one there immediately updates the log
|
||||
preview's column picker, with no page reload needed.
|
||||
Metrics panel's declaration form makes per param matcher. Declaring one there updates the log preview's
|
||||
column picker immediately, with no page reload needed.
|
||||
</p>
|
||||
</div>
|
||||
),
|
||||
@@ -130,14 +124,14 @@ const SECTIONS: DocSection[] = [
|
||||
<div className="flex flex-col gap-2">
|
||||
<p>
|
||||
The timeline above the log table renders log volume per bucket over the active window. Drag across it to
|
||||
select a fixed time range — it narrows the log table, metrics, and dashboards below to that window.
|
||||
select a fixed range, narrowing the log table, metrics, and dashboards below to that window.
|
||||
</p>
|
||||
<p>
|
||||
The preset buttons (<Code>All</Code>, <Code>5m</Code>, <Code>15m</Code>, <Code>1h</Code>,{' '}
|
||||
<Code>24h</Code>) are Grafana-style shortcuts for the same thing: <Code>All</Code> clears the range
|
||||
entirely, and a relative preset is <em>rolling</em> — its window keeps advancing to stay "last N minutes
|
||||
from now" rather than freezing at the moment you picked it. Dragging on the timeline while a preset is
|
||||
active switches back to a fixed (non-rolling) range, same as dragging normally would.
|
||||
<Code>24h</Code>) are Grafana-style shortcuts: <Code>All</Code> clears the range entirely, and a relative
|
||||
preset is <em>rolling</em> — its window keeps advancing to stay "last N minutes from now" instead of
|
||||
freezing at selection time. Dragging while a preset is active switches to a fixed (non-rolling) range, same
|
||||
as a normal drag.
|
||||
</p>
|
||||
</div>
|
||||
),
|
||||
@@ -149,8 +143,8 @@ const SECTIONS: DocSection[] = [
|
||||
<div className="flex flex-col gap-2">
|
||||
<p>
|
||||
The log table lists ingested lines newest-last, each with its sequence number, timestamp, and raw text. The
|
||||
column picker (top-right) adds a column per declared param, showing that line's derived value for it (or a
|
||||
blank if it didn't match) — a quick way to eyeball params without switching to the Metrics panel.
|
||||
column picker (top-right) adds a column per declared param, showing that line's derived value (blank if it
|
||||
didn't match) — a quick way to eyeball params without switching to the Metrics panel.
|
||||
</p>
|
||||
</div>
|
||||
),
|
||||
@@ -167,20 +161,19 @@ const SECTIONS: DocSection[] = [
|
||||
</p>
|
||||
<p>
|
||||
Each param is either a <strong>label</strong> (the default) or the metric's single <strong>value</strong>{' '}
|
||||
param. Label params identify which bucket an occurrence belongs to; a value param's captured number becomes
|
||||
that occurrence's stored value instead of a label — e.g. a <Code>duration</Code> param marked{' '}
|
||||
<em>value</em> lets you query the distribution or latest reading of durations, not just how often they
|
||||
occurred. A metric with only label params still works exactly as before: every occurrence's value defaults
|
||||
to a fixed <Code>1</Code> (a pure occurrence count).
|
||||
param. Labels identify which bucket an occurrence belongs to; a value param's captured number becomes the
|
||||
occurrence's stored value instead of a label — e.g. a <Code>duration</Code> param marked <em>value</em>{' '}
|
||||
lets you query the distribution or latest reading, not just how often it occurred. A metric with only
|
||||
label params defaults every occurrence's value to a fixed <Code>1</Code> (a pure occurrence count).
|
||||
</p>
|
||||
<p>
|
||||
Declaring a metric backfills it against everything already ingested, then continues indexing new lines as
|
||||
they arrive — there's no need to restart ingestion or re-declare after the fact.
|
||||
Declaring a metric backfills it against everything already ingested, then keeps indexing new lines as they
|
||||
arrive — no need to restart ingestion or re-declare after the fact.
|
||||
</p>
|
||||
<p>
|
||||
The Metrics panel lets you declare a metric, pick one from the declared list, and see its bucket values
|
||||
grouped by label combination over the active time range — occurrence count per group, plus a small trend
|
||||
sparkline. That same data is what dashboard panels query via PromQL (see below), including two forms{' '}
|
||||
sparkline. Dashboard panels query that same data via PromQL (see below), including two forms{' '}
|
||||
<em>derived</em> from a counter's value param rather than stored separately:
|
||||
</p>
|
||||
<ul className="list-disc pl-5">
|
||||
@@ -188,7 +181,7 @@ const SECTIONS: DocSection[] = [
|
||||
<span className="font-semibold text-text">
|
||||
<Code>histogram(metric_name[width])</Code>
|
||||
</span>{' '}
|
||||
— buckets every matching event's value into fixed-width buckets of size <Code>width</Code>, e.g.{' '}
|
||||
— buckets each matching event's value into fixed-width buckets of size <Code>width</Code>, e.g.{' '}
|
||||
<Code>histogram(durations[10])</Code>.
|
||||
</li>
|
||||
<li>
|
||||
@@ -208,9 +201,9 @@ const SECTIONS: DocSection[] = [
|
||||
body: (
|
||||
<div className="flex flex-col gap-2">
|
||||
<p>
|
||||
Dashboards work like Grafana: each one is a named set of panels, and each panel is a PromQL query paired
|
||||
with a chart type — bar, line, or pie. Panels preview live as you edit the query, and a malformed query
|
||||
shows an inline error instead of breaking the page.
|
||||
Dashboards work like Grafana: each is a named set of panels, and each panel is a PromQL query paired with a
|
||||
chart type — bar, line, or pie. Panels preview live as you edit the query, and a malformed query shows an
|
||||
inline error instead of breaking the page.
|
||||
</p>
|
||||
<ul className="list-disc pl-5">
|
||||
<li>
|
||||
@@ -218,8 +211,7 @@ const SECTIONS: DocSection[] = [
|
||||
x-axis.
|
||||
</li>
|
||||
<li>
|
||||
<span className="font-semibold text-text">Bar</span> — the latest value per series (a snapshot of "where
|
||||
things are now").
|
||||
<span className="font-semibold text-text">Bar</span> — the latest value per series (a snapshot of "now").
|
||||
</li>
|
||||
<li>
|
||||
<span className="font-semibold text-text">Pie</span> — each series' share of the summed total across the
|
||||
@@ -227,8 +219,8 @@ const SECTIONS: DocSection[] = [
|
||||
</li>
|
||||
</ul>
|
||||
<p>
|
||||
Dashboards are saved per profile, so they travel with a profile when you switch or copy it, and require an
|
||||
active profile to create or save — pick or create one from the settings menu first.
|
||||
Dashboards are saved per profile, so they travel with it when you switch or copy it — an active profile is
|
||||
required to create or save one; pick or create one from the settings menu first.
|
||||
</p>
|
||||
</div>
|
||||
),
|
||||
@@ -239,12 +231,12 @@ const SECTIONS: DocSection[] = [
|
||||
body: (
|
||||
<div className="flex flex-col gap-2">
|
||||
<p>
|
||||
An alert rule fires a notification when its condition matches a newly-ingested log line — evaluated per
|
||||
line as it arrives, not backfilled against history. There are four condition types:
|
||||
An alert rule fires a notification when its condition matches a newly-ingested log line, evaluated per line
|
||||
as it arrives rather than backfilled against history. There are four condition types:
|
||||
</p>
|
||||
<ul className="list-disc pl-5">
|
||||
<li>
|
||||
<span className="font-semibold text-text">Immediate</span> — fires once per line matching a query.
|
||||
<span className="font-semibold text-text">Immediate</span> — fires once per matching line.
|
||||
</li>
|
||||
<li>
|
||||
<span className="font-semibold text-text">Threshold</span> — fires when a query's match count within a
|
||||
@@ -260,8 +252,8 @@ const SECTIONS: DocSection[] = [
|
||||
</li>
|
||||
</ul>
|
||||
<p>
|
||||
Query-based conditions (Immediate/Threshold/Ratio) reuse the filter bar's query syntax, but only the subset
|
||||
that evaluates against a single line's own fields — <Code>regex(...)</Code>, <Code>sequence</Code>,{' '}
|
||||
Query-based conditions (Immediate/Threshold/Ratio) reuse the filter bar's syntax, but only the subset that
|
||||
evaluates a single line's own fields — <Code>regex(...)</Code>, <Code>sequence</Code>,{' '}
|
||||
<Code>timestamp</Code>, and <Code>and</Code>/<Code>or</Code>. A <Code>metric_name[param]</Code> comparison
|
||||
isn't supported in an alert query.
|
||||
</p>
|
||||
@@ -270,10 +262,9 @@ const SECTIONS: DocSection[] = [
|
||||
<Code>5m</Code>, <Code>1h</Code>.
|
||||
</p>
|
||||
<p>
|
||||
Every rule delivers to an <em>ntfy</em> sink: a topic (required) and an optional server override (defaults
|
||||
to <Code>https://ntfy.sh</Code>). "Send test notification" delivers to whatever topic/server is currently
|
||||
entered in the form, without needing to save the rule first — handy for verifying the sink before
|
||||
committing to it.
|
||||
Every rule delivers to an <em>ntfy</em> sink: a topic (required) and an optional server override (default{' '}
|
||||
<Code>https://ntfy.sh</Code>). "Send test notification" delivers to the form's current topic/server without
|
||||
needing to save the rule first.
|
||||
</p>
|
||||
</div>
|
||||
),
|
||||
@@ -284,9 +275,9 @@ const SECTIONS: DocSection[] = [
|
||||
body: (
|
||||
<div className="flex flex-col gap-2">
|
||||
<p>
|
||||
The Settings panel controls process-wide state: colour scheme, the maximum number of logs kept in memory
|
||||
(blank means unlimited), and imported/linked CSS. These apply immediately and persist to the single
|
||||
settings file on disk — no save button needed.
|
||||
The Settings panel controls process-wide state: colour scheme, the max number of logs kept in memory
|
||||
(blank means unlimited), and imported/linked CSS. Changes apply immediately and persist to the settings
|
||||
file on disk — no save button needed.
|
||||
</p>
|
||||
</div>
|
||||
),
|
||||
@@ -298,15 +289,14 @@ const SECTIONS: DocSection[] = [
|
||||
<div className="flex flex-col gap-2">
|
||||
<p>
|
||||
The Presets panel manages profiles: create, switch, and delete them. Switching activates a profile's
|
||||
params/metrics in the running engine immediately, with no restart needed — see the <em>General idea</em>{' '}
|
||||
section above for what a profile stores.
|
||||
params/metrics in the running engine immediately, no restart needed — see <em>General idea</em> above for
|
||||
what a profile stores.
|
||||
</p>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
]
|
||||
|
||||
/** How long a flashed section stays highlighted before settling back to normal. */
|
||||
const FLASH_DURATION_MS = 1200
|
||||
|
||||
export function DocsPanel({ target }: { target?: DocsTarget | null }) {
|
||||
@@ -314,9 +304,7 @@ export function DocsPanel({ target }: { target?: DocsTarget | null }) {
|
||||
const [flashId, setFlashId] = useState<string | null>(null)
|
||||
const sectionRefs = useRef<Record<string, HTMLDivElement | null>>({})
|
||||
|
||||
// Keyed on `target?.nonce` (not `target?.sectionId`) so clicking the same
|
||||
// "?" icon twice in a row re-triggers the flash rather than being a no-op
|
||||
// because the panel is already open to that section.
|
||||
// Keyed on `target?.nonce`, not `target?.sectionId`, so clicking the same "?" icon twice re-triggers the flash instead of no-op'ing when already open.
|
||||
useEffect(() => {
|
||||
if (!target) return
|
||||
setOpenId(target.sectionId)
|
||||
|
||||
Reference in New Issue
Block a user