Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c1c7cb3cc7 | |||
| b67f2e7601 | |||
| f056547160 | |||
| de3eb1d06a | |||
| f929be30ed | |||
| 66b761314b | |||
| 9a9c7eb86d | |||
| 48262d6233 | |||
| 062c8b3445 |
@@ -0,0 +1,43 @@
|
|||||||
|
# 自动检测文本文件并统一换行符
|
||||||
|
* text=auto eol=lf
|
||||||
|
|
||||||
|
# 源码强制使用 LF(跨平台一致)
|
||||||
|
*.ts text eol=lf
|
||||||
|
*.tsx text eol=lf
|
||||||
|
*.js text eol=lf
|
||||||
|
*.jsx text eol=lf
|
||||||
|
*.mjs text eol=lf
|
||||||
|
*.cjs text eol=lf
|
||||||
|
*.json text eol=lf
|
||||||
|
*.css text eol=lf
|
||||||
|
*.html text eol=lf
|
||||||
|
*.md text eol=lf
|
||||||
|
*.svg text eol=lf
|
||||||
|
|
||||||
|
# 配置类(统一 LF)
|
||||||
|
*.yml text eol=lf
|
||||||
|
*.yaml text eol=lf
|
||||||
|
*.toml text eol=lf
|
||||||
|
*.conf text eol=lf
|
||||||
|
|
||||||
|
# Windows 专用脚本保持 CRLF
|
||||||
|
*.bat text eol=crlf
|
||||||
|
*.cmd text eol=crlf
|
||||||
|
|
||||||
|
# 二进制文件,不做换行符转换
|
||||||
|
*.png binary
|
||||||
|
*.jpg binary
|
||||||
|
*.jpeg binary
|
||||||
|
*.gif binary
|
||||||
|
*.webp binary
|
||||||
|
*.ico binary
|
||||||
|
*.woff binary
|
||||||
|
*.woff2 binary
|
||||||
|
*.ttf binary
|
||||||
|
*.eot binary
|
||||||
|
*.otf binary
|
||||||
|
*.mp4 binary
|
||||||
|
*.mp3 binary
|
||||||
|
*.pdf binary
|
||||||
|
*.zip binary
|
||||||
|
*.gz binary
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from "./aiGenerationClient.ts";
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from "./apiErrorUtils.ts";
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from "./generationRecordClient.ts";
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from "./serverConnection.ts";
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from "./taskSubscription.ts";
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from "./webGenerationGateway.ts";
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from "./toastStore.ts";
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from "./ossAssets.ts";
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from "./workflows.ts";
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
export { default } from "./EcommercePage.tsx";
|
||||||
|
export * from "./EcommercePage.tsx";
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import {
|
import {
|
||||||
AppstoreOutlined,
|
AppstoreOutlined,
|
||||||
ClearOutlined,
|
ClearOutlined,
|
||||||
CloudUploadOutlined,
|
CloudUploadOutlined,
|
||||||
@@ -16,6 +16,7 @@
|
|||||||
MenuFoldOutlined,
|
MenuFoldOutlined,
|
||||||
MenuUnfoldOutlined,
|
MenuUnfoldOutlined,
|
||||||
PaperClipOutlined,
|
PaperClipOutlined,
|
||||||
|
PlusOutlined,
|
||||||
QuestionCircleOutlined,
|
QuestionCircleOutlined,
|
||||||
ReloadOutlined,
|
ReloadOutlined,
|
||||||
ScissorOutlined,
|
ScissorOutlined,
|
||||||
@@ -281,6 +282,12 @@ type CloneBasicSelectKey = "platform" | "market" | "language" | "ratio";
|
|||||||
type CloneModelSelectKey = "gender" | "age" | "ethnicity" | "body";
|
type CloneModelSelectKey = "gender" | "age" | "ethnicity" | "body";
|
||||||
type CloneReferenceMode = "upload" | "link";
|
type CloneReferenceMode = "upload" | "link";
|
||||||
type CloneReplicateLevelKey = "style" | "high";
|
type CloneReplicateLevelKey = "style" | "high";
|
||||||
|
type CloneTemplateAsset = {
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
prompt: string;
|
||||||
|
mediaUrl: string;
|
||||||
|
};
|
||||||
type TryOnModelSource = "ai" | "library";
|
type TryOnModelSource = "ai" | "library";
|
||||||
type TryOnStatus = "idle" | "modeling" | "ready" | "generating" | "done" | "failed";
|
type TryOnStatus = "idle" | "modeling" | "ready" | "generating" | "done" | "failed";
|
||||||
type DetailStatus = "idle" | "ready" | "generating" | "done" | "failed";
|
type DetailStatus = "idle" | "ready" | "generating" | "done" | "failed";
|
||||||
@@ -1001,10 +1008,115 @@ const productSetOutputOptions: Array<{ key: ProductSetOutputKey; label: string;
|
|||||||
{ key: "model", label: "模特图", desc: "真人穿搭展示", icon: <SkinOutlined /> },
|
{ key: "model", label: "模特图", desc: "真人穿搭展示", icon: <SkinOutlined /> },
|
||||||
{ key: "video", label: "短视频", desc: "分镜视频链路", icon: <VideoCameraOutlined /> },
|
{ key: "video", label: "短视频", desc: "分镜视频链路", icon: <VideoCameraOutlined /> },
|
||||||
];
|
];
|
||||||
const cloneOutputOptions: Array<{ key: CloneOutputKey; label: string; desc: string; icon: ReactNode }> = [
|
const cloneOutputOptions: Array<{ key: ProductSetOutputKey; label: string; desc: string; icon: ReactNode }> = [
|
||||||
...productSetOutputOptions,
|
...productSetOutputOptions,
|
||||||
{ key: "hot", label: "爆款复刻", desc: "参考图风格迁移", icon: <FireOutlined /> },
|
|
||||||
];
|
];
|
||||||
|
const cloneTemplateCards: Record<Exclude<CloneOutputKey, "hot">, CloneTemplateAsset[]> = {
|
||||||
|
set: [
|
||||||
|
{
|
||||||
|
id: "set-main",
|
||||||
|
title: "商品套图主图",
|
||||||
|
prompt: "生成一组统一风格的商品套图,包含主图、卖点图、场景图和细节图,主体清晰,色调统一,符合电商平台展示规范。",
|
||||||
|
mediaUrl: ossAssets.ecommerce.productSet.main,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "set-scene",
|
||||||
|
title: "商品套图场景",
|
||||||
|
prompt: "生成生活化场景商品套图,突出商品在真实环境中的使用感、氛围感和转化卖点。",
|
||||||
|
mediaUrl: ossAssets.ecommerce.productSet.scene,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "set-detail",
|
||||||
|
title: "商品套图细节",
|
||||||
|
prompt: "生成突出材质、工艺和边缘细节的商品套图,画面干净,信息聚焦,适合电商详情展示。",
|
||||||
|
mediaUrl: ossAssets.ecommerce.productSet.detail,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "set-selling",
|
||||||
|
title: "商品套图卖点",
|
||||||
|
prompt: "生成强调核心卖点和对比优势的商品套图,信息层级清晰,适合列表页和转化场景。",
|
||||||
|
mediaUrl: ossAssets.ecommerce.productSet.selling,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
detail: [
|
||||||
|
{
|
||||||
|
id: "detail-hero",
|
||||||
|
title: "详情图头图",
|
||||||
|
prompt: "生成适用于 A+ 详情页的头图模块,突出品牌感、主卖点和视觉中心,版式清晰高级。",
|
||||||
|
mediaUrl: ossAssets.ecommerce.detail.longPage,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "detail-grid-a",
|
||||||
|
title: "详情图模块 A",
|
||||||
|
prompt: "生成模块化详情长图,重点展示产品卖点、功能说明和适用场景,适合滚动阅读。",
|
||||||
|
mediaUrl: ossAssets.ecommerce.detail.gridA,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "detail-grid-b",
|
||||||
|
title: "详情图模块 B",
|
||||||
|
prompt: "生成模块化详情长图,强化材质、规格和使用说明,视觉简洁,信息明确。",
|
||||||
|
mediaUrl: ossAssets.ecommerce.detail.gridB,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "detail-grid-c",
|
||||||
|
title: "详情图模块 C",
|
||||||
|
prompt: "生成模块化详情页内容,突出品牌叙事、细节拆解和购买理由,保持统一排版。",
|
||||||
|
mediaUrl: ossAssets.ecommerce.detail.gridC,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
model: [
|
||||||
|
{
|
||||||
|
id: "model-dress-a",
|
||||||
|
title: "模特图穿搭 A",
|
||||||
|
prompt: "生成真人模特穿搭展示图,突出服装版型、上身效果和整体气质,姿态自然。",
|
||||||
|
mediaUrl: ossAssets.ecommerce.tryOn.dressA,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "model-dress-b",
|
||||||
|
title: "模特图穿搭 B",
|
||||||
|
prompt: "生成适合商品展示的模特图,强调衣型、垂感和真实穿着效果,画面干净。",
|
||||||
|
mediaUrl: ossAssets.ecommerce.tryOn.dressB,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "model-woman",
|
||||||
|
title: "模特图女模",
|
||||||
|
prompt: "生成自然站姿的女模特展示图,适合服饰、配件和穿搭类商品展示。",
|
||||||
|
mediaUrl: ossAssets.ecommerce.tryOn.modelWoman,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "model-man",
|
||||||
|
title: "模特图男模",
|
||||||
|
prompt: "生成真实感更强的男模特展示图,突出上身效果、轮廓和场景氛围。",
|
||||||
|
mediaUrl: ossAssets.ecommerce.tryOn.modelMan,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
video: [
|
||||||
|
{
|
||||||
|
id: "video-hook",
|
||||||
|
title: "短视频开场",
|
||||||
|
prompt: "生成适合电商短视频的开场镜头,节奏明确,第一秒就突出产品和核心看点。",
|
||||||
|
mediaUrl: ossAssets.ecommerce.inspiration.tiktokPreference,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "video-scene",
|
||||||
|
title: "短视频场景",
|
||||||
|
prompt: "生成生活化使用场景的短视频分镜,画面连贯,围绕商品使用过程展开。",
|
||||||
|
mediaUrl: ossAssets.ecommerce.inspiration.officeStyleSet,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "video-review",
|
||||||
|
title: "短视频口播",
|
||||||
|
prompt: "生成适合口播讲解的电商短视频结构,包含产品亮点、卖点说明和收尾引导。",
|
||||||
|
mediaUrl: ossAssets.ecommerce.inspiration.asinListing,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "video-conversion",
|
||||||
|
title: "短视频转化",
|
||||||
|
prompt: "生成以转化为目标的短视频分镜,强化开头钩子、卖点展示和行动引导。",
|
||||||
|
mediaUrl: ossAssets.ecommerce.inspiration.competitorListing,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
const cloneSetCountOptions: Array<{
|
const cloneSetCountOptions: Array<{
|
||||||
key: CloneSetCountKey;
|
key: CloneSetCountKey;
|
||||||
title: string;
|
title: string;
|
||||||
@@ -1385,6 +1497,8 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
|
|||||||
const garmentInputRef = useRef<HTMLInputElement>(null);
|
const garmentInputRef = useRef<HTMLInputElement>(null);
|
||||||
const detailInputRef = useRef<HTMLInputElement>(null);
|
const detailInputRef = useRef<HTMLInputElement>(null);
|
||||||
const detailProgressRef = useRef<number | null>(null);
|
const detailProgressRef = useRef<number | null>(null);
|
||||||
|
const hotProgressRef = useRef<number | null>(null);
|
||||||
|
const hotMaterialInputRef = useRef<HTMLInputElement>(null);
|
||||||
const countHoldTimeoutRef = useRef<number | null>(null);
|
const countHoldTimeoutRef = useRef<number | null>(null);
|
||||||
const countHoldIntervalRef = useRef<number | null>(null);
|
const countHoldIntervalRef = useRef<number | null>(null);
|
||||||
const isAuthenticated = Boolean((_props as Record<string, unknown>).isAuthenticated);
|
const isAuthenticated = Boolean((_props as Record<string, unknown>).isAuthenticated);
|
||||||
@@ -1418,7 +1532,7 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
|
|||||||
const [selectedProductSetPreview, setSelectedProductSetPreview] = useState<ProductSetPreviewSelection | null>(null);
|
const [selectedProductSetPreview, setSelectedProductSetPreview] = useState<ProductSetPreviewSelection | null>(null);
|
||||||
const [showHostingModal, setShowHostingModal] = useState(false);
|
const [showHostingModal, setShowHostingModal] = useState(false);
|
||||||
const [productImages, setProductImages] = useState<CloneImageItem[]>([]);
|
const [productImages, setProductImages] = useState<CloneImageItem[]>([]);
|
||||||
const [activeQuickTool, setActiveQuickTool] = useState<"cutout" | "detail" | "watermark" | "image-edit" | "translate" | null>(null);
|
const [activeQuickTool, setActiveQuickTool] = useState<"cutout" | "detail" | "watermark" | "image-edit" | "translate" | "hot" | null>(null);
|
||||||
const [smartCutoutImage, setSmartCutoutImage] = useState<SmartCutoutImageItem | null>(null);
|
const [smartCutoutImage, setSmartCutoutImage] = useState<SmartCutoutImageItem | null>(null);
|
||||||
const [smartCutoutBatchImages, setSmartCutoutBatchImages] = useState<SmartCutoutImageItem[]>([]);
|
const [smartCutoutBatchImages, setSmartCutoutBatchImages] = useState<SmartCutoutImageItem[]>([]);
|
||||||
const [smartCutoutBackgroundColor, setSmartCutoutBackgroundColor] = useState("#ffffff");
|
const [smartCutoutBackgroundColor, setSmartCutoutBackgroundColor] = useState("#ffffff");
|
||||||
@@ -1455,6 +1569,7 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
|
|||||||
const [imageWorkbenchProgress, setImageWorkbenchProgress] = useState(0);
|
const [imageWorkbenchProgress, setImageWorkbenchProgress] = useState(0);
|
||||||
const [isProductUploadDragging, setIsProductUploadDragging] = useState(false);
|
const [isProductUploadDragging, setIsProductUploadDragging] = useState(false);
|
||||||
const [cloneOutput, setCloneOutput] = useState<CloneOutputKey>(defaultCloneOutput);
|
const [cloneOutput, setCloneOutput] = useState<CloneOutputKey>(defaultCloneOutput);
|
||||||
|
const [isCloneTemplateStripVisible, setIsCloneTemplateStripVisible] = useState(false);
|
||||||
const [videoHistoryVisible, setVideoHistoryVisible] = useState(false);
|
const [videoHistoryVisible, setVideoHistoryVisible] = useState(false);
|
||||||
const [isVideoWorkspaceVisible, setIsVideoWorkspaceVisible] = useState(false);
|
const [isVideoWorkspaceVisible, setIsVideoWorkspaceVisible] = useState(false);
|
||||||
const [videoPlanTrigger, setVideoPlanTrigger] = useState(0);
|
const [videoPlanTrigger, setVideoPlanTrigger] = useState(0);
|
||||||
@@ -1905,24 +2020,25 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
|
|||||||
const [detailStatus, setDetailStatus] = useState<DetailStatus>("idle");
|
const [detailStatus, setDetailStatus] = useState<DetailStatus>("idle");
|
||||||
const [detailResultUrl, setDetailResultUrl] = useState<string | null>(null);
|
const [detailResultUrl, setDetailResultUrl] = useState<string | null>(null);
|
||||||
const [detailProgress, setDetailProgress] = useState(0);
|
const [detailProgress, setDetailProgress] = useState(0);
|
||||||
|
const [hotRequirement, setHotRequirement] = useState("");
|
||||||
|
const [isHotMaterialDragging, setIsHotMaterialDragging] = useState(false);
|
||||||
|
const [hotMaterialHoverZoom, setHotMaterialHoverZoom] = useState<{ src: string; x: number; y: number; placement: "above" | "below" } | null>(null);
|
||||||
|
const [hotPlatform, setHotPlatform] = useState(platformOptions[0]);
|
||||||
|
const [hotMarket, setHotMarket] = useState(marketOptions[0]);
|
||||||
|
const [hotLanguage, setHotLanguage] = useState(getPlatformDefaultLanguage(platformOptions[0], marketOptions[0]));
|
||||||
|
const [hotRatio, setHotRatio] = useState(getQuickSetRatioValue(getPlatformDefaultRatio(platformOptions[0], "detail")));
|
||||||
|
const [hotStatus, setHotStatus] = useState<DetailStatus>("idle");
|
||||||
|
const [hotResultUrl, setHotResultUrl] = useState<string | null>(null);
|
||||||
|
const [hotProgress, setHotProgress] = useState(0);
|
||||||
const productSetRatioOptions = useMemo(
|
const productSetRatioOptions = useMemo(
|
||||||
() => getPlatformRatioOptions(productSetPlatform, productSetOutput),
|
() => getPlatformRatioOptions(productSetPlatform, productSetOutput),
|
||||||
[productSetOutput, productSetPlatform],
|
[productSetOutput, productSetPlatform],
|
||||||
);
|
);
|
||||||
const hotUploadedRatioOption = useMemo(
|
|
||||||
() => cloneOutput === "hot" ? formatUploadedImageRatio(cloneReferenceImages[0]) : null,
|
|
||||||
[cloneOutput, cloneReferenceImages],
|
|
||||||
);
|
|
||||||
const baseCloneRatioOptions = useMemo(
|
const baseCloneRatioOptions = useMemo(
|
||||||
() => getPlatformRatioOptions(platform, cloneOutput),
|
() => getPlatformRatioOptions(platform, cloneOutput),
|
||||||
[cloneOutput, platform],
|
[cloneOutput, platform],
|
||||||
);
|
);
|
||||||
const cloneRatioOptions = useMemo(
|
const cloneRatioOptions = baseCloneRatioOptions;
|
||||||
() => hotUploadedRatioOption
|
|
||||||
? getUniqueRatioOptions([...baseCloneRatioOptions, hotUploadedRatioOption])
|
|
||||||
: baseCloneRatioOptions,
|
|
||||||
[baseCloneRatioOptions, hotUploadedRatioOption],
|
|
||||||
);
|
|
||||||
const productSetLanguageOptions = useMemo(
|
const productSetLanguageOptions = useMemo(
|
||||||
() => getPlatformLanguageOptions(productSetPlatform, productSetMarket),
|
() => getPlatformLanguageOptions(productSetPlatform, productSetMarket),
|
||||||
[productSetMarket, productSetPlatform],
|
[productSetMarket, productSetPlatform],
|
||||||
@@ -1935,6 +2051,10 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
|
|||||||
() => getPlatformLanguageOptions(detailPlatform, detailMarket),
|
() => getPlatformLanguageOptions(detailPlatform, detailMarket),
|
||||||
[detailMarket, detailPlatform],
|
[detailMarket, detailPlatform],
|
||||||
);
|
);
|
||||||
|
const hotLanguageOptions = useMemo(
|
||||||
|
() => getPlatformLanguageOptions(hotPlatform, hotMarket),
|
||||||
|
[hotMarket, hotPlatform],
|
||||||
|
);
|
||||||
const ecommerceMentionImages: MentionImageOption[] = [
|
const ecommerceMentionImages: MentionImageOption[] = [
|
||||||
...productImages.map((image, index) => ({ ...image, label: `商品图 ${index + 1}` })),
|
...productImages.map((image, index) => ({ ...image, label: `商品图 ${index + 1}` })),
|
||||||
...cloneReferenceImages.map((image, index) => ({ ...image, label: `参考图 ${index + 1}` })),
|
...cloneReferenceImages.map((image, index) => ({ ...image, label: `参考图 ${index + 1}` })),
|
||||||
@@ -1952,6 +2072,7 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
|
|||||||
const selectedProductSetOutput =
|
const selectedProductSetOutput =
|
||||||
productSetOutputOptions.find((option) => option.key === productSetOutput) ?? productSetOutputOptions[0]!;
|
productSetOutputOptions.find((option) => option.key === productSetOutput) ?? productSetOutputOptions[0]!;
|
||||||
const selectedCloneOutput = cloneOutputOptions.find((option) => option.key === cloneOutput) ?? cloneOutputOptions[1]!;
|
const selectedCloneOutput = cloneOutputOptions.find((option) => option.key === cloneOutput) ?? cloneOutputOptions[1]!;
|
||||||
|
const activeCloneTemplateCards = cloneTemplateCards[cloneOutput === "hot" ? "set" : cloneOutput];
|
||||||
const cloneRequirementPlaceholder =
|
const cloneRequirementPlaceholder =
|
||||||
cloneOutput === "model"
|
cloneOutput === "model"
|
||||||
? "建议包含以下信息:产品名称、核心卖点、期望场景、模特外貌描述(如小麦色皮肤、齐刘海、眼角有泪痣)、具体参数"
|
? "建议包含以下信息:产品名称、核心卖点、期望场景、模特外貌描述(如小麦色皮肤、齐刘海、眼角有泪痣)、具体参数"
|
||||||
@@ -1965,6 +2086,7 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
|
|||||||
const canGenerate = productImages.length > 0 && status !== "generating";
|
const canGenerate = productImages.length > 0 && status !== "generating";
|
||||||
const canGenerateTryOn = garmentImages.length > 0 && tryOnStatus !== "generating" && tryOnStatus !== "modeling";
|
const canGenerateTryOn = garmentImages.length > 0 && tryOnStatus !== "generating" && tryOnStatus !== "modeling";
|
||||||
const canGenerateDetail = detailProductImages.length > 0 && detailStatus !== "generating";
|
const canGenerateDetail = detailProductImages.length > 0 && detailStatus !== "generating";
|
||||||
|
const canGenerateHot = cloneReferenceImages.length > 0 && hotStatus !== "generating";
|
||||||
const cloneVideoDurationProgress =
|
const cloneVideoDurationProgress =
|
||||||
((cloneVideoDuration - cloneVideoDurationMin) / (cloneVideoDurationMax - cloneVideoDurationMin)) * 100;
|
((cloneVideoDuration - cloneVideoDurationMin) / (cloneVideoDurationMax - cloneVideoDurationMin)) * 100;
|
||||||
const cloneVideoDurationStyle: CSSProperties = useMemo(
|
const cloneVideoDurationStyle: CSSProperties = useMemo(
|
||||||
@@ -2079,21 +2201,8 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
|
|||||||
|
|
||||||
const openSmartCutoutUpload = () => {
|
const openSmartCutoutUpload = () => {
|
||||||
clearSmartCutoutTransition();
|
clearSmartCutoutTransition();
|
||||||
setSmartCutoutTransitionMessage({
|
|
||||||
title: "正在进入智能抠图",
|
|
||||||
subtitle: "为你打开图片处理工具",
|
|
||||||
});
|
|
||||||
setActiveQuickTool("cutout");
|
|
||||||
setSmartCutoutBatchImages((current) => {
|
|
||||||
revokeSmartCutoutItems(current);
|
|
||||||
return [];
|
|
||||||
});
|
|
||||||
setSmartCutoutImage((current) => {
|
|
||||||
revokeSmartCutoutItem(current);
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
setIsSmartCutoutComparing(false);
|
|
||||||
setComposerMenu(null);
|
setComposerMenu(null);
|
||||||
|
toast.info("功能正在优化中");
|
||||||
};
|
};
|
||||||
|
|
||||||
const openWatermarkRemovalPage = () => {
|
const openWatermarkRemovalPage = () => {
|
||||||
@@ -2307,9 +2416,8 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
|
|||||||
|
|
||||||
const openImageTranslatePage = () => {
|
const openImageTranslatePage = () => {
|
||||||
clearSmartCutoutTransition();
|
clearSmartCutoutTransition();
|
||||||
setActiveQuickTool("translate");
|
|
||||||
setComposerMenu(null);
|
setComposerMenu(null);
|
||||||
setIsCloneSettingsCollapsed(false);
|
toast.info("功能正在优化中");
|
||||||
};
|
};
|
||||||
|
|
||||||
const closeImageTranslatePage = () => {
|
const closeImageTranslatePage = () => {
|
||||||
@@ -2843,6 +2951,16 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
|
|||||||
if (!selectedDetailModules.length) setSelectedDetailModules(defaultCloneDetailModuleIds);
|
if (!selectedDetailModules.length) setSelectedDetailModules(defaultCloneDetailModuleIds);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const openHotClonePage = () => {
|
||||||
|
clearSmartCutoutTransition();
|
||||||
|
setActiveQuickTool("hot");
|
||||||
|
setComposerMenu(null);
|
||||||
|
setIsCloneSettingsCollapsed(false);
|
||||||
|
setIsQuickPanelCollapsed(false);
|
||||||
|
setPreviewZoom(1);
|
||||||
|
resetQuickSetSelectState();
|
||||||
|
};
|
||||||
|
|
||||||
const closeSmartCutoutTool = () => {
|
const closeSmartCutoutTool = () => {
|
||||||
runSmartCutoutPageTransition(
|
runSmartCutoutPageTransition(
|
||||||
{
|
{
|
||||||
@@ -3201,6 +3319,17 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const removeCloneReferenceImage = (imageId: string) => {
|
||||||
|
setCloneReferenceImages((current) => {
|
||||||
|
const next = current.filter((item) => item.id !== imageId);
|
||||||
|
if (next.length === 0) {
|
||||||
|
setHotStatus("idle");
|
||||||
|
setHotResultUrl(null);
|
||||||
|
}
|
||||||
|
return next;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const handleCloneReferenceUpload = (event: ChangeEvent<HTMLInputElement>) => {
|
const handleCloneReferenceUpload = (event: ChangeEvent<HTMLInputElement>) => {
|
||||||
const files = event.target.files;
|
const files = event.target.files;
|
||||||
if (!files?.length) return;
|
if (!files?.length) return;
|
||||||
@@ -3305,23 +3434,29 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
|
|||||||
const normalizedPlatform = normalizePlatform(nextPlatform);
|
const normalizedPlatform = normalizePlatform(nextPlatform);
|
||||||
setPlatform(normalizedPlatform);
|
setPlatform(normalizedPlatform);
|
||||||
setRatio((current) =>
|
setRatio((current) =>
|
||||||
cloneOutput === "hot" && current.startsWith("上传图片") && hotUploadedRatioOption
|
normalizeRatioForPlatform(normalizedPlatform, current, cloneOutput),
|
||||||
? hotUploadedRatioOption
|
|
||||||
: normalizeRatioForPlatform(normalizedPlatform, current, cloneOutput),
|
|
||||||
);
|
);
|
||||||
setLanguage(getPlatformDefaultLanguage(normalizedPlatform, market));
|
setLanguage(getPlatformDefaultLanguage(normalizedPlatform, market));
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleCloneOutputChange = (nextOutput: CloneOutputKey) => {
|
const handleCloneOutputChange = (nextOutput: CloneOutputKey) => {
|
||||||
setCloneOutput(nextOutput);
|
setCloneOutput(nextOutput);
|
||||||
|
setIsCloneTemplateStripVisible(true);
|
||||||
if (nextOutput !== "video") setIsVideoWorkspaceVisible(false);
|
if (nextOutput !== "video") setIsVideoWorkspaceVisible(false);
|
||||||
setRatio((current) =>
|
setRatio((current) =>
|
||||||
nextOutput === "hot" && current.startsWith("上传图片") && hotUploadedRatioOption
|
normalizeRatioForPlatform(platform, current, nextOutput),
|
||||||
? hotUploadedRatioOption
|
|
||||||
: normalizeRatioForPlatform(platform, current, nextOutput),
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleCloneModeTabClick = (nextOutput: CloneOutputKey) => {
|
||||||
|
if (nextOutput === cloneOutput) {
|
||||||
|
setIsCloneTemplateStripVisible((visible) => !visible);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
handleCloneOutputChange(nextOutput);
|
||||||
|
setComposerMenu(null);
|
||||||
|
};
|
||||||
|
|
||||||
const handleCloneMarketChange = (nextMarket: string) => {
|
const handleCloneMarketChange = (nextMarket: string) => {
|
||||||
const normalizedMarket = normalizeMarket(nextMarket);
|
const normalizedMarket = normalizeMarket(nextMarket);
|
||||||
setMarket(normalizedMarket);
|
setMarket(normalizedMarket);
|
||||||
@@ -3457,14 +3592,12 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setRatio((current) => {
|
setRatio((current) => {
|
||||||
const platformRatios = getPlatformRatioOptions(platform, cloneOutput);
|
const platformRatios = getPlatformRatioOptions(platform, cloneOutput);
|
||||||
const availableRatios = hotUploadedRatioOption ? getUniqueRatioOptions([...platformRatios, hotUploadedRatioOption]) : platformRatios;
|
if (platformRatios.includes(current)) return current;
|
||||||
if (current.startsWith("上传图片") && hotUploadedRatioOption) return hotUploadedRatioOption;
|
|
||||||
if (availableRatios.includes(current)) return current;
|
|
||||||
const normalizedRatio = normalizeRatioToken(current);
|
const normalizedRatio = normalizeRatioToken(current);
|
||||||
const matchedRatio = availableRatios.find((option) => normalizeRatioToken(option).includes(normalizedRatio));
|
const matchedRatio = platformRatios.find((option) => normalizeRatioToken(option).includes(normalizedRatio));
|
||||||
return matchedRatio ?? getPlatformDefaultRatio(platform, cloneOutput);
|
return matchedRatio ?? getPlatformDefaultRatio(platform, cloneOutput);
|
||||||
});
|
});
|
||||||
}, [cloneOutput, hotUploadedRatioOption, platform]);
|
}, [cloneOutput, platform]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (skipInitialCloneAutoSaveRef.current) {
|
if (skipInitialCloneAutoSaveRef.current) {
|
||||||
@@ -4196,6 +4329,133 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleHotPlatformChange = (nextPlatform: string) => {
|
||||||
|
const normalizedPlatform = normalizePlatform(nextPlatform);
|
||||||
|
setHotPlatform(normalizedPlatform);
|
||||||
|
setHotLanguage(getPlatformDefaultLanguage(normalizedPlatform, hotMarket));
|
||||||
|
setHotRatio((current) => getQuickSetRatioValue(current));
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleHotMarketChange = (nextMarket: string) => {
|
||||||
|
const normalizedMarket = normalizeMarket(nextMarket);
|
||||||
|
setHotMarket(normalizedMarket);
|
||||||
|
setHotLanguage(getPlatformDefaultLanguage(hotPlatform, normalizedMarket));
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleHotAiWrite = () => {
|
||||||
|
setHotRequirement(
|
||||||
|
"1.产品名称:便携式咖啡保温杯\n2.核心卖点:316不锈钢内胆、12小时长效保温、防漏便携、大容量\n3.参考风格:极简日系、暖光氛围、生活场景\n4.期望场景:办公桌面、户外通勤、运动健身\n5.具体参数:容量500ml、口径4.5cm、高度22cm",
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const stopHotProgress = () => {
|
||||||
|
if (hotProgressRef.current !== null) {
|
||||||
|
window.clearInterval(hotProgressRef.current);
|
||||||
|
hotProgressRef.current = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const startHotProgress = () => {
|
||||||
|
stopHotProgress();
|
||||||
|
setHotProgress(0);
|
||||||
|
hotProgressRef.current = window.setInterval(() => {
|
||||||
|
setHotProgress((prev) => {
|
||||||
|
if (prev >= 90) {
|
||||||
|
stopHotProgress();
|
||||||
|
return 90;
|
||||||
|
}
|
||||||
|
return prev + (90 - prev) * 0.06;
|
||||||
|
});
|
||||||
|
}, 500);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleHotGenerate = () => {
|
||||||
|
if (!canGenerateHot) return;
|
||||||
|
imageAbortRef.current = { current: false };
|
||||||
|
lastFailedActionRef.current = null;
|
||||||
|
startHotProgress();
|
||||||
|
void generateEcommerceImage(
|
||||||
|
"hot", cloneReferenceImages, hotRequirement,
|
||||||
|
hotPlatform, hotRatio, hotLanguage, hotMarket,
|
||||||
|
undefined,
|
||||||
|
(s: string) => {
|
||||||
|
setHotStatus(s as DetailStatus);
|
||||||
|
if (s === "done") {
|
||||||
|
stopHotProgress();
|
||||||
|
setHotProgress(100);
|
||||||
|
} else if (s === "failed" || s === "idle") {
|
||||||
|
stopHotProgress();
|
||||||
|
setHotProgress(0);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
(res) => setHotResultUrl(res[0]?.src ?? null),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleHotMaterialMouseEnter = (src: string, event: ReactMouseEvent<HTMLElement>) => {
|
||||||
|
const rect = event.currentTarget.getBoundingClientRect();
|
||||||
|
const previewHalfWidth = 150;
|
||||||
|
const previewHeight = 360;
|
||||||
|
const gap = 12;
|
||||||
|
const viewportWidth = window.innerWidth || document.documentElement.clientWidth;
|
||||||
|
const viewportHeight = window.innerHeight || document.documentElement.clientHeight;
|
||||||
|
const x = Math.min(
|
||||||
|
Math.max(rect.left + rect.width / 2, previewHalfWidth + gap),
|
||||||
|
Math.max(previewHalfWidth + gap, viewportWidth - previewHalfWidth - gap),
|
||||||
|
);
|
||||||
|
const showAbove = rect.top > previewHeight + gap;
|
||||||
|
const y = showAbove
|
||||||
|
? rect.top - gap
|
||||||
|
: Math.min(rect.bottom + gap, viewportHeight - gap);
|
||||||
|
setHotMaterialHoverZoom({ src, x, y, placement: showAbove ? "above" : "below" });
|
||||||
|
};
|
||||||
|
const handleHotMaterialMouseLeave = () => setHotMaterialHoverZoom(null);
|
||||||
|
|
||||||
|
const renderHotMaterialThumbs = (items: CloneImageItem[], onRemove: (imageId: string) => void) => (
|
||||||
|
<div className="ecom-quick-upload-thumbs" aria-label="已上传商品素材">
|
||||||
|
{items.map((item) => (
|
||||||
|
<figure
|
||||||
|
key={item.id}
|
||||||
|
className="ecom-command-asset-thumb ecom-quick-upload-thumb"
|
||||||
|
onMouseEnter={(e) => handleHotMaterialMouseEnter(item.src, e)}
|
||||||
|
onMouseLeave={handleHotMaterialMouseLeave}
|
||||||
|
>
|
||||||
|
<img src={item.src} alt={item.name} />
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="ecom-hot-material-delete"
|
||||||
|
aria-label={`删除${item.name || "图片"}`}
|
||||||
|
onClick={(event) => {
|
||||||
|
event.stopPropagation();
|
||||||
|
setHotMaterialHoverZoom(null);
|
||||||
|
onRemove(item.id);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false">
|
||||||
|
<path d="M9 6V5a2 2 0 0 1 2-2h2a2 2 0 0 1 2 2v1" />
|
||||||
|
<path d="M5 6h14" />
|
||||||
|
<path d="M8 6l1 14h6l1-14" />
|
||||||
|
<path d="M10.5 10v6" />
|
||||||
|
<path d="M13.5 10v6" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</figure>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
const closeHotClonePage = () => {
|
||||||
|
stopHotProgress();
|
||||||
|
setActiveQuickTool(null);
|
||||||
|
setHotStatus("idle");
|
||||||
|
setHotResultUrl(null);
|
||||||
|
setHotProgress(0);
|
||||||
|
setHotRequirement("");
|
||||||
|
setIsHotMaterialDragging(false);
|
||||||
|
setHotMaterialHoverZoom(null);
|
||||||
|
setComposerMenu(null);
|
||||||
|
};
|
||||||
|
|
||||||
const resetTask = () => {
|
const resetTask = () => {
|
||||||
setSetImages([]);
|
setSetImages([]);
|
||||||
setProductSetRequirement("");
|
setProductSetRequirement("");
|
||||||
@@ -4258,6 +4518,7 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
|
|||||||
const isWatermarkTool = isCloneTool && activeQuickTool === "watermark";
|
const isWatermarkTool = isCloneTool && activeQuickTool === "watermark";
|
||||||
const isImageEditTool = isCloneTool && activeQuickTool === "image-edit";
|
const isImageEditTool = isCloneTool && activeQuickTool === "image-edit";
|
||||||
const isTranslateTool = isCloneTool && activeQuickTool === "translate";
|
const isTranslateTool = isCloneTool && activeQuickTool === "translate";
|
||||||
|
const isHotCloneTool = isCloneTool && activeQuickTool === "hot";
|
||||||
const pageLabel = isSetTool ? "商品套图" : isDetail ? "A+/详情页" : isTryOn ? "AI服饰穿戴" : activeToolMeta?.label || "商品工具";
|
const pageLabel = isSetTool ? "商品套图" : isDetail ? "A+/详情页" : isTryOn ? "AI服饰穿戴" : activeToolMeta?.label || "商品工具";
|
||||||
const setPrimaryLabel =
|
const setPrimaryLabel =
|
||||||
setImages.length === 0
|
setImages.length === 0
|
||||||
@@ -4535,6 +4796,19 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
|
|||||||
{ key: "ratio", label: "尺寸/比例", value: getQuickSetRatioValue(detailRatio), options: quickSetRatioOptions, onChange: setDetailRatio },
|
{ key: "ratio", label: "尺寸/比例", value: getQuickSetRatioValue(detailRatio), options: quickSetRatioOptions, onChange: setDetailRatio },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const quickHotBasicSelects: Array<{
|
||||||
|
key: CloneBasicSelectKey;
|
||||||
|
label: string;
|
||||||
|
value: string;
|
||||||
|
options: string[];
|
||||||
|
onChange: (value: string) => void;
|
||||||
|
}> = [
|
||||||
|
{ key: "platform", label: "平台", value: hotPlatform, options: platformOptions, onChange: handleHotPlatformChange },
|
||||||
|
{ key: "market", label: "国家", value: hotMarket, options: marketOptions, onChange: handleHotMarketChange },
|
||||||
|
{ key: "language", label: "语种", value: hotLanguage, options: hotLanguageOptions, onChange: setHotLanguage },
|
||||||
|
{ key: "ratio", label: "尺寸/比例", value: getQuickSetRatioValue(hotRatio), options: quickSetRatioOptions, onChange: setHotRatio },
|
||||||
|
];
|
||||||
|
|
||||||
const cloneModelSelects: Array<{
|
const cloneModelSelects: Array<{
|
||||||
key: CloneModelSelectKey;
|
key: CloneModelSelectKey;
|
||||||
label: string;
|
label: string;
|
||||||
@@ -4814,8 +5088,6 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
|
|||||||
? cloneModelPanelTab === "scene" ? "场景设置" : "模特设置"
|
? cloneModelPanelTab === "scene" ? "场景设置" : "模特设置"
|
||||||
: cloneOutput === "video"
|
: cloneOutput === "video"
|
||||||
? String(cloneVideoDuration) + "秒 " + (cloneVideoQuality === "standard" ? "720P" : "1080P")
|
? String(cloneVideoDuration) + "秒 " + (cloneVideoQuality === "standard" ? "720P" : "1080P")
|
||||||
: cloneOutput === "hot"
|
|
||||||
? cloneReplicateLevel === "style" ? "风格复刻" : "高度复刻"
|
|
||||||
: "换装素材";
|
: "换装素材";
|
||||||
|
|
||||||
const renderComposerMenu = () => {
|
const renderComposerMenu = () => {
|
||||||
@@ -4952,48 +5224,6 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
|
|||||||
<input type="range" min={cloneVideoDurationMin} max={cloneVideoDurationMax} step={5} value={cloneVideoDuration} onChange={(event) => setCloneVideoDuration(clampCloneVideoDuration(Number(event.target.value)))} />
|
<input type="range" min={cloneVideoDurationMin} max={cloneVideoDurationMax} step={5} value={cloneVideoDuration} onChange={(event) => setCloneVideoDuration(clampCloneVideoDuration(Number(event.target.value)))} />
|
||||||
</label>
|
</label>
|
||||||
</>
|
</>
|
||||||
) : cloneOutput === "hot" ? (
|
|
||||||
<>
|
|
||||||
<header><strong>爆款复刻设置</strong><span>{cloneReferenceImages.length}/{maxCloneReferenceImages}</span></header>
|
|
||||||
<div className="ecom-command-hot-layout">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className={`ecom-command-hot-upload${isCloneReferenceDragging ? " is-dragging" : ""}${cloneReferenceImages.length ? " has-image" : ""}`}
|
|
||||||
onClick={() => cloneReferenceInputRef.current?.click()}
|
|
||||||
onDragOver={handleCloneReferenceDragOver}
|
|
||||||
onDragLeave={handleCloneReferenceDragLeave}
|
|
||||||
onDrop={handleCloneReferenceDrop}
|
|
||||||
>
|
|
||||||
{cloneReferenceImages[0]?.src ? (
|
|
||||||
<>
|
|
||||||
<span className="ecom-command-hot-thumb-grid">
|
|
||||||
{cloneReferenceImages.map((image, index) => (
|
|
||||||
<span key={image.id} className="ecom-command-hot-thumb">
|
|
||||||
<img src={image.src} alt={`参考图 ${index + 1}`} />
|
|
||||||
<span className="ecom-command-hot-zoom" aria-hidden="true">
|
|
||||||
<img src={image.src} alt="" />
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
))}
|
|
||||||
</span>
|
|
||||||
<span>已上传 {cloneReferenceImages.length} 张,点击继续上传</span>
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<strong>+ 上传参考图片</strong>
|
|
||||||
<span>支持拖拽上传</span>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</button>
|
|
||||||
<div className="ecom-command-hot-levels">
|
|
||||||
{cloneReplicateLevelOptions.map((option) => (
|
|
||||||
<button key={option.key} type="button" className={cloneReplicateLevel === option.key ? "is-active" : ""} onClick={() => setCloneReplicateLevel(option.key)}>
|
|
||||||
<strong>{option.title}</strong><span>{option.desc}</span>
|
|
||||||
</button>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<header><strong>视频换装设置</strong><span>上传视频和服装参考</span></header>
|
<header><strong>视频换装设置</strong><span>上传视频和服装参考</span></header>
|
||||||
@@ -5065,6 +5295,60 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
|
|||||||
toast.success("提示词已填入指令栏");
|
toast.success("提示词已填入指令栏");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const applyComposerPrompt = (prompt: string) => {
|
||||||
|
const nextValue = prompt.slice(0, 500);
|
||||||
|
setActiveQuickTool(null);
|
||||||
|
setComposerMenu(null);
|
||||||
|
setRequirement(nextValue);
|
||||||
|
syncRequirementMentionQuery(nextValue, nextValue.length);
|
||||||
|
setInspirationPreview(null);
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
const textarea = requirementTextareaRef.current;
|
||||||
|
if (textarea) {
|
||||||
|
textarea.focus();
|
||||||
|
textarea.setSelectionRange(nextValue.length, nextValue.length);
|
||||||
|
textarea.scrollIntoView({ behavior: "smooth", block: "center" });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const addTemplateImageToComposer = async (card: CloneTemplateAsset) => {
|
||||||
|
if (productImages.length >= maxCloneProductImages) {
|
||||||
|
toast.info("模板图片已达上限");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const stamp = Date.now();
|
||||||
|
const uploaded = await aiGenerationClient.uploadAssetByUrl({
|
||||||
|
sourceUrl: card.mediaUrl,
|
||||||
|
name: `${card.id}-${stamp}`,
|
||||||
|
scope: ecommerceOssScopes.productSource,
|
||||||
|
});
|
||||||
|
const nextImage: CloneImageItem = {
|
||||||
|
id: `template-${card.id}-${stamp}`,
|
||||||
|
src: uploaded.url || card.mediaUrl,
|
||||||
|
name: card.title,
|
||||||
|
ossKey: uploaded.ossKey,
|
||||||
|
};
|
||||||
|
setProductImages((current) => [...current, nextImage].slice(0, maxCloneProductImages));
|
||||||
|
void readImageDimensions(nextImage.src)
|
||||||
|
.then(({ width, height }) => {
|
||||||
|
setProductImages((current) =>
|
||||||
|
current.map((item) => (item.id === nextImage.id ? { ...item, width, height } : item)),
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.catch(() => undefined);
|
||||||
|
} catch {
|
||||||
|
toast.error("模板图片导入失败");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCloneTemplateCardClick = (card: CloneTemplateAsset) => {
|
||||||
|
void addTemplateImageToComposer(card);
|
||||||
|
applyComposerPrompt(card.prompt);
|
||||||
|
};
|
||||||
|
|
||||||
const inspirationPreviewOverlay =
|
const inspirationPreviewOverlay =
|
||||||
inspirationPreview && typeof document !== "undefined"
|
inspirationPreview && typeof document !== "undefined"
|
||||||
? createPortal(
|
? createPortal(
|
||||||
@@ -5403,7 +5687,7 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
|
|||||||
key={option.key}
|
key={option.key}
|
||||||
type="button"
|
type="button"
|
||||||
className={cloneOutput === option.key ? "is-active" : ""}
|
className={cloneOutput === option.key ? "is-active" : ""}
|
||||||
onClick={() => handleCloneOutputChange(option.key)}
|
onClick={() => handleCloneModeTabClick(option.key)}
|
||||||
>
|
>
|
||||||
<span className={`ecom-command-mode-icon ecom-command-mode-icon--${option.key}`} aria-hidden="true">{option.icon}</span>
|
<span className={`ecom-command-mode-icon ecom-command-mode-icon--${option.key}`} aria-hidden="true">{option.icon}</span>
|
||||||
<strong>{option.label}</strong>
|
<strong>{option.label}</strong>
|
||||||
@@ -5502,10 +5786,30 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
|
|||||||
</div>
|
</div>
|
||||||
{renderComposerMenu()}
|
{renderComposerMenu()}
|
||||||
</div>
|
</div>
|
||||||
|
{(status === "idle" || status === "ready") && !showMainVideoWorkspace && isCloneTemplateStripVisible ? (
|
||||||
|
<section className={`ecom-command-template-strip ecom-command-template-strip--${cloneOutput}`} aria-label="模板卡片">
|
||||||
|
{activeCloneTemplateCards.map((card) => (
|
||||||
|
<button
|
||||||
|
key={card.id}
|
||||||
|
type="button"
|
||||||
|
className="ecom-command-template-card"
|
||||||
|
aria-label={card.title}
|
||||||
|
onClick={(event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
handleCloneTemplateCardClick(card);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span className="ecom-command-template-card__blank" aria-hidden="true" />
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</section>
|
||||||
|
) : null}
|
||||||
{(status === "idle" || status === "ready") && !showMainVideoWorkspace ? (
|
{(status === "idle" || status === "ready") && !showMainVideoWorkspace ? (
|
||||||
<section className="ecom-command-quick-board" aria-label="快捷功能">
|
<section className="ecom-command-quick-board" aria-label="快捷功能">
|
||||||
{[
|
{[
|
||||||
{ label: "A+/详情页", tone: "detail", icon: <LayoutOutlined />, onClick: openQuickDetailPage },
|
{ label: "A+/详情页", tone: "detail", icon: <LayoutOutlined />, onClick: openQuickDetailPage },
|
||||||
|
{ label: "爆款复刻", tone: "hot", icon: <FireOutlined />, onClick: openHotClonePage },
|
||||||
{ label: "图片修改", tone: "edit", icon: <EditOutlined />, onClick: openImageWorkbenchPage },
|
{ label: "图片修改", tone: "edit", icon: <EditOutlined />, onClick: openImageWorkbenchPage },
|
||||||
{ label: "智能抠图", tone: "cutout", icon: <ScissorOutlined />, onClick: openSmartCutoutUpload },
|
{ label: "智能抠图", tone: "cutout", icon: <ScissorOutlined />, onClick: openSmartCutoutUpload },
|
||||||
{ label: "去除水印", tone: "watermark", icon: <ClearOutlined />, onClick: openWatermarkRemovalPage },
|
{ label: "去除水印", tone: "watermark", icon: <ClearOutlined />, onClick: openWatermarkRemovalPage },
|
||||||
@@ -6523,6 +6827,7 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const quickDetailVisibleSelect = quickDetailBasicSelects.find((item) => item.key === visibleQuickSetSelect) ?? null;
|
const quickDetailVisibleSelect = quickDetailBasicSelects.find((item) => item.key === visibleQuickSetSelect) ?? null;
|
||||||
|
const quickHotVisibleSelect = quickHotBasicSelects.find((item) => item.key === visibleQuickSetSelect) ?? null;
|
||||||
|
|
||||||
const quickDetailPreview = (
|
const quickDetailPreview = (
|
||||||
<main key="quick-detail" className={`ecom-quick-set-page ecom-quick-detail-page ecom-tool-page-enter${isQuickPanelCollapsed ? " is-panel-collapsed" : ""}`} aria-label="A+详情页生成">
|
<main key="quick-detail" className={`ecom-quick-set-page ecom-quick-detail-page ecom-tool-page-enter${isQuickPanelCollapsed ? " is-panel-collapsed" : ""}`} aria-label="A+详情页生成">
|
||||||
@@ -6720,6 +7025,270 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
|
|||||||
</main>
|
</main>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const hotClonePreview = (
|
||||||
|
<main key="quick-hot" className={`ecom-quick-set-page ecom-quick-hot-page ecom-tool-page-enter${isQuickPanelCollapsed ? " is-panel-collapsed" : ""}`} aria-label="爆款复刻生成">
|
||||||
|
<div className="ecom-quick-set-body">
|
||||||
|
<aside className="ecom-quick-set-panel" aria-label="爆款复刻设置" onWheel={handleQuickPanelWheel}>
|
||||||
|
<header className="ecom-quick-set-panel-head">
|
||||||
|
<strong className="ecom-quick-set-page-title">爆款复刻</strong>
|
||||||
|
<button type="button" className="ecom-quick-set-back" onClick={closeHotClonePage}>
|
||||||
|
首页
|
||||||
|
</button>
|
||||||
|
<button type="button" className="ecom-quick-set-back" onClick={closeHotClonePage}>
|
||||||
|
上一页
|
||||||
|
</button>
|
||||||
|
</header>
|
||||||
|
<section>
|
||||||
|
<strong><FileImageOutlined /> 上传素材</strong>
|
||||||
|
{productImages.length ? (
|
||||||
|
<div
|
||||||
|
role="button"
|
||||||
|
tabIndex={0}
|
||||||
|
className={`ecom-quick-set-upload ecom-quick-hot-material has-images${isHotMaterialDragging ? " is-dragging" : ""}`}
|
||||||
|
onClick={() => hotMaterialInputRef.current?.click()}
|
||||||
|
onKeyDown={(event) => openQuickUploadWithKeyboard(event, hotMaterialInputRef)}
|
||||||
|
onDragOver={(event) => { event.preventDefault(); event.stopPropagation(); if (event.dataTransfer.types.includes("Files")) setIsHotMaterialDragging(true); }}
|
||||||
|
onDragLeave={(event) => { event.preventDefault(); event.stopPropagation(); if (event.currentTarget === event.target || !event.currentTarget.contains(event.relatedTarget as Node)) setIsHotMaterialDragging(false); }}
|
||||||
|
onDrop={(event) => { event.preventDefault(); event.stopPropagation(); setIsHotMaterialDragging(false); const files = Array.from(event.dataTransfer.files); if (files.length) addProductImages(files); }}
|
||||||
|
>
|
||||||
|
{renderHotMaterialThumbs(productImages, removeProductImage)}
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="ecom-quick-hot-add-btn"
|
||||||
|
aria-label="添加更多素材"
|
||||||
|
onClick={(event) => {
|
||||||
|
event.stopPropagation();
|
||||||
|
hotMaterialInputRef.current?.click();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<PlusOutlined />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div
|
||||||
|
role="button"
|
||||||
|
tabIndex={0}
|
||||||
|
className={`ecom-quick-set-upload ecom-quick-hot-material${isHotMaterialDragging ? " is-dragging" : ""}`}
|
||||||
|
onClick={() => hotMaterialInputRef.current?.click()}
|
||||||
|
onKeyDown={(event) => openQuickUploadWithKeyboard(event, hotMaterialInputRef)}
|
||||||
|
onDragOver={(event) => { event.preventDefault(); event.stopPropagation(); if (event.dataTransfer.types.includes("Files")) setIsHotMaterialDragging(true); }}
|
||||||
|
onDragLeave={(event) => { event.preventDefault(); event.stopPropagation(); if (event.currentTarget === event.target || !event.currentTarget.contains(event.relatedTarget as Node)) setIsHotMaterialDragging(false); }}
|
||||||
|
onDrop={(event) => { event.preventDefault(); event.stopPropagation(); setIsHotMaterialDragging(false); const files = Array.from(event.dataTransfer.files); if (files.length) addProductImages(files); }}
|
||||||
|
>
|
||||||
|
<FileImageOutlined />
|
||||||
|
<span>拖拽或点击上传</span>
|
||||||
|
<em>上传商品素材图,最多 {maxCloneProductImages} 张</em>
|
||||||
|
<b>+ 上传图片</b>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<input
|
||||||
|
ref={hotMaterialInputRef}
|
||||||
|
type="file"
|
||||||
|
accept="image/*"
|
||||||
|
multiple
|
||||||
|
className="ecom-command-hidden-file"
|
||||||
|
onChange={handleProductUpload}
|
||||||
|
aria-label="上传爆款复刻素材"
|
||||||
|
/>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<strong><FireOutlined /> 上传参考图片</strong>
|
||||||
|
<div
|
||||||
|
role="button"
|
||||||
|
tabIndex={0}
|
||||||
|
className={`ecom-quick-set-upload ecom-quick-hot-material ecom-quick-hot-reference${cloneReferenceImages.length ? " has-images" : ""}${isCloneReferenceDragging ? " is-dragging" : ""}`}
|
||||||
|
onClick={() => cloneReferenceInputRef.current?.click()}
|
||||||
|
onKeyDown={(event) => openQuickUploadWithKeyboard(event, cloneReferenceInputRef)}
|
||||||
|
onDragOver={(event) => { event.preventDefault(); event.stopPropagation(); if (event.dataTransfer.types.includes("Files")) setIsCloneReferenceDragging(true); }}
|
||||||
|
onDragLeave={(event) => { event.preventDefault(); event.stopPropagation(); if (event.currentTarget === event.target || !event.currentTarget.contains(event.relatedTarget as Node)) setIsCloneReferenceDragging(false); }}
|
||||||
|
onDrop={(event) => { event.preventDefault(); event.stopPropagation(); setIsCloneReferenceDragging(false); const files = Array.from(event.dataTransfer.files); if (files.length) addCloneReferenceImages(files); }}
|
||||||
|
>
|
||||||
|
<FileImageOutlined />
|
||||||
|
<span>拖拽或点击上传</span>
|
||||||
|
<em>参考图用于风格迁移,最多 {maxCloneReferenceImages} 张</em>
|
||||||
|
<b>+ 上传图片</b>
|
||||||
|
{cloneReferenceImages.length ? (
|
||||||
|
<>
|
||||||
|
{renderHotMaterialThumbs(cloneReferenceImages, removeCloneReferenceImage)}
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="ecom-quick-hot-add-btn"
|
||||||
|
aria-label="添加更多参考图"
|
||||||
|
onClick={(event) => {
|
||||||
|
event.stopPropagation();
|
||||||
|
cloneReferenceInputRef.current?.click();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<PlusOutlined />
|
||||||
|
</button>
|
||||||
|
</>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
<input
|
||||||
|
ref={cloneReferenceInputRef}
|
||||||
|
type="file"
|
||||||
|
accept="image/*"
|
||||||
|
multiple
|
||||||
|
className="ecom-command-hidden-file"
|
||||||
|
onChange={handleCloneReferenceUpload}
|
||||||
|
aria-label="上传爆款复刻参考图"
|
||||||
|
/>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<strong>复刻强度</strong>
|
||||||
|
<div className="ecom-quick-detail-modules">
|
||||||
|
{cloneReplicateLevelOptions.map((option) => (
|
||||||
|
<button
|
||||||
|
key={option.key}
|
||||||
|
type="button"
|
||||||
|
className={cloneReplicateLevel === option.key ? "is-active" : ""}
|
||||||
|
onClick={() => setCloneReplicateLevel(option.key)}
|
||||||
|
>
|
||||||
|
<strong>{option.title}</strong>
|
||||||
|
<span>{option.desc}</span>
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<section className="ecom-quick-set-basic-section">
|
||||||
|
<span className="ecom-quick-set-label">基础设置</span>
|
||||||
|
<div className="ecom-quick-set-select-anchor">
|
||||||
|
<div className="ecom-quick-set-selects">
|
||||||
|
{quickHotBasicSelects.map((item) => (
|
||||||
|
<button
|
||||||
|
key={item.key}
|
||||||
|
type="button"
|
||||||
|
className={openQuickSetSelect === item.key ? "is-active" : ""}
|
||||||
|
onClick={() => toggleQuickSetSelect(item.key)}
|
||||||
|
>
|
||||||
|
<span>{item.label}</span><strong>{formatRatioDisplayValue(item.value)}</strong><em>⌄</em>
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
{quickHotVisibleSelect ? (
|
||||||
|
<div
|
||||||
|
className={`ecom-quick-set-dropdown ecom-quick-set-dropdown--${quickHotVisibleSelect.key}${isQuickSetSelectClosing ? " is-closing" : ""}`}
|
||||||
|
role="listbox"
|
||||||
|
aria-label={quickHotVisibleSelect.label}
|
||||||
|
>
|
||||||
|
{quickHotVisibleSelect.options.map((option) => (
|
||||||
|
<button
|
||||||
|
key={option}
|
||||||
|
type="button"
|
||||||
|
className={quickHotVisibleSelect.value === option ? "is-active" : ""}
|
||||||
|
onClick={() => {
|
||||||
|
quickHotVisibleSelect.onChange(option);
|
||||||
|
closeQuickSetSelect();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{formatRatioDisplayValue(option)}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<section className="ecom-quick-hot-requirement">
|
||||||
|
<div className="ecom-quick-hot-requirement__head">
|
||||||
|
<strong>商品卖点 & 需求</strong>
|
||||||
|
<button type="button" className="ecom-quick-hot-requirement__ai" onClick={handleHotAiWrite}>AI 帮写</button>
|
||||||
|
</div>
|
||||||
|
<div className="ecom-quick-hot-requirement__input">
|
||||||
|
<textarea
|
||||||
|
value={hotRequirement}
|
||||||
|
onChange={(event) => setHotRequirement(event.target.value.slice(0, 500))}
|
||||||
|
placeholder="建议包含以下信息:产品名称、核心卖点、参考风格、期望场景、具体参数"
|
||||||
|
maxLength={500}
|
||||||
|
/>
|
||||||
|
<span>{hotRequirement.length}/500</span>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<div className="ecom-quick-hot-actions">
|
||||||
|
<button type="button" className="ecom-quick-set-primary ecom-quick-hot-generate" onClick={handleHotGenerate} disabled={!canGenerateHot}>
|
||||||
|
{hotStatus === "generating" ? <LoadingOutlined /> : "✦"} 开始复刻
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className={`ecom-quick-set-primary ecom-quick-set-primary--cancel${hotStatus !== "generating" ? " is-disabled" : ""}`}
|
||||||
|
onClick={hotStatus === "generating" ? handleCancelGenerate : undefined}
|
||||||
|
disabled={hotStatus !== "generating"}
|
||||||
|
>
|
||||||
|
取消复刻
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</aside>
|
||||||
|
{hotMaterialHoverZoom && typeof document !== "undefined"
|
||||||
|
? createPortal(
|
||||||
|
<div
|
||||||
|
className={`ecom-hot-material-zoom-portal is-${hotMaterialHoverZoom.placement}`}
|
||||||
|
style={{ left: hotMaterialHoverZoom.x, top: hotMaterialHoverZoom.y }}
|
||||||
|
>
|
||||||
|
<img src={hotMaterialHoverZoom.src} alt="" />
|
||||||
|
</div>,
|
||||||
|
document.body,
|
||||||
|
)
|
||||||
|
: null}
|
||||||
|
<section className="ecom-quick-set-stage">
|
||||||
|
<header className="ecom-quick-set-preview-head">
|
||||||
|
<h1>预览</h1>
|
||||||
|
<p>上传参考图,AI 按选定风格强度 <span>复刻同款视觉表现</span>,快速产出高转化素材。</p>
|
||||||
|
<div>
|
||||||
|
<button type="button" onClick={() => setPreviewZoom((value) => Math.max(0.25, value - 0.1))}>-</button>
|
||||||
|
<strong>{Math.round(previewZoom * 100)}%</strong>
|
||||||
|
<button type="button" onClick={() => setPreviewZoom((value) => Math.min(2, value + 0.1))}>+</button>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
<div className="ecom-quick-set-canvas" onWheel={handleQuickPreviewWheel}>
|
||||||
|
{hotStatus === "done" && hotResultUrl ? (
|
||||||
|
<section className="ecom-quick-detail-result" style={{ transform: `scale(${previewZoom})` }}>
|
||||||
|
<img src={hotResultUrl} alt="爆款复刻结果" />
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="ecom-quick-detail-download"
|
||||||
|
onClick={() => {
|
||||||
|
const link = document.createElement("a");
|
||||||
|
link.href = hotResultUrl;
|
||||||
|
link.download = `爆款复刻-${Date.now()}.png`;
|
||||||
|
document.body.appendChild(link);
|
||||||
|
link.click();
|
||||||
|
link.remove();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<CloudUploadOutlined />
|
||||||
|
保存本地
|
||||||
|
</button>
|
||||||
|
</section>
|
||||||
|
) : hotStatus === "generating" ? (
|
||||||
|
<section className="ecom-quick-set-generating">
|
||||||
|
<LoadingOutlined />
|
||||||
|
<strong>正在生成爆款复刻</strong>
|
||||||
|
<span>AI 正在根据参考图和复刻强度生成同款素材,请稍候...</span>
|
||||||
|
<div className="ecom-quick-set-progress">
|
||||||
|
<div className="ecom-quick-set-progress-bar" style={{ width: `${Math.round(hotProgress)}%` }} />
|
||||||
|
</div>
|
||||||
|
<em className="ecom-quick-set-progress-text">{Math.round(hotProgress)}%</em>
|
||||||
|
</section>
|
||||||
|
) : hotStatus === "failed" ? (
|
||||||
|
<section className="ecom-quick-set-failed">
|
||||||
|
<FrownOutlined />
|
||||||
|
<strong>生成失败</strong>
|
||||||
|
<span>请检查网络或重试,如余额不足请先充值。</span>
|
||||||
|
<button type="button" onClick={handleHotGenerate} disabled={!canGenerateHot}>重新生成</button>
|
||||||
|
</section>
|
||||||
|
) : (
|
||||||
|
<section className="ecom-quick-set-empty">
|
||||||
|
<FileImageOutlined />
|
||||||
|
<strong>等待生成</strong>
|
||||||
|
<span>上传参考图片并选择复刻强度后,AI 将在这里展示生成结果。</span>
|
||||||
|
</section>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
<button type="button" className="ecom-quick-set-help" aria-label="帮助" onClick={() => toast.info("上传参考图后,选择复刻强度和平台即可生成爆款同款。")}>?</button>
|
||||||
|
</main>
|
||||||
|
);
|
||||||
|
|
||||||
const detailPreview = (
|
const detailPreview = (
|
||||||
<main className="product-clone-preview product-clone-preview--detail" aria-label="A+详情预览" onWheel={handlePreviewWheel}>
|
<main className="product-clone-preview product-clone-preview--detail" aria-label="A+详情预览" onWheel={handlePreviewWheel}>
|
||||||
<div className="product-clone-preview__headline">
|
<div className="product-clone-preview__headline">
|
||||||
@@ -6836,6 +7405,12 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
|
|||||||
{quickDetailPreview}
|
{quickDetailPreview}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
: isHotCloneTool
|
||||||
|
? (
|
||||||
|
<div key={`quick-${activeQuickTool}`} className="ecom-quick-page-wrap ecom-tool-page-enter">
|
||||||
|
{hotClonePreview}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
: clonePreview
|
: clonePreview
|
||||||
: placeholderPreview;
|
: placeholderPreview;
|
||||||
const isMainCloneWorkspace = isCloneTool && !isSmartCutoutTool && !isQuickDetailTool && !isWatermarkTool && !isTranslateTool && !isImageEditTool;
|
const isMainCloneWorkspace = isCloneTool && !isSmartCutoutTool && !isQuickDetailTool && !isWatermarkTool && !isTranslateTool && !isImageEditTool;
|
||||||
@@ -6865,7 +7440,7 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<section
|
<section
|
||||||
className={`product-clone-page page-motion${isCloneTool && isCloneSettingsCollapsed ? " is-settings-collapsed" : ""}${isCloneTool && isCommandHistoryCollapsed ? " is-history-collapsed" : ""}${isRecordDetailWorkspace && isCloneConversationCollapsed ? " is-conversation-collapsed" : ""}${isRecordDetailWorkspace ? " is-history-detail" : ""}${isSmartCutoutTool ? " is-smart-cutout-page" : ""}${isQuickDetailTool ? " is-quick-set-page" : ""}${isWatermarkTool ? " is-watermark-page" : ""}${isTranslateTool ? " is-translate-page" : ""}${isImageEditTool ? " is-image-workbench-page" : ""}`}
|
className={`product-clone-page page-motion${isCloneTool && isCloneSettingsCollapsed ? " is-settings-collapsed" : ""}${isCloneTool && isCommandHistoryCollapsed ? " is-history-collapsed" : ""}${isRecordDetailWorkspace && isCloneConversationCollapsed ? " is-conversation-collapsed" : ""}${isRecordDetailWorkspace ? " is-history-detail" : ""}${isSmartCutoutTool ? " is-smart-cutout-page" : ""}${isQuickDetailTool ? " is-quick-set-page" : ""}${isWatermarkTool ? " is-watermark-page" : ""}${isTranslateTool ? " is-translate-page" : ""}${isImageEditTool ? " is-image-workbench-page" : ""}${isHotCloneTool ? " is-hot-clone-page" : ""}`}
|
||||||
data-tool={activeTool}
|
data-tool={activeTool}
|
||||||
aria-label={pageLabel}
|
aria-label={pageLabel}
|
||||||
>
|
>
|
||||||
@@ -6889,7 +7464,7 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
|
|||||||
{isSetTool ? setPanel : isDetail ? detailPanel : isTryOn ? tryOnPanel : isCloneTool ? clonePanel : placeholderPanel}
|
{isSetTool ? setPanel : isDetail ? detailPanel : isTryOn ? tryOnPanel : isCloneTool ? clonePanel : placeholderPanel}
|
||||||
</aside>
|
</aside>
|
||||||
|
|
||||||
{isCloneTool && !isSmartCutoutTool && !isQuickDetailTool && !isWatermarkTool && !isTranslateTool && !isImageEditTool ? (
|
{isCloneTool && !isSmartCutoutTool && !isQuickDetailTool && !isWatermarkTool && !isTranslateTool && !isImageEditTool && !isHotCloneTool ? (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="clone-ai-settings-toggle"
|
className="clone-ai-settings-toggle"
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from "./ecommerceGenerationPersistence.ts";
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from "./ecommerceImageValidation.ts";
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from "./ecommerceTemplates.ts";
|
||||||
@@ -381,108 +381,6 @@ export default function EcommerceClonePanel({
|
|||||||
</section>
|
</section>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
{cloneOutput === "hot" ? (
|
|
||||||
<section className="clone-ai-replicate-panel" aria-label="爆款图复刻设置">
|
|
||||||
<div className="clone-ai-dynamic-head">
|
|
||||||
<strong>爆款图参考设置</strong>
|
|
||||||
<span>随生成模式切换</span>
|
|
||||||
</div>
|
|
||||||
<div className="clone-ai-replicate-section">
|
|
||||||
<span className="clone-ai-replicate-title">参考内容</span>
|
|
||||||
<div className="clone-ai-replicate-tabs" role="tablist" aria-label="参考内容来源">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className={cloneReferenceMode === "upload" ? "is-active" : ""}
|
|
||||||
aria-selected={cloneReferenceMode === "upload"}
|
|
||||||
onClick={() => setCloneReferenceMode("upload")}
|
|
||||||
>
|
|
||||||
上传参考图
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className={cloneReferenceMode === "link" ? "is-active" : ""}
|
|
||||||
aria-selected={cloneReferenceMode === "link"}
|
|
||||||
onClick={() => setCloneReferenceMode("link")}
|
|
||||||
>
|
|
||||||
导入链接
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
{cloneReferenceMode === "upload" ? (
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className={`clone-ai-replicate-upload${isCloneReferenceDragging ? " is-dragging" : ""}${cloneReferenceImages.length ? " has-files" : ""}`}
|
|
||||||
onClick={() => cloneReferenceInputRef.current?.click()}
|
|
||||||
onDragOver={handleCloneReferenceDragOver}
|
|
||||||
onDragLeave={handleCloneReferenceDragLeave}
|
|
||||||
onDrop={handleCloneReferenceDrop}
|
|
||||||
>
|
|
||||||
{cloneReferenceImages.length ? (
|
|
||||||
<>
|
|
||||||
<div className="clone-ai-replicate-files">
|
|
||||||
{cloneReferenceImages.map((item) => (
|
|
||||||
<figure
|
|
||||||
key={item.id}
|
|
||||||
className="clone-ai-replicate-file"
|
|
||||||
onMouseEnter={(e) => handleFileMouseEnter(item.src, e)}
|
|
||||||
onMouseLeave={handleFileMouseLeave}
|
|
||||||
>
|
|
||||||
<img src={item.src} alt="" />
|
|
||||||
</figure>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
<span className="clone-ai-replicate-add-more">
|
|
||||||
<CloudUploadOutlined />
|
|
||||||
点击继续上传文件
|
|
||||||
</span>
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<span>
|
|
||||||
<CloudUploadOutlined />
|
|
||||||
<span className="clone-ai-replicate-upload-text">拖拽或点击上传参考图</span>
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
<em>{cloneReferenceImages.length ? `已选 ${cloneReferenceImages.length}/${maxCloneReferenceImages}` : `最多 ${maxCloneReferenceImages} 张`}</em>
|
|
||||||
{isCloneReferenceDragging ? (
|
|
||||||
<div className="clone-ai-replicate-upload-overlay">
|
|
||||||
<CloudUploadOutlined />
|
|
||||||
<span>释放文件以上传</span>
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
</button>
|
|
||||||
) : (
|
|
||||||
<label className="clone-ai-replicate-link">
|
|
||||||
<input placeholder="粘贴商品图或详情页链接" />
|
|
||||||
</label>
|
|
||||||
)}
|
|
||||||
<input
|
|
||||||
ref={cloneReferenceInputRef}
|
|
||||||
type="file"
|
|
||||||
accept="image/jpeg,image/png,image/webp"
|
|
||||||
multiple
|
|
||||||
onChange={handleCloneReferenceUpload}
|
|
||||||
aria-label="上传参考图片"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="clone-ai-replicate-section">
|
|
||||||
<span className="clone-ai-replicate-title">复刻程度</span>
|
|
||||||
<div className="clone-ai-replicate-levels" role="toolbar" aria-label="复刻程度">
|
|
||||||
{cloneReplicateLevelOptions.map((option) => (
|
|
||||||
<button
|
|
||||||
key={option.key}
|
|
||||||
type="button"
|
|
||||||
className={cloneReplicateLevel === option.key ? "is-active" : ""}
|
|
||||||
aria-pressed={cloneReplicateLevel === option.key}
|
|
||||||
onClick={() => setCloneReplicateLevel(option.key)}
|
|
||||||
>
|
|
||||||
<strong>{option.title}</strong>
|
|
||||||
<span>{option.desc}</span>
|
|
||||||
</button>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
) : null}
|
|
||||||
|
|
||||||
{cloneOutput === "set" ? (
|
{cloneOutput === "set" ? (
|
||||||
<section className="clone-ai-count-panel" aria-label="套图图片数量">
|
<section className="clone-ai-count-panel" aria-label="套图图片数量">
|
||||||
<div className="clone-ai-dynamic-head">
|
<div className="clone-ai-dynamic-head">
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from "./workbenchDownload.ts";
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from "./useGenerationTasks.ts";
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from "./useTypewriter.ts";
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from "./backgroundTaskRunner.ts";
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from "./index.ts";
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from "./useAppStore.ts";
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from "./useGenerationStore.ts";
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from "./useProjectStore.ts";
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from "./useSessionStore.ts";
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from "./useTaskStore.ts";
|
||||||
@@ -4537,6 +4537,275 @@
|
|||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .product-clone-page[data-tool="clone"].is-hot-clone-page {
|
||||||
|
display: block !important;
|
||||||
|
height: 100% !important;
|
||||||
|
min-height: calc(100vh - 58px) !important;
|
||||||
|
overflow: hidden !important;
|
||||||
|
background: #f3f5f8 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .product-clone-page[data-tool="clone"].is-hot-clone-page .product-clone-shell {
|
||||||
|
display: block !important;
|
||||||
|
width: 100% !important;
|
||||||
|
height: 100% !important;
|
||||||
|
padding: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .product-clone-page[data-tool="clone"].is-hot-clone-page .product-clone-rail,
|
||||||
|
.ecommerce-standalone .product-clone-page[data-tool="clone"].is-hot-clone-page .product-clone-panel,
|
||||||
|
.ecommerce-standalone .product-clone-page[data-tool="clone"].is-hot-clone-page .clone-ai-settings-toggle,
|
||||||
|
.ecommerce-standalone .product-clone-page[data-tool="clone"].is-hot-clone-page .ecom-command-history {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Hot Clone: requirement input in left panel ── */
|
||||||
|
.ecommerce-standalone .ecom-quick-hot-page .ecom-quick-hot-requirement {
|
||||||
|
display: flex !important;
|
||||||
|
flex-direction: column !important;
|
||||||
|
gap: 8px !important;
|
||||||
|
margin-top: 2px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-quick-hot-page .ecom-quick-hot-requirement__head {
|
||||||
|
display: flex !important;
|
||||||
|
align-items: center !important;
|
||||||
|
justify-content: space-between !important;
|
||||||
|
gap: 8px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-quick-hot-page .ecom-quick-hot-requirement__head > strong {
|
||||||
|
font-size: 13px !important;
|
||||||
|
font-weight: 800 !important;
|
||||||
|
color: #1a2b3c !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-quick-hot-page .ecom-quick-hot-requirement__input {
|
||||||
|
position: relative !important;
|
||||||
|
min-height: 158px !important;
|
||||||
|
border: 1px dashed rgba(30, 189, 219, 0.34) !important;
|
||||||
|
border-radius: 8px !important;
|
||||||
|
background: linear-gradient(180deg, rgba(237, 248, 255, 0.72), rgba(255, 255, 255, 0.94)) !important;
|
||||||
|
overflow: hidden !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-quick-hot-page .ecom-quick-hot-requirement__input textarea {
|
||||||
|
display: block !important;
|
||||||
|
width: 100% !important;
|
||||||
|
min-height: 140px !important;
|
||||||
|
max-height: 240px !important;
|
||||||
|
resize: none !important;
|
||||||
|
border: 0 !important;
|
||||||
|
outline: none !important;
|
||||||
|
padding: 14px 14px 24px !important;
|
||||||
|
color: #172636 !important;
|
||||||
|
background: transparent !important;
|
||||||
|
font-size: 13px !important;
|
||||||
|
font-weight: 700 !important;
|
||||||
|
line-height: 1.6 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-quick-hot-page .ecom-quick-hot-requirement__input textarea::placeholder {
|
||||||
|
color: #9badb9 !important;
|
||||||
|
font-weight: 500 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-quick-hot-page .ecom-quick-hot-requirement__input > span {
|
||||||
|
position: absolute !important;
|
||||||
|
right: 12px !important;
|
||||||
|
bottom: 6px !important;
|
||||||
|
color: #9badb9 !important;
|
||||||
|
font-size: 11px !important;
|
||||||
|
font-weight: 600 !important;
|
||||||
|
pointer-events: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-quick-hot-page .ecom-quick-hot-requirement__ai {
|
||||||
|
padding: 3px 12px !important;
|
||||||
|
border: 1.5px solid rgba(16, 115, 204, 0.18) !important;
|
||||||
|
border-radius: 20px !important;
|
||||||
|
background: linear-gradient(135deg, rgba(16, 115, 204, 0.06), rgba(25, 173, 200, 0.06)) !important;
|
||||||
|
color: #1073cc !important;
|
||||||
|
font-size: 11px !important;
|
||||||
|
font-weight: 800 !important;
|
||||||
|
cursor: pointer !important;
|
||||||
|
white-space: nowrap !important;
|
||||||
|
flex-shrink: 0 !important;
|
||||||
|
transition: background 160ms ease, border-color 160ms ease !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-quick-hot-page .ecom-quick-hot-requirement__ai:hover {
|
||||||
|
background: linear-gradient(135deg, rgba(16, 115, 204, 0.12), rgba(25, 173, 200, 0.12)) !important;
|
||||||
|
border-color: rgba(16, 115, 204, 0.3) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Hot Clone: material upload with images ── */
|
||||||
|
.ecommerce-standalone .ecom-quick-hot-page .ecom-quick-hot-material.has-images {
|
||||||
|
display: flex !important;
|
||||||
|
flex-wrap: wrap !important;
|
||||||
|
align-items: flex-start !important;
|
||||||
|
justify-content: flex-start !important;
|
||||||
|
gap: 10px !important;
|
||||||
|
padding: 12px !important;
|
||||||
|
place-items: unset !important;
|
||||||
|
background: #f9fafa !important;
|
||||||
|
border: 1px solid rgba(30, 189, 219, 0.22) !important;
|
||||||
|
border-style: solid !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-quick-hot-page .ecom-quick-hot-material.has-images .ecom-quick-upload-thumbs {
|
||||||
|
display: contents !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-quick-hot-page .ecom-quick-hot-material.has-images .ecom-quick-upload-thumbs figure {
|
||||||
|
position: relative !important;
|
||||||
|
width: 80px !important;
|
||||||
|
height: 80px !important;
|
||||||
|
margin: 0 !important;
|
||||||
|
border: 1px solid #e8edf0 !important;
|
||||||
|
border-radius: 10px !important;
|
||||||
|
overflow: hidden !important;
|
||||||
|
background: #fff !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-quick-hot-page .ecom-quick-hot-material.has-images .ecom-quick-upload-thumbs figure > img {
|
||||||
|
display: block !important;
|
||||||
|
width: 100% !important;
|
||||||
|
height: 100% !important;
|
||||||
|
object-fit: cover !important;
|
||||||
|
border-radius: 9px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-quick-hot-page .ecom-quick-hot-material.has-images .ecom-quick-upload-thumbs figure > button {
|
||||||
|
position: absolute !important;
|
||||||
|
top: 4px !important;
|
||||||
|
right: 4px !important;
|
||||||
|
width: 20px !important;
|
||||||
|
height: 20px !important;
|
||||||
|
border: none !important;
|
||||||
|
border-radius: 50% !important;
|
||||||
|
background: rgba(0, 0, 0, 0.48) !important;
|
||||||
|
color: #fff !important;
|
||||||
|
font-size: 11px !important;
|
||||||
|
display: flex !important;
|
||||||
|
align-items: center !important;
|
||||||
|
justify-content: center !important;
|
||||||
|
cursor: pointer !important;
|
||||||
|
opacity: 0 !important;
|
||||||
|
transition: opacity 140ms ease, background 140ms ease !important;
|
||||||
|
padding: 0 !important;
|
||||||
|
z-index: 3 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-quick-hot-page .ecom-quick-hot-material.has-images .ecom-quick-upload-thumbs figure:hover > button {
|
||||||
|
opacity: 1 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-quick-hot-page .ecom-quick-hot-material.has-images .ecom-quick-upload-thumbs figure > button:hover {
|
||||||
|
background: rgba(220, 53, 69, 0.85) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hide old CSS zoom in material section (portal replaces it) */
|
||||||
|
.ecommerce-standalone .ecom-quick-hot-page .ecom-quick-hot-material.has-images .ecom-command-asset-zoom {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-quick-hot-page .ecom-quick-hot-add-btn {
|
||||||
|
display: inline-flex !important;
|
||||||
|
align-items: center !important;
|
||||||
|
justify-content: center !important;
|
||||||
|
width: 80px !important;
|
||||||
|
height: 80px !important;
|
||||||
|
border: 1px solid #e8edf0 !important;
|
||||||
|
border-radius: 10px !important;
|
||||||
|
color: #3a4555 !important;
|
||||||
|
background: #f5f5f5 !important;
|
||||||
|
font-size: 22px !important;
|
||||||
|
cursor: pointer !important;
|
||||||
|
transition: background 160ms ease, border-color 160ms ease, color 160ms ease !important;
|
||||||
|
flex-shrink: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-quick-hot-page .ecom-quick-hot-add-btn:hover {
|
||||||
|
color: #ffffff !important;
|
||||||
|
background: linear-gradient(135deg, #1073cc, #1ebddb) !important;
|
||||||
|
border-color: #1073cc !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Hot Clone: sticky bottom action buttons ── */
|
||||||
|
.ecommerce-standalone .ecom-quick-hot-page .ecom-quick-hot-actions {
|
||||||
|
position: sticky !important;
|
||||||
|
bottom: 0 !important;
|
||||||
|
z-index: 5 !important;
|
||||||
|
display: flex !important;
|
||||||
|
flex-direction: column !important;
|
||||||
|
gap: 8px !important;
|
||||||
|
margin: 0 -14px -86px -14px !important;
|
||||||
|
padding: 14px 14px 16px !important;
|
||||||
|
background: linear-gradient(to top, #feffff 60%, rgba(254, 255, 255, 0.92) 80%, transparent) !important;
|
||||||
|
backdrop-filter: blur(6px) !important;
|
||||||
|
flex-shrink: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-quick-hot-page .ecom-quick-hot-actions .ecom-quick-set-primary {
|
||||||
|
position: static !important;
|
||||||
|
left: auto !important;
|
||||||
|
right: auto !important;
|
||||||
|
bottom: auto !important;
|
||||||
|
width: 100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-quick-hot-page .ecom-quick-hot-actions .ecom-quick-set-primary--cancel.is-disabled {
|
||||||
|
color: #c0ccd4 !important;
|
||||||
|
background: #f0f3f5 !important;
|
||||||
|
border-color: #e4e9ec !important;
|
||||||
|
cursor: not-allowed !important;
|
||||||
|
opacity: 0.55 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .ecom-quick-hot-page .ecom-quick-hot-actions .ecom-quick-set-primary--cancel.is-disabled:hover {
|
||||||
|
color: #c0ccd4 !important;
|
||||||
|
background: #f0f3f5 !important;
|
||||||
|
border-color: #e4e9ec !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Hot Clone: stage fills space without prompt ── */
|
||||||
|
.ecommerce-standalone .ecom-quick-hot-page .ecom-quick-set-stage {
|
||||||
|
grid-template-rows: auto minmax(0, 1fr) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Hot Clone: portal zoom preview (avoids overflow clipping) ── */
|
||||||
|
.ecom-hot-material-zoom-portal {
|
||||||
|
position: fixed !important;
|
||||||
|
z-index: 2147483647 !important;
|
||||||
|
width: min(280px, calc(100vw - 24px)) !important;
|
||||||
|
max-height: 340px !important;
|
||||||
|
border: 1px solid rgba(30, 189, 219, 0.2) !important;
|
||||||
|
border-radius: 14px !important;
|
||||||
|
background: #ffffff !important;
|
||||||
|
padding: 8px !important;
|
||||||
|
box-shadow: 0 22px 48px rgba(20, 80, 100, 0.22) !important;
|
||||||
|
pointer-events: none !important;
|
||||||
|
isolation: isolate !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecom-hot-material-zoom-portal.is-above {
|
||||||
|
transform: translate(-50%, -100%) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecom-hot-material-zoom-portal.is-below {
|
||||||
|
transform: translate(-50%, 0) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecom-hot-material-zoom-portal img {
|
||||||
|
display: block !important;
|
||||||
|
width: 100% !important;
|
||||||
|
height: auto !important;
|
||||||
|
max-height: 324px !important;
|
||||||
|
border-radius: 8px !important;
|
||||||
|
object-fit: contain !important;
|
||||||
|
}
|
||||||
|
|
||||||
.ecommerce-standalone .product-clone-page[data-tool="clone"].is-watermark-page {
|
.ecommerce-standalone .product-clone-page[data-tool="clone"].is-watermark-page {
|
||||||
display: block !important;
|
display: block !important;
|
||||||
height: 100% !important;
|
height: 100% !important;
|
||||||
@@ -6783,31 +7052,36 @@
|
|||||||
background: rgba(16, 115, 204, 0.28) !important;
|
background: rgba(16, 115, 204, 0.28) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ecommerce-standalone .ecom-quick-detail-page .ecom-quick-set-panel {
|
.ecommerce-standalone .ecom-quick-detail-page .ecom-quick-set-panel,
|
||||||
|
.ecommerce-standalone .ecom-quick-hot-page .ecom-quick-set-panel {
|
||||||
overflow-y: auto !important;
|
overflow-y: auto !important;
|
||||||
padding-bottom: 16px !important;
|
padding-bottom: 16px !important;
|
||||||
scrollbar-width: auto !important;
|
scrollbar-width: auto !important;
|
||||||
scrollbar-color: rgba(16, 115, 204, 0.56) rgba(16, 115, 204, 0.08) !important;
|
scrollbar-color: rgba(16, 115, 204, 0.56) rgba(16, 115, 204, 0.08) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ecommerce-standalone .ecom-quick-detail-page .ecom-quick-set-panel::-webkit-scrollbar {
|
.ecommerce-standalone .ecom-quick-detail-page .ecom-quick-set-panel::-webkit-scrollbar,
|
||||||
|
.ecommerce-standalone .ecom-quick-hot-page .ecom-quick-set-panel::-webkit-scrollbar {
|
||||||
display: block !important;
|
display: block !important;
|
||||||
width: 14px !important;
|
width: 14px !important;
|
||||||
height: 14px !important;
|
height: 14px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ecommerce-standalone .ecom-quick-detail-page .ecom-quick-set-panel::-webkit-scrollbar-track {
|
.ecommerce-standalone .ecom-quick-detail-page .ecom-quick-set-panel::-webkit-scrollbar-track,
|
||||||
|
.ecommerce-standalone .ecom-quick-hot-page .ecom-quick-set-panel::-webkit-scrollbar-track {
|
||||||
border-radius: 999px !important;
|
border-radius: 999px !important;
|
||||||
background: rgba(16, 115, 204, 0.08) !important;
|
background: rgba(16, 115, 204, 0.08) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ecommerce-standalone .ecom-quick-detail-page .ecom-quick-set-panel::-webkit-scrollbar-thumb {
|
.ecommerce-standalone .ecom-quick-detail-page .ecom-quick-set-panel::-webkit-scrollbar-thumb,
|
||||||
|
.ecommerce-standalone .ecom-quick-hot-page .ecom-quick-set-panel::-webkit-scrollbar-thumb {
|
||||||
border: 3px solid rgba(248, 249, 250, 0.95) !important;
|
border: 3px solid rgba(248, 249, 250, 0.95) !important;
|
||||||
border-radius: 999px !important;
|
border-radius: 999px !important;
|
||||||
background: rgba(16, 115, 204, 0.56) !important;
|
background: rgba(16, 115, 204, 0.56) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ecommerce-standalone .ecom-quick-detail-page .ecom-quick-set-panel::-webkit-scrollbar-thumb:hover {
|
.ecommerce-standalone .ecom-quick-detail-page .ecom-quick-set-panel::-webkit-scrollbar-thumb:hover,
|
||||||
|
.ecommerce-standalone .ecom-quick-hot-page .ecom-quick-set-panel::-webkit-scrollbar-thumb:hover {
|
||||||
background: rgba(16, 115, 204, 0.72) !important;
|
background: rgba(16, 115, 204, 0.72) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -8766,6 +9040,56 @@
|
|||||||
z-index: 0 !important;
|
z-index: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .product-clone-page[data-tool="clone"] .ecom-command-template-strip {
|
||||||
|
display: grid !important;
|
||||||
|
grid-template-columns: repeat(4, minmax(0, 1fr)) !important;
|
||||||
|
gap: 12px !important;
|
||||||
|
width: 100% !important;
|
||||||
|
margin: 14px 0 10px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .product-clone-page[data-tool="clone"] .ecom-command-template-card {
|
||||||
|
position: relative !important;
|
||||||
|
display: block !important;
|
||||||
|
width: 100% !important;
|
||||||
|
aspect-ratio: 1.46 / 1 !important;
|
||||||
|
min-height: 92px !important;
|
||||||
|
padding: 0 !important;
|
||||||
|
border: 1px solid rgba(30, 189, 219, 0.18) !important;
|
||||||
|
border-radius: 16px !important;
|
||||||
|
background:
|
||||||
|
linear-gradient(180deg, rgba(255, 255, 255, 0.94), rgba(246, 251, 253, 0.98)),
|
||||||
|
#ffffff !important;
|
||||||
|
box-shadow:
|
||||||
|
0 16px 34px rgba(16, 115, 204, 0.08),
|
||||||
|
inset 0 1px 0 rgba(255, 255, 255, 0.94) !important;
|
||||||
|
overflow: hidden !important;
|
||||||
|
cursor: pointer !important;
|
||||||
|
transition: transform 180ms ease, border-color 180ms ease, box-shadow 180ms ease, background 180ms ease !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .product-clone-page[data-tool="clone"] .ecom-command-template-card:hover {
|
||||||
|
transform: translateY(-1px) !important;
|
||||||
|
border-color: rgba(30, 189, 219, 0.34) !important;
|
||||||
|
box-shadow:
|
||||||
|
0 20px 42px rgba(16, 115, 204, 0.11),
|
||||||
|
0 0 0 1px rgba(30, 189, 219, 0.12) inset !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .product-clone-page[data-tool="clone"] .ecom-command-template-card:focus-visible {
|
||||||
|
outline: none !important;
|
||||||
|
border-color: rgba(30, 189, 219, 0.48) !important;
|
||||||
|
box-shadow:
|
||||||
|
0 0 0 3px rgba(30, 189, 219, 0.15),
|
||||||
|
0 20px 42px rgba(16, 115, 204, 0.11) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .product-clone-page[data-tool="clone"] .ecom-command-template-card__blank {
|
||||||
|
display: block !important;
|
||||||
|
width: 100% !important;
|
||||||
|
height: 100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
.ecommerce-standalone .product-clone-page[data-tool="clone"] .clone-ai-preview {
|
.ecommerce-standalone .product-clone-page[data-tool="clone"] .clone-ai-preview {
|
||||||
background: #f8f9fa !important;
|
background: #f8f9fa !important;
|
||||||
transition: padding-top 520ms cubic-bezier(0.16, 1, 0.3, 1),
|
transition: padding-top 520ms cubic-bezier(0.16, 1, 0.3, 1),
|
||||||
@@ -8984,6 +9308,10 @@
|
|||||||
min-height: 224px !important;
|
min-height: 224px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ecommerce-standalone .product-clone-page[data-tool="clone"] .ecom-command-template-strip {
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr)) !important;
|
||||||
|
}
|
||||||
|
|
||||||
.ecommerce-standalone .product-clone-page[data-tool="clone"] .ecom-command-quick-board button:not(:nth-child(6n + 1))::before {
|
.ecommerce-standalone .product-clone-page[data-tool="clone"] .ecom-command-quick-board button:not(:nth-child(6n + 1))::before {
|
||||||
content: none !important;
|
content: none !important;
|
||||||
}
|
}
|
||||||
@@ -11773,6 +12101,15 @@ html body #root .ecommerce-standalone.ecommerce-standalone .product-clone-page[d
|
|||||||
--quick-text: #164e63;
|
--quick-text: #164e63;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
html body #root .ecommerce-standalone.ecommerce-standalone .product-clone-page[data-tool="clone"][data-tool="clone"] .ecom-command-quick-board .ecom-command-quick-card--hot {
|
||||||
|
--quick-accent: #e8590c;
|
||||||
|
--quick-bg: #fff4e6;
|
||||||
|
--quick-text: #5c2d0e;
|
||||||
|
--quick-icon: #d9480f;
|
||||||
|
--quick-border: rgba(232, 89, 12, 0.12);
|
||||||
|
--quick-shadow: rgba(232, 89, 12, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
html body #root .ecommerce-standalone.ecommerce-standalone .product-clone-page[data-tool="clone"][data-tool="clone"] .ecom-command-quick-board,
|
html body #root .ecommerce-standalone.ecommerce-standalone .product-clone-page[data-tool="clone"][data-tool="clone"] .ecom-command-quick-board,
|
||||||
html body #root .ecommerce-standalone.ecommerce-standalone .product-clone-page[data-tool="clone"][data-tool="clone"] .ecom-command-quick-board button,
|
html body #root .ecommerce-standalone.ecommerce-standalone .product-clone-page[data-tool="clone"][data-tool="clone"] .ecom-command-quick-board button,
|
||||||
html body #root .ecommerce-standalone.ecommerce-standalone .product-clone-page[data-tool="clone"][data-tool="clone"] .ecom-command-quick-board button > span,
|
html body #root .ecommerce-standalone.ecommerce-standalone .product-clone-page[data-tool="clone"][data-tool="clone"] .ecom-command-quick-board button > span,
|
||||||
@@ -13172,6 +13509,7 @@ html body #root .ecommerce-standalone.ecommerce-standalone .product-clone-page[d
|
|||||||
}
|
}
|
||||||
|
|
||||||
html body #root .ecommerce-standalone.ecommerce-standalone .product-clone-page[data-tool="clone"][data-tool="clone"] .ecom-command-quick-board .ecom-command-quick-card--detail,
|
html body #root .ecommerce-standalone.ecommerce-standalone .product-clone-page[data-tool="clone"][data-tool="clone"] .ecom-command-quick-board .ecom-command-quick-card--detail,
|
||||||
|
html body #root .ecommerce-standalone.ecommerce-standalone .product-clone-page[data-tool="clone"][data-tool="clone"] .ecom-command-quick-board .ecom-command-quick-card--hot,
|
||||||
html body #root .ecommerce-standalone.ecommerce-standalone .product-clone-page[data-tool="clone"][data-tool="clone"] .ecom-command-quick-board .ecom-command-quick-card--edit,
|
html body #root .ecommerce-standalone.ecommerce-standalone .product-clone-page[data-tool="clone"][data-tool="clone"] .ecom-command-quick-board .ecom-command-quick-card--edit,
|
||||||
html body #root .ecommerce-standalone.ecommerce-standalone .product-clone-page[data-tool="clone"][data-tool="clone"] .ecom-command-quick-board .ecom-command-quick-card--cutout,
|
html body #root .ecommerce-standalone.ecommerce-standalone .product-clone-page[data-tool="clone"][data-tool="clone"] .ecom-command-quick-board .ecom-command-quick-card--cutout,
|
||||||
html body #root .ecommerce-standalone.ecommerce-standalone .product-clone-page[data-tool="clone"][data-tool="clone"] .ecom-command-quick-board .ecom-command-quick-card--watermark {
|
html body #root .ecommerce-standalone.ecommerce-standalone .product-clone-page[data-tool="clone"][data-tool="clone"] .ecom-command-quick-board .ecom-command-quick-card--watermark {
|
||||||
@@ -13397,6 +13735,16 @@ html body #root .ecommerce-standalone.ecommerce-standalone .product-clone-page[d
|
|||||||
--quick-shadow: rgba(122, 90, 248, 0.1) !important;
|
--quick-shadow: rgba(122, 90, 248, 0.1) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
html body #root .ecommerce-standalone.ecommerce-standalone .product-clone-page[data-tool="clone"][data-tool="clone"] .ecom-command-quick-board .ecom-command-quick-card--hot {
|
||||||
|
--quick-accent: #e8590c !important;
|
||||||
|
--quick-bg: #fff4e6 !important;
|
||||||
|
--quick-text: #5c2d0e !important;
|
||||||
|
--quick-icon: #d9480f !important;
|
||||||
|
--quick-icon-bg: rgba(232, 89, 12, 0.13) !important;
|
||||||
|
--quick-border: rgba(232, 89, 12, 0.12) !important;
|
||||||
|
--quick-shadow: rgba(232, 89, 12, 0.1) !important;
|
||||||
|
}
|
||||||
|
|
||||||
html body #root .ecommerce-standalone.ecommerce-standalone .product-clone-page[data-tool="clone"][data-tool="clone"] .ecom-command-quick-board .ecom-command-quick-card--edit {
|
html body #root .ecommerce-standalone.ecommerce-standalone .product-clone-page[data-tool="clone"][data-tool="clone"] .ecom-command-quick-board .ecom-command-quick-card--edit {
|
||||||
--quick-accent: #cc6b14 !important;
|
--quick-accent: #cc6b14 !important;
|
||||||
--quick-bg: #fff2e5 !important;
|
--quick-bg: #fff2e5 !important;
|
||||||
@@ -13492,7 +13840,7 @@ html body #root .ecommerce-standalone.ecommerce-standalone .product-clone-page[d
|
|||||||
|
|
||||||
/* #/imageWorkbench quick actions: soften each action so the tones blend into the page. */
|
/* #/imageWorkbench quick actions: soften each action so the tones blend into the page. */
|
||||||
html body #root .ecommerce-standalone.ecommerce-standalone .product-clone-page[data-tool="clone"][data-tool="clone"] .ecom-command-composer-wrap.ecom-command-composer-wrap:has(.ecom-inspiration-lab) .ecom-command-quick-board {
|
html body #root .ecommerce-standalone.ecommerce-standalone .product-clone-page[data-tool="clone"][data-tool="clone"] .ecom-command-composer-wrap.ecom-command-composer-wrap:has(.ecom-inspiration-lab) .ecom-command-quick-board {
|
||||||
background: rgba(255, 255, 255, 0.3) !important;
|
background: transparent !important;
|
||||||
box-shadow: none !important;
|
box-shadow: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -13600,7 +13948,7 @@ html body #root .ecommerce-standalone.ecommerce-standalone .product-clone-page[d
|
|||||||
/* #/imageWorkbench composer redesign: mode tabs outside, settings and assets inside. */
|
/* #/imageWorkbench composer redesign: mode tabs outside, settings and assets inside. */
|
||||||
html body #root .ecommerce-standalone.ecommerce-standalone .product-clone-page[data-tool="clone"][data-tool="clone"] .ecom-command-mode-tabs {
|
html body #root .ecommerce-standalone.ecommerce-standalone .product-clone-page[data-tool="clone"][data-tool="clone"] .ecom-command-mode-tabs {
|
||||||
display: grid !important;
|
display: grid !important;
|
||||||
grid-template-columns: repeat(5, minmax(0, 1fr)) !important;
|
grid-template-columns: repeat(4, minmax(0, 1fr)) !important;
|
||||||
gap: 8px !important;
|
gap: 8px !important;
|
||||||
width: min(100%, 760px) !important;
|
width: min(100%, 760px) !important;
|
||||||
margin: 0 auto 12px !important;
|
margin: 0 auto 12px !important;
|
||||||
@@ -13887,7 +14235,7 @@ html body #root .ecommerce-standalone.ecommerce-standalone .product-clone-page[d
|
|||||||
|
|
||||||
@media (max-width: 900px) {
|
@media (max-width: 900px) {
|
||||||
html body #root .ecommerce-standalone.ecommerce-standalone .product-clone-page[data-tool="clone"][data-tool="clone"] .ecom-command-mode-tabs {
|
html body #root .ecommerce-standalone.ecommerce-standalone .product-clone-page[data-tool="clone"][data-tool="clone"] .ecom-command-mode-tabs {
|
||||||
grid-template-columns: repeat(5, minmax(94px, 1fr)) !important;
|
grid-template-columns: repeat(4, minmax(104px, 1fr)) !important;
|
||||||
width: 100% !important;
|
width: 100% !important;
|
||||||
overflow-x: auto !important;
|
overflow-x: auto !important;
|
||||||
overscroll-behavior-x: contain !important;
|
overscroll-behavior-x: contain !important;
|
||||||
@@ -13901,7 +14249,7 @@ html body #root .ecommerce-standalone.ecommerce-standalone .product-clone-page[d
|
|||||||
|
|
||||||
@media (max-width: 640px) {
|
@media (max-width: 640px) {
|
||||||
html body #root .ecommerce-standalone.ecommerce-standalone .product-clone-page[data-tool="clone"][data-tool="clone"] .ecom-command-mode-tabs {
|
html body #root .ecommerce-standalone.ecommerce-standalone .product-clone-page[data-tool="clone"][data-tool="clone"] .ecom-command-mode-tabs {
|
||||||
grid-template-columns: repeat(5, minmax(88px, 1fr)) !important;
|
grid-template-columns: repeat(4, minmax(92px, 1fr)) !important;
|
||||||
margin-bottom: 10px !important;
|
margin-bottom: 10px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -14082,6 +14430,261 @@ html body #root .ecommerce-standalone.ecommerce-standalone .product-clone-page[d
|
|||||||
font-size: 22px !important;
|
font-size: 22px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Hot clone uploaded material thumbnails: compact grid and consistent delete control. */
|
||||||
|
html body #root .ecommerce-standalone.ecommerce-standalone .product-clone-page[data-tool="clone"].is-hot-clone-page .ecom-quick-hot-page .ecom-quick-set-upload.has-images {
|
||||||
|
display: flex !important;
|
||||||
|
flex-wrap: wrap !important;
|
||||||
|
align-items: flex-start !important;
|
||||||
|
justify-content: flex-start !important;
|
||||||
|
align-content: flex-start !important;
|
||||||
|
gap: 10px !important;
|
||||||
|
width: 100% !important;
|
||||||
|
min-height: 0 !important;
|
||||||
|
height: auto !important;
|
||||||
|
padding: 10px !important;
|
||||||
|
border: 1px solid #e8edf0 !important;
|
||||||
|
border-radius: 8px !important;
|
||||||
|
background: #ffffff !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
transform: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
html body #root .ecommerce-standalone.ecommerce-standalone .product-clone-page[data-tool="clone"].is-hot-clone-page .ecom-quick-hot-page .ecom-quick-set-upload.has-images:hover,
|
||||||
|
html body #root .ecommerce-standalone.ecommerce-standalone .product-clone-page[data-tool="clone"].is-hot-clone-page .ecom-quick-hot-page .ecom-quick-set-upload.has-images:focus-visible,
|
||||||
|
html body #root .ecommerce-standalone.ecommerce-standalone .product-clone-page[data-tool="clone"].is-hot-clone-page .ecom-quick-hot-page .ecom-quick-set-upload.has-images.is-dragging {
|
||||||
|
border-color: rgba(30, 189, 219, 0.42) !important;
|
||||||
|
background: #fbfdff !important;
|
||||||
|
box-shadow: 0 10px 24px rgba(16, 115, 204, 0.08) !important;
|
||||||
|
transform: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
html body #root .ecommerce-standalone.ecommerce-standalone .product-clone-page[data-tool="clone"].is-hot-clone-page .ecom-quick-hot-page .ecom-quick-set-upload.has-images .ecom-quick-upload-thumbs {
|
||||||
|
display: contents !important;
|
||||||
|
width: auto !important;
|
||||||
|
max-width: none !important;
|
||||||
|
padding: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
html body #root .ecommerce-standalone.ecommerce-standalone .product-clone-page[data-tool="clone"].is-hot-clone-page .ecom-quick-hot-page .ecom-quick-set-upload.has-images .ecom-command-asset-thumb.ecom-quick-upload-thumb {
|
||||||
|
position: relative !important;
|
||||||
|
flex: 0 0 64px !important;
|
||||||
|
width: 64px !important;
|
||||||
|
height: 64px !important;
|
||||||
|
min-width: 64px !important;
|
||||||
|
min-height: 64px !important;
|
||||||
|
margin: 0 !important;
|
||||||
|
overflow: hidden !important;
|
||||||
|
border: 1px solid #e8edf0 !important;
|
||||||
|
border-radius: 8px !important;
|
||||||
|
background: #f6f8fa !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
html body #root .ecommerce-standalone.ecommerce-standalone .product-clone-page[data-tool="clone"].is-hot-clone-page .ecom-quick-hot-page .ecom-quick-set-upload.has-images .ecom-command-asset-thumb.ecom-quick-upload-thumb > img {
|
||||||
|
display: block !important;
|
||||||
|
width: 100% !important;
|
||||||
|
height: 100% !important;
|
||||||
|
border-radius: 7px !important;
|
||||||
|
object-fit: cover !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
html body #root .ecommerce-standalone.ecommerce-standalone .product-clone-page[data-tool="clone"].is-hot-clone-page .ecom-quick-hot-page .ecom-quick-set-upload.has-images .ecom-command-asset-thumb.ecom-quick-upload-thumb > button {
|
||||||
|
position: absolute !important;
|
||||||
|
top: 4px !important;
|
||||||
|
right: 4px !important;
|
||||||
|
z-index: 6 !important;
|
||||||
|
display: inline-flex !important;
|
||||||
|
align-items: center !important;
|
||||||
|
justify-content: center !important;
|
||||||
|
width: 22px !important;
|
||||||
|
height: 22px !important;
|
||||||
|
min-width: 22px !important;
|
||||||
|
min-height: 22px !important;
|
||||||
|
padding: 0 !important;
|
||||||
|
border: 1px solid rgba(239, 68, 68, 0.42) !important;
|
||||||
|
border-radius: 999px !important;
|
||||||
|
color: #ef4444 !important;
|
||||||
|
background: rgba(255, 255, 255, 0.92) !important;
|
||||||
|
box-shadow: 0 8px 18px rgba(239, 68, 68, 0.16) !important;
|
||||||
|
cursor: pointer !important;
|
||||||
|
opacity: 0 !important;
|
||||||
|
pointer-events: none !important;
|
||||||
|
transform: scale(0.92) !important;
|
||||||
|
visibility: hidden !important;
|
||||||
|
transition:
|
||||||
|
opacity 150ms ease,
|
||||||
|
transform 150ms ease,
|
||||||
|
background 150ms ease,
|
||||||
|
box-shadow 150ms ease,
|
||||||
|
visibility 150ms ease !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
html body #root .ecommerce-standalone.ecommerce-standalone .product-clone-page[data-tool="clone"].is-hot-clone-page .ecom-quick-hot-page .ecom-quick-set-upload.has-images .ecom-command-asset-thumb.ecom-quick-upload-thumb:hover > button,
|
||||||
|
html body #root .ecommerce-standalone.ecommerce-standalone .product-clone-page[data-tool="clone"].is-hot-clone-page .ecom-quick-hot-page .ecom-quick-set-upload.has-images .ecom-command-asset-thumb.ecom-quick-upload-thumb:focus-within > button {
|
||||||
|
opacity: 1 !important;
|
||||||
|
pointer-events: auto !important;
|
||||||
|
transform: scale(1) !important;
|
||||||
|
visibility: visible !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
html body #root .ecommerce-standalone.ecommerce-standalone .product-clone-page[data-tool="clone"].is-hot-clone-page .ecom-quick-hot-page .ecom-quick-set-upload.has-images .ecom-command-asset-thumb.ecom-quick-upload-thumb > button:hover {
|
||||||
|
border-color: rgba(220, 38, 38, 0.72) !important;
|
||||||
|
color: #dc2626 !important;
|
||||||
|
background: #fff1f2 !important;
|
||||||
|
box-shadow: 0 10px 22px rgba(220, 38, 38, 0.22) !important;
|
||||||
|
transform: scale(1.04) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
html body #root .ecommerce-standalone.ecommerce-standalone .product-clone-page[data-tool="clone"].is-hot-clone-page .ecom-quick-hot-page .ecom-quick-set-upload.has-images .ecom-command-asset-thumb.ecom-quick-upload-thumb > button .anticon {
|
||||||
|
display: inline-flex !important;
|
||||||
|
font-size: 13px !important;
|
||||||
|
line-height: 1 !important;
|
||||||
|
color: currentColor !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
html body #root .ecommerce-standalone.ecommerce-standalone .product-clone-page[data-tool="clone"].is-hot-clone-page .ecom-quick-hot-page .ecom-quick-set-upload.has-images .ecom-quick-hot-add-btn {
|
||||||
|
flex: 0 0 64px !important;
|
||||||
|
width: 64px !important;
|
||||||
|
height: 64px !important;
|
||||||
|
min-width: 64px !important;
|
||||||
|
min-height: 64px !important;
|
||||||
|
border: 1px solid #e8edf0 !important;
|
||||||
|
border-radius: 8px !important;
|
||||||
|
color: #111827 !important;
|
||||||
|
background: #f3f4f6 !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
font-size: 22px !important;
|
||||||
|
transform: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Keep hot material upload controls visible after files are added. */
|
||||||
|
html body #root .ecommerce-standalone.ecommerce-standalone .product-clone-page[data-tool="clone"].is-hot-clone-page .ecom-quick-hot-page .ecom-quick-hot-material.has-images {
|
||||||
|
display: flex !important;
|
||||||
|
flex-wrap: wrap !important;
|
||||||
|
align-items: flex-start !important;
|
||||||
|
justify-content: flex-start !important;
|
||||||
|
gap: 10px !important;
|
||||||
|
min-height: 0 !important;
|
||||||
|
height: auto !important;
|
||||||
|
padding: 10px !important;
|
||||||
|
overflow: visible !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
html body #root .ecommerce-standalone.ecommerce-standalone .product-clone-page[data-tool="clone"].is-hot-clone-page .ecom-quick-hot-page .ecom-quick-hot-material.has-images .ecom-quick-upload-thumbs {
|
||||||
|
display: contents !important;
|
||||||
|
width: auto !important;
|
||||||
|
max-width: none !important;
|
||||||
|
padding: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
html body #root .ecommerce-standalone.ecommerce-standalone .product-clone-page[data-tool="clone"].is-hot-clone-page .ecom-quick-hot-page .ecom-quick-hot-material.has-images .ecom-command-asset-thumb.ecom-quick-upload-thumb {
|
||||||
|
flex: 0 0 64px !important;
|
||||||
|
width: 64px !important;
|
||||||
|
height: 64px !important;
|
||||||
|
min-width: 64px !important;
|
||||||
|
min-height: 64px !important;
|
||||||
|
overflow: visible !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
html body #root .ecommerce-standalone.ecommerce-standalone .product-clone-page[data-tool="clone"].is-hot-clone-page .ecom-quick-hot-page .ecom-quick-hot-material.has-images .ecom-command-asset-thumb.ecom-quick-upload-thumb > img {
|
||||||
|
overflow: hidden !important;
|
||||||
|
border-radius: 8px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
html body #root .ecommerce-standalone.ecommerce-standalone .product-clone-page[data-tool="clone"].is-hot-clone-page .ecom-quick-hot-page .ecom-quick-hot-material.has-images .ecom-command-asset-thumb.ecom-quick-upload-thumb > button {
|
||||||
|
top: -7px !important;
|
||||||
|
right: -7px !important;
|
||||||
|
width: 22px !important;
|
||||||
|
height: 22px !important;
|
||||||
|
min-width: 22px !important;
|
||||||
|
min-height: 22px !important;
|
||||||
|
opacity: 1 !important;
|
||||||
|
visibility: visible !important;
|
||||||
|
pointer-events: auto !important;
|
||||||
|
transform: none !important;
|
||||||
|
color: #ef4444 !important;
|
||||||
|
background: #ffffff !important;
|
||||||
|
border: 1px solid rgba(239, 68, 68, 0.5) !important;
|
||||||
|
box-shadow: 0 8px 18px rgba(239, 68, 68, 0.16) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
html body #root .ecommerce-standalone.ecommerce-standalone .product-clone-page[data-tool="clone"].is-hot-clone-page .ecom-quick-hot-page .ecom-quick-hot-material.has-images .ecom-command-asset-thumb.ecom-quick-upload-thumb > button .anticon,
|
||||||
|
html body #root .ecommerce-standalone.ecommerce-standalone .product-clone-page[data-tool="clone"].is-hot-clone-page .ecom-quick-hot-page .ecom-quick-hot-material.has-images .ecom-command-asset-thumb.ecom-quick-upload-thumb > button svg {
|
||||||
|
display: block !important;
|
||||||
|
width: 12px !important;
|
||||||
|
height: 12px !important;
|
||||||
|
color: currentColor !important;
|
||||||
|
fill: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
html body #root .ecommerce-standalone.ecommerce-standalone .product-clone-page[data-tool="clone"].is-hot-clone-page .ecom-quick-hot-page .ecom-quick-hot-material.has-images .ecom-quick-hot-add-btn {
|
||||||
|
display: inline-flex !important;
|
||||||
|
flex: 0 0 64px !important;
|
||||||
|
width: 64px !important;
|
||||||
|
height: 64px !important;
|
||||||
|
min-width: 64px !important;
|
||||||
|
min-height: 64px !important;
|
||||||
|
margin: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
html body #root .ecommerce-standalone.ecommerce-standalone .product-clone-page[data-tool="clone"].is-hot-clone-page .ecom-quick-hot-page .ecom-quick-hot-material.has-images .ecom-command-asset-thumb.ecom-quick-upload-thumb > button.ecom-hot-material-delete {
|
||||||
|
position: absolute !important;
|
||||||
|
top: -8px !important;
|
||||||
|
right: -8px !important;
|
||||||
|
z-index: 20 !important;
|
||||||
|
display: inline-flex !important;
|
||||||
|
align-items: center !important;
|
||||||
|
justify-content: center !important;
|
||||||
|
width: 24px !important;
|
||||||
|
height: 24px !important;
|
||||||
|
min-width: 24px !important;
|
||||||
|
min-height: 24px !important;
|
||||||
|
padding: 0 !important;
|
||||||
|
overflow: visible !important;
|
||||||
|
border: 1px solid rgba(239, 68, 68, 0.62) !important;
|
||||||
|
border-radius: 999px !important;
|
||||||
|
color: #ef4444 !important;
|
||||||
|
background: #ffffff !important;
|
||||||
|
box-shadow: 0 8px 18px rgba(239, 68, 68, 0.16) !important;
|
||||||
|
cursor: pointer !important;
|
||||||
|
opacity: 1 !important;
|
||||||
|
pointer-events: auto !important;
|
||||||
|
transform: none !important;
|
||||||
|
visibility: visible !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
html body #root .ecommerce-standalone.ecommerce-standalone .product-clone-page[data-tool="clone"].is-hot-clone-page .ecom-quick-hot-page .ecom-quick-hot-material.has-images .ecom-command-asset-thumb.ecom-quick-upload-thumb > button.ecom-hot-material-delete:hover {
|
||||||
|
border-color: #dc2626 !important;
|
||||||
|
color: #dc2626 !important;
|
||||||
|
background: #fff1f2 !important;
|
||||||
|
box-shadow: 0 10px 22px rgba(220, 38, 38, 0.24) !important;
|
||||||
|
transform: scale(1.04) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
html body #root .ecommerce-standalone.ecommerce-standalone .product-clone-page[data-tool="clone"].is-hot-clone-page .ecom-quick-hot-page .ecom-quick-hot-material.has-images .ecom-command-asset-thumb.ecom-quick-upload-thumb > button.ecom-hot-material-delete svg {
|
||||||
|
display: block !important;
|
||||||
|
width: 14px !important;
|
||||||
|
height: 14px !important;
|
||||||
|
stroke: currentColor !important;
|
||||||
|
stroke-width: 1.9 !important;
|
||||||
|
stroke-linecap: round !important;
|
||||||
|
stroke-linejoin: round !important;
|
||||||
|
fill: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
html body #root .ecommerce-standalone.ecommerce-standalone .product-clone-page[data-tool="clone"].is-hot-clone-page .ecom-quick-hot-page .ecom-quick-hot-material:not(.has-images) {
|
||||||
|
min-height: 94px !important;
|
||||||
|
padding: 12px 14px !important;
|
||||||
|
gap: 6px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
html body #root .ecommerce-standalone.ecommerce-standalone .product-clone-page[data-tool="clone"].is-hot-clone-page .ecom-quick-hot-page .ecom-quick-hot-reference.has-images > .anticon,
|
||||||
|
html body #root .ecommerce-standalone.ecommerce-standalone .product-clone-page[data-tool="clone"].is-hot-clone-page .ecom-quick-hot-page .ecom-quick-hot-reference.has-images > span,
|
||||||
|
html body #root .ecommerce-standalone.ecommerce-standalone .product-clone-page[data-tool="clone"].is-hot-clone-page .ecom-quick-hot-page .ecom-quick-hot-reference.has-images > em,
|
||||||
|
html body #root .ecommerce-standalone.ecommerce-standalone .product-clone-page[data-tool="clone"].is-hot-clone-page .ecom-quick-hot-page .ecom-quick-hot-reference.has-images > b {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
/* Generation record detail workspace: left chat, center canvas, right history drawer. */
|
/* Generation record detail workspace: left chat, center canvas, right history drawer. */
|
||||||
html body #root .ecommerce-standalone.ecommerce-standalone .product-clone-page[data-tool="clone"][data-tool="clone"].is-history-detail {
|
html body #root .ecommerce-standalone.ecommerce-standalone .product-clone-page[data-tool="clone"][data-tool="clone"].is-history-detail {
|
||||||
--clone-chat-width: 352px;
|
--clone-chat-width: 352px;
|
||||||
@@ -16649,7 +17252,7 @@ html body #root .ecommerce-standalone.ecommerce-standalone .product-clone-page[d
|
|||||||
|
|
||||||
html body #root .ecommerce-standalone.ecommerce-standalone .product-clone-page[data-tool="clone"][data-tool="clone"].is-history-detail .clone-ai-node-drag-handle {
|
html body #root .ecommerce-standalone.ecommerce-standalone .product-clone-page[data-tool="clone"][data-tool="clone"].is-history-detail .clone-ai-node-drag-handle {
|
||||||
height: 38px !important;
|
height: 38px !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Record detail chat polish: wider drawer and clearer turn-taking. */
|
/* Record detail chat polish: wider drawer and clearer turn-taking. */
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from "./types.ts";
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from "./enterpriseVideoPolicy.ts";
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from "./happyHorseRouting.ts";
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from "./pixverseRouting.ts";
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from "./resolveVideoModel.ts";
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from "./taskLifecycle.ts";
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from "./translateTaskError.ts";
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from "./viduRouting.ts";
|
||||||
Reference in New Issue
Block a user