mirror of
https://github.com/hansjone/dsh-im-ops.git
synced 2026-10-09 01:53:21 +08:00
feat: split group and direct context enhancement
This commit is contained in:
parent
38096d1589
commit
75ebaebf2e
21 changed files with 1423 additions and 873 deletions
|
|
@ -2,9 +2,10 @@ import * as React from 'react';
|
|||
import { createPortal } from 'react-dom';
|
||||
|
||||
import {
|
||||
CONTEXT_DIRECT_GUIDANCE_EXAMPLE,
|
||||
CONTEXT_ENHANCEMENT_FIELDS,
|
||||
CONTEXT_ENHANCEMENT_GUIDANCE_MAX_LENGTH,
|
||||
CONTEXT_GUIDANCE_EXAMPLE,
|
||||
CONTEXT_GROUP_GUIDANCE_EXAMPLE,
|
||||
normalizeContextEnhancementConfig,
|
||||
validateContextEnhancementConfig,
|
||||
} from '../../src/channels/shared/context-enhancement.mjs';
|
||||
|
|
@ -18,11 +19,34 @@ const FIELD_LABELS = Object.freeze({
|
|||
botId: '机器人标识',
|
||||
});
|
||||
|
||||
const SCOPE_COPY = Object.freeze({
|
||||
group: Object.freeze({
|
||||
title: '群聊',
|
||||
enable: '启用',
|
||||
fieldsHelpLabel: '查看群聊来源字段说明',
|
||||
senderNameHelpLabel: '查看群聊发送者昵称字段说明',
|
||||
guidanceLabel: '增强提示词',
|
||||
guidanceHelpLabel: '查看群聊增强提示词使用说明',
|
||||
guidanceUsage: '用于告诉模型如何使用当前群聊消息的 <dsh_im_source> 来源字段。只填写正文,插件会自动添加 <dsh_im_source_guidance> 成对标签。',
|
||||
guidanceBehavior: '仅在群聊开关开启时使用。清空并保存后不再附加群聊增强提示词;所选来源字段仍按当前场景设置发送。',
|
||||
}),
|
||||
direct: Object.freeze({
|
||||
title: '私聊',
|
||||
enable: '启用',
|
||||
fieldsHelpLabel: '查看私聊来源字段说明',
|
||||
senderNameHelpLabel: '查看私聊发送者昵称字段说明',
|
||||
guidanceLabel: '增强提示词',
|
||||
guidanceHelpLabel: '查看私聊增强提示词使用说明',
|
||||
guidanceUsage: '用于告诉模型如何使用当前私聊消息的 <dsh_im_source> 来源字段。只填写正文,插件会自动添加 <dsh_im_source_guidance> 成对标签。',
|
||||
guidanceBehavior: '仅在私聊开关开启时使用。清空并保存后不再附加私聊增强提示词;所选来源字段仍按当前场景设置发送。',
|
||||
}),
|
||||
});
|
||||
|
||||
export function contextEnhancementLabel(config) {
|
||||
const { groupEnabled, directEnabled } = normalizeContextEnhancementConfig(config);
|
||||
if (groupEnabled && directEnabled) return '群聊和私聊';
|
||||
if (groupEnabled) return '仅群聊';
|
||||
if (directEnabled) return '仅私聊';
|
||||
const { group, direct } = normalizeContextEnhancementConfig(config);
|
||||
if (group.enabled && direct.enabled) return '群聊和私聊';
|
||||
if (group.enabled) return '仅群聊';
|
||||
if (direct.enabled) return '仅私聊';
|
||||
return '未开启';
|
||||
}
|
||||
|
||||
|
|
@ -37,27 +61,132 @@ function ContextIcon({ kind = 'sliders' }) {
|
|||
}, h('path', { d: path }));
|
||||
}
|
||||
|
||||
function ContextEnhancementScopeEditor({
|
||||
kind,
|
||||
scope,
|
||||
example,
|
||||
supported,
|
||||
busy,
|
||||
idPrefix,
|
||||
onChange,
|
||||
}) {
|
||||
const copy = SCOPE_COPY[kind];
|
||||
const unavailableId = `${idPrefix}-${kind}-unavailable`;
|
||||
const fieldsHelpId = `${idPrefix}-${kind}-fields-help`;
|
||||
const senderNameHelpId = `${idPrefix}-${kind}-sender-name-help`;
|
||||
const guidanceId = `${idPrefix}-${kind}-guidance`;
|
||||
const guidanceHelpId = `${idPrefix}-${kind}-guidance-help`;
|
||||
const disabled = busy || !supported;
|
||||
|
||||
return h('fieldset', {
|
||||
className: 'dim-contextSection dim-contextScope',
|
||||
'data-context-kind': kind,
|
||||
'aria-label': copy.title,
|
||||
disabled,
|
||||
},
|
||||
h('label', { className: 'dim-contextSwitchRow' },
|
||||
h('span', { className: 'dim-contextSwitchLabel' },
|
||||
h('span', null, copy.enable),
|
||||
!supported ? h('span', {
|
||||
id: unavailableId, className: 'dim-contextUnavailable',
|
||||
}, '(当前渠道不支持群聊)') : null),
|
||||
h('input', {
|
||||
type: 'checkbox', role: 'switch', className: 'dim-contextSwitch',
|
||||
checked: supported && scope.enabled,
|
||||
disabled,
|
||||
'aria-describedby': !supported ? unavailableId : undefined,
|
||||
onChange: (event) => { if (supported) onChange('enabled', event.target.checked); },
|
||||
})),
|
||||
h('div', { className: 'dim-contextScopeBlock' },
|
||||
h('div', { className: 'dim-contextLegend' },
|
||||
h('span', null, '来源字段'),
|
||||
h('span', { className: 'dim-contextHelp dim-contextLegendHelp' },
|
||||
h('button', {
|
||||
type: 'button', className: 'dim-contextHelpButton', disabled,
|
||||
'aria-label': copy.fieldsHelpLabel, 'aria-describedby': fieldsHelpId,
|
||||
}, h('span', { 'aria-hidden': 'true' }, '?')),
|
||||
h('span', { id: fieldsHelpId, className: 'dim-contextTooltip dim-contextLegendTooltip', role: 'tooltip' },
|
||||
'增强提示词中请使用字段名(如 senderId、conversationType)引用这些信息。只发送当前会话中勾选且可用的字段,不会额外查询或补全。'))),
|
||||
h('div', { className: 'dim-contextFields' }, CONTEXT_ENHANCEMENT_FIELDS.map((field) => {
|
||||
const fieldId = `${idPrefix}-${kind}-field-${field}`;
|
||||
return h('div', { key: field, className: 'dim-contextField' },
|
||||
h('input', {
|
||||
id: fieldId, type: 'checkbox', name: `${kind}-${field}`,
|
||||
checked: scope.fields.includes(field), disabled,
|
||||
onChange: (event) => onChange('fields', event.target.checked
|
||||
? [...scope.fields, field] : scope.fields.filter((value) => value !== field)),
|
||||
}),
|
||||
h('span', { className: 'dim-contextFieldText' },
|
||||
h('label', { className: 'dim-contextFieldName', htmlFor: fieldId }, FIELD_LABELS[field]),
|
||||
field === 'senderName' ? h('span', { className: 'dim-contextHelp dim-contextFieldHelp' },
|
||||
h('button', {
|
||||
type: 'button', className: 'dim-contextHelpButton dim-contextFieldHelpButton', disabled,
|
||||
'aria-label': copy.senderNameHelpLabel, 'aria-describedby': senderNameHelpId,
|
||||
}, h('span', { 'aria-hidden': 'true' }, '?')),
|
||||
h('span', {
|
||||
id: senderNameHelpId,
|
||||
className: 'dim-contextTooltip dim-contextFieldTooltip',
|
||||
role: 'tooltip',
|
||||
}, '该字段不是每个渠道都能提供。当前消息没有发送者昵称时,即使已选择该字段,<dsh_im_source> 中也会省略 senderName。')) : null,
|
||||
h('label', { className: 'dim-contextFieldKey', htmlFor: fieldId }, field)));
|
||||
}))),
|
||||
h('div', { className: 'dim-contextGuidance dim-contextScopeBlock' },
|
||||
h('div', { className: 'dim-contextEditorHeader' },
|
||||
h('span', { className: 'dim-contextEditorTitle' },
|
||||
h('label', { htmlFor: guidanceId }, copy.guidanceLabel),
|
||||
h('span', { className: 'dim-contextHelp' },
|
||||
h('button', {
|
||||
type: 'button', className: 'dim-contextHelpButton', disabled,
|
||||
'aria-label': copy.guidanceHelpLabel, 'aria-describedby': guidanceHelpId,
|
||||
}, h('span', { 'aria-hidden': 'true' }, '?')),
|
||||
h('span', { id: guidanceHelpId, className: 'dim-contextTooltip dim-contextGuidanceTooltip', role: 'tooltip' },
|
||||
h('strong', null, '使用说明'),
|
||||
h('span', null, copy.guidanceUsage),
|
||||
h('strong', null, '生效规则'),
|
||||
h('span', null, copy.guidanceBehavior),
|
||||
h('strong', null, '隐私提示'),
|
||||
h('span', null, '发送者标识可能包含平台用户 ID 或电话号码形式的标识。关闭开关不会删除已经写入会话历史的信息。'),
|
||||
h('strong', null, '使用示例'),
|
||||
h('span', { className: 'dim-contextTooltipExample' }, example)))),
|
||||
h('div', { className: 'dim-contextTextActions' },
|
||||
h('button', { type: 'button', disabled, onClick: () => onChange('guidance', example) }, '填入示例'),
|
||||
h('button', { type: 'button', disabled, onClick: () => onChange('guidance', '') }, '清空'))),
|
||||
h('textarea', {
|
||||
id: guidanceId, value: scope.guidance, placeholder: example, rows: 4, disabled,
|
||||
'data-context-kind': kind,
|
||||
maxLength: CONTEXT_ENHANCEMENT_GUIDANCE_MAX_LENGTH,
|
||||
'aria-describedby': guidanceHelpId,
|
||||
onChange: (event) => onChange('guidance', event.target.value),
|
||||
})));
|
||||
}
|
||||
|
||||
function ContextEnhancementDialog({ config, groupSupported, disabled, onSave, onClose, returnFocusRef, id }) {
|
||||
// A mounted dialog owns its draft; status refreshes must not replace unsaved edits.
|
||||
const [draft, setDraft] = React.useState(() => ({
|
||||
...normalizeContextEnhancementConfig(config),
|
||||
...(!groupSupported ? { groupEnabled: false } : {}),
|
||||
}));
|
||||
const [draft, setDraft] = React.useState(() => {
|
||||
const normalized = normalizeContextEnhancementConfig(config);
|
||||
return {
|
||||
...normalized,
|
||||
...(!groupSupported ? {
|
||||
group: { ...normalized.group, enabled: false },
|
||||
} : {}),
|
||||
};
|
||||
});
|
||||
const [saving, setSaving] = React.useState(false);
|
||||
const [error, setError] = React.useState(null);
|
||||
const [activeScope, setActiveScope] = React.useState('direct');
|
||||
const savingRef = React.useRef(false);
|
||||
const dialogRef = React.useRef(null);
|
||||
const mountedRef = React.useRef(true);
|
||||
const groupTabRef = React.useRef(null);
|
||||
const directTabRef = React.useRef(null);
|
||||
const titleId = React.useId();
|
||||
const descriptionId = React.useId();
|
||||
const groupNoticeId = React.useId();
|
||||
const guidanceId = React.useId();
|
||||
const guidanceHelpId = React.useId();
|
||||
const fieldIdPrefix = React.useId();
|
||||
const fieldsHelpId = React.useId();
|
||||
const senderNameHelpId = React.useId();
|
||||
const guidanceExample = localizeText(CONTEXT_GUIDANCE_EXAMPLE);
|
||||
const scopeIdPrefix = React.useId();
|
||||
const groupGuidanceExample = localizeText(CONTEXT_GROUP_GUIDANCE_EXAMPLE);
|
||||
const directGuidanceExample = localizeText(CONTEXT_DIRECT_GUIDANCE_EXAMPLE);
|
||||
const busy = disabled || saving;
|
||||
const scopeKinds = ['direct', 'group'];
|
||||
const tabRefs = { group: groupTabRef, direct: directTabRef };
|
||||
|
||||
React.useEffect(() => {
|
||||
mountedRef.current = true;
|
||||
|
|
@ -73,9 +202,12 @@ function ContextEnhancementDialog({ config, groupSupported, disabled, onSave, on
|
|||
};
|
||||
}, [returnFocusRef]);
|
||||
|
||||
const change = (key, value) => {
|
||||
const changeScope = (kind, key, value) => {
|
||||
if (busy || savingRef.current) return;
|
||||
setDraft((current) => ({ ...current, [key]: value }));
|
||||
setDraft((current) => ({
|
||||
...current,
|
||||
[kind]: { ...current[kind], [key]: value },
|
||||
}));
|
||||
setError(null);
|
||||
};
|
||||
|
||||
|
|
@ -83,6 +215,25 @@ function ContextEnhancementDialog({ config, groupSupported, disabled, onSave, on
|
|||
if (!savingRef.current) onClose();
|
||||
};
|
||||
|
||||
const activateScope = (kind, focus = false) => {
|
||||
if (busy || !scopeKinds.includes(kind)) return;
|
||||
setActiveScope(kind);
|
||||
if (focus) tabRefs[kind].current?.focus?.();
|
||||
};
|
||||
|
||||
const handleTabKeyDown = (event, kind) => {
|
||||
let next;
|
||||
if (event.key === 'Home') next = scopeKinds[0];
|
||||
else if (event.key === 'End') next = scopeKinds.at(-1);
|
||||
else if (event.key === 'ArrowLeft' || event.key === 'ArrowRight') {
|
||||
const offset = event.key === 'ArrowRight' ? 1 : -1;
|
||||
next = scopeKinds[(scopeKinds.indexOf(kind) + offset + scopeKinds.length) % scopeKinds.length];
|
||||
}
|
||||
if (!next) return;
|
||||
event.preventDefault();
|
||||
activateScope(next, true);
|
||||
};
|
||||
|
||||
const save = async () => {
|
||||
if (busy || savingRef.current) return;
|
||||
savingRef.current = true;
|
||||
|
|
@ -155,90 +306,42 @@ function ContextEnhancementDialog({ config, groupSupported, disabled, onSave, on
|
|||
type: 'button', className: 'dim-contextClose', 'aria-label': '关闭弹窗',
|
||||
disabled: saving, onClick: cancel,
|
||||
}, h(ContextIcon, { kind: 'close' }))),
|
||||
h('fieldset', { className: 'dim-contextSection', disabled: busy },
|
||||
h('legend', null, '启用范围'),
|
||||
h('div', { className: 'dim-contextSwitches' },
|
||||
h('label', { className: 'dim-contextSwitchRow' },
|
||||
h('span', { className: 'dim-contextSwitchLabel' },
|
||||
h('span', null, '群聊中启用'),
|
||||
!groupSupported ? h('span', {
|
||||
id: groupNoticeId, className: 'dim-contextUnavailable',
|
||||
}, '(当前渠道不支持群聊)') : null),
|
||||
h('input', {
|
||||
type: 'checkbox', role: 'switch', className: 'dim-contextSwitch',
|
||||
checked: groupSupported && draft.groupEnabled,
|
||||
disabled: busy || !groupSupported,
|
||||
'aria-describedby': !groupSupported ? groupNoticeId : undefined,
|
||||
onChange: (event) => { if (groupSupported) change('groupEnabled', event.target.checked); },
|
||||
})),
|
||||
h('label', { className: 'dim-contextSwitchRow' },
|
||||
h('span', null, '私聊中启用'),
|
||||
h('input', {
|
||||
type: 'checkbox', role: 'switch', className: 'dim-contextSwitch',
|
||||
checked: draft.directEnabled, disabled: busy,
|
||||
onChange: (event) => change('directEnabled', event.target.checked),
|
||||
}))),
|
||||
),
|
||||
h('fieldset', { className: 'dim-contextSection', disabled: busy },
|
||||
h('legend', { className: 'dim-contextLegend' },
|
||||
h('span', null, '来源字段'),
|
||||
h('span', { className: 'dim-contextHelp dim-contextLegendHelp' },
|
||||
h('button', {
|
||||
type: 'button', className: 'dim-contextHelpButton', disabled: busy,
|
||||
'aria-label': '查看来源字段说明', 'aria-describedby': fieldsHelpId,
|
||||
}, h('span', { 'aria-hidden': 'true' }, '?')),
|
||||
h('span', { id: fieldsHelpId, className: 'dim-contextTooltip dim-contextLegendTooltip', role: 'tooltip' },
|
||||
'增强提示词中请使用字段名(如 senderId、conversationType)引用这些信息。只发送勾选且当前消息中可用的字段,不会额外查询或补全。'))),
|
||||
h('div', { className: 'dim-contextFields' }, CONTEXT_ENHANCEMENT_FIELDS.map((field) => {
|
||||
const fieldId = `${fieldIdPrefix}-${field}`;
|
||||
return h('div', { key: field, className: 'dim-contextField' },
|
||||
h('input', {
|
||||
id: fieldId, type: 'checkbox', name: field,
|
||||
checked: draft.fields.includes(field), disabled: busy,
|
||||
onChange: (event) => change('fields', event.target.checked
|
||||
? [...draft.fields, field] : draft.fields.filter((value) => value !== field)),
|
||||
}),
|
||||
h('span', { className: 'dim-contextFieldText' },
|
||||
h('label', { className: 'dim-contextFieldName', htmlFor: fieldId }, FIELD_LABELS[field]),
|
||||
field === 'senderName' ? h('span', { className: 'dim-contextHelp dim-contextFieldHelp' },
|
||||
h('button', {
|
||||
type: 'button', className: 'dim-contextHelpButton dim-contextFieldHelpButton', disabled: busy,
|
||||
'aria-label': '查看发送者昵称字段说明', 'aria-describedby': senderNameHelpId,
|
||||
}, h('span', { 'aria-hidden': 'true' }, '?')),
|
||||
h('span', {
|
||||
id: senderNameHelpId,
|
||||
className: 'dim-contextTooltip dim-contextFieldTooltip',
|
||||
role: 'tooltip',
|
||||
}, '该字段不是每个渠道都能提供。当前消息没有发送者昵称时,即使已选择该字段,<dsh_im_source> 中也会省略 senderName。')) : null,
|
||||
h('label', { className: 'dim-contextFieldKey', htmlFor: fieldId }, field)));
|
||||
}))),
|
||||
h('div', { className: 'dim-contextGuidance' },
|
||||
h('div', { className: 'dim-contextEditorHeader' },
|
||||
h('span', { className: 'dim-contextEditorTitle' },
|
||||
h('label', { htmlFor: guidanceId }, '增强提示词'),
|
||||
h('span', { className: 'dim-contextHelp' },
|
||||
h('button', {
|
||||
type: 'button', className: 'dim-contextHelpButton', disabled: busy,
|
||||
'aria-label': '查看增强提示词使用说明', 'aria-describedby': guidanceHelpId,
|
||||
}, h('span', { 'aria-hidden': 'true' }, '?')),
|
||||
h('span', { id: guidanceHelpId, className: 'dim-contextTooltip dim-contextGuidanceTooltip', role: 'tooltip' },
|
||||
h('strong', null, '使用说明'),
|
||||
h('span', null, '用于告诉模型如何使用 <dsh_im_source> 中已选择的来源字段。只填写正文,插件会自动添加 <dsh_im_source_guidance> 成对标签。'),
|
||||
h('strong', null, '生效规则'),
|
||||
h('span', null, '只需填写正文,插件自动添加 <dsh_im_source_guidance> 成对标签。清空并保存后,不再附加增强提示词,已选来源字段仍按开关设置发送。'),
|
||||
h('strong', null, '隐私提示'),
|
||||
h('span', null, '发送者标识可能包含平台用户 ID 或电话号码形式的标识。关闭开关不会删除已经写入会话历史的信息。'),
|
||||
h('strong', null, '使用示例'),
|
||||
h('span', { className: 'dim-contextTooltipExample' }, guidanceExample)))),
|
||||
h('div', { className: 'dim-contextTextActions' },
|
||||
h('button', { type: 'button', disabled: busy, onClick: () => change('guidance', guidanceExample) }, '填入示例'),
|
||||
h('button', { type: 'button', disabled: busy, onClick: () => change('guidance', '') }, '清空'))),
|
||||
h('textarea', {
|
||||
id: guidanceId, value: draft.guidance, placeholder: guidanceExample, rows: 4, disabled: busy,
|
||||
maxLength: CONTEXT_ENHANCEMENT_GUIDANCE_MAX_LENGTH,
|
||||
'aria-describedby': guidanceHelpId,
|
||||
onChange: (event) => change('guidance', event.target.value),
|
||||
h('div', { className: 'dim-contextTabs', role: 'tablist', 'aria-label': '上下文增强范围' },
|
||||
scopeKinds.map((kind) => {
|
||||
const selected = activeScope === kind;
|
||||
return h('button', {
|
||||
key: kind,
|
||||
id: `${scopeIdPrefix}-${kind}-tab`,
|
||||
ref: tabRefs[kind],
|
||||
type: 'button',
|
||||
role: 'tab',
|
||||
className: 'dim-contextTab',
|
||||
'data-context-kind': kind,
|
||||
'aria-selected': selected,
|
||||
'aria-controls': `${scopeIdPrefix}-${kind}-panel`,
|
||||
tabIndex: selected ? 0 : -1,
|
||||
disabled: busy,
|
||||
onClick: () => activateScope(kind),
|
||||
onKeyDown: (event) => handleTabKeyDown(event, kind),
|
||||
}, SCOPE_COPY[kind].title);
|
||||
})),
|
||||
scopeKinds.map((kind) => h('div', {
|
||||
key: kind,
|
||||
id: `${scopeIdPrefix}-${kind}-panel`,
|
||||
className: 'dim-contextTabPanel',
|
||||
role: 'tabpanel',
|
||||
'data-context-kind': kind,
|
||||
'aria-labelledby': `${scopeIdPrefix}-${kind}-tab`,
|
||||
hidden: activeScope !== kind,
|
||||
}, h(ContextEnhancementScopeEditor, {
|
||||
kind,
|
||||
scope: draft[kind],
|
||||
example: kind === 'group' ? groupGuidanceExample : directGuidanceExample,
|
||||
supported: kind === 'group' ? groupSupported : true,
|
||||
busy,
|
||||
idPrefix: scopeIdPrefix,
|
||||
onChange: (key, value) => changeScope(kind, key, value),
|
||||
}))),
|
||||
error ? h('p', { className: 'dim-contextError', role: 'alert' }, error) : null,
|
||||
h('footer', { className: 'dim-contextFooter' },
|
||||
h('button', { type: 'button', disabled: saving, onClick: cancel }, '取消'),
|
||||
|
|
@ -256,7 +359,9 @@ export function ContextEnhancementEditor({ config, groupSupported = true, disabl
|
|||
const dialogId = React.useId();
|
||||
const statusId = React.useId();
|
||||
const saved = normalizeContextEnhancementConfig(config);
|
||||
const label = contextEnhancementLabel(groupSupported ? saved : { ...saved, groupEnabled: false });
|
||||
const label = contextEnhancementLabel(groupSupported ? saved : {
|
||||
...saved, group: { ...saved.group, enabled: false },
|
||||
});
|
||||
|
||||
return h(React.Fragment, null,
|
||||
h('button', {
|
||||
|
|
|
|||
|
|
@ -216,31 +216,44 @@ const EN = Object.freeze({
|
|||
'群聊和私聊': 'Groups and direct',
|
||||
'关闭弹窗': 'Close dialog',
|
||||
'查看上下文增强说明': 'View context enhancement details',
|
||||
'上下文增强范围': 'Context enhancement scope',
|
||||
'启用范围': 'Enable in',
|
||||
'群聊中启用': 'Enable in group chats',
|
||||
'私聊中启用': 'Enable in direct chats',
|
||||
'启用': 'Enabled',
|
||||
'(当前渠道不支持群聊)': '(This channel does not support group chats)',
|
||||
'来源字段': 'Source fields',
|
||||
'查看来源字段说明': 'View source field details',
|
||||
'查看群聊来源字段说明': 'View group source field details',
|
||||
'查看私聊来源字段说明': 'View direct source field details',
|
||||
'渠道': 'Channel',
|
||||
'会话类型': 'Conversation type',
|
||||
'发送者标识': 'Sender ID',
|
||||
'发送者昵称': 'Sender name',
|
||||
'查看发送者昵称字段说明': 'View sender name field details',
|
||||
'查看群聊发送者昵称字段说明': 'View group sender name field details',
|
||||
'查看私聊发送者昵称字段说明': 'View direct sender name field details',
|
||||
'该字段不是每个渠道都能提供。当前消息没有发送者昵称时,即使已选择该字段,<dsh_im_source> 中也会省略 senderName。': 'This field is not available on every channel. If the current message has no sender name, <dsh_im_source> omits senderName even when the field is selected.',
|
||||
'机器人标识': 'Bot ID',
|
||||
'增强提示词': 'Guidance',
|
||||
'查看增强提示词使用说明': 'View guidance instructions',
|
||||
'查看群聊增强提示词使用说明': 'View group guidance instructions',
|
||||
'查看私聊增强提示词使用说明': 'View direct guidance instructions',
|
||||
'使用说明': 'How to use',
|
||||
'用于告诉模型如何使用 <dsh_im_source> 中已选择的来源字段。只填写正文,插件会自动添加 <dsh_im_source_guidance> 成对标签。': 'Tell the model how to use the selected fields in <dsh_im_source>. Enter only the body; the plugin adds paired <dsh_im_source_guidance> tags.',
|
||||
'生效规则': 'Behavior',
|
||||
'隐私提示': 'Privacy',
|
||||
'使用示例': 'Example',
|
||||
'仅依据当前消息的 <dsh_im_source> 中实际提供的字段理解来源;没有提供的字段不要猜测或补全。\nconversationType是群聊时回复严肃一点,conversationType是私聊时回复一定要幽默搞笑,像周星驰的电影一样搞笑': 'Understand the source only from fields actually present in <dsh_im_source> for the current message; do not guess or fill in missing fields.\nWhen conversationType is group, reply more seriously. When conversationType is direct, make the reply humorous and funny.',
|
||||
'仅依据当前消息的 <dsh_im_source> 中实际提供的字段理解来源;没有提供的字段不要猜测或补全。\n当前消息来自群聊,请使用严肃、克制、简洁的表达方式。': 'Understand the source only from fields actually present in <dsh_im_source> for the current message; do not guess or fill in missing fields.\nThis message is from a group chat. Use a serious, restrained, and concise style.',
|
||||
'仅依据当前消息的 <dsh_im_source> 中实际提供的字段理解来源;没有提供的字段不要猜测或补全。\n当前消息来自私聊,可以使用更轻松、幽默、详细的表达方式。': 'Understand the source only from fields actually present in <dsh_im_source> for the current message; do not guess or fill in missing fields.\nThis message is from a direct chat. You may use a lighter, more humorous, and detailed style.',
|
||||
'填入示例': 'Use example',
|
||||
'清空': 'Clear',
|
||||
'选择在哪些会话中启用、提供哪些来源字段,以及如何使用这些信息。仅使用已有消息元数据,不查询平台 API。': 'Choose which conversations to enhance, which source fields to include, and how to use them. Only existing message metadata is used; no platform APIs are queried.',
|
||||
'增强提示词中请使用字段名(如 senderId、conversationType)引用这些信息。只发送勾选且当前消息中可用的字段,不会额外查询或补全。': 'Reference these values by field name (such as senderId or conversationType) in the guidance. Only selected fields available in the current message are sent; no extra lookups are made.',
|
||||
'增强提示词中请使用字段名(如 senderId、conversationType)引用这些信息。只发送当前会话中勾选且可用的字段,不会额外查询或补全。': 'Reference these values by field name (such as senderId or conversationType) in the guidance. Only fields selected and available in the current conversation are sent; no extra lookups are made.',
|
||||
'用于告诉模型如何使用当前群聊消息的 <dsh_im_source> 来源字段。只填写正文,插件会自动添加 <dsh_im_source_guidance> 成对标签。': 'Tell the model how to use <dsh_im_source> fields for the current group message. Enter only the body; the plugin adds paired <dsh_im_source_guidance> tags.',
|
||||
'用于告诉模型如何使用当前私聊消息的 <dsh_im_source> 来源字段。只填写正文,插件会自动添加 <dsh_im_source_guidance> 成对标签。': 'Tell the model how to use <dsh_im_source> fields for the current direct message. Enter only the body; the plugin adds paired <dsh_im_source_guidance> tags.',
|
||||
'仅在群聊开关开启时使用。清空并保存后不再附加群聊增强提示词;所选来源字段仍按当前场景设置发送。': 'Used only when group enhancement is enabled. Clear and save to omit group guidance; selected source fields still follow the group settings.',
|
||||
'仅在私聊开关开启时使用。清空并保存后不再附加私聊增强提示词;所选来源字段仍按当前场景设置发送。': 'Used only when direct enhancement is enabled. Clear and save to omit direct guidance; selected source fields still follow the direct settings.',
|
||||
'只需填写正文,插件自动添加 <dsh_im_source_guidance> 成对标签。清空并保存后,不再附加增强提示词,已选来源字段仍按开关设置发送。': 'Enter only the body; the plugin adds paired <dsh_im_source_guidance> tags. Clear and save to omit guidance. Selected source fields still follow the conversation switches.',
|
||||
'发送者标识可能包含平台用户 ID 或电话号码形式的标识。关闭开关不会删除已经写入会话历史的信息。': 'Sender IDs may contain platform user IDs or phone-number-like identifiers. Turning this off does not remove information already saved in conversation history.',
|
||||
'上下文增强保存失败,请重试。': 'Could not save context enhancement. Try again.',
|
||||
|
|
|
|||
|
|
@ -228,10 +228,15 @@ const CSS = String.raw`
|
|||
.dim-contextHeaderTooltip { width: min(340px, calc(100vw - 72px)); }
|
||||
.dim-contextClose { width: 30px; height: 30px; flex: none; display: grid; place-items: center; padding: 0; border: 0; border-radius: 6px; color: var(--dsw-alias-label-secondary, #646a73); background: transparent; cursor: pointer; }
|
||||
.dim-contextClose:hover:not(:disabled) { background: var(--dsw-alias-interactive-bg-hover, #eef0f3); }
|
||||
.dim-contextSection { min-width: 0; margin: 14px 0; padding: 0; border: 0; }
|
||||
.dim-contextSection legend { margin-bottom: 8px; padding: 0; font-weight: 500; }
|
||||
.dim-contextTabs { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 3px; margin-top: 14px; padding: 3px; border: 1px solid var(--dsw-alias-border-l1, #eef0f3); border-radius: 8px; background: var(--dsw-alias-bg-module-platform, #f7f8fa); }
|
||||
.dim-contextTab { min-width: 0; min-height: 34px; padding: 5px 12px; border: 0; border-radius: 6px; color: var(--dsw-alias-label-secondary, #646a73); background: transparent; font: inherit; font-weight: 500; cursor: pointer; transition: color .15s ease, background .15s ease, box-shadow .15s ease; }
|
||||
.dim-contextTab:hover:not(:disabled):not([aria-selected="true"]) { color: var(--dsw-alias-label-primary, #1f2329); background: var(--dsw-alias-interactive-bg-hover, #eef0f3); }
|
||||
.dim-contextTab[aria-selected="true"] { color: var(--dsw-alias-state-business-primary, #3370ff); background: var(--dsw-alias-bg-layer-1, #fff); box-shadow: 0 1px 3px rgb(31 35 41 / 12%); }
|
||||
.dim-contextTabPanel[hidden] { display: none; }
|
||||
.dim-contextSection { min-width: 0; margin: 0; padding: 0; border: 0; }
|
||||
.dim-contextScope { margin-top: 10px; padding: 12px; border: 1px solid var(--dsw-alias-border-l2, #dfe1e5); border-radius: 9px; background: var(--dsw-alias-bg-layer-1, #fff); }
|
||||
.dim-contextScopeBlock { margin-top: 12px; }
|
||||
.dim-contextLegend { position: relative; display: inline-flex; align-items: center; gap: 6px; }
|
||||
.dim-contextSwitches { display: grid; grid-template-columns: 1fr; gap: 2px; }
|
||||
.dim-contextSwitchRow { display: flex; align-items: center; justify-content: space-between; gap: 8px; min-height: 34px; cursor: pointer; }
|
||||
.dim-contextSwitchLabel { min-width: 0; display: inline-flex; align-items: baseline; gap: 5px; flex-wrap: wrap; }
|
||||
.dim-contextUnavailable { color: var(--dsw-alias-label-tertiary, #8f959e); font-size: 11px; line-height: 16px; font-weight: 400; }
|
||||
|
|
@ -274,7 +279,7 @@ const CSS = String.raw`
|
|||
.dim-contextEntry:focus-visible, .dim-contextDialog button:focus-visible, .dim-contextDialog input:focus-visible, .dim-contextDialog textarea:focus-visible { outline: 2px solid var(--dsw-alias-state-business-primary, #3370ff); outline-offset: 2px; }
|
||||
.dim-contextEntry:disabled, .dim-contextDialog button:disabled, .dim-contextDialog input:disabled, .dim-contextDialog textarea:disabled { opacity: .55; cursor: not-allowed; }
|
||||
@media (pointer: coarse) {
|
||||
.dim-contextEntry, .dim-contextClose, .dim-contextFooter button, .dim-contextTextActions button, .dim-contextField, .dim-contextSwitchRow { min-height: 44px; }
|
||||
.dim-contextEntry, .dim-contextTab, .dim-contextClose, .dim-contextFooter button, .dim-contextTextActions button, .dim-contextField, .dim-contextSwitchRow { min-height: 44px; }
|
||||
.dim-contextClose, .dim-contextTextActions button { min-width: 44px; }
|
||||
.dim-contextGuidance textarea { font-size: 16px; }
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue