fix(agent): keep pure provider base urls read-only
This commit is contained in:
parent
21a92e5740
commit
048a786fa5
|
|
@ -191,6 +191,13 @@ pub struct BuiltinAgentConfig {
|
|||
}
|
||||
|
||||
impl BuiltinAgentConfig {
|
||||
pub fn base_url_editable(&self) -> bool {
|
||||
!matches!(
|
||||
self.preset,
|
||||
BuiltinAgentPresetKey::Anthropic | BuiltinAgentPresetKey::OpenAi
|
||||
)
|
||||
}
|
||||
|
||||
pub fn ready(&self) -> bool {
|
||||
self.enabled && !self.api_key.trim().is_empty() && !self.model.trim().is_empty()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -156,6 +156,19 @@ fn pure_builtin_presets_do_not_toggle_api_format() {
|
|||
assert_eq!(openai.base_url, "https://api.openai.com/v1");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pure_builtin_presets_keep_base_url_read_only_like_ts() {
|
||||
let mut s = AgentSettings::default();
|
||||
|
||||
for _ in 0..3 {
|
||||
s.add_builtin_agent();
|
||||
}
|
||||
|
||||
assert!(!s.builtin_agents[0].base_url_editable());
|
||||
assert!(!s.builtin_agents[1].base_url_editable());
|
||||
assert!(s.builtin_agents[2].base_url_editable());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_acp_agent_assigns_id_and_defaults_to_local_config() {
|
||||
let mut s = AgentSettings::default();
|
||||
|
|
|
|||
|
|
@ -2,6 +2,11 @@
|
|||
|
||||
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,
|
||||
rect_contains, CARD_GAP, EMPTY_HEIGHT, HEADER_HEIGHT, SUBTITLE_HEIGHT, TOP_HEADER_RIGHT_INSET,
|
||||
};
|
||||
use crate::widgets::agent_settings_builtin_parts;
|
||||
use crate::widgets::agent_settings_caret::{
|
||||
caret_x_for_text, paint_caret, settings_caret_for_focus,
|
||||
|
|
@ -21,21 +26,6 @@ use op_editor_core::agent_settings::{
|
|||
use op_editor_core::editor_ui_state::EditorUiState;
|
||||
use op_editor_core::BuiltinAgentPresetKey;
|
||||
|
||||
const HEADER_HEIGHT: f32 = 28.0;
|
||||
const SUBTITLE_HEIGHT: f32 = 28.0;
|
||||
const EMPTY_HEIGHT: f32 = 64.0;
|
||||
const COMPACT_CARD_HEIGHT: f32 = 60.0;
|
||||
const EXPANDED_CARD_HEIGHT: f32 = 196.0;
|
||||
const DRAFT_ACTION_HEIGHT: f32 = 36.0;
|
||||
const CARD_GAP: f32 = 8.0;
|
||||
const ADD_W: f32 = 96.0;
|
||||
const TOP_HEADER_RIGHT_INSET: f32 = 12.0;
|
||||
const FIELD_LABEL_W: f32 = 68.0;
|
||||
const FIELD_H: f32 = 24.0;
|
||||
const SWITCH_W: f32 = 34.0;
|
||||
const SWITCH_H: f32 = 20.0;
|
||||
const ACTION_W: f32 = 24.0;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum BuiltinHit {
|
||||
AddProvider,
|
||||
|
|
@ -125,6 +115,9 @@ pub fn hit_test(content: Rect, settings: &AgentSettings, point: Point2D) -> Buil
|
|||
.into_iter()
|
||||
.enumerate()
|
||||
{
|
||||
if field == BuiltinAgentField::BaseUrl && !agent.base_url_editable() {
|
||||
continue;
|
||||
}
|
||||
if rect_contains(field_input_rect(settings, card, Some(index), row), point) {
|
||||
return BuiltinHit::Focus { index, field };
|
||||
}
|
||||
|
|
@ -179,6 +172,9 @@ pub fn hit_test(content: Rect, settings: &AgentSettings, point: Point2D) -> Buil
|
|||
.into_iter()
|
||||
.enumerate()
|
||||
{
|
||||
if field == BuiltinAgentField::BaseUrl && !agent.base_url_editable() {
|
||||
continue;
|
||||
}
|
||||
if rect_contains(field_input_rect(settings, card, None, row), point) {
|
||||
return BuiltinHit::FocusDraft(field);
|
||||
}
|
||||
|
|
@ -632,6 +628,7 @@ fn paint_field(
|
|||
BuiltinAgentField::BaseUrl => "Base URL",
|
||||
};
|
||||
let input = field_input_rect(settings, card, index, row);
|
||||
let editable = field != BuiltinAgentField::BaseUrl || agent.base_url_editable();
|
||||
let label_y = input.origin.y + 16.0;
|
||||
draw_text(
|
||||
cx,
|
||||
|
|
@ -662,7 +659,11 @@ fn paint_field(
|
|||
cx,
|
||||
&clipped,
|
||||
11.0,
|
||||
theme.foreground,
|
||||
if editable {
|
||||
theme.foreground
|
||||
} else {
|
||||
theme.muted_foreground
|
||||
},
|
||||
text_x,
|
||||
input.origin.y + 16.0,
|
||||
);
|
||||
|
|
@ -749,99 +750,6 @@ fn mask_key(api_key: &str) -> String {
|
|||
}
|
||||
}
|
||||
|
||||
fn is_editing(settings: &AgentSettings, index: usize) -> bool {
|
||||
matches!(
|
||||
settings.focus,
|
||||
Some(SettingsFocus::BuiltinAgent { index: i, .. }) if i == index
|
||||
)
|
||||
}
|
||||
|
||||
fn card_height(settings: &AgentSettings, index: usize) -> f32 {
|
||||
if is_editing(settings, index) {
|
||||
expanded_card_height(settings, Some(index))
|
||||
} else {
|
||||
COMPACT_CARD_HEIGHT
|
||||
}
|
||||
}
|
||||
|
||||
fn expanded_card_height(settings: &AgentSettings, index: Option<usize>) -> f32 {
|
||||
EXPANDED_CARD_HEIGHT + agent_settings_builtin_parts::preset_menu_height(settings, index)
|
||||
}
|
||||
|
||||
fn draft_card_height(settings: &AgentSettings) -> f32 {
|
||||
expanded_card_height(settings, None) + DRAFT_ACTION_HEIGHT
|
||||
}
|
||||
|
||||
fn add_provider_rect(content: Rect, y: f32) -> Rect {
|
||||
Rect {
|
||||
origin: Point2D::new(
|
||||
content.origin.x + content.size.x - TOP_HEADER_RIGHT_INSET - ADD_W,
|
||||
y,
|
||||
),
|
||||
size: Point2D::new(ADD_W, 24.0),
|
||||
}
|
||||
}
|
||||
|
||||
fn card_rect(x: f32, y: f32, w: f32, h: f32) -> Rect {
|
||||
Rect {
|
||||
origin: Point2D::new(x, y),
|
||||
size: Point2D::new(w, h),
|
||||
}
|
||||
}
|
||||
|
||||
fn compact_switch_rect(card: Rect) -> Rect {
|
||||
Rect {
|
||||
origin: Point2D::new(
|
||||
card.origin.x + card.size.x - 12.0 - SWITCH_W - 8.0 - ACTION_W * 2.0 - 4.0,
|
||||
card.origin.y + (card.size.y - SWITCH_H) / 2.0,
|
||||
),
|
||||
size: Point2D::new(SWITCH_W, SWITCH_H),
|
||||
}
|
||||
}
|
||||
|
||||
fn compact_edit_rect(card: Rect) -> Rect {
|
||||
Rect {
|
||||
origin: Point2D::new(
|
||||
compact_switch_rect(card).origin.x + SWITCH_W + 8.0,
|
||||
card.origin.y + (card.size.y - ACTION_W) / 2.0,
|
||||
),
|
||||
size: Point2D::new(ACTION_W, ACTION_W),
|
||||
}
|
||||
}
|
||||
|
||||
fn compact_remove_rect(card: Rect) -> Rect {
|
||||
Rect {
|
||||
origin: Point2D::new(
|
||||
compact_edit_rect(card).origin.x + ACTION_W + 4.0,
|
||||
card.origin.y + (card.size.y - ACTION_W) / 2.0,
|
||||
),
|
||||
size: Point2D::new(ACTION_W, ACTION_W),
|
||||
}
|
||||
}
|
||||
|
||||
fn field_input_rect(
|
||||
settings: &AgentSettings,
|
||||
card: Rect,
|
||||
index: Option<usize>,
|
||||
row: usize,
|
||||
) -> Rect {
|
||||
let menu_h = agent_settings_builtin_parts::preset_menu_height(settings, index);
|
||||
Rect {
|
||||
origin: Point2D::new(
|
||||
card.origin.x + 12.0 + FIELD_LABEL_W,
|
||||
card.origin.y + 76.0 + menu_h + row as f32 * 28.0,
|
||||
),
|
||||
size: Point2D::new(card.size.x - 24.0 - FIELD_LABEL_W, FIELD_H),
|
||||
}
|
||||
}
|
||||
|
||||
fn rect_contains(r: Rect, p: Point2D) -> bool {
|
||||
p.x >= r.origin.x
|
||||
&& p.y >= r.origin.y
|
||||
&& p.x <= r.origin.x + r.size.x
|
||||
&& p.y <= r.origin.y + r.size.y
|
||||
}
|
||||
|
||||
fn to_jian(c: Color) -> jian_core::scene::Color {
|
||||
fn ch(v: f32) -> u8 {
|
||||
(v.clamp(0.0, 1.0) * 255.0).round() as u8
|
||||
|
|
|
|||
111
crates/op-editor-ui/src/widgets/agent_settings_builtin_layout.rs
Normal file
111
crates/op-editor-ui/src/widgets/agent_settings_builtin_layout.rs
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
use crate::widgets::agent_settings_builtin_parts;
|
||||
use crate::{Point2D, Rect};
|
||||
use op_editor_core::agent_settings::{AgentSettings, SettingsFocus};
|
||||
|
||||
pub(super) const HEADER_HEIGHT: f32 = 28.0;
|
||||
pub(super) const SUBTITLE_HEIGHT: f32 = 28.0;
|
||||
pub(super) const EMPTY_HEIGHT: f32 = 64.0;
|
||||
const COMPACT_CARD_HEIGHT: f32 = 60.0;
|
||||
const EXPANDED_CARD_HEIGHT: f32 = 196.0;
|
||||
const DRAFT_ACTION_HEIGHT: f32 = 36.0;
|
||||
pub(super) const CARD_GAP: f32 = 8.0;
|
||||
const ADD_W: f32 = 96.0;
|
||||
pub(super) const TOP_HEADER_RIGHT_INSET: f32 = 12.0;
|
||||
const FIELD_LABEL_W: f32 = 68.0;
|
||||
const FIELD_H: f32 = 24.0;
|
||||
const SWITCH_W: f32 = 34.0;
|
||||
const SWITCH_H: f32 = 20.0;
|
||||
const ACTION_W: f32 = 24.0;
|
||||
|
||||
pub(super) fn is_editing(settings: &AgentSettings, index: usize) -> bool {
|
||||
matches!(
|
||||
settings.focus,
|
||||
Some(SettingsFocus::BuiltinAgent { index: i, .. }) if i == index
|
||||
)
|
||||
}
|
||||
|
||||
pub(super) fn card_height(settings: &AgentSettings, index: usize) -> f32 {
|
||||
if is_editing(settings, index) {
|
||||
expanded_card_height(settings, Some(index))
|
||||
} else {
|
||||
COMPACT_CARD_HEIGHT
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn expanded_card_height(settings: &AgentSettings, index: Option<usize>) -> f32 {
|
||||
EXPANDED_CARD_HEIGHT + agent_settings_builtin_parts::preset_menu_height(settings, index)
|
||||
}
|
||||
|
||||
pub(super) fn draft_card_height(settings: &AgentSettings) -> f32 {
|
||||
expanded_card_height(settings, None) + DRAFT_ACTION_HEIGHT
|
||||
}
|
||||
|
||||
pub(super) fn add_provider_rect(content: Rect, y: f32) -> Rect {
|
||||
Rect {
|
||||
origin: Point2D::new(
|
||||
content.origin.x + content.size.x - TOP_HEADER_RIGHT_INSET - ADD_W,
|
||||
y,
|
||||
),
|
||||
size: Point2D::new(ADD_W, 24.0),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn card_rect(x: f32, y: f32, w: f32, h: f32) -> Rect {
|
||||
Rect {
|
||||
origin: Point2D::new(x, y),
|
||||
size: Point2D::new(w, h),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn compact_switch_rect(card: Rect) -> Rect {
|
||||
Rect {
|
||||
origin: Point2D::new(
|
||||
card.origin.x + card.size.x - 12.0 - SWITCH_W - 8.0 - ACTION_W * 2.0 - 4.0,
|
||||
card.origin.y + (card.size.y - SWITCH_H) / 2.0,
|
||||
),
|
||||
size: Point2D::new(SWITCH_W, SWITCH_H),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn compact_edit_rect(card: Rect) -> Rect {
|
||||
Rect {
|
||||
origin: Point2D::new(
|
||||
compact_switch_rect(card).origin.x + SWITCH_W + 8.0,
|
||||
card.origin.y + (card.size.y - ACTION_W) / 2.0,
|
||||
),
|
||||
size: Point2D::new(ACTION_W, ACTION_W),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn compact_remove_rect(card: Rect) -> Rect {
|
||||
Rect {
|
||||
origin: Point2D::new(
|
||||
compact_edit_rect(card).origin.x + ACTION_W + 4.0,
|
||||
card.origin.y + (card.size.y - ACTION_W) / 2.0,
|
||||
),
|
||||
size: Point2D::new(ACTION_W, ACTION_W),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn field_input_rect(
|
||||
settings: &AgentSettings,
|
||||
card: Rect,
|
||||
index: Option<usize>,
|
||||
row: usize,
|
||||
) -> Rect {
|
||||
let menu_h = agent_settings_builtin_parts::preset_menu_height(settings, index);
|
||||
Rect {
|
||||
origin: Point2D::new(
|
||||
card.origin.x + 12.0 + FIELD_LABEL_W,
|
||||
card.origin.y + 76.0 + menu_h + row as f32 * 28.0,
|
||||
),
|
||||
size: Point2D::new(card.size.x - 24.0 - FIELD_LABEL_W, FIELD_H),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn rect_contains(r: Rect, p: Point2D) -> bool {
|
||||
p.x >= r.origin.x
|
||||
&& p.y >= r.origin.y
|
||||
&& p.x <= r.origin.x + r.size.x
|
||||
&& p.y <= r.origin.y + r.size.y
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
use crate::widgets::agent_settings_panel::{AgentSettingsHit, AgentSettingsPanel};
|
||||
use crate::Point2D;
|
||||
use op_editor_core::agent_settings::{BuiltinAgentField, SettingsFocus};
|
||||
use op_editor_core::EditorState;
|
||||
|
||||
#[test]
|
||||
fn pure_builtin_provider_base_url_is_read_only_hit_target() {
|
||||
let mut state = EditorState::default();
|
||||
state.editor_ui.agent_settings.add_builtin_agent();
|
||||
state.editor_ui.agent_settings.focus = Some(SettingsFocus::BuiltinAgent {
|
||||
index: 0,
|
||||
field: BuiltinAgentField::ApiKey,
|
||||
});
|
||||
|
||||
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 first_card_y = content_y + 12.0 + 28.0 + 28.0;
|
||||
let point = Point2D::new(content_x + 92.0, first_card_y + 170.0);
|
||||
|
||||
assert_eq!(panel.hit_test(rect, point), AgentSettingsHit::Inside);
|
||||
}
|
||||
|
|
@ -101,7 +101,10 @@ 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)]
|
||||
mod agent_settings_builtin_tests;
|
||||
mod agent_settings_caret;
|
||||
mod agent_settings_form_actions;
|
||||
pub mod agent_settings_i18n;
|
||||
|
|
|
|||
|
|
@ -15,6 +15,9 @@ impl WidgetHostNative {
|
|||
.builtin_agent_draft
|
||||
.as_ref()
|
||||
{
|
||||
if field == BuiltinAgentField::BaseUrl && !agent.base_url_editable() {
|
||||
return;
|
||||
}
|
||||
self.editor_state.editor_ui.settings_input_draft = match field {
|
||||
BuiltinAgentField::DisplayName => agent.display_name.clone(),
|
||||
BuiltinAgentField::ApiKey => agent.api_key.clone(),
|
||||
|
|
|
|||
|
|
@ -176,6 +176,34 @@ fn builtin_agent_kind_toggle_commits_focused_api_key_draft() {
|
|||
assert!(host.editor_state().editor_ui.agent_settings.focus.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pure_builtin_agent_base_url_commit_is_ignored_like_ts_read_only_input() {
|
||||
let mut host = WidgetHostNative::new();
|
||||
host.editor_state_mut()
|
||||
.editor_ui
|
||||
.agent_settings
|
||||
.add_builtin_agent();
|
||||
host.editor_state_mut().editor_ui.agent_settings.focus = Some(SettingsFocus::BuiltinAgent {
|
||||
index: 0,
|
||||
field: BuiltinAgentField::BaseUrl,
|
||||
});
|
||||
host.editor_state_mut().editor_ui.settings_input_draft = "https://example.invalid".into();
|
||||
|
||||
assert!(host.apply_send());
|
||||
|
||||
let settings = &host.editor_state().editor_ui.agent_settings;
|
||||
assert_eq!(
|
||||
settings.builtin_agents[0].base_url,
|
||||
"https://api.anthropic.com"
|
||||
);
|
||||
assert!(settings.focus.is_none());
|
||||
assert!(host
|
||||
.editor_state()
|
||||
.editor_ui
|
||||
.settings_input_draft
|
||||
.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_provider_opens_unsaved_builtin_agent_draft() {
|
||||
let mut host = WidgetHostNative::new();
|
||||
|
|
|
|||
|
|
@ -234,6 +234,11 @@ impl WidgetHostNative {
|
|||
.builtin_agents
|
||||
.get(index)
|
||||
{
|
||||
if field == op_editor_core::agent_settings::BuiltinAgentField::BaseUrl
|
||||
&& !agent.base_url_editable()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
self.editor_state.editor_ui.settings_input_draft = match field {
|
||||
op_editor_core::agent_settings::BuiltinAgentField::DisplayName => {
|
||||
agent.display_name.clone()
|
||||
|
|
|
|||
|
|
@ -54,11 +54,13 @@ impl WidgetHostNative {
|
|||
agent.model = draft.trim().to_string();
|
||||
}
|
||||
BuiltinAgentField::BaseUrl => {
|
||||
agent.base_url = if draft.trim().is_empty() {
|
||||
agent.kind.default_base_url().to_string()
|
||||
} else {
|
||||
draft.trim().to_string()
|
||||
};
|
||||
if agent.base_url_editable() {
|
||||
agent.base_url = if draft.trim().is_empty() {
|
||||
agent.kind.default_base_url().to_string()
|
||||
} else {
|
||||
draft.trim().to_string()
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
self.editor_state.rebuild_chat_models();
|
||||
|
|
@ -85,11 +87,13 @@ impl WidgetHostNative {
|
|||
agent.model = draft.trim().to_string();
|
||||
}
|
||||
BuiltinAgentField::BaseUrl => {
|
||||
agent.base_url = if draft.trim().is_empty() {
|
||||
agent.kind.default_base_url().to_string()
|
||||
} else {
|
||||
draft.trim().to_string()
|
||||
};
|
||||
if agent.base_url_editable() {
|
||||
agent.base_url = if draft.trim().is_empty() {
|
||||
agent.kind.default_base_url().to_string()
|
||||
} else {
|
||||
draft.trim().to_string()
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue