Skip to content

Commit 92a30dd

Browse files
0xrinegadeclaude
andcommitted
fix(tui): Restore graph navigation in handle_key_event
The previous implementation broke graph node/edge navigation by directly manipulating viewport coordinates instead of using GraphInput enum. Fixed key bindings for Graph tab: - Arrow keys + h/j/k/l: GraphInput::Up/Down/Left/Right for node selection - Enter: GraphInput::HopToWallet to navigate to selected wallet - Space: GraphInput::Toggle to collapse/expand nodes - w/a/s/d: GraphInput::Pan* for viewport panning - +/-: GraphInput::ZoomIn/ZoomOut - [/]: GraphInput::IncreaseDepth/DecreaseDepth - /: GraphInput::StartSearch - n/N: GraphInput::SearchNext/SearchPrev - y: GraphInput::Copy - </> or ,/.: GraphInput::ScrollDetailUp/Down This ensures web input has feature parity with local terminal input. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
1 parent 43c53cd commit 92a30dd

File tree

2 files changed

+120
-38
lines changed

2 files changed

+120
-38
lines changed
28.9 KB
Binary file not shown.

src/utils/tui/app.rs

Lines changed: 120 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -2698,7 +2698,7 @@ impl OsvmApp {
26982698
}
26992699

27002700
/// Handle a key event (from either local terminal or web)
2701-
/// This is a simplified handler for web input - supports essential navigation keys
2701+
/// Mirrors the main event_loop key handling for consistent behavior
27022702
fn handle_key_event(&mut self, key: KeyEvent) {
27032703
match key.code {
27042704
// Quit/Escape - handle different contexts
@@ -2750,74 +2750,129 @@ impl OsvmApp {
27502750
KeyCode::Char('?') => {
27512751
self.show_help = !self.show_help;
27522752
}
2753-
// Scrolling - handle tab-specific scroll states
2753+
// Navigation - Up/k
27542754
KeyCode::Up | KeyCode::Char('k') if !self.chat_input_active => {
2755-
match self.active_tab {
2756-
TabIndex::Chat => { self.chat_scroll = self.chat_scroll.saturating_sub(1); }
2757-
TabIndex::Logs => { self.log_scroll = self.log_scroll.saturating_sub(1); }
2758-
TabIndex::Graph => {
2759-
// Pan up in graph view
2760-
if let Ok(mut graph) = self.wallet_graph.lock() {
2761-
let (cx, cy, zoom) = graph.viewport;
2762-
graph.viewport = (cx, cy + 5.0 / zoom, zoom);
2755+
if self.show_help {
2756+
self.help_scroll = self.help_scroll.saturating_sub(1);
2757+
} else {
2758+
match self.active_tab {
2759+
TabIndex::Chat => { self.chat_scroll = self.chat_scroll.saturating_sub(1); }
2760+
TabIndex::Dashboard => { self.ai_insights_scroll = self.ai_insights_scroll.saturating_sub(1); }
2761+
TabIndex::Logs => { self.log_scroll = self.log_scroll.saturating_sub(1); }
2762+
TabIndex::Graph => {
2763+
if let Ok(mut graph) = self.wallet_graph.lock() {
2764+
graph.handle_input(GraphInput::Up);
2765+
}
27632766
}
2767+
_ => {}
27642768
}
2765-
_ => {}
27662769
}
27672770
}
2771+
// Navigation - Down/j
27682772
KeyCode::Down | KeyCode::Char('j') if !self.chat_input_active => {
2769-
match self.active_tab {
2770-
TabIndex::Chat => { self.chat_scroll = self.chat_scroll.saturating_add(1); }
2771-
TabIndex::Logs => { self.log_scroll = self.log_scroll.saturating_add(1); }
2772-
TabIndex::Graph => {
2773-
// Pan down in graph view
2774-
if let Ok(mut graph) = self.wallet_graph.lock() {
2775-
let (cx, cy, zoom) = graph.viewport;
2776-
graph.viewport = (cx, cy - 5.0 / zoom, zoom);
2773+
if self.show_help {
2774+
self.help_scroll = self.help_scroll.saturating_add(1);
2775+
} else {
2776+
match self.active_tab {
2777+
TabIndex::Chat => { self.chat_scroll = self.chat_scroll.saturating_add(1); }
2778+
TabIndex::Dashboard => { self.ai_insights_scroll = self.ai_insights_scroll.saturating_add(1); }
2779+
TabIndex::Logs => { self.log_scroll = self.log_scroll.saturating_add(1); }
2780+
TabIndex::Graph => {
2781+
if let Ok(mut graph) = self.wallet_graph.lock() {
2782+
graph.handle_input(GraphInput::Down);
2783+
}
27772784
}
2785+
_ => {}
27782786
}
2779-
_ => {}
27802787
}
27812788
}
2782-
// Graph panning (left/right)
2789+
// Navigation - Left/h (graph node/edge selection)
27832790
KeyCode::Left | KeyCode::Char('h') if self.active_tab == TabIndex::Graph => {
27842791
if let Ok(mut graph) = self.wallet_graph.lock() {
2785-
let (cx, cy, zoom) = graph.viewport;
2786-
graph.viewport = (cx - 5.0 / zoom, cy, zoom);
2792+
graph.handle_input(GraphInput::Left);
27872793
}
27882794
}
2795+
// Navigation - Right/l (graph node/edge selection)
27892796
KeyCode::Right | KeyCode::Char('l') if self.active_tab == TabIndex::Graph => {
27902797
if let Ok(mut graph) = self.wallet_graph.lock() {
2791-
let (cx, cy, zoom) = graph.viewport;
2792-
graph.viewport = (cx + 5.0 / zoom, cy, zoom);
2798+
graph.handle_input(GraphInput::Right);
2799+
}
2800+
}
2801+
// Enter - Hop to selected wallet
2802+
KeyCode::Enter if self.active_tab == TabIndex::Graph && !self.chat_input_active => {
2803+
if let Ok(mut graph) = self.wallet_graph.lock() {
2804+
graph.handle_input(GraphInput::HopToWallet);
2805+
}
2806+
}
2807+
// Space - Toggle node collapse/expand
2808+
KeyCode::Char(' ') if self.active_tab == TabIndex::Graph => {
2809+
if let Ok(mut graph) = self.wallet_graph.lock() {
2810+
graph.handle_input(GraphInput::Toggle);
27932811
}
27942812
}
27952813
// Graph zoom
27962814
KeyCode::Char('+') | KeyCode::Char('=') if self.active_tab == TabIndex::Graph => {
27972815
if let Ok(mut graph) = self.wallet_graph.lock() {
2798-
let (cx, cy, zoom) = graph.viewport;
2799-
graph.viewport = (cx, cy, (zoom * 1.2).min(10.0));
2816+
graph.handle_input(GraphInput::ZoomIn);
28002817
}
28012818
}
2802-
KeyCode::Char('-') if self.active_tab == TabIndex::Graph => {
2819+
KeyCode::Char('-') | KeyCode::Char('_') if self.active_tab == TabIndex::Graph => {
28032820
if let Ok(mut graph) = self.wallet_graph.lock() {
2804-
let (cx, cy, zoom) = graph.viewport;
2805-
graph.viewport = (cx, cy, (zoom / 1.2).max(0.1));
2821+
graph.handle_input(GraphInput::ZoomOut);
2822+
}
2823+
}
2824+
// Graph panning (w/a/s/d for pan)
2825+
KeyCode::Char('w') if self.active_tab == TabIndex::Graph => {
2826+
if let Ok(mut graph) = self.wallet_graph.lock() {
2827+
graph.handle_input(GraphInput::PanUp);
2828+
}
2829+
}
2830+
KeyCode::Char('s') if self.active_tab == TabIndex::Graph && !self.chat_input_active => {
2831+
if let Ok(mut graph) = self.wallet_graph.lock() {
2832+
graph.handle_input(GraphInput::PanDown);
2833+
}
2834+
}
2835+
KeyCode::Char('a') if self.active_tab == TabIndex::Graph => {
2836+
if let Ok(mut graph) = self.wallet_graph.lock() {
2837+
graph.handle_input(GraphInput::PanLeft);
2838+
}
2839+
}
2840+
KeyCode::Char('d') if self.active_tab == TabIndex::Graph => {
2841+
if let Ok(mut graph) = self.wallet_graph.lock() {
2842+
graph.handle_input(GraphInput::PanRight);
28062843
}
28072844
}
28082845
// Graph depth control
28092846
KeyCode::Char('[') if self.active_tab == TabIndex::Graph => {
28102847
if let Ok(mut graph) = self.wallet_graph.lock() {
2811-
if graph.current_depth > 1 {
2812-
graph.current_depth -= 1;
2813-
}
2848+
graph.handle_input(GraphInput::DecreaseDepth);
28142849
}
28152850
}
28162851
KeyCode::Char(']') if self.active_tab == TabIndex::Graph => {
28172852
if let Ok(mut graph) = self.wallet_graph.lock() {
2818-
if graph.current_depth < graph.max_depth {
2819-
graph.current_depth += 1;
2820-
}
2853+
graph.handle_input(GraphInput::IncreaseDepth);
2854+
}
2855+
}
2856+
// Graph search
2857+
KeyCode::Char('/') if self.active_tab == TabIndex::Graph => {
2858+
if let Ok(mut graph) = self.wallet_graph.lock() {
2859+
graph.handle_input(GraphInput::StartSearch);
2860+
}
2861+
}
2862+
KeyCode::Char('n') if self.active_tab == TabIndex::Graph => {
2863+
if let Ok(mut graph) = self.wallet_graph.lock() {
2864+
graph.handle_input(GraphInput::SearchNext);
2865+
}
2866+
}
2867+
KeyCode::Char('N') if self.active_tab == TabIndex::Graph => {
2868+
if let Ok(mut graph) = self.wallet_graph.lock() {
2869+
graph.handle_input(GraphInput::SearchPrev);
2870+
}
2871+
}
2872+
// Graph copy to clipboard
2873+
KeyCode::Char('y') if self.active_tab == TabIndex::Graph => {
2874+
if let Ok(mut graph) = self.wallet_graph.lock() {
2875+
graph.handle_input(GraphInput::Copy);
28212876
}
28222877
}
28232878
// Graph trail toggle
@@ -2832,6 +2887,17 @@ impl OsvmApp {
28322887
graph.viewport = (0.0, 0.0, 1.0);
28332888
}
28342889
}
2890+
// Detail panel scroll (< and >)
2891+
KeyCode::Char('<') | KeyCode::Char(',') if self.active_tab == TabIndex::Graph => {
2892+
if let Ok(mut graph) = self.wallet_graph.lock() {
2893+
graph.handle_input(GraphInput::ScrollDetailUp);
2894+
}
2895+
}
2896+
KeyCode::Char('>') | KeyCode::Char('.') if self.active_tab == TabIndex::Graph => {
2897+
if let Ok(mut graph) = self.wallet_graph.lock() {
2898+
graph.handle_input(GraphInput::ScrollDetailDown);
2899+
}
2900+
}
28352901
// Number keys 1-5 to select tabs
28362902
KeyCode::Char('1') if !self.chat_input_active => {
28372903
self.active_tab = TabIndex::Chat;
@@ -2857,15 +2923,31 @@ impl OsvmApp {
28572923
self.chat_input.push(c);
28582924
}
28592925
}
2860-
KeyCode::Backspace if self.chat_input_active => {
2861-
self.chat_input.pop();
2926+
KeyCode::Backspace => {
2927+
if self.chat_input_active {
2928+
self.chat_input.pop();
2929+
} else if self.active_tab == TabIndex::Graph {
2930+
if let Ok(mut graph) = self.wallet_graph.lock() {
2931+
if graph.search_active {
2932+
graph.handle_input(GraphInput::SearchBackspace);
2933+
}
2934+
}
2935+
}
28622936
}
28632937
KeyCode::Enter if self.chat_input_active => {
28642938
if !self.chat_input.trim().is_empty() {
28652939
self.send_chat_message();
28662940
}
28672941
self.chat_input_active = false;
28682942
}
2943+
// Handle graph search character input
2944+
KeyCode::Char(c) if self.active_tab == TabIndex::Graph => {
2945+
if let Ok(mut graph) = self.wallet_graph.lock() {
2946+
if graph.search_active {
2947+
graph.handle_input(GraphInput::SearchChar(c));
2948+
}
2949+
}
2950+
}
28692951
_ => {}
28702952
}
28712953
}

0 commit comments

Comments
 (0)