Private
Public Access
0
0

docs(nerv_theme): fix 4 drift clusters (color table, render_nerv_fx fiction, [nerv] config, apply_nerv body)

This commit is contained in:
2026-06-10 23:14:21 -04:00
parent e1287a4cf4
commit 7aa484649f
+70 -77
View File
@@ -35,20 +35,18 @@ The theme is **opt-in**, not the default. Most users will use the standard dark
### Color Palette
| Role | Hex | Usage |
|---|---|---|
| **Background** | `#000000` | Primary window background |
| **Panel** | `#0A0A0A` | Panel and group backgrounds |
| **Text Primary** | `#E0E0E0` | Body text, labels |
| **Text Secondary** | `#A0A0A0` | Subdued text, hints |
| **Accent (NERV Orange)** | `#FF6B00` | Primary action color, active state |
| **Accent (NERV Red)** | `#E50000` | Error, blocked, abort state |
| **Accent (NERV Green)** | `#00C800` | Success, completed state |
| **Accent (NERV Blue)** | `#0080FF` | Information, in-progress state |
| **Border** | `#1A1A1A` | Panel and window borders |
| **Border Active** | `#FF6B00` | Active focus indicator |
The palette constants in `src/theme_nerv.py:8-13` (computed from RGB triples via `_c()`):
The accent colors are the iconic NERV orange, red, green, and blue — visible against the black void without being saturated to the point of eye strain.
| Constant | RGB (0-255) | Hex | Usage |
|---|---|---|---|
| `BLACK` | (0, 0, 0) | `#000000` | Primary window, panel, popup, frame, title, menu, scrollbar, button, tab backgrounds |
| `STEEL` | (224, 224, 216) | `#E0E0D8` | Text, separator, scrollbar-grab-hovered, resize-grip-hovered |
| `NERV_ORANGE` | (255, 152, 48) | `#FF9830` | Border, scrollbar-grab, resize-grip, nav-cursor, border-shadow alpha=0 |
| `ALERT_RED` | (255, 72, 64) | `#FF4840` | Used as 1.0,0.0,0.0 with sin-pulsed alpha in `AlertPulsing.render` (not as a steady color in `NERV_PALETTE`) |
| `DATA_GREEN` | (80, 255, 80) | `#50FF50` | Check-mark, slider-grab-active, plot-histogram, drag-drop-target, nav-windowing-highlight |
| `WIRE_CYAN` | (32, 240, 255) | `#20F0FF` | Scrollbar-grab-active, slider-grab, plot-lines, separator-hovered, resize-grip-active |
The full `NERV_PALETTE` dict (47 `imgui.Col_` entries, `src/theme_nerv.py:15-63`) maps each constant to the relevant imgui style color. The "header/header-hovered/header-active" colors are semi-transparent NERV orange (alphas 60, 100, 140) used for table-header and tree-node selection.
### Geometry
@@ -70,118 +68,113 @@ This produces a "military hardware" feel that contrasts with consumer-app aesthe
### Status Flickering
The active tier indicator in the MMA Dashboard, and other "operational status" elements, flicker at a low frequency (default: 0.5 Hz, configurable). The flicker is implemented as a sine wave on the alpha channel:
The `StatusFlicker.get_alpha()` method in `src/theme_nerv_fx.py:67-73` returns a value in the range [0.7, 1.0] from a sine wave:
```python
alpha = 0.7 + 0.3 * math.sin(time.monotonic() * 2 * math.pi * 0.5)
return 0.85 + 0.15 * math.sin(time.time() * 20.0)
```
This creates a subtle "tactical display" effect without being distracting.
The angular frequency is hard-coded (`20.0` rad/s ≈ **3.18 Hz**, not 0.5 Hz) and the method is currently a utility — there is no production caller wiring it into a tier indicator at the time of writing.
### CRT Scanlines
A repeating horizontal line overlay applied via the `custom_background` callback. The scanlines are drawn at 2-pixel intervals, with the scanline alpha at ~30% by default (configurable).
`CRTFilter.render(width, height)` in `src/theme_nerv_fx.py:8-65` is invoked once per frame from `theme_2.render_post_fx()` (at `src/theme_2.py:400-408`) when NERV is active. The render method:
**Implementation**: Uses the FBO pipeline described in [guide_shaders_and_window.md#1-shader-injection-strategy](guide_shaders_and_window.md#1-shader-injection-strategy). The scanline pass is a fragment shader that modulates output color based on the screen-space Y coordinate.
1. Draws 1.2px (major, every 4 lines) and 0.8px (minor, every 2 lines) horizontal black lines at alphas 0.08 / 0.04
2. Draws 1.0px vertical black lines every 3px at alpha 0.05 (simulated aperture-grille shadow mask)
3. Draws 20 nested rounded-rectangle outlines with exponentially increasing alpha (max 0.25) for vignette/tube-curvature
4. Adds 40 small white noise pixels per frame with flickering alpha (0.01-0.04) modulated by a 60 Hz sin wave
**Performance cost**: ~1-2ms per frame on a 1920×1080 display with default settings.
The scanline alphas and noise density are **hard-coded** in the FX source — there is no config knob for them.
**Performance cost**: ~1-2ms per frame on a 1920×1080 display with default settings (per the original 2026-05 design estimate; the new shader-less draw-list implementation in `src/theme_nerv_fx.py` may be faster).
### Alert Animations
When MMA state transitions to `"blocked"` or `"error"`, a red border pulse animation runs for ~1.5 seconds:
`AlertPulsing` in `src/theme_nerv_fx.py:75-97` is a stateful on/off border overlay:
1. At t=0, the border alpha spikes to 1.0.
2. Over 1.5 seconds, the alpha decays exponentially to 0.1.
3. The border color is the NERV red (`#E50000`).
1. `update(status: str)` at `:79-83` sets `self.active = status.lower().startswith("error")` — i.e. any AI status string that begins with `"error"` (case-insensitive) triggers the pulse. There is no MMA-specific state-machine check; the call site at `src/theme_2.py:405` passes the live `ai_status` from the controller.
2. `render(width, height)` at `:85-97` is a no-op while `not self.active`. When active, it draws a full-screen red rectangle with alpha pulsing at `0.05 + 0.15 * (sin(t·4) + 1) / 2` (so alpha oscillates 0.05 ↔ 0.20 at ~0.64 Hz, no decay).
3. The pulse persists for the entire duration that `ai_status` starts with `"error"`; it does not have a 1.5-second auto-decay. There is no `alert_pulse_duration_seconds` config.
This makes operational failures immediately visible without requiring the user to look at a specific panel.
The color is the constant `(1.0, 0.0, 0.0)` in `src/theme_nerv_fx.py:96` (pure red, not the `ALERT_RED` constant), and the thickness is 10px.
---
## Implementation
### `src/theme_nerv.py` — Base Theme
### `src/theme_nerv.py` — Base Theme (88 lines)
Defines the color palette, geometry overrides, and font selection. The application function applies these to the active ImGui style.
Defines the 6 color constants (lines 8-13), the `NERV_PALETTE` dict (lines 15-63, 47 `imgui.Col_` entries), and the `apply_nerv()` function (lines 65-87). The actual `apply_nerv()` is a tight loop over the palette dict, not a hand-written sequence of `style.colors[col] = ...` assignments:
```python
def apply_nerv() -> None:
style = imgui.get_style()
style.colors[imgui.Col_.window_bg] = (0.0, 0.0, 0.0, 1.0)
style.colors[imgui.Col_.text] = (0.88, 0.88, 0.88, 1.0)
# ... (all other colors per the palette table)
style.window_rounding = 0.0
style.frame_rounding = 0.0
style.child_rounding = 0.0
# ... (geometry overrides)
# Apply fonts
for col_enum, rgba in NERV_PALETTE.items():
style.set_color_(col_enum, imgui.ImVec4(*rgba))
# Hard Edges
style.window_rounding = 0.0
style.child_rounding = 0.0
style.frame_rounding = 0.0
style.popup_rounding = 0.0
style.scrollbar_rounding = 0.0
style.grab_rounding = 0.0
style.tab_rounding = 0.0
# Border sizes
style.window_border_size = 1.0
style.frame_border_size = 1.0
style.popup_border_size = 1.0
style.child_border_size = 1.0
style.tab_border_size = 1.0
```
### `src/theme_nerv_fx.py` — Visual Effects
### `src/theme_nerv_fx.py` — Visual Effects (97 lines)
Implements the per-frame effects that go beyond static styling:
Three classes, no module-level entry point:
- **Status flickering**: Sine-wave alpha modulation for active-state indicators.
- **CRT scanlines**: Custom background shader (delegated to `bg_shader.py`).
- **Alert animations**: Time-based border pulse on state transitions.
- `CRTFilter` (lines 8-65) — `__init__(self)` sets `self.enabled = True`; `render(self, width, height)` is the scanline/vignette/noise overlay. The `enabled` flag is the only runtime toggle; the GUI controls it via the `crt_enabled` parameter to `theme_2.render_post_fx()`.
- `StatusFlicker` (lines 67-73) — `get_alpha(self)` returns a hard-coded 3.18 Hz sine. Currently a utility; no production caller (see §"Status Flickering" above).
- `AlertPulsing` (lines 75-97) — `__init__(self)` sets `self.active = False`; `update(self, status)` enables the pulse if `status.lower().startswith("error")`; `render(self, width, height)` is the no-op-or-pulse draw.
There is no `render_nerv_fx(fx_state: dict)` aggregator. The actual entry point that wires the FX layer into the GUI is `theme_2.render_post_fx(width, height, ai_status, crt_enabled)` at `src/theme_2.py:400-408`:
```python
def render_nerv_fx(fx_state: dict) -> None:
"""Per-frame NERV FX. Called from the main render loop when NERV is active."""
if not fx_state.get("fx_enabled", True):
return
render_scanlines(fx_state.get("scanline_alpha", 0.3))
render_status_flicker(fx_state.get("flicker_rate_hz", 0.5))
render_alert_pulse(fx_state.get("alert_state"))
def render_post_fx(width: float, height: float, ai_status: str, crt_enabled: bool) -> None:
"""Updates and renders the alert and CRT filters."""
theme_nerv_fx = _require_warmed("src.theme_nerv_fx")
alert_pulsing = theme_nerv_fx.AlertPulsing()
crt_filter = theme_nerv_fx.CRTFilter()
alert_pulsing.update(ai_status)
alert_pulsing.render(width, height)
crt_filter.enabled = crt_enabled
crt_filter.render(width, height)
```
### `src/bg_shader.py` — Background Shader
The custom background shader (used for scanlines) is in `src/bg_shader.py`. It uses the FBO pipeline described in the Shaders & Window guide.
Note: `theme_2.py:111` has a code comment saying "NERV FX objects (CRTFilter, AlertPulsing, StatusFlicker) are now created [in `render_post_fx`]" — this confirms the per-frame create-and-discard pattern. The previous design (FX as long-lived module-level singletons) is no longer in use.
### Activation
The NERV theme is activated via the GUI's theme picker:
```
User Menu → Theme → NERV
```
Or programmatically:
The NERV theme is activated via the GUI's theme picker (NERV is one of the 4 built-in syntax palettes selectable via the multi-theme TOML system). The `apply_nerv()` call is wired into the theme-switch path of `theme_2.py`. To switch programmatically:
```python
from src.theme_nerv import apply_nerv
apply_nerv()
```
When activated, the active color set, geometry style, and font are swapped. The FX layer is enabled/disabled via the `[nerv].fx_enabled` config flag (default: enabled).
The FX layer's `CRTFilter.enabled` is set per-frame by the caller of `theme_2.render_post_fx(crt_enabled=...)` — there is no `[nerv].fx_enabled` config key (see §"Configuration" below for why).
---
## Configuration
`config.toml`:
**There is no `[nerv]` config section in `config.toml`.** As of 2026-06-10, the NERV FX layer's parameters (scanline alpha, flicker frequency, alert pulse color, alert pulse duration) are all hard-coded in `src/theme_nerv_fx.py` and have no TOML or env-var override. The only runtime toggles are:
```toml
[nerv]
fx_enabled = true # Master toggle for all FX
scanline_alpha = 0.3 # 0.0 (none) to 1.0 (opaque)
flicker_rate_hz = 0.5 # Flicker frequency for active-state indicators
alert_pulse_duration_seconds = 1.5 # How long the red border pulse runs
alert_pulse_color = "#E50000" # Hex color for the alert pulse
```
- **`CRTFilter.enabled`** — set per-frame by the caller of `theme_2.render_post_fx(crt_enabled=...)`; the GUI uses the NERV theme's "FX enabled" toggle to drive this.
- **`AlertPulsing.active`** — set automatically by `update(ai_status)` to `True` if `ai_status.lower().startswith("error")`.
| Key | Default | Description |
|---|---|---|
| `fx_enabled` | `true` | Master toggle. When `false`, no scanlines, no flickering, no alerts. |
| `scanline_alpha` | `0.3` | Alpha of the scanline overlay. `0.0` disables scanlines; `1.0` makes them solid (usually too aggressive). |
| `flicker_rate_hz` | `0.5` | Frequency of status flickering. `0.0` disables. Values > `2.0` become seizure-inducing. |
| `alert_pulse_duration_seconds` | `1.5` | How long the red border pulse runs on errors. `0.0` disables. |
| `alert_pulse_color` | `"#E50000"` | Hex color of the alert pulse. |
### Per-Project Override
Projects can override the NERV settings in `manual_slop.toml` under a `[theme_nerv]` section (with the same keys). The project override wins over the global `config.toml`.
**Earlier versions of this guide** (pre-2026-06-10) documented a `[nerv]` section with `fx_enabled`, `scanline_alpha`, `flicker_rate_hz`, `alert_pulse_duration_seconds`, and `alert_pulse_color` keys. None of those keys were ever wired into the source code — they were aspirational documentation, not configuration. This is the "stale doc" that the docs-sync track on 2026-06-10 surfaced and removed.
---