From 214136707651ad71f1bca45b295ba700beb83001 Mon Sep 17 00:00:00 2001
From: ddCat <2695092045@qq.com>
Date: Tue, 1 Sep 2026 20:01:52 +0800
Subject: [PATCH] =?UTF-8?q?fix(canvas):=20=E5=88=9B=E4=BD=9C=E8=BE=93?=
=?UTF-8?q?=E5=85=A5=E6=A0=8F=20-=20=E5=88=86=E7=A6=BB=E6=A8=A1=E5=9E=8B?=
=?UTF-8?q?=E4=BB=B7=E6=A0=BC=E5=B1=95=E7=A4=BA=E5=B9=B6=E6=94=B6=E6=95=9B?=
=?UTF-8?q?=E5=BD=95=E9=9F=B3=E4=B8=8E=E9=99=84=E4=BB=B6=E5=85=A5=E5=8F=A3?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../conversation/voice-recording-inline.tsx | 2 +-
web/src/components/model-picker.tsx | 4 +-
web/src/pages/create/index.tsx | 39 ++-----------------
web/src/styles/globals.css | 17 ++++++++
web/test/create-library-button.test.ts | 11 +++---
5 files changed, 29 insertions(+), 44 deletions(-)
diff --git a/web/src/components/conversation/voice-recording-inline.tsx b/web/src/components/conversation/voice-recording-inline.tsx
index 9606d997a..9923744f6 100644
--- a/web/src/components/conversation/voice-recording-inline.tsx
+++ b/web/src/components/conversation/voice-recording-inline.tsx
@@ -108,7 +108,7 @@ export function VoiceRecordingInline({ onTranscribed, onCancel }: VoiceRecording
return (
{displayError ? (
diff --git a/web/src/components/model-picker.tsx b/web/src/components/model-picker.tsx
index dbcfcd40a..e69efce9f 100644
--- a/web/src/components/model-picker.tsx
+++ b/web/src/components/model-picker.tsx
@@ -24,6 +24,7 @@ type ModelPickerProps = {
placeholder?: string;
onMissingConfig?: () => void;
showSelectedPrice?: boolean;
+ showOptionPrices?: boolean;
variant?: "default" | "creation";
requirements?: ModelRequirements;
showConfiguredModelName?: boolean;
@@ -40,6 +41,7 @@ export function ModelPicker({
placeholder = "选择模型",
onMissingConfig,
showSelectedPrice = true,
+ showOptionPrices = showSelectedPrice,
variant = "default",
requirements,
showConfiguredModelName = false,
@@ -226,7 +228,7 @@ export function ModelPicker({
theme={theme}
creationVariant={creationVariant}
showConfiguredModelName={showConfiguredModelName}
- showPrice={showSelectedPrice && creditsEnabled}
+ showPrice={showOptionPrices && creditsEnabled}
disabledReason={disabledReason}
/>
{selected ?
: null}
diff --git a/web/src/pages/create/index.tsx b/web/src/pages/create/index.tsx
index 7041868cb..dcc5d3d09 100644
--- a/web/src/pages/create/index.tsx
+++ b/web/src/pages/create/index.tsx
@@ -1,7 +1,7 @@
-import { useCallback, useEffect, useMemo, useRef, useState, type ChangeEvent, type CSSProperties, type PointerEvent, type ReactNode, type RefObject } from "react";
+import { useCallback, useEffect, useMemo, useRef, useState, type CSSProperties, type PointerEvent, type ReactNode, type RefObject } from "react";
import { App, Button, Drawer, Modal, Popover, Spin, Tooltip } from "antd";
import { Reorder } from "motion/react";
-import { ArrowDown, ArrowUp, Check, ChevronDown, ChevronLeft, ChevronRight, Clapperboard, Clock3, Copy, Download, FileText, Film, History, Image as ImageIcon, LoaderCircle, Maximize2, MessageSquareText, Minimize2, Music2, Paperclip, Plus, RefreshCw, Search, SlidersHorizontal, Sparkles, Trash2, WandSparkles, X } from "lucide-react";
+import { ArrowDown, ArrowUp, Check, ChevronDown, ChevronLeft, ChevronRight, Clapperboard, Clock3, Copy, Download, FileText, Film, History, Image as ImageIcon, LoaderCircle, Maximize2, MessageSquareText, Minimize2, Music2, Plus, RefreshCw, Search, SlidersHorizontal, Sparkles, Trash2, WandSparkles, X } from "lucide-react";
import { Link } from "react-router";
import { AIMessageMarkdown } from "@/components/ai/ai-message-markdown";
@@ -171,7 +171,6 @@ export default function CreatePage() {
const [libraryOpen, setLibraryOpen] = useState(false);
const externalAssetSources = useExternalAssetSources(libraryOpen);
const abortRef = useRef
(null);
- const fileInputRef = useRef(null);
const composerFocusRef = useRef(null);
const threadScrollRef = useRef(null);
const followLatestMessageRef = useRef(true);
@@ -418,27 +417,6 @@ export default function CreatePage() {
attachment: creationAttachmentFromImage(file, uploaded),
};
};
- const addAttachments = (files: FileList | File[]) => {
- if ((mode === "image" || mode === "video") && maxReferences === 0) {
- toast.warning(mode === "image" ? "当前图片模型不支持参考图" : "当前模型不支持图生视频");
- return;
- }
- const next = Array.from(files)
- .filter((file) => creationFileAccepted(mode, file))
- .slice(0, Math.max(0, maxReferences - attachments.length));
- if (!next.length) return;
- void Promise.allSettled(next.map(async (file) => {
- const { asset, attachment } = await uploadCreationAsset(file);
- if (asset) addAsset(asset);
- return attachment;
- })).then((settled) => {
- const items = settled.flatMap((entry) => entry.status === "fulfilled" ? [entry.value] : []);
- const failed = settled.filter((entry) => entry.status === "rejected");
- if (items.length) setAttachments((current) => [...current, ...items].slice(0, maxReferences));
- if (failed.length) toast.error(`${failed.length} 个参考素材上传失败,请重试`);
- });
- };
-
const uploadLibraryAssets = async (files: FileList | File[]) => {
const next = Array.from(files).filter((file) => creationFileAccepted(mode, file));
if (!next.length) return [];
@@ -453,11 +431,6 @@ export default function CreatePage() {
return assetIds;
};
- const handleFileChange = (event: ChangeEvent) => {
- if (event.target.files) addAttachments(event.target.files);
- event.target.value = "";
- };
-
const handleLibrarySelect = (selectedIds: string[]) => {
const next = selectedIds.flatMap((id): CreationAttachment[] => {
const asset = assets.find((item) => item.id === id);
@@ -893,8 +866,6 @@ export default function CreatePage() {
onReplaceAttachment: replaceReferenceFromTrack,
onReplaceReferenceFiles: replaceReferenceFromFiles,
onOpenLibrary: () => setLibraryOpen(true),
- fileInputRef,
- onFileChange: handleFileChange,
onModeChange: selectMode,
model: selectedModel,
modelRequirements,
@@ -1164,8 +1135,6 @@ type ComposerProps = {
onReplaceAttachment: (targetAttachmentId: string, replacement: CreationAttachment) => void;
onReplaceReferenceFiles: (targetAttachmentId: string, files: File[]) => void;
onOpenLibrary: () => void;
- fileInputRef: RefObject;
- onFileChange: (event: ChangeEvent) => void;
onModeChange: (mode: CreationMode) => void;
model: string;
modelRequirements: ModelRequirements;
@@ -1327,7 +1296,6 @@ function CreationComposer(props: ComposerProps) {
};
const composer =
-
{ const target = props.references.find((item) => item.id === reference.id); if (target?.attachmentId) props.onReplaceReferenceFiles(target.attachmentId, files); }} />
{props.attachments.length || referencesSupported ?
@@ -1416,8 +1384,7 @@ function CreationComposer(props: ComposerProps) {
优化
: null}
-
-
+
{props.mode === "video" || (props.mode === "image" && imageSettingsSupported) ?
: null}
{props.mode === "video" ?
: null}
diff --git a/web/src/styles/globals.css b/web/src/styles/globals.css
index 31e8dcc6f..15bbdce60 100644
--- a/web/src/styles/globals.css
+++ b/web/src/styles/globals.css
@@ -10816,6 +10816,23 @@ html:not(.dark) .creation-home .creation-chat-control:hover,
html:not(.dark) .creation-home .creation-chat-control[aria-expanded="true"],
html:not(.dark) .creation-home .creation-chat-composer .creation-model-picker.canvas-composer-model-picker:hover { background: rgba(28, 32, 37, .06) !important; }
+.creation-home .creation-chat-controls > .voice-recording-inline {
+ width: min(236px, calc(100vw - 120px));
+ max-width: 236px;
+ flex: 0 1 236px;
+ min-height: 30px;
+ height: 30px;
+ overflow: hidden;
+ border: 0;
+ border-radius: 7px;
+ background: transparent !important;
+ padding: 0 2px;
+}
+.creation-home .creation-chat-controls > .voice-recording-inline > svg {
+ flex: 0 1 160px;
+ max-width: 160px;
+}
+
.creation-chat-intro h1 > .creation-intro-emphasis {
display: inline-block;
margin: 0 4px;
diff --git a/web/test/create-library-button.test.ts b/web/test/create-library-button.test.ts
index 4655efd0e..84cfa26db 100644
--- a/web/test/create-library-button.test.ts
+++ b/web/test/create-library-button.test.ts
@@ -18,14 +18,13 @@ describe("creation library button", () => {
expect(dockEnd).toBeGreaterThan(dockStart);
const dockSource = compactSource(source.slice(dockStart, dockEnd));
const modePickerIndex = dockSource.indexOf(" {
const source = readFileSync(resolve(import.meta.dir, "../src/pages/create/index.tsx"), "utf8");
const pickerSource = readFileSync(resolve(import.meta.dir, "../src/components/assets/asset-library-picker-modal.tsx"), "utf8");
const uploadStart = source.indexOf("const uploadLibraryAssets = async");
- const uploadEnd = source.indexOf("const handleFileChange", uploadStart);
+ const uploadEnd = source.indexOf("const handleLibrarySelect", uploadStart);
expect(uploadStart).toBeGreaterThanOrEqual(0);
expect(uploadEnd).toBeGreaterThan(uploadStart);
@@ -128,12 +127,12 @@ describe("creation library button", () => {
expect(removeCreationAttachment(attachments, "last").map((item) => item.id)).toEqual(["first", "middle"]);
});
- test("删除按钮在指针按下阶段隔离拖拽,附件与素材库入口职责独立", () => {
+ test("删除按钮在指针按下阶段隔离拖拽,素材库入口职责独立", () => {
const source = compactSource(readFileSync(resolve(import.meta.dir, "../src/pages/create/index.tsx"), "utf8"));
expect(source).toContain("onPointerDownCapture={(event) => event.stopPropagation()}");
expect(source).toContain("onRemove(item.id)");
- expect(source).toContain("onClick={() => props.fileInputRef.current?.click()}");
expect(source).toContain("onClick={props.onOpenLibrary}");
+ expect(source).not.toContain("onClick={() => props.fileInputRef.current?.click()}");
});
});