mirror of
https://github.com/hansjone/dsh-im-ops.git
synced 2026-10-09 01:53:21 +08:00
119 lines
4 KiB
JavaScript
119 lines
4 KiB
JavaScript
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 helpId = React.useId();
|
||
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', { className: 'dim-presetTitle' },
|
||
h('span', null, 'Agent Preset'),
|
||
h('span', { className: 'dim-presetHelp' },
|
||
h('button', {
|
||
type: 'button',
|
||
className: 'dim-presetHelpButton',
|
||
'aria-label': '查看 Agent Preset 说明',
|
||
'aria-describedby': helpId,
|
||
}, h('span', { 'aria-hidden': 'true' }, '?')),
|
||
h('span', {
|
||
id: helpId,
|
||
className: 'dim-presetTooltip',
|
||
role: 'tooltip',
|
||
}, '只影响新建会话;若当前聊天已有会话,先发送 /new,再发送普通消息生效。'))),
|
||
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,
|
||
)),
|
||
),
|
||
error || currentUnavailable ? h(
|
||
'p',
|
||
{ className: 'dim-presetError', role: error ? 'alert' : 'status' },
|
||
error ?? '当前 Agent Preset 已不可用,请选择其他 Preset 或跟随 Host 默认。',
|
||
) : null,
|
||
);
|
||
}
|