From b99ad798b926e79ac2152c15e658edb676c0face Mon Sep 17 00:00:00 2001 From: ddCat <2695092045@qq.com> Date: Fri, 18 Sep 2026 23:27:34 +0800 Subject: [PATCH] =?UTF-8?q?fix(canvas):=20=E7=94=BB=E5=B8=83=E4=B8=8E?= =?UTF-8?q?=E7=B4=A0=E6=9D=90=E5=90=8C=E6=AD=A5=20-=20=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E6=89=B9=E9=87=8F=E7=BC=A9=E7=95=A5=E5=9B=BE=E3=80=81=E8=BF=9C?= =?UTF-8?q?=E7=A8=8B=E8=B5=84=E6=BA=90=E5=9B=9E=E9=80=80=E4=B8=8E=E9=87=8D?= =?UTF-8?q?=E5=91=BD=E5=90=8D=E5=90=8C=E6=AD=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/internal/handler/user_data.go | 4 +- .../assets/asset-library-picker-modal.tsx | 6 +- .../canvas-agent-skill-library-modal.tsx | 226 +++--- .../canvas/canvas-batch-table-node.tsx | 12 +- .../canvas/canvas-cloud-agent-chat-ui.tsx | 4 +- .../components/canvas/canvas-cloud-agent.css | 684 +++++++++--------- .../canvas/canvas-node-prompt-panel.tsx | 22 +- .../canvas/canvas-project-asset-modal.tsx | 2 +- .../components/canvas/canvas-project-card.tsx | 18 +- .../canvas-resource-mention-textarea.tsx | 12 +- .../lib/canvas/canvas-image-batch-retry.ts | 15 +- web/src/lib/canvas/canvas-project-domain.ts | 4 +- .../pages/canvas/use-canvas-batch-table.ts | 18 +- .../canvas/use-canvas-project-lifecycle.ts | 6 +- .../detail/workflow-production-workbench.tsx | 20 +- web/src/pages/projects/index.tsx | 1 - web/src/services/api/resources.ts | 53 +- web/src/services/resource-blob-cache.ts | 20 +- web/test/canvas-image-batch-retry.test.ts | 21 + 19 files changed, 635 insertions(+), 513 deletions(-) diff --git a/backend/internal/handler/user_data.go b/backend/internal/handler/user_data.go index e130d7dc7..d5800435a 100644 --- a/backend/internal/handler/user_data.go +++ b/backend/internal/handler/user_data.go @@ -295,8 +295,8 @@ func RegisterUserDataRoutes(r *gin.RouterGroup, svc *service.Service) { return } if delivery.RedirectURL != "" { - // CDN 或对象存储直连地址允许安全短期缓存 - c.Header("Cache-Control", "private, max-age=86400, stale-while-revalidate=3600") + // 签名链接仅有效 5 分钟;重定向缓存必须短于签名 TTL,避免命中过期地址。 + c.Header("Cache-Control", "private, max-age=240") c.Header("Referrer-Policy", "no-referrer") c.Header("X-Content-Type-Options", "nosniff") c.Redirect(http.StatusTemporaryRedirect, delivery.RedirectURL) diff --git a/web/src/components/assets/asset-library-picker-modal.tsx b/web/src/components/assets/asset-library-picker-modal.tsx index 5c781d6f9..84c68951d 100644 --- a/web/src/components/assets/asset-library-picker-modal.tsx +++ b/web/src/components/assets/asset-library-picker-modal.tsx @@ -488,7 +488,7 @@ export function AssetLibraryPickerModal({
- {remoteEnabled && remoteQuery.isError ?
素材读取失败
: loading || (useRemoteItems && remoteQuery.isFetching) ? ( + {remoteEnabled && remoteQuery.isError && !localItems.length ?
素材读取失败服务端暂时不可用,重试不会影响本地已保存素材。
: loading || (useRemoteItems && remoteQuery.isFetching) ? (
正在读取素材 @@ -519,8 +519,8 @@ export function AssetLibraryPickerModal({ - ) : footerNote ? ( - {footerNote} + ) : footerNote || (remoteEnabled && remoteQuery.isError) ? ( + {remoteEnabled && remoteQuery.isError ? "云端素材暂时不可用,当前显示本地缓存" : footerNote} ) : ( )} diff --git a/web/src/components/canvas/canvas-agent-skill-library-modal.tsx b/web/src/components/canvas/canvas-agent-skill-library-modal.tsx index e04874931..a2430222a 100644 --- a/web/src/components/canvas/canvas-agent-skill-library-modal.tsx +++ b/web/src/components/canvas/canvas-agent-skill-library-modal.tsx @@ -1,6 +1,6 @@ import { useEffect, useMemo, useRef, useState } from "react"; import { Button, Input } from "antd"; -import { Check, LibraryBig, LoaderCircle, Plus, Search, Sparkles, Users } from "lucide-react"; +import { Check, LoaderCircle, Plus, Search, Sparkles, Users } from "lucide-react"; import { AppModal } from "@/components/ui/product/app-modal"; import type { CanvasTheme } from "@/lib/canvas-theme"; @@ -45,16 +45,20 @@ export function CanvasAgentSkillLibraryModal({ onInstall, onLoadMore, }: CanvasAgentSkillLibraryModalProps) { - const [tab, setTab] = useState("enabled"); + const [tab, setTab] = useState("market"); const listRef = useRef(null); const loadMoreRef = useRef(null); const wasOpenRef = useRef(false); const selectedCount = selectedSkillIds.length; useEffect(() => { - if (open && !wasOpenRef.current) setTab(selectedCount > 0 ? "enabled" : "installed"); + if (open && !wasOpenRef.current) setTab("market"); wasOpenRef.current = open; - }, [open, selectedCount]); + }, [open]); + + useEffect(() => { + listRef.current?.scrollTo({ top: 0 }); + }, [category, search, tab]); const visibleSkills = useMemo(() => { const keyword = search.trim().toLocaleLowerCase("zh-CN"); @@ -86,16 +90,21 @@ export function CanvasAgentSkillLibraryModal({ const categoryItems = useMemo(() => { const unique = new Map(); categories.forEach((item) => { - if (item.value && item.label) unique.set(item.value, item); + if (item.value && item.value !== "all" && item.label) unique.set(item.value, item); }); - return [{ value: "all", label: "全部" }, ...unique.values()]; + return [...unique.values()]; }, [categories]); const emptyText = tab === "enabled" - ? "本轮还没有启用 Skill,可在“已加入”中选择" + ? "本轮还没有启用技能,可在“我的技能”中选择" : tab === "installed" - ? "还没有匹配的已加入 Skill,可前往“发现 Skills”添加" - : "没有匹配的公开 Skill,换个关键词或分类试试"; + ? "还没有匹配的已加入技能,可前往“全部”添加" + : "没有匹配的公开技能,换个关键词或分类试试"; + + const changeTab = (value: SkillLibraryTab) => { + setTab(value); + onCategoryChange("all"); + }; return ( ( +
+ + {modal} +
+ )} onCancel={onClose} flush > @@ -115,85 +133,78 @@ export function CanvasAgentSkillLibraryModal({ data-canvas-wheel-scroll aria-label="Agent Skills 技能库" > -
-
-
-
-

按分类浏览和搜索技能,本轮最多启用 8 个,发送消息时固定版本。

-
-
- 已启用 = 8 ? "var(--destructive)" : theme.accent.primary }}>{selectedCount}/8 -
-
- -
-
- setTab("enabled")} /> - setTab("installed")} /> - setTab("market")} /> -
+
+ + - + -
- {visibleSkills.map((skill) => ( - onToggle(skill.skillId)} - onInstall={() => onInstall(skill)} - /> - ))} - {loading && visibleSkills.length === 0 ? ( -
-
- ) : null} - {!loading && visibleSkills.length === 0 ? ( -
-
- ) : null} - {tab === "market" && hasMore ? -
+
- {filled ? : fallback} + {filled ? : fallback} {label} @@ -399,14 +399,14 @@ function ResultThumbnail({ output, status, theme, onFocus }: { output?: CanvasNo type="button" aria-label={title} disabled={!output} - className="relative size-16 overflow-hidden rounded-lg border-2" + className="relative box-border grid size-16 shrink-0 place-items-center overflow-hidden rounded-lg border-2" style={{ borderColor: tone, cursor: output ? "pointer" : "default" }} onClick={(event) => { event.stopPropagation(); if (output) onFocus(); }} > - {filled && output ? } /> : } + {filled && output ? } /> : } {status.loading ? : null} @@ -422,7 +422,7 @@ function statusColor(tone: RowStatusTone, fallback: string) { } function EmptyThumbnail({ theme, compact = false }: { theme: CanvasTheme; compact?: boolean }): ReactNode { - const sizeClass = compact ? "size-12" : "size-16"; + const sizeClass = "size-full"; return (