diff --git a/canvas/examples/suggestions2.rs b/canvas/examples/suggestions2.rs index 59752e8..f60eeff 100644 --- a/canvas/examples/suggestions2.rs +++ b/canvas/examples/suggestions2.rs @@ -1,8 +1,8 @@ // examples/suggestions2.rs -//! Production-ready non-blocking suggestions demonstration +//! Production-ready Tab-triggered suggestions demonstration //! //! This example demonstrates: -//! - Instant, responsive suggestions dropdown +//! - Tab-triggered suggestions dropdown //! - Non-blocking architecture for real network/database calls //! - Multiple suggestion field types //! - Professional-grade user experience @@ -62,7 +62,7 @@ impl AutoCursorFormEditor { Self { editor: FormEditor::new(data_provider), has_unsaved_changes: false, - debug_message: "πŸš€ Production-Ready Suggestions Demo - Copy this architecture for your app!".to_string(), + debug_message: "πŸš€ Production-Ready Tab-Triggered Suggestions Demo - Copy this architecture for your app!".to_string(), command_buffer: String::new(), } } @@ -232,12 +232,12 @@ impl AutoCursorFormEditor { fn enter_edit_mode(&mut self) { self.editor.enter_edit_mode(); // 🎯 Library automatically sets cursor to bar | - self.debug_message = "✏️ INSERT MODE - Cursor: Steady Bar |".to_string(); + self.debug_message = "✏️ INSERT MODE - Cursor: Steady Bar | - Press Tab for suggestions".to_string(); } fn enter_append_mode(&mut self) { self.editor.enter_append_mode(); // 🎯 Library automatically positions cursor and sets mode - self.debug_message = "✏️ INSERT (append) - Cursor: Steady Bar |".to_string(); + self.debug_message = "✏️ INSERT (append) - Cursor: Steady Bar | - Press Tab for suggestions".to_string(); } fn exit_edit_mode(&mut self) { @@ -257,7 +257,7 @@ impl AutoCursorFormEditor { // === PRODUCTION-READY NON-BLOCKING SUGGESTIONS === /// Trigger suggestions with non-blocking approach (production pattern) - /// + /// /// This method demonstrates the proper way to integrate suggestions with /// real APIs, databases, or any async data source without blocking the UI. async fn trigger_suggestions_async( @@ -289,17 +289,6 @@ impl AutoCursorFormEditor { } } - /// Auto-trigger suggestions for current field (production pattern) - /// - /// Call this after character input, deletion, or field entry to automatically - /// show suggestions. Perfect for real-time search-as-you-type functionality. - async fn auto_trigger_suggestions(&mut self, provider: &mut ProductionSuggestionsProvider) { - let field_index = self.current_field(); - if self.data_provider().supports_suggestions(field_index) { - self.trigger_suggestions_async(provider, field_index).await; - } - } - fn suggestions_next(&mut self) { self.editor.suggestions_next(); } @@ -437,15 +426,15 @@ impl DataProvider for ApplicationData { // =================================================================== /// Production-ready suggestions provider -/// +/// /// Replace the data sources below with your actual: /// - REST API calls (reqwest, hyper) -/// - Database queries (sqlx, diesel) +/// - Database queries (sqlx, diesel) /// - Search engines (elasticsearch, algolia) /// - Cache lookups (redis, memcached) /// - GraphQL queries /// - gRPC services -/// +/// /// The non-blocking architecture works with any async data source. struct ProductionSuggestionsProvider { // Add your API clients, database connections, cache clients here @@ -473,7 +462,7 @@ impl ProductionSuggestionsProvider { // .send() // .await?; // let fruits: Vec = response.json().await?; - + let fruits = vec![ ("Apple", "🍎 Crisp and sweet"), ("Banana", "🍌 Rich in potassium"), @@ -500,7 +489,7 @@ impl ProductionSuggestionsProvider { // ) // .fetch_all(&self.db_pool) // .await?; - + let jobs = vec![ ("Software Engineer", "πŸ‘¨β€πŸ’» Build applications"), ("Product Manager", "πŸ“‹ Manage product roadmap"), @@ -522,7 +511,7 @@ impl ProductionSuggestionsProvider { // if let Some(cached_result) = cached { // return Ok(serde_json::from_str(&cached_result)?); // } - + let languages = vec![ ("Rust", "πŸ¦€ Systems programming"), ("Python", "🐍 Versatile and popular"), @@ -545,7 +534,7 @@ impl ProductionSuggestionsProvider { // .send() // .await?; // let countries: Vec = response.json().await?; - + let countries = vec![ ("United States", "πŸ‡ΊπŸ‡Έ North America"), ("Canada", "πŸ‡¨πŸ‡¦ Great neighbors"), @@ -598,7 +587,7 @@ impl SuggestionsProvider for ProductionSuggestionsProvider { async fn fetch_suggestions(&mut self, field_index: usize, query: &str) -> Result> { match field_index { 0 => self.get_fruit_suggestions(query).await, // API call - 1 => self.get_job_suggestions(query).await, // Database query + 1 => self.get_job_suggestions(query).await, // Database query 2 => self.get_language_suggestions(query).await, // Cache + API 3 => self.get_country_suggestions(query).await, // Geographic API 4 => self.get_color_suggestions(query).await, // Local data @@ -607,7 +596,7 @@ impl SuggestionsProvider for ProductionSuggestionsProvider { } } -/// Production-ready key handling with non-blocking suggestions +/// Production-ready key handling with Tab-triggered suggestions async fn handle_key_press( key: KeyCode, modifiers: KeyModifiers, @@ -625,7 +614,7 @@ async fn handle_key_press( } match (mode, key, modifiers) { - // === NON-BLOCKING SUGGESTIONS HANDLING === + // === TAB-TRIGGERED SUGGESTIONS HANDLING === (_, KeyCode::Tab, _) => { if editor.is_suggestions_active() { // Cycle through suggestions @@ -677,22 +666,20 @@ async fn handle_key_press( } } - // === MODE TRANSITIONS WITH AUTO-SUGGESTIONS === + // === MODE TRANSITIONS (NO AUTO-SUGGESTIONS) === (AppMode::ReadOnly, KeyCode::Char('i'), _) => { editor.enter_edit_mode(); editor.clear_command_buffer(); - // Auto-show suggestions on entering insert mode - editor.auto_trigger_suggestions(suggestions_provider).await; } (AppMode::ReadOnly, KeyCode::Char('a'), _) => { editor.enter_append_mode(); - editor.set_debug_message("✏️ INSERT (append) - Cursor: Steady Bar |".to_string()); + editor.set_debug_message("✏️ INSERT (append) - Cursor: Steady Bar | - Press Tab for suggestions".to_string()); editor.clear_command_buffer(); } (AppMode::ReadOnly, KeyCode::Char('A'), _) => { editor.move_line_end(); editor.enter_edit_mode(); - editor.set_debug_message("✏️ INSERT (end of line) - Cursor: Steady Bar |".to_string()); + editor.set_debug_message("✏️ INSERT (end of line) - Cursor: Steady Bar | - Press Tab for suggestions".to_string()); editor.clear_command_buffer(); } @@ -821,16 +808,22 @@ async fn handle_key_press( editor.move_line_end(); } - // === DELETE OPERATIONS WITH AUTO-SUGGESTIONS === + // === DELETE OPERATIONS (AUTO-FETCH WHEN SUGGESTIONS ACTIVE) === (AppMode::Edit, KeyCode::Backspace, _) => { editor.delete_backward()?; - // Auto-trigger suggestions after deletion - editor.auto_trigger_suggestions(suggestions_provider).await; + // Auto-fetch only if suggestions are already active (triggered by Tab) + if editor.is_suggestions_active() { + let field_index = editor.current_field(); + editor.trigger_suggestions_async(suggestions_provider, field_index).await; + } } (AppMode::Edit, KeyCode::Delete, _) => { editor.delete_forward()?; - // Auto-trigger suggestions after deletion - editor.auto_trigger_suggestions(suggestions_provider).await; + // Auto-fetch only if suggestions are already active (triggered by Tab) + if editor.is_suggestions_active() { + let field_index = editor.current_field(); + editor.trigger_suggestions_async(suggestions_provider, field_index).await; + } } // Delete operations in normal mode (vim x) @@ -843,11 +836,14 @@ async fn handle_key_press( editor.set_debug_message("X: deleted character backward".to_string()); } - // === CHARACTER INPUT WITH REAL-TIME SUGGESTIONS === + // === CHARACTER INPUT (AUTO-FETCH WHEN SUGGESTIONS ACTIVE) === (AppMode::Edit, KeyCode::Char(c), m) if !m.contains(KeyModifiers::CONTROL) => { editor.insert_char(c)?; - // Auto-trigger suggestions after typing - this is the magic! - editor.auto_trigger_suggestions(suggestions_provider).await; + // Auto-fetch only if suggestions are already active (triggered by Tab) + if editor.is_suggestions_active() { + let field_index = editor.current_field(); + editor.trigger_suggestions_async(suggestions_provider, field_index).await; + } } // === DEBUG/INFO COMMANDS === @@ -980,7 +976,7 @@ fn render_status_and_help( ); let status = Paragraph::new(Line::from(Span::raw(status_text))) - .block(Block::default().borders(Borders::ALL).title("πŸš€ Production-Ready Non-Blocking Suggestions")); + .block(Block::default().borders(Borders::ALL).title("πŸš€ Production-Ready Smart Suggestions (Tab to activate β†’ type to filter)")); f.render_widget(status, chunks[0]); @@ -992,19 +988,19 @@ fn render_status_and_help( Actions: i/a/A=insert, v/V=visual, x/X=delete, ?=info, Enter=next field\n\ Integration: Replace data sources with your APIs, databases, caches\n\ Architecture: Non-blocking β€’ Instant UI β€’ Stale protection β€’ Professional UX\n\ - Tab=suggestions, Enter=select β€’ Ready for: REST, GraphQL, SQL, Redis, etc." + πŸ”‘ Tab=activate suggestions β†’ type to filter β€’ Enter=select β€’ Ready for: REST, GraphQL, SQL, Redis" } AppMode::Edit => { - "πŸš€ INSERT MODE - Type for instant suggestions!\n\ - Real-time search-as-you-type with non-blocking architecture\n\ - Perfect for: User search, autocomplete, typeahead, smart suggestions\n\ + "πŸš€ INSERT MODE - Press Tab to activate suggestions, then type to filter!\n\ + Tab=activate suggestions β€’ Type/Backspace=filter while active β€’ Enter=select\n\ + Perfect for: Autocomplete, search dropdowns, data entry assistance\n\ Navigation: arrows=move, Ctrl+arrows=words, Home/End=line edges\n\ Copy this pattern for production: API calls, database queries, cache lookups" } AppMode::Highlight => { "πŸš€ VISUAL MODE - Selection with suggestions support\n\ Selection: hjkl/arrows=extend, w/b/e=word selection, Esc=normal\n\ - Professional editor experience with modern autocomplete!" + Professional editor experience with Tab-triggered autocomplete!" } _ => "πŸš€ Copy this suggestions architecture for your production app!" }; @@ -1019,23 +1015,24 @@ fn render_status_and_help( #[tokio::main] async fn main() -> Result<(), Box> { // Print production-ready information - println!("πŸš€ Production-Ready Non-Blocking Suggestions Demo"); - println!("βœ… Instant, responsive UI - no blocking on network/database calls"); + println!("πŸš€ Production-Ready Tab-Triggered Suggestions Demo"); + println!("βœ… Press Tab to activate suggestions, then type to filter in real-time"); println!("βœ… Professional autocomplete architecture"); println!("βœ… Copy this pattern for your production application!"); println!(); println!("πŸ—οΈ Integration Ready For:"); println!(" πŸ“‘ REST APIs (reqwest, hyper)"); - println!(" πŸ—„οΈ Databases (sqlx, diesel, mongodb)"); + println!(" πŸ—„οΈ Databases (sqlx, diesel, mongodb)"); println!(" πŸ” Search Engines (elasticsearch, algolia, typesense)"); println!(" πŸ’Ύ Caches (redis, memcached)"); println!(" 🌐 GraphQL APIs"); println!(" πŸ”— gRPC Services"); println!(); println!("⚑ Key Features:"); - println!(" β€’ Dropdown appears instantly (never waits for network)"); + println!(" β€’ Press Tab to activate suggestions dropdown"); + println!(" β€’ Real-time filtering while suggestions are active"); println!(" β€’ Built-in stale result protection"); - println!(" β€’ Search-as-you-type with real-time filtering"); + println!(" β€’ Tab cycles through suggestions"); println!(" β€’ Professional-grade user experience"); println!(" β€’ Easy to integrate with any async data source"); println!();