library is now conflicting with client and its breaking it, but lets change the client

This commit is contained in:
Priec
2025-07-29 23:25:10 +02:00
parent 05bb84fc98
commit 20b428264e
10 changed files with 109 additions and 324 deletions

View File

@@ -0,0 +1,93 @@
// src/autocomplete/actions.rs
use crate::canvas::state::{CanvasState, ActionContext};
use crate::autocomplete::state::AutocompleteCanvasState;
use crate::canvas::actions::types::{CanvasAction, ActionResult};
use crate::canvas::actions::edit::handle_generic_canvas_action; // Import the core function
use anyhow::Result;
/// Version for states that implement rich autocomplete
pub async fn execute_canvas_action_with_autocomplete<S: CanvasState + AutocompleteCanvasState>(
action: CanvasAction,
state: &mut S,
ideal_cursor_column: &mut usize,
) -> Result<ActionResult> {
// 1. Try feature-specific handler first
let context = ActionContext {
key_code: None,
ideal_cursor_column: *ideal_cursor_column,
current_input: state.get_current_input().to_string(),
current_field: state.current_field(),
};
if let Some(result) = state.handle_feature_action(&action, &context) {
return Ok(ActionResult::HandledByFeature(result));
}
// 2. Handle rich autocomplete actions
if let Some(result) = handle_rich_autocomplete_action(&action, state)? {
return Ok(result);
}
// 3. Handle generic canvas actions
handle_generic_canvas_action(action, state, ideal_cursor_column).await
}
/// Handle rich autocomplete actions for AutocompleteCanvasState
fn handle_rich_autocomplete_action<S: CanvasState + AutocompleteCanvasState>(
action: &CanvasAction,
state: &mut S,
) -> Result<Option<ActionResult>> {
match action {
CanvasAction::TriggerAutocomplete => {
if state.supports_autocomplete(state.current_field()) {
state.activate_autocomplete();
Ok(Some(ActionResult::success_with_message("Autocomplete activated - fetching suggestions...")))
} else {
Ok(Some(ActionResult::error("Autocomplete not supported for this field")))
}
}
CanvasAction::SuggestionDown => {
if state.is_autocomplete_ready() {
if let Some(autocomplete_state) = state.autocomplete_state_mut() {
autocomplete_state.select_next();
return Ok(Some(ActionResult::success()));
}
}
Ok(None)
}
CanvasAction::SuggestionUp => {
if state.is_autocomplete_ready() {
if let Some(autocomplete_state) = state.autocomplete_state_mut() {
autocomplete_state.select_previous();
return Ok(Some(ActionResult::success()));
}
}
Ok(None)
}
CanvasAction::SelectSuggestion => {
if state.is_autocomplete_ready() {
if let Some(message) = state.apply_autocomplete_selection() {
return Ok(Some(ActionResult::success_with_message(message)));
} else {
return Ok(Some(ActionResult::error("No suggestion selected")));
}
}
Ok(None)
}
CanvasAction::ExitSuggestions => {
if state.is_autocomplete_active() {
state.deactivate_autocomplete();
Ok(Some(ActionResult::success_with_message("Autocomplete cancelled")))
} else {
Ok(None)
}
}
_ => Ok(None),
}
}

View File

@@ -8,7 +8,7 @@ use ratatui::{
Frame,
};
use crate::autocomplete::AutocompleteState;
use crate::autocomplete::types::AutocompleteState;
#[cfg(feature = "gui")]
use crate::canvas::theme::CanvasTheme;

View File

@@ -1,8 +1,10 @@
// src/autocomplete/mod.rs
pub mod types;
pub mod gui;
pub mod state; // Add this line
pub mod state;
pub mod actions;
// Re-export autocomplete types
pub use types::{SuggestionItem, AutocompleteState};
pub use state::AutocompleteCanvasState; // Add this line
pub use state::AutocompleteCanvasState;
pub use actions::execute_canvas_action_with_autocomplete;

View File

