feat(ui): 优化模型选择器交互、插件页分类留白、更新日志与暗黑模式适配
This commit is contained in:
@@ -8,6 +8,23 @@
|
||||
z-index: calc(var(--z-modal-overlay) + 1000) !important;
|
||||
}
|
||||
|
||||
.agent-model-picker-popover .canvas-model-picker-menu {
|
||||
width: min(340px, calc(100vw - 24px)) !important;
|
||||
max-width: min(340px, calc(100vw - 24px)) !important;
|
||||
}
|
||||
|
||||
.agent-model-picker-popover .canvas-model-picker-two-pane {
|
||||
grid-template-columns: minmax(0, 1fr) !important;
|
||||
}
|
||||
|
||||
.agent-model-picker-popover .canvas-model-picker-brand-rail {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.agent-model-picker-popover .canvas-model-picker-model-pane {
|
||||
min-width: 0 !important;
|
||||
}
|
||||
|
||||
/* Flex 子页必须允许收缩,长模型名和 Skill 文案不能决定窗口宽度。 */
|
||||
.canvas-agent-panel > div,
|
||||
.canvas-agent-settings,
|
||||
|
||||
@@ -11,7 +11,6 @@ export function AppChangelogDialog({ open, onClose }: { open: boolean; onClose:
|
||||
|
||||
return (
|
||||
<AppModal
|
||||
flush
|
||||
rootClassName="app-spatial-modal app-changelog-modal"
|
||||
title={
|
||||
<div className="app-changelog-heading">
|
||||
@@ -30,6 +29,7 @@ export function AppChangelogDialog({ open, onClose }: { open: boolean; onClose:
|
||||
footer={null}
|
||||
centered
|
||||
onCancel={onClose}
|
||||
styles={{ body: { padding: 0 } }}
|
||||
modalRender={(node) => (
|
||||
<motion.div initial={reducedMotion ? false : { opacity: 0, y: 14, scale: 0.975 }} animate={{ opacity: 1, y: 0, scale: 1 }} transition={{ duration: aceternityMotion.duration.panel, ease: aceternityMotion.easing.enter }}>
|
||||
{node}
|
||||
|
||||
@@ -258,7 +258,7 @@ export function ModelPicker({
|
||||
disabledReason={disabledReason}
|
||||
showDescription={selected || previewedModel === displayModel}
|
||||
/>
|
||||
{selected ? <Check className="canvas-model-picker-option-check" style={{ color: theme.node.activeStroke }} /> : null}
|
||||
{selected ? <Check className="canvas-model-picker-option-check ml-1 shrink-0" style={{ color: theme.node.activeStroke }} /> : null}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
@@ -353,13 +353,17 @@ function ModelLabel({
|
||||
<span className="grid size-6 shrink-0 place-items-center rounded-md" style={{ background: theme.toolbar.itemHover }}>
|
||||
<ModelIcon config={config} model={model} />
|
||||
</span>
|
||||
<span className="min-w-0 flex-1 overflow-hidden">
|
||||
<span className="min-w-44 flex-1 overflow-hidden">
|
||||
<span className="block min-w-0 truncate text-[var(--fs-label)] font-medium leading-none">{pickerModelDisplayName(config, model, showConfiguredModelName)}</span>
|
||||
<span className={cn("canvas-model-picker-description mt-1 block truncate text-[var(--fs-tiny)]", showDescription && "is-visible")} style={{ color: theme.node.muted }} title={capabilitySummary}>
|
||||
{capabilitySummary}
|
||||
</span>
|
||||
</span>
|
||||
{showPrice ? <ModelPrice price={modelMenuPrice(config, model, capability, true)} /> : null}
|
||||
{showPrice ? (
|
||||
<span className="ml-auto shrink-0 pl-2">
|
||||
<ModelPrice price={modelMenuPrice(config, model, capability, true)} />
|
||||
</span>
|
||||
) : null}
|
||||
{!creationVariant && meta.time ? (
|
||||
<span className="shrink-0 rounded-full px-1.5 py-0.5 text-[var(--fs-tiny)] tabular-nums" style={{ background: theme.toolbar.itemHover, color: theme.node.muted }}>
|
||||
{meta.time}
|
||||
|
||||
@@ -354,6 +354,20 @@ export function getAntThemeConfig(dark: boolean, skinID: unknown = "classic"): T
|
||||
headerFontSize: 15,
|
||||
bodyPadding: 18,
|
||||
},
|
||||
Message: {
|
||||
contentBg: elevatedBackground,
|
||||
contentPadding: "10px 18px",
|
||||
},
|
||||
Notification: {
|
||||
colorBgElevated: elevatedBackground,
|
||||
},
|
||||
Tooltip: {
|
||||
colorBgSpotlight: elevatedBackground,
|
||||
colorTextLightSolid: color.selectedFg,
|
||||
},
|
||||
Popover: {
|
||||
colorBgElevated: elevatedBackground,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -385,6 +399,9 @@ export function getWorkspaceAntThemeConfig(): ThemeConfig {
|
||||
Dropdown: { borderRadiusLG: 16, controlItemBgHover: "var(--user-surface-muted)", paddingBlock: 6 },
|
||||
Modal: { borderRadiusLG: 20 },
|
||||
Popconfirm: { borderRadiusLG: 16 },
|
||||
Message: { borderRadiusLG: 14, contentPadding: "10px 18px" },
|
||||
Notification: { borderRadiusLG: 18 },
|
||||
Tooltip: { borderRadius: 8 },
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -87,7 +87,6 @@ export default function PluginsPage() {
|
||||
const [detailsRestoreFocus, setDetailsRestoreFocus] = useState(false);
|
||||
const [search, setSearch] = useState("");
|
||||
const [categoryFilter, setCategoryFilter] = useState("all");
|
||||
const [scrollTarget, setScrollTarget] = useState<string | null>(null);
|
||||
const [statusFilter, setStatusFilter] = useState<"all" | "enabled" | "disabled">("all");
|
||||
const [trustFilter, setTrustFilter] = useState<"all" | "trusted">("all");
|
||||
const [eagleBaseUrl, setEagleBaseUrl] = useState("http://localhost:41595");
|
||||
@@ -96,7 +95,6 @@ export default function PluginsPage() {
|
||||
const [eagleFolders, setEagleFolders] = useState<EagleFolder[]>([]);
|
||||
const [eagleFoldersLoading, setEagleFoldersLoading] = useState(false);
|
||||
const [eagleFoldersError, setEagleFoldersError] = useState("");
|
||||
const sectionRefs = useRef<Record<string, HTMLElement | null>>({});
|
||||
|
||||
useEffect(() => {
|
||||
for (const plugin of builtinPlugins) ensurePlugin(plugin.manifest);
|
||||
@@ -168,24 +166,15 @@ export default function PluginsPage() {
|
||||
[categoryFilter, filteredPlugins],
|
||||
);
|
||||
|
||||
const scrollContainerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const selectCategory = (key: string) => {
|
||||
setCategoryFilter(key);
|
||||
setScrollTarget(key === "all" ? null : key);
|
||||
if (scrollContainerRef.current) {
|
||||
scrollContainerRef.current.scrollTop = 0;
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!scrollTarget) return;
|
||||
const section = sectionRefs.current[scrollTarget];
|
||||
if (!section) return;
|
||||
|
||||
const frame = window.requestAnimationFrame(() => {
|
||||
const reducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
||||
section.scrollIntoView({ behavior: reducedMotion ? "auto" : "smooth", block: "start" });
|
||||
setScrollTarget(null);
|
||||
});
|
||||
return () => window.cancelAnimationFrame(frame);
|
||||
}, [pluginSections, scrollTarget]);
|
||||
|
||||
const categoryCounts = useMemo(() => {
|
||||
const visiblePlugins = registeredPlugins.filter((plugin) => user?.role === "admin" || features.systemPluginsVisibleToUsers || isOfficialApplicationPluginId(plugin.manifest.id));
|
||||
const counts: Record<string, number> = { all: visiblePlugins.length, text: 0, image: 0, video: 0, audio: 0, payment: 0, other: 0 };
|
||||
@@ -248,7 +237,7 @@ export default function PluginsPage() {
|
||||
|
||||
return (
|
||||
<main className="app-workspace-page plugins-page flex h-full min-h-0 flex-col text-foreground">
|
||||
<div className="app-workspace-scroll min-h-0 flex-1 overflow-y-auto">
|
||||
<div ref={scrollContainerRef} className="app-workspace-scroll min-h-0 flex-1 overflow-y-auto">
|
||||
<div className="plugins-page-layout">
|
||||
<aside className="plugins-sidebar" aria-label="插件分类">
|
||||
<div className="plugins-sidebar-heading">
|
||||
@@ -334,15 +323,11 @@ export default function PluginsPage() {
|
||||
{filteredPlugins.length ? (
|
||||
<div className="plugins-sections">
|
||||
{pluginSections.map((section) => {
|
||||
if (!section.plugins.length) return null;
|
||||
const SectionIcon = section.icon;
|
||||
return (
|
||||
<section
|
||||
key={section.key}
|
||||
id={`plugin-section-${section.key}`}
|
||||
ref={(element) => {
|
||||
sectionRefs.current[section.key] = element;
|
||||
}}
|
||||
className="plugin-section"
|
||||
data-category={section.key === "image" ? "creative" : section.key === "video" ? "drama" : section.key === "audio" ? "social" : section.key === "payment" ? "ecommerce" : undefined}
|
||||
>
|
||||
@@ -356,114 +341,120 @@ export default function PluginsPage() {
|
||||
</div>
|
||||
<span className="plugin-section-count">{section.plugins.length}</span>
|
||||
</header>
|
||||
<div className="plugins-grid">
|
||||
{section.plugins.map((plugin) => {
|
||||
const installation = installations.find((item) => item.manifest.id === plugin.manifest.id);
|
||||
const remote = backendPluginById.get(plugin.manifest.id);
|
||||
const enabled = isPluginEnabled(plugin, installation);
|
||||
const trusted = Boolean(plugin.manifest.trusted);
|
||||
const state = pluginStates[plugin.manifest.id];
|
||||
const sourceLabel = pluginSourceLabel(plugin, state);
|
||||
const canConfigure = canConfigurePlugin(plugin);
|
||||
return (
|
||||
<section key={plugin.manifest.id} className={`product-collection-card plugin-card library-card-surface${trusted ? " is-trusted" : ""}`}>
|
||||
<button
|
||||
type="button"
|
||||
className="plugin-card-main"
|
||||
aria-label={`查看${plugin.manifest.name}文档`}
|
||||
onClick={(event) => {
|
||||
const openedByKeyboard = event.detail === 0;
|
||||
setDetailsRestoreFocus(openedByKeyboard);
|
||||
setDetailsPluginId(plugin.manifest.id);
|
||||
if (!openedByKeyboard) event.currentTarget.blur();
|
||||
}}
|
||||
>
|
||||
<div className="plugin-card-heading">
|
||||
<span className={`plugin-icon-tile${trusted ? " is-trusted" : ""}`} aria-hidden="true">
|
||||
<SectionIcon className="size-5" />
|
||||
</span>
|
||||
<div className="min-w-0 flex-1">
|
||||
<div className="plugin-card-title-row">
|
||||
<h3>{plugin.manifest.name}</h3>
|
||||
<span className="plugin-version">v{plugin.manifest.version}</span>
|
||||
</div>
|
||||
<div className="plugin-card-labels">
|
||||
<span className={`plugin-source-label${sourceLabel === "系统插件" ? " is-system" : ""}`}>{sourceLabel}</span>
|
||||
{trusted ? (
|
||||
<span className="plugin-trust-label">
|
||||
<ShieldCheck className="size-3.5" />
|
||||
可信插件
|
||||
{section.plugins.length ? (
|
||||
<div className="plugins-grid">
|
||||
{section.plugins.map((plugin) => {
|
||||
const installation = installations.find((item) => item.manifest.id === plugin.manifest.id);
|
||||
const remote = backendPluginById.get(plugin.manifest.id);
|
||||
const enabled = isPluginEnabled(plugin, installation);
|
||||
const trusted = Boolean(plugin.manifest.trusted);
|
||||
const state = pluginStates[plugin.manifest.id];
|
||||
const sourceLabel = pluginSourceLabel(plugin, state);
|
||||
const canConfigure = canConfigurePlugin(plugin);
|
||||
return (
|
||||
<section key={plugin.manifest.id} className={`product-collection-card plugin-card library-card-surface${trusted ? " is-trusted" : ""}`}>
|
||||
<button
|
||||
type="button"
|
||||
className="plugin-card-main"
|
||||
aria-label={`查看${plugin.manifest.name}文档`}
|
||||
onClick={(event) => {
|
||||
const openedByKeyboard = event.detail === 0;
|
||||
setDetailsRestoreFocus(openedByKeyboard);
|
||||
setDetailsPluginId(plugin.manifest.id);
|
||||
if (!openedByKeyboard) event.currentTarget.blur();
|
||||
}}
|
||||
>
|
||||
<div className="plugin-card-heading">
|
||||
<span className={`plugin-icon-tile${trusted ? " is-trusted" : ""}`} aria-hidden="true">
|
||||
<SectionIcon className="size-5" />
|
||||
</span>
|
||||
<div className="min-w-0 flex-1">
|
||||
<div className="plugin-card-title-row">
|
||||
<h3>{plugin.manifest.name}</h3>
|
||||
<span className="plugin-version">v{plugin.manifest.version}</span>
|
||||
</div>
|
||||
<div className="plugin-card-labels">
|
||||
<span className={`plugin-source-label${sourceLabel === "系统插件" ? " is-system" : ""}`}>{sourceLabel}</span>
|
||||
{trusted ? (
|
||||
<span className="plugin-trust-label">
|
||||
<ShieldCheck className="size-3.5" />
|
||||
可信插件
|
||||
</span>
|
||||
) : null}
|
||||
<span className="plugin-category-label">
|
||||
{contributionKindsFor(plugin.manifest)
|
||||
.map((kind) => categoryLabels[kind] ?? kind)
|
||||
.join(" · ")}
|
||||
</span>
|
||||
) : null}
|
||||
<span className="plugin-category-label">
|
||||
{contributionKindsFor(plugin.manifest)
|
||||
.map((kind) => categoryLabels[kind] ?? kind)
|
||||
.join(" · ")}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p className="plugin-card-description">{plugin.manifest.description}</p>
|
||||
<p className="plugin-card-description">{plugin.manifest.description}</p>
|
||||
|
||||
<div className="plugin-card-meta">
|
||||
<span>
|
||||
<CalendarDays className="size-3.5" />
|
||||
发布 {formatPluginDate(plugin.manifest.publishedAt)}
|
||||
</span>
|
||||
<span>
|
||||
<Clock3 className="size-3.5" />
|
||||
更新 {formatPluginDate(plugin.manifest.updatedAt)}
|
||||
<div className="plugin-card-meta">
|
||||
<span>
|
||||
<CalendarDays className="size-3.5" />
|
||||
发布 {formatPluginDate(plugin.manifest.publishedAt)}
|
||||
</span>
|
||||
<span>
|
||||
<Clock3 className="size-3.5" />
|
||||
更新 {formatPluginDate(plugin.manifest.updatedAt)}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="plugin-card-tags">
|
||||
{(plugin.manifest.surfaces || []).map((surface) => (
|
||||
<span key={surface}>{surfaceLabels[surface] ?? surface}</span>
|
||||
))}
|
||||
{providerCapabilitiesFor(plugin.manifest).map((capability) => (
|
||||
<span key={capability}>{capabilityLabel(capability)}</span>
|
||||
))}
|
||||
{plugin.manifest.contributes.providers?.some((provider) => provider.poll) ? <span>异步轮询</span> : null}
|
||||
<span>{plugin.manifest.permissions.length} 项能力</span>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<div className="plugin-card-actions">
|
||||
<span role="status" className={`settings-channel-status ${enabled ? "is-ready" : ""}`}>
|
||||
<i aria-hidden="true" />
|
||||
{!state?.platformAvailable && state?.blockedReason ? state.blockedReason : enabled ? "已启用" : "已停用"}
|
||||
</span>
|
||||
<Switch
|
||||
className="plugin-state-switch"
|
||||
disabled={!state?.canToggle}
|
||||
checked={enabled}
|
||||
aria-label={`${plugin.manifest.name},当前${enabled ? "已启用,点击停用" : "已停用,点击启用"}`}
|
||||
title={state?.blockedReason}
|
||||
onChange={(checked) => void togglePlugin(plugin, checked)}
|
||||
/>
|
||||
{canConfigure ? (
|
||||
<Button
|
||||
className="plugin-settings-button"
|
||||
icon={<Settings2 className="size-4" />}
|
||||
aria-expanded={settingsPluginId === plugin.manifest.id}
|
||||
aria-haspopup="dialog"
|
||||
onClick={() => setSettingsPluginId(plugin.manifest.id)}
|
||||
>
|
||||
设置
|
||||
</Button>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
<div className="plugin-card-tags">
|
||||
{(plugin.manifest.surfaces || []).map((surface) => (
|
||||
<span key={surface}>{surfaceLabels[surface] ?? surface}</span>
|
||||
))}
|
||||
{providerCapabilitiesFor(plugin.manifest).map((capability) => (
|
||||
<span key={capability}>{capabilityLabel(capability)}</span>
|
||||
))}
|
||||
{plugin.manifest.contributes.providers?.some((provider) => provider.poll) ? <span>异步轮询</span> : null}
|
||||
<span>{plugin.manifest.permissions.length} 项能力</span>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<div className="plugin-card-actions">
|
||||
<span role="status" className={`settings-channel-status ${enabled ? "is-ready" : ""}`}>
|
||||
<i aria-hidden="true" />
|
||||
{!state?.platformAvailable && state?.blockedReason ? state.blockedReason : enabled ? "已启用" : "已停用"}
|
||||
</span>
|
||||
<Switch
|
||||
className="plugin-state-switch"
|
||||
disabled={!state?.canToggle}
|
||||
checked={enabled}
|
||||
aria-label={`${plugin.manifest.name},当前${enabled ? "已启用,点击停用" : "已停用,点击启用"}`}
|
||||
title={state?.blockedReason}
|
||||
onChange={(checked) => void togglePlugin(plugin, checked)}
|
||||
/>
|
||||
{canConfigure ? (
|
||||
<Button
|
||||
className="plugin-settings-button"
|
||||
icon={<Settings2 className="size-4" />}
|
||||
aria-expanded={settingsPluginId === plugin.manifest.id}
|
||||
aria-haspopup="dialog"
|
||||
onClick={() => setSettingsPluginId(plugin.manifest.id)}
|
||||
>
|
||||
设置
|
||||
</Button>
|
||||
{installation?.lastError || remote?.error ? (
|
||||
<Typography.Text type="danger" className="plugin-error" role="alert">
|
||||
{installation?.lastError || remote?.error}
|
||||
</Typography.Text>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
{installation?.lastError || remote?.error ? (
|
||||
<Typography.Text type="danger" className="plugin-error" role="alert">
|
||||
{installation?.lastError || remote?.error}
|
||||
</Typography.Text>
|
||||
) : null}
|
||||
</section>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
) : (
|
||||
<div className="plugin-section-empty-hint">
|
||||
<span>暂无该类型的插件</span>
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
);
|
||||
})}
|
||||
|
||||
@@ -18,10 +18,13 @@
|
||||
padding: 0 0 var(--space-8);
|
||||
}
|
||||
|
||||
.plugins-page-content .collection-toolbar {
|
||||
margin-top: var(--space-2);
|
||||
}
|
||||
|
||||
.plugins-page-layout {
|
||||
display: grid;
|
||||
width: min(var(--product-content-max), calc(100% - 64px));
|
||||
min-height: 100%;
|
||||
margin: 24px auto 0;
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
align-items: start;
|
||||
@@ -31,7 +34,7 @@
|
||||
position: static;
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
padding: 0 0 var(--space-4);
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.plugins-sidebar-heading {
|
||||
@@ -250,6 +253,19 @@
|
||||
margin-top: var(--space-3);
|
||||
}
|
||||
|
||||
.plugin-section-empty-hint {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 140px;
|
||||
margin-top: var(--space-3);
|
||||
border-radius: var(--plugins-card-radius);
|
||||
border: 1px dashed var(--user-border);
|
||||
background: color-mix(in srgb, var(--foreground) 2%, transparent);
|
||||
color: color-mix(in srgb, var(--foreground) 45%, transparent);
|
||||
font-size: var(--fs-label);
|
||||
}
|
||||
|
||||
.plugin-card {
|
||||
display: flex;
|
||||
min-width: 0;
|
||||
@@ -841,7 +857,7 @@
|
||||
position: static;
|
||||
min-height: 0;
|
||||
border-right: 0;
|
||||
padding: 0 0 var(--space-4);
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.plugins-sidebar-heading {
|
||||
|
||||
@@ -222,6 +222,8 @@ export default function ProjectsPage() {
|
||||
variant="creation"
|
||||
placeholder="选择文本模型"
|
||||
showSelectedPrice={false}
|
||||
showOptionPrices
|
||||
popoverClassName="agent-model-picker-popover"
|
||||
/>
|
||||
<Button type="default" icon={<Sparkles className="size-3.5" />} disabled={!storyDraft.trim() || generating} loading={generating} onClick={() => void generateStory()}>AI 生成章节</Button>
|
||||
<Button type="primary" icon={<Plus className="size-3.5" />} onClick={() => openCreate(createSource)}>开始创作</Button>
|
||||
|
||||
@@ -1305,6 +1305,41 @@
|
||||
--workspace-overlay-shadow: 0 30px 84px rgba(0, 0, 0, 0.62), 0 8px 24px rgba(0, 0, 0, 0.34);
|
||||
}
|
||||
|
||||
/* 全局轻量通知与消息提示框 */
|
||||
.ant-message-notice .ant-message-notice-content {
|
||||
color: var(--foreground) !important;
|
||||
background: var(--color-surface, var(--workspace-overlay-bg-strong, #ffffff)) !important;
|
||||
border: 1px solid var(--color-border, var(--workspace-overlay-border, rgba(0, 0, 0, 0.12))) !important;
|
||||
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.18) !important;
|
||||
}
|
||||
|
||||
.dark .ant-message-notice .ant-message-notice-content,
|
||||
.dark body .ant-message-notice-content {
|
||||
color: #f4f4f5 !important;
|
||||
background: #1e1e20 !important;
|
||||
border-color: rgba(255, 255, 255, 0.16) !important;
|
||||
box-shadow: 0 12px 36px rgba(0, 0, 0, 0.55) !important;
|
||||
}
|
||||
|
||||
.ant-notification-notice {
|
||||
color: var(--foreground) !important;
|
||||
background: var(--color-surface, var(--workspace-overlay-bg-strong, #ffffff)) !important;
|
||||
border: 1px solid var(--color-border, var(--workspace-overlay-border, rgba(0, 0, 0, 0.12))) !important;
|
||||
}
|
||||
|
||||
.dark .ant-notification-notice,
|
||||
.dark body .ant-notification-notice {
|
||||
color: #f4f4f5 !important;
|
||||
background: #1e1e20 !important;
|
||||
border-color: rgba(255, 255, 255, 0.16) !important;
|
||||
}
|
||||
|
||||
.dark .ant-tooltip .ant-tooltip-inner {
|
||||
color: #f4f4f5 !important;
|
||||
background: #1e1e20 !important;
|
||||
border: 1px solid rgba(255, 255, 255, 0.14) !important;
|
||||
}
|
||||
|
||||
body.app-spatial-overlays :where(
|
||||
.ant-dropdown-menu,
|
||||
.ant-select-dropdown,
|
||||
@@ -7746,7 +7781,7 @@ html:not(.dark) .creation-studio-control:hover,
|
||||
|
||||
.canvas-model-picker-menu {
|
||||
width: max-content !important;
|
||||
min-width: var(--canvas-model-picker-trigger-width, 0px);
|
||||
min-width: max(var(--canvas-model-picker-trigger-width, 0px), 260px);
|
||||
max-width: calc(100vw - 24px);
|
||||
max-height: min(420px, calc(100vh - 32px));
|
||||
overflow-x: hidden;
|
||||
@@ -7806,6 +7841,9 @@ html:not(.dark) .creation-studio-control:hover,
|
||||
flex: 1 1 auto;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.canvas-model-picker-popover .canvas-model-picker-option > span:first-child > span:nth-child(2) {
|
||||
@@ -15947,7 +15985,7 @@ html:not(.dark) .creation-history-drawer-root {
|
||||
|
||||
/* 空态:banner 剧照 + 三张欢迎卡 */
|
||||
.assets-empty { display: flex; width: 100%; flex-direction: column; align-items: center; padding: 5vh 0 8vh; }
|
||||
.assets-empty-banner { position: relative; width: min(720px, calc(100% - 24px)); height: 180px; }
|
||||
.assets-empty-banner { position: relative; width: min(720px, calc(100% - 24px)); height: 180px; margin-bottom: 24px; }
|
||||
.assets-empty-banner-frame { position: absolute; overflow: hidden; border: 1px solid color-mix(in srgb, var(--foreground) 14%, transparent); border-radius: var(--r-lg); box-shadow: var(--elevation-card); }
|
||||
.assets-empty-banner-frame img { width: 100%; height: 100%; object-fit: cover; filter: grayscale(.45) contrast(.94) saturate(.7); }
|
||||
.assets-empty-banner-frame.is-back { top: 16px; left: 2%; width: 24%; height: 132px; transform: rotate(-6deg); opacity: .45; }
|
||||
@@ -15955,9 +15993,9 @@ html:not(.dark) .creation-history-drawer-root {
|
||||
.assets-empty-banner-frame.is-front { top: 20px; right: 2%; width: 24%; height: 132px; transform: rotate(6deg); opacity: .5; }
|
||||
.assets-empty-banner-frame.is-main::after { position: absolute; inset: 0; background: linear-gradient(180deg, transparent 40%, color-mix(in srgb, var(--foreground) 55%, transparent)); content: ""; }
|
||||
.assets-empty-banner-frame span { position: absolute; right: 12px; bottom: 10px; z-index: 1; color: #fff; font-size: var(--fs-micro); letter-spacing: .08em; text-shadow: 0 1px 6px rgba(0, 0, 0, .5); }
|
||||
.assets-empty-banner-caption { position: absolute; right: 19%; bottom: 2px; color: color-mix(in srgb, var(--foreground) 44%, transparent); font-size: var(--fs-micro); letter-spacing: .1em; }
|
||||
.assets-empty-banner-caption { position: absolute; left: 0; right: 0; bottom: -22px; text-align: center; color: color-mix(in srgb, var(--foreground) 44%, transparent); font-size: var(--fs-micro); letter-spacing: .1em; z-index: 3; }
|
||||
.assets-empty-banner-caption span { color: var(--workspace-accent); font-weight: 620; font-variant-numeric: tabular-nums; }
|
||||
.assets-empty-cards { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px; width: min(680px, calc(100% - 24px)); margin-top: 34px; }
|
||||
.assets-empty-cards { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px; width: min(680px, calc(100% - 24px)); margin-top: 24px; }
|
||||
.assets-empty-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -17402,13 +17440,32 @@ html:not(.dark) .creation-home .creation-user-message-copy-wrap { box-shadow: 0
|
||||
flex: 0 0 34px;
|
||||
place-items: center;
|
||||
border-radius: 10px;
|
||||
color: rgba(0, 0, 0, .46);
|
||||
color: var(--user-ink-muted);
|
||||
transition: background-color .18s ease, color .18s ease;
|
||||
}
|
||||
|
||||
.app-user-workspace .app-workspace-sidebar-notification:hover {
|
||||
background: rgba(72, 72, 73, .055);
|
||||
color: #171717;
|
||||
background: var(--user-surface-hover);
|
||||
color: var(--user-ink);
|
||||
}
|
||||
|
||||
.dark .app-user-workspace .app-workspace-sidebar-notification,
|
||||
.dark .app-user-workspace .app-workspace-topbar-icon-button,
|
||||
.dark .app-user-workspace .app-workspace-sidebar-rail-button {
|
||||
color: rgba(255, 255, 255, 0.65) !important;
|
||||
}
|
||||
|
||||
.dark .app-user-workspace .app-workspace-sidebar-notification:hover,
|
||||
.dark .app-user-workspace .app-workspace-topbar-icon-button:hover,
|
||||
.dark .app-user-workspace .app-workspace-sidebar-rail-button:hover {
|
||||
background: rgba(255, 255, 255, 0.08) !important;
|
||||
color: #ffffff !important;
|
||||
}
|
||||
|
||||
.dark .app-user-workspace .app-workspace-topbar-icon-button svg,
|
||||
.dark .app-user-workspace .app-workspace-sidebar-notification svg,
|
||||
.dark .app-user-workspace .app-workspace-sidebar-rail-button svg {
|
||||
color: currentColor !important;
|
||||
}
|
||||
|
||||
.app-user-workspace .app-workspace-sidebar-notification svg {
|
||||
@@ -17732,7 +17789,25 @@ html:not(.dark) .creation-home .creation-user-message-copy-wrap { box-shadow: 0
|
||||
.dark .app-user-workspace .app-workspace-sidebar-profile-menu button:hover { background: rgba(108,94,236,.15); color: #dcd8ff; }
|
||||
.dark .app-user-workspace .creation-home-heading h1 { color: #f5f5f5; }
|
||||
.dark .app-user-workspace .creation-home-heading p,
|
||||
.dark .app-user-workspace .app-workspace-topbar-breadcrumb { color: rgba(255, 255, 255, .48); }
|
||||
.dark .app-user-workspace .app-workspace-topbar-breadcrumb > span:not(.truncate) {
|
||||
color: rgba(255, 255, 255, 0.28) !important;
|
||||
}
|
||||
|
||||
.dark .app-user-workspace .app-workspace-sidebar-collapse-button {
|
||||
color: rgba(255, 255, 255, 0.55) !important;
|
||||
opacity: 0.85;
|
||||
}
|
||||
|
||||
.dark .app-user-workspace .app-workspace-sidebar-collapse-button:hover,
|
||||
.dark .app-user-workspace .app-workspace-sidebar-collapse-button:focus-visible {
|
||||
background: rgba(255, 255, 255, 0.08) !important;
|
||||
color: #ffffff !important;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.dark .app-user-workspace .app-workspace-sidebar-collapse-button svg {
|
||||
color: currentColor !important;
|
||||
}
|
||||
.dark .app-user-workspace .app-workspace-topbar { border-bottom-color: rgba(255, 255, 255, .08); }
|
||||
.dark .app-user-workspace .creation-empty-workspace > .creation-mode-capsule { background: #232326; border-color: rgba(255,255,255,.08); }
|
||||
.dark .app-user-workspace .creation-empty-workspace > .creation-mode-capsule .creation-chat-control[aria-selected="true"] { background: #303034; color: #ffffff; }
|
||||
|
||||
@@ -665,6 +665,157 @@
|
||||
.app-product-workspace .story-launcher-panel { padding: 16px; border-radius: 16px; }
|
||||
}
|
||||
|
||||
/* Changelog Modal Styles */
|
||||
.app-changelog-modal .ant-modal-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
border: 1px solid var(--user-border);
|
||||
border-radius: 20px;
|
||||
background: var(--user-page-bg);
|
||||
box-shadow: 0 24px 70px rgba(0, 0, 0, 0.28);
|
||||
max-height: min(84vh, 780px);
|
||||
}
|
||||
|
||||
.app-changelog-modal .ant-modal-header {
|
||||
margin-bottom: 0;
|
||||
padding: 20px 24px 16px;
|
||||
border-bottom: 1px solid var(--user-border);
|
||||
background: var(--user-surface);
|
||||
}
|
||||
|
||||
.app-changelog-heading {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.app-changelog-heading-icon {
|
||||
display: grid;
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
flex: 0 0 38px;
|
||||
place-items: center;
|
||||
border-radius: 11px;
|
||||
background: color-mix(in srgb, var(--user-accent) 14%, var(--user-surface));
|
||||
color: var(--user-accent);
|
||||
}
|
||||
|
||||
.app-changelog-heading-copy {
|
||||
display: grid;
|
||||
min-width: 0;
|
||||
flex: 1 1 auto;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.app-changelog-heading-title {
|
||||
color: var(--user-ink);
|
||||
font-size: 16px;
|
||||
font-weight: 650;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.app-changelog-heading-description {
|
||||
color: var(--user-ink-muted);
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.app-changelog-current-version {
|
||||
margin-right: 28px;
|
||||
border-radius: 999px;
|
||||
border: 1px solid color-mix(in srgb, var(--user-accent) 25%, transparent);
|
||||
background: color-mix(in srgb, var(--user-accent) 10%, var(--user-surface));
|
||||
padding: 3px 10px;
|
||||
color: var(--user-accent);
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.app-changelog-scroll {
|
||||
max-height: min(66vh, 600px);
|
||||
overflow-y: auto;
|
||||
padding: 20px 28px 32px;
|
||||
color: var(--user-ink);
|
||||
}
|
||||
|
||||
.app-changelog-section-heading {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin: 24px 0 12px;
|
||||
padding-bottom: 6px;
|
||||
border-bottom: 1px solid var(--user-border);
|
||||
color: var(--user-ink);
|
||||
font-size: 15px;
|
||||
font-weight: 650;
|
||||
}
|
||||
|
||||
.app-changelog-section-heading:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.app-changelog-section-marker {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 50%;
|
||||
background: var(--user-ink-muted);
|
||||
}
|
||||
|
||||
.app-changelog-section-heading.is-latest .app-changelog-section-marker {
|
||||
background: var(--user-accent);
|
||||
}
|
||||
|
||||
.app-changelog-latest-badge {
|
||||
border-radius: 999px;
|
||||
background: var(--user-accent);
|
||||
padding: 1px 7px;
|
||||
color: var(--user-accent-foreground, #fff);
|
||||
font-size: 10px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.app-changelog-list {
|
||||
margin: 8px 0 16px 20px;
|
||||
padding: 0;
|
||||
list-style-type: disc;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.app-changelog-list li {
|
||||
color: color-mix(in srgb, var(--user-ink) 88%, transparent);
|
||||
font-size: 13px;
|
||||
line-height: 1.65;
|
||||
}
|
||||
|
||||
.app-changelog-paragraph {
|
||||
margin: 6px 0;
|
||||
color: color-mix(in srgb, var(--user-ink) 78%, transparent);
|
||||
font-size: 13px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.app-changelog-code {
|
||||
border-radius: 5px;
|
||||
border: 1px solid var(--user-border);
|
||||
background: var(--user-surface-muted);
|
||||
padding: 1px 5px;
|
||||
color: var(--user-accent);
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
||||
font-size: 11.5px;
|
||||
}
|
||||
|
||||
@media (max-width: 620px) {
|
||||
.app-changelog-heading-description { display: none; }
|
||||
.app-changelog-current-version { display: none; }
|
||||
.app-changelog-scroll { padding: 14px 16px 24px; }
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.app-product-workspace *,
|
||||
.app-product-overlays * { animation-duration: 1ms !important; animation-delay: 0ms !important; animation-iteration-count: 1 !important; transition-duration: 1ms !important; }
|
||||
|
||||
Reference in New Issue
Block a user