Fix image proxy public access and model avatar fallback
This commit is contained in:
parent
c27a707776
commit
15c3b519a7
@ -1437,6 +1437,34 @@ function buildPreviewImageUrl(url: string, width: number): string {
|
||||
});
|
||||
}
|
||||
|
||||
function ModelAvatarImage({
|
||||
src,
|
||||
alt,
|
||||
className,
|
||||
fallback,
|
||||
}: {
|
||||
src: string;
|
||||
alt: string;
|
||||
className: string;
|
||||
fallback: JSX.Element;
|
||||
}) {
|
||||
const [loadFailed, setLoadFailed] = useState(false);
|
||||
if (!src || loadFailed) {
|
||||
return fallback;
|
||||
}
|
||||
|
||||
return (
|
||||
<img
|
||||
src={src}
|
||||
alt={alt}
|
||||
className={className}
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
onError={() => setLoadFailed(true)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const CANVAS_IMAGE_ANALYSIS_TITLE = "图片分析";
|
||||
const CANVAS_IMAGE_DONE_SUMMARY = "已完成本次生成,如需微调可继续告诉我。";
|
||||
const CANVAS_AGENT_SECTION_TITLES = new Set([
|
||||
@ -15115,17 +15143,22 @@ export function CanvasChatPanel({
|
||||
)}
|
||||
>
|
||||
{model.avatarUrl ? (
|
||||
<img
|
||||
<ModelAvatarImage
|
||||
src={buildPreviewImageUrl(model.avatarUrl, 40)}
|
||||
alt={model.displayName}
|
||||
className="block h-full w-full object-contain"
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
onError={(event) => {
|
||||
if (event.currentTarget.src !== model.avatarUrl) {
|
||||
event.currentTarget.src = model.avatarUrl || "";
|
||||
}
|
||||
}}
|
||||
fallback={
|
||||
<span
|
||||
className={cn(
|
||||
"flex h-full w-full items-center justify-center text-[10px] font-medium leading-none",
|
||||
isVideoWorkspace
|
||||
? "text-muted-foreground"
|
||||
: "text-[#6b7280]"
|
||||
)}
|
||||
>
|
||||
{model.id === "auto" ? "A" : model.displayName.slice(0, 1)}
|
||||
</span>
|
||||
}
|
||||
/>
|
||||
) : (
|
||||
<span
|
||||
@ -18648,17 +18681,20 @@ export function CanvasChatPanel({
|
||||
)}
|
||||
>
|
||||
{selectDisplayModel?.avatarUrl ? (
|
||||
<img
|
||||
<ModelAvatarImage
|
||||
src={buildPreviewImageUrl(selectDisplayModel.avatarUrl, 64)}
|
||||
alt={selectDisplayModel.displayName || "模型头像"}
|
||||
className="block h-full w-full object-contain"
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
onError={(event) => {
|
||||
if (event.currentTarget.src !== selectDisplayModel.avatarUrl) {
|
||||
event.currentTarget.src = selectDisplayModel.avatarUrl || "";
|
||||
}
|
||||
}}
|
||||
fallback={
|
||||
<span
|
||||
className={cn(
|
||||
"flex h-full w-full items-center justify-center text-[10px] font-medium leading-none",
|
||||
isVideoToolbarDark ? "text-zinc-100" : "text-[#4b5563] dark:text-zinc-900"
|
||||
)}
|
||||
>
|
||||
{(selectDisplayModel?.displayName || "A").slice(0, 1)}
|
||||
</span>
|
||||
}
|
||||
/>
|
||||
) : getToolbarItem("chatModelSelector").iconUrl ? (
|
||||
<CanvasToolbarIcon
|
||||
|
||||
@ -151,6 +151,36 @@ function isNanoBananaProModel(model: { displayName?: string | null; modelId?: st
|
||||
modelId.includes("nano-banana")
|
||||
);
|
||||
}
|
||||
|
||||
function ModelAvatarImage({
|
||||
src,
|
||||
alt,
|
||||
className,
|
||||
fallback,
|
||||
}: {
|
||||
src: string;
|
||||
alt: string;
|
||||
className: string;
|
||||
fallback: JSX.Element;
|
||||
}) {
|
||||
const [loadFailed, setLoadFailed] = useState<boolean>(false);
|
||||
|
||||
if (!src || loadFailed) {
|
||||
return fallback;
|
||||
}
|
||||
|
||||
return (
|
||||
<img
|
||||
src={src}
|
||||
alt={alt}
|
||||
className={className}
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
onError={() => setLoadFailed(true)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
import { Trigger } from "@radix-ui/react-select";
|
||||
import type { UIMessage } from "ai";
|
||||
import equal from "fast-deep-equal";
|
||||
@ -2132,10 +2162,11 @@ function PureModelSelectorCompact({
|
||||
title={readOnlyName}
|
||||
>
|
||||
{readOnlyAvatarUrl ? (
|
||||
<img
|
||||
<ModelAvatarImage
|
||||
src={readOnlyAvatarUrl}
|
||||
alt={readOnlyName}
|
||||
className="h-7 w-7 rounded-full object-cover"
|
||||
fallback={<Bot className="h-4 w-4 text-[#8a919f]" />}
|
||||
/>
|
||||
) : (
|
||||
<Bot className="h-4 w-4" />
|
||||
@ -2189,10 +2220,11 @@ function PureModelSelectorCompact({
|
||||
variant="ghost"
|
||||
>
|
||||
{selectedModel?.avatarUrl ? (
|
||||
<img
|
||||
<ModelAvatarImage
|
||||
src={selectedModel.avatarUrl}
|
||||
alt={selectedModel.name}
|
||||
className="h-7 w-7 rounded-full object-cover"
|
||||
fallback={<Bot className="h-4 w-4 text-[#8a919f]" />}
|
||||
/>
|
||||
) : (
|
||||
<Bot className="h-4 w-4" />
|
||||
@ -2227,10 +2259,26 @@ function PureModelSelectorCompact({
|
||||
<span className="flex min-w-0 flex-1 items-center gap-3 overflow-visible">
|
||||
<span className="flex h-6 w-6 shrink-0 items-center justify-center text-[#2f3640]">
|
||||
{model.avatarUrl ? (
|
||||
<img
|
||||
<ModelAvatarImage
|
||||
src={model.avatarUrl}
|
||||
alt={model.displayName}
|
||||
className="h-6 w-6 rounded-full object-cover"
|
||||
fallback={
|
||||
<span className="flex h-6 w-6 items-center justify-center text-[#2f3640]">
|
||||
{(() => {
|
||||
if (isVideoProviderType(model)) {
|
||||
return <Video className="h-[19px] w-[19px]" strokeWidth={2.2} />;
|
||||
}
|
||||
if (isImageGenerationModel(model)) {
|
||||
return <ImageIcon className="h-[19px] w-[19px]" strokeWidth={2.2} />;
|
||||
}
|
||||
if ((model.providerType || "") === "system") {
|
||||
return <Sparkles className="h-[19px] w-[19px]" strokeWidth={2.2} />;
|
||||
}
|
||||
return <Bot className="h-[19px] w-[19px]" strokeWidth={2.2} />;
|
||||
})()}
|
||||
</span>
|
||||
}
|
||||
/>
|
||||
) : (
|
||||
<span className="flex h-6 w-6 items-center justify-center">
|
||||
|
||||
5
proxy.ts
5
proxy.ts
@ -778,6 +778,11 @@ export async function proxy(request: NextRequest, event: NextFetchEvent) {
|
||||
return NextResponse.next();
|
||||
}
|
||||
|
||||
// 图片代理用于站内图片优化/转码,需支持未登录用户下的公开图片链接渲染。
|
||||
if (pathname === "/api/image-proxy" && request.method === "GET") {
|
||||
return NextResponse.next();
|
||||
}
|
||||
|
||||
// 允许匿名访问新版首页灵感数据
|
||||
if (pathname === "/api/featured" || pathname.startsWith("/api/featured/")) {
|
||||
return NextResponse.next();
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user