This commit is contained in:
@@ -27,7 +27,6 @@ console.log(' 1. MODULE DEPENDENCY GRAPH ANALYSIS');
|
|||||||
console.log('═══════════════════════════════════════════════');
|
console.log('═══════════════════════════════════════════════');
|
||||||
|
|
||||||
const importMap = new Map(); // file -> [imports]
|
const importMap = new Map(); // file -> [imports]
|
||||||
const importedBy = new Map(); // file -> [importers]
|
|
||||||
|
|
||||||
for (const r of results) {
|
for (const r of results) {
|
||||||
const imports = [];
|
const imports = [];
|
||||||
@@ -71,10 +70,13 @@ function findCircular(file, visited = new Set(), path = []) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for (const file of importMap.keys()) {
|
||||||
|
findCircular(file);
|
||||||
|
}
|
||||||
|
|
||||||
// Check high-fanin files (imported by many)
|
// Check high-fanin files (imported by many)
|
||||||
const fanIn = new Map();
|
const fanIn = new Map();
|
||||||
for (const [file, imports] of importMap) {
|
for (const imports of importMap.values()) {
|
||||||
for (const imp of imports) {
|
for (const imp of imports) {
|
||||||
const key = imp.replace(/\[dynamic\]/, '');
|
const key = imp.replace(/\[dynamic\]/, '');
|
||||||
fanIn.set(key, (fanIn.get(key) || 0) + 1);
|
fanIn.set(key, (fanIn.get(key) || 0) + 1);
|
||||||
@@ -101,7 +103,7 @@ for (const [file, count] of sortedFanOut) {
|
|||||||
// Dynamic imports analysis (lazy loading effectiveness)
|
// Dynamic imports analysis (lazy loading effectiveness)
|
||||||
console.log('\n--- Lazy Loading (Dynamic Imports) ---');
|
console.log('\n--- Lazy Loading (Dynamic Imports) ---');
|
||||||
let dynamicImports = 0, staticImports = 0;
|
let dynamicImports = 0, staticImports = 0;
|
||||||
for (const [file, imports] of importMap) {
|
for (const imports of importMap.values()) {
|
||||||
for (const imp of imports) {
|
for (const imp of imports) {
|
||||||
if (imp.startsWith('[dynamic]')) dynamicImports++;
|
if (imp.startsWith('[dynamic]')) dynamicImports++;
|
||||||
else staticImports++;
|
else staticImports++;
|
||||||
@@ -177,6 +179,9 @@ for (const r of results) {
|
|||||||
if (noDeps > 0) {
|
if (noDeps > 0) {
|
||||||
console.log(` [RENDER-COST] ${r.file}: ${noDeps} useEffect(s) run EVERY render`);
|
console.log(` [RENDER-COST] ${r.file}: ${noDeps} useEffect(s) run EVERY render`);
|
||||||
}
|
}
|
||||||
|
if (emptyDeps > 0) {
|
||||||
|
console.log(` [MOUNT-EFFECT] ${r.file}: ${emptyDeps} useEffect(s) run on mount only`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── 4. Zustand Store Analysis ───
|
// ─── 4. Zustand Store Analysis ───
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
* Measures: page load, bundle sizes, memory, rendering, network.
|
* Measures: page load, bundle sizes, memory, rendering, network.
|
||||||
*/
|
*/
|
||||||
import { chromium } from 'playwright';
|
import { chromium } from 'playwright';
|
||||||
import { readFileSync, readdirSync, statSync } from 'fs';
|
import { readdirSync, statSync } from 'fs';
|
||||||
import { join } from 'path';
|
import { join } from 'path';
|
||||||
|
|
||||||
const DIST = join(import.meta.dirname, '..', 'dist');
|
const DIST = join(import.meta.dirname, '..', 'dist');
|
||||||
@@ -187,7 +187,7 @@ async function runtimeAnalysis() {
|
|||||||
const allElements = document.querySelectorAll('*');
|
const allElements = document.querySelectorAll('*');
|
||||||
const tagCounts = {};
|
const tagCounts = {};
|
||||||
let maxDepth = 0;
|
let maxDepth = 0;
|
||||||
let totalNodes = allElements.length;
|
const totalNodes = allElements.length;
|
||||||
|
|
||||||
allElements.forEach(el => {
|
allElements.forEach(el => {
|
||||||
const tag = el.tagName.toLowerCase();
|
const tag = el.tagName.toLowerCase();
|
||||||
@@ -297,7 +297,7 @@ console.log('╔═════════════════════
|
|||||||
console.log('║ OmniAI Web Preview - Performance Analysis ║');
|
console.log('║ OmniAI Web Preview - Performance Analysis ║');
|
||||||
console.log('╚═══════════════════════════════════════════════╝');
|
console.log('╚═══════════════════════════════════════════════╝');
|
||||||
|
|
||||||
const bundleResult = analyzeBundles();
|
analyzeBundles();
|
||||||
await runtimeAnalysis();
|
await runtimeAnalysis();
|
||||||
|
|
||||||
console.log('\n═══════════════════════════════════════════════');
|
console.log('\n═══════════════════════════════════════════════');
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { readdirSync, readFileSync, statSync } from 'fs';
|
import { readdirSync, readFileSync } from 'fs';
|
||||||
import { join, relative } from 'path';
|
import { join, relative } from 'path';
|
||||||
|
|
||||||
const SRC = join(import.meta.dirname, '..', 'src');
|
const SRC = join(import.meta.dirname, '..', 'src');
|
||||||
@@ -114,7 +114,6 @@ for (const r of results) {
|
|||||||
console.log('\n=== HIGH COMPLEXITY: Deep nesting ===');
|
console.log('\n=== HIGH COMPLEXITY: Deep nesting ===');
|
||||||
for (const r of results) {
|
for (const r of results) {
|
||||||
const lines = r.content.split('\n');
|
const lines = r.content.split('\n');
|
||||||
let maxIndent = 0;
|
|
||||||
for (let i = 0; i < lines.length; i++) {
|
for (let i = 0; i < lines.length; i++) {
|
||||||
const line = lines[i];
|
const line = lines[i];
|
||||||
if (line.trim() === '') continue;
|
if (line.trim() === '') continue;
|
||||||
|
|||||||
-26
@@ -295,7 +295,6 @@ function App() {
|
|||||||
openDeleteProject: openDeleteProjectModal,
|
openDeleteProject: openDeleteProjectModal,
|
||||||
closeDeleteProject: closeDeleteProjectModal,
|
closeDeleteProject: closeDeleteProjectModal,
|
||||||
setDeleteProjectSubmitting,
|
setDeleteProjectSubmitting,
|
||||||
clearProjectState,
|
|
||||||
} = useProjectStore(useShallow((s) => ({
|
} = useProjectStore(useShallow((s) => ({
|
||||||
projects: s.projects,
|
projects: s.projects,
|
||||||
projectsLoaded: s.projectsLoaded,
|
projectsLoaded: s.projectsLoaded,
|
||||||
@@ -310,7 +309,6 @@ function App() {
|
|||||||
openDeleteProject: s.openDeleteProject,
|
openDeleteProject: s.openDeleteProject,
|
||||||
closeDeleteProject: s.closeDeleteProject,
|
closeDeleteProject: s.closeDeleteProject,
|
||||||
setDeleteProjectSubmitting: s.setDeleteProjectSubmitting,
|
setDeleteProjectSubmitting: s.setDeleteProjectSubmitting,
|
||||||
clearProjectState: s.clearProjectState,
|
|
||||||
})));
|
})));
|
||||||
|
|
||||||
// Task store
|
// Task store
|
||||||
@@ -349,7 +347,6 @@ function App() {
|
|||||||
setBackendHealth,
|
setBackendHealth,
|
||||||
markNotificationRead,
|
markNotificationRead,
|
||||||
markAllNotificationsRead,
|
markAllNotificationsRead,
|
||||||
clearAppState,
|
|
||||||
} = useAppStore(useShallow((s) => ({
|
} = useAppStore(useShallow((s) => ({
|
||||||
usage: s.usage,
|
usage: s.usage,
|
||||||
runtimeNotifications: s.runtimeNotifications,
|
runtimeNotifications: s.runtimeNotifications,
|
||||||
@@ -370,7 +367,6 @@ function App() {
|
|||||||
setBackendHealth: s.setBackendHealth,
|
setBackendHealth: s.setBackendHealth,
|
||||||
markNotificationRead: s.markNotificationRead,
|
markNotificationRead: s.markNotificationRead,
|
||||||
markAllNotificationsRead: s.markAllNotificationsRead,
|
markAllNotificationsRead: s.markAllNotificationsRead,
|
||||||
clearAppState: s.clearAppState,
|
|
||||||
})));
|
})));
|
||||||
|
|
||||||
const [ecommerceEverMounted, setEcommerceEverMounted] = useState(false);
|
const [ecommerceEverMounted, setEcommerceEverMounted] = useState(false);
|
||||||
@@ -1104,28 +1100,6 @@ function App() {
|
|||||||
[handleSetView, setImageWorkbenchTool],
|
[handleSetView, setImageWorkbenchTool],
|
||||||
);
|
);
|
||||||
|
|
||||||
const renderAdminOnlyPage = useCallback(
|
|
||||||
(content: React.ReactNode) => {
|
|
||||||
if (isAdminAccount(session)) return content;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="feature-access-gate">
|
|
||||||
<div className="feature-access-gate__content" aria-hidden="true">
|
|
||||||
{content}
|
|
||||||
</div>
|
|
||||||
<div className="feature-access-gate__overlay" role="dialog" aria-modal="true" aria-labelledby="feature-access-title">
|
|
||||||
<section className="feature-access-gate__panel panel-surface">
|
|
||||||
<span className="feature-access-gate__eyebrow">功能内测中</span>
|
|
||||||
<h2 id="feature-access-title">暂未开放</h2>
|
|
||||||
<p>敬请期待,该功能还在开发中。</p>
|
|
||||||
</section>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
[session],
|
|
||||||
);
|
|
||||||
|
|
||||||
const PUBLIC_VIEWS = PUBLIC_VIEW_SET;
|
const PUBLIC_VIEWS = PUBLIC_VIEW_SET;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import NotificationCenter from "./NotificationCenter";
|
|||||||
import BetaApplicationModal from "./BetaApplicationModal";
|
import BetaApplicationModal from "./BetaApplicationModal";
|
||||||
import { AnimatedPanel } from "./AnimatedPanel";
|
import { AnimatedPanel } from "./AnimatedPanel";
|
||||||
import AdminMonitor from "./AdminMonitor";
|
import AdminMonitor from "./AdminMonitor";
|
||||||
import CookieConsentBanner from "./CookieConsentBanner";
|
|
||||||
import { loadRechargeModal, type RechargeModalComponent } from "./RechargeModal/loadRechargeModal";
|
import { loadRechargeModal, type RechargeModalComponent } from "./RechargeModal/loadRechargeModal";
|
||||||
import { ShellIcon } from "./ShellIcon";
|
import { ShellIcon } from "./ShellIcon";
|
||||||
import { loadDarkGreenTheme } from "../styles/loadDarkGreenTheme";
|
import { loadDarkGreenTheme } from "../styles/loadDarkGreenTheme";
|
||||||
@@ -76,7 +75,6 @@ function AppShell({
|
|||||||
session,
|
session,
|
||||||
usage,
|
usage,
|
||||||
notifications,
|
notifications,
|
||||||
backendHealth,
|
|
||||||
workspaceExpanded,
|
workspaceExpanded,
|
||||||
onSelectView,
|
onSelectView,
|
||||||
onLogout,
|
onLogout,
|
||||||
@@ -85,7 +83,6 @@ function AppShell({
|
|||||||
onMarkAllNotificationsRead,
|
onMarkAllNotificationsRead,
|
||||||
children,
|
children,
|
||||||
}: AppShellProps) {
|
}: AppShellProps) {
|
||||||
const activePackage = session?.user.activePackages?.[0];
|
|
||||||
const profileRef = useRef<HTMLDivElement>(null);
|
const profileRef = useRef<HTMLDivElement>(null);
|
||||||
const submenuHideTimerRef = useRef<number | null>(null);
|
const submenuHideTimerRef = useRef<number | null>(null);
|
||||||
const [profileOpen, setProfileOpen] = useState(false);
|
const [profileOpen, setProfileOpen] = useState(false);
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { HomeOutlined } from "@ant-design/icons";
|
import { HomeOutlined } from "@ant-design/icons";
|
||||||
import { useCallback } from "react";
|
|
||||||
import "../styles/pages/not-found.css";
|
import "../styles/pages/not-found.css";
|
||||||
|
|
||||||
interface NotFoundPageProps {
|
interface NotFoundPageProps {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useCallback, useEffect, useMemo, useRef, useState, type CSSProperties } from "react";
|
import { useCallback, useEffect, useRef, useState } from "react";
|
||||||
import { createPortal } from "react-dom";
|
import { createPortal } from "react-dom";
|
||||||
import { CloseOutlined, LeftOutlined, RightOutlined } from "@ant-design/icons";
|
import { CloseOutlined, LeftOutlined, RightOutlined } from "@ant-design/icons";
|
||||||
import "../styles/components/onboarding.css";
|
import "../styles/components/onboarding.css";
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useCallback, useEffect, useRef, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
export type ToastType = "success" | "error" | "info";
|
export type ToastType = "success" | "error" | "info";
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import {
|
|||||||
SearchOutlined,
|
SearchOutlined,
|
||||||
UserOutlined,
|
UserOutlined,
|
||||||
} from "@ant-design/icons";
|
} from "@ant-design/icons";
|
||||||
import { useCallback, useEffect, useMemo, useRef, useState, type ChangeEvent, type DragEvent, type ReactElement } from "react";
|
import { useCallback, useEffect, useMemo, useRef, useState, type DragEvent, type ReactElement } from "react";
|
||||||
import "../../styles/pages/assets.css";
|
import "../../styles/pages/assets.css";
|
||||||
import { assetClient, type ServerAssetItem } from "../../api/assetClient";
|
import { assetClient, type ServerAssetItem } from "../../api/assetClient";
|
||||||
import { aiGenerationClient } from "../../api/aiGenerationClient";
|
import { aiGenerationClient } from "../../api/aiGenerationClient";
|
||||||
|
|||||||
@@ -106,7 +106,11 @@ function blobToDataUrl(blob: Blob): Promise<string> {
|
|||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
reader.onload = () => {
|
reader.onload = () => {
|
||||||
typeof reader.result === "string" ? resolve(reader.result) : reject(new Error("Unable to read canvas result"));
|
if (typeof reader.result === "string") {
|
||||||
|
resolve(reader.result);
|
||||||
|
} else {
|
||||||
|
reject(new Error("Unable to read canvas result"));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
reader.onerror = () => reject(reader.error || new Error("Unable to read canvas result"));
|
reader.onerror = () => reject(reader.error || new Error("Unable to read canvas result"));
|
||||||
reader.readAsDataURL(blob);
|
reader.readAsDataURL(blob);
|
||||||
|
|||||||
@@ -1,17 +1,10 @@
|
|||||||
import {
|
import {
|
||||||
CopyOutlined,
|
|
||||||
DeleteOutlined,
|
|
||||||
DownloadOutlined,
|
|
||||||
DownOutlined,
|
DownOutlined,
|
||||||
ExpandOutlined,
|
ExpandOutlined,
|
||||||
MutedOutlined,
|
MutedOutlined,
|
||||||
PauseCircleOutlined,
|
PauseCircleOutlined,
|
||||||
PictureOutlined,
|
|
||||||
PlayCircleOutlined,
|
PlayCircleOutlined,
|
||||||
ReloadOutlined,
|
|
||||||
SaveOutlined,
|
|
||||||
SoundOutlined,
|
SoundOutlined,
|
||||||
ThunderboltOutlined,
|
|
||||||
} from "@ant-design/icons";
|
} from "@ant-design/icons";
|
||||||
import { useEffect, useRef, useState, type ChangeEvent, type MouseEvent } from "react";
|
import { useEffect, useRef, useState, type ChangeEvent, type MouseEvent } from "react";
|
||||||
import type { CanvasOption } from "./canvasTypes";
|
import type { CanvasOption } from "./canvasTypes";
|
||||||
|
|||||||
@@ -146,26 +146,6 @@ export function CanvasInpaintPanel({ imageUrl, onComplete }: CanvasToolPanelProp
|
|||||||
ctx.fill();
|
ctx.fill();
|
||||||
};
|
};
|
||||||
|
|
||||||
const getMaskDataUrl = (): string => {
|
|
||||||
const canvas = canvasRef.current!;
|
|
||||||
const maskCanvas = document.createElement("canvas");
|
|
||||||
maskCanvas.width = canvas.width;
|
|
||||||
maskCanvas.height = canvas.height;
|
|
||||||
const srcCtx = canvas.getContext("2d")!;
|
|
||||||
const maskCtx = maskCanvas.getContext("2d")!;
|
|
||||||
const imgData = srcCtx.getImageData(0, 0, canvas.width, canvas.height);
|
|
||||||
const maskData = maskCtx.createImageData(canvas.width, canvas.height);
|
|
||||||
for (let i = 0; i < imgData.data.length; i += 4) {
|
|
||||||
const hasColor = imgData.data[i + 3] > 10;
|
|
||||||
maskData.data[i] = hasColor ? 255 : 0;
|
|
||||||
maskData.data[i + 1] = hasColor ? 255 : 0;
|
|
||||||
maskData.data[i + 2] = hasColor ? 255 : 0;
|
|
||||||
maskData.data[i + 3] = 255;
|
|
||||||
}
|
|
||||||
maskCtx.putImageData(maskData, 0, 0);
|
|
||||||
return maskCanvas.toDataURL("image/png");
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleInpaint = useCallback(async () => {
|
const handleInpaint = useCallback(async () => {
|
||||||
if (!imageUrl || !prompt) {
|
if (!imageUrl || !prompt) {
|
||||||
toast.error("请输入重绘提示词");
|
toast.error("请输入重绘提示词");
|
||||||
@@ -174,7 +154,6 @@ export function CanvasInpaintPanel({ imageUrl, onComplete }: CanvasToolPanelProp
|
|||||||
setLoading(true);
|
setLoading(true);
|
||||||
cancelRef.current = false;
|
cancelRef.current = false;
|
||||||
try {
|
try {
|
||||||
const maskDataUrl = getMaskDataUrl();
|
|
||||||
const { taskId } = await aiGenerationClient.createImageEditTask({
|
const { taskId } = await aiGenerationClient.createImageEditTask({
|
||||||
imageUrl,
|
imageUrl,
|
||||||
function: "inpaint",
|
function: "inpaint",
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import type { CSSProperties } from "react";
|
import type { CSSProperties } from "react";
|
||||||
import { aiGenerationClient, type AiTaskStatus } from "../../api/aiGenerationClient";
|
import type { AiTaskStatus } from "../../api/aiGenerationClient";
|
||||||
import type { ServerCommunityCase } from "../../api/communityClient";
|
import type { ServerCommunityCase } from "../../api/communityClient";
|
||||||
import { waitForTask } from "../../api/taskSubscription";
|
import { waitForTask } from "../../api/taskSubscription";
|
||||||
import type { WebCanvasWorkflow } from "../../types";
|
import type { WebCanvasWorkflow } from "../../types";
|
||||||
@@ -22,7 +22,6 @@ import type {
|
|||||||
CanvasVideoMode,
|
CanvasVideoMode,
|
||||||
} from "./canvasTypes";
|
} from "./canvasTypes";
|
||||||
import {
|
import {
|
||||||
assetLibraryCategories,
|
|
||||||
assetTypePromptLabel,
|
assetTypePromptLabel,
|
||||||
canvasNodeDefaultSizes,
|
canvasNodeDefaultSizes,
|
||||||
canvasNodeMaxSizes,
|
canvasNodeMaxSizes,
|
||||||
@@ -194,7 +193,7 @@ export function resolveWorkflowImageModel(node: WebCanvasWorkflow["nodes"][numbe
|
|||||||
return defaultImageModel;
|
return defaultImageModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function resolveWorkflowVideoModel(node: WebCanvasWorkflow["nodes"][number], workflowModel: string) {
|
export function resolveWorkflowVideoModel(node: WebCanvasWorkflow["nodes"][number], _workflowModel: string) {
|
||||||
const raw = getWorkflowNodeMetadataString(node, "model");
|
const raw = getWorkflowNodeMetadataString(node, "model");
|
||||||
const storedModel = toPixverseDisplayModel(toViduDisplayModel(toHappyHorseDisplayModel(raw)));
|
const storedModel = toPixverseDisplayModel(toViduDisplayModel(toHappyHorseDisplayModel(raw)));
|
||||||
if (hasCanvasOptionValue(videoModelOptions, storedModel)) return storedModel;
|
if (hasCanvasOptionValue(videoModelOptions, storedModel)) return storedModel;
|
||||||
@@ -242,7 +241,11 @@ export function blobToDataUrl(blob: Blob) {
|
|||||||
return new Promise<string>((resolve, reject) => {
|
return new Promise<string>((resolve, reject) => {
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
reader.onload = () => {
|
reader.onload = () => {
|
||||||
typeof reader.result === "string" ? resolve(reader.result) : reject(new Error("Unable to read canvas image"));
|
if (typeof reader.result === "string") {
|
||||||
|
resolve(reader.result);
|
||||||
|
} else {
|
||||||
|
reject(new Error("Unable to read canvas image"));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
reader.onerror = () => reject(reader.error || new Error("Unable to read canvas image"));
|
reader.onerror = () => reject(reader.error || new Error("Unable to read canvas image"));
|
||||||
reader.readAsDataURL(blob);
|
reader.readAsDataURL(blob);
|
||||||
@@ -253,7 +256,9 @@ export async function waitForImageTaskResult(taskId: string, onStatus?: (status:
|
|||||||
const resultUrl = await waitForTask(taskId, {
|
const resultUrl = await waitForTask(taskId, {
|
||||||
kind: "image",
|
kind: "image",
|
||||||
onProgress: (e) => {
|
onProgress: (e) => {
|
||||||
onStatus?.({ taskId, status: e.status, progress: e.progress, resultUrl: e.resultUrl ?? undefined, error: e.error ?? undefined } as AiTaskStatus);
|
if (onStatus) {
|
||||||
|
onStatus({ taskId, status: e.status, progress: e.progress, resultUrl: e.resultUrl ?? undefined, error: e.error ?? undefined } as AiTaskStatus);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
if (!resultUrl) throw new Error("生成任务已完成,但服务器没有返回结果地址,请稍后重试");
|
if (!resultUrl) throw new Error("生成任务已完成,但服务器没有返回结果地址,请稍后重试");
|
||||||
@@ -264,7 +269,9 @@ export async function waitForVideoTaskResult(taskId: string, onStatus?: (status:
|
|||||||
const resultUrl = await waitForTask(taskId, {
|
const resultUrl = await waitForTask(taskId, {
|
||||||
kind: "video",
|
kind: "video",
|
||||||
onProgress: (e) => {
|
onProgress: (e) => {
|
||||||
onStatus?.({ taskId, status: e.status, progress: e.progress, resultUrl: e.resultUrl ?? undefined, error: e.error ?? undefined } as AiTaskStatus);
|
if (onStatus) {
|
||||||
|
onStatus({ taskId, status: e.status, progress: e.progress, resultUrl: e.resultUrl ?? undefined, error: e.error ?? undefined } as AiTaskStatus);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
if (!resultUrl) throw new Error("视频生成任务已完成,但服务器没有返回结果地址,请稍后重试");
|
if (!resultUrl) throw new Error("视频生成任务已完成,但服务器没有返回结果地址,请稍后重试");
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ export function buildCanvasVideoTaskInput(workflow: WebCanvasWorkflow, nodeId: s
|
|||||||
const params = context.node.params || {};
|
const params = context.node.params || {};
|
||||||
const referenceUrls = context.imageReferences.map((item) => item.url);
|
const referenceUrls = context.imageReferences.map((item) => item.url);
|
||||||
const displayModel = toHappyHorseDisplayModel(String(params.model || workflow.settings.model || "happyhorse-1.0"));
|
const displayModel = toHappyHorseDisplayModel(String(params.model || workflow.settings.model || "happyhorse-1.0"));
|
||||||
let model = resolveVideoRequestModel({ model: displayModel, referenceUrls });
|
const model = resolveVideoRequestModel({ model: displayModel, referenceUrls });
|
||||||
return {
|
return {
|
||||||
title: context.node.label || "视频节点生成",
|
title: context.node.label || "视频节点生成",
|
||||||
type: "video",
|
type: "video",
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { type Dispatch, type MutableRefObject, type SetStateAction, useEffect, useRef, useState } from "react";
|
import { type Dispatch, type SetStateAction, useEffect, useRef, useState } from "react";
|
||||||
import type {
|
import type {
|
||||||
CanvasImageGenerationState,
|
CanvasImageGenerationState,
|
||||||
CanvasImageNode,
|
CanvasImageNode,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { type Dispatch, type MouseEvent, type MutableRefObject, type SetStateAction, useEffect, useRef, useState } from "react";
|
import { type Dispatch, type MouseEvent, type MutableRefObject, type SetStateAction, useEffect, useState } from "react";
|
||||||
import { canvasNodeClickMoveThreshold } from "./canvasConstants";
|
import { canvasNodeClickMoveThreshold } from "./canvasConstants";
|
||||||
import type {
|
import type {
|
||||||
CanvasAlignGuide,
|
CanvasAlignGuide,
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import {
|
|||||||
RightOutlined,
|
RightOutlined,
|
||||||
ScissorOutlined,
|
ScissorOutlined,
|
||||||
SwapOutlined,
|
SwapOutlined,
|
||||||
ThunderboltOutlined,
|
|
||||||
VideoCameraOutlined,
|
VideoCameraOutlined,
|
||||||
} from "@ant-design/icons";
|
} from "@ant-design/icons";
|
||||||
import { useCallback, useEffect, useRef, useState, type DragEvent } from "react";
|
import { useCallback, useEffect, useRef, useState, type DragEvent } from "react";
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ import {
|
|||||||
RightOutlined,
|
RightOutlined,
|
||||||
ScissorOutlined,
|
ScissorOutlined,
|
||||||
SwapOutlined,
|
SwapOutlined,
|
||||||
ThunderboltOutlined,
|
|
||||||
UserOutlined,
|
UserOutlined,
|
||||||
} from "@ant-design/icons";
|
} from "@ant-design/icons";
|
||||||
import { useCallback, useEffect, useRef, useState, type DragEvent } from "react";
|
import { useCallback, useEffect, useRef, useState, type DragEvent } from "react";
|
||||||
@@ -100,7 +99,6 @@ function DigitalHumanPage({
|
|||||||
const [isDragging, setIsDragging] = useState(false);
|
const [isDragging, setIsDragging] = useState(false);
|
||||||
const imageInputRef = useRef<HTMLInputElement | null>(null);
|
const imageInputRef = useRef<HTMLInputElement | null>(null);
|
||||||
const audioInputRef = useRef<HTMLInputElement | null>(null);
|
const audioInputRef = useRef<HTMLInputElement | null>(null);
|
||||||
const canvasDragCounterRef = useRef(0);
|
|
||||||
const [isCanvasDragging, setIsCanvasDragging] = useState(false);
|
const [isCanvasDragging, setIsCanvasDragging] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import {
|
import {
|
||||||
AppstoreOutlined,
|
AppstoreOutlined,
|
||||||
CloudUploadOutlined,
|
|
||||||
CloseOutlined,
|
CloseOutlined,
|
||||||
FileImageOutlined,
|
FileImageOutlined,
|
||||||
FrownOutlined,
|
FrownOutlined,
|
||||||
@@ -9,7 +8,6 @@ import {
|
|||||||
MenuUnfoldOutlined,
|
MenuUnfoldOutlined,
|
||||||
QuestionCircleOutlined,
|
QuestionCircleOutlined,
|
||||||
ReloadOutlined,
|
ReloadOutlined,
|
||||||
SettingOutlined,
|
|
||||||
SkinOutlined,
|
SkinOutlined,
|
||||||
} from "@ant-design/icons";
|
} from "@ant-design/icons";
|
||||||
import { useEffect, useMemo, useRef, useState, type CSSProperties, type ChangeEvent, type DragEvent, type ReactNode } from "react";
|
import { useEffect, useMemo, useRef, useState, type CSSProperties, type ChangeEvent, type DragEvent, type ReactNode } from "react";
|
||||||
@@ -624,11 +622,6 @@ const tryOnModelOptions = {
|
|||||||
ethnicity: ["欧美白人", "亚洲人", "拉美裔", "非洲裔"],
|
ethnicity: ["欧美白人", "亚洲人", "拉美裔", "非洲裔"],
|
||||||
body: ["标准", "高挑", "微胖", "运动"],
|
body: ["标准", "高挑", "微胖", "运动"],
|
||||||
};
|
};
|
||||||
const sampleResults = [
|
|
||||||
ossAssets.ecommerce.slides.slide4,
|
|
||||||
ossAssets.ecommerce.generated,
|
|
||||||
ossAssets.ecommerce.slides.slide5,
|
|
||||||
];
|
|
||||||
const productSetAssets = ossAssets.ecommerce.productSet;
|
const productSetAssets = ossAssets.ecommerce.productSet;
|
||||||
const productSetPreviewCards = [
|
const productSetPreviewCards = [
|
||||||
{ id: "main", label: "01 主图 (白底/合规)", src: productSetAssets.main },
|
{ id: "main", label: "01 主图 (白底/合规)", src: productSetAssets.main },
|
||||||
@@ -701,14 +694,6 @@ function readImageDimensions(src: string): Promise<{ width: number; height: numb
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const blobToDataUrl = (blob: Blob): Promise<string> =>
|
|
||||||
new Promise((resolve, reject) => {
|
|
||||||
const reader = new FileReader();
|
|
||||||
reader.onload = () => resolve(String(reader.result || ""));
|
|
||||||
reader.onerror = () => reject(reader.error || new Error("文件读取失败"));
|
|
||||||
reader.readAsDataURL(blob);
|
|
||||||
});
|
|
||||||
|
|
||||||
async function createUploadedImageItems(files: File[], limit: number, prefix: string): Promise<CloneImageItem[]> {
|
async function createUploadedImageItems(files: File[], limit: number, prefix: string): Promise<CloneImageItem[]> {
|
||||||
const selectedFiles = Array.from(files).slice(0, limit);
|
const selectedFiles = Array.from(files).slice(0, limit);
|
||||||
const stamp = Date.now();
|
const stamp = Date.now();
|
||||||
@@ -1702,7 +1687,6 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const generatedUrls: string[] = [];
|
const generatedUrls: string[] = [];
|
||||||
const stamp = Date.now();
|
|
||||||
|
|
||||||
for (const countKey of cloneSetCountKeys) {
|
for (const countKey of cloneSetCountKeys) {
|
||||||
if (imageAbortRef.current.current) break;
|
if (imageAbortRef.current.current) break;
|
||||||
@@ -2039,7 +2023,7 @@ function ProductClonePage(_props: ProductClonePageProps = {}) {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const resetTask = () => {
|
const _resetTask = () => {
|
||||||
setSetImages([]);
|
setSetImages([]);
|
||||||
setProductSetRequirement("");
|
setProductSetRequirement("");
|
||||||
setProductSetOutput("video");
|
setProductSetOutput("video");
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Fragment, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||||
import "../../styles/pages/ecommerce-video.css";
|
import "../../styles/pages/ecommerce-video.css";
|
||||||
import {
|
import {
|
||||||
CloseOutlined,
|
CloseOutlined,
|
||||||
@@ -14,7 +14,6 @@ import {
|
|||||||
import { runVideoPlan, renderSceneImage, renderScene, buildSceneTasks, saveVideoHistory } from "./ecommerceVideoService";
|
import { runVideoPlan, renderSceneImage, renderScene, buildSceneTasks, saveVideoHistory } from "./ecommerceVideoService";
|
||||||
import {
|
import {
|
||||||
PLAN_STEP_LABELS,
|
PLAN_STEP_LABELS,
|
||||||
PLAN_STEPS_DISPLAY,
|
|
||||||
type EcommerceVideoStage,
|
type EcommerceVideoStage,
|
||||||
type EcommerceVideoSceneTask,
|
type EcommerceVideoSceneTask,
|
||||||
type EcommerceVideoPlanProgress,
|
type EcommerceVideoPlanProgress,
|
||||||
@@ -29,7 +28,6 @@ import { useGenerationTasks } from "../../hooks/useGenerationTasks";
|
|||||||
import {
|
import {
|
||||||
saveEcommerceVideoState,
|
saveEcommerceVideoState,
|
||||||
loadEcommerceVideoState,
|
loadEcommerceVideoState,
|
||||||
clearEcommerceVideoState,
|
|
||||||
} from "./ecommerceVideoKeepalive";
|
} from "./ecommerceVideoKeepalive";
|
||||||
|
|
||||||
interface EcommerceVideoWorkspaceProps {
|
interface EcommerceVideoWorkspaceProps {
|
||||||
@@ -298,7 +296,7 @@ export default function EcommerceVideoWorkspace({
|
|||||||
}, 3000);
|
}, 3000);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleDownload = async (url: string) => {
|
const _handleDownload = async (url: string) => {
|
||||||
try {
|
try {
|
||||||
await saveToolResultToLocal({
|
await saveToolResultToLocal({
|
||||||
url, name: `ecommerce-video-${Date.now()}`, type: "video",
|
url, name: `ecommerce-video-${Date.now()}`, type: "video",
|
||||||
@@ -310,7 +308,7 @@ export default function EcommerceVideoWorkspace({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSaveAsset = async (url: string) => {
|
const _handleSaveAsset = async (url: string) => {
|
||||||
try {
|
try {
|
||||||
const result = await addToolResultToAssetLibrary({
|
const result = await addToolResultToAssetLibrary({
|
||||||
url, name: `电商短视频-${Date.now()}.mp4`, description: "电商广告视频生成结果",
|
url, name: `电商短视频-${Date.now()}.mp4`, description: "电商广告视频生成结果",
|
||||||
@@ -596,9 +594,6 @@ export default function EcommerceVideoWorkspace({
|
|||||||
const sourceImage = sourceImageUrls[0] || planResult?.imageUrls[0] || productImageDataUrls[0] || "";
|
const sourceImage = sourceImageUrls[0] || planResult?.imageUrls[0] || productImageDataUrls[0] || "";
|
||||||
const flowHasStarted = stage !== "idle" || completedSteps.length > 0;
|
const flowHasStarted = stage !== "idle" || completedSteps.length > 0;
|
||||||
const flowMeta = `${platform} / ${aspectRatio} / ${durationSeconds}s / ${resolution}`;
|
const flowMeta = `${platform} / ${aspectRatio} / ${durationSeconds}s / ${resolution}`;
|
||||||
const hasImaging = stage === "imaging" || stage === "imaged" || stage === "rendering" || stage === "completed" || stage === "partial_failed";
|
|
||||||
const hasRendering = stage === "rendering" || stage === "completed" || stage === "partial_failed";
|
|
||||||
const visiblePlanSteps = PLAN_STEPS_DISPLAY.filter((s) => completedSteps.includes(s));
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="ecom-video-workspace" data-stage={stage}>
|
<div className="ecom-video-workspace" data-stage={stage}>
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ const [
|
|||||||
ecommerceCarouselImage2,
|
ecommerceCarouselImage2,
|
||||||
ecommerceCarouselImage3,
|
ecommerceCarouselImage3,
|
||||||
ecommerceCarouselImage4,
|
ecommerceCarouselImage4,
|
||||||
ecommerceCarouselImage5,
|
,
|
||||||
ecommerceCarouselImage6,
|
ecommerceCarouselImage6,
|
||||||
] = ossAssets.ecommerce.templateCases;
|
] = ossAssets.ecommerce.templateCases;
|
||||||
const ecommerceCarouselGenerated = ossAssets.ecommerce.generated;
|
const ecommerceCarouselGenerated = ossAssets.ecommerce.generated;
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import {
|
import {
|
||||||
ArrowRightOutlined,
|
ArrowRightOutlined,
|
||||||
DashboardOutlined,
|
|
||||||
FileSearchOutlined,
|
FileSearchOutlined,
|
||||||
PlayCircleOutlined,
|
PlayCircleOutlined,
|
||||||
PlusOutlined,
|
PlusOutlined,
|
||||||
@@ -9,7 +8,6 @@ import {
|
|||||||
} from "@ant-design/icons";
|
} from "@ant-design/icons";
|
||||||
import { Fragment, useCallback, useEffect, useMemo, useRef, useState, type CSSProperties } from "react";
|
import { Fragment, useCallback, useEffect, useMemo, useRef, useState, type CSSProperties } from "react";
|
||||||
import type { WebViewKey, WebImageWorkbenchTool } from "../../types";
|
import type { WebViewKey, WebImageWorkbenchTool } from "../../types";
|
||||||
import { useScrollEntrance } from "../../hooks/useScrollEntrance";
|
|
||||||
import { ossAssets } from "../../data/ossAssets";
|
import { ossAssets } from "../../data/ossAssets";
|
||||||
import "../../styles/pages/home.css";
|
import "../../styles/pages/home.css";
|
||||||
import WelcomeSplash from "./WelcomeSplash";
|
import WelcomeSplash from "./WelcomeSplash";
|
||||||
@@ -17,15 +15,6 @@ import ToolboxSection from "./ToolboxSection";
|
|||||||
import ScriptReviewShowcase from "./ScriptReviewShowcase";
|
import ScriptReviewShowcase from "./ScriptReviewShowcase";
|
||||||
import ModelGenerationShowcase from "./ModelGenerationShowcase";
|
import ModelGenerationShowcase from "./ModelGenerationShowcase";
|
||||||
|
|
||||||
function ScrollEntrance({ children, className, ...rest }: { children: React.ReactNode; className?: string } & React.HTMLAttributes<HTMLElement>) {
|
|
||||||
const { ref, isVisible } = useScrollEntrance<HTMLElement>();
|
|
||||||
return (
|
|
||||||
<section ref={ref} className={`${className ?? ""} scroll-entrance${isVisible ? " is-visible" : ""}`} {...rest}>
|
|
||||||
{children}
|
|
||||||
</section>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const [heroImage1, heroImage2, heroImage3] = ossAssets.home.heroSlides;
|
const [heroImage1, heroImage2, heroImage3] = ossAssets.home.heroSlides;
|
||||||
const {
|
const {
|
||||||
ecommerce: featureEcommerceImage,
|
ecommerce: featureEcommerceImage,
|
||||||
@@ -469,7 +458,7 @@ function EcommerceFeatureShowcase() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function HomePage({ onOpenGenerate, onStartOnboarding, onOpenCanvas, onOpenEcommerce, onOpenScriptReview, onOpenTokenMonitor, onSelectView, onOpenImageTool }: HomePageProps) {
|
function HomePage({ onOpenGenerate, onStartOnboarding, onOpenCanvas, onOpenEcommerce, onOpenScriptReview, onSelectView, onOpenImageTool }: HomePageProps) {
|
||||||
const [splashDismissed, setSplashDismissed] = useState(() => sessionStorage.getItem("omniai:splash-seen") === "1");
|
const [splashDismissed, setSplashDismissed] = useState(() => sessionStorage.getItem("omniai:splash-seen") === "1");
|
||||||
const [activeSlideIndex, setActiveSlideIndex] = useState(0);
|
const [activeSlideIndex, setActiveSlideIndex] = useState(0);
|
||||||
const [carouselMotion, setCarouselMotion] = useState<HomeCarouselMotion | null>(null);
|
const [carouselMotion, setCarouselMotion] = useState<HomeCarouselMotion | null>(null);
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ function ScriptReviewVisual() {
|
|||||||
const [animated, setAnimated] = useState(false);
|
const [animated, setAnimated] = useState(false);
|
||||||
const [activeDim, setActiveDim] = useState<number | null>(null);
|
const [activeDim, setActiveDim] = useState<number | null>(null);
|
||||||
const [score, setScore] = useState(0);
|
const [score, setScore] = useState(0);
|
||||||
const scoreRef = useRef<number>(0);
|
|
||||||
const frameRef = useRef<number | null>(null);
|
const frameRef = useRef<number | null>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@@ -145,7 +145,7 @@ const CARDS = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
function ToolboxSection({ onSelectView, onOpenImageTool }: ToolboxSectionProps) {
|
function ToolboxSection({ onSelectView }: ToolboxSectionProps) {
|
||||||
const handleCardClick = (targetView: WebViewKey) => {
|
const handleCardClick = (targetView: WebViewKey) => {
|
||||||
onSelectView(targetView);
|
onSelectView(targetView);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ import {
|
|||||||
ScissorOutlined,
|
ScissorOutlined,
|
||||||
SwapOutlined,
|
SwapOutlined,
|
||||||
TableOutlined,
|
TableOutlined,
|
||||||
ThunderboltOutlined,
|
|
||||||
} from "@ant-design/icons";
|
} from "@ant-design/icons";
|
||||||
import { useCallback, useEffect, useRef, useState, type DragEvent } from "react";
|
import { useCallback, useEffect, useRef, useState, type DragEvent } from "react";
|
||||||
import "../../styles/pages/more-tools.css";
|
import "../../styles/pages/more-tools.css";
|
||||||
@@ -30,7 +29,6 @@ import type { WebImageWorkbenchTool, WebViewKey } from "../../types";
|
|||||||
import { aiGenerationClient } from "../../api/aiGenerationClient";
|
import { aiGenerationClient } from "../../api/aiGenerationClient";
|
||||||
import { waitForTask } from "../../api/taskSubscription";
|
import { waitForTask } from "../../api/taskSubscription";
|
||||||
import { saveToolTaskState, loadToolTaskState, clearToolTaskState } from "../workbench/toolKeepalive";
|
import { saveToolTaskState, loadToolTaskState, clearToolTaskState } from "../workbench/toolKeepalive";
|
||||||
import { translateTaskError } from "../../utils/translateTaskError";
|
|
||||||
import { addToolResultToAssetLibrary, saveToolResultToLocal } from "../workbench/toolResultActions";
|
import { addToolResultToAssetLibrary, saveToolResultToLocal } from "../workbench/toolResultActions";
|
||||||
import { useCanvasDrawing } from "./useCanvasDrawing";
|
import { useCanvasDrawing } from "./useCanvasDrawing";
|
||||||
import CameraViewport3D from "./CameraViewport3D";
|
import CameraViewport3D from "./CameraViewport3D";
|
||||||
@@ -40,7 +38,6 @@ type OutputSize = "9:16" | "16:9" | "4:3" | "3:4" | "1:1";
|
|||||||
type OutputCount = 1 | 2 | 3 | 4;
|
type OutputCount = 1 | 2 | 3 | 4;
|
||||||
|
|
||||||
const OUTPUT_SIZE_OPTIONS: OutputSize[] = ["9:16", "16:9", "4:3", "3:4", "1:1"];
|
const OUTPUT_SIZE_OPTIONS: OutputSize[] = ["9:16", "16:9", "4:3", "3:4", "1:1"];
|
||||||
const OUTPUT_COUNT_OPTIONS: OutputCount[] = [1, 2, 3, 4];
|
|
||||||
|
|
||||||
const SIZE_TO_RATIO: Record<OutputSize, string> = {
|
const SIZE_TO_RATIO: Record<OutputSize, string> = {
|
||||||
"9:16": "9:16",
|
"9:16": "9:16",
|
||||||
|
|||||||
@@ -1488,6 +1488,32 @@ function ProfilePage({
|
|||||||
</span>
|
</span>
|
||||||
) : null}
|
) : null}
|
||||||
</label>
|
</label>
|
||||||
|
{mode === "register" ? (
|
||||||
|
<label className={`auth-page__field${fieldErrors.emailCode ? " auth-page__field--error" : ""}`}>
|
||||||
|
<span>
|
||||||
|
<SafetyOutlined /> 邮箱验证码
|
||||||
|
</span>
|
||||||
|
<div className="auth-page__sms-row">
|
||||||
|
<input
|
||||||
|
value={emailCode}
|
||||||
|
onChange={(event) => { setEmailCode(event.target.value); clearFieldError("emailCode"); }}
|
||||||
|
onBlur={() => handleFieldBlur("emailCode", emailCode)}
|
||||||
|
placeholder="输入 6 位验证码"
|
||||||
|
maxLength={6}
|
||||||
|
autoComplete="one-time-code"
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="auth-page__sms-btn"
|
||||||
|
disabled={emailCooldown > 0 || !email.trim() || isSendingEmail || !betaCode.trim()}
|
||||||
|
onClick={() => void handleSendEmailCode("register")}
|
||||||
|
>
|
||||||
|
{isSendingEmail ? "发送中" : emailCooldown > 0 ? `${emailCooldown}s` : "获取验证码"}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
{fieldErrors.emailCode ? <span className="auth-page__field-error">{fieldErrors.emailCode}</span> : null}
|
||||||
|
</label>
|
||||||
|
) : null}
|
||||||
</>
|
</>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
|
|||||||
@@ -21,9 +21,8 @@ import "../../styles/pages/image-workbench.css";
|
|||||||
import { aiGenerationClient } from "../../api/aiGenerationClient";
|
import { aiGenerationClient } from "../../api/aiGenerationClient";
|
||||||
import { waitForTask } from "../../api/taskSubscription";
|
import { waitForTask } from "../../api/taskSubscription";
|
||||||
import { saveToolTaskState, loadToolTaskState, clearToolTaskState } from "../workbench/toolKeepalive";
|
import { saveToolTaskState, loadToolTaskState, clearToolTaskState } from "../workbench/toolKeepalive";
|
||||||
import { translateTaskError } from "../../utils/translateTaskError";
|
|
||||||
import { getServerBaseUrl, isServerRequestError } from "../../api/serverConnection";
|
import { getServerBaseUrl, isServerRequestError } from "../../api/serverConnection";
|
||||||
import { summarizeUrl, formatFileSize, fileToDataUrl, wait } from "../../utils/toolPageUtils";
|
import { summarizeUrl, formatFileSize, fileToDataUrl } from "../../utils/toolPageUtils";
|
||||||
import TaskStatusBar from "../../components/TaskStatusBar";
|
import TaskStatusBar from "../../components/TaskStatusBar";
|
||||||
import BeforeAfterCompare from "../../components/BeforeAfterCompare";
|
import BeforeAfterCompare from "../../components/BeforeAfterCompare";
|
||||||
import { addToolResultToAssetLibrary, saveToolResultToLocal } from "../workbench/toolResultActions";
|
import { addToolResultToAssetLibrary, saveToolResultToLocal } from "../workbench/toolResultActions";
|
||||||
|
|||||||
@@ -1,12 +1,6 @@
|
|||||||
import {
|
import {
|
||||||
BarChartOutlined,
|
|
||||||
CheckCircleFilled,
|
CheckCircleFilled,
|
||||||
CloseOutlined,
|
CloseOutlined,
|
||||||
CopyOutlined,
|
|
||||||
DownloadOutlined,
|
|
||||||
FileTextOutlined,
|
|
||||||
LoadingOutlined,
|
|
||||||
ThunderboltOutlined,
|
|
||||||
UploadOutlined,
|
UploadOutlined,
|
||||||
} from "@ant-design/icons";
|
} from "@ant-design/icons";
|
||||||
import { useEffect, useRef, useState, type ChangeEvent, type DragEvent, type KeyboardEvent } from "react";
|
import { useEffect, useRef, useState, type ChangeEvent, type DragEvent, type KeyboardEvent } from "react";
|
||||||
@@ -264,7 +258,7 @@ function getDimensionEvidence(result: EvalResult, dim: ScoreDimension): string[]
|
|||||||
return normalizeEvidenceItems(evidence, 3);
|
return normalizeEvidenceItems(evidence, 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatReportMarkdown(result: EvalResult, script: string): string {
|
function formatReportMarkdown(result: EvalResult): string {
|
||||||
const lines: string[] = [];
|
const lines: string[] = [];
|
||||||
lines.push(`# 剧本评测报告`);
|
lines.push(`# 剧本评测报告`);
|
||||||
lines.push("");
|
lines.push("");
|
||||||
@@ -438,7 +432,7 @@ function ScriptTokensPage() {
|
|||||||
|
|
||||||
const handleCopyReport = async () => {
|
const handleCopyReport = async () => {
|
||||||
if (!result) return;
|
if (!result) return;
|
||||||
const text = formatReportMarkdown(result, script);
|
const text = formatReportMarkdown(result);
|
||||||
try {
|
try {
|
||||||
await navigator.clipboard.writeText(text);
|
await navigator.clipboard.writeText(text);
|
||||||
setCopied(true);
|
setCopied(true);
|
||||||
@@ -459,7 +453,7 @@ function ScriptTokensPage() {
|
|||||||
|
|
||||||
const handleExportMarkdown = () => {
|
const handleExportMarkdown = () => {
|
||||||
if (!result) return;
|
if (!result) return;
|
||||||
const md = formatReportMarkdown(result, script);
|
const md = formatReportMarkdown(result);
|
||||||
const blob = new Blob([md], { type: "text/markdown;charset=utf-8" });
|
const blob = new Blob([md], { type: "text/markdown;charset=utf-8" });
|
||||||
const url = URL.createObjectURL(blob);
|
const url = URL.createObjectURL(blob);
|
||||||
const a = document.createElement("a");
|
const a = document.createElement("a");
|
||||||
|
|||||||
@@ -130,8 +130,6 @@ function TokenUsagePage({
|
|||||||
loadEnterpriseUsage,
|
loadEnterpriseUsage,
|
||||||
loadPersonalUsage,
|
loadPersonalUsage,
|
||||||
onOpenMore,
|
onOpenMore,
|
||||||
onOpenImageTool,
|
|
||||||
onSelectView,
|
|
||||||
}: TokenUsagePageProps) {
|
}: TokenUsagePageProps) {
|
||||||
const [enterpriseUsage, setEnterpriseUsage] = useState<WebEnterpriseUsageSummary | null>(null);
|
const [enterpriseUsage, setEnterpriseUsage] = useState<WebEnterpriseUsageSummary | null>(null);
|
||||||
const [enterpriseUsageLoading, setEnterpriseUsageLoading] = useState(false);
|
const [enterpriseUsageLoading, setEnterpriseUsageLoading] = useState(false);
|
||||||
|
|||||||
@@ -947,7 +947,6 @@ function SizeTemplatePage({ onOpenEcommerce }: SizeTemplatePageProps) {
|
|||||||
);
|
);
|
||||||
const selectedPreset =
|
const selectedPreset =
|
||||||
filteredTemplates.find((item) => item.title === activePresetTitle) ?? filteredTemplates[0] ?? sizeTemplatePresets[0]!;
|
filteredTemplates.find((item) => item.title === activePresetTitle) ?? filteredTemplates[0] ?? sizeTemplatePresets[0]!;
|
||||||
const activeGroupLabel = sizeTemplateGroups.find((item) => item.key === selectedPreset.group)?.label ?? "尺寸模板";
|
|
||||||
const platformOptions =
|
const platformOptions =
|
||||||
activeGroup === "socialCn"
|
activeGroup === "socialCn"
|
||||||
? socialContentPlatformOptions
|
? socialContentPlatformOptions
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import {
|
import {
|
||||||
CloseOutlined,
|
|
||||||
DeleteOutlined,
|
DeleteOutlined,
|
||||||
EditOutlined,
|
EditOutlined,
|
||||||
MenuFoldOutlined,
|
MenuFoldOutlined,
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import {
|
|||||||
type WorkbenchMode,
|
type WorkbenchMode,
|
||||||
type ReferenceKind,
|
type ReferenceKind,
|
||||||
type ReferenceItem,
|
type ReferenceItem,
|
||||||
type WorkbenchOption,
|
|
||||||
} from "./workbenchConstants";
|
} from "./workbenchConstants";
|
||||||
import { resolvePreUploadedUrl } from "../../api/referenceUploadService";
|
import { resolvePreUploadedUrl } from "../../api/referenceUploadService";
|
||||||
|
|
||||||
@@ -82,7 +81,11 @@ export function fileToDataUrl(file: File) {
|
|||||||
return new Promise<string>((resolve, reject) => {
|
return new Promise<string>((resolve, reject) => {
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
reader.onload = () => {
|
reader.onload = () => {
|
||||||
typeof reader.result === "string" ? resolve(reader.result) : reject(new Error("Unable to read reference file"));
|
if (typeof reader.result === "string") {
|
||||||
|
resolve(reader.result);
|
||||||
|
} else {
|
||||||
|
reject(new Error("Unable to read reference file"));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
reader.onerror = () => reject(reader.error || new Error("Unable to read reference file"));
|
reader.onerror = () => reject(reader.error || new Error("Unable to read reference file"));
|
||||||
reader.readAsDataURL(file);
|
reader.readAsDataURL(file);
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { create } from "zustand";
|
import { create } from "zustand";
|
||||||
import type { WebGenerationPreviewTask } from "../types";
|
|
||||||
|
|
||||||
export type QueueItemStatus = "pending" | "running" | "completed" | "failed" | "cancelled";
|
export type QueueItemStatus = "pending" | "running" | "completed" | "failed" | "cancelled";
|
||||||
|
|
||||||
@@ -63,17 +62,6 @@ interface GenerationStoreState {
|
|||||||
clearTerminal: () => void;
|
clearTerminal: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
function hashUserId(): string {
|
|
||||||
try {
|
|
||||||
const raw = localStorage.getItem("omniai-web-session");
|
|
||||||
if (!raw) return "anon";
|
|
||||||
const parsed = JSON.parse(raw) as { user?: { id?: number | string } };
|
|
||||||
return String(parsed?.user?.id || "anon");
|
|
||||||
} catch {
|
|
||||||
return "anon";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const initialQueue = loadPersistedQueue();
|
const initialQueue = loadPersistedQueue();
|
||||||
|
|
||||||
export const useGenerationStore = create<GenerationStoreState>((set, get) => ({
|
export const useGenerationStore = create<GenerationStoreState>((set, get) => ({
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ interface ErrorReport {
|
|||||||
sessionId?: string;
|
sessionId?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
let reportQueue: ErrorReport[] = [];
|
const reportQueue: ErrorReport[] = [];
|
||||||
let flushTimer: ReturnType<typeof setTimeout> | null = null;
|
let flushTimer: ReturnType<typeof setTimeout> | null = null;
|
||||||
|
|
||||||
function getSessionId(): string | undefined {
|
function getSessionId(): string | undefined {
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ export function detectMentionTrigger(textBeforeCursor: string): MentionTriggerMa
|
|||||||
const query = textBeforeCursor.slice(atIdx + 1);
|
const query = textBeforeCursor.slice(atIdx + 1);
|
||||||
|
|
||||||
// Query must not contain spaces or punctuation that would break a mention token
|
// Query must not contain spaces or punctuation that would break a mention token
|
||||||
if (/[\s,。、;:!??!.,;:(){}\[\]<>""''《》【】@]/.test(query)) {
|
if (/[\s,。、;:!??!.,;:(){}[\]<>""''《》【】@]/.test(query)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user