feat(ui): surface friendly agent and project failure messages
Add friendlyAgentErrors to map raw agent/image errors to user-facing copy, and use it for timeline error bubbles and project failure banners. Redirect home with a toast when a project returns 403/404 instead of showing a generic error, and render the final agent response inline under skill progress. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -4,6 +4,7 @@ import { useCallback, useEffect, useRef, type Dispatch, type RefObject, type Set
|
||||
import type { AgentChatPayload, AgentMessage, Project } from "@/domain/design";
|
||||
import { designGateway } from "@/infrastructure/designGateway";
|
||||
import { useUserMessage } from "@/ui/hooks/useUserMessage";
|
||||
import { friendlyProjectFailureMessage } from "@/ui/lib/friendlyAgentErrors";
|
||||
import { generatorTaskPollIntervalMs } from "@/ui/pages/CanvasWorkspace/canvas/generatorTaskPolling";
|
||||
|
||||
export type GenerationChannel = "agent" | "image-generator" | "canvas-action";
|
||||
@@ -404,6 +405,5 @@ function isActiveCanvasWorkNode(node: Project["nodes"][number]) {
|
||||
}
|
||||
|
||||
function projectFailureMessage(project: Project) {
|
||||
const failure = [...project.messages].reverse().find((message) => message.role === "error" || message.status === "failed");
|
||||
return failure?.content || failure?.title || "";
|
||||
return friendlyProjectFailureMessage(project.messages);
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover
|
||||
import type { AgentContent, AgentMessage, AgentThreadSummary, CanvasNode, CanvasViewport, ExtractedTextLayer, ExtractNodeTextResponse, MockupModel, MockupModelMap, MockupPlacement, MockupSurface, NodeActionRequest, Project } from "@/domain/design";
|
||||
import { useI18n, type Locale } from "@/i18n/i18n";
|
||||
import { designGateway } from "@/infrastructure/designGateway";
|
||||
import { httpStatusOf } from "@/infrastructure/userMessages";
|
||||
import { useAuth } from "@/ui/auth/AuthProvider";
|
||||
import { AccountManagementDialog } from "@/ui/components/AccountManagementDialog";
|
||||
import { ArtboardPreview } from "@/ui/components/ArtboardPreview";
|
||||
@@ -29,6 +30,7 @@ import { TextStyleToolbar, type TextStylePatch } from "@/ui/components/TextStyle
|
||||
import { loadMotevaFontCatalog } from "@/ui/components/TextStyleToolbar/fontCatalog";
|
||||
import { WorkspaceTitle } from "@/ui/components/WorkspaceTitle";
|
||||
import { autoImageAdjustments, defaultImageAdjustments, parseImageAdjustments, serializeImageAdjustments } from "@/ui/lib/imageAdjustments";
|
||||
import { friendlyProjectFailureMessage } from "@/ui/lib/friendlyAgentErrors";
|
||||
import { canvasImageUrl } from "@/ui/lib/imageDelivery";
|
||||
import { visiblePromptText } from "@/ui/lib/promptImageReferences";
|
||||
import { isImageGeneratorThreadMessage, visibleAgentMessages } from "./canvas/agentMessageFilters";
|
||||
@@ -257,6 +259,11 @@ function shouldBypassCanvasWheel(target: EventTarget | null) {
|
||||
return target instanceof Element && Boolean(target.closest(canvasWheelGuardSelector));
|
||||
}
|
||||
|
||||
function isProjectAccessDenied(err: unknown) {
|
||||
const status = httpStatusOf(err);
|
||||
return status === 403 || status === 404;
|
||||
}
|
||||
|
||||
function expandTargetGeometryForNode(node: CanvasNode, options: ExpandOptions) {
|
||||
const ratio = expandRatioValue(options.ratio, node.width / Math.max(node.height, 1));
|
||||
const sourceRatio = node.width / Math.max(node.height, 1);
|
||||
@@ -795,7 +802,14 @@ export function CanvasWorkspace({ projectId }: { projectId: string }) {
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch((err: unknown) => setError(toUserMessageText(err, "generateError")));
|
||||
.catch((err: unknown) => {
|
||||
if (isProjectAccessDenied(err)) {
|
||||
showToast("你没有访问该项目权限", "error");
|
||||
openHome();
|
||||
return;
|
||||
}
|
||||
setError(toUserMessageText(err, "generateError"));
|
||||
});
|
||||
}, [applyProject, projectId, startProjectEvents]);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -5330,8 +5344,7 @@ function projectHasWebSearchEnabled(project: Project) {
|
||||
}
|
||||
|
||||
function projectFailureMessage(project: Project) {
|
||||
const failure = [...project.messages].reverse().find((message) => message.role === "error" || message.status === "failed");
|
||||
return failure?.content || failure?.title || "";
|
||||
return friendlyProjectFailureMessage(project.messages);
|
||||
}
|
||||
|
||||
function runningCanvasActionThreadIds(project: Project) {
|
||||
|
||||
Reference in New Issue
Block a user