fix(ai): edit openverse image search credentials
This commit is contained in:
parent
3e843e4afa
commit
04746248a7
|
|
@ -108,9 +108,16 @@ pub enum ImageGenField {
|
|||
BaseUrl,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum ImageSearchField {
|
||||
ClientId,
|
||||
ClientSecret,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum SettingsFocus {
|
||||
McpPort,
|
||||
ImageSearch(ImageSearchField),
|
||||
BuiltinAgent {
|
||||
index: usize,
|
||||
field: BuiltinAgentField,
|
||||
|
|
@ -216,6 +223,8 @@ pub struct AgentSettings {
|
|||
pub mcp_cli_enabled: [bool; 6],
|
||||
pub images_advanced_open: bool,
|
||||
pub images_search_ready: bool,
|
||||
pub openverse_client_id: String,
|
||||
pub openverse_client_secret: String,
|
||||
pub image_gen_profiles: Vec<ImageGenProfile>,
|
||||
pub active_image_gen_profile_id: Option<String>,
|
||||
pub next_image_gen_profile_id: u64,
|
||||
|
|
@ -245,6 +254,8 @@ impl Default for AgentSettings {
|
|||
mcp_cli_enabled: [false; 6],
|
||||
images_advanced_open: false,
|
||||
images_search_ready: true,
|
||||
openverse_client_id: String::new(),
|
||||
openverse_client_secret: String::new(),
|
||||
image_gen_profiles: Vec::new(),
|
||||
active_image_gen_profile_id: None,
|
||||
next_image_gen_profile_id: 1,
|
||||
|
|
@ -345,6 +356,8 @@ mod tests {
|
|||
assert!(s.image_gen_profiles.is_empty());
|
||||
assert!(s.active_image_gen_profile_id.is_none());
|
||||
assert!(!s.images_advanced_open);
|
||||
assert!(s.openverse_client_id.is_empty());
|
||||
assert!(s.openverse_client_secret.is_empty());
|
||||
assert_eq!(s.mcp_server.port, 3100);
|
||||
assert!(s.auto_update_enabled);
|
||||
assert!(s.focus.is_none());
|
||||
|
|
|
|||
|
|
@ -70,8 +70,8 @@ mod tests_pages;
|
|||
|
||||
pub use agent_settings::{
|
||||
AgentSettings, AgentSettingsDrag, AgentSettingsTab, BuiltinAgentConfig, BuiltinAgentField,
|
||||
BuiltinAgentKind, ImageGenField, ImageGenProfile, ImageGenProvider, McpCli, McpServer,
|
||||
SettingsFocus,
|
||||
BuiltinAgentKind, ImageGenField, ImageGenProfile, ImageGenProvider, ImageSearchField, McpCli,
|
||||
McpServer, SettingsFocus,
|
||||
};
|
||||
pub use align::AlignAction;
|
||||
pub use chat::{
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ use crate::widgets::icons::{draw_icon, Icon};
|
|||
use crate::widgets::PaintCx;
|
||||
use crate::{Color, Point2D, Rect, TextLayout};
|
||||
use op_editor_core::agent_settings::{
|
||||
AgentSettings, ImageGenField, ImageGenProfile, SettingsFocus,
|
||||
AgentSettings, ImageGenField, ImageGenProfile, ImageSearchField, SettingsFocus,
|
||||
};
|
||||
use op_editor_core::editor_ui_state::EditorUiState;
|
||||
|
||||
|
|
@ -33,6 +33,7 @@ const PROFILE_FIELD_H: f32 = 24.0;
|
|||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum ImagesHit {
|
||||
ToggleAdvanced,
|
||||
FocusSearchField(ImageSearchField),
|
||||
TestSearch,
|
||||
AddGenConfig,
|
||||
SetActiveGenConfig(usize),
|
||||
|
|
@ -94,6 +95,23 @@ fn register_link_y(content: Rect) -> f32 {
|
|||
content.origin.y + TITLE_H + ADVANCED_ROW_H + SUBTITLE_H + ROW_H + ROW_VGAP + ROW_H + BODY_GAP
|
||||
}
|
||||
|
||||
fn search_field_rect(content: Rect, index: usize) -> Rect {
|
||||
let y = content.origin.y
|
||||
+ TITLE_H
|
||||
+ ADVANCED_ROW_H
|
||||
+ SUBTITLE_H
|
||||
+ if index == 0 { 0.0 } else { ROW_H + ROW_VGAP };
|
||||
Rect {
|
||||
origin: Point2D::new(content.origin.x + LABEL_W, y),
|
||||
size: Point2D::new(content.size.x - LABEL_W, ROW_H),
|
||||
}
|
||||
}
|
||||
|
||||
fn has_search_credentials(settings: &AgentSettings) -> bool {
|
||||
!settings.openverse_client_id.trim().is_empty()
|
||||
|| !settings.openverse_client_secret.trim().is_empty()
|
||||
}
|
||||
|
||||
fn test_btn_rect(content: Rect, settings: &AgentSettings) -> Rect {
|
||||
if !settings.images_advanced_open {
|
||||
return Rect {
|
||||
|
|
@ -169,8 +187,18 @@ pub fn hit_test(content: Rect, settings: &AgentSettings, scrolled: Point2D) -> I
|
|||
if rect_contains(advanced_toggle_rect(content), scrolled) {
|
||||
return ImagesHit::ToggleAdvanced;
|
||||
}
|
||||
if settings.images_advanced_open && rect_contains(test_btn_rect(content, settings), scrolled) {
|
||||
return ImagesHit::TestSearch;
|
||||
if settings.images_advanced_open {
|
||||
if rect_contains(search_field_rect(content, 0), scrolled) {
|
||||
return ImagesHit::FocusSearchField(ImageSearchField::ClientId);
|
||||
}
|
||||
if rect_contains(search_field_rect(content, 1), scrolled) {
|
||||
return ImagesHit::FocusSearchField(ImageSearchField::ClientSecret);
|
||||
}
|
||||
if has_search_credentials(settings)
|
||||
&& rect_contains(test_btn_rect(content, settings), scrolled)
|
||||
{
|
||||
return ImagesHit::TestSearch;
|
||||
}
|
||||
}
|
||||
if rect_contains(add_btn_rect(content, settings), scrolled) {
|
||||
return ImagesHit::AddGenConfig;
|
||||
|
|
@ -298,9 +326,12 @@ pub(super) fn paint_images_tab(
|
|||
cx.backend
|
||||
.draw_text(&sub, Point2D::new(content.origin.x, y + 14.0));
|
||||
y += SUBTITLE_H;
|
||||
paint_input_row(
|
||||
paint_search_input_row(
|
||||
cx,
|
||||
theme,
|
||||
settings,
|
||||
ui,
|
||||
ImageSearchField::ClientId,
|
||||
t_settings(ui, "settings.images.clientId"),
|
||||
t_settings(ui, "settings.images.clientIdPlaceholder"),
|
||||
content.origin.x,
|
||||
|
|
@ -308,9 +339,12 @@ pub(super) fn paint_images_tab(
|
|||
content.size.x,
|
||||
);
|
||||
y += ROW_H + ROW_VGAP;
|
||||
paint_input_row(
|
||||
paint_search_input_row(
|
||||
cx,
|
||||
theme,
|
||||
settings,
|
||||
ui,
|
||||
ImageSearchField::ClientSecret,
|
||||
t_settings(ui, "settings.images.clientSecret"),
|
||||
t_settings(ui, "settings.images.clientSecretPlaceholder"),
|
||||
content.origin.x,
|
||||
|
|
@ -588,9 +622,13 @@ fn paint_profile_field(
|
|||
);
|
||||
}
|
||||
|
||||
fn paint_input_row(
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn paint_search_input_row(
|
||||
cx: &mut PaintCx<'_>,
|
||||
theme: &Theme,
|
||||
settings: &AgentSettings,
|
||||
ui: &EditorUiState,
|
||||
field_kind: ImageSearchField,
|
||||
label: &str,
|
||||
placeholder: &str,
|
||||
x: f32,
|
||||
|
|
@ -611,16 +649,44 @@ fn paint_input_row(
|
|||
size: Point2D::new(w - LABEL_W, ROW_H),
|
||||
};
|
||||
cx.backend.fill_round_rect(field, 6.0, theme.background);
|
||||
cx.backend.stroke_round_rect(field, 6.0, theme.border, 1.0);
|
||||
let ph = TextLayout::single_run(
|
||||
placeholder,
|
||||
let focused = settings.focus == Some(SettingsFocus::ImageSearch(field_kind));
|
||||
cx.backend.stroke_round_rect(
|
||||
field,
|
||||
6.0,
|
||||
if focused { theme.primary } else { theme.border },
|
||||
1.0,
|
||||
);
|
||||
let stored = match field_kind {
|
||||
ImageSearchField::ClientId => settings.openverse_client_id.as_str(),
|
||||
ImageSearchField::ClientSecret => settings.openverse_client_secret.as_str(),
|
||||
};
|
||||
let text = if focused {
|
||||
ui.settings_input_draft.as_str()
|
||||
} else if matches!(field_kind, ImageSearchField::ClientSecret) && !stored.is_empty() {
|
||||
"********"
|
||||
} else {
|
||||
stored
|
||||
};
|
||||
let showing_placeholder = text.is_empty();
|
||||
let value = if showing_placeholder {
|
||||
placeholder
|
||||
} else {
|
||||
text
|
||||
};
|
||||
let value = ellipsize(cx, value, field.size.x - 24.0, 13.0);
|
||||
let lay = TextLayout::single_run(
|
||||
&value,
|
||||
"system-ui",
|
||||
13.0,
|
||||
to_jian(theme.muted_foreground),
|
||||
to_jian(if showing_placeholder {
|
||||
theme.muted_foreground
|
||||
} else {
|
||||
theme.foreground
|
||||
}),
|
||||
Point2D::new(0.0, 0.0),
|
||||
);
|
||||
cx.backend.draw_text(
|
||||
&ph,
|
||||
&lay,
|
||||
Point2D::new(field.origin.x + 12.0, field.origin.y + ROW_H / 2.0 + 5.0),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
//! Multi-tab settings modal opened via `Cmd+,`. Left sidebar
|
||||
//! nav (Agents / MCP / Images / System) + scrollable right pane.
|
||||
//! Visual parity with the TS app's settings panel.
|
||||
|
||||
use crate::theme::Theme;
|
||||
use crate::widgets::agent_settings_builtin::{self, BuiltinHit};
|
||||
|
|
@ -14,7 +13,8 @@ use crate::widgets::icons::{draw_icon, Icon};
|
|||
use crate::widgets::{PaintCx, Widget, WidgetId};
|
||||
use crate::{Color, Point2D, Rect, TextLayout};
|
||||
use op_editor_core::agent_settings::{
|
||||
AgentProvider, AgentSettings, AgentSettingsTab, BuiltinAgentField, ImageGenField, McpCli,
|
||||
AgentProvider, AgentSettings, AgentSettingsTab, BuiltinAgentField, ImageGenField,
|
||||
ImageSearchField, McpCli,
|
||||
};
|
||||
use op_editor_core::editor_ui_state::EditorUiState;
|
||||
use op_editor_core::EditorState;
|
||||
|
|
@ -54,6 +54,7 @@ pub enum AgentSettingsHit {
|
|||
ToggleMcpServer,
|
||||
ToggleMcpCli(McpCli),
|
||||
ToggleImagesAdvanced,
|
||||
FocusSearchField(ImageSearchField),
|
||||
TestImageSearch,
|
||||
AddGenConfig,
|
||||
SetActiveGenConfig(usize),
|
||||
|
|
@ -166,6 +167,9 @@ impl<'a> AgentSettingsPanel<'a> {
|
|||
match agent_settings_images::hit_test(content_rect(panel), &self.settings, scrolled)
|
||||
{
|
||||
ImagesHit::ToggleAdvanced => return AgentSettingsHit::ToggleImagesAdvanced,
|
||||
ImagesHit::FocusSearchField(field) => {
|
||||
return AgentSettingsHit::FocusSearchField(field);
|
||||
}
|
||||
ImagesHit::TestSearch => return AgentSettingsHit::TestImageSearch,
|
||||
ImagesHit::AddGenConfig => return AgentSettingsHit::AddGenConfig,
|
||||
ImagesHit::SetActiveGenConfig(index) => {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use crate::widgets::agent_settings_panel::{AgentSettingsHit, AgentSettingsPanel};
|
||||
use op_editor_core::agent_settings::{
|
||||
AgentSettingsTab, BuiltinAgentField, ImageGenField, SettingsFocus,
|
||||
AgentSettingsTab, BuiltinAgentField, ImageGenField, ImageSearchField, SettingsFocus,
|
||||
};
|
||||
use op_editor_core::EditorState;
|
||||
|
||||
|
|
@ -197,6 +197,56 @@ fn images_tab_profile_rows_expose_active_and_remove_targets() {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn images_tab_advanced_search_fields_are_focusable() {
|
||||
let mut state = EditorState::default();
|
||||
state.editor_ui.agent_settings.tab = AgentSettingsTab::Images;
|
||||
state.editor_ui.agent_settings.images_advanced_open = true;
|
||||
let panel = AgentSettingsPanel::for_editor(&state);
|
||||
let rect = panel.rect(1200.0, 800.0);
|
||||
let content_x = rect.origin.x + 200.0 + 24.0;
|
||||
let content_y = rect.origin.y + 24.0;
|
||||
let field_x = content_x + 110.0 + 16.0;
|
||||
let first_field_y = content_y + 36.0 + 24.0 + 22.0;
|
||||
|
||||
assert_eq!(
|
||||
panel.hit_test(rect, crate::Point2D::new(field_x, first_field_y + 18.0)),
|
||||
AgentSettingsHit::FocusSearchField(ImageSearchField::ClientId)
|
||||
);
|
||||
assert_eq!(
|
||||
panel.hit_test(
|
||||
rect,
|
||||
crate::Point2D::new(field_x, first_field_y + 36.0 + 10.0 + 18.0)
|
||||
),
|
||||
AgentSettingsHit::FocusSearchField(ImageSearchField::ClientSecret)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn images_tab_test_search_requires_some_oauth_text() {
|
||||
let mut state = EditorState::default();
|
||||
state.editor_ui.agent_settings.tab = AgentSettingsTab::Images;
|
||||
state.editor_ui.agent_settings.images_advanced_open = true;
|
||||
let panel = AgentSettingsPanel::for_editor(&state);
|
||||
let rect = panel.rect(1200.0, 800.0);
|
||||
let content_y = rect.origin.y + 24.0;
|
||||
let content_w = rect.size.x - 200.0 - 48.0;
|
||||
let button_x = rect.origin.x + 200.0 + 24.0 + content_w - 28.0;
|
||||
let button_y = content_y + 36.0 + 24.0 + 22.0 + 36.0 + 10.0 + 36.0 + 14.0 + 18.0;
|
||||
|
||||
assert_eq!(
|
||||
panel.hit_test(rect, crate::Point2D::new(button_x, button_y)),
|
||||
AgentSettingsHit::Inside
|
||||
);
|
||||
|
||||
state.editor_ui.agent_settings.openverse_client_id = "client".into();
|
||||
let panel = AgentSettingsPanel::for_editor(&state);
|
||||
assert_eq!(
|
||||
panel.hit_test(rect, crate::Point2D::new(button_x, button_y)),
|
||||
AgentSettingsHit::TestImageSearch
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn images_tab_content_height_includes_profile_rows() {
|
||||
let mut empty = EditorState::default();
|
||||
|
|
|
|||
|
|
@ -51,6 +51,12 @@ struct ImageGenProfilePayload {
|
|||
base_url: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
struct OpenverseOAuthPayload {
|
||||
client_id: String,
|
||||
client_secret: String,
|
||||
}
|
||||
|
||||
/// Cheap snapshot of every persisted field. Captured before each
|
||||
/// dispatch; if it differs after, save the file.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
|
|
@ -60,6 +66,8 @@ pub struct Fingerprint {
|
|||
port: u16,
|
||||
cli: [bool; 6],
|
||||
images_adv: bool,
|
||||
openverse_client_id: String,
|
||||
openverse_client_secret: String,
|
||||
auto_update_enabled: bool,
|
||||
connected: [bool; 5],
|
||||
builtin_agents: Vec<BuiltinAgentConfig>,
|
||||
|
|
@ -75,6 +83,8 @@ pub fn fingerprint(state: &EditorState) -> Fingerprint {
|
|||
port: eui.agent_settings.mcp_server.port,
|
||||
cli: eui.agent_settings.mcp_cli_enabled,
|
||||
images_adv: eui.agent_settings.images_advanced_open,
|
||||
openverse_client_id: eui.agent_settings.openverse_client_id.clone(),
|
||||
openverse_client_secret: eui.agent_settings.openverse_client_secret.clone(),
|
||||
auto_update_enabled: eui.agent_settings.auto_update_enabled,
|
||||
connected: eui.agent_settings.connected,
|
||||
builtin_agents: eui.agent_settings.builtin_agents.clone(),
|
||||
|
|
@ -107,6 +117,8 @@ struct SettingsPayload {
|
|||
#[serde(default)]
|
||||
images_advanced_open: Option<bool>,
|
||||
#[serde(default)]
|
||||
openverse_oauth: Option<OpenverseOAuthPayload>,
|
||||
#[serde(default)]
|
||||
auto_update_enabled: Option<bool>,
|
||||
/// Per-provider connect state, indexed by `AgentProvider::ALL`
|
||||
/// (Claude / Codex / OpenCode / Copilot / Gemini). Restored on
|
||||
|
|
@ -141,6 +153,7 @@ fn to_payload(state: &EditorState) -> SettingsPayload {
|
|||
mcp_port: Some(eui.agent_settings.mcp_server.port),
|
||||
mcp_cli_enabled: Some(eui.agent_settings.mcp_cli_enabled),
|
||||
images_advanced_open: Some(eui.agent_settings.images_advanced_open),
|
||||
openverse_oauth: openverse_oauth_to_payload(&eui.agent_settings),
|
||||
auto_update_enabled: Some(eui.agent_settings.auto_update_enabled),
|
||||
connected: Some(eui.agent_settings.connected),
|
||||
builtin_agents: Some(
|
||||
|
|
@ -201,6 +214,10 @@ fn apply_payload(state: &mut EditorState, payload: SettingsPayload) {
|
|||
if let Some(b) = payload.images_advanced_open {
|
||||
eui.agent_settings.images_advanced_open = b;
|
||||
}
|
||||
if let Some(oauth) = payload.openverse_oauth {
|
||||
eui.agent_settings.openverse_client_id = oauth.client_id;
|
||||
eui.agent_settings.openverse_client_secret = oauth.client_secret;
|
||||
}
|
||||
if let Some(b) = payload.auto_update_enabled {
|
||||
eui.agent_settings.auto_update_enabled = b;
|
||||
}
|
||||
|
|
@ -296,6 +313,21 @@ fn builtin_agent_from_payload(payload: BuiltinAgentPayload) -> Option<BuiltinAge
|
|||
})
|
||||
}
|
||||
|
||||
fn openverse_oauth_to_payload(
|
||||
settings: &op_editor_core::agent_settings::AgentSettings,
|
||||
) -> Option<OpenverseOAuthPayload> {
|
||||
let client_id = settings.openverse_client_id.trim();
|
||||
let client_secret = settings.openverse_client_secret.trim();
|
||||
if client_id.is_empty() && client_secret.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(OpenverseOAuthPayload {
|
||||
client_id: client_id.to_string(),
|
||||
client_secret: client_secret.to_string(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn image_gen_profile_to_payload(profile: &ImageGenProfile) -> ImageGenProfilePayload {
|
||||
ImageGenProfilePayload {
|
||||
id: profile.id.clone(),
|
||||
|
|
@ -597,6 +629,27 @@ mod tests {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn openverse_oauth_round_trips_through_payload() {
|
||||
let mut src = EditorState::new();
|
||||
src.editor_ui.agent_settings.openverse_client_id = "client-id".into();
|
||||
src.editor_ui.agent_settings.openverse_client_secret = "client-secret".into();
|
||||
|
||||
let json = serde_json::to_string(&to_payload(&src)).unwrap();
|
||||
let payload: SettingsPayload = serde_json::from_str(&json).unwrap();
|
||||
let mut dst = EditorState::new();
|
||||
apply_payload(&mut dst, payload);
|
||||
|
||||
assert_eq!(
|
||||
dst.editor_ui.agent_settings.openverse_client_id,
|
||||
"client-id"
|
||||
);
|
||||
assert_eq!(
|
||||
dst.editor_ui.agent_settings.openverse_client_secret,
|
||||
"client-secret"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn auto_update_preference_round_trips_through_payload() {
|
||||
let mut src = EditorState::new();
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use super::WidgetHostNative;
|
||||
use op_editor_core::agent_settings::{
|
||||
AgentSettingsTab, BuiltinAgentField, ImageGenField, SettingsFocus,
|
||||
AgentSettingsTab, BuiltinAgentField, ImageGenField, ImageSearchField, SettingsFocus,
|
||||
};
|
||||
use op_editor_ui::widgets::agent_settings_panel::AgentSettingsPanel;
|
||||
|
||||
|
|
@ -282,3 +282,83 @@ fn image_generation_profile_focus_accepts_text_and_commits() {
|
|||
.settings_input_draft
|
||||
.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn image_search_oauth_focus_accepts_text_and_commits() {
|
||||
let mut host = WidgetHostNative::new();
|
||||
host.editor_state_mut().editor_ui.agent_settings.tab = AgentSettingsTab::Images;
|
||||
host.editor_state_mut().editor_ui.agent_settings.focus =
|
||||
Some(SettingsFocus::ImageSearch(ImageSearchField::ClientId));
|
||||
host.editor_state_mut()
|
||||
.editor_ui
|
||||
.settings_input_draft
|
||||
.clear();
|
||||
|
||||
for c in "openverse-client".chars() {
|
||||
assert!(host.apply_text(c));
|
||||
}
|
||||
assert!(host.apply_send());
|
||||
|
||||
host.editor_state_mut().editor_ui.agent_settings.focus =
|
||||
Some(SettingsFocus::ImageSearch(ImageSearchField::ClientSecret));
|
||||
host.editor_state_mut()
|
||||
.editor_ui
|
||||
.settings_input_draft
|
||||
.clear();
|
||||
for c in "openverse-secret".chars() {
|
||||
assert!(host.apply_text(c));
|
||||
}
|
||||
assert!(host.apply_send());
|
||||
|
||||
let settings = &host.editor_state().editor_ui.agent_settings;
|
||||
assert_eq!(settings.openverse_client_id, "openverse-client");
|
||||
assert_eq!(settings.openverse_client_secret, "openverse-secret");
|
||||
assert!(settings.focus.is_none());
|
||||
assert!(host
|
||||
.editor_state()
|
||||
.editor_ui
|
||||
.settings_input_draft
|
||||
.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn image_search_test_updates_ready_status_from_oauth_completeness() {
|
||||
let mut host = WidgetHostNative::new();
|
||||
host.editor_state_mut().editor_ui.agent_settings.tab = AgentSettingsTab::Images;
|
||||
host.editor_state_mut()
|
||||
.editor_ui
|
||||
.agent_settings
|
||||
.images_advanced_open = true;
|
||||
host.editor_state_mut()
|
||||
.editor_ui
|
||||
.agent_settings
|
||||
.openverse_client_id = "client".into();
|
||||
|
||||
let panel = AgentSettingsPanel::for_editor(host.editor_state());
|
||||
let rect = panel.rect(1200.0, 800.0);
|
||||
let content_y = rect.origin.y + 24.0;
|
||||
let content_w = rect.size.x - 200.0 - 48.0;
|
||||
let x = rect.origin.x + 200.0 + 24.0 + content_w - 28.0;
|
||||
let y = content_y + 36.0 + 24.0 + 22.0 + 36.0 + 10.0 + 36.0 + 14.0 + 18.0;
|
||||
|
||||
assert!(host.dispatch_agent_settings_press(x, y, 1200.0, 800.0));
|
||||
assert!(
|
||||
!host
|
||||
.editor_state()
|
||||
.editor_ui
|
||||
.agent_settings
|
||||
.images_search_ready
|
||||
);
|
||||
|
||||
host.editor_state_mut()
|
||||
.editor_ui
|
||||
.agent_settings
|
||||
.openverse_client_secret = "secret".into();
|
||||
assert!(host.dispatch_agent_settings_press(x, y, 1200.0, 800.0));
|
||||
assert!(
|
||||
host.editor_state()
|
||||
.editor_ui
|
||||
.agent_settings
|
||||
.images_search_ready
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,9 @@ impl WidgetHostNative {
|
|||
op_editor_core::agent_settings::SettingsFocus::McpPort => {
|
||||
c.is_ascii_digit() && draft.len() < 5
|
||||
}
|
||||
op_editor_core::agent_settings::SettingsFocus::ImageSearch(_) => {
|
||||
!c.is_control() && draft.len() < 512
|
||||
}
|
||||
op_editor_core::agent_settings::SettingsFocus::BuiltinAgent { .. } => {
|
||||
!c.is_control() && draft.len() < 512
|
||||
}
|
||||
|
|
|
|||
|
|
@ -103,6 +103,32 @@ impl WidgetHostNative {
|
|||
.images_advanced_open;
|
||||
*v = !*v;
|
||||
}
|
||||
AgentSettingsHit::FocusSearchField(field) => {
|
||||
self.commit_settings_focus_if_any();
|
||||
self.editor_state.editor_ui.settings_input_draft = match field {
|
||||
op_editor_core::agent_settings::ImageSearchField::ClientId => self
|
||||
.editor_state
|
||||
.editor_ui
|
||||
.agent_settings
|
||||
.openverse_client_id
|
||||
.clone(),
|
||||
op_editor_core::agent_settings::ImageSearchField::ClientSecret => self
|
||||
.editor_state
|
||||
.editor_ui
|
||||
.agent_settings
|
||||
.openverse_client_secret
|
||||
.clone(),
|
||||
};
|
||||
self.editor_state.editor_ui.agent_settings.focus = Some(
|
||||
op_editor_core::agent_settings::SettingsFocus::ImageSearch(field),
|
||||
);
|
||||
}
|
||||
AgentSettingsHit::TestImageSearch => {
|
||||
self.commit_settings_focus_if_any();
|
||||
let settings = &mut self.editor_state.editor_ui.agent_settings;
|
||||
settings.images_search_ready = !settings.openverse_client_id.trim().is_empty()
|
||||
&& !settings.openverse_client_secret.trim().is_empty();
|
||||
}
|
||||
AgentSettingsHit::SetActiveGenConfig(index) => {
|
||||
self.commit_settings_focus_if_any();
|
||||
if let Some(id) = self
|
||||
|
|
@ -306,9 +332,7 @@ impl WidgetHostNative {
|
|||
self.editor_state.editor_ui.settings_input_draft.clear();
|
||||
self.editor_state.rebuild_chat_models();
|
||||
}
|
||||
AgentSettingsHit::AddAcpAgent
|
||||
| AgentSettingsHit::TestImageSearch
|
||||
| AgentSettingsHit::Inside => {}
|
||||
AgentSettingsHit::AddAcpAgent | AgentSettingsHit::Inside => {}
|
||||
AgentSettingsHit::AddGenConfig => {
|
||||
self.commit_settings_focus_if_any();
|
||||
let id = self
|
||||
|
|
|
|||
|
|
@ -16,6 +16,20 @@ impl WidgetHostNative {
|
|||
self.editor_state.editor_ui.agent_settings.mcp_server.port = port.max(1024);
|
||||
}
|
||||
}
|
||||
SettingsFocus::ImageSearch(field) => match field {
|
||||
op_editor_core::agent_settings::ImageSearchField::ClientId => {
|
||||
self.editor_state
|
||||
.editor_ui
|
||||
.agent_settings
|
||||
.openverse_client_id = draft.trim().to_string();
|
||||
}
|
||||
op_editor_core::agent_settings::ImageSearchField::ClientSecret => {
|
||||
self.editor_state
|
||||
.editor_ui
|
||||
.agent_settings
|
||||
.openverse_client_secret = draft.trim().to_string();
|
||||
}
|
||||
},
|
||||
SettingsFocus::BuiltinAgent { index, field } => {
|
||||
if let Some(agent) = self
|
||||
.editor_state
|
||||
|
|
|
|||
|
|
@ -13,9 +13,20 @@ impl WidgetHost {
|
|||
/// Push a typed character into the focused chat / settings input.
|
||||
/// Returns true if anything changed.
|
||||
pub fn apply_text(&mut self, c: char) -> bool {
|
||||
if self.editor_state.editor_ui.agent_settings.focus.is_some() {
|
||||
if c.is_ascii_digit() && self.editor_state.editor_ui.settings_input_draft.len() < 5 {
|
||||
self.editor_state.editor_ui.settings_input_draft.push(c);
|
||||
if let Some(focus) = self.editor_state.editor_ui.agent_settings.focus {
|
||||
let draft = &mut self.editor_state.editor_ui.settings_input_draft;
|
||||
let accepts = match focus {
|
||||
op_editor_core::agent_settings::SettingsFocus::McpPort => {
|
||||
c.is_ascii_digit() && draft.len() < 5
|
||||
}
|
||||
op_editor_core::agent_settings::SettingsFocus::ImageSearch(_)
|
||||
| op_editor_core::agent_settings::SettingsFocus::BuiltinAgent { .. }
|
||||
| op_editor_core::agent_settings::SettingsFocus::ImageGenProfile { .. } => {
|
||||
!c.is_control() && draft.len() < 512
|
||||
}
|
||||
};
|
||||
if accepts {
|
||||
draft.push(c);
|
||||
self.mark_dirty();
|
||||
return true;
|
||||
}
|
||||
|
|
@ -404,6 +415,20 @@ impl WidgetHost {
|
|||
self.editor_state.editor_ui.agent_settings.mcp_server.port = port.max(1024);
|
||||
}
|
||||
}
|
||||
SettingsFocus::ImageSearch(field) => match field {
|
||||
op_editor_core::agent_settings::ImageSearchField::ClientId => {
|
||||
self.editor_state
|
||||
.editor_ui
|
||||
.agent_settings
|
||||
.openverse_client_id = draft.trim().to_string();
|
||||
}
|
||||
op_editor_core::agent_settings::ImageSearchField::ClientSecret => {
|
||||
self.editor_state
|
||||
.editor_ui
|
||||
.agent_settings
|
||||
.openverse_client_secret = draft.trim().to_string();
|
||||
}
|
||||
},
|
||||
SettingsFocus::BuiltinAgent { .. } | SettingsFocus::ImageGenProfile { .. } => {}
|
||||
}
|
||||
self.mark_dirty();
|
||||
|
|
|
|||
|
|
@ -718,7 +718,6 @@ impl WidgetHost {
|
|||
was_focused
|
||||
}
|
||||
|
||||
/// Cmd+, settings modal — dispatch hit-tests on the modal.
|
||||
/// Returns true once the modal swallowed the press.
|
||||
fn dispatch_agent_settings_press(&mut self, x: f32, y: f32, vw: f32, vh: f32) -> bool {
|
||||
use op_editor_ui::widgets::agent_settings_panel::{AgentSettingsHit, AgentSettingsPanel};
|
||||
|
|
@ -791,6 +790,7 @@ impl WidgetHost {
|
|||
| AgentSettingsHit::AddGenConfig
|
||||
| AgentSettingsHit::SetActiveGenConfig(_)
|
||||
| AgentSettingsHit::RemoveGenConfig(_)
|
||||
| AgentSettingsHit::FocusSearchField(_)
|
||||
| AgentSettingsHit::FocusGenConfig { .. }
|
||||
| AgentSettingsHit::Inside => {}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue