diff --git a/frontend/package.json b/frontend/package.json index e936c34..46ca46a 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -73,6 +73,7 @@ "react-dom": "^19.0.0", "react-resizable-panels": "^4.4.1", "rehype-katex": "^7.0.1", + "rehype-raw": "^7.0.0", "remark-gfm": "^4.0.1", "remark-math": "^6.0.0", "shiki": "3.15.0", diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index 9fc1f0c..cb04b35 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -182,6 +182,9 @@ importers: rehype-katex: specifier: ^7.0.1 version: 7.0.1 + rehype-raw: + specifier: ^7.0.0 + version: 7.0.0 remark-gfm: specifier: ^4.0.1 version: 4.0.1 diff --git a/frontend/src/components/workspace/settings/memory-settings-page.tsx b/frontend/src/components/workspace/settings/memory-settings-page.tsx index a5225e6..831aa38 100644 --- a/frontend/src/components/workspace/settings/memory-settings-page.tsx +++ b/frontend/src/components/workspace/settings/memory-settings-page.tsx @@ -31,6 +31,26 @@ function confidenceToLevelKey(confidence: unknown): { return { key: "normal", value }; } +function formatMemorySection( + title: string, + summary: string, + updatedAt: string | undefined, + t: ReturnType["t"], +): string { + const content = + summary.trim() || + `${t.settings.memory.markdown.empty}`; + return [ + `### ${title}`, + content, + "", + updatedAt && + `> ${t.settings.memory.markdown.updatedAt}: \`${formatTimeAgo(updatedAt)}\``, + ] + .filter(Boolean) + .join("\n"); +} + function memoryToMarkdown( memory: UserMemory, t: ReturnType["t"], @@ -44,65 +64,61 @@ function memoryToMarkdown( parts.push(`\n## ${t.settings.memory.markdown.userContext}`); parts.push( - [ - `### ${t.settings.memory.markdown.work}`, - memory.user.workContext.summary || "-", - "", - memory.user.workContext.updatedAt && - `> ${t.settings.memory.markdown.updatedAt}: \`${formatTimeAgo(memory.user.workContext.updatedAt)}\``, - ].join("\n"), + formatMemorySection( + t.settings.memory.markdown.work, + memory.user.workContext.summary, + memory.user.workContext.updatedAt, + t, + ), ); parts.push( - [ - `### ${t.settings.memory.markdown.personal}`, - memory.user.personalContext.summary || "-", - "", - memory.user.personalContext.updatedAt && - `> ${t.settings.memory.markdown.updatedAt}: \`${formatTimeAgo(memory.user.personalContext.updatedAt)}\``, - ].join("\n"), + formatMemorySection( + t.settings.memory.markdown.personal, + memory.user.personalContext.summary, + memory.user.personalContext.updatedAt, + t, + ), ); parts.push( - [ - `### ${t.settings.memory.markdown.topOfMind}`, - memory.user.topOfMind.summary || "-", - "", - memory.user.topOfMind.updatedAt && - `> ${t.settings.memory.markdown.updatedAt}: \`${formatTimeAgo(memory.user.topOfMind.updatedAt)}\``, - ].join("\n"), + formatMemorySection( + t.settings.memory.markdown.topOfMind, + memory.user.topOfMind.summary, + memory.user.topOfMind.updatedAt, + t, + ), ); parts.push(`\n## ${t.settings.memory.markdown.historyBackground}`); parts.push( - [ - `### ${t.settings.memory.markdown.recentMonths}`, - memory.history.recentMonths.summary || "-", - "", - memory.history.recentMonths.updatedAt && - `> ${t.settings.memory.markdown.updatedAt}: \`${formatTimeAgo(memory.history.recentMonths.updatedAt)}\``, - ].join("\n"), + formatMemorySection( + t.settings.memory.markdown.recentMonths, + memory.history.recentMonths.summary, + memory.history.recentMonths.updatedAt, + t, + ), ); parts.push( - [ - `### ${t.settings.memory.markdown.earlierContext}`, - memory.history.earlierContext.summary || "-", - "", - memory.history.earlierContext.updatedAt && - `> ${t.settings.memory.markdown.updatedAt}: \`${formatTimeAgo(memory.history.earlierContext.updatedAt)}\``, - ].join("\n"), + formatMemorySection( + t.settings.memory.markdown.earlierContext, + memory.history.earlierContext.summary, + memory.history.earlierContext.updatedAt, + t, + ), ); parts.push( - [ - `### ${t.settings.memory.markdown.longTermBackground}`, - memory.history.longTermBackground.summary || "-", - "", - memory.history.longTermBackground.updatedAt && - `> ${t.settings.memory.markdown.updatedAt}: \`${formatTimeAgo(memory.history.longTermBackground.updatedAt)}\``, - ].join("\n"), + formatMemorySection( + t.settings.memory.markdown.longTermBackground, + memory.history.longTermBackground.summary, + memory.history.longTermBackground.updatedAt, + t, + ), ); parts.push(`\n## ${t.settings.memory.markdown.facts}`); if (memory.facts.length === 0) { - parts.push(`_${t.settings.memory.markdown.empty}_`); + parts.push( + `${t.settings.memory.markdown.empty}`, + ); } else { parts.push( [ diff --git a/frontend/src/components/workspace/settings/skill-settings-page.tsx b/frontend/src/components/workspace/settings/skill-settings-page.tsx index 24398fa..c564256 100644 --- a/frontend/src/components/workspace/settings/skill-settings-page.tsx +++ b/frontend/src/components/workspace/settings/skill-settings-page.tsx @@ -115,20 +115,20 @@ function SkillSettingsList({ } function EmptySkill({ onCreateSkill }: { onCreateSkill: () => void }) { + const { t } = useI18n(); return ( - No agent skill yet + {t.settings.skills.emptyTitle} - Put your agent skill folders under the `/skills/custom` folder under - the root folder of DeerFlow. + {t.settings.skills.emptyDescription} - + ); diff --git a/frontend/src/core/i18n/locales/en-US.ts b/frontend/src/core/i18n/locales/en-US.ts index ac53d66..1bc60a9 100644 --- a/frontend/src/core/i18n/locales/en-US.ts +++ b/frontend/src/core/i18n/locales/en-US.ts @@ -243,7 +243,7 @@ export const enUS: Translations = { longTermBackground: "Long-term background", updatedAt: "Updated at", facts: "Facts", - empty: "Empty", + empty: "(empty)", table: { category: "Category", confidence: "Confidence", @@ -282,6 +282,10 @@ export const enUS: Translations = { description: "Manage the configuration and enabled status of the agent skills.", createSkill: "Create skill", + emptyTitle: "No agent skill yet", + emptyDescription: + "Put your agent skill folders under the `/skills/custom` folder under the root folder of DeerFlow.", + emptyButton: "Create Your First Skill", }, notification: { title: "Notification", diff --git a/frontend/src/core/i18n/locales/types.ts b/frontend/src/core/i18n/locales/types.ts index 843f517..7213efa 100644 --- a/frontend/src/core/i18n/locales/types.ts +++ b/frontend/src/core/i18n/locales/types.ts @@ -225,6 +225,9 @@ export interface Translations { title: string; description: string; createSkill: string; + emptyTitle: string; + emptyDescription: string; + emptyButton: string; }; notification: { title: string; diff --git a/frontend/src/core/i18n/locales/zh-CN.ts b/frontend/src/core/i18n/locales/zh-CN.ts index 4f03539..a3d399b 100644 --- a/frontend/src/core/i18n/locales/zh-CN.ts +++ b/frontend/src/core/i18n/locales/zh-CN.ts @@ -275,6 +275,10 @@ export const zhCN: Translations = { title: "技能", description: "管理 Agent Skill 配置和启用状态。", createSkill: "新建技能", + emptyTitle: "还没有技能", + emptyDescription: + "将你的 Agent Skill 文件夹放在 DeerFlow 根目录下的 `/skills/custom` 文件夹中。", + emptyButton: "创建你的第一个技能", }, notification: { title: "通知", diff --git a/frontend/src/core/streamdown/plugins.ts b/frontend/src/core/streamdown/plugins.ts index d829a53..e921403 100644 --- a/frontend/src/core/streamdown/plugins.ts +++ b/frontend/src/core/streamdown/plugins.ts @@ -1,4 +1,5 @@ import rehypeKatex from "rehype-katex"; +import rehypeRaw from "rehype-raw"; import remarkGfm from "remark-gfm"; import remarkMath from "remark-math"; import type { StreamdownProps } from "streamdown"; @@ -11,6 +12,7 @@ export const streamdownPlugins = { [remarkMath, { singleDollarTextMath: true }], ] as StreamdownProps["remarkPlugins"], rehypePlugins: [ + rehypeRaw, [rehypeKatex, { output: "html" }], ] as StreamdownProps["rehypePlugins"], };