feat(aliases): support editing model aliases
This commit is contained in:
1 parent
5a05ad83fa
commit
ee223424af
7 files changed
+319
-52
No files matched your search
@@ -499,6 +499,7 @@ pub(crate) async fn create_thinking_alias(
|
||||
alias: String,
|
||||
effort: String,
|
||||
fast: Option<bool>,
|
||||
original_alias: Option<String>,
|
||||
) -> Result<Vec<ThinkingAliasEntry>, String> {
|
||||
let config = gui_config_state.snapshot()?;
|
||||
let source_id = source_id.trim().to_string();
|
||||
@@ -513,6 +514,10 @@ pub(crate) async fn create_thinking_alias(
|
||||
};
|
||||
let fast = fast.unwrap_or(false);
|
||||
let content = fetch_management_config_yaml(&config).await?;
|
||||
let working_content = match original_alias.as_deref() {
|
||||
Some(original) => prepare_model_alias_edit(&content, original)?,
|
||||
None => content.clone(),
|
||||
};
|
||||
let available_models =
|
||||
fetch_agent_models(config.port, effective_agent_api_key(&config)).await?;
|
||||
let definitions = fetch_oauth_model_definitions(&config).await;
|
||||
@@ -523,12 +528,33 @@ pub(crate) async fn create_thinking_alias(
|
||||
} else {
|
||||
AliasSourceCapability::Base
|
||||
};
|
||||
let sources =
|
||||
let sources = resolved_oauth_alias_sources(
|
||||
&working_content,
|
||||
&definitions,
|
||||
&available_models,
|
||||
capability,
|
||||
)?;
|
||||
// Removing a configured alias can shift model indices in source IDs.
|
||||
let original_sources =
|
||||
resolved_oauth_alias_sources(&content, &definitions, &available_models, capability)?;
|
||||
let source = sources
|
||||
let original_source = original_sources
|
||||
.iter()
|
||||
.find(|source| source.source.id == source_id)
|
||||
.cloned()
|
||||
.find(|source| source.source.id == source_id);
|
||||
let candidates = sources
|
||||
.iter()
|
||||
.filter(|source| {
|
||||
if original_alias.is_none() {
|
||||
return source.source.id == source_id;
|
||||
}
|
||||
original_source.is_some_and(|original| {
|
||||
source.source.model == original.source.model
|
||||
&& source.source.kind == original.source.kind
|
||||
&& source.source.provider == original.source.provider
|
||||
})
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let source = (candidates.len() == 1)
|
||||
.then(|| candidates[0].clone())
|
||||
.ok_or_else(|| {
|
||||
"原模型已不在内核当前可用模型中,或其配置来源已经变化,请刷新后重新选择".to_string()
|
||||
})?;
|
||||
@@ -551,13 +577,15 @@ pub(crate) async fn create_thinking_alias(
|
||||
return Err("别名模型不能和原模型相同".to_string());
|
||||
}
|
||||
|
||||
if available_models
|
||||
.iter()
|
||||
.any(|model| model.name.eq_ignore_ascii_case(&alias))
|
||||
{
|
||||
if available_models.iter().any(|model| {
|
||||
model.name.eq_ignore_ascii_case(&alias)
|
||||
&& !original_alias
|
||||
.as_deref()
|
||||
.is_some_and(|original| original.eq_ignore_ascii_case(&alias))
|
||||
}) {
|
||||
return Err(format!("{alias} 已经是实际模型 ID,不能再作为别名"));
|
||||
}
|
||||
let document = serde_norway::from_str::<serde_norway::Value>(&content)
|
||||
let document = serde_norway::from_str::<serde_norway::Value>(&working_content)
|
||||
.map_err(|error| format!("解析内核 YAML 配置失败: {error}"))?;
|
||||
let root = document
|
||||
.as_mapping()
|
||||
@@ -566,7 +594,7 @@ pub(crate) async fn create_thinking_alias(
|
||||
return Err(format!("别名模型 {alias} 已存在"));
|
||||
}
|
||||
|
||||
let updated = add_model_alias_to_yaml(&content, &source, &alias, &effort, fast)?;
|
||||
let updated = add_model_alias_to_yaml(&working_content, &source, &alias, &effort, fast)?;
|
||||
put_management_alias_config_changes(&config, &content, &updated).await?;
|
||||
thinking_aliases_from_yaml(&updated)
|
||||
}
|
||||
|
||||
@@ -1619,6 +1619,46 @@ pub(crate) fn insert_thinking_effort_params(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Build the replacement in memory; callers only persist after all validation succeeds.
|
||||
pub(crate) fn prepare_model_alias_edit(content: &str, alias: &str) -> Result<String, String> {
|
||||
let mut document = yaml_serde_edit::YamlValue::parse(content)
|
||||
.map_err(|error| format!("解析内核 YAML 配置失败: {error}"))?;
|
||||
let mut updated = document.get().clone();
|
||||
let root = updated
|
||||
.as_mapping_mut()
|
||||
.ok_or_else(|| "内核配置顶层必须是 YAML 映射".to_string())?;
|
||||
let matches = |model: &&serde_norway::Value| {
|
||||
configured_model_identity(model)
|
||||
.is_some_and(|(_, name, _)| name.eq_ignore_ascii_case(alias))
|
||||
};
|
||||
let api_count = MODEL_ALIAS_CONFIG_SECTIONS
|
||||
.into_iter()
|
||||
.filter_map(|section| yaml_mapping_value(root, section))
|
||||
.filter_map(serde_norway::Value::as_sequence)
|
||||
.flatten()
|
||||
.filter_map(serde_norway::Value::as_mapping)
|
||||
.filter_map(|provider| yaml_mapping_value(provider, "models"))
|
||||
.filter_map(serde_norway::Value::as_sequence)
|
||||
.flatten()
|
||||
.filter(matches)
|
||||
.count();
|
||||
let oauth_count = yaml_mapping_value(root, "oauth-model-alias")
|
||||
.and_then(serde_norway::Value::as_mapping)
|
||||
.into_iter()
|
||||
.flat_map(|channels| channels.values())
|
||||
.filter_map(serde_norway::Value::as_sequence)
|
||||
.flatten()
|
||||
.filter(matches)
|
||||
.count();
|
||||
if api_count + oauth_count != 1 {
|
||||
return Err("别名不存在或存在多个同名映射,请刷新并检查配置后重试".to_string());
|
||||
}
|
||||
remove_existing_claude_model_alias(root, alias)?;
|
||||
remove_thinking_payload_model(root, alias)?;
|
||||
remove_speed_payload_model(root, alias)?;
|
||||
render_updated_core_yaml(&mut document, updated)
|
||||
}
|
||||
|
||||
pub(crate) fn add_model_alias_to_yaml(
|
||||
content: &str,
|
||||
source: &ResolvedThinkingAliasSource,
|
||||
|
||||
@@ -671,3 +671,32 @@ fn speed_alias_supports_codex_api_model_entries() {
|
||||
assert_eq!(entries.len(), 1);
|
||||
assert_eq!(entries[0].kind, "codex-api");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn edit_model_alias_replaces_name_effort_and_fast_in_memory() {
|
||||
let source = test_oauth_thinking_source("codex", "gpt-test");
|
||||
let original =
|
||||
add_model_alias_to_yaml("port: 8317\n", &source, "old-alias", "high", true).unwrap();
|
||||
let prepared = prepare_model_alias_edit(&original, "old-alias").unwrap();
|
||||
let updated = add_model_alias_to_yaml(&prepared, &source, "new-alias", "low", false).unwrap();
|
||||
let entries = thinking_aliases_from_yaml(&updated).unwrap();
|
||||
assert_eq!(entries.len(), 1);
|
||||
assert_eq!(entries[0].alias, "new-alias");
|
||||
assert_eq!(entries[0].effort.as_deref(), Some("low"));
|
||||
assert!(speed_aliases_from_yaml(&updated).unwrap().is_empty());
|
||||
assert!(updated.contains("port: 8317"));
|
||||
assert!(!updated.contains("old-alias"));
|
||||
let unchanged_name =
|
||||
add_model_alias_to_yaml(&prepared, &source, "old-alias", "", false).unwrap();
|
||||
assert_eq!(
|
||||
thinking_aliases_from_yaml(&unchanged_name).unwrap()[0].effort,
|
||||
None
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn edit_model_alias_rejects_missing_and_ambiguous_aliases() {
|
||||
assert!(prepare_model_alias_edit("port: 8317\n", "missing").is_err());
|
||||
let content = "oauth-model-alias:\n codex:\n - name: model-a\n alias: shared\n claude:\n - name: model-b\n alias: shared\n";
|
||||
assert!(prepare_model_alias_edit(content, "shared").is_err());
|
||||
}
|
||||
@@ -435,7 +435,7 @@ export const en: Record<MessageKey, string> = {
|
||||
'aliases.createdList.description': 'Each alias shows its fixed reasoning effort and Fast status',
|
||||
'aliases.loadingConfig': 'Loading configuration',
|
||||
'aliases.empty.title': 'No model aliases yet',
|
||||
'aliases.empty.description': 'Select a model and alias option on the left to create one',
|
||||
'aliases.empty.description': 'Click “Create Alias” to add your first model alias',
|
||||
'aliases.unboundEffort': 'No effort bound',
|
||||
'aliases.delete': 'Delete {alias}',
|
||||
'speedAliases.created': 'Created {alias} with the Fast request tier fixed',
|
||||
|
||||
@@ -434,7 +434,7 @@ export const zhCN = {
|
||||
'aliases.createdList.description': '每个别名会显示已固定的思考强度和 Fast 状态',
|
||||
'aliases.loadingConfig': '读取配置中',
|
||||
'aliases.empty.title': '尚未创建模型别名',
|
||||
'aliases.empty.description': '从左侧选择模型和别名选项即可创建',
|
||||
'aliases.empty.description': '点击“创建别名”添加第一个模型别名',
|
||||
'aliases.unboundEffort': '未绑定强度',
|
||||
'aliases.delete': '删除 {alias}',
|
||||
'speedAliases.created': '已创建 {alias},并固定使用 Fast 请求层级',
|
||||
|
||||
@@ -14,8 +14,11 @@ import {
|
||||
Check,
|
||||
GitFork,
|
||||
LoaderCircle,
|
||||
Plus,
|
||||
Search,
|
||||
Pencil,
|
||||
Trash2,
|
||||
X,
|
||||
Zap,
|
||||
} from 'lucide-react';
|
||||
import { getCurrentLocale, translate, useI18n } from '../i18n';
|
||||
@@ -186,6 +189,8 @@ export function ThinkingAliasesPage() {
|
||||
const [effort, setEffort] = useState('');
|
||||
const [fastEnabled, setFastEnabled] = useState(false);
|
||||
const [alias, setAlias] = useState('');
|
||||
const [editingEntry, setEditingEntry] = useState<AliasListEntry | null>(null);
|
||||
const [editorOpen, setEditorOpen] = useState(false);
|
||||
const [search, setSearch] = useState('');
|
||||
const [modelPickerOpen, setModelPickerOpen] = useState(false);
|
||||
const [activeSourceIndex, setActiveSourceIndex] = useState(0);
|
||||
@@ -304,7 +309,7 @@ export function ThinkingAliasesPage() {
|
||||
setSelectedSourceId(source.id);
|
||||
setEffort('');
|
||||
setFastEnabled((current) => current && source.supportsFast);
|
||||
setAlias('');
|
||||
if (!editingEntry) setAlias('');
|
||||
generatedAliasRef.current = '';
|
||||
setModelPickerOpen(false);
|
||||
setSearch('');
|
||||
@@ -352,6 +357,7 @@ export function ThinkingAliasesPage() {
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (editingEntry) return;
|
||||
setAlias((current) => {
|
||||
const currentValue = current.trim();
|
||||
const canReplace = !currentValue || currentValue === generatedAliasRef.current;
|
||||
@@ -359,7 +365,7 @@ export function ThinkingAliasesPage() {
|
||||
generatedAliasRef.current = uniqueDefaultAlias;
|
||||
return uniqueDefaultAlias;
|
||||
});
|
||||
}, [uniqueDefaultAlias]);
|
||||
}, [uniqueDefaultAlias, editingEntry]);
|
||||
|
||||
const createAlias = async () => {
|
||||
if (!selectedSource) {
|
||||
@@ -384,7 +390,14 @@ export function ThinkingAliasesPage() {
|
||||
setError('');
|
||||
setNotice('');
|
||||
try {
|
||||
if (normalizedEffort) {
|
||||
if (editingEntry) {
|
||||
await invoke('create_thinking_alias', {
|
||||
sourceId: selectedSource.id, alias: normalizedAlias,
|
||||
effort: normalizedEffort, fast: fastEnabled, originalAlias: editingEntry.alias,
|
||||
});
|
||||
setNotice('');
|
||||
setEditingEntry(null);
|
||||
} else if (normalizedEffort) {
|
||||
await invoke<ThinkingAliasEntry[]>('create_thinking_alias', {
|
||||
sourceId: selectedSource.id,
|
||||
alias: normalizedAlias,
|
||||
@@ -412,6 +425,7 @@ export function ThinkingAliasesPage() {
|
||||
}
|
||||
setAlias('');
|
||||
await load();
|
||||
setEditorOpen(false);
|
||||
} catch (requestError) {
|
||||
setError(String(requestError));
|
||||
} finally {
|
||||
@@ -420,6 +434,50 @@ export function ThinkingAliasesPage() {
|
||||
}
|
||||
};
|
||||
|
||||
const resetEditor = () => {
|
||||
setEditingEntry(null);
|
||||
setSelectedSourceId('');
|
||||
setEffort('');
|
||||
setFastEnabled(false);
|
||||
setAlias('');
|
||||
setSearch('');
|
||||
setModelPickerOpen(false);
|
||||
generatedAliasRef.current = '';
|
||||
};
|
||||
|
||||
const closeEditor = () => {
|
||||
if (busyAlias) return;
|
||||
setEditorOpen(false);
|
||||
resetEditor();
|
||||
};
|
||||
|
||||
const addAlias = () => {
|
||||
resetEditor();
|
||||
setError('');
|
||||
setNotice('');
|
||||
setEditorOpen(true);
|
||||
};
|
||||
|
||||
const editAlias = (entry: AliasListEntry) => {
|
||||
const source = sources.find((candidate) => candidate.model === entry.sourceModel
|
||||
&& candidate.kind === entry.kind && candidate.provider === entry.provider);
|
||||
if (!source) {
|
||||
setError(`${entry.sourceModel}: ${t('common.unavailable')}`);
|
||||
return;
|
||||
}
|
||||
setEditingEntry(entry);
|
||||
setSelectedSourceId(source.id);
|
||||
setEffort(entry.effort ?? '');
|
||||
setFastEnabled(Boolean(entry.serviceTier));
|
||||
setAlias(entry.alias);
|
||||
generatedAliasRef.current = '';
|
||||
setModelPickerOpen(false);
|
||||
setSearch('');
|
||||
setError('');
|
||||
setNotice('');
|
||||
setEditorOpen(true);
|
||||
};
|
||||
|
||||
const deleteAlias = async (entry: AliasListEntry) => {
|
||||
if (!await askConfirmation({ title: t('common.delete'), message: t('aliases.deleteConfirm', { alias: entry.alias }), confirmText: t('common.delete'), variant: 'danger' })) return;
|
||||
setBusyAlias(entry.alias);
|
||||
@@ -456,16 +514,46 @@ export function ThinkingAliasesPage() {
|
||||
{!error && notice ? <div className="management-alert success">{notice}</div> : null}
|
||||
</div>
|
||||
|
||||
<div className="thinking-alias-workbench">
|
||||
<section className="panel thinking-alias-editor-panel">
|
||||
<header className="management-header">
|
||||
<div>
|
||||
<h1>{t('app.nav.thinkingAliases')}</h1>
|
||||
</div>
|
||||
<div className="management-heading-actions">
|
||||
<span className="muted-summary">{entries.length}</span>
|
||||
<button
|
||||
type="button"
|
||||
className="primary-button compact-button"
|
||||
onClick={addAlias}
|
||||
disabled={loading || Boolean(busyAlias)}
|
||||
title={t('aliases.create')}
|
||||
aria-label={t('aliases.create')}
|
||||
>
|
||||
<Plus size={18} aria-hidden="true" />
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{editorOpen ? (
|
||||
<div className="config-dialog-backdrop" onMouseDown={(event) => event.currentTarget === event.target && closeEditor()}>
|
||||
<section
|
||||
className="panel management-dialog thinking-alias-editor-panel thinking-alias-dialog"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-labelledby="thinking-alias-editor-title"
|
||||
onKeyDown={(event) => { if (event.key === 'Escape') closeEditor(); }}
|
||||
>
|
||||
<div className="thinking-alias-panel-heading">
|
||||
<span><GitFork size={18} /></span>
|
||||
<div>
|
||||
<h2>{t('aliases.create.title')}</h2>
|
||||
<h2 id="thinking-alias-editor-title">{t(editingEntry ? 'common.edit' : 'aliases.create.title')}</h2>
|
||||
<p>{t('aliases.create.description')}</p>
|
||||
</div>
|
||||
<button type="button" className="icon-button quiet" onClick={closeEditor} disabled={Boolean(busyAlias)} title={t('common.close')} aria-label={t('common.close')}>
|
||||
<X size={18} />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="thinking-alias-dialog-body">
|
||||
<div className="thinking-alias-field thinking-model-field">
|
||||
<label htmlFor="thinking-model-search">{t('aliases.originalModel')}</label>
|
||||
<div className="thinking-model-picker" ref={modelPickerRef}>
|
||||
@@ -497,7 +585,7 @@ export function ThinkingAliasesPage() {
|
||||
setSelectedSourceId('');
|
||||
setEffort('');
|
||||
setFastEnabled(false);
|
||||
setAlias('');
|
||||
if (!editingEntry) setAlias('');
|
||||
generatedAliasRef.current = '';
|
||||
}
|
||||
}}
|
||||
@@ -505,7 +593,7 @@ export function ThinkingAliasesPage() {
|
||||
placeholder={loading ? t('aliases.loadingModels') : t('aliases.searchModel')}
|
||||
autoComplete="off"
|
||||
spellCheck={false}
|
||||
disabled={loading}
|
||||
disabled={loading || Boolean(busyAlias)}
|
||||
/>
|
||||
{!modelPickerOpen && selectedSource ? (
|
||||
<span className="thinking-source-kind">
|
||||
@@ -662,7 +750,12 @@ export function ThinkingAliasesPage() {
|
||||
<span>{selectedSource?.model || t('aliases.notSelected')} <ArrowRight size={13} /> {alias || t('aliases.enterAlias')}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="thinking-alias-dialog-actions">
|
||||
<button type="button" className="secondary-button" disabled={Boolean(busyAlias)} onClick={closeEditor}>
|
||||
{t('common.cancel')}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="primary-button thinking-alias-create"
|
||||
@@ -672,19 +765,14 @@ export function ThinkingAliasesPage() {
|
||||
{busyAction === 'create'
|
||||
? <LoaderCircle size={16} className="spin" />
|
||||
: fastEnabled && !normalizedEffort ? <Zap size={16} /> : <GitFork size={16} />}
|
||||
{busyAction === 'create' ? t('aliases.creating') : t('aliases.create')}
|
||||
{busyAction === 'create' ? t(editingEntry ? 'common.saving' : 'aliases.creating') : t(editingEntry ? 'common.save' : 'aliases.create')}
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
<section className="panel thinking-alias-list-panel">
|
||||
<div className="thinking-alias-list-heading">
|
||||
<div>
|
||||
<h2>{t('aliases.createdList.title')}</h2>
|
||||
<span>{t('aliases.createdList.description')}</span>
|
||||
</div>
|
||||
<strong>{entries.length}</strong>
|
||||
</div>
|
||||
|
||||
<div className="thinking-alias-list">
|
||||
{loading ? (
|
||||
<div className="management-loading"><LoaderCircle size={20} className="spin" />{t('aliases.loadingConfig')}</div>
|
||||
@@ -716,10 +804,14 @@ export function ThinkingAliasesPage() {
|
||||
{entry.serviceTier ? (
|
||||
<span className="thinking-effort-badge fast">{t('speedAliases.fast.title')}</span>
|
||||
) : null}
|
||||
<button type="button" className="icon-button quiet" disabled={loading || Boolean(busyAlias)}
|
||||
onClick={() => editAlias(entry)} title={t('common.edit')} aria-label={t('common.edit')}>
|
||||
<Pencil size={15} />
|
||||
</button>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
className="icon-button danger"
|
||||
className="icon-button quiet danger"
|
||||
onClick={() => void deleteAlias(entry)}
|
||||
disabled={Boolean(busyAlias)}
|
||||
title={t('aliases.delete', { alias: entry.alias })}
|
||||
@@ -732,7 +824,6 @@ export function ThinkingAliasesPage() {
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
+100
-21
@@ -4782,6 +4782,85 @@ textarea:focus-visible {
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.thinking-alias-dialog {
|
||||
display: grid;
|
||||
grid-template-rows: auto minmax(0, 1fr) auto;
|
||||
width: min(640px, calc(100vw - 32px));
|
||||
height: auto;
|
||||
min-height: 0;
|
||||
max-height: min(780px, calc(100vh - 40px));
|
||||
padding: 0;
|
||||
gap: 0;
|
||||
overflow: hidden;
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.thinking-alias-dialog .thinking-alias-panel-heading {
|
||||
padding: 18px 20px 16px;
|
||||
}
|
||||
|
||||
.thinking-alias-dialog .thinking-alias-panel-heading > div {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.thinking-alias-dialog .thinking-alias-panel-heading > .icon-button {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.thinking-alias-dialog-body {
|
||||
display: grid;
|
||||
gap: 18px;
|
||||
min-height: 0;
|
||||
padding: 18px 20px;
|
||||
overflow-y: auto;
|
||||
scrollbar-gutter: stable;
|
||||
}
|
||||
|
||||
.thinking-alias-dialog .thinking-alias-field > label {
|
||||
display: block;
|
||||
height: auto;
|
||||
grid-template-rows: none;
|
||||
}
|
||||
|
||||
.thinking-alias-dialog .thinking-field-heading > strong {
|
||||
flex: none;
|
||||
}
|
||||
|
||||
.thinking-alias-dialog .thinking-field-heading {
|
||||
display: grid;
|
||||
justify-content: stretch;
|
||||
gap: 3px;
|
||||
}
|
||||
|
||||
.thinking-alias-dialog .thinking-field-heading > span {
|
||||
min-width: 0;
|
||||
line-height: var(--line-height-body);
|
||||
text-overflow: initial;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
.thinking-alias-dialog .thinking-fast-option {
|
||||
display: flex;
|
||||
grid-template-rows: none;
|
||||
}
|
||||
|
||||
.thinking-alias-dialog-actions {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
padding: 14px 20px 18px;
|
||||
border-top: 1px solid var(--theme-e3e1db);
|
||||
background: var(--theme-fffdf8);
|
||||
}
|
||||
|
||||
.thinking-alias-dialog-actions > button {
|
||||
width: auto;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.thinking-alias-dialog-actions .thinking-alias-create {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.thinking-alias-section-divider {
|
||||
height: 1px;
|
||||
flex: none;
|
||||
@@ -5204,8 +5283,7 @@ textarea:focus-visible {
|
||||
|
||||
.thinking-alias-list-panel {
|
||||
display: grid;
|
||||
grid-template-rows: 64px minmax(0, 1fr);
|
||||
gap: 12px;
|
||||
grid-template-rows: minmax(0, 1fr);
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
@@ -5345,25 +5423,6 @@ textarea:focus-visible {
|
||||
color: var(--theme-805a24);
|
||||
}
|
||||
|
||||
@media (min-width: 1081px) {
|
||||
.thinking-alias-list-panel .thinking-alias-row {
|
||||
grid-template-columns: minmax(0, 1fr) auto 34px;
|
||||
min-height: 94px;
|
||||
}
|
||||
|
||||
.thinking-alias-list-panel .thinking-alias-route {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
|
||||
.thinking-alias-list-panel .thinking-alias-badges {
|
||||
grid-column: 2;
|
||||
}
|
||||
|
||||
.thinking-alias-list-panel .thinking-alias-row > .icon-button {
|
||||
grid-column: 3;
|
||||
}
|
||||
}
|
||||
|
||||
.agent-workbench {
|
||||
display: grid;
|
||||
grid-template-columns: 230px minmax(0, 1fr);
|
||||
@@ -13741,3 +13800,23 @@ body.table-col-resizing * {
|
||||
.agent-save-bar { flex-wrap: wrap; gap: 12px; }
|
||||
.agent-save-actions { margin-left: auto; }
|
||||
}
|
||||
|
||||
.thinking-alias-page > .management-header {
|
||||
align-items: center;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.thinking-alias-page > .management-header .management-heading-actions {
|
||||
width: auto;
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
|
||||
.thinking-alias-page > .management-header .muted-summary {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.thinking-alias-page > .management-header .management-heading-actions button {
|
||||
width: 42px;
|
||||
flex: 0 0 42px;
|
||||
padding: 0;
|
||||
}
|
||||
Reference in new issue
Block a user