Skip to content
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# BUG-T16: Tool Distribution (Pie) widget is cropped at medium widths

## Objective
Eliminate pie widget cropping at medium viewport widths (around 1200px) while preserving correct rendering at wide desktop and mobile sizes.

## Scope
- Frontend-only changes in Web UI static assets.
- Responsive layout and chart legend behavior for chart containers.

## Deliverables
- Update `src/mcpbridge_wrapper/webui/static/dashboard.css` to ensure chart containers shrink without clipping and canvas sizing remains responsive.
- Update `src/mcpbridge_wrapper/webui/static/dashboard.js` to reposition doughnut legends for medium widths.
- Add/adjust frontend tests for responsive legend behavior and resize handling.
- Validation report in `SPECS/INPROGRESS/BUG-T16_Validation_Report.md`.

## Acceptance Criteria
- Pie widget is not visually cropped at ~1200px viewport.
- Existing behavior at ~1450px and <768px remains functional.
- Chart rendering remains stable during resize events.
- Test suite passes, including required quality gates.

## Dependencies
- Existing Web UI dashboard chart rendering (P10-T1).

## Risks
- Resize handler overhead if updates trigger too often.
- Chart legend changes could affect visual density at desktop widths.

## Validation Plan
- Run automated tests for web UI frontend behavior.
- Run repository quality gates (`pytest`, `ruff check src/`, `mypy src/`, `pytest --cov`).
- Manual verification checklist at representative widths: 1450px, 1200px, 1024px, 768px.
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# BUG-T16 Validation Report

## Task
BUG-T16 — Tool Distribution (Pie) widget is cropped at medium widths

## Implementation Summary
- Updated chart container CSS to allow safe shrink behavior and prevent clipping at medium widths.
- Added responsive canvas sizing defaults for chart rendering stability.
- Added JS responsive legend-layout logic for doughnut charts (pie + error breakdown):
- Right-side legend at wide widths.
- Bottom legend at medium/mobile widths (<= 1280px).
- Added a unit test asserting responsive doughnut legend logic is present in dashboard JS.

## Files Changed
- `src/mcpbridge_wrapper/webui/static/dashboard.css`
- `src/mcpbridge_wrapper/webui/static/dashboard.js`
- `tests/unit/webui/test_server.py`

## Quality Gates
- `pytest -q` => PASS (629 passed, 5 skipped)
- `ruff check src/` => PASS
- `mypy src/` => PASS
- `pytest --cov` => PASS (91.33% total, threshold >= 90%)

## Acceptance Criteria Check
- Pie widget cropping at medium widths addressed via responsive container + legend reflow => PASS
- Behavior at wide and mobile breakpoints preserved by breakpoint-aware legend positioning => PASS
- Resize handling added (`window.resize` listener) => PASS
- Automated tests and quality gates passing => PASS

## Notes
- Existing deprecation warnings from `websockets`/`uvicorn` are unchanged and non-blocking for this task.
6 changes: 5 additions & 1 deletion SPECS/ARCHIVE/INDEX.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# mcpbridge-wrapper Tasks Archive

**Last Updated:** 2026-02-20 (BUG-T15_WebUI_Port_Config_Investigation)
**Last Updated:** 2026-02-20 (BUG-T16_Tool_Distribution_Pie_widget_is_cropped_at_medium_widths)

## Archived Tasks

