- {expanded ? (
-
-
-
-
{plugin.manifest.name} 设置
-
只展示这个插件实际支持的配置项。
-
-
{plugin.manifest.trusted ? "可信插件" : "第三方插件"}
+ {settingsPlugin.manifest.id === EAGLE_PLUGIN_ID ? (
+ <>
+
+
+
+
setEagleBaseUrl(event.target.value)} placeholder="http://localhost:41595" />
+
Eagle 必须在本机运行;影策通过插件直接读取和写入 Eagle 原始文件。
-
- {plugin.manifest.id === EAGLE_PLUGIN_ID ? (
- <>
-
-
-
-
setEagleBaseUrl(event.target.value)} placeholder="http://localhost:41595" className="mt-2 w-full" />
-
Eagle 必须在本机运行;影策通过插件直接读取和写入 Eagle 原始文件。
-
-
-
-
-
-
-
图片、视频和音频生成成功后,自动写入 Eagle;影策本地素材仍会保留。
-
-
-
-
-
-
-
+
+
+
+
-
-
} onClick={saveEagleConfig}>保存配置
-
} disabled={!enabled} onClick={() => navigate("/plugins/eagle")}>打开 Eagle 素材库
-
} href="https://api.eagle.cool/" target="_blank">查看 API
+
图片、视频和音频生成成功后,自动写入 Eagle;影策本地素材仍会保留。
+
+
+
+
+
- >
- ) : (
-
该插件暂无可编辑设置项。当前接入位置和权限会根据插件清单自动生效。
- )}
-
-
-
接入位置{plugin.manifest.surfaces.map((surface) => surfaceLabels[surface] ?? surface).join("、")}
-
插件能力{plugin.manifest.permissions.map((permission) => permissionLabels[permission] ?? permission).join("、")}
+
- ) : null}
+
+ } onClick={saveEagleConfig}>保存配置
+ } disabled={!settingsEnabled} onClick={() => navigate("/plugins/eagle")}>打开 Eagle 素材库
+ } href="https://api.eagle.cool/" target="_blank">查看 API
+
+ >
+ ) : (
+
该插件暂无可编辑设置项。当前接入位置和权限会根据插件清单自动生效。
+ )}
- {installation?.lastError ?
{installation.lastError} : null}
-
- );
- })}
-
+
+
接入位置{settingsPlugin.manifest.surfaces.map((surface) => surfaceLabels[surface] ?? surface).join("、")}
+
插件能力{settingsPlugin.manifest.permissions.map((permission) => permissionLabels[permission] ?? permission).join("、")}
+
+
+ ) : null}
+
);
}
+function formatPluginDate(value?: string) {
+ if (!value) return "未记录";
+ const timestamp = Date.parse(value);
+ return Number.isFinite(timestamp) ? pluginDateFormatter.format(timestamp) : "未记录";
+}
+
function eagleFolderOptions(folders: EagleFolder[]) {
const byId = new Map(folders.map((folder) => [folder.id, folder]));
const pathFor = (folder: EagleFolder) => {
diff --git a/web/src/pages/plugins/plugins.css b/web/src/pages/plugins/plugins.css
new file mode 100644
index 000000000..206458ae9
--- /dev/null
+++ b/web/src/pages/plugins/plugins.css
@@ -0,0 +1,385 @@
+.plugins-page {
+ --plugins-card-radius: var(--r-xl);
+}
+
+.plugins-page-content {
+ width: min(100%, 1440px);
+ margin: 0 auto;
+}
+
+.plugin-trust-label {
+ display: inline-flex;
+ align-items: center;
+ gap: var(--space-1);
+ color: var(--status-success);
+ font-weight: 600;
+}
+
+.plugins-page-content {
+ padding: var(--space-5) var(--space-6) var(--space-8);
+}
+
+.plugins-toolbar {
+ display: grid;
+ grid-template-columns: minmax(240px, 1fr) repeat(3, minmax(132px, 0.24fr)) auto;
+ align-items: center;
+ gap: var(--space-2);
+}
+
+.plugins-search,
+.plugins-filter {
+ min-width: 0;
+}
+
+.plugins-search.ant-input-affix-wrapper,
+.plugins-filter.ant-select .ant-select-selector {
+ min-height: 38px;
+ border-color: var(--workspace-border);
+ border-radius: var(--r-lg);
+ background: color-mix(in srgb, var(--workspace-surface-strong) 84%, transparent);
+}
+
+.plugins-search.ant-input-affix-wrapper:hover,
+.plugins-search.ant-input-affix-wrapper:focus-within,
+.plugins-filter.ant-select:hover .ant-select-selector,
+.plugins-filter.ant-select-focused .ant-select-selector {
+ border-color: color-mix(in srgb, var(--workspace-accent) 48%, var(--workspace-border));
+}
+
+.plugins-filter-icon {
+ display: grid;
+ width: 38px;
+ height: 38px;
+ place-items: center;
+ border-radius: var(--r-lg);
+ background: color-mix(in srgb, var(--foreground) 5%, transparent);
+ color: color-mix(in srgb, var(--foreground) 48%, transparent);
+}
+
+.plugins-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
+ align-items: stretch;
+ gap: var(--space-4);
+ margin-top: var(--space-5);
+}
+
+.plugin-card {
+ display: flex;
+ min-width: 0;
+ min-height: 286px;
+ flex-direction: column;
+ padding: var(--space-4);
+ border-radius: var(--plugins-card-radius);
+ transition: transform var(--motion-state) var(--motion-ease-out), background-color var(--motion-state) ease-out;
+}
+
+.plugin-card:hover,
+.plugin-card:focus-within {
+ transform: translateY(var(--library-hover-offset));
+}
+
+.plugin-card-main {
+ min-width: 0;
+ flex: 1 1 auto;
+}
+
+.plugin-card-heading {
+ display: flex;
+ min-width: 0;
+ align-items: flex-start;
+ gap: var(--space-3);
+}
+
+.plugin-icon-tile {
+ display: grid;
+ width: 48px;
+ height: 48px;
+ flex: 0 0 auto;
+ place-items: center;
+ border-radius: var(--r-lg);
+ background: color-mix(in srgb, var(--foreground) 7%, transparent);
+ color: color-mix(in srgb, var(--foreground) 65%, transparent);
+ transition: background-color var(--motion-state) ease-out, color var(--motion-state) ease-out, transform var(--motion-state) var(--motion-ease-out);
+}
+
+.plugin-card:hover .plugin-icon-tile,
+.plugin-card:focus-within .plugin-icon-tile {
+ transform: translateY(-2px);
+ background: color-mix(in srgb, var(--foreground) 10%, transparent);
+ color: var(--foreground);
+}
+
+.plugin-icon-tile.is-trusted {
+ background: color-mix(in srgb, var(--status-success) 14%, transparent);
+ color: var(--status-success);
+}
+
+.plugin-card-title-row {
+ display: flex;
+ min-width: 0;
+ align-items: center;
+ gap: var(--space-2);
+}
+
+.plugin-card-title-row h3 {
+ min-width: 0;
+ overflow: hidden;
+ color: var(--foreground);
+ font-size: var(--fs-body-lg);
+ font-weight: 650;
+ line-height: 1.35;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.plugin-version {
+ flex: 0 0 auto;
+ color: color-mix(in srgb, var(--foreground) 40%, transparent);
+ font-size: var(--fs-tiny);
+ font-variant-numeric: tabular-nums;
+}
+
+.plugin-card-labels,
+.plugin-card-tags,
+.plugin-card-meta {
+ display: flex;
+ min-width: 0;
+ flex-wrap: wrap;
+ align-items: center;
+}
+
+.plugin-card-labels {
+ gap: var(--space-2);
+ margin-top: var(--space-1);
+}
+
+.plugin-category-label,
+.plugin-card-tags span {
+ display: inline-flex;
+ align-items: center;
+ gap: var(--space-1);
+ border-radius: var(--r-sm);
+ background: color-mix(in srgb, var(--foreground) 6%, transparent);
+ padding: var(--space-half) var(--space-2);
+ color: color-mix(in srgb, var(--foreground) 50%, transparent);
+ font-size: var(--fs-micro);
+ line-height: 1.4;
+}
+
+.plugin-trust-label {
+ font-size: var(--fs-micro);
+}
+
+.plugin-card-description {
+ display: -webkit-box;
+ min-height: 3.2em;
+ margin-top: var(--space-4);
+ overflow: hidden;
+ color: color-mix(in srgb, var(--foreground) 52%, transparent);
+ font-size: var(--fs-caption);
+ line-height: 1.6;
+ -webkit-box-orient: vertical;
+ -webkit-line-clamp: 2;
+}
+
+.plugin-card-meta {
+ gap: var(--space-3);
+ margin-top: var(--space-4);
+ color: color-mix(in srgb, var(--foreground) 43%, transparent);
+ font-size: var(--fs-label);
+}
+
+.plugin-card-meta span {
+ display: inline-flex;
+ align-items: center;
+ gap: var(--space-1);
+ white-space: nowrap;
+}
+
+.plugin-card-tags {
+ gap: var(--space-2);
+ margin-top: var(--space-3);
+}
+
+.plugin-card-actions {
+ display: flex;
+ min-width: 0;
+ align-items: center;
+ gap: var(--space-2);
+ margin-top: var(--space-5);
+ padding-top: var(--space-3);
+ border-top: 1px solid color-mix(in srgb, var(--workspace-border) 72%, transparent);
+}
+
+.plugin-card-actions .settings-channel-status {
+ margin-right: auto;
+}
+
+.plugin-settings-button {
+ flex: 0 0 auto;
+}
+
+.plugin-settings-panel {
+ margin-top: var(--space-4);
+ padding-top: var(--space-4);
+ border-top: 1px solid var(--workspace-border);
+}
+
+.plugin-settings-modal-panel {
+ margin-top: 0;
+ padding-top: 0;
+ border-top: 0;
+}
+
+.plugin-settings-heading,
+.plugin-setting-label-row {
+ display: flex;
+ align-items: flex-start;
+ justify-content: space-between;
+ gap: var(--space-3);
+}
+
+.plugin-settings-heading h4 {
+ color: var(--foreground);
+ font-size: var(--fs-body);
+ font-weight: 650;
+}
+
+.plugin-settings-heading p,
+.plugin-settings-fields p {
+ margin-top: var(--space-1);
+ color: color-mix(in srgb, var(--foreground) 44%, transparent);
+ font-size: var(--fs-label);
+ line-height: 1.55;
+}
+
+.plugin-settings-fields {
+ display: grid;
+ gap: var(--space-4);
+ margin-top: var(--space-4);
+}
+
+.plugin-settings-fields label {
+ display: block;
+ color: color-mix(in srgb, var(--foreground) 62%, transparent);
+ font-size: var(--fs-label);
+ font-weight: 600;
+}
+
+.plugin-settings-fields .ant-input,
+.plugin-settings-fields .ant-select {
+ width: 100%;
+ margin-top: var(--space-2);
+}
+
+.plugin-setting-label-row label {
+ margin-top: var(--space-half);
+}
+
+.plugin-setting-label-row .ant-btn-link {
+ padding: 0;
+}
+
+.plugin-settings-actions {
+ display: flex;
+ flex-wrap: wrap;
+ gap: var(--space-2);
+ margin-top: var(--space-4);
+}
+
+.plugin-settings-empty {
+ margin-top: var(--space-4);
+ border-radius: var(--r-lg);
+ background: color-mix(in srgb, var(--foreground) 4%, transparent);
+ padding: var(--space-3);
+ color: color-mix(in srgb, var(--foreground) 52%, transparent);
+ font-size: var(--fs-caption);
+}
+
+.plugin-permissions {
+ display: grid;
+ gap: var(--space-2);
+ margin-top: var(--space-4);
+ color: color-mix(in srgb, var(--foreground) 52%, transparent);
+ font-size: var(--fs-label);
+ line-height: 1.6;
+}
+
+.plugin-permissions span {
+ display: inline-block;
+ margin-right: var(--space-2);
+ color: color-mix(in srgb, var(--foreground) 36%, transparent);
+}
+
+.plugin-error {
+ display: block;
+ margin-top: var(--space-3);
+ font-size: var(--fs-label);
+}
+
+.plugins-empty-state {
+ display: grid;
+ min-height: 260px;
+ place-items: center;
+ align-content: center;
+ gap: var(--space-2);
+ border-radius: var(--plugins-card-radius);
+ background: color-mix(in srgb, var(--foreground) 3%, transparent);
+ color: color-mix(in srgb, var(--foreground) 45%, transparent);
+ text-align: center;
+}
+
+.plugins-empty-state h3 {
+ margin-top: var(--space-2);
+ color: color-mix(in srgb, var(--foreground) 78%, transparent);
+ font-size: var(--fs-body-lg);
+ font-weight: 650;
+}
+
+.plugins-empty-state p {
+ font-size: var(--fs-caption);
+}
+
+@media (max-width: 900px) {
+ .plugins-toolbar {
+ grid-template-columns: minmax(0, 1fr) repeat(3, minmax(120px, 1fr));
+ }
+
+ .plugins-search {
+ grid-column: 1 / -1;
+ }
+
+ .plugins-filter-icon {
+ display: none;
+ }
+}
+
+@media (max-width: 640px) {
+ .plugins-page-content {
+ padding-inline: var(--space-4);
+ }
+
+ .plugins-toolbar {
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ }
+
+ .plugins-search {
+ grid-column: 1 / -1;
+ }
+
+ .plugins-grid {
+ grid-template-columns: 1fr;
+ }
+
+ .plugin-card {
+ min-height: 0;
+ }
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .plugin-card,
+ .plugin-icon-tile {
+ transition: none;
+ }
+}
diff --git a/web/src/pages/projects/index.tsx b/web/src/pages/projects/index.tsx
index 06ddebb13..7532a68e5 100644
--- a/web/src/pages/projects/index.tsx
+++ b/web/src/pages/projects/index.tsx
@@ -1,5 +1,5 @@
-import { useEffect, useMemo, useState, type ReactNode } from "react";
-import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
+import { useEffect, useMemo, useRef, useState, type ReactNode } from "react";
+import { useInfiniteQuery, useMutation, useQueryClient } from "@tanstack/react-query";
import { App, Button, Form, Input, Modal, Select } from "antd";
import { ArrowRight, BookOpenText, FileText, FolderKanban, Images, LayoutGrid, Palette, Plus, Search, Sparkles, Trash2 } from "lucide-react";
import { Link, useNavigate, useSearchParams } from "react-router";
@@ -112,7 +112,14 @@ export default function ProjectsPage() {
setGenerationPreview("");
}
};
- const query = useQuery({ queryKey: ["projects"], queryFn: listProjects });
+ const loadMoreRef = useRef
(null);
+ const query = useInfiniteQuery({
+ // 分页查询和画布页的全量项目查询不能共用缓存形状,否则两个页面会互相覆盖缓存数据。
+ queryKey: ["projects", "paged"],
+ queryFn: ({ pageParam }) => listProjects({ page: pageParam, pageSize: 50 }),
+ initialPageParam: 1,
+ getNextPageParam: (lastPage) => (lastPage.hasMore ? lastPage.page + 1 : undefined),
+ });
const mutation = useMutation({
mutationFn: createProject,
onSuccess: ({ project }) => {
@@ -140,9 +147,10 @@ export default function ProjectsPage() {
onOk: () => deleteMutation.mutate(projectId),
});
};
+ const allProjects = useMemo(() => query.data?.pages.flatMap((page) => page.projects) || [], [query.data]);
const rows = useMemo(() => {
const normalizedKeyword = keyword.trim().toLowerCase();
- return [...(query.data?.projects || [])]
+ return [...allProjects]
.filter(({ project }) => status === "all" || project.status === status)
.filter(({ project }) => !normalizedKeyword || `${project.name} ${project.description} ${project.stylePresetId} ${parseStyleProfile(project.styleProfileJson)?.title || resolveCanvasStylePreset(project.stylePresetId)?.title || ""}`.toLowerCase().includes(normalizedKeyword))
.sort((left, right) => {
@@ -150,17 +158,23 @@ export default function ProjectsPage() {
if (sort === "progress") return projectSummaryCompletion(right) - projectSummaryCompletion(left);
return right.project.updatedAt.localeCompare(left.project.updatedAt);
});
- }, [keyword, query.data, sort, status]);
+ }, [allProjects, keyword, sort, status]);
+ const totalProjectCount = query.data?.pages[0]?.total ?? allProjects.length;
+ useEffect(() => {
+ const node = loadMoreRef.current;
+ if (!node || !query.hasNextPage || query.isError) return;
+ const observer = new IntersectionObserver(
+ ([entry]) => {
+ if (entry?.isIntersecting && !query.isFetchingNextPage) void query.fetchNextPage();
+ },
+ { rootMargin: "600px" },
+ );
+ observer.observe(node);
+ return () => observer.disconnect();
+ }, [query.fetchNextPage, query.hasNextPage, query.isError, query.isFetchingNextPage]);
+ const hasInitialError = query.isError && !query.data;
return (
-
-
{rows.length} 个}
- actions={null}
- />
-
@@ -217,14 +231,17 @@ export default function ProjectsPage() {
- {query.isError ?
void query.refetch()} /> : null}
+ {hasInitialError ? void query.refetch()} /> : null}
{query.isLoading ? : null}
- {!query.isLoading && !query.isError && rows.length ? (
+ {!query.isLoading && !hasInitialError && rows.length ? (
{rows.map((row) => confirmDeleteProject(row.project.id, row.project.name)} />)}
) : null}
- {!query.isLoading && !rows.length && !query.isError && (keyword || status !== "all") ? (
+ {!query.isLoading && !hasInitialError ?
+ {query.isFetchingNextPage ? "正在加载更多项目…" : query.isError ? : query.hasNextPage ? "继续下滑加载更多(每页 50 条)" : allProjects.length ? `已加载全部 ${totalProjectCount} 个项目` : null}
+
: null}
+ {!query.isLoading && !rows.length && !hasInitialError && (keyword || status !== "all") ? (
(api.get("/projects"));
+export function listProjects(): Promise<{ projects: ProjectSummary[] }>;
+export function listProjects(params: { page: number; pageSize: number }): Promise;
+export function listProjects(params?: { page: number; pageSize: number }) {
+ return request<{ projects: ProjectSummary[] } | ProjectListPage>(api.get("/projects", params ? { params: { page: params.page, page_size: params.pageSize } } : undefined));
}
export function getProject(id: string) {
diff --git a/web/src/styles/globals.css b/web/src/styles/globals.css
index 142bf5913..c9c44a3ff 100644
--- a/web/src/styles/globals.css
+++ b/web/src/styles/globals.css
@@ -413,6 +413,9 @@
--workspace-sidebar: var(--workspace-navigation);
--workspace-accent: #171717;
--workspace-accent-soft: rgba(17, 17, 17, 0.07);
+ --workspace-nav-active-bg: color-mix(in srgb, var(--workspace-accent) 12%, transparent);
+ --workspace-nav-active-hover-bg: color-mix(in srgb, var(--workspace-accent) 16%, transparent);
+ --workspace-nav-active-indicator: color-mix(in srgb, var(--workspace-accent) 78%, transparent);
--workspace-sidebar-width: 208px; /* 侧栏展开宽度:较原 260px 收窄 20% */
--workspace-sidebar-nav-width: 208px; /* 展开态导航内容宽度 */
--workspace-sidebar-collapsed-width: 56px; /* 折叠态保留可发现的图标菜单轨 */
@@ -550,6 +553,9 @@
--workspace-sidebar: var(--workspace-navigation);
--workspace-accent: #f5f5f5;
--workspace-accent-soft: rgba(255, 255, 255, 0.09);
+ --workspace-nav-active-bg: color-mix(in srgb, var(--workspace-accent) 13%, transparent);
+ --workspace-nav-active-hover-bg: color-mix(in srgb, var(--workspace-accent) 18%, transparent);
+ --workspace-nav-active-indicator: color-mix(in srgb, var(--workspace-accent) 82%, transparent);
--chart-1: oklch(0.87 0 0);
--chart-2: oklch(0.556 0 0);
--chart-3: oklch(0.439 0 0);
@@ -1522,13 +1528,32 @@
letter-spacing: 0.01em;
transition: color var(--motion-instant) ease-out;
}
- /* 侧栏激活项:主操作角色成对定义(浅色/深色主题同一 token) */
+ .app-workspace-nav-link {
+ position: relative;
+ }
+
+ /* 侧栏激活项同时保留底色和左侧指示条,避免只靠轻微文字变色识别当前位置。 */
.app-workspace-nav-link.is-active {
- background: var(--workspace-accent-soft);
+ background: var(--workspace-nav-active-bg);
color: var(--foreground);
box-shadow: none;
}
+ .app-workspace-nav-link.is-active::before {
+ position: absolute;
+ top: 7px;
+ bottom: 7px;
+ left: 0;
+ width: 3px;
+ border-radius: var(--r-full);
+ background: var(--workspace-nav-active-indicator);
+ content: "";
+ }
+
+ .app-workspace-nav-link.is-active:hover {
+ background: var(--workspace-nav-active-hover-bg);
+ }
+
.app-workspace-icon-button {
@@ -3739,7 +3764,6 @@
z-index: 1;
display: flex;
width: 100%;
- height: 100%;
cursor: pointer;
flex-direction: column;
gap: 0;
@@ -3768,7 +3792,12 @@
box-shadow: none;
}
- .canvas-folder-preview > :where(.canvas-project-media, .canvas-project-video, .canvas-project-empty, .canvas-project-preview-canvas),
+ .canvas-folder-preview > :where(.canvas-project-media, .canvas-project-video, .canvas-project-empty, .canvas-project-preview-canvas) {
+ position: absolute;
+ inset: 0;
+ width: 100%;
+ height: 100%;
+ }
.canvas-folder-preview .cached-resource-image-shell { width: 100%; height: 100%; }
.canvas-folder-preview .canvas-project-media img, .canvas-folder-preview .cached-resource-image-shell > img { display: block; width: 100%; height: 100%; object-fit: cover; }
.canvas-folder-preview .canvas-project-media img { filter: saturate(.94) contrast(1.02); transition: transform 420ms var(--motion-ease-out), filter 240ms ease-out; }
@@ -3798,6 +3827,10 @@
}
.canvas-folder-meta-item { min-width: 0; }
.canvas-folder-meta-separator { color: color-mix(in srgb, var(--foreground) 24%, transparent); }
+ .canvas-folder-dates { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; padding-top: 2px; color: color-mix(in srgb, var(--foreground) 52%, transparent); font-size: var(--fs-micro); font-variant-numeric: tabular-nums; }
+ .canvas-folder-dates > span { display: flex; min-width: 0; flex-direction: column; gap: 2px; }
+ .canvas-folder-dates small { color: color-mix(in srgb, var(--foreground) 34%, transparent); font-size: var(--fs-micro); }
+ .canvas-folder-dates time { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.canvas-folder-select {
position: absolute;
@@ -3842,7 +3875,7 @@
.canvas-folder-card:hover .canvas-folder-actions, .canvas-folder-card:focus-within .canvas-folder-actions { opacity: 1; transform: translateY(0); }
- .canvas-folder-rename, .canvas-folder-action {
+ .canvas-folder-rename, .canvas-folder-more {
display: grid;
width: 28px;
height: 28px;
@@ -3855,8 +3888,8 @@
transition: background-color var(--motion-instant) ease-out, color var(--motion-instant) ease-out;
}
- :where(.canvas-folder-rename, .canvas-folder-action):hover { background: color-mix(in srgb, var(--foreground) 8%, transparent); color: var(--foreground); }
- :where(.canvas-folder-rename, .canvas-folder-action) svg { width: 16px; height: 16px; }
+ :where(.canvas-folder-rename, .canvas-folder-more):hover { background: color-mix(in srgb, var(--foreground) 8%, transparent); color: var(--foreground); }
+ :where(.canvas-folder-rename, .canvas-folder-more) svg { width: 16px; height: 16px; }
@media (max-width: 639px) {
.canvas-folder-open { gap: 0; padding: 0; }
@@ -4340,6 +4373,9 @@
.project-library-progress i { display: block; height: 4px; margin-top: 7px; overflow: hidden; border-radius: var(--r-full); background: color-mix(in srgb, var(--foreground) 9%, transparent); }
.project-library-progress b { display: block; height: 100%; border-radius: inherit; background: var(--workspace-accent); transition: width 420ms var(--motion-ease-out); }
.project-library-stats { display: flex; align-items: center; gap: 14px; margin-top: 12px; color: color-mix(in srgb, var(--foreground) 40%, transparent); font-size: var(--fs-label); }
+ .library-load-more { display: flex; min-height: 44px; align-items: center; justify-content: center; padding: 14px 0 6px; color: color-mix(in srgb, var(--foreground) 42%, transparent); font-size: var(--fs-caption); }
+ .library-load-more button { cursor: pointer; border: 0; background: transparent; color: inherit; font: inherit; text-decoration: underline; text-underline-offset: 3px; }
+ .library-load-more button:focus:not(:focus-visible) { outline: none; }
/* 任务列表:整屏几十条长 prompt 极易糊成一片,靠三件事拉开密度——
行距(min-height + gap)、标题只给一行且宽度封顶、次要信息降到 42% 前景色。 */
@@ -8428,6 +8464,17 @@ html:not(.dark) .creation-media-preview-modal .ant-modal-container { background:
background: var(--admin-sidebar);
}
+/* 管理后台侧栏不复用通用 Tailwind 宽度:通用 sidebar 规则在后面会覆盖 w-*,导致视觉收缩但布局仍占展开宽度。 */
+.admin-shell .admin-sidebar {
+ width: 212px;
+ flex-basis: 212px;
+}
+
+.admin-shell .admin-sidebar.is-collapsed {
+ width: var(--workspace-sidebar-collapsed-width, 56px);
+ flex-basis: var(--workspace-sidebar-collapsed-width, 56px);
+}
+
.admin-shell .app-user-content,
.admin-shell .app-workspace-page,
.admin-shell .admin-page-root {