Skip to content

Plan frontend UI improvements: buttons, tabs, filters, colors - #71

Merged
marota merged 27 commits into
mainfrom
claude/frontend-ui-improvements-BGTMe
Apr 6, 2026
Merged

marota merged 27 commits into
mainfrom
claude/frontend-ui-improvements-BGTMe

Conversation

@marota

@marota marota commented Apr 1, 2026

Copy link
Copy Markdown
Collaborator

Summary

This PR adds a comprehensive design document outlining four major frontend UI improvements to enhance usability and visual clarity across the network analysis interface.

Key Changes

  • Move & Rename "Run Analysis" Button: Relocates the analysis trigger from the contingency selector card into the ActionFeed panel header, consolidating it with the processing state and results display into a single unified three-state button slot (Idle → "🔍 Analyze & Suggest", Running → "⚙️ Analyzing…", Pending → "📊 Display N prioritized actions")

  • Always-Visible Visualization Tabs with Placeholders: Converts the four visualization tabs (Network, Contingency, Action, Overflow Analysis) from conditionally-rendered to always-visible, with placeholder messages guiding users when content is unavailable. Adds dynamic labeling for the Action tab to show the selected action ID.

  • Collapsible Voltage Filter: Transforms the voltage filter sidebar from always-visible to collapsed-by-default, showing only a small toggle button (▸ kV) on the right edge that expands to the full filter UI on demand.

  • Revised Color Code for Highlights: Updates the SVG highlight color scheme to improve visual distinction:

    • Remedial Actions: Yellow → Purple-pink (#e040fb)
    • Contingency: Orange → Yellow (#f5c542)
    • Overloads: Orange (unchanged, #ff8c00)
    • Breakers/Switches: Purple → Lighter purple (#ce93d8)

Implementation Details

  • All changes are designed to be independently deployable in the recommended order: colors → voltage filter → tabs → button relocation
  • Includes detailed file-by-file change specifications for React components (App.tsx, ActionFeed.tsx, VisualizationPanel.tsx), CSS updates, test modifications, and the standalone HTML interface
  • Provides specific code examples and CSS snippets for implementation
  • Includes optional enhancements like a color legend and clone-based glow effects for overload highlighting

Files Affected

  • frontend/src/App.tsx
  • frontend/src/components/ActionFeed.tsx
  • frontend/src/components/VisualizationPanel.tsx
  • frontend/src/App.css
  • frontend/src/utils/svgUtils.ts (optional)
  • frontend/src/utils/cssRegression.test.ts
  • frontend/src/App.settings.test.tsx
  • frontend/src/App.session.test.tsx
  • standalone_interface.html

https://claude.ai/code/session_013iLF6N1ofNsjzgQ3VRYYy3

claude and others added 7 commits April 1, 2026 13:03
Covers 4 changes: relocate analysis button to ActionFeed,
always-visible visualization tabs with placeholders, collapsible
voltage filter, and revised color scheme (purple-pink for actions,
yellow for contingency, orange for overloads).

https://claude.ai/code/session_013iLF6N1ofNsjzgQ3VRYYy3
1. Move & rename analysis button: Remove "Run Analysis" from contingency
   card; add unified "Analyze & Suggest" / "Analyzing…" / "Display N
   prioritized actions" three-state slot in ActionFeed Suggested Actions
   section.

2. Always-visible visualization tabs: All 4 tabs (N, N-1, Remedial
   Action, Overflow Analysis) always rendered. Unavailable tabs shown
   dimmed/italic with placeholder tooltips. Action tab label updates to
   "Remedial Action: {actionId}" when an action is selected.

3. Collapsible voltage filter: Collapsed by default to a 24px "kV ▸"
   toggle button; expands on click to the full sidebar slider.

4. Revised highlight color scheme:
   - Actions: purple-pink #e040fb (was yellow #fffb00)
   - Contingency: yellow #f5c542 (was orange #ff9800)
   - Overloads: orange #ff8c00 (unchanged) + new glow filter
   - Breakers: light purple #ce93d8 (was bold purple #e040fb)
   Applied to App.css and standalone_interface.html.

All 339 tests pass.

https://claude.ai/code/session_013iLF6N1ofNsjzgQ3VRYYy3
Mirror the 4 frontend UI changes in standalone_interface.html:
1. Always-visible tabs with placeholder tooltips, dynamic
   "Remedial Action: {id}" label
2. Collapsible voltage filter (collapsed by default)
3. "Analyze & Suggest" button moved to suggested actions section
4. Color scheme already updated in previous commit

https://claude.ai/code/session_013iLF6N1ofNsjzgQ3VRYYy3
…laceholder text

- Add close button to the Recommender Settings warning in ActionFeed
- Fix standalone_interface.html to respect dismissedRecommenderWarning state
- Update overflow tab placeholder text to reference "Analyze & Suggest"

https://claude.ai/code/session_013iLF6N1ofNsjzgQ3VRYYy3
… approach

- Action highlight: #e040fb → #ff4081 (more pink, distinct from voltage level purple)
- Contingency highlight: #f5c542 → #ffe033 (more yellow, less orange)
- Overload highlights: upgraded from simple CSS class to clone-based halo
  (same approach as action/contingency) with stroke-width 120px for visibility
- Overloads now properly visible in Action tab NAD visualization
- Updated tab accent color for Remedial Action tab to match pink theme

https://claude.ai/code/session_013iLF6N1ofNsjzgQ3VRYYy3
@marota marota mentioned this pull request Apr 2, 2026
10 of 11 tasks
claude and others added 20 commits April 2, 2026 12:01
…ton after use

- Action highlight clones now fully opaque (no transparency bleeding through)
- Fixed overload highlights in action tab: applyActionTargetHighlights no longer
  removes all highlight clones (only its own); reordered highlight application
  so overloads render after action targets
- Hide "Analyze & Suggest" button once actions have been displayed (same result,
  not useful to run again)

https://claude.ai/code/session_013iLF6N1ofNsjzgQ3VRYYy3
…verloads filtering

Overload highlights in action tab:
- Fixed threshold: check rho_after > monitoringFactor instead of > 1.0
  (rho values are raw fractions where 1.0 = thermal limit, monitoringFactor
  is the operational threshold e.g. 0.95)
- Filter overloaded lines by selectedOverloads so user-deselected N-1
  overloads are not highlighted
- Added monitoringFactor and selectedOverloads to highlight effect deps

Loading display values (backend):
- Removed monitoring_factor multiplication from rho_before, rho_after, and
  max_rho in simulate_manual_action, _enrich_actions, and compute_superposition
- monitoring_factor is an overload detection threshold, not a display scaling
  factor — displayed loadings now show true physical loading percentages

N-1 tab overload highlights:
- Filter by selectedOverloads to discard user-deselected overloads

https://claude.ai/code/session_013iLF6N1ofNsjzgQ3VRYYy3
…verloads filtering

Overload highlights in action tab:
- Fixed threshold: check rho_after > monitoringFactor instead of > 1.0
- Use lines_overloaded_after when available, fallback to rho_after check
- Filter overloaded lines by selectedOverloads (user-deselected N-1
  overloads are excluded from highlights)

Loading display values (backend):
- Removed monitoring_factor multiplication from rho_before, rho_after, and
  max_rho in _enrich_actions, simulate_manual_action, and compute_superposition
- monitoring_factor is a threshold for overload detection, not a display
  scaling factor — displayed loadings now show true physical loading %

N-1 tab overload highlights:
- Filter by selectedOverloads to exclude user-deselected overloads

Updated svgUtils tests to match clone-based overload highlight approach.

https://claude.ai/code/session_013iLF6N1ofNsjzgQ3VRYYy3
…uggested actions

- Restored monitoring_factor multiplication for rho_before/rho_after/max_rho
  in _enrich_actions, simulate_manual_action, and compute_superposition
  (monitoring_factor IS needed for operational loading display, not just threshold)
- Fixed lines_overloaded_after check: empty array [] is truthy in JS, so
  suggested actions (which don't have lines_overloaded_after from discovery
  engine) would skip the rho_after fallback and show no overload highlights.
  Now checks .length > 0 in both standalone_interface.html and App.tsx.

https://claude.ai/code/session_013iLF6N1ofNsjzgQ3VRYYy3
applyContingencyHighlight had two bugs:
1. Did not clear previous contingency clones before adding new ones,
   causing old highlights to accumulate when switching tabs
2. Used a persistent cached bgCTM (backgroundLayer._cachedScreenCTM)
   that went stale after panning/zooming, causing position drift

Fix: clear all .nad-highlight-clone.nad-contingency-highlight clones
at the start of each call, and always compute a fresh bgCTM.

https://claude.ai/code/session_013iLF6N1ofNsjzgQ3VRYYy3
…ioning

getScreenCTM() includes the screen-space offset which changes with
pan/zoom and tab visibility, causing highlight clones to drift away from
their target elements when switching between tabs. getCTM() gives the
transform relative to the SVG viewport, which is stable regardless of
screen position.

Applied to all three highlight functions: applyContingencyHighlight,
applyActionTargetHighlights, and applyOverloadedHighlights.

https://claude.ai/code/session_013iLF6N1ofNsjzgQ3VRYYy3
…nd layer

The CTM-based approach (placing clones in a separate background layer
with computed transforms) caused highlight drift when switching tabs
because the CTM values depend on screen/viewport state that changes.

New approach: insert the clone as an immediate previous sibling of the
original element. This inherits all parent transforms naturally (no CTM
math needed) and SVG paint order ensures the halo renders behind the
original element.

https://claude.ai/code/session_013iLF6N1ofNsjzgQ3VRYYy3
… solid yellow halo without transparency or dashes
…dering: ensures it stays behind nodes and labels
…hts (overloads, actions, contingencies) in React and Standalone interfaces
@marota
marota merged commit d1f311d into main Apr 6, 2026
2 checks passed
marota pushed a commit that referenced this pull request Apr 19, 2026
…tton release

Four user-reported issues from the last screenshot batch:

## 1. Combine-modal Simulate button stuck in running mode

`simulatingActionId` was only reset in the outer try's finally, AFTER
the async action-variant-diagram fetch completed.  On big grids
that's ~5-6 seconds of stale "spinner" UI even though the action
CARD (what matters to the sidebar feed) has already landed.

Fix: call setSimulatingActionId(null) + setIsSimulatingCombined(false)
the moment the simulate response arrives and the result has been
merged.  The subsequent variant-diagram fetch continues
asynchronously — buttons release immediately, tab flips happen
naturally when the diagram is ready, matching React's perceived
responsiveness.  Applied to both handleAddManualAction and
handleSimulateCombined.

## 2. Tab switching / action diagram loading felt sluggish

Two React performance optimisations were missing on the standalone:

### 2a. `/api/network-diagram` served as plain JSON → large JSON.parse

The backend exposes a `format=text` variant that returns
`{header}\n<svg…>` instead of embedding the multi-MB SVG inside a
JSON string.  Skips a ~500 ms main-thread JSON.parse on the
PyPSA-EUR France grid (see docs/perf-loading-parallel.md).  React
has used this since PR #71.

Ported to the standalone via a new `_fetchNetworkDiagramTextFormat()`
helper that fetches + splits on the first newline, with a graceful
fallback to the JSON endpoint if the text variant 404s (older
backends).

### 2b. Base NAD fetched AFTER branches + VLs + nominal-voltages

Previously the standalone did `Promise.all([branches, VLs, nomV])`
THEN called `fetchBaseDiagram`.  Since network-diagram is by far
the slowest XHR, stacking it after the metadata round-trips adds
~1-2 s to the critical path.  React parallelises all four since
PR #88 (docs/perf-loading-parallel.md).

Ported: both call sites (applySettingsImmediate + handleLoadConfig
equivalents) now issue a 4-way Promise.all that includes the
network-diagram.  The SVG is processed synchronously after the
Promise.all resolves, matching the React flow.

## 3. Pin rendering

Prior implementation was an SVG circle rendered in a viewBox-
scaled layer, so pins shrunk / grew and drifted as the user
zoomed.  Label was the rank number, not the loading percentage.
Anchor math was fine but the visual anchoring looked off because
the pin's centroid scaled with the map.

Rewrite:

  - Pins rendered as absolutely-positioned `<div>` elements in
    SCREEN pixel space.  A `projectSvgPoint(x, y)` function maps
    each pin's SVG-local coordinates through the current
    overviewVb + overlayRect using the same `xMidYMid meet`
    projection as the SVG itself, so pins stay exactly on the
    line midpoint as the user pans + zooms.
  - Teardrop glyph: inline <svg> with a <path> for the classic
    Google-Maps pin shape (`d="M18 0 … Z"`), tip at (0, 48) so
    `translate(-50%, -100%)` on the wrapper plants the tip on
    the asset.  Inner white circle for label contrast.
  - Label is the loading percentage (rounded), falling back to
    the rank when rho isn't set — matches the React ActionOverview
    component's pin label.
  - Size is 36x48 CSS-px regardless of zoom.  A ResizeObserver
    keeps overlayRect current when the window or sidebar resizes,
    so pin projection stays accurate through responsive layout
    changes.

Combined-pair curves (React's Bézier connecting two pins) are
still omitted — it's purely cosmetic and the per-pin events +
metadata round-trip already cover replay parity.

## 4. Deselecting an action jumped to N-1 tab instead of staying on Action

React switched this behaviour in PR #93 — clicking the selected
action card again (or the × chip) now keeps the user on the
Remedial Action tab, which falls through to the
ActionOverviewDiagram pin view.  The standalone was still doing
`setActiveTab('n-1')` in the deselect branch of handleActionSelect.

Fix: drop that line.  The tab stays on 'action', selectedActionId
becomes null, and the conditional render below picks the Overview
component because it's the only branch where
`result?.actions && !selectedActionId`.

---

Parity-script impact:

  - Layer 1 still 51/51, exits 0.
  - Layer 2 unchanged (26/30 React, 30/30 standalone).
  - Layer 3a 22/22.
  - Vitest 972/972.

https://claude.ai/code/session_01LGL7gvedQLrUGGzqVm4wAG
marota pushed a commit that referenced this pull request Apr 30, 2026
Reconciliation of section 2 (0.5.0):
- Drop misattributed PRs that were actually pre-rebrand: save/reload
  (#49/#52), MW Start (#62), interaction-logging (#64), SLD highlights
  (#63), load shedding initial integration (#61). All now properly
  cited in section 1.5–1.6.
- Disambiguate App.tsx refactor history: PR #56 (hooks, 2100 → 800,
  pre-rebrand) vs PR #74 (components, 1000 → 650, 0.5.0) vs PR #75
  (memoization Phase 2, same LoC).
- Add accurate 0.5.0 PRs: #66 (vectorization w/ benchmark table),
  #69/#70/#71 (UI polish), #72 (curtailment), #73 (loads_p/gens_p
  format + configurable MW), #74/#75 (App.tsx decomposition),
  #78 (PST tap re-simulation), #84/#86/#87/#90 (detachable tabs).
- Add a recap table summarizing what's truly new in 0.5.0.

Diagrams added (Mermaid, GitHub-rendered):
- Gantt timeline of all 4 phases (top of doc).
- High-level architecture (frontend / backend / data).
- Two-step analysis sequence diagram (section 1.6).
- App.tsx LoC evolution flow (section 2.4).
- Backend mixin decomposition before/after PR #104/#106 (section 3).
- PyPSA-EUR pipeline flowchart (section 4).

https://claude.ai/code/session_01Pg7fuCUG2edfm5PyHS6SbN
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants