feat: support settings

This commit is contained in:
Henry Li
2026-01-20 23:43:21 +08:00
parent 7ead7c93f8
commit 1b70e00642
25 changed files with 1355 additions and 217 deletions

View File

@@ -1,6 +1,11 @@
import type { Translations } from "./types";
export const enUS: Translations = {
// Locale meta
locale: {
localName: "English",
},
// Common
common: {
home: "Home",
@@ -83,4 +88,41 @@ export const enUS: Translations = {
readFile: "Read file",
writeFile: "Write file",
},
// Settings
settings: {
title: "Settings",
description: "Adjust how DeerFlow looks and behaves for you.",
sections: {
appearance: "Appearance",
tools: "Tools",
skills: "Skills",
acknowledge: "Acknowledge",
},
appearance: {
themeTitle: "Theme",
themeDescription:
"Choose how the interface follows your device or stays fixed.",
system: "System",
light: "Light",
dark: "Dark",
systemDescription: "Match the operating system preference automatically.",
lightDescription: "Bright palette with higher contrast for daytime.",
darkDescription: "Dim palette that reduces glare for focus.",
languageTitle: "Language",
languageDescription: "Switch between languages.",
},
tools: {
title: "Tools",
description: "Manage the configuration and enabled status of MCP tools.",
},
skills: {
title: "Skills",
description: "Manage the configuration and enabled status of the skills.",
},
acknowledge: {
emptyTitle: "Acknowledgements",
emptyDescription: "Credits and acknowledgements will show here.",
},
},
};

View File

@@ -1,4 +1,9 @@
export interface Translations {
// Locale meta
locale: {
localName: string;
};
// Common
common: {
home: string;
@@ -80,4 +85,40 @@ export interface Translations {
readFile: string;
writeFile: string;
};
// Settings
settings: {
title: string;
description: string;
sections: {
appearance: string;
tools: string;
skills: string;
acknowledge: string;
};
appearance: {
themeTitle: string;
themeDescription: string;
system: string;
light: string;
dark: string;
systemDescription: string;
lightDescription: string;
darkDescription: string;
languageTitle: string;
languageDescription: string;
};
tools: {
title: string;
description: string;
};
skills: {
title: string;
description: string;
};
acknowledge: {
emptyTitle: string;
emptyDescription: string;
};
};
}

View File

@@ -1,6 +1,11 @@
import type { Translations } from "./types";
export const zhCN: Translations = {
// Locale meta
locale: {
localName: "中文",
},
// Common
common: {
home: "首页",
@@ -83,4 +88,40 @@ export const zhCN: Translations = {
readFile: "读取文件",
writeFile: "写入文件",
},
// Settings
settings: {
title: "设置",
description: "根据你的偏好调整 DeerFlow 的界面和行为。",
sections: {
appearance: "外观",
tools: "工具",
skills: "技能",
acknowledge: "致谢",
},
appearance: {
themeTitle: "主题",
themeDescription: "跟随系统或选择固定的界面模式。",
system: "系统",
light: "浅色",
dark: "深色",
systemDescription: "自动匹配操作系统偏好。",
lightDescription: "更明亮的配色,适合日间使用。",
darkDescription: "更暗的配色,减少眩光方便专注。",
languageTitle: "语言",
languageDescription: "在不同语言之间切换。",
},
tools: {
title: "工具",
description: "管理 MCP 工具的配置和启用状态。",
},
skills: {
title: "技能",
description: "管理智能体的技能配置和启用状态。",
},
acknowledge: {
emptyTitle: "致谢",
emptyDescription: "相关的致谢信息会展示在这里。",
},
},
};

View File

@@ -0,0 +1,24 @@
import { env } from "@/env";
import type { MCPConfig } from "./types";
export async function loadMCPConfig() {
const response = await fetch(
`${env.NEXT_PUBLIC_BACKEND_BASE_URL}/api/mcp/config`,
);
return response.json() as Promise<MCPConfig>;
}
export async function updateMCPConfig(config: MCPConfig) {
const response = await fetch(
`${env.NEXT_PUBLIC_BACKEND_BASE_URL}/api/mcp/config`,
{
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(config),
},
);
return response.json();
}

View File

@@ -0,0 +1,44 @@
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { loadMCPConfig, updateMCPConfig } from "./api";
export function useMCPConfig() {
const { data, isLoading, error } = useQuery({
queryKey: ["mcpConfig"],
queryFn: () => loadMCPConfig(),
});
return { config: data, isLoading, error };
}
export function useEnableMCPServer() {
const queryClient = useQueryClient();
const { config } = useMCPConfig();
return useMutation({
mutationFn: async ({
serverName,
enabled,
}: {
serverName: string;
enabled: boolean;
}) => {
if (!config) {
throw new Error("MCP config not found");
}
if (!config.mcp_servers[serverName]) {
throw new Error(`MCP server ${serverName} not found`);
}
await updateMCPConfig({
mcp_servers: {
...config.mcp_servers,
[serverName]: {
...config.mcp_servers[serverName],
enabled,
},
},
});
},
onSuccess: () => {
void queryClient.invalidateQueries({ queryKey: ["mcpConfig"] });
},
});
}

View File

@@ -0,0 +1,2 @@
export * from "./api";
export * from "./types";

View File

@@ -0,0 +1,8 @@
export interface MCPServerConfig extends Record<string, unknown> {
enabled: boolean;
description: string;
}
export interface MCPConfig {
mcp_servers: Record<string, MCPServerConfig>;
}

View File

@@ -0,0 +1,25 @@
import { env } from "@/env";
import type { Skill } from "./type";
export async function loadSkills() {
const skills = await fetch(`${env.NEXT_PUBLIC_BACKEND_BASE_URL}/api/skills`);
const json = await skills.json();
return json.skills as Skill[];
}
export async function enableSkill(skillName: string, enabled: boolean) {
const response = await fetch(
`${env.NEXT_PUBLIC_BACKEND_BASE_URL}/api/skills/${skillName}`,
{
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
enabled,
}),
},
);
return response.json();
}

View File

@@ -0,0 +1,31 @@
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { enableSkill } from "./api";
import { loadSkills } from ".";
export function useSkills() {
const { data, isLoading, error } = useQuery({
queryKey: ["skills"],
queryFn: () => loadSkills(),
});
return { skills: data ?? [], isLoading, error };
}
export function useEnableSkill() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async ({
skillName,
enabled,
}: {
skillName: string;
enabled: boolean;
}) => {
await enableSkill(skillName, enabled);
},
onSuccess: () => {
void queryClient.invalidateQueries({ queryKey: ["skills"] });
},
});
}

View File

@@ -0,0 +1,2 @@
export * from "./api";
export * from "./type";

View File

@@ -0,0 +1,7 @@
export interface Skill {
name: string;
description: string;
category: string;
license: string;
enabled: boolean;
}