feat(ui): character status dock with per-chip threat state#63
Conversation
Adds a horizontal strip of CharacterChip widgets above the preview grid: each chip shows a colored-initials avatar (deterministic accent per character), the character name, current system, and a threat-tint dot fed from the same intel pipeline as the preview borders (PR1). Clicking a chip activates the matching window via the existing focus path. StatusDock owns chip lifecycle (add/remove/clear/sync) and fans threat state to all chips. MainTab._sync_status_dock mirrors window_manager state on every add/remove site. MainWindowV21._on_intel_alert pushes VISUAL_BORDER alerts to the dock alongside the existing preview-frame fan-out, so border + chip dot stay in lock-step. Visibility gated on thumbnails.show_status_dock (default true). Per-character system tracking still uses the shared parser current_system for now — the chip already accepts a system arg so per-char tracking can land later without API changes. 35 new tests in tests/test_status_dock.py + 1 updated existing setup-ui test for the +1 addWidget call. Suite: 2239 passed, 5 skipped. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Code Review
This pull request introduces the StatusDock component, which provides a horizontal strip of character chips displaying character avatars, system names, and threat indicators. The changes include the implementation of the StatusDock and CharacterChip widgets, integration into the main tab and intel alert pipeline, and comprehensive unit tests. Feedback focuses on ensuring deterministic character accent colors by replacing the non-deterministic hash() function, adjusting layout margins to prevent visual overlap between threat dots and system labels, and implementing threat decay logic to synchronize behavior with the preview frames.
|
|
||
| def accent_for(name: str) -> QColor: | ||
| """Deterministic accent color for a character name.""" | ||
| r, g, b = CHIP_ACCENT_COLORS[abs(hash(name)) % len(CHIP_ACCENT_COLORS)] |
There was a problem hiding this comment.
The use of Python's built-in hash() function is not deterministic across different application sessions because of hash randomization (salting) introduced in Python 3.3. This means character accent colors will change every time the application is restarted, which contradicts the goal of having a stable accent palette as stated in the comments. Consider using a stable hashing method like a sum of character codes or zlib.adler32 to ensure consistency.
| r, g, b = CHIP_ACCENT_COLORS[abs(hash(name)) % len(CHIP_ACCENT_COLORS)] | |
| r, g, b = CHIP_ACCENT_COLORS[sum(ord(c) for c in name) % len(CHIP_ACCENT_COLORS)] |
| self._apply_base_style() | ||
|
|
||
| layout = QHBoxLayout(self) | ||
| layout.setContentsMargins(6, 4, 8, 4) |
There was a problem hiding this comment.
The current right margin of 8px is insufficient to prevent the threat dot (which is 10px wide and offset by 8px from the right edge) from overlapping with the _system_label text, especially since the label is right-aligned. Increasing the right margin to 24px will ensure the dot is drawn in the margin area and does not obscure the system name.
| layout.setContentsMargins(6, 4, 8, 4) | |
| layout.setContentsMargins(6, 4, 24, 4) |
| def set_threat_state( | ||
| self, level: ThreatLevel | None, system: str | None = None, alpha: float = 1.0 | ||
| ) -> None: | ||
| if level is None or level == ThreatLevel.CLEAR: | ||
| self._threat_level = None | ||
| self._threat_alpha = 0.0 | ||
| else: | ||
| self._threat_level = level | ||
| self._threat_alpha = max(0.0, min(1.0, alpha)) | ||
| if system is not None: | ||
| self.set_system(system) | ||
| else: | ||
| self.setToolTip(self._tooltip_text()) | ||
| self.update() | ||
|
|
There was a problem hiding this comment.
The CharacterChip lacks the threat decay logic implemented in WindowPreviewWidget. While the preview borders fade over time (via THREAT_DECAY_DURATION_MS), the threat dots in the status dock will remain at full opacity until a CLEAR report is received or the character changes systems. This breaks the 'lock-step' behavior mentioned in the PR description. Consider implementing a similar decay timer here or passing the current alpha from the preview frames if they are intended to be perfectly synchronized.
…' into feat/character-status-dock
Bumps version to 3.2.0 and documents the 10-PR intel-aware UI arc that landed via #74. Also fixes the pre-existing version drift between pyproject.toml (3.1.2) and __init__.py (3.0.4) by syncing both to 3.2.0. CHANGELOG entry covers: - Intel-aware preview borders (PR #62) - Character status dock (PR #63) - Preview focus mode (PR #64) - Per-character system tracking from EVE Local logs (PR #65) - Smart per-character threat fan-out (PR #66) - Jumps-from fan-out with adjacency falloff (PR #67) - "+Nj" distance badges on chips + frames (PR #68 + PR #71) - Per-character accent border (PR #70) - Toggleable replay strip with frame scrubbing (PR #72) Plus the supporting infrastructure (intel/threat_filter, accent palette promotion, set_threat_state kwarg additions) and the new settings keys that gate the features. Co-authored-by: AreteDriver <AreteDriver@users.noreply.github.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Summary
Why this matters
EVE-O Preview cannot show per-character system or threat state — it has no access to the chat-log intel parser running in this process. The dock makes that data first-class, glanceable, and clickable. It's also the foundation for the next round of differentiators (focus mode, system-cluster grouping, etc.).
Architecture notes
Test plan
Follow-ups
🤖 Generated with Claude Code