Merge origin/master into feat/dialog-generator-cancel-generation
This commit is contained in:
+187
-80
@@ -1,20 +1,5 @@
|
||||
import {
|
||||
BarChartOutlined,
|
||||
BranchesOutlined,
|
||||
CustomerServiceOutlined,
|
||||
DeleteOutlined,
|
||||
FolderOpenOutlined,
|
||||
GlobalOutlined,
|
||||
HeartOutlined,
|
||||
HomeOutlined,
|
||||
LayoutOutlined,
|
||||
RobotOutlined,
|
||||
ShoppingOutlined,
|
||||
SwapOutlined,
|
||||
ToolOutlined,
|
||||
WalletOutlined,
|
||||
} from "@ant-design/icons";
|
||||
import { lazy, Suspense, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import { useShallow } from "zustand/react/shallow";
|
||||
import ErrorBoundary from "./components/ErrorBoundary";
|
||||
import { reportError } from "./utils/errorReporting";
|
||||
import { initNotificationPermission } from "./utils/generationNotifier";
|
||||
@@ -36,6 +21,7 @@ import { webGenerationGateway, type CreatePreviewTaskInput } from "./api/webGene
|
||||
import { translateTaskError } from "./utils/translateTaskError";
|
||||
import { recoverAndResumeTasks } from "./services/backgroundTaskRunner";
|
||||
import AppShell from "./components/AppShell";
|
||||
import { ShellIcon } from "./components/ShellIcon";
|
||||
const NotFoundPage = lazy(() => import("./components/NotFoundPage"));
|
||||
const CompliancePage = lazy(() => import("./features/compliance/CompliancePage"));
|
||||
import { cloneWorkflow, createBlankWorkflow } from "./data/workflows";
|
||||
@@ -50,6 +36,7 @@ const AvatarConsolePage = lazy(() => import("./features/digital-human/AvatarCons
|
||||
const DigitalHumanPage = lazy(() => import("./features/digital-human/DigitalHumanPage"));
|
||||
const DialogGeneratorPage = lazy(() => import("./features/dialog-generator/DialogGeneratorPage"));
|
||||
const EcommercePage = lazy(() => import("./features/ecommerce/EcommercePage"));
|
||||
const EcommerceTemplatesPage = lazy(() => import("./features/ecommerce/EcommerceTemplatesPage"));
|
||||
const HomePage = lazy(() => import("./features/home/HomePage"));
|
||||
const ImageWorkbenchPage = lazy(() => import("./features/image-workbench/ImageWorkbenchPage"));
|
||||
const MorePage = lazy(() => import("./features/more/MorePage"));
|
||||
@@ -60,6 +47,7 @@ const ResolutionUpscalePage = lazy(() => import("./features/resolution-upscale/R
|
||||
const WatermarkRemovalPage = lazy(() => import("./features/watermark-removal/WatermarkRemovalPage"));
|
||||
const SubtitleRemovalPage = lazy(() => import("./features/subtitle-removal/SubtitleRemovalPage"));
|
||||
const ScriptTokensPage = lazy(() => import("./features/script-tokens/ScriptTokensPage"));
|
||||
const SizeTemplatePage = lazy(() => import("./features/size-template/SizeTemplatePage"));
|
||||
const TokenUsagePage = lazy(() => import("./features/script-tokens/TokenUsagePage"));
|
||||
const WorkbenchPage = lazy(() => import("./features/workbench/WorkbenchPage"));
|
||||
import type { WorkbenchResultActionPayload } from "./features/workbench/WorkbenchPage";
|
||||
@@ -105,6 +93,8 @@ const VIEW_KEYS = new Set<WebViewKey>([
|
||||
"assets",
|
||||
"ecommerceHub",
|
||||
"ecommerce",
|
||||
"ecommerceTemplates",
|
||||
"sizeTemplate",
|
||||
"scriptTokens",
|
||||
"tokenUsage",
|
||||
"imageWorkbench",
|
||||
@@ -126,6 +116,29 @@ const VIEW_KEYS = new Set<WebViewKey>([
|
||||
]);
|
||||
|
||||
const PUBLIC_VIEW_SET = new Set<WebViewKey>(["home", "login", "community", "more", "dialogGenerator", "userAgreement", "privacyPolicy", "not-found"]);
|
||||
const LEGACY_PAGE_STYLE_VIEWS = new Set<WebViewKey>([
|
||||
"login",
|
||||
"workbench",
|
||||
"canvas",
|
||||
"community",
|
||||
"communityReview",
|
||||
"communityCaseAdd",
|
||||
"assets",
|
||||
"ecommerce",
|
||||
"ecommerceHub",
|
||||
"ecommerceTemplates",
|
||||
"sizeTemplate",
|
||||
"digitalHuman",
|
||||
"characterMix",
|
||||
"more",
|
||||
]);
|
||||
|
||||
let legacyPageStylesPromise: Promise<unknown> | null = null;
|
||||
|
||||
function loadLegacyPageStyles(): Promise<unknown> {
|
||||
legacyPageStylesPromise ??= import("./styles/pages/legacy-pages.css");
|
||||
return legacyPageStylesPromise;
|
||||
}
|
||||
|
||||
function normalizeViewKey(rawView: string): WebViewKey {
|
||||
const normalized =
|
||||
@@ -133,6 +146,8 @@ function normalizeViewKey(rawView: string): WebViewKey {
|
||||
? "login"
|
||||
: rawView === "ecommerceHub"
|
||||
? "ecommerce"
|
||||
: rawView === "bug-feedback" || rawView === "feedback"
|
||||
? "report"
|
||||
: rawView === "terms" || rawView === "agreement" || rawView === "user-agreement"
|
||||
? "userAgreement"
|
||||
: rawView === "privacy" || rawView === "privacy-policy"
|
||||
@@ -233,62 +248,124 @@ function App() {
|
||||
const canvasAutoOpenedRecentRef = useRef(false);
|
||||
|
||||
// Session store
|
||||
const session = useSessionStore((s) => s.session);
|
||||
const loginPromptOpen = useSessionStore((s) => s.loginPromptOpen);
|
||||
const pendingAction = useSessionStore((s) => s.pendingAction);
|
||||
const sessionReplacedOpen = useSessionStore((s) => s.sessionReplacedOpen);
|
||||
const sessionReplacedMessage = useSessionStore((s) => s.sessionReplacedMessage);
|
||||
const setSession = useSessionStore((s) => s.setSession);
|
||||
const openLoginPrompt = useSessionStore((s) => s.openLoginPrompt);
|
||||
const closeLoginPrompt = useSessionStore((s) => s.closeLoginPrompt);
|
||||
const showSessionReplaced = useSessionStore((s) => s.showSessionReplaced);
|
||||
const hideSessionReplaced = useSessionStore((s) => s.hideSessionReplaced);
|
||||
const clearSessionState = useSessionStore((s) => s.clearSession);
|
||||
const {
|
||||
session,
|
||||
loginPromptOpen,
|
||||
pendingAction,
|
||||
sessionReplacedOpen,
|
||||
sessionReplacedMessage,
|
||||
setSession,
|
||||
openLoginPrompt,
|
||||
closeLoginPrompt,
|
||||
showSessionReplaced,
|
||||
hideSessionReplaced,
|
||||
clearSession: clearSessionState,
|
||||
} = useSessionStore(useShallow((s) => ({
|
||||
session: s.session,
|
||||
loginPromptOpen: s.loginPromptOpen,
|
||||
pendingAction: s.pendingAction,
|
||||
sessionReplacedOpen: s.sessionReplacedOpen,
|
||||
sessionReplacedMessage: s.sessionReplacedMessage,
|
||||
setSession: s.setSession,
|
||||
openLoginPrompt: s.openLoginPrompt,
|
||||
closeLoginPrompt: s.closeLoginPrompt,
|
||||
showSessionReplaced: s.showSessionReplaced,
|
||||
hideSessionReplaced: s.hideSessionReplaced,
|
||||
clearSession: s.clearSession,
|
||||
})));
|
||||
|
||||
// Project store
|
||||
const projects = useProjectStore((s) => s.projects);
|
||||
const projectsLoaded = useProjectStore((s) => s.projectsLoaded);
|
||||
const canvasWorkflow = useProjectStore((s) => s.canvasWorkflow);
|
||||
const currentCanvasProjectId = useProjectStore((s) => s.currentCanvasProjectId);
|
||||
const pendingDeleteProject = useProjectStore((s) => s.pendingDeleteProject);
|
||||
const deleteProjectSubmitting = useProjectStore((s) => s.deleteProjectSubmitting);
|
||||
const setProjects = useProjectStore((s) => s.setProjects);
|
||||
const setProjectsLoaded = useProjectStore((s) => s.setProjectsLoaded);
|
||||
const setCanvasWorkflow = useProjectStore((s) => s.setCanvasWorkflow);
|
||||
const setCurrentCanvasProjectId = useProjectStore((s) => s.setCurrentCanvasProjectId);
|
||||
const openDeleteProjectModal = useProjectStore((s) => s.openDeleteProject);
|
||||
const closeDeleteProjectModal = useProjectStore((s) => s.closeDeleteProject);
|
||||
const setDeleteProjectSubmitting = useProjectStore((s) => s.setDeleteProjectSubmitting);
|
||||
const clearProjectState = useProjectStore((s) => s.clearProjectState);
|
||||
const {
|
||||
projects,
|
||||
projectsLoaded,
|
||||
canvasWorkflow,
|
||||
currentCanvasProjectId,
|
||||
pendingDeleteProject,
|
||||
deleteProjectSubmitting,
|
||||
setProjects,
|
||||
setProjectsLoaded,
|
||||
setCanvasWorkflow,
|
||||
setCurrentCanvasProjectId,
|
||||
openDeleteProject: openDeleteProjectModal,
|
||||
closeDeleteProject: closeDeleteProjectModal,
|
||||
setDeleteProjectSubmitting,
|
||||
clearProjectState,
|
||||
} = useProjectStore(useShallow((s) => ({
|
||||
projects: s.projects,
|
||||
projectsLoaded: s.projectsLoaded,
|
||||
canvasWorkflow: s.canvasWorkflow,
|
||||
currentCanvasProjectId: s.currentCanvasProjectId,
|
||||
pendingDeleteProject: s.pendingDeleteProject,
|
||||
deleteProjectSubmitting: s.deleteProjectSubmitting,
|
||||
setProjects: s.setProjects,
|
||||
setProjectsLoaded: s.setProjectsLoaded,
|
||||
setCanvasWorkflow: s.setCanvasWorkflow,
|
||||
setCurrentCanvasProjectId: s.setCurrentCanvasProjectId,
|
||||
openDeleteProject: s.openDeleteProject,
|
||||
closeDeleteProject: s.closeDeleteProject,
|
||||
setDeleteProjectSubmitting: s.setDeleteProjectSubmitting,
|
||||
clearProjectState: s.clearProjectState,
|
||||
})));
|
||||
|
||||
// Task store
|
||||
const tasks = useTaskStore((s) => s.tasks);
|
||||
const setTasks = useTaskStore((s) => s.setTasks);
|
||||
const appendTask = useTaskStore((s) => s.appendTask);
|
||||
const mergeServerTasks = useTaskStore((s) => s.mergeServerTasks);
|
||||
const clearTasks = useTaskStore((s) => s.clearTasks);
|
||||
const {
|
||||
tasks,
|
||||
setTasks,
|
||||
appendTask,
|
||||
mergeServerTasks,
|
||||
clearTasks,
|
||||
} = useTaskStore(useShallow((s) => ({
|
||||
tasks: s.tasks,
|
||||
setTasks: s.setTasks,
|
||||
appendTask: s.appendTask,
|
||||
mergeServerTasks: s.mergeServerTasks,
|
||||
clearTasks: s.clearTasks,
|
||||
})));
|
||||
|
||||
// App store
|
||||
const usage = useAppStore((s) => s.usage);
|
||||
const runtimeNotifications = useAppStore((s) => s.runtimeNotifications);
|
||||
const serverNotifications = useAppStore((s) => s.serverNotifications);
|
||||
const activeView = useAppStore((s) => s.activeView);
|
||||
const workspaceExpanded = useAppStore((s) => s.workspaceExpanded);
|
||||
const imageWorkbenchTool = useAppStore((s) => s.imageWorkbenchTool);
|
||||
const pendingEcommerceTemplate = useAppStore((s) => s.pendingEcommerceTemplate);
|
||||
const backendHealth = useAppStore((s) => s.backendHealth);
|
||||
const setUsage = useAppStore((s) => s.setUsage);
|
||||
const pushNotification = useAppStore((s) => s.pushNotification);
|
||||
const setRuntimeNotifications = useAppStore((s) => s.setRuntimeNotifications);
|
||||
const setServerNotifications = useAppStore((s) => s.setServerNotifications);
|
||||
const setView = useAppStore((s) => s.setView);
|
||||
const setWorkspaceExpanded = useAppStore((s) => s.setWorkspaceExpanded);
|
||||
const setImageWorkbenchTool = useAppStore((s) => s.setImageWorkbenchTool);
|
||||
const setPendingEcommerceTemplate = useAppStore((s) => s.setPendingEcommerceTemplate);
|
||||
const setBackendHealth = useAppStore((s) => s.setBackendHealth);
|
||||
const markNotificationRead = useAppStore((s) => s.markNotificationRead);
|
||||
const markAllNotificationsRead = useAppStore((s) => s.markAllNotificationsRead);
|
||||
const clearAppState = useAppStore((s) => s.clearAppState);
|
||||
const {
|
||||
usage,
|
||||
runtimeNotifications,
|
||||
serverNotifications,
|
||||
activeView,
|
||||
workspaceExpanded,
|
||||
imageWorkbenchTool,
|
||||
pendingEcommerceTemplate,
|
||||
backendHealth,
|
||||
setUsage,
|
||||
pushNotification,
|
||||
setRuntimeNotifications,
|
||||
setServerNotifications,
|
||||
setView,
|
||||
setWorkspaceExpanded,
|
||||
setImageWorkbenchTool,
|
||||
setPendingEcommerceTemplate,
|
||||
setBackendHealth,
|
||||
markNotificationRead,
|
||||
markAllNotificationsRead,
|
||||
clearAppState,
|
||||
} = useAppStore(useShallow((s) => ({
|
||||
usage: s.usage,
|
||||
runtimeNotifications: s.runtimeNotifications,
|
||||
serverNotifications: s.serverNotifications,
|
||||
activeView: s.activeView,
|
||||
workspaceExpanded: s.workspaceExpanded,
|
||||
imageWorkbenchTool: s.imageWorkbenchTool,
|
||||
pendingEcommerceTemplate: s.pendingEcommerceTemplate,
|
||||
backendHealth: s.backendHealth,
|
||||
setUsage: s.setUsage,
|
||||
pushNotification: s.pushNotification,
|
||||
setRuntimeNotifications: s.setRuntimeNotifications,
|
||||
setServerNotifications: s.setServerNotifications,
|
||||
setView: s.setView,
|
||||
setWorkspaceExpanded: s.setWorkspaceExpanded,
|
||||
setImageWorkbenchTool: s.setImageWorkbenchTool,
|
||||
setPendingEcommerceTemplate: s.setPendingEcommerceTemplate,
|
||||
setBackendHealth: s.setBackendHealth,
|
||||
markNotificationRead: s.markNotificationRead,
|
||||
markAllNotificationsRead: s.markAllNotificationsRead,
|
||||
clearAppState: s.clearAppState,
|
||||
})));
|
||||
|
||||
const [ecommerceEverMounted, setEcommerceEverMounted] = useState(false);
|
||||
const isEcommerceActive = activeView === "ecommerce" || activeView === "ecommerceHub";
|
||||
@@ -296,6 +373,12 @@ function App() {
|
||||
if (isEcommerceActive && !ecommerceEverMounted) setEcommerceEverMounted(true);
|
||||
}, [isEcommerceActive]); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
|
||||
useEffect(() => {
|
||||
if (LEGACY_PAGE_STYLE_VIEWS.has(activeView) || ecommerceEverMounted) {
|
||||
void loadLegacyPageStyles();
|
||||
}
|
||||
}, [activeView, ecommerceEverMounted]);
|
||||
|
||||
// Dismiss boot splash after first render
|
||||
useEffect(() => {
|
||||
const splash = document.getElementById("app-boot-splash");
|
||||
@@ -347,24 +430,24 @@ function App() {
|
||||
|
||||
const navItems = useMemo<WebNavItem[]>(
|
||||
() => [
|
||||
{ key: "home", label: "首页", hint: "项目入口", icon: <HomeOutlined /> },
|
||||
{ key: "workbench", label: "生成", hint: "对话生成页面", icon: <RobotOutlined /> },
|
||||
{ key: "home", label: "首页", hint: "项目入口", icon: <ShellIcon name="home" /> },
|
||||
{ key: "workbench", label: "生成", hint: "对话生成页面", icon: <ShellIcon name="robot" /> },
|
||||
{
|
||||
key: "ecommerce",
|
||||
label: "电商生成",
|
||||
hint: "AI创作与海报生成",
|
||||
icon: <ShoppingOutlined />,
|
||||
icon: <ShellIcon name="shopping" />,
|
||||
},
|
||||
{ key: "canvas", label: "画布", hint: "进入自由画布编排", icon: <BranchesOutlined /> },
|
||||
{ key: "community", label: "社区", hint: "案例分享与导入", icon: <GlobalOutlined /> },
|
||||
{ key: "scriptTokens", label: "剧本评分", hint: "剧本评分系统", icon: <BarChartOutlined /> },
|
||||
{ key: "tokenUsage", label: "Token消耗", hint: "成员、服务与调用记录", icon: <WalletOutlined /> },
|
||||
{ key: "providerHealth", label: "服务商健康", hint: "AI 服务商状态与监控", icon: <HeartOutlined /> },
|
||||
{ key: "assets", label: "资产库", hint: "角色、场景、道具", icon: <FolderOpenOutlined /> },
|
||||
{ key: "agent", label: "Agent", hint: "拆解与规划", icon: <RobotOutlined /> },
|
||||
{ key: "digitalHuman", label: "数字人", hint: "口播与人像生成", icon: <CustomerServiceOutlined /> },
|
||||
{ key: "characterMix", label: "角色迁移", hint: "人物视频迁移", icon: <SwapOutlined /> },
|
||||
{ key: "more", label: "工具盒", hint: "图像与镜头工具", icon: <ToolOutlined /> },
|
||||
{ key: "canvas", label: "画布", hint: "进入自由画布编排", icon: <ShellIcon name="branches" /> },
|
||||
{ key: "community", label: "社区", hint: "案例分享与导入", icon: <ShellIcon name="global" /> },
|
||||
{ key: "scriptTokens", label: "剧本评分", hint: "剧本评分系统", icon: <ShellIcon name="bar-chart" /> },
|
||||
{ key: "tokenUsage", label: "Token消耗", hint: "成员、服务与调用记录", icon: <ShellIcon name="wallet" /> },
|
||||
{ key: "providerHealth", label: "服务商健康", hint: "AI 服务商状态与监控", icon: <ShellIcon name="heart" /> },
|
||||
{ key: "assets", label: "资产库", hint: "角色、场景、道具", icon: <ShellIcon name="folder" /> },
|
||||
{ key: "agent", label: "Agent", hint: "拆解与规划", icon: <ShellIcon name="robot" /> },
|
||||
{ key: "digitalHuman", label: "数字人", hint: "口播与人像生成", icon: <ShellIcon name="customer-service" /> },
|
||||
{ key: "characterMix", label: "角色迁移", hint: "人物视频迁移", icon: <ShellIcon name="swap" /> },
|
||||
{ key: "more", label: "工具盒", hint: "图像与镜头工具", icon: <ShellIcon name="tool" /> },
|
||||
],
|
||||
[],
|
||||
);
|
||||
@@ -1090,6 +1173,30 @@ function App() {
|
||||
case "ecommerce":
|
||||
case "ecommerceHub":
|
||||
return null;
|
||||
case "ecommerceTemplates":
|
||||
return (
|
||||
<EcommerceTemplatesPage
|
||||
projects={projects}
|
||||
onOpenMore={() => handleSetView("more")}
|
||||
onOpenEcommerce={() => handleSetView("ecommerce")}
|
||||
onSelectTemplate={(template) => {
|
||||
setPendingEcommerceTemplate(template);
|
||||
handleSetView("ecommerce");
|
||||
}}
|
||||
onStartCreate={handleStartTemplateCanvasCreate}
|
||||
onOpenProject={handleOpenProject}
|
||||
onDeleteProject={handleDeleteProject}
|
||||
/>
|
||||
);
|
||||
case "sizeTemplate":
|
||||
return (
|
||||
<SizeTemplatePage
|
||||
isAuthenticated={Boolean(session)}
|
||||
onOpenMore={() => handleSetView("more")}
|
||||
onOpenEcommerce={() => handleSetView("ecommerce")}
|
||||
onSelectView={handleSetView}
|
||||
/>
|
||||
);
|
||||
case "digitalHuman":
|
||||
return (
|
||||
<DigitalHumanPage
|
||||
@@ -1339,7 +1446,7 @@ function App() {
|
||||
/>
|
||||
<section className="project-delete-modal__panel">
|
||||
<span className="project-delete-modal__icon">
|
||||
<DeleteOutlined />
|
||||
<ShellIcon name="delete" />
|
||||
</span>
|
||||
<h2 id="project-delete-title">删除项目</h2>
|
||||
<p>确认删除项目「{pendingDeleteProject.name}」?删除后将从服务器项目列表移除。</p>
|
||||
|
||||
Reference in New Issue
Block a user