refactor(editor): centralize agent draft readiness
This commit is contained in:
parent
ce3626da2d
commit
f4c339ed0a
|
|
@ -1346,6 +1346,77 @@ impl EditorUiState {
|
|||
self.variables_preset_menu_open && self.variables_preset_name_focus
|
||||
}
|
||||
|
||||
pub fn builtin_agent_draft_ready(&self) -> bool {
|
||||
use crate::agent_settings::BuiltinAgentField;
|
||||
|
||||
let Some(name) = self.builtin_agent_draft_field_text(BuiltinAgentField::DisplayName) else {
|
||||
return false;
|
||||
};
|
||||
let Some(api_key) = self.builtin_agent_draft_field_text(BuiltinAgentField::ApiKey) else {
|
||||
return false;
|
||||
};
|
||||
let Some(model) = self.builtin_agent_draft_field_text(BuiltinAgentField::Model) else {
|
||||
return false;
|
||||
};
|
||||
!name.trim().is_empty() && !api_key.trim().is_empty() && !model.trim().is_empty()
|
||||
}
|
||||
|
||||
pub fn acp_agent_draft_ready(&self) -> bool {
|
||||
use crate::agent_settings::{AcpAgentField, AcpConnectionType};
|
||||
|
||||
let Some(draft) = self.agent_settings.acp_agent_draft.as_ref() else {
|
||||
return false;
|
||||
};
|
||||
let Some(name) = self.acp_agent_draft_field_text(AcpAgentField::DisplayName) else {
|
||||
return false;
|
||||
};
|
||||
let endpoint_field = match draft.connection_type {
|
||||
AcpConnectionType::Local => AcpAgentField::Command,
|
||||
AcpConnectionType::Remote => AcpAgentField::Url,
|
||||
};
|
||||
let Some(endpoint) = self.acp_agent_draft_field_text(endpoint_field) else {
|
||||
return false;
|
||||
};
|
||||
!name.trim().is_empty() && !endpoint.trim().is_empty()
|
||||
}
|
||||
|
||||
pub fn builtin_agent_draft_field_text(
|
||||
&self,
|
||||
field: crate::agent_settings::BuiltinAgentField,
|
||||
) -> Option<&str> {
|
||||
use crate::agent_settings::{BuiltinAgentField, SettingsFocus};
|
||||
|
||||
let draft = self.agent_settings.builtin_agent_draft.as_ref()?;
|
||||
if self.agent_settings.focus == Some(SettingsFocus::BuiltinAgentDraft(field)) {
|
||||
return Some(self.settings_input.text());
|
||||
}
|
||||
Some(match field {
|
||||
BuiltinAgentField::DisplayName => draft.display_name.as_str(),
|
||||
BuiltinAgentField::ApiKey => draft.api_key.as_str(),
|
||||
BuiltinAgentField::Model => draft.model.as_str(),
|
||||
BuiltinAgentField::BaseUrl => draft.base_url.as_str(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn acp_agent_draft_field_text(
|
||||
&self,
|
||||
field: crate::agent_settings::AcpAgentField,
|
||||
) -> Option<std::borrow::Cow<'_, str>> {
|
||||
use crate::agent_settings::{AcpAgentField, SettingsFocus};
|
||||
|
||||
let draft = self.agent_settings.acp_agent_draft.as_ref()?;
|
||||
if self.agent_settings.focus == Some(SettingsFocus::AcpAgentDraft(field)) {
|
||||
return Some(std::borrow::Cow::Borrowed(self.settings_input.text()));
|
||||
}
|
||||
Some(match field {
|
||||
AcpAgentField::DisplayName => std::borrow::Cow::Borrowed(draft.display_name.as_str()),
|
||||
AcpAgentField::Command => std::borrow::Cow::Borrowed(draft.command.as_str()),
|
||||
AcpAgentField::Args => std::borrow::Cow::Owned(draft.args_text()),
|
||||
AcpAgentField::Env => std::borrow::Cow::Owned(draft.env_text()),
|
||||
AcpAgentField::Url => std::borrow::Cow::Borrowed(draft.url.as_deref().unwrap_or("")),
|
||||
})
|
||||
}
|
||||
|
||||
/// Clear transient UI state that references specific document nodes/pages
|
||||
/// or the (now-cleared) selection, so a wholesale document replacement
|
||||
/// ([`crate::EditorState::replace_document`]) can't leave hover highlights,
|
||||
|
|
@ -1427,6 +1498,34 @@ mod tests {
|
|||
assert_eq!(ExportFormat::Jpeg.extension(), "jpg");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn builtin_agent_draft_ready_reads_focused_settings_input() {
|
||||
use crate::agent_settings::{BuiltinAgentField, SettingsFocus};
|
||||
|
||||
let mut ui = EditorUiState::new();
|
||||
ui.agent_settings.begin_builtin_agent_draft();
|
||||
assert!(!ui.builtin_agent_draft_ready());
|
||||
|
||||
ui.agent_settings.focus = Some(SettingsFocus::BuiltinAgentDraft(BuiltinAgentField::ApiKey));
|
||||
ui.settings_input.set_text("sk-test");
|
||||
|
||||
assert!(ui.builtin_agent_draft_ready());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn acp_agent_draft_ready_reads_focused_settings_input() {
|
||||
use crate::agent_settings::{AcpAgentField, SettingsFocus};
|
||||
|
||||
let mut ui = EditorUiState::new();
|
||||
ui.agent_settings.begin_acp_agent_draft();
|
||||
assert!(!ui.acp_agent_draft_ready());
|
||||
|
||||
ui.agent_settings.focus = Some(SettingsFocus::AcpAgentDraft(AcpAgentField::Command));
|
||||
ui.settings_input.set_text("op-agent");
|
||||
|
||||
assert!(ui.acp_agent_draft_ready());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dirty_ready_repo_keeps_header_popovers_allowed() {
|
||||
// TS parity (the ready view now shows for dirty trees too): a
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
//! ACP Agent section for the Agent settings panel.
|
||||
|
||||
use crate::theme::Theme;
|
||||
use crate::widgets::agent_settings_acp_draft;
|
||||
use crate::widgets::agent_settings_caret::{paint_settings_input_view, settings_input_text};
|
||||
use crate::widgets::agent_settings_form_actions::{
|
||||
cancel_button_rect, paint_form_actions, save_button_rect,
|
||||
|
|
@ -195,7 +194,7 @@ pub fn paint_acp_section(
|
|||
ui,
|
||||
card,
|
||||
EXPANDED_CARD_H,
|
||||
agent_settings_acp_draft::ready(settings, ui),
|
||||
ui.acp_agent_draft_ready(),
|
||||
);
|
||||
y += card.size.y + CARD_GAP;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,43 +0,0 @@
|
|||
//! ACP agent draft helpers.
|
||||
|
||||
use op_editor_core::agent_settings::{
|
||||
AcpAgentField, AcpConnectionType, AgentSettings, SettingsFocus,
|
||||
};
|
||||
use op_editor_core::editor_ui_state::EditorUiState;
|
||||
|
||||
pub fn ready(settings: &AgentSettings, ui: &EditorUiState) -> bool {
|
||||
let Some(draft) = settings.acp_agent_draft.as_ref() else {
|
||||
return false;
|
||||
};
|
||||
let name = field_value(
|
||||
settings,
|
||||
ui,
|
||||
AcpAgentField::DisplayName,
|
||||
&draft.display_name,
|
||||
);
|
||||
let endpoint = match draft.connection_type {
|
||||
AcpConnectionType::Local => {
|
||||
field_value(settings, ui, AcpAgentField::Command, &draft.command)
|
||||
}
|
||||
AcpConnectionType::Remote => field_value(
|
||||
settings,
|
||||
ui,
|
||||
AcpAgentField::Url,
|
||||
draft.url.as_deref().unwrap_or(""),
|
||||
),
|
||||
};
|
||||
!name.trim().is_empty() && !endpoint.trim().is_empty()
|
||||
}
|
||||
|
||||
fn field_value<'a>(
|
||||
settings: &AgentSettings,
|
||||
ui: &'a EditorUiState,
|
||||
field: AcpAgentField,
|
||||
fallback: &'a str,
|
||||
) -> &'a str {
|
||||
if settings.focus == Some(SettingsFocus::AcpAgentDraft(field)) {
|
||||
ui.settings_input.text()
|
||||
} else {
|
||||
fallback
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
//! Built-in provider section for the Agent settings panel.
|
||||
|
||||
use crate::theme::Theme;
|
||||
use crate::widgets::agent_settings_builtin_draft;
|
||||
use crate::widgets::agent_settings_builtin_layout::{
|
||||
add_provider_rect, card_height, card_rect, compact_edit_rect, compact_remove_rect,
|
||||
compact_switch_rect, draft_card_height, expanded_card_height, field_input_rect, is_editing,
|
||||
|
|
@ -309,14 +308,7 @@ pub fn paint_builtin_section(
|
|||
);
|
||||
paint_builtin_agent_form(cx, theme, settings, ui, draft, None, card, now_ms);
|
||||
let form_h = expanded_card_height(settings, None);
|
||||
paint_form_actions(
|
||||
cx,
|
||||
theme,
|
||||
ui,
|
||||
card,
|
||||
form_h,
|
||||
agent_settings_builtin_draft::ready(settings, ui),
|
||||
);
|
||||
paint_form_actions(cx, theme, ui, card, form_h, ui.builtin_agent_draft_ready());
|
||||
y += card.size.y + CARD_GAP;
|
||||
}
|
||||
y
|
||||
|
|
|
|||
|
|
@ -1,32 +0,0 @@
|
|||
//! Built-in provider draft helpers.
|
||||
|
||||
use op_editor_core::agent_settings::{AgentSettings, BuiltinAgentField, SettingsFocus};
|
||||
use op_editor_core::editor_ui_state::EditorUiState;
|
||||
|
||||
pub fn ready(settings: &AgentSettings, ui: &EditorUiState) -> bool {
|
||||
let Some(draft) = settings.builtin_agent_draft.as_ref() else {
|
||||
return false;
|
||||
};
|
||||
let api_key = field_value(settings, ui, BuiltinAgentField::ApiKey, &draft.api_key);
|
||||
let model = field_value(settings, ui, BuiltinAgentField::Model, &draft.model);
|
||||
let name = field_value(
|
||||
settings,
|
||||
ui,
|
||||
BuiltinAgentField::DisplayName,
|
||||
&draft.display_name,
|
||||
);
|
||||
!api_key.trim().is_empty() && !model.trim().is_empty() && !name.trim().is_empty()
|
||||
}
|
||||
|
||||
fn field_value<'a>(
|
||||
settings: &AgentSettings,
|
||||
ui: &'a EditorUiState,
|
||||
field: BuiltinAgentField,
|
||||
fallback: &'a str,
|
||||
) -> &'a str {
|
||||
if settings.focus == Some(SettingsFocus::BuiltinAgentDraft(field)) {
|
||||
ui.settings_input.text()
|
||||
} else {
|
||||
fallback
|
||||
}
|
||||
}
|
||||
|
|
@ -117,9 +117,7 @@ pub mod brand_icons;
|
|||
|
||||
// Step 4 — extra editor-chrome widgets (TS app parity).
|
||||
pub mod agent_settings_acp;
|
||||
mod agent_settings_acp_draft;
|
||||
pub mod agent_settings_builtin;
|
||||
mod agent_settings_builtin_draft;
|
||||
mod agent_settings_builtin_layout;
|
||||
mod agent_settings_builtin_parts;
|
||||
#[cfg(test)]
|
||||
|
|
|
|||
Loading…
Reference in a new issue