Expand Down Expand Up @@ -92,6 +92,7 @@
| P11-T1 | [P11-T1_Add_Tool_Call_Detail_Inspector/](P11-T1_Add_Tool_Call_Detail_Inspector/) | 2026-02-15 | PASS |
| BUG-T8 | [BUG-T8_Audit_Log_Cross_Process_Visibility/](BUG-T8_Audit_Log_Cross_Process_Visibility/) | 2026-02-15 | PASS |
| BUG-T15 | [BUG-T15_WebUI_Port_Config_Investigation/](BUG-T15_WebUI_Port_Config_Investigation/) | 2026-02-20 | PASS |
| BUG-T16 | [BUG-T16_Tool_Distribution_Pie_widget_is_cropped_at_medium_widths/](BUG-T16_Tool_Distribution_Pie_widget_is_cropped_at_medium_widths/) | 2026-02-20 | PASS |
| P11-T2 | [P11-T2_Add_Session_Timeline_View/](P11-T2_Add_Session_Timeline_View/) | 2026-02-15 | PASS |
| P11-T3 | [P11-T3_Add_Dashboard_Theme_Toggle/](P11-T3_Add_Dashboard_Theme_Toggle/) | 2026-02-15 | PASS |
| P11-T4 | [P11-T4_Add_Keyboard_Shortcuts_Command_Palette/](P11-T4_Add_Keyboard_Shortcuts_Command_Palette/) | 2026-02-15 | PASS |
Expand Down Expand Up @@ -239,6 +240,7 @@
| [REVIEW_P14-T5_broker_socket_path_limit.md](_Historical/REVIEW_P14-T5_broker_socket_path_limit.md) | Review report for P14-T5 |
| [REVIEW_FU-P14-T5-1_macos_ci_socket_path.md](_Historical/REVIEW_FU-P14-T5-1_macos_ci_socket_path.md) | Review report for FU-P14-T5-1 |
| [REVIEW_bug_t15_webui_port_config.md](_Historical/REVIEW_bug_t15_webui_port_config.md) | Review report for BUG-T15 |
| [REVIEW_bug_t16_pie_responsive.md](_Historical/REVIEW_bug_t16_pie_responsive.md) | Review report for BUG-T16 |

## Archive Log

Expand Down Expand Up @@ -433,3 +435,5 @@
| 2026-02-20 | FU-P14-T5-1 | Archived REVIEW_FU-P14-T5-1_macos_ci_socket_path report |
| 2026-02-20 | BUG-T15 | Archived WebUI_Port_Config_Investigation (PASS) |
| 2026-02-20 | BUG-T15 | Archived REVIEW_bug_t15_webui_port_config report |
| 2026-02-20 | BUG-T16 | Archived Tool_Distribution_Pie_widget_is_cropped_at_medium_widths (PASS) |
| 2026-02-20 | BUG-T16 | Archived REVIEW_bug_t16_pie_responsive report |
31 changes: 31 additions & 0 deletions SPECS/ARCHIVE/_Historical/REVIEW_bug_t16_pie_responsive.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
## REVIEW REPORT — BUG-T16 Pie Responsive Layout

**Scope:** origin/main..HEAD
**Files:** 8

### Summary Verdict
- [x] Approve
- [ ] Approve with comments
- [ ] Request changes
- [ ] Block

### Critical Issues
- None.

### Secondary Issues
- None.

### Architectural Notes
- The fix is minimal and targeted: CSS shrink-safety (`min-width: 0`, `canvas width: 100%`) plus dynamic doughnut legend placement for medium widths.
- Resize handling is scoped to chart legend orientation and does not alter data flow.

### Tests
- Added static-asset assertion test for responsive doughnut legend logic in `tests/unit/webui/test_server.py`.
- Quality gates all pass:
- `pytest -q` PASS
- `ruff check src/` PASS
- `mypy src/` PASS
- `pytest --cov` PASS (`91.33%`)

### Next Steps
- FOLLOW-UP skipped: no actionable issues identified.
5 changes: 4 additions & 1 deletion SPECS/INPROGRESS/next.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@

## Recently Archived

- **BUG-T16** — Tool Distribution (Pie) widget is cropped at medium widths (2026-02-20, PASS)
- **BUG-T15** — Web UI fails to come up in MCP client runs when `--web-ui-port` and `--web-ui-config` are combined (2026-02-20, PASS)

## Suggested Next Tasks

- None currently ready.
- BUG-T17 — Rows in Audit Log table automatically fold after user unfolds them
- BUG-T14 — Rows in Per-Tool Latency Statistics fold automatically immediately after unfolding
- BUG-T10 — Tool chart colors change on update of tool type count
82 changes: 79 additions & 3 deletions SPECS/Workplan.md
Original file line number Diff line number Diff line change
Expand Up @@ -1436,7 +1436,7 @@ Enable parameter capture by passing `--web-ui-config` with `metrics.capture_para

---

### BUG-T14: Rows in Per-Tool Latency Statistics collapse after 1 second
### BUG-T14: Rows in Per-Tool Latency Statistics fold automatically immediately after unfolding
- **Type:** Bug / Web UI / UI Stability
- **Status:** 🔴 Open
- **Priority:** P1
Expand All @@ -1446,12 +1446,12 @@ Enable parameter capture by passing `--web-ui-config` with `metrics.capture_para
- **Affected Surface:** Per-Tool Latency Statistics table

