7e009a4c94
- 新增 features/project-workspace/reference-source(types + 资源/Skill/附件/运行画面区域四个 provider 工厂),输入区按 providers 顺序取第一个非空回答,不再判断引用种类 - ResourceReferenceInput 删除 assets / versions / activeVersionId / skills / showTriggerButton / openPicker,改为 providers / inputActions / submitSuppressed / containerRef 四个通用接缝 - 新增 ResourceReferencePicker 与 ResourceReferencePickerAction:面板自己拿数据(素材、版本、缩略图预览 invoke)并自管筛选态,由宿主渲染,确认后只经 insertReferences 交回输入区 - 四个宿主改为按需注入 provider 并各自渲染选择器:DirectProject 注入资源+Skill+附件+运行区域,策划输入盒 / 画布生成面板 / 资源卡快速编辑只注入资源+两个静默 provider,非 DirectProject 宿主不再出现 $ Skill 候选 - 附件并入 ChatReference,编辑器收敛为单一 ResourceReferenceNode(删除 AttachmentReferenceNode),附件 chip 的 DOM 契约(data-attachment-reference / data-attachment-status / title / 移除按钮 aria-label)逐字保留 - 附件改为导入成功即以芯片进入正文、失败与异常一律不插入;控制器删除待发附件状态与 removeAttachment,ComposerPendingAttachments 与其提交时拼接一并删除,单次上限按草稿中的附件芯片数计算,导入进行中禁止发送 - directCodexContentToPromptText 的引用 token 前后各留一个空白(不重复),显示名反查改由调用方注入 resourceLabelResolver(assets),并补 TODO:后续改为按组件渲染引用 - 队列 chip 文案、快速编辑出站提示词、润色回填的引用恢复统一走 joinMentionText 的同一份 token 口径 - 新增 tests/referenceSourceProviders.test.ts 规则矩阵单测(触发符、候选过滤与截断、toReference、refresh、mentionToken、Skill 目录懒加载与失败重试),既有输入区/聊天用例迁移为注入 provider - styles.css 删除随面板外移与待发附件列表一起失效的规则
71 lines
2.1 KiB
TypeScript
71 lines
2.1 KiB
TypeScript
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
|
|
import { $getNodeByKey, type NodeKey } from 'lexical';
|
|
import { X } from 'lucide-react';
|
|
|
|
import type { ChatReference } from './resourceReferences';
|
|
|
|
function chipTitle(reference: ChatReference) {
|
|
if (reference.type === 'resource') {
|
|
return `${reference.label} · ${reference.kind}`;
|
|
}
|
|
if (reference.type === 'skill') {
|
|
return `${reference.name} · Skill`;
|
|
}
|
|
if (reference.type === 'attachment') {
|
|
return `${reference.name} · 附件`;
|
|
}
|
|
return `${reference.label} · 运行区域`;
|
|
}
|
|
|
|
/** chip 显示的引用名:附件有自己的芯片(`AttachmentReferenceChip`),这里只是把联合收窄。 */
|
|
function chipLabel(reference: ChatReference) {
|
|
if (reference.type === 'skill' || reference.type === 'attachment') {
|
|
return reference.name;
|
|
}
|
|
return reference.label;
|
|
}
|
|
|
|
export function ResourceReferenceChip({
|
|
reference,
|
|
nodeKey,
|
|
}: {
|
|
reference: ChatReference;
|
|
nodeKey: NodeKey;
|
|
}) {
|
|
const [editor] = useLexicalComposerContext();
|
|
return (
|
|
<span
|
|
className="resource-reference-chip"
|
|
data-resource-reference-id={
|
|
reference.type === 'resource' ? reference.resourceId : undefined
|
|
}
|
|
data-runtime-region-reference={
|
|
reference.type === 'runtime-region' ? 'true' : undefined
|
|
}
|
|
data-skill-reference-name={
|
|
reference.type === 'skill' ? reference.name : undefined
|
|
}
|
|
contentEditable={false}
|
|
title={chipTitle(reference)}
|
|
>
|
|
<span aria-hidden="true">{reference.type === 'skill' ? '$' : '@'}</span>
|
|
<span className="resource-reference-chip-label">
|
|
{chipLabel(reference)}
|
|
</span>
|
|
<button
|
|
type="button"
|
|
aria-label={`移除引用 ${chipLabel(reference)}`}
|
|
onMouseDown={(event) => event.preventDefault()}
|
|
onClick={() => {
|
|
editor.update(() => {
|
|
const node = $getNodeByKey(nodeKey);
|
|
node?.remove();
|
|
});
|
|
}}
|
|
>
|
|
<X size={11} aria-hidden="true" />
|
|
</button>
|
|
</span>
|
|
);
|
|
}
|