dsh-im-ops/plugin-src/client/agent-preset.js

110 lines
3.6 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import * as React from 'react';
import { h } from './i18n.js';
export const SET_AGENT_PRESET_ENDPOINT = 'bot.preset.set';
const PRESET_ID = /^[a-z0-9][a-z0-9-]*$/;
export const EMPTY_AGENT_PRESET_CATALOG = Object.freeze({
defaultId: '',
items: Object.freeze([]),
});
export const AgentPresetCatalogContext = React.createContext(EMPTY_AGENT_PRESET_CATALOG);
export function normalizeAgentPresetId(value) {
if (typeof value !== 'string') return '';
const id = value.trim();
return PRESET_ID.test(id) ? id : '';
}
export function normalizeAgentPresetCatalog(value) {
if (!value || typeof value !== 'object' || Array.isArray(value)) {
return { defaultId: '', items: [] };
}
const items = [];
const seen = new Set();
for (const entry of Array.isArray(value.items) ? value.items : []) {
const id = typeof entry === 'string'
? normalizeAgentPresetId(entry)
: normalizeAgentPresetId(entry?.id);
if (!id || seen.has(id)) continue;
seen.add(id);
const label = typeof entry?.label === 'string' && entry.label.trim()
? entry.label.trim().slice(0, 128)
: typeof entry?.name === 'string' && entry.name.trim()
? entry.name.trim().slice(0, 128)
: id;
items.push({ id, label });
}
return {
defaultId: normalizeAgentPresetId(value.defaultId),
items,
};
}
export function AgentPresetEditor({ agentPreset = '', disabled = false, onSave }) {
const catalog = React.useContext(AgentPresetCatalogContext) ?? EMPTY_AGENT_PRESET_CATALOG;
const current = normalizeAgentPresetId(agentPreset);
const [saving, setSaving] = React.useState(false);
const [error, setError] = React.useState(null);
const items = [];
const seen = new Set();
for (const item of Array.isArray(catalog.items) ? catalog.items : []) {
if (!item?.id || seen.has(item.id)) continue;
seen.add(item.id);
items.push(item);
}
const currentUnavailable = Boolean(current && !seen.has(current));
if (currentUnavailable) items.push({ id: current, label: current, unavailable: true });
const inheritLabel = '跟随 Host 默认';
const change = async (event) => {
const next = event.target.value;
if (next === current || saving || disabled) return;
setSaving(true);
setError(null);
try {
await onSave?.(next || null);
} catch (cause) {
setError(cause?.message ?? 'Agent Preset 修改失败,请重试。');
} finally {
setSaving(false);
}
};
return h('div', { className: 'dim-preset' },
h('div', { className: 'dim-presetHeader' },
h('span', null, 'Agent Preset'),
saving ? h('span', { className: 'dim-presetStatus' }, '保存中…') : null),
React.createElement('select', {
className: 'dim-presetSelect',
value: current,
disabled: disabled || saving,
'aria-label': 'Agent Preset',
onChange: (event) => { void change(event); },
},
h('option', { value: '' }, inheritLabel),
...items.map((item) => h(
'option',
{ key: item.id, value: item.id },
item.unavailable
? [item.id, '(已不可用)']
: item.label && item.label !== item.id ? `${item.label}(${item.id})` : item.id,
)),
),
h(
'small',
{ className: 'dim-presetHelp' },
'只影响新建会话;若当前聊天已有会话,先发送 /new,再发送普通消息生效。',
),
error || currentUnavailable ? h(
'p',
{ className: 'dim-presetError', role: error ? 'alert' : 'status' },
error ?? '当前 Agent Preset 已不可用,请选择其他 Preset 或跟随 Host 默认。',
) : null,
);
}