#### Description
Expanded or highlighted rows in the Per-Tool Latency Statistics table collapse/reset approximately every 1 second. This coincides with the dashboard's default WebSocket refresh interval (`dashboard.refresh_interval_ms: 1000`), suggesting the table is being fully re-rendered on each update, discarding any user interaction state (expanded rows, hover highlights, selected rows).
Expanded or highlighted rows in the Per-Tool Latency Statistics table fold/collapse automatically immediately (or within about 1 second) after the user unfolds them. This coincides with the dashboard's default WebSocket refresh interval (`dashboard.refresh_interval_ms: 1000`), suggesting the table is being fully re-rendered on each update, discarding user interaction state (expanded rows, hover highlights, selected rows).

#### Symptoms
```
User expands a row in the Per-Tool Latency Statistics table to inspect details.
After ~1 second the row collapses back to its default state.
Immediately (or after ~1 second) the row collapses back to its default state.
Behaviour repeats on every subsequent refresh cycle.
```

Expand Down Expand Up @@ -1518,6 +1518,82 @@ Use `--web-ui` with `--web-ui-config` only, and set the port in the config file.

---

### BUG-T16: Tool Distribution (Pie) widget is cropped at medium widths
- **Type:** Bug / Web UI / Responsive Layout
- **Status:** ✅ Fixed (2026-02-20)
- **Priority:** P1
- **Discovered:** 2026-02-20
- **Completed:** 2026-02-20
- **Component:** Web UI Dashboard (`webui/static/`, chart layout CSS)
- **Affected Clients:** All clients using Web UI dashboard
- **Affected Surface:** Tool Distribution (Pie) widget

#### Description
The "Tool Distribution (Pie)" widget does not flow correctly when the dashboard width is reduced to medium breakpoints. At around `1450px` layout is fine, around `1200px` the pie widget gets cropped, and below `768px` it becomes okay again.

#### Symptoms
```text
~1450px viewport: widget displays correctly
~1200px viewport: Tool Distribution (Pie) widget is cropped
<768px viewport: widget displays correctly again
```

#### Root Cause Analysis
Likely a responsive breakpoint/layout gap between desktop and mobile rules where the chart container keeps a width/min-width/flex basis that overflows or clips at mid-range viewport sizes.

#### Workaround
Resize the browser to either wide desktop (`~1450px+`) or narrow mobile (`<768px`) widths where the widget renders correctly.

#### Resolution Path
- [x] Reproduce and capture exact breakpoint range where cropping begins/ends
- [x] Inspect chart container/grid/flex CSS rules for mid-width breakpoints (especially min-width, fixed widths, and overflow settings)
- [x] Update responsive CSS so the pie widget reflows without clipping at medium widths
- [x] Add/extend frontend responsiveness test or manual regression checklist for `1200px` range
- [x] Validate layout at `1450px`, `1200px`, `1024px`, `768px`, and mobile widths

#### Related Items
- **P10-T1** ✅ — Web UI Dashboard implementation (chart layout subsystem)
- **BUG-T10** — Tool chart rendering instability; related chart UX surface

---

### BUG-T17: Rows in Audit Log table automatically fold after user unfolds them
- **Type:** Bug / Web UI / UI Stability
- **Status:** 🔴 Open
- **Priority:** P1
- **Discovered:** 2026-02-20
- **Component:** Web UI Dashboard (`webui/static/`, audit log table rendering)
- **Affected Clients:** All clients using Web UI dashboard
- **Affected Surface:** Audit Log table row expand/collapse behavior

#### Description
Rows in the Audit Log table automatically fold/collapse several seconds after a user unfolds them. This interrupts inspection workflows and suggests expanded row UI state is being reset during periodic dashboard updates.

