diff --git a/frontend/src/components/workspace/iframe-test-panel.tsx b/frontend/src/components/workspace/iframe-test-panel.tsx index e8d8c327..23623b78 100644 --- a/frontend/src/components/workspace/iframe-test-panel.tsx +++ b/frontend/src/components/workspace/iframe-test-panel.tsx @@ -313,6 +313,23 @@ export function IframeTestPanel() { > 📦 模拟 selectedSkills(数组 message) + + */} ))} diff --git a/frontend/src/hooks/use-iframe-skill.ts b/frontend/src/hooks/use-iframe-skill.ts index 33c5329b..10561850 100644 --- a/frontend/src/hooks/use-iframe-skill.ts +++ b/frontend/src/hooks/use-iframe-skill.ts @@ -60,7 +60,7 @@ interface UseIframeSkillReturn { title: string; }) => Promise; openSkillDialog: () => void; - clearSkill: () => void; + clearSkill: (skillId?: string) => void; } interface UseIframeSkillOptions { @@ -149,12 +149,18 @@ export function useIframeSkill( // 4. 选择变化时同步到 localStorage useEffect(() => { + const threadKey = getThreadStorageKey(threadId); if (selectedSkills.length === 0) { + // 空数组也要同步到存储,避免 UI 状态与缓存不一致 + window.localStorage.removeItem(STORAGE_KEYS.latest); + if (threadKey) { + window.localStorage.removeItem(threadKey); + } return; } + const payload = JSON.stringify(selectedSkills); window.localStorage.setItem(STORAGE_KEYS.latest, payload); - const threadKey = getThreadStorageKey(threadId); if (threadKey) { window.localStorage.setItem(threadKey, payload); } @@ -262,19 +268,58 @@ export function useIframeSkill( }, []); // 清除选中并发送空 selectedSkills 数组给主页 - const clearSkill = useCallback(() => { - setSelectedSkill(null); - setSelectedSkills([]); - window.localStorage.removeItem(STORAGE_KEYS.latest); - const threadKey = getThreadStorageKey(threadId); - if (threadKey) { - window.localStorage.removeItem(threadKey); - } - // 发送空数组给主页,通知取消选择 - const message = { type: POST_MESSAGE_TYPES.SELECT_SKILLS, selectedSkills: [] }; - console.log("[useIframeSkill] clearSkill, sending selectedSkills=[]:", message); - sendToParent(message); - }, [threadId]); + const clearSkill = useCallback( + (skillId?: string) => { + const removeAll = !skillId; + const nextSelectedSkills = removeAll + ? [] + : selectedSkills.filter((skill) => skill.skill_id !== String(skillId)); + + setSelectedSkills(nextSelectedSkills); + setSelectedSkill(nextSelectedSkills[0] ?? null); + + // 同步 latest 缓存:仅删除对应 skill(或全部清空) + const latestSkills = parseStoredSkills( + window.localStorage.getItem(STORAGE_KEYS.latest), + ); + const nextLatestSkills = removeAll + ? [] + : latestSkills.filter((skill) => skill.skill_id !== String(skillId)); + if (nextLatestSkills.length > 0) { + window.localStorage.setItem( + STORAGE_KEYS.latest, + JSON.stringify(nextLatestSkills), + ); + } else { + window.localStorage.removeItem(STORAGE_KEYS.latest); + } + + // 同步线程缓存:保存剩余数组,空则删除 key + const threadKey = getThreadStorageKey(threadId); + if (threadKey) { + if (nextSelectedSkills.length > 0) { + window.localStorage.setItem( + threadKey, + JSON.stringify(nextSelectedSkills), + ); + } else { + window.localStorage.removeItem(threadKey); + } + } + + // 通知宿主页当前剩余技能 + const message = { + type: POST_MESSAGE_TYPES.SELECT_SKILLS, + selectedSkills: nextSelectedSkills.map((skill) => ({ + id: skill.skill_id, + name: skill.title, + })), + } as const; + console.log("[useIframeSkill] clearSkill:", message); + sendToParent(message); + }, + [selectedSkills, threadId], + ); return { selectedSkill,