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
+12
-34
@@ -1,7 +1,4 @@
|
||||
/// `MetricPrimitive` is a Rust enum with derived `Serialize`, so its JSON
|
||||
/// form is externally tagged, e.g. `{ "String": "info" }`, `{ "Number":
|
||||
/// { "Int": 5 } }`, `{ "Timestamp": 1710000000000 }`, `{ "Duration": 1500 }`
|
||||
/// (milliseconds, ticket 023).
|
||||
// Externally-tagged JSON matching the Rust `MetricPrimitive` enum's derived `Serialize`, e.g. `{ "Duration": 1500 }` (milliseconds).
|
||||
export type MetricPrimitive =
|
||||
| { String: string }
|
||||
| { Number: { Int: number } | { Float: number } }
|
||||
@@ -20,9 +17,7 @@ export interface LogEntry {
|
||||
sequence: number
|
||||
timestamp_ms: number
|
||||
text: string
|
||||
/// Derived param values (declared via `POST /api/params`) that matched
|
||||
/// this line's text, keyed by param name. A param with no match on this
|
||||
/// line is simply absent from the map.
|
||||
// A param with no match on this line is simply absent from the map.
|
||||
params: Record<string, MetricPrimitive>
|
||||
}
|
||||
|
||||
@@ -46,7 +41,6 @@ export interface Stats {
|
||||
last_timestamp_ms: number | null
|
||||
}
|
||||
|
||||
/** Whether a param contributes to a bucket's labels or is its captured value (ticket 022). */
|
||||
export type ParamRole = 'label' | 'value'
|
||||
|
||||
export interface MetricParamMatcher {
|
||||
@@ -61,7 +55,7 @@ export interface MetricDeclaration {
|
||||
name: string
|
||||
message_match_regex: string
|
||||
params: MetricParamMatcher[]
|
||||
/** Fixed labels applied on a match/non-match, on top of any captured params (ticket 025). */
|
||||
/** Fixed labels applied on a match/non-match, on top of any captured params. */
|
||||
matched_labels?: Record<string, string> | null
|
||||
unmatched_labels?: Record<string, string> | null
|
||||
}
|
||||
@@ -73,8 +67,7 @@ export interface BucketValue {
|
||||
value: MetricPrimitive
|
||||
}
|
||||
|
||||
/// Error shape returned by API handlers on a 4xx/5xx response:
|
||||
/// `{ "error": "..." }` (see `ErrorDto` on the backend).
|
||||
// Error shape returned by API handlers on a 4xx/5xx response (see `ErrorDto` on the backend).
|
||||
export interface ApiErrorBody {
|
||||
error: string
|
||||
}
|
||||
@@ -103,7 +96,7 @@ export interface Settings {
|
||||
active_profile: string | null
|
||||
/** Maximum ingested logs kept in memory; `null` means unlimited. */
|
||||
max_logs: number | null
|
||||
/** Id of the selected custom theme entry (ticket 033), or `null` if `color_scheme` alone applies. */
|
||||
/** Id of the selected custom theme entry, or `null` if `color_scheme` alone applies. */
|
||||
custom_theme_id: string | null
|
||||
}
|
||||
|
||||
@@ -114,7 +107,7 @@ export interface CssEntry {
|
||||
kind: CssEntryKind
|
||||
filename: string | null
|
||||
path_or_url: string | null
|
||||
/** Whether this entry is a selectable colour-scheme theme (ticket 033) rather than arbitrary CSS. */
|
||||
/** Whether this entry is a selectable colour-scheme theme rather than arbitrary CSS. */
|
||||
is_theme: boolean
|
||||
}
|
||||
|
||||
@@ -125,9 +118,7 @@ export interface ProfileSummary {
|
||||
dashboard_count: number
|
||||
}
|
||||
|
||||
/// One chart-ready time series returned by `GET /api/query` (ticket 013):
|
||||
/// a label combination plus its `(timestamp_ms, value)` points, sorted by
|
||||
/// timestamp. See `PromqlSeriesDto` on the backend.
|
||||
// One chart-ready time series from `GET /api/query`: a label combination plus its `(timestamp_ms, value)` points, sorted by timestamp.
|
||||
export interface PromqlSeries {
|
||||
labels: Record<string, string>
|
||||
points: [number, number][]
|
||||
@@ -148,20 +139,16 @@ export interface Dashboard {
|
||||
panels: Panel[]
|
||||
}
|
||||
|
||||
/// Comparison operator shared by `AlertCondition::Threshold`/`Ratio` and
|
||||
/// filter-query metric comparisons (see `CompareOp` on the backend).
|
||||
export type CompareOp = 'lt' | 'gt' | 'le' | 'ge' | 'eq' | 'ne'
|
||||
|
||||
/// What triggers an alert rule (ticket 017/018). Externally tagged on
|
||||
/// `kind`, matching `AlertCondition`'s `#[serde(tag = "kind")]`.
|
||||
// Externally tagged on `kind`, matching `AlertCondition`'s `#[serde(tag = "kind")]` on the backend.
|
||||
export type AlertCondition =
|
||||
| { kind: 'immediate'; query: string }
|
||||
| { kind: 'threshold'; query: string; op: CompareOp; threshold: number; window_ms: number }
|
||||
| { kind: 'newvalue'; field_regex: string; capture_group: string }
|
||||
| { kind: 'ratio'; query_a: string; query_b: string; op: CompareOp; ratio: number; window_ms: number }
|
||||
|
||||
/// Where a fired alert is delivered (ticket 019). Only one sink kind exists
|
||||
/// today, but this stays a tagged union to match the backend.
|
||||
// Only one sink kind exists today, but this stays a tagged union to match the backend.
|
||||
export type AlertSink = { kind: 'ntfy'; topic: string; server: string | null }
|
||||
|
||||
export interface AlertRule {
|
||||
@@ -189,11 +176,7 @@ async function getJson<T>(path: string): Promise<T> {
|
||||
return res.json() as Promise<T>
|
||||
}
|
||||
|
||||
/// POSTs a JSON body to `path`. On a non-ok response, attempts to parse the
|
||||
/// body as `{ "error": "..." }` (the shape every declaration endpoint uses
|
||||
/// for validation failures) and throws an `ApiError` with that message so
|
||||
/// callers can surface it inline; falls back to a generic message if the
|
||||
/// body isn't in that shape.
|
||||
// Throws `ApiError` (not `Error`) with the parsed `{ "error": "..." }` message so callers can surface it inline.
|
||||
async function postJson<T>(path: string, body: unknown): Promise<T> {
|
||||
const res = await fetch(path, {
|
||||
method: 'POST',
|
||||
@@ -357,10 +340,7 @@ export function activateProfile(name: string): Promise<Settings> {
|
||||
return sendJson(`/api/settings/profiles/${encodeURIComponent(name)}/activate`, 'POST')
|
||||
}
|
||||
|
||||
/// Evaluates a PromQL query (ticket 013) via `GET /api/query`. Throws on a
|
||||
/// malformed query (`400` with `{ "error": "..." }`), so callers should catch
|
||||
/// and surface the message inline (e.g. per-panel) rather than letting it
|
||||
/// crash the page.
|
||||
// Throws on a malformed query (400); callers should catch and surface the message inline rather than letting it crash the page.
|
||||
export function fetchPromqlSeries(params: {
|
||||
promql: string
|
||||
startMs?: number
|
||||
@@ -419,9 +399,7 @@ export function deleteAlertRule(id: string): Promise<void> {
|
||||
return sendJson(`/api/settings/alerts/${encodeURIComponent(id)}`, 'DELETE')
|
||||
}
|
||||
|
||||
/// Delivers a test notification to `topic`/`server` (ticket 020) without
|
||||
/// requiring the rule to be saved first. Throws `ApiError` on failure (e.g.
|
||||
/// ntfy unreachable) so callers can surface it inline.
|
||||
// Delivers a test notification without requiring the rule to be saved first; throws `ApiError` on failure so callers can surface it inline.
|
||||
export function sendTestAlertNotification(topic: string, server: string | null): Promise<void> {
|
||||
return sendJson('/api/settings/alerts/test', 'POST', { topic, server })
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { createContext, useCallback, useContext, useMemo, useRef, useState, type ReactNode } from 'react'
|
||||
|
||||
/** One request to open the Docs panel scrolled to a specific section (ticket 021). */
|
||||
// One request to open the Docs panel scrolled to a specific section.
|
||||
export interface DocsTarget {
|
||||
sectionId: string
|
||||
/** Bumped on every request, even to the same section, so a repeat click re-triggers the flash. */
|
||||
|
||||
@@ -1,20 +1,4 @@
|
||||
/**
|
||||
* Downloadable/re-uploadable custom themes (ticket 033).
|
||||
*
|
||||
* A theme file is a small CSS file containing exactly one `:root { --c-*:
|
||||
* ...; }` block defining every variable in `THEME_CSS_VARS` (the full set
|
||||
* `index.css`'s `:root`/`html.<scheme>` blocks use to skin the app). Using
|
||||
* `:root` (rather than a scoped selector) keeps the file trivially portable:
|
||||
* the same content works whether it's applied at `:root` or scoped, and
|
||||
* round-tripping a downloaded file back through upload reproduces it
|
||||
* byte-for-byte-equivalent (same variable values).
|
||||
*
|
||||
* Applying a selected custom theme is left to `useSettingsPanel`, which
|
||||
* injects the entry's CSS as an unscoped stylesheet *only* while no built-in
|
||||
* `html.<scheme>` class is present — since both forms target `:root` with
|
||||
* equal specificity, the later stylesheet in the cascade wins, so no
|
||||
* rewriting of the file's own `:root` selector is needed.
|
||||
*/
|
||||
// A theme file is a CSS file with one `:root { --c-*: ...; }` block defining every variable in `THEME_CSS_VARS`; `useSettingsPanel` applies the selected one as an unscoped stylesheet only while no built-in `html.<scheme>` class is present, so it wins the cascade without rewriting the selector.
|
||||
|
||||
/** Canonical list of every `--c-*` variable every theme in `index.css` defines. */
|
||||
export const THEME_CSS_VARS = [
|
||||
@@ -42,16 +26,7 @@ export const THEME_CSS_VARS = [
|
||||
|
||||
export type ThemeVars = Record<(typeof THEME_CSS_VARS)[number], string>
|
||||
|
||||
/**
|
||||
* Built-in themes' fully-resolved variable values, matching `index.css`'s
|
||||
* current cascade (a theme that doesn't override `--c-danger-*` there falls
|
||||
* back to `:root`'s dark values there, so those are inlined here too — a
|
||||
* downloaded built-in file is always complete, never relies on a `:root`
|
||||
* fallback the download mechanism doesn't have access to).
|
||||
*
|
||||
* Keep in sync with `webapp/src/index.css` by hand; there's no built step
|
||||
* that derives one from the other.
|
||||
*/
|
||||
// Fully-resolved variable values for each built-in theme, including inherited fallbacks, so a downloaded file is always complete; keep in sync with `index.css` by hand, there's no build step linking the two.
|
||||
export const BUILTIN_THEMES: Record<string, ThemeVars> = {
|
||||
dark: {
|
||||
'--c-surface-0': '#0a0a0a',
|
||||
@@ -253,7 +228,6 @@ export const BUILTIN_THEMES: Record<string, ThemeVars> = {
|
||||
},
|
||||
}
|
||||
|
||||
/** Generates a downloadable theme CSS file's content from a full variable map. */
|
||||
export function generateThemeCss(vars: ThemeVars): string {
|
||||
const lines = THEME_CSS_VARS.map((name) => ` ${name}: ${vars[name]};`)
|
||||
return `:root {\n${lines.join('\n')}\n}\n`
|
||||
@@ -264,14 +238,7 @@ export interface ParsedTheme {
|
||||
missing: string[]
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a theme CSS file's content, pulling `--c-*: value;` declarations
|
||||
* out of its first `:root { ... }` block (or, failing that, out of the
|
||||
* whole file — tolerant of a hand-edited file that dropped the selector).
|
||||
* Returns every recognized variable found plus the list of canonical
|
||||
* variables (see `THEME_CSS_VARS`) that were missing, so callers can reject
|
||||
* an incomplete upload with a clear error.
|
||||
*/
|
||||
// Parses `--c-*: value;` declarations from the first `:root { ... }` block (or the whole file, tolerant of a dropped selector); reports missing canonical variables so callers can reject an incomplete upload.
|
||||
export function parseThemeCss(content: string): ParsedTheme {
|
||||
const rootMatch = content.match(/:root\s*\{([^}]*)\}/)
|
||||
const body = rootMatch ? rootMatch[1] : content
|
||||
@@ -286,7 +253,6 @@ export function parseThemeCss(content: string): ParsedTheme {
|
||||
return { vars, missing }
|
||||
}
|
||||
|
||||
/** Triggers a browser download of `content` as `filename` (a `.css` file). */
|
||||
export function downloadThemeFile(filename: string, content: string) {
|
||||
const blob = new Blob([content], { type: 'text/css' })
|
||||
const url = URL.createObjectURL(blob)
|
||||
|
||||
@@ -17,29 +17,16 @@ import {
|
||||
type Settings,
|
||||
} from './api'
|
||||
|
||||
/** `data-*` marker used to find/remove previously-injected CSS tags on re-apply. */
|
||||
// `data-*` marker used to find/remove previously-injected CSS tags on re-apply.
|
||||
const INJECTED_CSS_ATTR = 'data-ottyl-css-id'
|
||||
|
||||
/** `data-*` marker for the single injected `<link>` for the active custom theme, if any (ticket 033). */
|
||||
// `data-*` marker for the single injected `<link>` for the active custom theme, if any.
|
||||
const INJECTED_THEME_ATTR = 'data-ottyl-theme-css-id'
|
||||
|
||||
/** Themes selectable independently of `system`, each backed by a `html.<name>` block in `index.css`. */
|
||||
// Themes selectable independently of `system`, each backed by a `html.<name>` block in `index.css`.
|
||||
const THEME_CLASSES = ['light', 'dark', 'grey', 'red', 'green', 'blue', 'yellow', 'pink', 'orange'] as const
|
||||
|
||||
/**
|
||||
* Resolves `colorScheme` to an effective theme (following the OS preference
|
||||
* for `system`, which only ever resolves to `light`/`dark`) and reflects it
|
||||
* onto `<html>` as both a class (see `THEME_CLASSES`) and a
|
||||
* `data-color-scheme` attribute. Every visual role the app uses is a CSS
|
||||
* custom property keyed off that class (see `index.css`), so switching it
|
||||
* re-skins the whole app, not just the base page background.
|
||||
*
|
||||
* `customThemeActive` (ticket 033): when a custom theme is selected, no
|
||||
* built-in `html.<scheme>` class is added at all — its own injected `:root`
|
||||
* override (see `applyCustomThemeCss`) is the only thing setting the
|
||||
* `--c-*` variables, and adding a built-in class alongside it would win the
|
||||
* cascade (a class selector is more specific than `:root`) and mask it.
|
||||
*/
|
||||
// Reflects `colorScheme` onto `<html>` as a class + `data-color-scheme`; when a custom theme is active, no built-in class is added at all, since a class selector would out-specificity its injected `:root` override (see `applyCustomThemeCss`) and mask it.
|
||||
function applyColorScheme(colorScheme: ColorScheme, customThemeActive: boolean) {
|
||||
const root = document.documentElement
|
||||
root.classList.remove(...THEME_CLASSES)
|
||||
@@ -53,17 +40,7 @@ function applyColorScheme(colorScheme: ColorScheme, customThemeActive: boolean)
|
||||
root.dataset.colorScheme = colorScheme
|
||||
}
|
||||
|
||||
/**
|
||||
* Injects a `<link>` tag per CSS entry into `<head>` (imported entries point
|
||||
* at `GET /api/settings/css/:id/content`, linked entries point straight at
|
||||
* their `path_or_url`), after removing any tags this hook previously
|
||||
* injected — so the rendered app reflects the current entry list immediately
|
||||
* on every change, without a page reload.
|
||||
*
|
||||
* Only arbitrary (non-theme) entries are applied unconditionally this way;
|
||||
* theme entries (`is_theme: true`) are applied separately and only while
|
||||
* selected, see `applyCustomThemeCss`.
|
||||
*/
|
||||
// Injects a `<link>` per CSS entry, replacing any this hook previously injected. Only non-theme entries are applied unconditionally; theme entries are applied separately, only while selected — see `applyCustomThemeCss`.
|
||||
function applyCssEntries(entries: CssEntry[]) {
|
||||
document.querySelectorAll(`link[${INJECTED_CSS_ATTR}]`).forEach((el) => el.remove())
|
||||
for (const entry of entries) {
|
||||
@@ -77,13 +54,7 @@ function applyCssEntries(entries: CssEntry[]) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Injects the single active custom theme's CSS (ticket 033) as a
|
||||
* `<link>` — same mechanism as `applyCssEntries`, kept separate since this
|
||||
* one is conditional on `settings.custom_theme_id`, not always-on. `entry`
|
||||
* is `null` when no custom theme is selected, which just removes any
|
||||
* previously-injected tag.
|
||||
*/
|
||||
// Same injection mechanism as `applyCssEntries`, kept separate since this one is conditional on `settings.custom_theme_id`; `entry` null just removes any previously-injected tag.
|
||||
function applyCustomThemeCss(entry: CssEntry | null) {
|
||||
document.querySelectorAll(`link[${INJECTED_THEME_ATTR}]`).forEach((el) => el.remove())
|
||||
if (!entry) return
|
||||
@@ -96,12 +67,7 @@ function applyCustomThemeCss(entry: CssEntry | null) {
|
||||
document.head.appendChild(link)
|
||||
}
|
||||
|
||||
/**
|
||||
* Owns fetching + mutating settings/CSS/profile state for the NavMenu panels
|
||||
* (ticket 011), and applies the colour scheme / imported CSS to the document
|
||||
* as a side effect whenever that state changes — so toggling either is
|
||||
* visible in the running app immediately, with no reload.
|
||||
*/
|
||||
// Owns fetching + mutating settings/CSS/profile state for the NavMenu panels, and applies colour scheme / imported CSS to the document as a side effect so changes are visible immediately, with no reload.
|
||||
export function useSettingsPanel() {
|
||||
const [settings, setSettings] = useState<Settings | null>(null)
|
||||
const [cssEntries, setCssEntries] = useState<CssEntry[]>([])
|
||||
@@ -162,8 +128,7 @@ export function useSettingsPanel() {
|
||||
const setColorScheme = useCallback(
|
||||
(colorScheme: ColorScheme) =>
|
||||
guarded(async () => {
|
||||
// Selecting a built-in scheme always deselects any custom theme —
|
||||
// the two are mutually exclusive (see `applyColorScheme`).
|
||||
// Selecting a built-in scheme always deselects any custom theme — the two are mutually exclusive (see `applyColorScheme`).
|
||||
const next = await updateSettings({ colorScheme, customThemeId: null })
|
||||
setSettings(next)
|
||||
})(),
|
||||
@@ -220,8 +185,7 @@ export function useSettingsPanel() {
|
||||
guarded(async () => {
|
||||
await deleteCssEntry(id)
|
||||
await refreshCss()
|
||||
// The removed entry may have been the active custom theme — fall
|
||||
// back to `color_scheme` rather than leaving a dangling selection.
|
||||
// The removed entry may have been the active custom theme — fall back to `color_scheme` rather than leaving a dangling selection.
|
||||
if (settings?.custom_theme_id === id) {
|
||||
const next = await updateSettings({ customThemeId: null })
|
||||
setSettings(next)
|
||||
|
||||
Reference in New Issue
Block a user