#### Symptoms
```text
User unfolds an Audit Log row to inspect details.
After several seconds, the row folds automatically without user action.
```

#### Root Cause Analysis
Likely caused by full table re-render on refresh/WebSocket updates without preserving expanded-row state, similar to other dashboard state-reset issues.

#### Workaround
Temporarily increase dashboard refresh interval via config to reduce frequency of auto-fold behavior.

#### Resolution Path
- [ ] Reproduce with default refresh interval and identify the exact trigger (WebSocket update vs polling refresh)
- [ ] Refactor Audit Log table updates to patch rows by stable entry ID instead of full DOM replacement
- [ ] Persist expanded/collapsed row state across refresh cycles
- [ ] Add regression test (or manual checklist) confirming unfolded rows stay unfolded across updates
- [ ] Validate behavior under active tool-call traffic

#### Related Items
- **BUG-T12** — Audit Log update path not showing new calls; same component/surface
- **BUG-T14** — Per-Tool Latency row state resets on refresh; similar UI-state loss pattern

---

### Phase 10: Web UI Control & Audit Dashboard

**Intent:** Create a web-based dashboard for real-time monitoring, control, and audit logging of the XcodeMCPWrapper. Provides visibility into MCP tool usage, performance metrics, and operational control.
Expand Down
3 changes: 3 additions & 0 deletions src/mcpbridge_wrapper/webui/static/dashboard.css
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ main {
border: 1px solid var(--border-color);
border-radius: var(--radius);
padding: 16px;
min-width: 0;
}

.chart-container.wide {
Expand All @@ -185,6 +186,8 @@ main {
}

canvas {
display: block;
width: 100%;
max-height: 280px;
}

Expand Down
22 changes: 22 additions & 0 deletions src/mcpbridge_wrapper/webui/static/dashboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
"#f85149", "#79c0ff", "#56d364", "#d2a8ff",
"#e3b341", "#ffa198",
];
const MEDIUM_WIDTH_BREAKPOINT = 1280;

// --- Utility ---
function formatUptime(seconds) {
Expand Down Expand Up @@ -185,6 +186,26 @@
animation: { duration: 300 },
},
});

updateDoughnutLegendLayout();
}

function shouldUseBottomLegend() {
return window.innerWidth <= MEDIUM_WIDTH_BREAKPOINT;
}

function updateDoughnutLegendLayout() {
var legendPosition = shouldUseBottomLegend() ? "bottom" : "right";
["toolPie", "errorBreakdown"].forEach(function (chartName) {
var chart = charts[chartName];
if (!chart || !chart.options || !chart.options.plugins || !chart.options.plugins.legend) {
return;
}
if (chart.options.plugins.legend.position !== legendPosition) {
chart.options.plugins.legend.position = legendPosition;
chart.update("none");
}
});
}

// --- Update Functions ---
Expand Down Expand Up @@ -792,6 +813,7 @@
initCharts();
initTheme();
setupThemeToggle();
window.addEventListener("resize", updateDoughnutLegendLayout);
setupEventHandlers();
initKeyboardShortcuts();
connectWebSocket();
Expand Down
9 changes: 9 additions & 0 deletions tests/unit/webui/test_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,15 @@ def test_dashboard_js_uses_uniform_client_widget_escaping(self, client):
assert "var escapedCount = escapeHtml(String(count));" in response.text
assert "var escapedLastSeen = escapeHtml(String(lastSeen));" in response.text

def test_dashboard_js_has_responsive_doughnut_legend_logic(self, client):
"""Pie/error doughnut legends switch layout at medium widths."""
response = client.get("/static/dashboard.js")
assert response.status_code == 200
assert "const MEDIUM_WIDTH_BREAKPOINT = 1280;" in response.text
assert "function updateDoughnutLegendLayout()" in response.text
assert '["toolPie", "errorBreakdown"]' in response.text
assert 'window.addEventListener("resize", updateDoughnutLegendLayout);' in response.text

def test_websocket_metrics_update_includes_sessions(self, client, audit):
"""WebSocket metrics_update message includes sessions key."""
with client.websocket_connect("/ws/metrics") as websocket:
Expand Down