feat(themes): 添加可编辑的站点外观和皮肤主题(#408)
This commit is contained in:
@@ -41,7 +41,7 @@ export const CANVAS_AGENT_PANEL_MOTION_MS = 500;
|
||||
const PANEL_MOTION_SECONDS = CANVAS_AGENT_PANEL_MOTION_MS / 1000;
|
||||
const ONLINE_AGENT_MAX_STEPS = 8;
|
||||
const ONLINE_AGENT_PROMPT =
|
||||
`你是影策网页内置在线画布助手。首轮必须先调用 canvas_get_context;涉及已有节点时用 canvas_find_nodes 获取真实 id,涉及媒体参考时用 canvas_get_resources。流水线、工作流、管线、节点图或用户要求连线时,必须使用 canvas_create_workflow:把需求拆成有语义的节点类型、真实内容/提示词、边和布局,禁止把业务阶段退化成几个空文本卡片;工具会自动分配 id、布局并建立连线。复杂写操作先 canvas_validate_ops,再执行 canvas_apply_ops。任何写入后都必须检查工具返回的真实节点类型、connectionCount、overlapWarnings 和 verification;没有真实连线时绝不能说已连线,没有生成资源时绝不能说已完成。不要输出 JSON ops、不要猜 id、不要把未就绪资源当作可用素材、不要编造执行结果。需要用户选择时,给出可点击的短选项,不要只让用户输入 1、2、3。${SKILL_RUNTIME_AGENT_GUIDANCE}`;
|
||||
`你是当前创作工作台内置的在线画布助手。首轮必须先调用 canvas_get_context;涉及已有节点时用 canvas_find_nodes 获取真实 id,涉及媒体参考时用 canvas_get_resources。流水线、工作流、管线、节点图或用户要求连线时,必须使用 canvas_create_workflow:把需求拆成有语义的节点类型、真实内容/提示词、边和布局,禁止把业务阶段退化成几个空文本卡片;工具会自动分配 id、布局并建立连线。复杂写操作先 canvas_validate_ops,再执行 canvas_apply_ops。任何写入后都必须检查工具返回的真实节点类型、connectionCount、overlapWarnings 和 verification;没有真实连线时绝不能说已连线,没有生成资源时绝不能说已完成。不要输出 JSON ops、不要猜 id、不要把未就绪资源当作可用素材、不要编造执行结果。需要用户选择时,给出可点击的短选项,不要只让用户输入 1、2、3。${SKILL_RUNTIME_AGENT_GUIDANCE}`;
|
||||
const JSON_RECORD_SCHEMA = { type: "object", additionalProperties: true };
|
||||
const POSITION_SCHEMA = { type: "object", properties: { x: { type: "number" }, y: { type: "number" } }, required: ["x", "y"], additionalProperties: false };
|
||||
const VIEWPORT_SCHEMA = { type: "object", properties: { x: { type: "number" }, y: { type: "number" }, k: { type: "number" } }, required: ["x", "y", "k"], additionalProperties: false };
|
||||
@@ -1443,7 +1443,7 @@ function stringifyLog(value: unknown) {
|
||||
|
||||
function formatOnlineLogText(logs: OnlineAgentLog[], context: OnlineAgentLogContext) {
|
||||
const head = [
|
||||
"影策网站 Agent 诊断日志",
|
||||
"站点在线 Agent 诊断日志",
|
||||
`model: ${context.model || "none"}`,
|
||||
`running: ${context.running}`,
|
||||
`confirmTools: ${context.confirmTools}`,
|
||||
|
||||
@@ -112,7 +112,7 @@ export function CanvasFolderCard({ project, projectName, onClick, onPrefetch, op
|
||||
menu={{
|
||||
onClick: ({ domEvent }) => domEvent.stopPropagation(),
|
||||
items: [
|
||||
{ key: "export", icon: <Download className="size-3.5" />, label: "导出画布", onClick: () => void exportCanvasProjects([project], project.title || "影策画布") },
|
||||
{ key: "export", icon: <Download className="size-3.5" />, label: "导出画布", onClick: () => void exportCanvasProjects([project], project.title || "画布") },
|
||||
{ type: "divider" },
|
||||
{ key: "delete", danger: true, icon: <Trash2 className="size-3.5" />, label: "删除", onClick: () => setDeleteIds([project.id]) },
|
||||
],
|
||||
|
||||
@@ -1201,7 +1201,7 @@ function parseEventJson<T>(data: string) {
|
||||
|
||||
function formatLogText(logs: AgentEventLog[], context: AgentLogContext) {
|
||||
const head = [
|
||||
"影策 Canvas Agent 诊断日志",
|
||||
"站点 Canvas Agent 诊断日志",
|
||||
`连接: ${context.connected ? "在线" : context.enabled ? "连接中" : "未启用"}`,
|
||||
`状态: ${context.activity}`,
|
||||
`waiting: ${context.waiting}`,
|
||||
|
||||
@@ -114,7 +114,7 @@ export function CanvasProjectCard({ project, projectName, variant = "library", r
|
||||
menu={{
|
||||
onClick: ({ domEvent }) => domEvent.stopPropagation(),
|
||||
items: [
|
||||
{ key: "export", icon: <Download className="size-3.5" />, label: "导出画布", onClick: () => void exportCanvasProjects([project], project.title || "影策画布") },
|
||||
{ key: "export", icon: <Download className="size-3.5" />, label: "导出画布", onClick: () => void exportCanvasProjects([project], project.title || "画布") },
|
||||
{ type: "divider" },
|
||||
{ key: "delete", danger: true, icon: <Trash2 className="size-3.5" />, label: "删除", onClick: () => setDeleteIds([project.id]) },
|
||||
],
|
||||
|
||||
@@ -7,6 +7,7 @@ import zhCN from "antd/locale/zh_CN";
|
||||
import { AuthSessionHydrator } from "@/components/auth/auth-session-hydrator";
|
||||
import { ClientRootInit } from "@/components/layout/client-root-init";
|
||||
import { getAntThemeConfig } from "@/lib/app-theme";
|
||||
import { applySkinTheme } from "@/lib/skin-themes";
|
||||
import { appQueryClient } from "@/lib/query-client";
|
||||
import { useThemeStore } from "@/stores/use-theme-store";
|
||||
import { applyAppearanceMetadata, useAppearanceStore } from "@/stores/use-appearance-store";
|
||||
@@ -72,6 +73,7 @@ export function AppProviders({ children }: { children: ReactNode }) {
|
||||
useEffect(() => {
|
||||
document.documentElement.classList.toggle("dark", dark);
|
||||
document.documentElement.style.colorScheme = theme;
|
||||
applySkinTheme(appearance.activeSkin, theme);
|
||||
applyAppearanceMetadata(appearance);
|
||||
}, [appearance, dark, theme]);
|
||||
|
||||
@@ -81,7 +83,7 @@ export function AppProviders({ children }: { children: ReactNode }) {
|
||||
const isolateDevRepro = import.meta.env.DEV && typeof window !== "undefined" && window.location.pathname === "/dev/director-repro";
|
||||
|
||||
return (
|
||||
<ConfigProvider locale={zhCN} theme={getAntThemeConfig(dark)}>
|
||||
<ConfigProvider locale={zhCN} theme={getAntThemeConfig(dark, appearance.activeSkin)}>
|
||||
<App message={{ duration: 3, maxCount: 3 }} notification={{ duration: 4.5, maxCount: 3, placement: "topRight" }}>
|
||||
<QueryClientProvider client={appQueryClient}>
|
||||
{isolateDevRepro ? (
|
||||
|
||||
22
web/src/components/layout/site-compliance-footer.tsx
Normal file
22
web/src/components/layout/site-compliance-footer.tsx
Normal file
@@ -0,0 +1,22 @@
|
||||
import { ExternalLink } from "lucide-react";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
import { useAppearanceStore } from "@/stores/use-appearance-store";
|
||||
|
||||
const MIIT_FILING_URL = "https://beian.miit.gov.cn/";
|
||||
|
||||
export function SiteComplianceFooter({ className, variant = "default" }: { className?: string; variant?: "default" | "auth" }) {
|
||||
const appearance = useAppearanceStore((state) => state.appearance);
|
||||
|
||||
return (
|
||||
<footer className={cn("site-compliance-footer", variant === "auth" && "is-auth", className)} aria-label="站点版权与备案信息">
|
||||
<span>{appearance.footerCopyright}</span>
|
||||
{appearance.icpFilingEnabled && appearance.icpFilingNumber ? (
|
||||
<a href={MIIT_FILING_URL} target="_blank" rel="noopener noreferrer" aria-label={`${appearance.icpFilingNumber},前往工业和信息化部备案管理系统查询`}>
|
||||
{appearance.icpFilingNumber}
|
||||
<ExternalLink aria-hidden="true" />
|
||||
</a>
|
||||
) : null}
|
||||
</footer>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user