Files
ttstd-infinite-canvas/web/src/router.tsx
地球观察员 dc2cc71058 feat(create): 创作首页与灵感库 - 纵深画廊、灵感页面、外部提示词池 (#708)
* feat(create): 创作首页与灵感库 - 纵深画廊、灵感页面、外部提示词池

创作首页空态换成直线纵深图片画廊:11 张横版卡片沿 Z 轴前后排队,当前一张
在最前最大,滚轮、左右按钮或自动轮播切换,卡片带玻璃描边和"行动点 / 标题 /
描述"三段信息,跟随输入框的创作类型联动换队。队列是随机取样并记住最近出现过的
条目,走完一轮自动换批,不再反复看到同样那几张。

左侧「创作」下方新增灵感页:1341 条提示词按来源、分类和创作类型筛选,
支持搜索、收藏,以及自建灵感的新建、编辑、删除和 JSON 导入导出。

新增 scripts/import-inspirations.mjs,从 awesome-seedance、好好学 AI 和
YouMind 两个姊妹库同步条目,并为画廊池生成本地 webp 缩略图(560px / q72,
约 35KB 一张)。上游封面平均 400KB~700KB,直接引用一屏 11 张要拉 5MB 以上,
换成本地缩略图后约 400KB。

画廊池改走 public/inspirations/gallery-pool.json 运行时拉取:几百条提示词正文
压在首屏 JS 上是白背的,而这份数据只服务画廊一处。缩略图等距抽取,横跨各来源
的全部分类;只收描述非空的条目,保证卡片三行版式不缺角。

* docs(create): 功能清单与地图 - 补充纵深画廊与灵感库

功能清单新增「创作首页纵深画廊与灵感库」一节,并在功能地图里记下画廊、
灵感页、运行时目录加载和导入脚本各自的代码入口;待测试清单记下浏览器验收结果
与仍未确认的边界(弱网退回、窄屏、文本条目偏少、外部来源署名)。
2026-10-08 19:55:04 +08:00

227 lines
14 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { lazy, Suspense, type ReactNode } from "react";
import { createBrowserRouter, Navigate, Outlet, useLocation } from "react-router";
import { RequireAuth } from "@/components/auth/require-auth";
import { FullScreenLoader, WorkspaceRouteLoader } from "@/components/ui/aceternity/full-screen-loader";
import { loadAssetsPage, loadCanvasPage, loadCanvasProjectPage, loadCreatePage, loadInspirationsPage, loadProjectDetailPage, loadProjectsPage } from "@/lib/workspace-route-modules";
import { CanvasRefreshShell } from "@/pages/canvas/canvas-refresh-shell";
import { AuthScene } from "@/pages/auth/auth-scene";
import RouteErrorPage from "@/pages/route-error";
import { isGuestWorkspacePath } from "@/lib/guest-workspace";
const AdminPage = lazy(() => import("@/pages/admin"));
const AnalyticsPage = lazy(() => import("@/pages/admin/admin-route-pages").then((module) => ({ default: module.AnalyticsPage })));
const AnnouncementsPage = lazy(() => import("@/pages/admin/admin-route-pages").then((module) => ({ default: module.AnnouncementsPage })));
const BannerAnnouncementsPage = lazy(() => import("@/pages/admin/admin-route-pages").then((module) => ({ default: module.BannerAnnouncementsPage })));
const StorageResourcesPage = lazy(() => import("@/pages/admin/admin-route-pages").then((module) => ({ default: module.StorageResourcesPage })));
const CreditOperationsPage = lazy(() => import("@/pages/admin/admin-route-pages").then((module) => ({ default: module.CreditOperationsPage })));
const AccessSettingsPage = lazy(() => import("@/pages/admin/admin-route-pages").then((module) => ({ default: module.AccessSettingsPage })));
const EmailSettingsPage = lazy(() => import("@/pages/admin/admin-route-pages").then((module) => ({ default: module.EmailSettingsPage })));
const FeatureAvailabilityPage = lazy(() => import("@/pages/admin/admin-route-pages").then((module) => ({ default: module.FeatureAvailabilityPage })));
const AgentLessonsPage = lazy(() => import("@/pages/admin/admin-route-pages").then((module) => ({ default: module.AgentLessonsPage })));
const ChannelsPage = lazy(() => import("@/pages/admin/channels/channels-page"));
const LogicalModelsPage = lazy(() => import("@/pages/admin/logical-models/logical-models-page"));
const AdminPluginsPage = lazy(() => import("@/pages/admin/plugins/plugins-page"));
const SkillCurationPage = lazy(() => import("@/pages/admin/skill-curation-page"));
const AdminPaymentsPage = lazy(() => import("@/pages/admin/payments/payments-page"));
const LogsPage = lazy(() => import("@/pages/admin/logs/logs-page"));
const RedemptionCodesPage = lazy(() => import("@/pages/admin/redemption-codes/redemption-codes-page"));
const RuntimePolicySettingsPage = lazy(() => import("@/pages/admin/settings/runtime-policy-settings-page"));
const AppearanceSettingsPage = lazy(() => import("@/pages/admin/settings/appearance-settings-page"));
const DrawingEngineSettingsPage = lazy(() => import("@/pages/admin/settings/drawing-engine-settings-page"));
const StorageSettingsPage = lazy(() => import("@/pages/admin/settings/storage-settings-page"));
const ArkPrivateAssetsSettingsPage = lazy(() => import("@/pages/admin/settings/ark-private-assets-settings-page"));
const ResponseInterceptionSettingsPage = lazy(() => import("@/pages/admin/settings/response-interception-settings-page"));
const ThirdPartySettingsPage = lazy(() => import("@/pages/admin/settings/libtv-settings-page"));
const SystemUpdatePage = lazy(() => import("@/pages/admin/settings/system-update-page"));
const SystemPerformancePage = lazy(() => import("@/pages/admin/settings/system-performance-page"));
const StoryboardPromptsPage = lazy(() => import("@/pages/admin/storyboard-prompts/storyboard-prompts-page"));
const UsersPage = lazy(() => import("@/pages/admin/users/users-page"));
const AssetsPage = lazy(loadAssetsPage);
const LoginPage = lazy(() => import("@/pages/auth/login"));
const RegisterPage = lazy(() => import("@/pages/auth/register"));
const ForgotPasswordPage = lazy(() => import("@/pages/auth/forgot-password"));
const CanvasPage = lazy(loadCanvasPage);
const CanvasProjectPage = lazy(loadCanvasProjectPage);
const SharedCanvasPage = lazy(() => import("@/pages/canvas/shared"));
const CreatePage = lazy(loadCreatePage);
const InspirationsPage = lazy(loadInspirationsPage);
const NotFound = lazy(() => import("@/pages/not-found"));
const SkillsPage = lazy(() => import("@/pages/skills"));
const PluginsPage = lazy(() => import("@/pages/plugins"));
const EagleLibraryPage = lazy(() => import("@/pages/plugins/eagle"));
const TasksPage = lazy(() => import("@/pages/tasks"));
const ProjectsPage = lazy(loadProjectsPage);
const ProjectDetailPage = lazy(loadProjectDetailPage);
const SettingsPage = lazy(() => import("@/pages/settings"));
const TestVoiceRecording = lazy(() => import("@/pages/test-voice-recording"));
const UserLayout = lazy(() => import("@/layouts/user-layout"));
const RequireFeature = lazy(() => import("@/components/auth/require-feature").then((module) => ({ default: module.RequireFeature })));
function deferred(element: ReactNode) {
return <Suspense fallback={<WorkspaceRouteLoader />}>{element}</Suspense>;
}
function fullScreenDeferred(element: ReactNode) {
return <Suspense fallback={<FullScreenLoader label="正在打开创作空间" detail="准备当前页面" />}>{element}</Suspense>;
}
function AuthenticatedWorkspaceLayout() {
const { pathname } = useLocation();
const isCanvasProjectRoute = pathname.startsWith("/canvas/");
const fallback = isCanvasProjectRoute ? <CanvasRefreshShell /> : <FullScreenLoader label="正在打开创作空间" detail="准备当前页面" />;
return <RequireAuth allowAnonymous={isGuestWorkspacePath(pathname)}><Suspense fallback={fallback}><UserLayout><Outlet /></UserLayout></Suspense></RequireAuth>;
}
/**
* DEV 专用实验室路由。
*
* lazy(() => import(...)) 写在函数体内,而不是模块顶层常量:
* 生产构建时 import.meta.env.DEV 被替换为 false,本函数随之不可达,
* 摇树会连同其中的动态 import 一起删除,实验室代码不进入生产依赖图。
* 若把 lazy 提到模块顶层,动态 import 会被静态分析成真实 chunk 并打进 dist。
*/
function devRoutes() {
const FolderPreviewLab = lazy(() => import("@/pages/dev/folder-preview-lab"));
const PrevisReproLab = lazy(() => import("@/pages/dev/previs-repro-lab"));
return [
{ path: "/dev/folders", element: fullScreenDeferred(<FolderPreviewLab />), errorElement: <RouteErrorPage /> },
{ path: "/dev/previs-repro", element: fullScreenDeferred(<PrevisReproLab />), errorElement: <RouteErrorPage /> },
];
}
export const router = createBrowserRouter([
{
element: <AuthScene />,
errorElement: <RouteErrorPage />,
children: [
{ path: "/login", element: fullScreenDeferred(<LoginPage />) },
{ path: "/register", element: fullScreenDeferred(<RegisterPage />) },
{ path: "/forgot-password", element: fullScreenDeferred(<ForgotPasswordPage />) },
],
},
{ path: "/share/canvas/:token", element: fullScreenDeferred(<SharedCanvasPage />), errorElement: <RouteErrorPage /> },
...(import.meta.env.DEV ? devRoutes() : []),
{
element: <AuthenticatedWorkspaceLayout />,
errorElement: <RouteErrorPage />,
children: [
{ path: "/", element: deferred(<CreatePage />) },
{ path: "/create", element: deferred(<CreatePage />) },
{ path: "/inspirations", element: deferred(<InspirationsPage />) },
{
path: "/tasks",
element: (
<RequireAuth>
<RequireFeature feature="taskCenterEnabled">{deferred(<TasksPage />)}</RequireFeature>
</RequireAuth>
),
},
{ path: "/assets", element: <RequireAuth>{deferred(<AssetsPage />)}</RequireAuth> },
{ path: "/skills", element: <RequireAuth>{deferred(<SkillsPage />)}</RequireAuth> },
{
path: "/plugins",
element: (
<RequireAuth>
<RequireFeature feature="pluginCenterEnabled">{deferred(<PluginsPage />)}</RequireFeature>
</RequireAuth>
),
},
{
path: "/plugins/eagle",
element: (
<RequireAuth>
<RequireFeature feature="pluginCenterEnabled">{deferred(<EagleLibraryPage />)}</RequireFeature>
</RequireAuth>
),
},
{
path: "/wallet",
element: <RequireAuth>{null}</RequireAuth>,
},
{ path: "/settings", element: <RequireAuth>{deferred(<SettingsPage />)}</RequireAuth> },
{ path: "/test-voice-recording", element: <RequireAuth>{deferred(<TestVoiceRecording />)}</RequireAuth> },
{
path: "/projects",
element: (
<RequireAuth>
<RequireFeature feature="shortDramaEnabled">{deferred(<ProjectsPage />)}</RequireFeature>
</RequireAuth>
),
},
{
path: "/projects/:projectId",
element: (
<RequireAuth>
<RequireFeature feature="shortDramaEnabled">{deferred(<ProjectDetailPage />)}</RequireFeature>
</RequireAuth>
),
},
{
path: "/projects/:projectId/:view",
element: (
<RequireAuth>
<RequireFeature feature="shortDramaEnabled">{deferred(<ProjectDetailPage />)}</RequireFeature>
</RequireAuth>
),
},
{
path: "/projects/:projectId/chapters/:chapterId",
element: (
<RequireAuth>
<RequireFeature feature="shortDramaEnabled">{deferred(<ProjectDetailPage />)}</RequireFeature>
</RequireAuth>
),
},
{
path: "/projects/:projectId/workflow/:unitId/:stage",
element: (
<RequireAuth>
<RequireFeature feature="shortDramaEnabled">{deferred(<ProjectDetailPage />)}</RequireFeature>
</RequireAuth>
),
},
{ path: "/canvas", element: <RequireAuth>{deferred(<CanvasPage />)}</RequireAuth> },
{ path: "/canvas/:id", element: <RequireAuth><CanvasProjectPage /></RequireAuth> },
{
path: "/admin",
element: <RequireAuth>{deferred(<AdminPage />)}</RequireAuth>,
children: [
{ index: true, element: <AnalyticsPage /> },
{ path: "users", element: <UsersPage /> },
{ path: "channels", element: <ChannelsPage /> },
{ path: "models", element: <RequireFeature feature="frontendModelsEnabled"><LogicalModelsPage /></RequireFeature> },
{ path: "plugins", element: <AdminPluginsPage /> },
{ path: "skill-curation", element: <SkillCurationPage /> },
{ path: "payments", element: <AdminPaymentsPage /> },
{ path: "prompt-templates", element: <StoryboardPromptsPage /> },
{ path: "storyboard-prompts", element: <Navigate to="/admin/prompt-templates" replace /> },
{ path: "announcements", element: <AnnouncementsPage /> },
{ path: "banner-announcements", element: <BannerAnnouncementsPage /> },
{ path: "agent-lessons", element: <AgentLessonsPage /> },
{ path: "resources", element: <StorageResourcesPage /> },
{ path: "credit-operations", element: <CreditOperationsPage /> },
{ path: "redemption-codes", element: <RedemptionCodesPage /> },
{ path: "logs", element: <LogsPage /> },
{ path: "settings", element: <Navigate to="runtime-policy" replace /> },
{ path: "settings/appearance", element: <AppearanceSettingsPage /> },
{ path: "settings/drawing-engine", element: <DrawingEngineSettingsPage /> },
{ path: "settings/concurrency", element: <Navigate to="/admin/settings/runtime-policy" replace /> },
{ path: "settings/runtime-policy", element: <RuntimePolicySettingsPage /> },
{ path: "settings/features", element: <FeatureAvailabilityPage /> },
{ path: "settings/access", element: <AccessSettingsPage /> },
{ path: "settings/email", element: <EmailSettingsPage /> },
{ path: "settings/storage", element: <StorageSettingsPage /> },
{ path: "settings/ark-private-assets", element: <ArkPrivateAssetsSettingsPage /> },
{ path: "settings/response-interception", element: <ResponseInterceptionSettingsPage /> },
{ path: "settings/third-party", element: <ThirdPartySettingsPage /> },
{ path: "settings/system-update", element: <SystemUpdatePage /> },
{ path: "settings/system-performance", element: <SystemPerformancePage /> },
{ path: "settings/libtv", element: <Navigate to="/admin/settings/third-party" replace /> },
],
},
],
},
{ path: "*", element: fullScreenDeferred(<NotFound />) },
]);