diff --git a/CHANGES.md b/CHANGES.md index 1bf36e1..fc6753b 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,6 +2,18 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](https://semver.org/). +## 1.5.0 + +### New Features + +- Logs viewer: in-page search with match highlighting and a match count, one-click export of the full log to a file, and a responsive full-screen console (both service and manager logs). +- Dashboard polish: wider Services panel with a slim, self-explanatory Notifications rail; `LOGS`/`DOCS` rendered as clear buttons with keyboard focus rings; content biased to the top with a small capped gap; and denser service rows so more services fit without scrolling. + +### Bugfixes + +- Logs export and search now return the actual log instead of a validation error (the view no longer requests the invalid `tail=all`). +- Stabilized the flaky `test_read_timerange` SDK test (a concurrent database clean could trim rows mid-test). + ## 1.4.0 ### New Features diff --git a/docs/superpowers/plans/2026-06-29-dashboard-main-page-polish.md b/docs/superpowers/plans/2026-06-29-dashboard-main-page-polish.md new file mode 100644 index 0000000..bf91e15 --- /dev/null +++ b/docs/superpowers/plans/2026-06-29-dashboard-main-page-polish.md @@ -0,0 +1,307 @@ +# Dashboard Main-Page Polish Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Polish the manager dashboard so Services is the clear hero, Notifications is a slim self-explanatory rail, LOGS/DOCS read as buttons, and short content no longer leaves a top-heavy void. + +**Architecture:** Frontend-only. Two files: `manager/routers/dashboard_api.py` (Dash layout markup — one added subtitle element) and `manager/assets/dashboard_styles.css` (all styling). Guard tests are string-assert checks in `tests/manager_test/test_ui_redesign.py`, matching the existing pattern (read the CSS file / `str(app.layout)` and assert substrings). + +**Tech Stack:** Dash (Plotly) layout in Python, plain CSS with design tokens from `tokens.css`, pytest guard tests via `test_client` fixtures in `tests/conftest.py`. + +## Global Constraints + +- No backend, API, endpoint, or data-model changes. CSS + one markup element only. +- No external resources: CSS must contain no `http://` or `https://` (enforced by `test_dashboard_css_uses_tokens`). +- Reuse existing design tokens from `tokens.css` (`var(--muted)`, `var(--line)`, `var(--accent)`, etc.) — introduce no new colors. +- The logs view page (`logs.html`), login, and `max-width:1600px` on `.page` stay as-is. Do not reintroduce `max-width:1180px` (guarded by `test_dashboard_page_width_widened`). +- Run before pushing: `/home/kaveh/miniconda3/bin/python -m black --check`, `... -m flake8`, `... -m pylint manager`, and `./venv/bin/python -m pytest tests/manager_test/test_ui_redesign.py`. +- Mobile rule `@media (max-width:760px){ .grid{grid-template-columns:1fr} ... }` must keep collapsing the grid to one column. + +--- + +### Task 1: Widen Services / slim sidebar + vertical fill + +**Files:** +- Modify: `manager/assets/dashboard_styles.css` (the `.page` rule at line 32 and the `.grid` rule at line 33) +- Test: `tests/manager_test/test_ui_redesign.py` + +**Interfaces:** +- Consumes: nothing from other tasks. +- Produces: the `.grid` / `.page` CSS shape that Task 2 and Task 3 visually sit inside; no code symbols. + +Current CSS (lines 31-33): + +```css +/* ---------- page / grid ---------- */ +.page{max-width:1600px;margin:0 auto;padding:1.8rem 1.6rem 4rem} +.grid{display:grid;grid-template-columns:1.85fr 1fr;gap:1.4rem;align-items:start} +``` + +- [ ] **Step 1: Write the failing test** + +Add to `tests/manager_test/test_ui_redesign.py`: + +```python +def test_dashboard_grid_widens_services_and_fills_viewport(): + css = (ASSETS / "dashboard_styles.css").read_text().replace(" ", "") + # Services hero is wider than the slim Notifications rail + assert "grid-template-columns:2.6fr0.9fr" in css + assert "grid-template-columns:1.85fr1fr" not in css + # Page fills the viewport height (minus the banner) and centers when short + assert "min-height:calc(100vh-4rem)" in css + assert "margin-block:auto" in css + assert "box-sizing:border-box" in css +``` + +- [ ] **Step 2: Run test to verify it fails** + +Run: `./venv/bin/python -m pytest "tests/manager_test/test_ui_redesign.py::test_dashboard_grid_widens_services_and_fills_viewport" -v` +Expected: FAIL (`assert "grid-template-columns:2.6fr0.9fr" in css` is False — file still has `1.85fr 1fr`). + +- [ ] **Step 3: Write minimal implementation** + +Replace lines 31-33 of `manager/assets/dashboard_styles.css` with: + +```css +/* ---------- page / grid ---------- */ +.page{ + max-width:1600px;margin:0 auto;padding:1.8rem 1.6rem 4rem; + box-sizing:border-box;min-height:calc(100vh - 4rem); + display:flex;flex-direction:column; +} +.grid{ + display:grid;grid-template-columns:2.6fr 0.9fr;gap:1.4rem;align-items:start; + margin-block:auto;width:100%; +} +``` + +Notes for the implementer: +- `vh` is viewport-relative, so `min-height:calc(100vh - 4rem)` makes `.page` nearly fill the screen without touching `body` or Dash's wrapper divs. The `4rem` budgets for the top banner. +- `box-sizing:border-box` is required because there is no global reset and `.page` has padding; without it the padding would add to the `100vh` height and force a scrollbar. +- `margin-block:auto` on `.grid` centers it vertically inside the tall `.page` when there is spare room; when the grid is taller than the page the auto margins collapse to 0 and the page grows/scrolls normally. +- `width:100%` keeps the grid full-width inside the flex column (a flex item would otherwise shrink to content width). + +- [ ] **Step 4: Run test to verify it passes** + +Run: `./venv/bin/python -m pytest "tests/manager_test/test_ui_redesign.py::test_dashboard_grid_widens_services_and_fills_viewport" -v` +Expected: PASS + +- [ ] **Step 5: Commit** + +```bash +git add manager/assets/dashboard_styles.css tests/manager_test/test_ui_redesign.py +git commit -m "Dashboard: widen Services, slim sidebar, fill viewport height" +``` + +--- + +### Task 2: Explain the Notifications panel + +**Files:** +- Modify: `manager/routers/dashboard_api.py` (the Notifications panel head in `app.layout`, around lines 334-340) +- Modify: `manager/assets/dashboard_styles.css` (add `.panel-sub` near the `.panel-head` rules, ~line 44) +- Test: `tests/manager_test/test_ui_redesign.py` + +**Interfaces:** +- Consumes: nothing from other tasks. +- Produces: a `
` element with the literal subtitle text below; relied on only by its own test.
+
+Current markup (lines 334-345 of `dashboard_api.py`):
+
+```python
+ # Notifications panel
+ html.Section(
+ className="panel",
+ children=[
+ html.Div(
+ className="panel-head",
+ children=[html.H2("Notifications")],
+ ),
+ html.Div(
+ id="notifications-content",
+ children=[generate_table_notifications()],
+ ),
+ ],
+ ),
+```
+
+- [ ] **Step 1: Write the failing test**
+
+Add to `tests/manager_test/test_ui_redesign.py`:
+
+```python
+def test_notifications_panel_has_explanation():
+ from manager.routers import dashboard_api
+
+ layout = str(dashboard_api.app.layout)
+ assert "Alerts services raise" in layout # one-line description of the panel
+ css = (ASSETS / "dashboard_styles.css").read_text()
+ assert ".panel-sub" in css
+```
+
+- [ ] **Step 2: Run test to verify it fails**
+
+Run: `./venv/bin/python -m pytest "tests/manager_test/test_ui_redesign.py::test_notifications_panel_has_explanation" -v`
+Expected: FAIL (`assert "Alerts services raise" in layout` is False — no subtitle yet).
+
+- [ ] **Step 3: Write minimal implementation**
+
+In `manager/routers/dashboard_api.py`, change the Notifications `panel-head` children to include a subtitle (replace the single-child `panel-head` Div shown above):
+
+```python
+ html.Div(
+ className="panel-head",
+ children=[
+ html.Div(
+ [
+ html.H2("Notifications"),
+ html.P(
+ "Alerts services raise via the SDK"
+ " notify() — info, warnings, critical.",
+ className="panel-sub",
+ ),
+ ]
+ )
+ ],
+ ),
+```
+
+Then add the `.panel-sub` style to `manager/assets/dashboard_styles.css` immediately after the `.panel-head h2` rule (line 44):
+
+```css
+.panel-sub{margin:.25rem 0 0;font-size:11px;line-height:1.4;color:var(--muted)}
+```
+
+Note: keep the `()` in `notify()` but do not include any URL — the CSS/markup must stay free of `http(s)://` (Task constraint). The text uses a plain hyphen.
+
+- [ ] **Step 4: Run test to verify it passes**
+
+Run: `./venv/bin/python -m pytest "tests/manager_test/test_ui_redesign.py::test_notifications_panel_has_explanation" -v`
+Expected: PASS
+
+- [ ] **Step 5: Commit**
+
+```bash
+git add manager/routers/dashboard_api.py manager/assets/dashboard_styles.css tests/manager_test/test_ui_redesign.py
+git commit -m "Dashboard: explain the Notifications panel with a subtitle"
+```
+
+---
+
+### Task 3: Make LOGS / DOCS read as buttons
+
+**Files:**
+- Modify: `manager/assets/dashboard_styles.css` (the `.lnk` rules at lines 90-95)
+- Test: `tests/manager_test/test_ui_redesign.py`
+
+**Interfaces:**
+- Consumes: nothing from other tasks.
+- Produces: restyled `.lnk` (markup unchanged — `get_service_log_link` / `get_service_docs_link` still emit ``).
+
+Current CSS (lines 88-95):
+
+```css
+/* ---------- service actions ---------- */
+.svc-actions{grid-area:actions;display:flex;gap:.4rem}
+.lnk{
+ font-family:var(--mono);font-size:10.5px;letter-spacing:.08em;text-transform:uppercase;
+ color:var(--muted);text-decoration:none;border-bottom:1px solid transparent;padding:.1rem 0;
+ transition:color .15s,border-color .15s;
+}
+.lnk:hover{color:var(--accent);border-color:var(--accent)}
+```
+
+- [ ] **Step 1: Write the failing test**
+
+Add to `tests/manager_test/test_ui_redesign.py`:
+
+```python
+def test_service_links_look_like_buttons():
+ css = (ASSETS / "dashboard_styles.css").read_text().replace(" ", "")
+ # .lnk is now a bordered pill, not underline-on-hover text
+ assert ".lnk{" in css
+ lnk_block = css.split(".lnk{", 1)[1].split("}", 1)[0]
+ assert "border:1px solid" in lnk_block
+ assert "border-radius:" in lnk_block
+ # keyboard focus ring, matching .act
+ assert ".lnk:focus-visible{" in css
+```
+
+- [ ] **Step 2: Run test to verify it fails**
+
+Run: `./venv/bin/python -m pytest "tests/manager_test/test_ui_redesign.py::test_service_links_look_like_buttons" -v`
+Expected: FAIL (the current `.lnk` block has `border-bottom`, not `border:1px solid`, and no `:focus-visible`).
+
+- [ ] **Step 3: Write minimal implementation**
+
+Replace lines 90-95 of `manager/assets/dashboard_styles.css` with:
+
+```css
+.lnk{
+ font-family:var(--mono);font-size:10px;letter-spacing:.1em;text-transform:uppercase;
+ color:var(--muted);text-decoration:none;
+ border:1px solid var(--line);border-radius:7px;padding:.34rem .6rem;
+ transition:color .15s,border-color .15s,background .15s;
+}
+.lnk:hover{color:var(--accent);border-color:var(--accent-dim);background:rgba(94,230,208,.06)}
+.lnk:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
+```
+
+Note: this mirrors the existing `.act` button look (border + rounded + accent hover + focus ring) so the two LOGS/DOCS controls clearly read as clickable. `.svc-actions` already has `gap:.4rem`, which spaces the two pills correctly — no change needed there.
+
+- [ ] **Step 4: Run test to verify it passes**
+
+Run: `./venv/bin/python -m pytest "tests/manager_test/test_ui_redesign.py::test_service_links_look_like_buttons" -v`
+Expected: PASS
+
+- [ ] **Step 5: Commit**
+
+```bash
+git add manager/assets/dashboard_styles.css tests/manager_test/test_ui_redesign.py
+git commit -m "Dashboard: style LOGS/DOCS as buttons with focus ring"
+```
+
+---
+
+### Task 4: Full guard-suite + lint pass
+
+**Files:**
+- No new code. Verification only.
+
+- [ ] **Step 1: Run the full UI guard suite**
+
+Run: `./venv/bin/python -m pytest tests/manager_test/test_ui_redesign.py -v`
+Expected: PASS (all existing tests + the three new ones). Confirms no earlier guard (e.g. `test_dashboard_layout_builds`, `test_dashboard_page_width_widened`, `test_notifications_panel_is_live`) regressed.
+
+- [ ] **Step 2: Lint the changed files**
+
+Run:
+```bash
+/home/kaveh/miniconda3/bin/python -m black --check manager/routers/dashboard_api.py tests/manager_test/test_ui_redesign.py
+/home/kaveh/miniconda3/bin/python -m flake8 manager/routers/dashboard_api.py tests/manager_test/test_ui_redesign.py
+/home/kaveh/miniconda3/bin/python -m pylint manager
+```
+Expected: black "would be left unchanged", flake8 no output, pylint 10.00/10. (CSS is not linted.) If black reports a reformat on `dashboard_api.py`, run it without `--check` to apply, then re-commit.
+
+- [ ] **Step 3: Commit any formatting fixup (only if Step 2 changed files)**
+
+```bash
+git add -A
+git commit -m "Dashboard polish: black formatting"
+```
+
+---
+
+## Self-Review
+
+**Spec coverage:**
+- Widen Services / slim sidebar → Task 1 (`2.6fr 0.9fr`). ✓
+- Explain Notifications → Task 2 (`panel-sub` subtitle). ✓
+- LOGS/DOCS obvious → Task 3 (bordered buttons + focus ring). ✓
+- Fix top-heavy emptiness → Task 1 (`min-height:calc(100vh - 4rem)` + `margin-block:auto`). ✓
+- Testing section (guard-test asserts) → Tasks 1-3 each add one test; Task 4 runs the full suite + lint. ✓
+- Non-goals (no stats bar, no badges, no logs-view change, no backend) → respected; no task touches them. ✓
+
+**Placeholder scan:** No TBD/TODO; every code step shows full CSS/markup and exact commands. ✓
+
+**Type/string consistency:** The subtitle literal "Alerts services raise via the SDK notify() — info, warnings, critical." in Task 2's implementation matches the test's substring "Alerts services raise". The class names `.panel-sub`, `.lnk`, `.grid`, `.page` are used identically across implementation and tests. The `2.6fr 0.9fr` value (space-stripped to `2.6fr0.9fr` in tests) is consistent. ✓
diff --git a/docs/superpowers/plans/2026-06-29-logs-viewer-enhancements.md b/docs/superpowers/plans/2026-06-29-logs-viewer-enhancements.md
new file mode 100644
index 0000000..ece6208
--- /dev/null
+++ b/docs/superpowers/plans/2026-06-29-logs-viewer-enhancements.md
@@ -0,0 +1,518 @@
+# Logs Viewer Enhancements + Responsive Layout — Implementation Plan
+
+> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
+
+**Goal:** Make the logs view a full-screen responsive console with log export and in-log search (over full history), and let the dashboard use more of the screen.
+
+**Architecture:** Pure front-end + two tiny route-context additions; no backend changes. The shared `manager/templates/logs.html` (serves both per-service `/services/~logs/view` and manager `/logs/view`) gains a responsive flex layout, an Export button, and a Search box. Export/search fetch the full log via the existing endpoints with `tail=all`. Dashboard width is a one-line CSS change.
+
+**Tech Stack:** FastAPI + Jinja2 templates, Plotly Dash dashboard, vanilla JS + CSS (no build step, no libraries).
+
+**Spec:** `docs/superpowers/specs/2026-06-29-logs-viewer-enhancements-design.md`
+
+## Global Constraints
+
+- **No backend changes.** Export/search use existing endpoints: service `GET /services/~logs?name=&version=&tail=all`; manager `GET /logs/stream?tail=all`. The live tail keeps `tail=200`/`tail=400` + `follow=true`.
+- **Reuse the shared `manager/templates/logs.html`** — both log views get every feature.
+- **Self-contained** (no CDNs); use the existing design tokens (`var(--accent)`, `var(--ground)`, etc.).
+- **XSS-safe log rendering**: insert log text via `textContent` / `createTextNode` — never `innerHTML` on log content (search highlight uses `` element nodes around text nodes).
+- **Preserve the live tail**: streaming + Follow/auto-scroll/Jump-to-latest stay; new features layer on top.
+- **Dashboard width:** `max-width: 1600px` centered (not fully fluid).
+
+## File Structure
+
+- `manager/assets/dashboard_styles.css` — **(modify)** `.page` max-width 1180 → 1600.
+- `manager/templates/logs.html` — **(modify)** responsive flex layout CSS; toolbar gains Search + Export; JS for cancellable live stream, search (filter/highlight/count), export (download).
+- `manager/routers/service_api.py` — **(modify)** `service_logs_view`: add `full_url` + `export_basename` to context.
+- `manager/routers/logging_api.py` — **(modify)** `manager_logs_view`: add `full_url` + `export_basename` to context.
+- `tests/manager_test/test_ui_redesign.py` — **(modify)** add guard tests.
+
+Run tests with `./venv/bin/python -m pytest tests/manager_test/test_ui_redesign.py -v` from the repo root. Lint with `/home/kaveh/miniconda3/bin/python -m black --check