feat(frontend): switch skill messaging to selectedSkills payload

This commit is contained in:
肖应宇 2026-04-08 13:09:47 +08:00
parent a8cfe1c42e
commit 10cf4f0b00
7 changed files with 61 additions and 30 deletions

View File

@ -56,13 +56,17 @@ export function IframeTestPanel() {
} }
function handleSendSelectSkill() { function handleSendSelectSkill() {
iframeSkill.sendSelectSkill(["skill_001"]); iframeSkill.sendSelectSkill([{ id: "skill_001", name: "测试技能1" }]);
addLog("postMessage → selectSkill (skill_id=['skill_001'])"); addLog("postMessage → selectedSkills ([{id:'skill_001',name:'测试技能1'}])");
} }
function handleSendSelectSkillArray() { function handleSendSelectSkillArray() {
iframeSkill.sendSelectSkill(["1246", "1247", "1248"]); iframeSkill.sendSelectSkill([
addLog("postMessage → selectSkill (skill_id=['1246','1247','1248'])"); { id: "1246", name: "技能A" },
{ id: "1247", name: "技能B" },
{ id: "1248", name: "技能C" },
]);
addLog("postMessage → selectedSkills (3 skills)");
} }
function handleOpenSkillDialog() { function handleOpenSkillDialog() {
@ -72,7 +76,7 @@ export function IframeTestPanel() {
function handleClearSkill() { function handleClearSkill() {
iframeSkill.clearSkill(); iframeSkill.clearSkill();
addLog("clearSkill 已调用postMessage → skill_id=[]"); addLog("clearSkill 已调用postMessage → selectedSkills=[]");
} }
function handleTestClipboardCopy() { function handleTestClipboardCopy() {

View File

@ -57,6 +57,10 @@ import {
DropdownMenuSeparator, DropdownMenuSeparator,
} from "@/components/ui/dropdown-menu"; } from "@/components/ui/dropdown-menu";
import { useI18n } from "@/core/i18n/hooks"; import { useI18n } from "@/core/i18n/hooks";
import type {
SelectedSkillPayloadItem,
SuggestionSkillChildren,
} from "@/core/i18n/locales/types";
import { POST_MESSAGE_TYPES, sendToParent } from "@/core/iframe-messages"; import { POST_MESSAGE_TYPES, sendToParent } from "@/core/iframe-messages";
import { useModels } from "@/core/models/hooks"; import { useModels } from "@/core/models/hooks";
import type { AgentThreadContext } from "@/core/threads"; import type { AgentThreadContext } from "@/core/threads";
@ -491,7 +495,7 @@ export function InputBox({
function SuggestionListContainer({ function SuggestionListContainer({
sendSelectSkill, sendSelectSkill,
}: { }: {
sendSelectSkill: (skill_id: string[]) => void; sendSelectSkill: (selectedSkills: SelectedSkillPayloadItem[]) => void;
}) { }) {
return ( return (
<div className="absolute right-0 bottom-0 left-0 z-0 flex translate-y-full items-center justify-center pt-4"> <div className="absolute right-0 bottom-0 left-0 z-0 flex translate-y-full items-center justify-center pt-4">
@ -504,7 +508,7 @@ function SuggestionListContainer({
function SuggestionList({ function SuggestionList({
sendSelectSkill, sendSelectSkill,
}: { }: {
sendSelectSkill: (skill_id: string[]) => void; sendSelectSkill: (selectedSkills: SelectedSkillPayloadItem[]) => void;
}) { }) {
const { t } = useI18n(); const { t } = useI18n();
const { textInput } = usePromptInputController(); const { textInput } = usePromptInputController();
@ -521,20 +525,31 @@ function SuggestionList({
suggestion: { suggestion: {
prompt: string; prompt: string;
skill_id?: string[]; skill_id?: string[];
children?: { skill_id: string[] }[]; children?: SuggestionSkillChildren[];
suggestion: string;
}, },
) => { ) => {
// 优先从 children 中提取 skill_id 数组,发送给宿主页 // 优先从 children 中提取 skill_id 数组,转换为 selectedSkills 发送给宿主页
const childSkillIds = (suggestion.children ?? []) const childSkillIds = (suggestion.children ?? [])
.flatMap((item) => item.skill_id) .flatMap((item) => item.skill_id)
.map((item) => item.trim()) .map((item) => item.trim())
.filter((id): id is string => Boolean(id)); .filter((id): id is string => Boolean(id));
if (childSkillIds.length > 0) { if (childSkillIds.length > 0) {
sendSelectSkill(childSkillIds); sendSelectSkill(
childSkillIds.map((id) => ({
id,
name: suggestion.suggestion,
})),
);
return; return;
} }
if (suggestion.skill_id && suggestion.skill_id.length > 0) { if (suggestion.skill_id && suggestion.skill_id.length > 0) {
sendSelectSkill(suggestion.skill_id); sendSelectSkill(
suggestion.skill_id.map((id) => ({
id,
name: suggestion.suggestion,
})),
);
return; return;
} }
// 原有逻辑 // 原有逻辑

View File

@ -120,34 +120,34 @@ export const enUS: Translations = {
prompt: prompt:
"Write an academic paper about [topic], including abstract, introduction, body and references.", "Write an academic paper about [topic], including abstract, introduction, body and references.",
icon: PenLineIcon, icon: PenLineIcon,
skill_id: "1245", children: [{ id: "1245", name: "Paper Writing" }],
}, },
{ {
suggestion: "Report Generation", suggestion: "Report Generation",
prompt: prompt:
"Analyze [topic] in depth and generate a well-structured research report.", "Analyze [topic] in depth and generate a well-structured research report.",
icon: MicroscopeIcon, icon: MicroscopeIcon,
skill_id: "520", children: [{ id: "520", name: "Report Generation" }],
}, },
{ {
suggestion: "Copywriting", suggestion: "Copywriting",
prompt: prompt:
"Create a complete planning proposal and promotional copy for [project/event].", "Create a complete planning proposal and promotional copy for [project/event].",
icon: ShapesIcon, icon: ShapesIcon,
skill_id: "409", children: [{ id: "409", name: "Copywriting" }],
}, },
{ {
suggestion: "Document Processing", suggestion: "Document Processing",
prompt: prompt:
"Process [document] with reading, summarizing, translating or format conversion.", "Process [document] with reading, summarizing, translating or format conversion.",
icon: CompassIcon, icon: CompassIcon,
skill_id: "5", children: [{ id: "5", name: "Document Processing" }],
}, },
{ {
suggestion: "Market Research", suggestion: "Market Research",
prompt: "TestingTestingTestingTestingTesting", prompt: "TestingTestingTestingTestingTesting",
icon: ShapesIcon, icon: ShapesIcon,
skill_id: "1216", children: [{ id: "1216", name: "Market Research" }],
}, },
], ],
suggestionsCreate: [ suggestionsCreate: [

View File

@ -1,5 +1,11 @@
import type { LucideIcon } from "lucide-react"; import type { LucideIcon } from "lucide-react";
export interface SelectedSkillPayloadItem {
id: string | number;
name: string;
}
export interface Translations { export interface Translations {
// Locale meta // Locale meta
locale: { locale: {
@ -97,7 +103,7 @@ export interface Translations {
suggestion: string; suggestion: string;
prompt: string; prompt: string;
icon: LucideIcon; icon: LucideIcon;
skill_id?: string; children: SelectedSkillPayloadItem[];
}[]; }[];
suggestionsCreate: ( suggestionsCreate: (
| { | {

View File

@ -117,31 +117,31 @@ export const zhCN: Translations = {
prompt: prompt:
"为[主题/产品]撰写吸引人的自媒体文案,包括标题、正文和话题标签。", "为[主题/产品]撰写吸引人的自媒体文案,包括标题、正文和话题标签。",
icon: PenLineIcon, icon: PenLineIcon,
skill_id: "1245", children: [{ id: "1245", name: "自媒体文案" }],
}, },
{ {
suggestion: "需求文档", suggestion: "需求文档",
prompt: "编写[项目/功能]的需求文档,包含功能描述、用户故事和验收标准。", prompt: "编写[项目/功能]的需求文档,包含功能描述、用户故事和验收标准。",
icon: CompassIcon, icon: CompassIcon,
skill_id: "520", children: [{ id: "520", name: "需求文档" }],
}, },
{ {
suggestion: "使用指南", suggestion: "使用指南",
prompt: "编写[产品/功能]的使用指南,包含操作步骤、注意事项和常见问题。", prompt: "编写[产品/功能]的使用指南,包含操作步骤、注意事项和常见问题。",
icon: GraduationCapIcon, icon: GraduationCapIcon,
skill_id: "409", children: [{ id: "409", name: "使用指南" }],
}, },
{ {
suggestion: "Excel数据分析", suggestion: "Excel数据分析",
prompt: "对[Excel文件/数据]进行分析,生成数据洞察和可视化建议。", prompt: "对[Excel文件/数据]进行分析,生成数据洞察和可视化建议。",
icon: MicroscopeIcon, icon: MicroscopeIcon,
skill_id: "5", children: [{ id: "5", name: "Excel数据分析" }],
}, },
{ {
suggestion: "市场调研", suggestion: "市场调研",
prompt: "针对[行业/产品]进行市场调研,分析市场规模、竞品和趋势。", prompt: "针对[行业/产品]进行市场调研,分析市场规模、竞品和趋势。",
icon: ShapesIcon, icon: ShapesIcon,
skill_id: "1216", children: [{ id: "1216", name: "市场调研" }],
}, },
], ],
suggestionsCreate: [ suggestionsCreate: [

View File

@ -12,7 +12,7 @@ export const POST_MESSAGE_TYPES = {
// 会话是否处于聊天态 // 会话是否处于聊天态
IS_CHATTING: "isChatting", IS_CHATTING: "isChatting",
// 选择预定义 skill // 选择预定义 skill
SELECT_SKILL: "selectSkill", SELECT_SKILL: "selectedSkills",
// 打开 skill 选择对话框 // 打开 skill 选择对话框
OPEN_SKILL_DIALOG: "openSkillDialog", OPEN_SKILL_DIALOG: "openSkillDialog",
} as const; } as const;
@ -42,7 +42,7 @@ export interface IsChattingMessage {
export interface SelectSkillMessage { export interface SelectSkillMessage {
type: typeof POST_MESSAGE_TYPES.SELECT_SKILL; type: typeof POST_MESSAGE_TYPES.SELECT_SKILL;
skill_id: string[]; selectedSkills: SelectedSkillPayloadItem[];
} }
export interface OpenSkillDialogMessage { export interface OpenSkillDialogMessage {
@ -56,6 +56,11 @@ export interface SelectedSkillMessage {
title: string; title: string;
} }
export interface SelectedSkillPayloadItem {
id: string | number;
name: string;
}
type UnknownRecord = Record<string, unknown>; type UnknownRecord = Record<string, unknown>;
function asRecord(value: unknown): UnknownRecord | null { function asRecord(value: unknown): UnknownRecord | null {

View File

@ -5,6 +5,7 @@ import {
POST_MESSAGE_TYPES, POST_MESSAGE_TYPES,
RECEIVE_MESSAGE_TYPES, RECEIVE_MESSAGE_TYPES,
isSelectedSkillMessage, isSelectedSkillMessage,
type SelectedSkillPayloadItem,
sendToParent, sendToParent,
} from "@/core/iframe-messages"; } from "@/core/iframe-messages";
@ -17,7 +18,7 @@ interface SkillData {
// Hook 返回类型 // Hook 返回类型
interface UseIframeSkillReturn { interface UseIframeSkillReturn {
selectedSkill: SkillData | null; selectedSkill: SkillData | null;
sendSelectSkill: (skill_id: string[]) => void; sendSelectSkill: (selectedSkills: SelectedSkillPayloadItem[]) => void;
openSkillDialog: () => void; openSkillDialog: () => void;
clearSkill: () => void; clearSkill: () => void;
} }
@ -68,8 +69,8 @@ export function useIframeSkill(): UseIframeSkillReturn {
}, []); }, []);
// 发送选择预定义 skill // 发送选择预定义 skill
const sendSelectSkill = useCallback((skill_id: string[]) => { const sendSelectSkill = useCallback((selectedSkills: SelectedSkillPayloadItem[]) => {
const message = { type: POST_MESSAGE_TYPES.SELECT_SKILL, skill_id }; const message = { type: POST_MESSAGE_TYPES.SELECT_SKILL, selectedSkills };
console.log("[useIframeSkill] sendSelectSkill:", message); console.log("[useIframeSkill] sendSelectSkill:", message);
sendToParent(message); sendToParent(message);
}, []); }, []);
@ -84,12 +85,12 @@ export function useIframeSkill(): UseIframeSkillReturn {
sendToParent(message); sendToParent(message);
}, []); }, []);
// 清除选中并发送空 skill_id 数组给主页 // 清除选中并发送空 selectedSkills 数组给主页
const clearSkill = useCallback(() => { const clearSkill = useCallback(() => {
setSelectedSkill(null); setSelectedSkill(null);
// 发送空数组给主页,通知取消选择 // 发送空数组给主页,通知取消选择
const message = { type: POST_MESSAGE_TYPES.SELECT_SKILL, skill_id: [] }; const message = { type: POST_MESSAGE_TYPES.SELECT_SKILL, selectedSkills: [] };
console.log("[useIframeSkill] clearSkill, sending skill_id=[]:", message); console.log("[useIframeSkill] clearSkill, sending selectedSkills=[]:", message);
sendToParent(message); sendToParent(message);
}, []); }, []);