From 93248f6c15c824ebd53f2721ec6e87e406fb34c4 Mon Sep 17 00:00:00 2001 From: kdletters <61648117+kdletters@users.noreply.github.com> Date: Mon, 5 Oct 2026 22:04:29 +0800 Subject: [PATCH] =?UTF-8?q?=E7=BB=A7=E7=BB=AD=E6=94=B6=E6=95=9B=E9=A6=96?= =?UTF-8?q?=E9=A1=B5=E5=89=AA=E8=B4=B4=E6=9D=BF=20IPC=20=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 区分空剪贴板与原生读取失败并显示脱敏正文 补充错误处理文档 --- .../home/components/RichInputArea/index.tsx | 60 ++++++++++++------- .../src/view/home/index.tsx | 12 +++- ...GC命令错误结构化与错误报告口径-2026-10-01.md | 1 + ...€�里程碑】AGC错误具体文本展示-2026-10-04.md | 1 + 4 files changed, 52 insertions(+), 22 deletions(-) diff --git a/apps/ai-game-creator-shell/src/view/home/components/RichInputArea/index.tsx b/apps/ai-game-creator-shell/src/view/home/components/RichInputArea/index.tsx index 76933a178..bc946481e 100644 --- a/apps/ai-game-creator-shell/src/view/home/components/RichInputArea/index.tsx +++ b/apps/ai-game-creator-shell/src/view/home/components/RichInputArea/index.tsx @@ -16,6 +16,7 @@ import { Upload } from 'lucide-react'; import React, { useEffect, useRef } from 'react'; import RichTextInput from '../../../../components/RichTextInput'; +import { visibleClientErrorMessage } from '../../../../services/errorReporting'; import type { Draft, HomeAttachmentDraft } from '../../useHomeDraftStore'; import { $createAttachmentNode, AttachmentNode } from './attachmentNode'; @@ -24,6 +25,7 @@ type RichInputAreaProps = { placeholder: string; onChange: (value: Draft) => void; onEnter: () => void; + onError?: (message: string) => void; children?: React.ReactNode; }; const INSERT_ATTACHMENTS_COMMAND: LexicalCommand = @@ -40,7 +42,10 @@ function createAttachmentDrafts(files: readonly File[]) { })); } -async function readNativeClipboardImageFile() { +async function readNativeClipboardImageFile(): Promise<{ + file: File | null; + error: unknown | null; +}> { // this used Tauri native api, unavailable in browser env. let image; try { @@ -54,7 +59,7 @@ async function readNativeClipboardImageFile() { canvas.height = height; const context = canvas.getContext('2d'); if (!context) { - return null; + return { file: null, error: null }; } context.putImageData( new ImageData(new Uint8ClampedArray(rgba), width, height), @@ -65,23 +70,29 @@ async function readNativeClipboardImageFile() { canvas.toBlob(resolve, 'image/png'), ); if (!blob) { - return null; + return { file: null, error: null }; } - return new File([blob], `pasted-image-${Date.now()}.png`, { - type: 'image/png', - }); - } catch { - return null; + return { + file: new File([blob], `pasted-image-${Date.now()}.png`, { + type: 'image/png', + }), + error: null, + }; + } catch (error) { + return { file: null, error }; } finally { await image?.close(); } } -async function readNativeClipboardText() { +async function readNativeClipboardText(): Promise<{ + text: string; + error: unknown | null; +}> { try { - return await readText(); - } catch { - return ''; + return { text: await readText(), error: null }; + } catch (error) { + return { text: '', error }; } } @@ -97,7 +108,7 @@ function selectEditableEndWhenNeeded() { root.selectEnd(); } -function EditorPlugins() { +function EditorPlugins({ onError }: { onError?: (message: string) => void }) { const [editor] = useLexicalComposerContext(); useEffect( @@ -145,23 +156,30 @@ function EditorPlugins() { PASTE_COMMAND, (event) => { event.preventDefault(); - void readNativeClipboardImageFile().then(async (file) => { - if (!file) { - const text = await readNativeClipboardText(); - if (text.length === 0) { + void readNativeClipboardImageFile().then(async (imageResult) => { + if (!imageResult.file) { + const textResult = await readNativeClipboardText(); + if (textResult.text.length === 0) { + const error = textResult.error ?? imageResult.error; + if (error) { + onError?.(visibleClientErrorMessage(error)); + } return; } - editor.dispatchCommand(INSERT_CLIPBOARD_TEXT_COMMAND, text); + editor.dispatchCommand( + INSERT_CLIPBOARD_TEXT_COMMAND, + textResult.text, + ); return; } - const nextAttachments = createAttachmentDrafts([file]); + const nextAttachments = createAttachmentDrafts([imageResult.file]); editor.dispatchCommand(INSERT_ATTACHMENTS_COMMAND, nextAttachments); }); return true; }, COMMAND_PRIORITY_HIGH, ), - [editor], + [editor, onError], ); return null; @@ -218,7 +236,7 @@ export default function RichInputArea(props: RichInputAreaProps) { } > - + {props.children} ); diff --git a/apps/ai-game-creator-shell/src/view/home/index.tsx b/apps/ai-game-creator-shell/src/view/home/index.tsx index 64307288d..ab11d81fc 100644 --- a/apps/ai-game-creator-shell/src/view/home/index.tsx +++ b/apps/ai-game-creator-shell/src/view/home/index.tsx @@ -153,6 +153,7 @@ export default function HomeView({ (state) => state.setRichText, ); const [homeCreationBusy, setHomeCreationBusy] = useState(false); + const [homeInputError, setHomeInputError] = useState(''); const [planningCompletionEnabled, setPlanningCompletionEnabled] = useState(false); const homeCreationBusyRef = useRef(false); @@ -271,7 +272,11 @@ export default function HomeView({ { + setHomeInputError(''); + setHomeRichText(value); + }} + onError={setHomeInputError} onEnter={() => { void createFromHome(); }} @@ -318,6 +323,11 @@ export default function HomeView({ + {homeInputError ? ( +

+ {homeInputError} +

+ ) : null}
{recoverableCreatedProjectPath && !creationBusy ? ( diff --git a/docs/adr/【ADR】AGC命令错误结构化与错误报告口径-2026-10-01.md b/docs/adr/【ADR】AGC命令错误结构化与错误报告口径-2026-10-01.md index 4a6b38ff3..8fbb9cb76 100644 --- a/docs/adr/【ADR】AGC命令错误结构化与错误报告口径-2026-10-01.md +++ b/docs/adr/【ADR】AGC命令错误结构化与错误报告口径-2026-10-01.md @@ -142,6 +142,7 @@ DirectProject 已经解决过同一类问题([`【ADR】DirectProject命令接 - 资源画布历史任务账本读取属于用户可见的恢复路径:异常正文必须进入提示条;只有响应形状错误这类没有底层异常的情况才使用明确的契约提示。 - 异步业务 facade 不得把底层异常折成“空结果”来逃避错误处理;只有真实的空回包、未配置或浏览器桥不可用等无正文事实允许返回空值,Tauri/网络/IPC 异常必须上抛到统一脱敏出口。 - Direct 活动回合事件桥注册失败即使仍有一次性快照,也必须标明事件更新通道已失联并展示安全正文,不能只显示旧快照而让用户误以为活动状态持续可靠。 +- 首页富文本剪贴板读取的“无内容”与“原生 IPC 失败”必须区分;后者展示安全正文,不能静默当作空剪贴板。 ## 后果与边界 diff --git a/docs/project-memory/plans/【里程碑】AGC错误具体文本展示-2026-10-04.md b/docs/project-memory/plans/【里程碑】AGC错误具体文本展示-2026-10-04.md index b15f8c216..3bf31f5b4 100644 --- a/docs/project-memory/plans/【里程碑】AGC错误具体文本展示-2026-10-04.md +++ b/docs/project-memory/plans/【里程碑】AGC错误具体文本展示-2026-10-04.md @@ -63,3 +63,4 @@ DirectProject 回合失败在确认不是客户端内部不可归类故障时, - 资源画布历史生成任务账本读取失败原来统一显示“暂时无法恢复历史任务”;现在保留 Tauri/文件读取正文,非数组响应仍单独保留契约错误提示。 - 发现默认 `requestChatPromptPolish` 还会把 Tauri 润色命令异常吞成 `null`,导致共享 hook 无法看到平台正文;现仅对空回包/桥不可用保留 `null`,原生异常继续上抛并统一脱敏。 - Direct 活动回合事件桥注册失败原来只退回启动快照,面板没有说明后续更新已失联;现保留事件桥 IPC 正文并在标题栏/活动项目面板显示。 +- 首页富文本输入的原生剪贴板图片/文本读取失败原来直接按空剪贴板处理;现通过输入区错误提示保留脱敏后的剪贴板 IPC 正文。