@@ -345,7 +358,7 @@ export function IframeTestPanel() {
size="sm"
className="flex-1 bg-slate-50 text-xs text-slate-700 hover:bg-slate-100"
variant="ghost"
- onClick={() => handleSendShowReuseWelcome(false)}
+ onClick={() => handleSendIsChatting(false)}
>
发送 false
diff --git a/frontend/src/components/workspace/input-box.tsx b/frontend/src/components/workspace/input-box.tsx
index 9f74c531..40bc98a0 100644
--- a/frontend/src/components/workspace/input-box.tsx
+++ b/frontend/src/components/workspace/input-box.tsx
@@ -213,8 +213,8 @@ export function InputBox({
setIsFocused(false);
if (showWelcomeStyle) {
sendToParent({
- type: POST_MESSAGE_TYPES.SHOW_REUSE_WELCOME,
- showReuseWelcome: false,
+ type: POST_MESSAGE_TYPES.IS_CHATTING,
+ isChatting: true,
});
}
onSubmit?.(message);
@@ -491,7 +491,7 @@ export function InputBox({
function SuggestionListContainer({
sendSelectSkill,
}: {
- sendSelectSkill: (skill_id: string) => void;
+ sendSelectSkill: (skill_id: string[]) => void;
}) {
return (
@@ -504,7 +504,7 @@ function SuggestionListContainer({
function SuggestionList({
sendSelectSkill,
}: {
- sendSelectSkill: (skill_id: string) => void;
+ sendSelectSkill: (skill_id: string[]) => void;
}) {
const { t } = useI18n();
const { textInput } = usePromptInputController();
@@ -517,9 +517,23 @@ function SuggestionList({
);
const handleSuggestionClick = useCallback(
- (suggestion: { prompt: string; skill_id?: string }) => {
- // 如果有 skill_id,发送给宿主页
- if (suggestion.skill_id) {
+ (
+ suggestion: {
+ prompt: string;
+ skill_id?: string[];
+ children?: { skill_id: string[] }[];
+ },
+ ) => {
+ // 优先从 children 中提取 skill_id 数组,发送给宿主页
+ const childSkillIds = (suggestion.children ?? [])
+ .flatMap((item) => item.skill_id)
+ .map((item) => item.trim())
+ .filter((id): id is string => Boolean(id));
+ if (childSkillIds.length > 0) {
+ sendSelectSkill(childSkillIds);
+ return;
+ }
+ if (suggestion.skill_id && suggestion.skill_id.length > 0) {
sendSelectSkill(suggestion.skill_id);
return;
}
diff --git a/frontend/src/core/iframe-messages.ts b/frontend/src/core/iframe-messages.ts
index a5cd892b..56eaf49b 100644
--- a/frontend/src/core/iframe-messages.ts
+++ b/frontend/src/core/iframe-messages.ts
@@ -9,8 +9,8 @@
export const POST_MESSAGE_TYPES = {
// 全屏切换
FULLSCREEN: "fullscreen",
- // 是否展示复用欢迎态
- SHOW_REUSE_WELCOME: "showReuseWelcome",
+ // 会话是否处于聊天态
+ IS_CHATTING: "isChatting",
// 选择预定义 skill
SELECT_SKILL: "selectSkill",
// 打开 skill 选择对话框
@@ -35,14 +35,14 @@ export interface FullscreenMessage {
fullscreen: boolean;
}
-export interface ShowReuseWelcomeMessage {
- type: typeof POST_MESSAGE_TYPES.SHOW_REUSE_WELCOME;
- showReuseWelcome: boolean;
+export interface IsChattingMessage {
+ type: typeof POST_MESSAGE_TYPES.IS_CHATTING;
+ isChatting: boolean;
}
export interface SelectSkillMessage {
type: typeof POST_MESSAGE_TYPES.SELECT_SKILL;
- skill_id: string;
+ skill_id: string[];
}
export interface OpenSkillDialogMessage {
@@ -79,7 +79,7 @@ export function isSelectedSkillMessage(value: unknown): value is SelectedSkillMe
export function sendToParent(
message:
| FullscreenMessage
- | ShowReuseWelcomeMessage
+ | IsChattingMessage
| SelectSkillMessage
| OpenSkillDialogMessage,
): void {
diff --git a/frontend/src/hooks/use-iframe-skill.ts b/frontend/src/hooks/use-iframe-skill.ts
index afb68636..2533eca6 100644
--- a/frontend/src/hooks/use-iframe-skill.ts
+++ b/frontend/src/hooks/use-iframe-skill.ts
@@ -17,7 +17,7 @@ interface SkillData {
// Hook 返回类型
interface UseIframeSkillReturn {
selectedSkill: SkillData | null;
- sendSelectSkill: (skill_id: string) => void;
+ sendSelectSkill: (skill_id: string[]) => void;
openSkillDialog: () => void;
clearSkill: () => void;
}
@@ -25,29 +25,30 @@ interface UseIframeSkillReturn {
export function useIframeSkill(): UseIframeSkillReturn {
const router = useRouter();
const searchParams = useSearchParams();
- const skillIdFromQuery = searchParams.get("skill_id");
- const titleFromQuery = searchParams.get("title");
const threadIdFromQuery = searchParams.get("thread_id");
- const showReuseWelcomeFromQuery = searchParams.get("show_reuse_welcome");
+ const isChattingFromQuery = searchParams.get("is_chatting");
const lastThreadIdRef = useRef(null);
const [selectedSkill, setSelectedSkill] = useState(null);
- // 1. 监听 query 参数变化
- useEffect(() => {
- if (skillIdFromQuery && titleFromQuery) {
- setSelectedSkill({ skill_id: skillIdFromQuery, title: titleFromQuery });
- }
- }, [skillIdFromQuery, titleFromQuery]);
+ // 1. 监听 query 参数变化(临时禁用)
+ // TODO: 当前 skill 仅通过 iframe postMessage 传递,暂不从 URL 读取 skill_id/title。
+ // useEffect(() => {
+ // const skillIdFromQuery = searchParams.get("skill_id");
+ // const titleFromQuery = searchParams.get("title");
+ // if (skillIdFromQuery && titleFromQuery) {
+ // setSelectedSkill({ skill_id: skillIdFromQuery, title: titleFromQuery });
+ // }
+ // }, [searchParams]);
- // 0. 监听 query 中 show_reuse_welcome=false 且带 thread_id 时跳转到 thread 页面
+ // 0. 监听 query 中 is_chatting=true 且带 thread_id 时跳转到 thread 页面
useEffect(() => {
if (!threadIdFromQuery) return;
- if (showReuseWelcomeFromQuery !== "false") return;
+ if (isChattingFromQuery !== "true") return;
if (lastThreadIdRef.current === threadIdFromQuery) return;
lastThreadIdRef.current = threadIdFromQuery;
router.replace(`/workspace/chats/${threadIdFromQuery}`);
- }, [router, showReuseWelcomeFromQuery, threadIdFromQuery]);
+ }, [isChattingFromQuery, router, threadIdFromQuery]);
// 2. 监听宿主页 postMessage
useEffect(() => {
@@ -67,7 +68,7 @@ export function useIframeSkill(): UseIframeSkillReturn {
}, []);
// 发送选择预定义 skill
- const sendSelectSkill = useCallback((skill_id: string) => {
+ const sendSelectSkill = useCallback((skill_id: string[]) => {
const message = { type: POST_MESSAGE_TYPES.SELECT_SKILL, skill_id };
console.log("[useIframeSkill] sendSelectSkill:", message);
sendToParent(message);
@@ -83,12 +84,12 @@ export function useIframeSkill(): UseIframeSkillReturn {
sendToParent(message);
}, []);
- // 清除选中并发送 skill_id=0 给主页
+ // 清除选中并发送空 skill_id 数组给主页
const clearSkill = useCallback(() => {
setSelectedSkill(null);
- // 发送 skill_id=0 给主页,通知取消选择
- const message = { type: POST_MESSAGE_TYPES.SELECT_SKILL, skill_id: "0" };
- console.log("[useIframeSkill] clearSkill, sending skill_id=0:", message);
+ // 发送空数组给主页,通知取消选择
+ const message = { type: POST_MESSAGE_TYPES.SELECT_SKILL, skill_id: [] };
+ console.log("[useIframeSkill] clearSkill, sending skill_id=[]:", message);
sendToParent(message);
}, []);
diff --git a/frontend/tests/e2e/support/chat-helpers.ts b/frontend/tests/e2e/support/chat-helpers.ts
index 446c8e22..a46586ad 100644
--- a/frontend/tests/e2e/support/chat-helpers.ts
+++ b/frontend/tests/e2e/support/chat-helpers.ts
@@ -29,11 +29,11 @@ export function skipIfMissingThread(
export function buildChatUrl({
pathThreadId,
- showReuseWelcome,
+ isChatting,
threadId,
}: {
pathThreadId?: string;
- showReuseWelcome: boolean;
+ isChatting: boolean;
threadId: string;
}) {
const resolvedThreadId = threadId ?? pathThreadId;
@@ -42,7 +42,7 @@ export function buildChatUrl({
}
const query = new URLSearchParams();
- query.set("show_reuse_welcome", String(showReuseWelcome));
+ query.set("is_chatting", String(isChatting));
if (resolvedThreadId) {
query.set("thread_id", resolvedThreadId);
}
@@ -55,20 +55,20 @@ export function buildChatUrl({
export function invalidNewChatUrl() {
const query = new URLSearchParams();
- query.set("show_reuse_welcome", "true");
+ query.set("is_chatting", "false");
return `/workspace/chats/new?${query.toString()}`;
}
export function newChatEntry(threadId: string) {
return buildChatUrl({
- showReuseWelcome: true,
+ isChatting: false,
threadId,
});
}
export function reuseThreadWelcomeEntry(threadId: string) {
return buildChatUrl({
- showReuseWelcome: true,
+ isChatting: false,
threadId,
});
}
@@ -76,7 +76,7 @@ export function reuseThreadWelcomeEntry(threadId: string) {
export function reuseThreadChatEntry(threadId: string) {
return buildChatUrl({
pathThreadId: threadId,
- showReuseWelcome: false,
+ isChatting: true,
threadId,
});
}