From dcb152f1be364bf491ad65cf772dcf137812e862 Mon Sep 17 00:00:00 2001 From: HelloWorldU Date: Wed, 27 May 2026 23:42:16 +0800 Subject: [PATCH 1/2] feat(frontend): update agent detail component MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update AgentDetail view and related global styles, including necessary dependency upgrades. 已有测试覆盖或无需补充。 --- kimi-code-swarm/package-lock.json | 51 +++++++ kimi-code-swarm/package.json | 4 + .../src/components/AgentDetail.vue | 27 +++- kimi-code-swarm/src/style.css | 132 ++++++++++++++++++ 4 files changed, 211 insertions(+), 3 deletions(-) diff --git a/kimi-code-swarm/package-lock.json b/kimi-code-swarm/package-lock.json index 00e7fc8..83cd657 100644 --- a/kimi-code-swarm/package-lock.json +++ b/kimi-code-swarm/package-lock.json @@ -12,7 +12,10 @@ "@tauri-apps/api": "^2.11.0", "@tauri-apps/plugin-store": "^2.4.3", "@vitejs/plugin-vue": "^6.0.6", + "dompurify": "^3.4.7", + "highlight.js": "^11.11.1", "lucide-vue-next": "^1.0.0", + "marked": "^18.0.4", "vue": "^3.5.33", "zod": "^4.4.3" }, @@ -20,6 +23,7 @@ "@eslint/js": "^9.39.4", "@playwright/test": "^1.59.1", "@tauri-apps/cli": "^2.11.0", + "@types/dompurify": "^3.0.5", "@vue/test-utils": "^2.4.10", "autoprefixer": "^10.5.0", "eslint": "^9.39.4", @@ -1439,6 +1443,16 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/dompurify": { + "version": "3.0.5", + "resolved": "https://registry.npmmirror.com/@types/dompurify/-/dompurify-3.0.5.tgz", + "integrity": "sha512-1Wg0g3BtQF7sSb27fJQAKck1HECM6zV1EB66j8JH9i3LCjYabJa0FSdiSgsD5K/RbrsR0SiraKacLB+T8ZVYAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/trusted-types": "*" + } + }, "node_modules/@types/estree": { "version": "1.0.8", "resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.8.tgz", @@ -1463,6 +1477,13 @@ "undici-types": "~7.19.0" } }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmmirror.com/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "devOptional": true, + "license": "MIT" + }, "node_modules/@types/whatwg-mimetype": { "version": "3.0.2", "resolved": "https://registry.npmmirror.com/@types/whatwg-mimetype/-/whatwg-mimetype-3.0.2.tgz", @@ -2595,6 +2616,15 @@ "dev": true, "license": "MIT" }, + "node_modules/dompurify": { + "version": "3.4.7", + "resolved": "https://registry.npmmirror.com/dompurify/-/dompurify-3.4.7.tgz", + "integrity": "sha512-2jBxDJY4RR06tQNy4w5FlFH7kfxsQZlufd0sbv+chfHCxeJwrFw2baUDsSwvBISD4K4RDbd0PTfy3uNXsR6siA==", + "license": "(MPL-2.0 OR Apache-2.0)", + "optionalDependencies": { + "@types/trusted-types": "^2.0.7" + } + }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmmirror.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz", @@ -3305,6 +3335,15 @@ "node": ">= 0.4" } }, + "node_modules/highlight.js": { + "version": "11.11.1", + "resolved": "https://registry.npmmirror.com/highlight.js/-/highlight.js-11.11.1.tgz", + "integrity": "sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmmirror.com/ignore/-/ignore-5.3.2.tgz", @@ -3861,6 +3900,18 @@ "@jridgewell/sourcemap-codec": "^1.5.5" } }, + "node_modules/marked": { + "version": "18.0.4", + "resolved": "https://registry.npmmirror.com/marked/-/marked-18.0.4.tgz", + "integrity": "sha512-c/BTaKzg0G6ezQx97DAkYU7k0HM6ys0FqYeKBL6hlBByZwy+ycA1+f0vDdjMHKKeEjdgkx0GOv9Il6D+85cOqA==", + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 20" + } + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmmirror.com/merge2/-/merge2-1.4.1.tgz", diff --git a/kimi-code-swarm/package.json b/kimi-code-swarm/package.json index 711cd28..c1db0cd 100644 --- a/kimi-code-swarm/package.json +++ b/kimi-code-swarm/package.json @@ -25,6 +25,7 @@ "@eslint/js": "^9.39.4", "@playwright/test": "^1.59.1", "@tauri-apps/cli": "^2.11.0", + "@types/dompurify": "^3.0.5", "@vue/test-utils": "^2.4.10", "autoprefixer": "^10.5.0", "eslint": "^9.39.4", @@ -44,7 +45,10 @@ "@tauri-apps/api": "^2.11.0", "@tauri-apps/plugin-store": "^2.4.3", "@vitejs/plugin-vue": "^6.0.6", + "dompurify": "^3.4.7", + "highlight.js": "^11.11.1", "lucide-vue-next": "^1.0.0", + "marked": "^18.0.4", "vue": "^3.5.33", "zod": "^4.4.3" } diff --git a/kimi-code-swarm/src/components/AgentDetail.vue b/kimi-code-swarm/src/components/AgentDetail.vue index 38d87cf..f49197c 100644 --- a/kimi-code-swarm/src/components/AgentDetail.vue +++ b/kimi-code-swarm/src/components/AgentDetail.vue @@ -6,10 +6,31 @@ import { User, Bot, Loader2, Brain, Wrench, Server, ChevronDown, ChevronRight } from 'lucide-vue-next' +import { marked } from 'marked' +import hljs from 'highlight.js' +import DOMPurify from 'dompurify' import type { AgentTask } from '../types' import { getLastInput } from '../utils/getLastInput' import { useSwarmStore } from '../store/useSwarmStore' +// Configure marked with syntax highlighting via custom renderer +const renderer = new marked.Renderer() +renderer.code = ({ text, lang }: { text: string; lang?: string }) => { + const language = lang && hljs.getLanguage(lang) ? lang : undefined + const highlighted = language + ? hljs.highlight(text, { language }).value + : hljs.highlightAuto(text).value + return `
${highlighted}
` +} +marked.use({ renderer }) +marked.setOptions({ breaks: true }) + +function renderMarkdown(content: string): string { + if (!content) return '' + const html = marked.parse(content, { async: false }) as string + return DOMPurify.sanitize(html) +} + const props = withDefaults(defineProps<{ agent: AgentTask /** 引擎是否已 restore 完毕;false 时禁用「启动/发送/停止」等向引擎发命令的按钮 */ @@ -370,7 +391,7 @@ watch(() => props.agent.status, async () => {
-

{{ log.content }}

+
{{ log.tokens }} tokens {{ new Date(log.timestamp).toLocaleTimeString() }} @@ -389,7 +410,7 @@ watch(() => props.agent.status, async () => {
-

{{ log.content }}

+
{{ new Date(log.timestamp).toLocaleTimeString() }}
@@ -413,7 +434,7 @@ watch(() => props.agent.status, async () => {
-

{{ log.content }}

+
{{ new Date(log.timestamp).toLocaleTimeString() }}
diff --git a/kimi-code-swarm/src/style.css b/kimi-code-swarm/src/style.css index cf54885..13cdbc7 100644 --- a/kimi-code-swarm/src/style.css +++ b/kimi-code-swarm/src/style.css @@ -1,3 +1,5 @@ +@import 'highlight.js/styles/github.css'; + @tailwind base; @tailwind components; @tailwind utilities; @@ -9,6 +11,136 @@ } } +/* Markdown rendering styles for chat bubbles */ +.markdown-content { + @apply text-sm leading-relaxed break-words; +} + +.markdown-content > *:first-child { + @apply mt-0; +} + +.markdown-content > *:last-child { + @apply mb-0; +} + +.markdown-content h1, +.markdown-content h2, +.markdown-content h3, +.markdown-content h4, +.markdown-content h5, +.markdown-content h6 { + @apply font-semibold mt-3 mb-2 text-gray-900; +} + +.markdown-content h1 { @apply text-lg; } +.markdown-content h2 { @apply text-base; } +.markdown-content h3 { @apply text-sm; } +.markdown-content h4, +.markdown-content h5, +.markdown-content h6 { @apply text-sm; } + +.markdown-content p { + @apply mb-2; +} + +.markdown-content ul, +.markdown-content ol { + @apply pl-5 mb-2; +} + +.markdown-content ul { + @apply list-disc; +} + +.markdown-content ol { + @apply list-decimal; +} + +.markdown-content blockquote { + @apply border-l-4 border-gray-300 pl-3 italic text-gray-600 my-2; +} + +.markdown-content a { + @apply text-swarm-600 underline; +} + +.markdown-content code { + @apply bg-gray-100 text-pink-600 px-1 py-0.5 rounded text-xs font-mono; +} + +.markdown-content pre { + @apply bg-gray-100 rounded-lg p-3 overflow-x-auto my-2; +} + +.markdown-content pre code { + @apply bg-transparent text-inherit p-0 rounded-none text-xs; +} + +.markdown-content table { + @apply w-full border-collapse text-xs my-2; +} + +.markdown-content th, +.markdown-content td { + @apply border border-gray-200 px-2 py-1; +} + +.markdown-content th { + @apply bg-gray-50 font-medium; +} + +.markdown-content hr { + @apply border-gray-200 my-3; +} + +.markdown-content img { + @apply max-w-full rounded-lg my-2; +} + +/* Inverted styles for user message bubble (green bg, white text) */ +.markdown-content-inverted h1, +.markdown-content-inverted h2, +.markdown-content-inverted h3, +.markdown-content-inverted h4, +.markdown-content-inverted h5, +.markdown-content-inverted h6 { + @apply text-white; +} + +.markdown-content-inverted blockquote { + @apply border-white/30 text-white/80; +} + +.markdown-content-inverted a { + @apply text-green-200; +} + +.markdown-content-inverted code { + @apply bg-white/20 text-white; +} + +.markdown-content-inverted pre { + @apply bg-white/15; +} + +.markdown-content-inverted pre code { + @apply text-white; +} + +.markdown-content-inverted th, +.markdown-content-inverted td { + @apply border-white/20; +} + +.markdown-content-inverted th { + @apply bg-white/10; +} + +.markdown-content-inverted hr { + @apply border-white/20; +} + @layer utilities { .scrollbar-thin::-webkit-scrollbar { width: 6px; From e5466e210c47b7ce70dfd283b3353a085b707766 Mon Sep 17 00:00:00 2001 From: HelloWorldU Date: Wed, 27 May 2026 23:47:06 +0800 Subject: [PATCH 2/2] docs(docs): update design docs and frontend conventions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refresh DESIGN, FRONTEND, COMPONENT_PATTERNS and CONSTRAINTS to align with current project standards and architecture. 无需补充测试(纯文档更新)。 --- docs/COMPONENT_PATTERNS.md | 2 +- docs/CONSTRAINTS.md | 1 + docs/DESIGN.md | 1 + docs/FRONTEND.md | 6 +++--- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/docs/COMPONENT_PATTERNS.md b/docs/COMPONENT_PATTERNS.md index 6843ced..e19c8fb 100644 --- a/docs/COMPONENT_PATTERNS.md +++ b/docs/COMPONENT_PATTERNS.md @@ -35,7 +35,7 @@ const emit = defineEmits<{...}>() - `CreateTaskModal.vue` — 表单弹窗组件:收集 name / repoUrl / tokenBudget 创建 Agent,Vue `` 原生过渡动画 - `SwarmConfirmModal.vue` — 确认弹窗组件:类型化图标 + 双按钮确认/取消,配合 useConfirm composable - `SwarmToast.vue` — Toast 通知组件:类型化图标 + 进度条 + 自动消失,配合 useToast composable -- `AgentDetail.vue` — 聊天式多轮对话组件:Header + Info + PR 审阅 + 文件变更 + 聊天消息区(input/output/system/error 直接渲染;think/tool_call/mcp/tool_result 为可折叠气泡,默认收起,点击展开查看完整内容)+ `