@@ -1,9 +1,7 @@
// canvas/src/actions/edit.rs
use crate::canvas::state::{CanvasState, ActionContext};
use crate::autocomplete::state::AutocompleteCanvasState;
use crate::canvas::actions::types::{CanvasAction, ActionResult};
use crossterm::event::{KeyCode, KeyEvent};
use anyhow::Result;
/// Execute a typed canvas action on any CanvasState implementation
@@ -12,7 +10,6 @@ pub async fn execute_canvas_action<S: CanvasState>(
state: &mut S,
ideal_cursor_column: &mut usize,
) -> Result<ActionResult> {
// 1. Try feature-specific handler first
let context = ActionContext {
key_code: None,
ideal_cursor_column: *ideal_cursor_column,
@@ -24,195 +21,11 @@ pub async fn execute_canvas_action<S: CanvasState>(
return Ok(ActionResult::HandledByFeature(result));
}
// 2. Handle autocomplete actions (falls back to legacy methods)
if let Some(result) = handle_autocomplete_action(&action, state)? {
return Ok(result);
}
// 3. Handle generic canvas actions
handle_generic_canvas_action(action, state, ideal_cursor_column).await
}
/// Version for states that implement rich autocomplete
pub async fn execute_canvas_action_with_autocomplete<S: CanvasState + AutocompleteCanvasState>(
action: CanvasAction,
state: &mut S,
ideal_cursor_column: &mut usize,
) -> Result<ActionResult> {
// 1. Try feature-specific handler first
let context = ActionContext {
key_code: None,
ideal_cursor_column: *ideal_cursor_column,
current_input: state.get_current_input().to_string(),
current_field: state.current_field(),
};
if let Some(result) = state.handle_feature_action(&action, &context) {
return Ok(ActionResult::HandledByFeature(result));
}
// 2. Handle rich autocomplete actions
if let Some(result) = handle_rich_autocomplete_action(&action, state)? {
return Ok(result);
}
// 3. Handle generic canvas actions
handle_generic_canvas_action(action, state, ideal_cursor_column).await
}
/// Legacy function for string-based actions (backwards compatibility)
pub async fn execute_edit_action<S: CanvasState>(
action: &str,
key: KeyEvent,
state: &mut S,
ideal_cursor_column: &mut usize,
) -> Result<String> {
let typed_action = match action {
"insert_char" => {
if let KeyCode::Char(c) = key.code {
CanvasAction::InsertChar(c)
} else {
return Ok("Error: insert_char called without a char key.".to_string());
}
}
_ => CanvasAction::from_string(action),
};
let result = execute_canvas_action(typed_action, state, ideal_cursor_column).await?;
// Convert ActionResult back to string for backwards compatibility
Ok(result.message().unwrap_or("").to_string())
}
/// Handle autocomplete actions for basic CanvasState (uses legacy methods)
fn handle_autocomplete_action<S: CanvasState>(
action: &CanvasAction,
state: &mut S,
) -> Result<Option<ActionResult>> {
match action {
CanvasAction::TriggerAutocomplete => {
// For basic CanvasState, just return an error or no-op
Ok(Some(ActionResult::error("Autocomplete not supported - implement AutocompleteCanvasState for rich autocomplete")))
}
CanvasAction::SuggestionDown => {
// Try legacy suggestions
if let Some(suggestions) = state.get_suggestions() {
if !suggestions.is_empty() {
let current = state.get_selected_suggestion_index().unwrap_or(0);
let next = (current + 1) % suggestions.len();
state.set_selected_suggestion_index(Some(next));
return Ok(Some(ActionResult::success()));
}
}
Ok(None)
}
CanvasAction::SuggestionUp => {
// Try legacy suggestions
if let Some(suggestions) = state.get_suggestions() {
if !suggestions.is_empty() {
let current = state.get_selected_suggestion_index().unwrap_or(0);
let prev = if current == 0 { suggestions.len() - 1 } else { current - 1 };
state.set_selected_suggestion_index(Some(prev));
return Ok(Some(ActionResult::success()));
}
}
Ok(None)
}
CanvasAction::SelectSuggestion => {
// Try legacy suggestions
if let Some(suggestions) = state.get_suggestions() {
if let Some(index) = state.get_selected_suggestion_index() {
if let Some(selected) = suggestions.get(index) {
// Clone the string first to avoid borrowing issues
let selected_text = selected.clone();
// Now we can mutate state without holding any references
*state.get_current_input_mut() = selected_text.clone();
state.set_current_cursor_pos(selected_text.len());
state.set_has_unsaved_changes(true);
state.deactivate_suggestions();
return Ok(Some(ActionResult::success_with_message(
format!("Selected: {}", selected_text)
)));
}
}
}
Ok(None)
}
CanvasAction::ExitSuggestions => {
state.deactivate_suggestions();
Ok(Some(ActionResult::success_with_message("Suggestions cancelled")))
}
_ => Ok(None),
}
}
/// Handle rich autocomplete actions for AutocompleteCanvasState
fn handle_rich_autocomplete_action<S: CanvasState + AutocompleteCanvasState>(
action: &CanvasAction,
state: &mut S,
) -> Result<Option<ActionResult>> {
match action {
CanvasAction::TriggerAutocomplete => {
if state.supports_autocomplete(state.current_field()) {
state.activate_autocomplete();
Ok(Some(ActionResult::success_with_message("Autocomplete activated - fetching suggestions...")))
} else {
Ok(Some(ActionResult::error("Autocomplete not supported for this field")))
}
}
CanvasAction::SuggestionDown => {
if state.is_autocomplete_ready() {
if let Some(autocomplete_state) = state.autocomplete_state_mut() {
autocomplete_state.select_next();
return Ok(Some(ActionResult::success()));
}
}
Ok(None)
}
CanvasAction::SuggestionUp => {
if state.is_autocomplete_ready() {
if let Some(autocomplete_state) = state.autocomplete_state_mut() {
autocomplete_state.select_previous();
return Ok(Some(ActionResult::success()));
}
}
Ok(None)
}
CanvasAction::SelectSuggestion => {
if state.is_autocomplete_ready() {
if let Some(message) = state.apply_autocomplete_selection() {
return Ok(Some(ActionResult::success_with_message(message)));
} else {
return Ok(Some(ActionResult::error("No suggestion selected")));
}
}
Ok(None)
}
CanvasAction::ExitSuggestions => {
if state.is_autocomplete_active() {
state.deactivate_autocomplete();
Ok(Some(ActionResult::success_with_message("Autocomplete cancelled")))
} else {
Ok(None)
}
}
_ => Ok(None),
}
}
/// Handle core canvas actions with full type safety
async fn handle_generic_canvas_action<S: CanvasState>(
pub async fn handle_generic_canvas_action<S: CanvasState>(
action: CanvasAction,
state: &mut S,
ideal_cursor_column: &mut usize,
@@ -432,9 +245,10 @@ async fn handle_generic_canvas_action<S: CanvasState>(
Ok(ActionResult::error(format!("Unknown or unhandled custom action: {}", action_str)))
}
// Autocomplete actions are handled by the autocomplete module
CanvasAction::TriggerAutocomplete | CanvasAction::SuggestionUp | CanvasAction::SuggestionDown |
CanvasAction::SelectSuggestion | CanvasAction::ExitSuggestions => {
Ok(ActionResult::error("Autocomplete action not handled properly"))
Ok(ActionResult::error("Autocomplete actions should be handled by autocomplete module"))
}
}
}

View File

@@ -1,8 +1,7 @@
// canvas/src/actions/mod.rs
// canvas/src/canvas/actions/mod.rs
pub mod types;
pub mod edit;
// Re-export the main types for convenience
pub use types::{CanvasAction, ActionResult};
pub use edit::{execute_canvas_action, execute_edit_action};
pub use edit::execute_canvas_action; // Remove execute_edit_action

View File

@@ -3,12 +3,15 @@ pub mod actions;
pub mod modes;
pub mod gui;
pub mod theme;
pub mod state; // Add this line
pub mod state;
// Re-export commonly used canvas types
pub use actions::{CanvasAction, ActionResult};
pub use modes::{AppMode, ModeManager, HighlightState};
pub use state::{CanvasState, ActionContext}; // Add this line
pub use state::{CanvasState, ActionContext};
#[cfg(feature = "gui")]
pub use theme::CanvasTheme;
#[cfg(feature = "gui")]
pub use gui::render_canvas;

View File

@@ -31,33 +31,6 @@ pub trait CanvasState {
fn has_unsaved_changes(&self) -> bool;
fn set_has_unsaved_changes(&mut self, changed: bool);
// --- LEGACY AUTOCOMPLETE SUPPORT (for backwards compatibility) ---
/// Legacy suggestion support (deprecated - use AutocompleteCanvasState for rich features)
fn get_suggestions(&self) -> Option<&[String]> {
None
}
/// Legacy selected suggestion index (deprecated)
fn get_selected_suggestion_index(&self) -> Option<usize> {
None
}
/// Legacy suggestion index setter (deprecated)
fn set_selected_suggestion_index(&mut self, _index: Option<usize>) {
// Default: no-op
}
/// Legacy activate suggestions (deprecated)
fn activate_suggestions(&mut self, _suggestions: Vec<String>) {
// Default: no-op
}
/// Legacy deactivate suggestions (deprecated)
fn deactivate_suggestions(&mut self) {
// Default: no-op
}
// --- Feature-specific action handling ---
/// Feature-specific action handling (NEW: Type-safe)
@@ -65,25 +38,7 @@ pub trait CanvasState {
None // Default: no feature-specific handling
}
/// Legacy string-based action handling (for backwards compatibility)
fn handle_feature_action_legacy(&mut self, action: &str, context: &ActionContext) -> Option<String> {
// Convert string to typed action and delegate
let typed_action = match action {
"insert_char" => {
// This is tricky - we need the char from the KeyCode in context
if let Some(crossterm::event::KeyCode::Char(c)) = context.key_code {
CanvasAction::InsertChar(c)
} else {
CanvasAction::Custom(action.to_string())
}
}
_ => CanvasAction::from_string(action),
};
self.handle_feature_action(&typed_action, context)
}
// --- Display Overrides (for links, computed values, etc.) ---
fn get_display_value_for_field(&self, index: usize) -> &str {
self.inputs()
.get(index)

View File

@@ -3,17 +3,3 @@ pub mod canvas;
pub mod autocomplete;
pub mod config;
pub mod dispatcher;
pub mod suggestions; // Keep for backwards compatibility
// Re-export from modules
pub use canvas::{CanvasAction, ActionResult, AppMode, ModeManager, HighlightState};
#[cfg(feature = "gui")]
pub use canvas::CanvasTheme;
pub use autocomplete::{SuggestionItem, AutocompleteState};
pub use dispatcher::ActionDispatcher;
pub use canvas::state::{CanvasState, ActionContext}; // Fixed path
// Backwards compatibility
pub use suggestions::SuggestionState;

View File

@@ -1,67 +0,0 @@
// canvas/src/suggestions.rs
/// Generic suggestion system that can be implemented by any CanvasState
#[derive(Debug, Clone)]
pub struct SuggestionState {
pub suggestions: Vec<String>,
pub selected_index: Option<usize>,
pub is_active: bool,
pub trigger_chars: Vec<char>, // Characters that trigger suggestions
}
impl Default for SuggestionState {
fn default() -> Self {
Self {
suggestions: Vec::new(),
selected_index: None,
is_active: false,
trigger_chars: vec![], // No auto-trigger by default
}
}
}
impl SuggestionState {
pub fn new(trigger_chars: Vec<char>) -> Self {
Self {
trigger_chars,
..Default::default()
}
}
pub fn activate_with_suggestions(&mut self, suggestions: Vec<String>) {
self.suggestions = suggestions;
self.is_active = !self.suggestions.is_empty();
self.selected_index = if self.is_active { Some(0) } else { None };
}
pub fn deactivate(&mut self) {
self.suggestions.clear();
self.selected_index = None;
self.is_active = false;
}
pub fn select_next(&mut self) {
if !self.suggestions.is_empty() {
let current = self.selected_index.unwrap_or(0);
self.selected_index = Some((current + 1) % self.suggestions.len());
}
}
pub fn select_previous(&mut self) {
if !self.suggestions.is_empty() {
let current = self.selected_index.unwrap_or(0);
self.selected_index = Some(
if current == 0 { self.suggestions.len() - 1 } else { current - 1 }
);
}
}
pub fn get_selected(&self) -> Option<&String> {
self.selected_index
.and_then(|idx| self.suggestions.get(idx))
}
pub fn should_trigger(&self, c: char) -> bool {
self.trigger_chars.contains(&c)
}
}

View File

@@ -1,7 +1,7 @@
// src/components/form/form.rs
use crate::components::common::autocomplete;
use crate::config::colors::themes::Theme;
use canvas::{CanvasState, render_canvas, HighlightState}; // CHANGED: Import HighlightState from canvas
use canvas::canvas::{CanvasState, render_canvas, HighlightState};
use crate::state::pages::form::FormState;
use ratatui::{
layout::{Alignment, Constraint, Direction, Layout, Margin, Rect},