56 Commits
Author SHA1 Message Date
bvandeusenandClaude Opus 5.5 e595bd922a feat(monitors): problems first, a page per monitor, adding on its own page (#5121)
CI / lint (push) Successful in 3s
CI / unit (push) Failing after 1m23s
CI / integration (push) Successful in 1m12s
CI / publish (push) Skipped
The Monitors list now opens with a count strip (down, up with how many
degraded, pending, paused) and sorts down, degraded, pending, up, paused,
by name within each. Degraded is up but worth a look: uptime at or below
its warning floor, or the latest response past its critical cutoff, both
from Settings → Thresholds; a slow-but-tolerable reply is coloured, not
sorted.

A monitor had no page of its own: every link went to the list. Now
/monitors/<id> shows its state and since when, latency, uptime for 24h/7d/
30d, TLS days, the last checks, recent failures with their errors, and its
settings (HTTP header names, never their values). Status links to it, and
adding or editing returns to it.

Adding is its own page (/monitors/new, form.html shared with edit) behind
an "Add monitor" button; a refused add re-renders the form with the error
and what was typed instead of a bare 400 text. Row actions are labelled
icon buttons, type names read Ping/TCP/DNS/HTTP (|monitor_type), and the
heartbeat uses pill-up/pill-down, so HEX_CEILING drops to 56.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 17:54:30 -04:00
bvandeusenandClaude Opus 5.5 fda54a3c31 feat(status): what is wrong first — down monitors and active alerts, and since when (#5120)
CI / lint (push) Successful in 3s
CI / integration (push) Successful in 56s
CI / unit (push) Successful in 1m11s
CI / publish (push) Successful in 1m15s
#5120 "Status page: what is wrong first".

- A "Needs attention" card heads the Status board: each down monitor with
  how long it has been down, and each active alert (firing, acknowledged,
  pending, no data) with its condition and how long, linking to its history.
- StatusEntry.since / since_at_least: when the current up/down run began,
  from the recent checks (streak_start); "for at least" when every check we
  have agrees.
- Counts as stat tiles (Down, Active alerts, Pending, Up); names wrap
  instead of being cut off; dots carry their label.
- Uptime and TLS are coloured by status_class against Settings → Thresholds
  instead of literal cutoffs; heartbeat pills and DOWN/UP use tokens and
  status_badge rather than hex and bold.
- tests/core/test_status_page.py: streak_start cases, and the rows render
  with the problem card before the list, each since-time, threshold tones
  and no hex colour.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 14:40:45 -04:00
bvandeusenandClaude Opus 5.5 e864cd4b6c feat(ui): share view on steward.css; breadcrumbs everywhere they belong; focus ring; tables scroll in their card (#5095)
#5095 "Share view on the shared stylesheet; breadcrumbs, focus-visible and
wide tables".

- dashboard/share_view.html loads theme.css and steward.css; its own copy of
  the old purple palette is gone. Only the read-only frame and the x/y/w/h
  grid stay inline, written in tokens. HEX_CEILING 75 -> 58.
- Breadcrumbs on settings/general, settings/thresholds, the dashboard share
  page and share-link-created. tests/test_breadcrumbs.py fails on any page
  extending base.html without a crumbs() trail, unless it is on the named
  list of section top pages, logged-out and error pages (rule 134).
- :focus-visible draws --fs-focus-ring on links, buttons, summaries, inputs
  and anything with tabindex.
- Any card holding a .table scrolls sideways (card:has(table.table)), so a
  wide table no longer widens the page at phone width; .card-flush scrolls
  too, main gets min-width:0, and .table-wrap covers a table outside a card.
- tests/test_share_view.py renders the share view: shared sheets, no hex
  colour, escaped name, widgets loading through the share token.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 14:37:39 -04:00
bvandeusenandClaude Opus 5.5 477b05b9d0 feat(ui): status_badge, status_dot, stat_tile, empty_state, page_header, icon_button and form classes (#5094)
#5094 "Status and layout macros, and form classes".

- formatting.STATES: one state -> (tone, label) map for up, down, stale,
  firing, pending, acknowledged, no_data, ok, warn, crit, resolved and
  inactive; status_info() reads it (an unmapped state is drawn dim under its
  own name). required() lets a macro refuse a missing argument.
- _macros.html: status_badge and status_dot (a dot always carries its label,
  sr-only unless shown), stat_tile, empty_state, page_header (actions via a
  call block) and icon_button, which raises without a label.
- steward.css: tone classes (badge-/dot-/stat- ok, warn, crit, info, dim),
  .sr-only, .page-header, .stat-tile, .empty-state, .btn-icon and the form
  classes .field, .field-label, .field-hint and .input. Info text is a 40%
  mix (--blue): the house --fs-info-fg falls to 4.18:1 on its tint over a
  hovered row, 40% holds 5.09:1.
- First users: the Alerts list, history and maintenance pages (headers,
  state badges, empty states) and the alert forms' hints; the inline inp/lbl
  style sets on the monitor and host forms become .input / .field-label.
- tests/core/test_macros.py renders each macro through the app environment.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 14:34:21 -04:00
bvandeusenandClaude Opus 5.5 65411d3ce8 feat(ui): base stylesheet on the Steward design-system tokens; readable status colours (#5092)
CI / unit (push) Successful in 1m20s
CI / lint (push) Successful in 5s
CI / integration (push) Successful in 1m12s
CI / publish (push) Successful in 1m43s
#5092 "Tokens". base.html's inline palette was its own: white text on the
gold button (1.81:1), a red at 3.26:1 on a card, a warning yellow at hue
42 beside the gold accent at 46, a lavender page title, serif labels at
13px and weights up to 700.

- static/theme.css is design system 6's generated sheet (FabledSword + the
  Steward accent). static/steward.css is base.html's stylesheet moved out,
  with :root now aliases onto those tokens (--bg, --text-muted, --green…)
  so every template keeps rendering; it states no colour of its own.
- Status text colours are Steward overrides recorded in the design system
  (tokens 182-184): success #5FB87A, warning #E08A3C, error #EC7A6C. The
  house -fg mixes read "up" as grey hint text (dE 8.2) and warning as the
  accent (dE 11.5); these are >= 4.5:1 on page, card, hover and their own
  15-25% tints. --orange is now the midpoint of warning and error.
- Primary button and active range button: dark text on gold (7.82:1).
  Danger button on the house destructive fill. Badges and alerts take the
  -fg colour on a tint; the gold badge uses --fs-accent-fg.
- Type: Inter body, Fraunces only for page titles and h1/h2, JetBrains
  Mono for code; section and widget titles become the tiny caps label in
  the body face; weights capped at 500.
- Removed: the lavender title and share-view gradient, #606080, and the
  undefined --card-bg, --accent-faint, --accent-bg, --bg-alt, --border-low
  and --row-alt (now real tokens); the dead settings/index.html.
- Guards (tests/test_style_tokens.py): every var(--x) used is defined;
  steward.css states no colour or weight over 500; hex colours in
  templates may only go down (ceiling 75, from 81 before this change),
  each with a self-test that it can fail.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 14:18:49 -04:00
bvandeusenandClaude Opus 5.5 bd28d3b0e7 feat(ui): serve htmx, Chart.js, Gridstack and the house fonts from Steward itself (#5091)
CI / lint (push) Successful in 3s
CI / integration (push) Successful in 48s
CI / unit (push) Successful in 1m6s
CI / publish (push) Successful in 1m11s
#5091 "Local assets". base.html, the dashboard and the share view loaded
htmx and Chart.js from unpkg/jsdelivr, Gridstack from jsdelivr and their
fonts from Google. On an instance with no outbound network (rule 164) every
page lost htmx, and so every button and poll; and each page load told those
hosts about the visitor (rule 155).

- steward/static/vendor: htmx 2.0.4, Chart.js 4.4.4, Gridstack 12.6.0 (the
  versions the pages already used), with their licences (0BSD, MIT, MIT).
- steward/static/fonts: Inter, Fraunces and JetBrains Mono, the design
  system's faces (SIL OFL, licences beside them), every unicode-range
  subset, with fonts.css. The serif face is now Fraunces in place of
  EB Garamond (base) and Libertinus Serif (share view); applying Inter and
  the type scale is the tokens step.
- Templates use url_for('static'); /static already passes every gate.
- Guard (tests/test_no_remote_assets.py): no template or stylesheet loads a
  script, stylesheet, font or image from another origin (comments stripped,
  example URLs in help text allowed), with a self-test that it can fail;
  every url_for('static') file and every font the sheet names exists.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 14:12:31 -04:00