@delorenj/claude-notifications 2.0.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/DO.md +5 -0
- package/FIXES-APPLIED.md +195 -0
- package/INTEGRATION.md +445 -0
- package/LAYOUT-INTEGRATION.md +191 -0
- package/QUICK-REFERENCE.md +195 -0
- package/README.md +145 -14
- package/TASK.md +15 -0
- package/ZELLIJ-NOTIFY.md +523 -0
- package/_bmad-output/implementation-artifacts/spec-install-multi-cli-hooks.md +241 -0
- package/bin/claude-notifications.js +417 -312
- package/bin/claude-notify.js +47 -1
- package/bin/zellij-notify.js +346 -0
- package/bun.lock +35 -0
- package/diagnose-zellij.sh +105 -0
- package/examples/settings-with-zellij.json +18 -0
- package/examples/settings-zellij-only.json +18 -0
- package/examples/zellij-notify-examples.sh +143 -0
- package/lib/adapters/_stub.js +35 -0
- package/lib/adapters/auggie.js +10 -0
- package/lib/adapters/claude-code.js +181 -0
- package/lib/adapters/codex.js +10 -0
- package/lib/adapters/copilot.js +10 -0
- package/lib/adapters/gemini.js +10 -0
- package/lib/adapters/index.js +240 -0
- package/lib/adapters/kimi.js +10 -0
- package/lib/adapters/opencode.js +14 -0
- package/lib/adapters/vibe.js +10 -0
- package/lib/config.js +44 -8
- package/lib/tui.js +115 -0
- package/lib/zellij.js +248 -0
- package/package.json +6 -4
- package/postinstall.js +28 -25
- package/preuninstall.js +18 -9
- package/test/adapters/claude-code.test.js +144 -0
- package/test/adapters/patches.test.js +81 -0
- package/test/adapters/registry.test.js +89 -0
- package/test/adapters/stubs.test.js +46 -0
- package/test/cli-json.test.js +79 -0
- package/test/helpers/fake-fs.js +59 -0
- package/test-integration.sh +113 -0
- package/test-notification-plugin.kdl +34 -0
- package/test-updated-layout.sh +75 -0
- package/test-zellij-cli.sh +72 -0
- package/zellij-plugin/.cargo/config.toml +5 -0
- package/zellij-plugin/.github/workflows/ci.yml +97 -0
- package/zellij-plugin/Cargo.lock +3558 -0
- package/zellij-plugin/Cargo.toml +40 -0
- package/zellij-plugin/README.md +290 -0
- package/zellij-plugin/build.sh +179 -0
- package/zellij-plugin/configs/examples/accessibility.kdl +31 -0
- package/zellij-plugin/configs/examples/catppuccin.kdl +32 -0
- package/zellij-plugin/configs/examples/default.kdl +34 -0
- package/zellij-plugin/configs/examples/minimal.kdl +22 -0
- package/zellij-plugin/docs/CONFIGURATION.md +191 -0
- package/zellij-plugin/docs/INTEGRATION.md +333 -0
- package/zellij-plugin/src/animation.rs +451 -0
- package/zellij-plugin/src/colors.rs +407 -0
- package/zellij-plugin/src/config.rs +664 -0
- package/zellij-plugin/src/event_bridge.rs +339 -0
- package/zellij-plugin/src/main.rs +420 -0
- package/zellij-plugin/src/notification.rs +466 -0
- package/zellij-plugin/src/queue.rs +399 -0
- package/zellij-plugin/src/renderer.rs +477 -0
- package/zellij-plugin/src/state.rs +338 -0
- package/zellij-plugin/src/tests.rs +413 -0
- package/.claude/checkpoints/1756392335.json +0 -1
- package/.claude/checkpoints/1756392341.json +0 -1
- package/.claude/checkpoints/1756392347.json +0 -1
- package/.claude/checkpoints/1756392376.json +0 -1
- package/.claude/checkpoints/1756392377.json +0 -1
- package/.claude/checkpoints/1756392386.json +0 -1
- package/.claude/checkpoints/1756392387.json +0 -1
- package/.claude/checkpoints/1756392398.json +0 -1
- package/.claude/checkpoints/1756392400.json +0 -1
- package/.claude/checkpoints/1756392427.json +0 -1
- package/.claude/checkpoints/1756392428.json +0 -1
- package/.claude/checkpoints/1756392486.json +0 -1
- package/.claude/checkpoints/1756392488.json +0 -1
- package/.claude/checkpoints/1756392558.json +0 -1
- package/.claude/checkpoints/1756392559.json +0 -1
- package/.claude/checkpoints/summary-session-20250828-105040.md +0 -57
- package/.claude/checkpoints/task-1756392207.json +0 -1
- package/.claude/checkpoints/task-1756392742.json +0 -1
- package/.claude/commands/analysis/COMMAND_COMPLIANCE_REPORT.md +0 -54
- package/.claude/commands/analysis/README.md +0 -9
- package/.claude/commands/analysis/bottleneck-detect.md +0 -162
- package/.claude/commands/analysis/performance-bottlenecks.md +0 -59
- package/.claude/commands/analysis/performance-report.md +0 -25
- package/.claude/commands/analysis/token-efficiency.md +0 -45
- package/.claude/commands/analysis/token-usage.md +0 -25
- package/.claude/commands/automation/README.md +0 -9
- package/.claude/commands/automation/auto-agent.md +0 -122
- package/.claude/commands/automation/self-healing.md +0 -106
- package/.claude/commands/automation/session-memory.md +0 -90
- package/.claude/commands/automation/smart-agents.md +0 -73
- package/.claude/commands/automation/smart-spawn.md +0 -25
- package/.claude/commands/automation/workflow-select.md +0 -25
- package/.claude/commands/coordination/README.md +0 -9
- package/.claude/commands/coordination/agent-spawn.md +0 -25
- package/.claude/commands/coordination/init.md +0 -44
- package/.claude/commands/coordination/orchestrate.md +0 -43
- package/.claude/commands/coordination/spawn.md +0 -45
- package/.claude/commands/coordination/swarm-init.md +0 -85
- package/.claude/commands/coordination/task-orchestrate.md +0 -25
- package/.claude/commands/github/README.md +0 -11
- package/.claude/commands/github/code-review-swarm.md +0 -514
- package/.claude/commands/github/code-review.md +0 -25
- package/.claude/commands/github/github-modes.md +0 -147
- package/.claude/commands/github/github-swarm.md +0 -121
- package/.claude/commands/github/issue-tracker.md +0 -292
- package/.claude/commands/github/issue-triage.md +0 -25
- package/.claude/commands/github/multi-repo-swarm.md +0 -519
- package/.claude/commands/github/pr-enhance.md +0 -26
- package/.claude/commands/github/pr-manager.md +0 -170
- package/.claude/commands/github/project-board-sync.md +0 -471
- package/.claude/commands/github/release-manager.md +0 -338
- package/.claude/commands/github/release-swarm.md +0 -544
- package/.claude/commands/github/repo-analyze.md +0 -25
- package/.claude/commands/github/repo-architect.md +0 -367
- package/.claude/commands/github/swarm-issue.md +0 -482
- package/.claude/commands/github/swarm-pr.md +0 -285
- package/.claude/commands/github/sync-coordinator.md +0 -301
- package/.claude/commands/github/workflow-automation.md +0 -442
- package/.claude/commands/hooks/README.md +0 -11
- package/.claude/commands/hooks/overview.md +0 -58
- package/.claude/commands/hooks/post-edit.md +0 -117
- package/.claude/commands/hooks/post-task.md +0 -112
- package/.claude/commands/hooks/pre-edit.md +0 -113
- package/.claude/commands/hooks/pre-task.md +0 -111
- package/.claude/commands/hooks/session-end.md +0 -118
- package/.claude/commands/hooks/setup.md +0 -103
- package/.claude/commands/memory/README.md +0 -9
- package/.claude/commands/memory/memory-persist.md +0 -25
- package/.claude/commands/memory/memory-search.md +0 -25
- package/.claude/commands/memory/memory-usage.md +0 -25
- package/.claude/commands/memory/neural.md +0 -47
- package/.claude/commands/memory/usage.md +0 -46
- package/.claude/commands/monitoring/README.md +0 -9
- package/.claude/commands/monitoring/agent-metrics.md +0 -25
- package/.claude/commands/monitoring/agents.md +0 -44
- package/.claude/commands/monitoring/real-time-view.md +0 -25
- package/.claude/commands/monitoring/status.md +0 -46
- package/.claude/commands/monitoring/swarm-monitor.md +0 -25
- package/.claude/commands/optimization/README.md +0 -9
- package/.claude/commands/optimization/auto-topology.md +0 -62
- package/.claude/commands/optimization/cache-manage.md +0 -25
- package/.claude/commands/optimization/parallel-execute.md +0 -25
- package/.claude/commands/optimization/parallel-execution.md +0 -50
- package/.claude/commands/optimization/topology-optimize.md +0 -25
- package/.claude/commands/pair/README.md +0 -261
- package/.claude/commands/pair/commands.md +0 -546
- package/.claude/commands/pair/config.md +0 -510
- package/.claude/commands/pair/examples.md +0 -512
- package/.claude/commands/pair/modes.md +0 -348
- package/.claude/commands/pair/session.md +0 -407
- package/.claude/commands/pair/start.md +0 -209
- package/.claude/commands/sparc/analyzer.md +0 -52
- package/.claude/commands/sparc/architect.md +0 -53
- package/.claude/commands/sparc/batch-executor.md +0 -54
- package/.claude/commands/sparc/coder.md +0 -54
- package/.claude/commands/sparc/debugger.md +0 -54
- package/.claude/commands/sparc/designer.md +0 -53
- package/.claude/commands/sparc/documenter.md +0 -54
- package/.claude/commands/sparc/innovator.md +0 -54
- package/.claude/commands/sparc/memory-manager.md +0 -54
- package/.claude/commands/sparc/optimizer.md +0 -54
- package/.claude/commands/sparc/orchestrator.md +0 -132
- package/.claude/commands/sparc/researcher.md +0 -54
- package/.claude/commands/sparc/reviewer.md +0 -54
- package/.claude/commands/sparc/sparc-modes.md +0 -174
- package/.claude/commands/sparc/swarm-coordinator.md +0 -54
- package/.claude/commands/sparc/tdd.md +0 -54
- package/.claude/commands/sparc/tester.md +0 -54
- package/.claude/commands/sparc/workflow-manager.md +0 -54
- package/.claude/commands/stream-chain/pipeline.md +0 -121
- package/.claude/commands/stream-chain/run.md +0 -70
- package/.claude/commands/swarm/analysis.md +0 -95
- package/.claude/commands/swarm/development.md +0 -96
- package/.claude/commands/swarm/examples.md +0 -168
- package/.claude/commands/swarm/maintenance.md +0 -102
- package/.claude/commands/swarm/optimization.md +0 -117
- package/.claude/commands/swarm/research.md +0 -136
- package/.claude/commands/swarm/testing.md +0 -131
- package/.claude/commands/training/README.md +0 -9
- package/.claude/commands/training/model-update.md +0 -25
- package/.claude/commands/training/neural-patterns.md +0 -74
- package/.claude/commands/training/neural-train.md +0 -25
- package/.claude/commands/training/pattern-learn.md +0 -25
- package/.claude/commands/training/specialization.md +0 -63
- package/.claude/commands/truth/start.md +0 -143
- package/.claude/commands/verify/check.md +0 -50
- package/.claude/commands/verify/start.md +0 -128
- package/.claude/commands/workflows/README.md +0 -9
- package/.claude/commands/workflows/development.md +0 -78
- package/.claude/commands/workflows/research.md +0 -63
- package/.claude/commands/workflows/workflow-create.md +0 -25
- package/.claude/commands/workflows/workflow-execute.md +0 -25
- package/.claude/commands/workflows/workflow-export.md +0 -25
- package/.claude/config.json +0 -36
- package/.claude/settings.json +0 -162
- package/.claude-flow/metrics/agent-metrics.json +0 -1
- package/.claude-flow/metrics/performance.json +0 -9
- package/.claude-flow/metrics/system-metrics.json +0 -230
- package/.claude-flow/metrics/task-metrics.json +0 -10
- package/FIXES.md +0 -75
- package/test-results.md +0 -163
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
//! State management module for Zellij Visual Notifications
|
|
2
|
+
//!
|
|
3
|
+
//! Manages visual states for panes and the overall plugin state machine.
|
|
4
|
+
|
|
5
|
+
use serde::{Deserialize, Serialize};
|
|
6
|
+
use crate::config::AnimationStyle;
|
|
7
|
+
use crate::notification::NotificationType;
|
|
8
|
+
|
|
9
|
+
/// Plugin lifecycle state
|
|
10
|
+
#[derive(Debug, Clone, PartialEq, Default)]
|
|
11
|
+
pub enum PluginState {
|
|
12
|
+
/// Plugin is initializing
|
|
13
|
+
#[default]
|
|
14
|
+
Initializing,
|
|
15
|
+
/// Plugin is initialized and waiting for permissions
|
|
16
|
+
Initialized,
|
|
17
|
+
/// Plugin is running normally
|
|
18
|
+
Running,
|
|
19
|
+
/// Plugin is in fallback mode (limited functionality)
|
|
20
|
+
FallbackMode,
|
|
21
|
+
/// Plugin encountered an error
|
|
22
|
+
Error(String),
|
|
23
|
+
/// Plugin is shutting down
|
|
24
|
+
ShuttingDown,
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/// Visual state for a single pane
|
|
28
|
+
#[derive(Debug, Clone, Default)]
|
|
29
|
+
pub struct VisualState {
|
|
30
|
+
/// Current state of visual notification
|
|
31
|
+
pub state: VisualNotificationState,
|
|
32
|
+
/// Border color (hex string)
|
|
33
|
+
pub border_color: Option<String>,
|
|
34
|
+
/// Badge icon (Unicode character)
|
|
35
|
+
pub badge_icon: Option<String>,
|
|
36
|
+
/// Whether animation is currently active
|
|
37
|
+
pub is_animating: bool,
|
|
38
|
+
/// Animation start tick
|
|
39
|
+
pub animation_start_tick: u64,
|
|
40
|
+
/// Current animation phase (0.0 - 1.0)
|
|
41
|
+
pub animation_phase: f32,
|
|
42
|
+
/// Animation style for this notification
|
|
43
|
+
pub animation_style: AnimationStyle,
|
|
44
|
+
/// Notification message
|
|
45
|
+
pub notification_message: Option<String>,
|
|
46
|
+
/// Notification type
|
|
47
|
+
pub notification_type: Option<NotificationType>,
|
|
48
|
+
/// Timestamp when notification was received
|
|
49
|
+
pub notification_timestamp: u64,
|
|
50
|
+
/// Whether the notification has been acknowledged
|
|
51
|
+
pub acknowledged: bool,
|
|
52
|
+
/// Brightness multiplier for animation (0.0 - 1.0)
|
|
53
|
+
pub brightness: f32,
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
impl VisualState {
|
|
57
|
+
/// Create a new visual state
|
|
58
|
+
pub fn new() -> Self {
|
|
59
|
+
Self {
|
|
60
|
+
state: VisualNotificationState::Idle,
|
|
61
|
+
border_color: None,
|
|
62
|
+
badge_icon: None,
|
|
63
|
+
is_animating: false,
|
|
64
|
+
animation_start_tick: 0,
|
|
65
|
+
animation_phase: 0.0,
|
|
66
|
+
animation_style: AnimationStyle::Pulse,
|
|
67
|
+
notification_message: None,
|
|
68
|
+
notification_type: None,
|
|
69
|
+
notification_timestamp: 0,
|
|
70
|
+
acknowledged: false,
|
|
71
|
+
brightness: 1.0,
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/// Clear the visual state
|
|
76
|
+
pub fn clear(&mut self) {
|
|
77
|
+
self.state = VisualNotificationState::Idle;
|
|
78
|
+
self.border_color = None;
|
|
79
|
+
self.badge_icon = None;
|
|
80
|
+
self.is_animating = false;
|
|
81
|
+
self.animation_phase = 0.0;
|
|
82
|
+
self.notification_message = None;
|
|
83
|
+
self.notification_type = None;
|
|
84
|
+
self.acknowledged = false;
|
|
85
|
+
self.brightness = 1.0;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/// Check if this state has an active notification
|
|
89
|
+
pub fn has_notification(&self) -> bool {
|
|
90
|
+
self.notification_type.is_some() && !self.acknowledged
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/// Set the notification state
|
|
94
|
+
pub fn set_notification(
|
|
95
|
+
&mut self,
|
|
96
|
+
notification_type: NotificationType,
|
|
97
|
+
message: String,
|
|
98
|
+
border_color: String,
|
|
99
|
+
badge_icon: String,
|
|
100
|
+
) {
|
|
101
|
+
self.state = VisualNotificationState::Active;
|
|
102
|
+
self.notification_type = Some(notification_type);
|
|
103
|
+
self.notification_message = Some(message);
|
|
104
|
+
self.border_color = Some(border_color);
|
|
105
|
+
self.badge_icon = Some(badge_icon);
|
|
106
|
+
self.acknowledged = false;
|
|
107
|
+
self.brightness = 1.0;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/// Start fading animation
|
|
111
|
+
pub fn start_fade(&mut self, tick: u64) {
|
|
112
|
+
self.state = VisualNotificationState::Fading;
|
|
113
|
+
self.is_animating = true;
|
|
114
|
+
self.animation_start_tick = tick;
|
|
115
|
+
self.animation_phase = 0.0;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/// Acknowledge the notification
|
|
119
|
+
pub fn acknowledge(&mut self) {
|
|
120
|
+
self.acknowledged = true;
|
|
121
|
+
self.state = VisualNotificationState::Fading;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/// Visual notification state machine states
|
|
126
|
+
#[derive(Debug, Clone, PartialEq, Default)]
|
|
127
|
+
pub enum VisualNotificationState {
|
|
128
|
+
/// No active notification
|
|
129
|
+
#[default]
|
|
130
|
+
Idle,
|
|
131
|
+
/// Notification is pending (queued)
|
|
132
|
+
Pending,
|
|
133
|
+
/// Notification is active and displayed
|
|
134
|
+
Active,
|
|
135
|
+
/// Notification is fading out
|
|
136
|
+
Fading,
|
|
137
|
+
/// Error state
|
|
138
|
+
Error,
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
impl VisualNotificationState {
|
|
142
|
+
/// Check if state allows transitions
|
|
143
|
+
pub fn can_transition_to(&self, target: &VisualNotificationState) -> bool {
|
|
144
|
+
match (self, target) {
|
|
145
|
+
// From Idle
|
|
146
|
+
(VisualNotificationState::Idle, VisualNotificationState::Pending) => true,
|
|
147
|
+
(VisualNotificationState::Idle, VisualNotificationState::Active) => true,
|
|
148
|
+
// From Pending
|
|
149
|
+
(VisualNotificationState::Pending, VisualNotificationState::Active) => true,
|
|
150
|
+
(VisualNotificationState::Pending, VisualNotificationState::Idle) => true, // Cancel
|
|
151
|
+
// From Active
|
|
152
|
+
(VisualNotificationState::Active, VisualNotificationState::Fading) => true,
|
|
153
|
+
(VisualNotificationState::Active, VisualNotificationState::Idle) => true, // Instant clear
|
|
154
|
+
// From Fading
|
|
155
|
+
(VisualNotificationState::Fading, VisualNotificationState::Idle) => true,
|
|
156
|
+
(VisualNotificationState::Fading, VisualNotificationState::Active) => true, // New notification
|
|
157
|
+
// From Error
|
|
158
|
+
(VisualNotificationState::Error, VisualNotificationState::Idle) => true,
|
|
159
|
+
(VisualNotificationState::Error, VisualNotificationState::Active) => true,
|
|
160
|
+
// Same state (no-op)
|
|
161
|
+
(a, b) if a == b => true,
|
|
162
|
+
// All other transitions are invalid
|
|
163
|
+
_ => false,
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/// Get the display name for this state
|
|
168
|
+
pub fn display_name(&self) -> &'static str {
|
|
169
|
+
match self {
|
|
170
|
+
VisualNotificationState::Idle => "Idle",
|
|
171
|
+
VisualNotificationState::Pending => "Pending",
|
|
172
|
+
VisualNotificationState::Active => "Active",
|
|
173
|
+
VisualNotificationState::Fading => "Fading",
|
|
174
|
+
VisualNotificationState::Error => "Error",
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/// State transition event
|
|
180
|
+
#[derive(Debug, Clone)]
|
|
181
|
+
pub struct StateTransition {
|
|
182
|
+
/// Source state
|
|
183
|
+
pub from: VisualNotificationState,
|
|
184
|
+
/// Target state
|
|
185
|
+
pub to: VisualNotificationState,
|
|
186
|
+
/// Timestamp of transition
|
|
187
|
+
pub timestamp: u64,
|
|
188
|
+
/// Reason for transition
|
|
189
|
+
pub reason: String,
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
impl StateTransition {
|
|
193
|
+
/// Create a new state transition
|
|
194
|
+
pub fn new(from: VisualNotificationState, to: VisualNotificationState, reason: &str) -> Self {
|
|
195
|
+
Self {
|
|
196
|
+
from,
|
|
197
|
+
to,
|
|
198
|
+
timestamp: 0, // Will be set by the caller
|
|
199
|
+
reason: reason.to_string(),
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/// State manager for tracking multiple pane states
|
|
205
|
+
#[derive(Debug, Default)]
|
|
206
|
+
pub struct StateManager {
|
|
207
|
+
/// History of state transitions (for debugging)
|
|
208
|
+
transition_history: Vec<StateTransition>,
|
|
209
|
+
/// Maximum history size
|
|
210
|
+
max_history_size: usize,
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
impl StateManager {
|
|
214
|
+
/// Create a new state manager
|
|
215
|
+
pub fn new() -> Self {
|
|
216
|
+
Self {
|
|
217
|
+
transition_history: Vec::new(),
|
|
218
|
+
max_history_size: 100,
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/// Record a state transition
|
|
223
|
+
pub fn record_transition(&mut self, transition: StateTransition) {
|
|
224
|
+
self.transition_history.push(transition);
|
|
225
|
+
|
|
226
|
+
// Keep history bounded
|
|
227
|
+
while self.transition_history.len() > self.max_history_size {
|
|
228
|
+
self.transition_history.remove(0);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/// Get recent transitions
|
|
233
|
+
pub fn recent_transitions(&self, count: usize) -> &[StateTransition] {
|
|
234
|
+
let start = if self.transition_history.len() > count {
|
|
235
|
+
self.transition_history.len() - count
|
|
236
|
+
} else {
|
|
237
|
+
0
|
|
238
|
+
};
|
|
239
|
+
&self.transition_history[start..]
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/// Clear transition history
|
|
243
|
+
pub fn clear_history(&mut self) {
|
|
244
|
+
self.transition_history.clear();
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/// Pane-specific notification state for synchronization
|
|
249
|
+
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
250
|
+
pub struct PaneNotificationState {
|
|
251
|
+
/// Pane ID
|
|
252
|
+
pub pane_id: u32,
|
|
253
|
+
/// Current visual state name
|
|
254
|
+
pub state: String,
|
|
255
|
+
/// Active notification type (if any)
|
|
256
|
+
pub notification_type: Option<String>,
|
|
257
|
+
/// Active notification message (if any)
|
|
258
|
+
pub notification_message: Option<String>,
|
|
259
|
+
/// Whether notification is acknowledged
|
|
260
|
+
pub acknowledged: bool,
|
|
261
|
+
/// Timestamp of last update
|
|
262
|
+
pub last_update: u64,
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
impl From<&VisualState> for PaneNotificationState {
|
|
266
|
+
fn from(state: &VisualState) -> Self {
|
|
267
|
+
Self {
|
|
268
|
+
pane_id: 0, // Will be set by caller
|
|
269
|
+
state: state.state.display_name().to_string(),
|
|
270
|
+
notification_type: state.notification_type.as_ref().map(|t| t.name().to_string()),
|
|
271
|
+
notification_message: state.notification_message.clone(),
|
|
272
|
+
acknowledged: state.acknowledged,
|
|
273
|
+
last_update: state.notification_timestamp,
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
#[cfg(test)]
|
|
279
|
+
mod tests {
|
|
280
|
+
use super::*;
|
|
281
|
+
|
|
282
|
+
#[test]
|
|
283
|
+
fn test_visual_state_default() {
|
|
284
|
+
let state = VisualState::default();
|
|
285
|
+
assert_eq!(state.state, VisualNotificationState::Idle);
|
|
286
|
+
assert!(!state.has_notification());
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
#[test]
|
|
290
|
+
fn test_visual_state_clear() {
|
|
291
|
+
let mut state = VisualState::new();
|
|
292
|
+
state.border_color = Some("#ff0000".to_string());
|
|
293
|
+
state.badge_icon = Some("!".to_string());
|
|
294
|
+
state.is_animating = true;
|
|
295
|
+
|
|
296
|
+
state.clear();
|
|
297
|
+
|
|
298
|
+
assert_eq!(state.state, VisualNotificationState::Idle);
|
|
299
|
+
assert!(state.border_color.is_none());
|
|
300
|
+
assert!(state.badge_icon.is_none());
|
|
301
|
+
assert!(!state.is_animating);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
#[test]
|
|
305
|
+
fn test_state_transitions() {
|
|
306
|
+
let idle = VisualNotificationState::Idle;
|
|
307
|
+
let pending = VisualNotificationState::Pending;
|
|
308
|
+
let active = VisualNotificationState::Active;
|
|
309
|
+
let fading = VisualNotificationState::Fading;
|
|
310
|
+
|
|
311
|
+
assert!(idle.can_transition_to(&pending));
|
|
312
|
+
assert!(idle.can_transition_to(&active));
|
|
313
|
+
assert!(pending.can_transition_to(&active));
|
|
314
|
+
assert!(active.can_transition_to(&fading));
|
|
315
|
+
assert!(fading.can_transition_to(&idle));
|
|
316
|
+
|
|
317
|
+
// Invalid transitions
|
|
318
|
+
assert!(!pending.can_transition_to(&fading));
|
|
319
|
+
assert!(!idle.can_transition_to(&fading));
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
#[test]
|
|
323
|
+
fn test_state_manager_history() {
|
|
324
|
+
let mut manager = StateManager::new();
|
|
325
|
+
|
|
326
|
+
for i in 0..10 {
|
|
327
|
+
let transition = StateTransition::new(
|
|
328
|
+
VisualNotificationState::Idle,
|
|
329
|
+
VisualNotificationState::Active,
|
|
330
|
+
&format!("Test {}", i),
|
|
331
|
+
);
|
|
332
|
+
manager.record_transition(transition);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
let recent = manager.recent_transitions(5);
|
|
336
|
+
assert_eq!(recent.len(), 5);
|
|
337
|
+
}
|
|
338
|
+
}
|