From 522e88a6a25f1f89c27077af763d35204460a470 Mon Sep 17 00:00:00 2001
From: Jerry Yan <792602257@qq.com>
Date: Fri, 12 Jun 2026 13:36:39 +0800
Subject: [PATCH] =?UTF-8?q?=E5=89=8D=E7=AB=AF=E4=BC=98=E5=8C=96?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
web/src/api/client.ts | 7 ++++++-
web/src/components/acp/AgentKindForm.vue | 12 ++++++++----
web/src/utils/markdown.ts | 3 +++
web/src/views/chat/ChatDetailView.vue | 1 +
.../views/chat/components/MessageBubble.vue | 5 +----
.../projects/components/ArtifactPanel.vue | 4 +---
.../components/RequirementChatPanel.vue | 1 +
.../components/RequirementHistoryPanel.vue | 3 +--
.../components/RequirementSummaryPanel.vue | 4 +---
.../projects/components/SaveArtifactModal.vue | 3 +--
web/vite.config.ts | 18 ++++++++++++++++++
11 files changed, 42 insertions(+), 19 deletions(-)
create mode 100644 web/src/utils/markdown.ts
diff --git a/web/src/api/client.ts b/web/src/api/client.ts
index edb0e1d..a7c1503 100644
--- a/web/src/api/client.ts
+++ b/web/src/api/client.ts
@@ -34,11 +34,13 @@ export async function request(path: string, opts: RequestOptions = {}): Promi
const controller = new AbortController()
const timer = setTimeout(() => controller.abort(), DEFAULT_TIMEOUT_MS)
+ let onAbort: (() => void) | undefined
if (opts.signal) {
if (opts.signal.aborted) {
controller.abort()
} else {
- opts.signal.addEventListener('abort', () => controller.abort(), { once: true })
+ onAbort = () => controller.abort()
+ opts.signal.addEventListener('abort', onAbort, { once: true })
}
}
@@ -78,6 +80,9 @@ export async function request(path: string, opts: RequestOptions = {}): Promi
return data as T
} finally {
clearTimeout(timer)
+ if (opts.signal && onAbort) {
+ opts.signal.removeEventListener('abort', onAbort)
+ }
}
}
diff --git a/web/src/components/acp/AgentKindForm.vue b/web/src/components/acp/AgentKindForm.vue
index 80e64ca..c8fcae1 100644
--- a/web/src/components/acp/AgentKindForm.vue
+++ b/web/src/components/acp/AgentKindForm.vue
@@ -57,11 +57,12 @@ const allowlistText = computed({
})
interface EnvRow {
+ id: string
k: string
v: string
}
const envEntries = ref(
- Object.entries(local.value.env).map(([k, v]) => ({ k, v })),
+ Object.entries(local.value.env).map(([k, v]) => ({ id: crypto.randomUUID(), k, v })),
)
watch(
@@ -77,7 +78,7 @@ watch(
)
function addEnv() {
- envEntries.value.push({ k: '', v: '' })
+ envEntries.value.push({ id: crypto.randomUUID(), k: '', v: '' })
}
function removeEnv(idx: number) {
envEntries.value.splice(idx, 1)
@@ -94,6 +95,7 @@ const mcpTypeOptions = [
]
interface McpRow {
+ id: string
name: string
type: 'stdio' | 'http' | 'sse'
command: string
@@ -123,6 +125,7 @@ function parseKV(s: string): Record | undefined {
function specsToRows(specs: McpServerSpec[]): McpRow[] {
return specs.map((s) => ({
+ id: crypto.randomUUID(),
name: s.name,
type: s.type,
command: s.command ?? '',
@@ -173,6 +176,7 @@ watch(
function addMcpServer() {
mcpRows.value.push({
+ id: crypto.randomUUID(),
name: '',
type: 'http',
command: '',
@@ -258,7 +262,7 @@ function removeMcpServer(idx: number) {
diff --git a/web/src/utils/markdown.ts b/web/src/utils/markdown.ts
new file mode 100644
index 0000000..b90c77b
--- /dev/null
+++ b/web/src/utils/markdown.ts
@@ -0,0 +1,3 @@
+import MarkdownIt from 'markdown-it'
+
+export const md = new MarkdownIt({ html: false, breaks: true, linkify: true })
diff --git a/web/src/views/chat/ChatDetailView.vue b/web/src/views/chat/ChatDetailView.vue
index b3e57ce..6995e8d 100644
--- a/web/src/views/chat/ChatDetailView.vue
+++ b/web/src/views/chat/ChatDetailView.vue
@@ -94,6 +94,7 @@
diff --git a/web/src/views/chat/components/MessageBubble.vue b/web/src/views/chat/components/MessageBubble.vue
index f294561..f4a4c10 100644
--- a/web/src/views/chat/components/MessageBubble.vue
+++ b/web/src/views/chat/components/MessageBubble.vue
@@ -93,13 +93,10 @@