`.
- The `selectedBranch` value is already passed (indirectly via `analysisLoading` disable logic). Also pass `canRunAnalysis={!!selectedBranch && !analysisLoading}` so ActionFeed can enable/disable.
#### `ActionFeed.tsx`
- **Add props**: `onRunAnalysis: () => void`, `canRunAnalysis: boolean`.
- In the **Suggested Actions section** (around line ~916β956), replace the current conditional rendering with the unified three-state slot:
```tsx
{/* Unified analysis action slot */}
{analysisLoading ? (
βοΈ Analyzingβ¦
) : pendingAnalysisResult ? (
π Display {count} prioritized actions
) : (
π Analyze & Suggest
)}
```
#### `standalone_interface.html`
- Apply the same relocation: remove the analysis button from the contingency selector area and add it into the action feed panel header.
#### Test Files
- Update `App.settings.test.tsx` and `App.session.test.tsx`: the `'π Run Analysis'` text selector changes to `'π Analyze & Suggest'`. Locate the button inside the action feed panel instead of the sidebar header.
---
## 2. Always-Visible Visualization Tabs with Placeholder Messages
### Current State
In `VisualizationPanel.tsx` (lines ~808β858), tabs are **conditionally rendered**:
- **Network (N)**: Always shown
- **Contingency (N-1)**: Only when `selectedBranch` is set
- **Action**: Only when `selectedActionId` is set
- **Overflow Analysis**: Only when `result?.pdf_url` exists
### Target State
All four tabs are **always visible**. When a tab's content is not yet available, clicking it shows a placeholder message guiding the user on what to do. Additionally, the **Action** tab dynamically updates its label to include the selected action ID.
#### Tab Definitions
| Tab | Label (default) | Label (populated) | Placeholder message |
|-----|------------------|--------------------|---------------------|
| N | `Network (N)` | β | *(always populated after config)* `Configure a network path in Settings to view the base-case diagram.` |
| N-1 | `Contingency (N-1)` | β | `Select a contingency element from the dropdown above to view the N-1 state.` |
| Action | `Remedial Action` | `Remedial Action: {actionId}` | `Select an action card from the suggestions panel to view its effect on the network.` |
| Overflow | `Overflow Analysis` | β | `Run an analysis to generate the overflow graph.` |
#### Tab Styling
- **Populated tabs**: Current active/inactive styling (bold + colored bottom border when active).
- **Unpopulated tabs**: Slightly dimmer text (`#aab`), italic label, no colored border even when active. Still clickable.
- **Active but unpopulated**: White background + placeholder message in content area (centered, grey italic text with a subtle icon).
### Files to Change
#### `VisualizationPanel.tsx`
- **Remove conditional rendering** of tab buttons. Render all four always.
- Add availability flags:
```tsx
const tabAvailable = {
'n': !!nDiagram?.svg,
'n-1': !!n1Diagram?.svg,
'action': !!actionDiagram?.svg,
'overflow': !!result?.pdf_url,
};
```
- Update the **Action** tab label:
```tsx
const actionTabLabel = selectedActionId
? `Remedial Action: ${selectedActionId}`
: 'Remedial Action';
```
Truncate long action IDs with CSS `text-overflow: ellipsis` (max ~200px) and show full ID on hover via `title` attribute.
- In the tab **content area**, wrap each tab's body:
```tsx
{activeTab === 'action' && (
tabAvailable['action']
?
:
)}
```
- Add a small `TabPlaceholder` component (inline or local):
```tsx
const TabPlaceholder: React.FC<{ message: string }> = ({ message }) => (
{message}
);
```
#### `standalone_interface.html`
- Mirror the same logic: always render all 4 tab buttons, show placeholder text when content is unavailable.
#### `App.tsx`
- Remove any logic that prevents switching to a tab when its content is unavailable (if present). The `onTabChange` handler should allow selecting any tab at any time.
---
## 3. Collapsible Voltage Filter
### Current State
The voltage filter is a **vertical sidebar** on the right side of the visualization panel (`VisualizationPanel.tsx` lines ~1074β1125, CSS class `.voltage-sidebar`). It is always visible when `uniqueVoltages.length > 1`, consuming ~62px of horizontal space.
### Target State
The voltage filter sidebar is **collapsed by default**, showing only a small toggle button. When expanded, it shows the full slider UI as today.
#### Collapsed State
- A small vertical button on the right edge: `βΈ kV` (or a filter icon from lucide-react).
- Width: ~24px, just enough for the icon/label.
- Click expands the filter.
#### Expanded State
- Full current sidebar (62px width) with an additional collapse button (`β` or `β`) at the top.
- Clicking the collapse button or clicking outside collapses it back.
### Files to Change
#### `VisualizationPanel.tsx`
- Add local state: `const [voltageFilterExpanded, setVoltageFilterExpanded] = useState(false);`
- Wrap the existing voltage sidebar in a conditional:
```tsx
{uniqueVoltages.length > 1 && (
voltageFilterExpanded ? (
setVoltageFilterExpanded(false)}
style={{ alignSelf: 'flex-end', background: 'none', border: 'none', cursor: 'pointer', fontSize: '14px' }}>
β
{/* ... existing slider content ... */}
) : (
setVoltageFilterExpanded(true)}
title="Show voltage filter"
>
kV βΈ
)
)}
```
#### `App.css`
- Add `.voltage-sidebar-toggle` style:
```css
.voltage-sidebar-toggle {
position: absolute;
top: 0;
right: 0;
bottom: 0;
width: 24px;
background: rgba(244, 244, 244, 0.85);
border: none;
border-left: 1px solid #ccc;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
z-index: 15;
color: #666;
font-size: 12px;
}
.voltage-sidebar-toggle:hover {
background: rgba(230, 230, 230, 0.95);
color: #333;
}
```
#### `standalone_interface.html`
- Add equivalent collapse/expand toggle with the same CSS and JS logic.
---
## 4. Revised Color Code for Highlights
### Current State
| Element | NAD Color | SLD Color | CSS Class |
|---------|-----------|-----------|-----------|
| Action targets | Yellow `#fffb00` | Yellow `#fffb00` | `.nad-action-target`, `.sld-highlight-action` |
| Contingency | Orange `#ff9800` | Orange `#ff9800` | `.nad-contingency-highlight`, `.sld-highlight-contingency` |
| Overloads | Orange `#ff8c00` | Orange `#ff8c00` dashed | `.nad-overloaded`, `.sld-highlight-overloaded` |
| Breakers/switches | β | Purple `#e040fb` | `.sld-highlight-breaker` |
**Problem**: Contingency and overloads both use orange, making them hard to distinguish. Actions use yellow which doesn't contrast well with orange on bright backgrounds.
### Target Color Scheme
| Element | New Color | Hex | Rationale |
|---------|-----------|-----|-----------|
| **Remedial Actions** | Purple-pink | `#e040fb` | Distinctive, stands out against network blues/greens |
| **Contingency** | Yellow | `#f5c542` | Warm "warning" tone, clearly different from orange |
| **Overloads** | Orange | `#ff8c00` | Kept β strong "danger" association, universally understood |
| **Breakers/switches** | Purple (lighter) | `#ce93d8` | Softer shade to distinguish from action purple-pink |
### Visual Hierarchy
```
π£ Purple-pink (#e040fb) β Actions (what the operator chose)
π‘ Yellow (#f5c542) β Contingency (the triggering event)
π Orange (#ff8c00) β Overloads (the problem to solve)
π£ Light purple (#ce93d8) β Breakers (topology detail)
```
### Files to Change
#### `App.css` β NAD Highlights
**Action targets**: Yellow β Purple-pink
```css
/* Before */
.nad-action-target {
filter: drop-shadow(0 0 8px #fffb00) drop-shadow(0 0 15px #fffb00) !important;
}
.nad-action-target path, .nad-action-target line, ... {
stroke: #fffb00 !important;
}
.nad-action-target circle, .nad-action-target rect {
stroke: #ffe600 !important;
fill: #ffe600 !important;
}
/* After */
.nad-action-target {
filter: drop-shadow(0 0 8px #e040fb) drop-shadow(0 0 15px #e040fb) !important;
}
.nad-action-target path, .nad-action-target line, ... {
stroke: #e040fb !important;
}
.nad-action-target circle, .nad-action-target rect {
stroke: #e040fb !important;
fill: #e040fb !important;
fill-opacity: 0.6 !important;
}
```
**Contingency**: Orange β Yellow
```css
/* Before */
.nad-contingency-highlight {
filter: drop-shadow(0 0 8px #ff9800) drop-shadow(0 0 15px #ff9800) !important;
}
.nad-contingency-highlight path, ... {
stroke: #ff9800 !important;
}
/* After */
.nad-contingency-highlight {
filter: drop-shadow(0 0 8px #f5c542) drop-shadow(0 0 15px #f5c542) !important;
}
.nad-contingency-highlight path, ... {
stroke: #f5c542 !important;
}
.nad-contingency-highlight circle, .nad-contingency-highlight rect {
stroke: #f5c542 !important;
fill: #f5c542 !important;
}
```
**Overloads**: No change (already orange `#ff8c00`). But add NAD highlight support for overloads using the same clone-based glow pattern used for contingency and actions, so overloaded lines get a visible orange halo instead of just a stroke-width bump.
#### `App.css` β SLD Highlights
```css
/* Action: yellow β purple-pink */
.sld-highlight-clone.sld-highlight-action {
filter: drop-shadow(0 0 4px #e040fb) drop-shadow(0 0 8px #e040fb);
}
.sld-highlight-clone.sld-highlight-action path, ... {
stroke: #e040fb !important;
}
/* Contingency: orange β yellow */
.sld-highlight-clone.sld-highlight-contingency {
filter: drop-shadow(0 0 4px #f5c542) drop-shadow(0 0 8px #f5c542);
}
.sld-highlight-clone.sld-highlight-contingency path, ... {
stroke: #f5c542 !important;
}
/* Overloads: unchanged (orange #ff8c00, dashed) */
/* Breakers: bold purple β lighter purple */
.sld-highlight-clone.sld-highlight-breaker {
filter: drop-shadow(0 0 3px #ce93d8) drop-shadow(0 0 6px #ce93d8);
}
.sld-highlight-clone.sld-highlight-breaker path, ... {
stroke: #ce93d8 !important;
}
```
#### `App.css` β Overload Highlight Enhancement
Add clone-based glow for overloaded lines (currently overloads only get a stroke-width increase, no halo/glow like actions and contingency do):
```css
.nad-overloaded {
filter: drop-shadow(0 0 6px #ff8c00) drop-shadow(0 0 12px #ff8c00) !important;
}
```
#### `svgUtils.ts` β Overload Highlighting
Currently `highlightOverloadedLines()` (line ~186) only adds the `nad-overloaded` class to existing elements. To match the clone-based approach used by actions and contingency (for consistent glow rendering), consider upgrading it to also create cloned background elements with the `.nad-overloaded` class. This gives overloads the same visual treatment (halo behind the original element).
Alternatively, the simpler CSS-only `filter: drop-shadow()` addition above may be sufficient if the existing stroke-width increase already makes overloads visible enough. Start with the CSS-only approach and upgrade to clones only if needed.
#### `standalone_interface.html`
- Update all matching CSS rules with the same color changes.
- The standalone file has its own embedded `