Skip to main content

tui_dispatch_debug/debug/
actions.rs

1//! Debug actions and side effects
2
3/// Debug actions provided by tui-dispatch
4///
5/// These are framework-level debug actions that apps can map from their own
6/// action types via keybindings.
7#[derive(Debug, Clone, PartialEq, Eq)]
8pub enum DebugAction {
9    /// Toggle debug freeze mode on/off
10    Toggle,
11    /// Copy frozen frame to clipboard
12    CopyFrame,
13    /// Toggle state overlay
14    ToggleState,
15    /// Toggle action log overlay
16    ToggleActionLog,
17    /// Toggle components overlay
18    ToggleComponents,
19    /// Toggle mouse capture mode for cell inspection
20    ToggleMouseCapture,
21    /// Inspect cell at position (from mouse click)
22    InspectCell { column: u16, row: u16 },
23    /// Close current overlay
24    CloseOverlay,
25    /// Request a new frame capture
26    RequestCapture,
27    /// Scroll action log up
28    ActionLogScrollUp,
29    /// Scroll action log down
30    ActionLogScrollDown,
31    /// Scroll action log to top
32    ActionLogScrollTop,
33    /// Scroll action log to bottom
34    ActionLogScrollBottom,
35    /// Page up in action log
36    ActionLogPageUp,
37    /// Page down in action log
38    ActionLogPageDown,
39    /// Show detail for selected action
40    ActionLogShowDetail,
41    /// Go back from detail view to action log
42    ActionLogBackToList,
43    /// Show detail for selected state tree entry
44    StateTreeShowDetail,
45    /// Go back from state detail to state tree
46    StateTreeBackToTree,
47    /// Show detail for selected component
48    ComponentShowDetail,
49    /// Go back from component detail to components list
50    ComponentBackToList,
51}
52
53impl DebugAction {
54    /// Standard command names for keybinding lookup
55    pub const CMD_TOGGLE: &'static str = "debug.toggle";
56    pub const CMD_COPY_FRAME: &'static str = "debug.copy";
57    pub const CMD_TOGGLE_STATE: &'static str = "debug.state";
58    pub const CMD_TOGGLE_ACTION_LOG: &'static str = "debug.action_log";
59    pub const CMD_TOGGLE_COMPONENTS: &'static str = "debug.components";
60    pub const CMD_TOGGLE_MOUSE: &'static str = "debug.mouse";
61    pub const CMD_CLOSE_OVERLAY: &'static str = "debug.close";
62
63    /// Try to parse a command string into a debug action
64    pub fn from_command(cmd: &str) -> Option<Self> {
65        match cmd {
66            Self::CMD_TOGGLE => Some(Self::Toggle),
67            Self::CMD_COPY_FRAME => Some(Self::CopyFrame),
68            Self::CMD_TOGGLE_STATE => Some(Self::ToggleState),
69            Self::CMD_TOGGLE_ACTION_LOG => Some(Self::ToggleActionLog),
70            Self::CMD_TOGGLE_COMPONENTS => Some(Self::ToggleComponents),
71            Self::CMD_TOGGLE_MOUSE => Some(Self::ToggleMouseCapture),
72            Self::CMD_CLOSE_OVERLAY => Some(Self::CloseOverlay),
73            _ => None,
74        }
75    }
76
77    /// Get the command string for this action
78    pub fn command(&self) -> Option<&'static str> {
79        match self {
80            Self::Toggle => Some(Self::CMD_TOGGLE),
81            Self::CopyFrame => Some(Self::CMD_COPY_FRAME),
82            Self::ToggleState => Some(Self::CMD_TOGGLE_STATE),
83            Self::ToggleActionLog => Some(Self::CMD_TOGGLE_ACTION_LOG),
84            Self::ToggleComponents => Some(Self::CMD_TOGGLE_COMPONENTS),
85            Self::ToggleMouseCapture => Some(Self::CMD_TOGGLE_MOUSE),
86            Self::CloseOverlay => Some(Self::CMD_CLOSE_OVERLAY),
87            // These don't have command strings (triggered programmatically)
88            Self::InspectCell { .. }
89            | Self::RequestCapture
90            | Self::ActionLogScrollUp
91            | Self::ActionLogScrollDown
92            | Self::ActionLogScrollTop
93            | Self::ActionLogScrollBottom
94            | Self::ActionLogPageUp
95            | Self::ActionLogPageDown
96            | Self::ActionLogShowDetail
97            | Self::ActionLogBackToList
98            | Self::StateTreeShowDetail
99            | Self::StateTreeBackToTree
100            | Self::ComponentShowDetail
101            | Self::ComponentBackToList => None,
102        }
103    }
104}
105
106/// Side effects that the app needs to handle after debug actions
107///
108/// The `DebugLayer` returns these when processing actions that require
109/// app-level handling (clipboard access, queued action processing).
110#[derive(Debug)]
111pub enum DebugSideEffect<A> {
112    /// Process queued actions (when exiting debug mode)
113    ///
114    /// These actions were queued while the UI was frozen and should
115    /// now be dispatched through the normal action pipeline.
116    ProcessQueuedActions(Vec<A>),
117
118    /// Copy text to clipboard
119    ///
120    /// The app should use its preferred clipboard mechanism (OSC52, etc).
121    CopyToClipboard(String),
122}
123
124#[cfg(test)]
125mod tests {
126    use super::*;
127
128    #[test]
129    fn test_from_command() {
130        assert_eq!(
131            DebugAction::from_command("debug.toggle"),
132            Some(DebugAction::Toggle)
133        );
134        assert_eq!(
135            DebugAction::from_command("debug.copy"),
136            Some(DebugAction::CopyFrame)
137        );
138        assert_eq!(
139            DebugAction::from_command("debug.state"),
140            Some(DebugAction::ToggleState)
141        );
142        assert_eq!(
143            DebugAction::from_command("debug.action_log"),
144            Some(DebugAction::ToggleActionLog)
145        );
146        assert_eq!(
147            DebugAction::from_command("debug.components"),
148            Some(DebugAction::ToggleComponents)
149        );
150        assert_eq!(DebugAction::from_command("unknown"), None);
151    }
152
153    #[test]
154    fn test_command_roundtrip() {
155        let actions = [
156            DebugAction::Toggle,
157            DebugAction::CopyFrame,
158            DebugAction::ToggleState,
159            DebugAction::ToggleActionLog,
160            DebugAction::ToggleComponents,
161            DebugAction::ToggleMouseCapture,
162            DebugAction::CloseOverlay,
163        ];
164
165        for action in actions {
166            let cmd = action.command().expect("should have command");
167            let parsed = DebugAction::from_command(cmd).expect("should parse");
168            assert_eq!(parsed, action);
169        }
170    }
171
172    #[test]
173    fn test_scroll_actions_no_command() {
174        // Scroll actions are triggered programmatically, not via commands
175        assert!(DebugAction::ActionLogScrollUp.command().is_none());
176        assert!(DebugAction::ActionLogScrollDown.command().is_none());
177        assert!(DebugAction::ActionLogScrollTop.command().is_none());
178        assert!(DebugAction::ActionLogScrollBottom.command().is_none());
179    }
180}