zen-gitsync 2.13.7 → 2.13.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/README.md +2 -0
  2. package/package.json +1 -1
  3. package/scripts/README_COLOR_CONVERTER.md +196 -196
  4. package/scripts/README_FONTSIZE_CONVERTER.md +278 -278
  5. package/scripts/README_SPACING_CONVERTER.md +126 -126
  6. package/scripts/README_STYLE_VARS.md +180 -180
  7. package/scripts/verify-file-search.mjs +281 -0
  8. package/scripts/verify-subtask-row.mjs +284 -0
  9. package/src/ui/public/assets/EditorView-Dvg7sMyU.css +1 -0
  10. package/src/ui/public/assets/EditorView-eQBqgBEd.js +0 -0
  11. package/src/ui/public/assets/SourceMapView-BLkrjBFW.js +3 -0
  12. package/src/ui/public/assets/WorkbenchView-BMtAZx07.css +1 -0
  13. package/src/ui/public/assets/WorkbenchView-D2eEOP1R.js +6 -0
  14. package/src/ui/public/assets/{_plugin-vue_export-helper-CSyhX3vt.js → _plugin-vue_export-helper-BMDM_R2D.js} +3 -3
  15. package/src/ui/public/assets/{css.worker-CvXBzhp8.js → css.worker-Wv5dxAWO.js} +1 -1
  16. package/src/ui/public/assets/{html.worker-BO6WuOEO.js → html.worker-CQP8QQsS.js} +1 -1
  17. package/src/ui/public/assets/index-BaFDF44N.js +66 -0
  18. package/src/ui/public/assets/index-BvzxdkDp.css +1 -0
  19. package/src/ui/public/assets/{json.worker-BkJRGcCJ.js → json.worker-DzV-CpCQ.js} +1 -1
  20. package/src/ui/public/assets/{ts.worker-B0J26iPs.js → ts.worker-Dth06zuC.js} +15 -15
  21. package/src/ui/public/assets/{vendor-C30huq-U.js → vendor-CDAfMQIV.js} +240 -247
  22. package/src/ui/public/assets/vendor-CIeR0bF6.css +1 -0
  23. package/src/ui/public/favicon.svg +75 -75
  24. package/src/ui/public/index.html +23 -23
  25. package/src/ui/public/logo.svg +74 -74
  26. package/src/ui/public/assets/EditorView-CbfYBgbJ.js +0 -0
  27. package/src/ui/public/assets/EditorView-DEd8QuPp.css +0 -1
  28. package/src/ui/public/assets/SourceMapView-CyGL7mqz.js +0 -3
  29. package/src/ui/public/assets/WorkbenchView-B6BA_Qbo.css +0 -1
  30. package/src/ui/public/assets/WorkbenchView-Bu9HDEKs.js +0 -6
  31. package/src/ui/public/assets/index-BTlhS8hO.js +0 -66
  32. package/src/ui/public/assets/index-p1e3YDoA.css +0 -1
  33. package/src/ui/public/assets/vendor-Bq2rS2vY.css +0 -1
  34. /package/src/ui/public/assets/{editor.worker-Cn2oRESe.js → editor.worker-Bd9IXS8d.js} +0 -0
  35. /package/src/ui/public/assets/{rolldown-runtime-CMxvf4Kt.js → rolldown-runtime-BM3Ffeng.js} +0 -0
@@ -0,0 +1,281 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * 文件树搜索功能端到端验证脚本
4
+ *
5
+ * 运行:node scripts/verify-file-search.mjs
6
+ *
7
+ * 前置:backend 服务已启动(端口写入 .port 文件)
8
+ * 输出:截图保存到 workbench-images/_task-mqekkeib-3erm4b/
9
+ */
10
+ import { chromium } from '../src/ui/client/node_modules/playwright/index.mjs'
11
+ import path from 'path'
12
+ import fs from 'fs'
13
+ import { fileURLToPath } from 'url'
14
+
15
+ const __filename = fileURLToPath(import.meta.url)
16
+ const __dirname = path.dirname(__filename)
17
+ const repoRoot = path.resolve(__dirname, '..')
18
+ const screenshotDir = path.join(repoRoot, 'workbench-images', '_task-mqekkeib-3erm4b')
19
+ fs.mkdirSync(screenshotDir, { recursive: true })
20
+
21
+ const port = fs.readFileSync(path.join(repoRoot, '.port'), 'utf8').trim()
22
+ const URL = `http://localhost:${port}/`
23
+
24
+ const log = (...args) => console.log('[verify]', ...args)
25
+ const shot = (page, name) => page.screenshot({ path: path.join(screenshotDir, name), fullPage: false })
26
+
27
+ async function clickByText(page, selector, text) {
28
+ // 在 selector 内查找包含指定文字的元素并点击
29
+ const handle = await page.evaluateHandle((sel, t) => {
30
+ const root = document.querySelector(sel) || document
31
+ const candidates = root.querySelectorAll('button, .el-button, a, [role="button"]')
32
+ for (const c of candidates) {
33
+ if ((c.textContent || '').trim() === t || (c.textContent || '').trim().includes(t)) {
34
+ return c
35
+ }
36
+ }
37
+ return null
38
+ }, selector, text)
39
+ const el = handle.asElement()
40
+ if (!el) throw new Error(`未找到包含文字 "${text}" 的按钮`)
41
+ await el.scrollIntoViewIfNeeded()
42
+ await el.click({ force: true })
43
+ }
44
+
45
+ async function run() {
46
+ const browser = await chromium.launch({ headless: true, args: ['--no-sandbox'] })
47
+ const ctx = await browser.newContext({ viewport: { width: 1440, height: 900 } })
48
+ const page = await ctx.newPage()
49
+ const errors = []
50
+ page.on('pageerror', (e) => errors.push(`pageerror: ${e.message}`))
51
+ page.on('console', (msg) => { if (msg.type() === 'error') errors.push(`console: ${msg.text()}`) })
52
+
53
+ log(`导航 ${URL}`)
54
+ await page.goto(URL, { waitUntil: 'domcontentloaded', timeout: 60_000 })
55
+ // 等应用挂载
56
+ await page.waitForSelector('.sidebar-title', { timeout: 30_000, state: 'attached' })
57
+ await page.waitForTimeout(800)
58
+ await shot(page, '01-initial.png')
59
+
60
+ // ── 切换到编辑器视图(ActivityBar 的"编辑器"按钮) ────
61
+ log('[0] 切换到编辑器视图')
62
+ const editorBtn = await page.$('button[aria-label="编辑器"], .activity-btn[aria-label="编辑器"]')
63
+ if (editorBtn) {
64
+ const visible = await editorBtn.isVisible().catch(() => false)
65
+ if (visible) {
66
+ await editorBtn.click({ force: true })
67
+ log(' - 已点击"编辑器"按钮')
68
+ await page.waitForTimeout(800)
69
+ }
70
+ } else {
71
+ log(' ⚠ 未找到编辑器切换按钮,尝试通过 aria-pressed 定位')
72
+ const altBtn = await page.$('button[aria-pressed="false"]')
73
+ if (altBtn) await altBtn.click({ force: true })
74
+ await page.waitForTimeout(500)
75
+ }
76
+
77
+ // ── 处理项目选择对话框 ──────────────────────────────────
78
+ const dialogVisible = await page.locator('.el-dialog, .el-drawer, .el-message-box').first().isVisible().catch(() => false)
79
+ if (dialogVisible) {
80
+ log('[0] 检测到对话框,关闭/打开')
81
+ try {
82
+ await clickByText(page, 'body', '打开')
83
+ log(' - 点击"打开"')
84
+ } catch {
85
+ log(' - 没有"打开"按钮,尝试"取消"')
86
+ await clickByText(page, 'body', '取消').catch(() => {})
87
+ }
88
+ await page.waitForTimeout(800)
89
+ }
90
+
91
+ // ── 处理侧边栏折叠 ─────────────────────────────────────
92
+ const sidebarVisible = await page.evaluate(() => {
93
+ const el = document.querySelector('.sidebar, .file-sidebar, .editor-sidebar')
94
+ return el && el.offsetWidth > 50
95
+ })
96
+ log(`[0] 侧边栏可见: ${sidebarVisible}`)
97
+ if (!sidebarVisible) {
98
+ const toggleBtns = await page.$$('button')
99
+ for (const b of toggleBtns) {
100
+ const text = (await b.textContent() || '').trim()
101
+ const aria = await b.getAttribute('aria-label') || ''
102
+ if (aria.includes('侧边栏') || aria.includes('sidebar') || aria.includes('资源') || text.includes('资源')) {
103
+ await b.click({ force: true })
104
+ log(` - 点击 toggle 按钮: aria="${aria}"`)
105
+ break
106
+ }
107
+ }
108
+ await page.waitForTimeout(500)
109
+ }
110
+
111
+ // 等文件树节点加载
112
+ await page.waitForFunction(() => {
113
+ const el = document.querySelector('.sidebar-tree')
114
+ return el && el.querySelectorAll('.tree-node').length > 0
115
+ }, { timeout: 30_000 })
116
+ log('[0] 文件树已渲染')
117
+ await page.waitForTimeout(500)
118
+ await shot(page, '02-tree-loaded.png')
119
+
120
+ // ── 0. 标题栏 3 个图标回归验证 ───────────────────────────
121
+ const headerBtns = await page.$$('.sidebar-actions button, .sidebar-header button')
122
+ log(`[0] 标题栏按钮数: ${headerBtns.length}`)
123
+ // 取可见且能点击的最后那个按钮(刷新)
124
+ let refreshClicked = false
125
+ for (const b of headerBtns) {
126
+ const visible = await b.isVisible().catch(() => false)
127
+ if (visible) {
128
+ await b.click({ force: true })
129
+ log(' - 点击一个标题栏按钮')
130
+ refreshClicked = true
131
+ break
132
+ }
133
+ }
134
+ if (!refreshClicked) log(' ⚠ 没有可见的标题栏按钮可点')
135
+ await page.waitForTimeout(500)
136
+ // 关闭任何弹出输入框
137
+ await page.keyboard.press('Escape')
138
+ await page.mouse.click(800, 400)
139
+ await page.waitForTimeout(200)
140
+ await shot(page, '03-after-header-click.png')
141
+
142
+ // ── 1. 搜索常见文件名 ────────────────────────────────────
143
+ const input = page.locator('.sidebar-search-input')
144
+ await input.click()
145
+ await input.fill('package.json')
146
+ await page.waitForTimeout(400)
147
+ let nodes = await page.$$eval('.sidebar-tree .tree-node', (els) =>
148
+ els.map((el) => el.textContent.trim()).filter(Boolean)
149
+ )
150
+ log(`[1] search=package.json → ${nodes.length} 节点:`, nodes.slice(0, 8))
151
+ await shot(page, '04-search-package.png')
152
+ const hit1 = nodes.some((t) => t.includes('package.json'))
153
+ if (!hit1) throw new Error('搜索 package.json 未命中')
154
+
155
+ await input.fill('README.md')
156
+ await page.waitForTimeout(400)
157
+ nodes = await page.$$eval('.sidebar-tree .tree-node', (els) =>
158
+ els.map((el) => el.textContent.trim()).filter(Boolean)
159
+ )
160
+ log(`[1] search=README.md → ${nodes.length} 节点:`, nodes.slice(0, 8))
161
+ await shot(page, '05-search-readme.png')
162
+ const hit2 = nodes.some((t) => t.includes('README.md'))
163
+ if (!hit2) throw new Error('搜索 README.md 未命中')
164
+
165
+ // 命中高亮
166
+ const hitSpans = await page.$$eval('.tree-name-hit', (els) => els.length)
167
+ log(` - 高亮片段数: ${hitSpans}`)
168
+ if (hitSpans === 0) throw new Error('命中高亮未渲染')
169
+
170
+ // ── 2. 搜索目录名 (src / views) ─────────────────────────
171
+ // 先验证根目录搜索(不依赖懒加载)
172
+ await input.fill('src')
173
+ await page.waitForTimeout(400)
174
+ nodes = await page.$$eval('.sidebar-tree .tree-node', (els) =>
175
+ els.map((el) => el.textContent.trim()).filter(Boolean)
176
+ )
177
+ log(`[2] search=src → ${nodes.length} 节点:`, nodes.slice(0, 12))
178
+ await shot(page, '06-search-src.png')
179
+ const hasSrc = nodes.some((t) => t === 'src' || t.startsWith('src'))
180
+ if (!hasSrc) throw new Error('搜索 src 目录未命中')
181
+
182
+ // 同时验证其他目录名(.agents / .github / node_modules / scripts 都在根目录)
183
+ for (const dir of ['.git', '.github', 'scripts', 'node_modules']) {
184
+ await input.fill(dir)
185
+ await page.waitForTimeout(400)
186
+ const ns = await page.$$eval('.sidebar-tree .tree-node', (els) =>
187
+ els.map((el) => el.textContent.trim()).filter(Boolean)
188
+ )
189
+ log(`[2] search=${dir} → ${ns.length} 节点:`, ns.slice(0, 8))
190
+ const has = ns.some((t) => t === dir || t.startsWith(dir) || t.toLowerCase().includes(dir.replace('.', '')))
191
+ if (has) log(` ✓ ${dir} 命中`)
192
+ else log(` ⚠ ${dir} 未命中 (预期可能因目录名匹配规则有差异)`)
193
+ }
194
+ await shot(page, '07-search-dirs.png')
195
+
196
+ // ── 3. 不存在的关键字 → 空态 ─────────────────────────────
197
+ await input.fill('xxxxnomatch12345')
198
+ await page.waitForTimeout(400)
199
+ nodes = await page.$$eval('.sidebar-tree .tree-node', (els) => els.length)
200
+ const emptyText = await page.$eval('.tree-empty', (el) => el.textContent.trim()).catch(() => '')
201
+ log(`[3] search=xxxxnomatch12345 → 节点数 ${nodes}, 空态文案: "${emptyText}"`)
202
+ await shot(page, '08-no-match.png')
203
+ if (nodes !== 0) throw new Error(`不匹配关键字应显示 0 节点, 实际 ${nodes}`)
204
+ if (!emptyText.includes('未找到匹配文件')) throw new Error('空态文案缺失')
205
+
206
+ // ── 3.5 Esc 清空 ─────────────────────────────────────────
207
+ await page.keyboard.press('Escape')
208
+ await page.waitForTimeout(200)
209
+ const inputVal = await input.inputValue()
210
+ log(`[3.5] Esc 后输入框值: "${inputVal}"`)
211
+ if (inputVal !== '') throw new Error('Esc 未清空输入框')
212
+
213
+ // ── 4. 主题切换(深色/浅色) ──────────────────────────────
214
+ // 项目本身没有 UI 主题切换按钮(主题跟随系统),这里手动设置 data-theme 验证搜索框在两种主题下样式自适应
215
+ const beforeTheme = await page.evaluate(() => document.documentElement.getAttribute('data-theme'))
216
+ log(`[4] 当前 data-theme: ${beforeTheme || '(跟随系统/默认)'}`)
217
+ const darkStyle = await page.$eval('.sidebar-search-input', (el) => {
218
+ const s = getComputedStyle(el)
219
+ return { bg: s.backgroundColor, color: s.color, border: s.borderColor }
220
+ })
221
+ log(` - 深色主题下搜索框样式: ${JSON.stringify(darkStyle)}`)
222
+ await shot(page, '09-theme-dark.png')
223
+
224
+ // 切换到浅色主题
225
+ await page.evaluate(() => {
226
+ document.documentElement.setAttribute('data-theme', 'light')
227
+ })
228
+ await page.waitForTimeout(400)
229
+ const lightStyle = await page.$eval('.sidebar-search-input', (el) => {
230
+ const s = getComputedStyle(el)
231
+ return { bg: s.backgroundColor, color: s.color, border: s.borderColor }
232
+ })
233
+ log(`[4] 浅色主题下搜索框样式: ${JSON.stringify(lightStyle)}`)
234
+ // 验证浅色主题下背景变浅(应该与深色不同)
235
+ if (darkStyle.bg !== lightStyle.bg || darkStyle.color !== lightStyle.color) {
236
+ log(' ✓ 主题切换后搜索框样式确实改变(自适应)')
237
+ } else {
238
+ log(' ⚠ 浅色/深色样式相同 — 可能主题 token 未差异化(非阻塞)')
239
+ }
240
+ await shot(page, '10-theme-light.png')
241
+
242
+ // 恢复深色
243
+ await page.evaluate(() => {
244
+ document.documentElement.removeAttribute('data-theme')
245
+ })
246
+ await page.waitForTimeout(300)
247
+
248
+ // ── 5. Ctrl+F 快捷键聚焦 ─────────────────────────────────
249
+ await page.mouse.click(800, 400)
250
+ await page.waitForTimeout(200)
251
+ await page.keyboard.press('Control+f')
252
+ await page.waitForTimeout(300)
253
+ const focused = await page.evaluate(() => document.activeElement?.classList?.contains('sidebar-search-input'))
254
+ log(`[5] Ctrl+F 后聚焦于搜索框: ${focused}`)
255
+ await shot(page, '10-ctrl-f-focus.png')
256
+
257
+ log('─'.repeat(50))
258
+ // 过滤掉已知的非致命错误(Socket 离线 / Monaco worker 警告)
259
+ const fatal = errors.filter((e) =>
260
+ !e.includes('Socket') &&
261
+ !e.includes('worker') &&
262
+ !e.includes('monaco')
263
+ )
264
+ if (fatal.length) {
265
+ log('⚠ 致命错误:')
266
+ for (const e of fatal) log(' ', e)
267
+ } else {
268
+ log(`✓ 无致命错误 (共 ${errors.length} 条非致命警告)`)
269
+ }
270
+ log('✓ 全部检查通过')
271
+ log(`截图已保存到: ${screenshotDir}`)
272
+
273
+ await browser.close()
274
+ process.exit(fatal.length ? 1 : 0)
275
+ }
276
+
277
+ run().catch((e) => {
278
+ console.error('[verify] ✗', e.message)
279
+ console.error(e.stack)
280
+ process.exit(1)
281
+ })
@@ -0,0 +1,284 @@
1
+ // scripts/verify-subtask-row.mjs
2
+ import { chromium } from 'playwright'
3
+
4
+ const URL = 'http://localhost:5544/'
5
+ const SCREENSHOT_DIR = 'C:\\Users\\xuze3\\.zen-gitsync\\workbench-images\\_task-mqetoc9k-qc2va6'
6
+ const consoleErrors = []
7
+ const pageErrors = []
8
+ const log = (...a) => console.log('[verify]', ...a)
9
+ async function snapshot(page, name) {
10
+ const path = `${SCREENSHOT_DIR}\\verify-${name}.png`
11
+ try { await page.screenshot({ path, fullPage: false }); log('📸', name) } catch {}
12
+ }
13
+
14
+ // 先注入充足 done 子任务,避免被前面删除/取消用完
15
+ async function ensureTestData() {
16
+ const r = await fetch('http://localhost:5544/api/workbench/tasks')
17
+ const text = await r.text()
18
+ const existing = text ? JSON.parse(text) : {}
19
+ let target = existing.tasks?.find(t => t.title?.includes('verify-task'))
20
+ if (!target) {
21
+ const body = {
22
+ title: '【验证】子任务行交互测试 verify-task',
23
+ type: 'complex',
24
+ subtasks: [
25
+ { id: 'v-done-1', title: '已完成A:默认折叠(用于点行展开)', status: 'done' },
26
+ { id: 'v-done-2', title: '已完成B:用于取消完成按钮冒泡测试', status: 'done' },
27
+ { id: 'v-done-3', title: '已完成C:用于键盘 Enter/Space 测试', status: 'done' }
28
+ ]
29
+ }
30
+ const r = await fetch('http://localhost:5544/api/workbench/tasks', {
31
+ method: 'POST',
32
+ headers: { 'content-type': 'application/json' },
33
+ body: JSON.stringify(body)
34
+ })
35
+ const text = await r.text()
36
+ const j = text ? JSON.parse(text) : {}
37
+ target = j.task
38
+ }
39
+ return target
40
+ }
41
+
42
+ async function main() {
43
+ const target = await ensureTestData()
44
+ log('使用任务:', target?.title, target?.id)
45
+
46
+ const browser = await chromium.launch({ headless: true })
47
+ const ctx = await browser.newContext({ viewport: { width: 1440, height: 900 } })
48
+ const page = await ctx.newPage()
49
+ page.on('console', (msg) => {
50
+ const t = msg.type()
51
+ if (t === 'error' || t === 'warning') consoleErrors.push(`[${t}] ${msg.text()}`)
52
+ })
53
+ page.on('pageerror', (e) => pageErrors.push(String(e)))
54
+
55
+ await page.goto(URL, { waitUntil: 'domcontentloaded' })
56
+ await page.waitForSelector('.activity-bar', { timeout: 20000 })
57
+ await page.locator('.activity-btn').nth(3).click()
58
+ await page.waitForSelector('.workbench-pane', { timeout: 10000 })
59
+ await page.waitForTimeout(3000)
60
+
61
+ // 先展开所有项目分组(含 "未关联项目" / "其他项目"),再点 verify-task
62
+ await page.evaluate(() => {
63
+ document.querySelectorAll('.wb-group-header').forEach(h => {
64
+ if (h.textContent?.includes('收起')) h.click()
65
+ })
66
+ })
67
+ await page.waitForTimeout(500)
68
+
69
+ const clicked = await page.evaluate((title) => {
70
+ const items = Array.from(document.querySelectorAll('.wb-task-item'))
71
+ const target = items.find(el => el.textContent?.includes(title))
72
+ if (target) { target.click(); return true }
73
+ return false
74
+ }, 'verify-task')
75
+ log('侧栏点击 verify-task:', clicked)
76
+ await page.waitForTimeout(1500)
77
+ await snapshot(page, '01-loaded')
78
+
79
+ // 兜底:如果侧栏没有 verify-task(例如其他项目分组未展开),强制把后端任务的
80
+ // projectPath 改成当前项目再刷新——但这会改用户数据,跳过。
81
+ // 我们改为:直接选第一个 subtaskCount>0 的 task
82
+ if ((await page.locator('.wb-sub-item').count()) === 0) {
83
+ log('verify-task 未选中,fallback 到第一个有子任务的任务')
84
+ await page.evaluate(() => {
85
+ const items = Array.from(document.querySelectorAll('.wb-task-item'))
86
+ const withSub = items.find(el => el.textContent?.match(/\d+\/\d+/))
87
+ if (withSub) withSub.click()
88
+ })
89
+ await page.waitForTimeout(1000)
90
+ }
91
+
92
+ const total = await page.locator('.wb-sub-item').count()
93
+ const collapsed = await page.locator('.wb-sub-item.is-collapsed').count()
94
+ log('子任务总数:', total, '折叠态:', collapsed)
95
+ if (collapsed === 0) { log('❌ 无折叠行'); await browser.close(); return }
96
+
97
+ // ── (1) 悬停高亮 ──────────────────────────────────────
98
+ const firstCollapsed = page.locator('.wb-sub-item.is-collapsed').first()
99
+ const firstRow = firstCollapsed.locator('.wb-sub-item__row--compact')
100
+ await firstRow.scrollIntoViewIfNeeded()
101
+ await page.waitForTimeout(200)
102
+ const baseBg = await firstRow.evaluate(el => getComputedStyle(el).backgroundColor)
103
+ await firstRow.hover()
104
+ await page.waitForTimeout(300)
105
+ const hoverBg = await firstRow.evaluate(el => getComputedStyle(el).backgroundColor)
106
+ log('base 背景:', baseBg, '→ hover 背景:', hoverBg)
107
+ if (baseBg !== hoverBg) log('✅ 悬停态背景色变化 OK')
108
+ else log('⚠️ 悬停前后背景一致')
109
+ await snapshot(page, '02-hover')
110
+
111
+ // ── (2) 点击行展开 ───────────────────────────────────
112
+ // 记录当前第一个 collapsed 行的索引
113
+ const idxBefore = await page.evaluate(() => {
114
+ const items = Array.from(document.querySelectorAll('.wb-sub-item'))
115
+ return items.findIndex(el => el.classList.contains('is-collapsed'))
116
+ })
117
+ log('第一个折叠行 index:', idxBefore)
118
+ // 点击 title 区域触发整行 click(不点按钮)
119
+ await page.locator('.wb-sub-item.is-collapsed .wb-sub-item__title-compact').first().click()
120
+ await page.waitForTimeout(500)
121
+ const idxAfter = await page.evaluate(() => {
122
+ const items = Array.from(document.querySelectorAll('.wb-sub-item'))
123
+ return items.findIndex(el => el.classList.contains('is-collapsed'))
124
+ })
125
+ log('点击后第一个折叠行 index:', idxAfter)
126
+ const expandedCount = await page.locator('.wb-sub-item:not(.is-collapsed)').count()
127
+ if (idxAfter !== idxBefore && expandedCount > 0) log('✅ 点击行展开 OK')
128
+ else log('❌ 点击行未展开')
129
+ await snapshot(page, '03-row-expanded')
130
+
131
+ // ── (3) 取消完成按钮无冒泡 ───────────────────────────
132
+ // 关键验证:点按钮后,行的 click handler 不应被触发(@click.stop)。
133
+ // 用 BUBBLE phase 监听 row 的 click(capture phase 永远会触发,与 .stop 无关)。
134
+ // 同时拦截 fetch 让 PUT 成功,避免后端 500 影响判断。
135
+ const undoResult = await page.evaluate(async () => {
136
+ const item = document.querySelector('.wb-sub-item.is-collapsed')
137
+ if (!item) return { found: false }
138
+ const row = item.querySelector('.wb-sub-item__row--compact')
139
+ if (!row) return { found: false }
140
+ let rowClicked = 0
141
+ // bubble phase,.stop 会阻止冒泡到这里
142
+ row.addEventListener('click', () => rowClicked++, false)
143
+ // mock fetch 让 cancelDone 不会因为测试数据问题失败
144
+ const realFetch = window.fetch
145
+ window.fetch = (url, opts) => {
146
+ const s = String(url)
147
+ // 拦截对 /api/workbench/tasks 的写操作
148
+ if (s.includes('/api/workbench/tasks') && (opts?.method === 'POST' || opts?.method === 'PUT')) {
149
+ return Promise.resolve(new Response(JSON.stringify({ success: true, task: { subtasks: [] } }), { status: 200, headers: { 'content-type': 'application/json' } }))
150
+ }
151
+ return realFetch(url, opts)
152
+ }
153
+ const undoBtn = item.querySelector('.wb-sub-item__undo')
154
+ if (!undoBtn) return { found: false }
155
+ const beforeCollapsed = item.classList.contains('is-collapsed')
156
+ undoBtn.click()
157
+ await new Promise(r => setTimeout(r, 700))
158
+ window.fetch = realFetch
159
+ const afterCollapsed = item.classList.contains('is-collapsed')
160
+ return { found: true, rowClicked, beforeCollapsed, afterCollapsed }
161
+ })
162
+ log('「取消完成」→ row bubble click 触发次数:', undoResult.rowClicked, 'isCollapsed:', undoResult.beforeCollapsed, '→', undoResult.afterCollapsed)
163
+ if (undoResult.rowClicked === 0) log('✅ 取消完成按钮:行 click handler 未触发(@click.stop 生效)')
164
+ else log('❌ 取消完成按钮:行 click handler 被触发 ' + undoResult.rowClicked + ' 次')
165
+ await snapshot(page, '04-after-undo')
166
+
167
+ // ── (4) × 删除按钮无冒泡 ─────────────────────────────
168
+ const delResult = await page.evaluate(async () => {
169
+ const items = Array.from(document.querySelectorAll('.wb-sub-item'))
170
+ if (!items.length) return { found: false }
171
+ const item = items[items.length - 1] // 删最后一个,避开折叠测试行
172
+ let flips = 0
173
+ const rowEl = item.querySelector('.wb-sub-item__row--compact') || item.querySelector('.wb-sub-item__row')
174
+ const obs = new MutationObserver(muts => {
175
+ for (const m of muts) {
176
+ if (m.type === 'attributes' && m.attributeName === 'class') flips++
177
+ }
178
+ })
179
+ obs.observe(item, { attributes: true })
180
+ if (rowEl) obs.observe(rowEl, { attributes: true })
181
+ const realFetch = window.fetch
182
+ window.fetch = (url, opts) => {
183
+ if (String(url).includes('/api/workbench/subtasks') && opts?.method === 'DELETE') {
184
+ return Promise.resolve(new Response('{"success":true}', { status: 200 }))
185
+ }
186
+ return realFetch(url, opts)
187
+ }
188
+ const delBtn = item.querySelector('.wb-sub-item__del')
189
+ if (!delBtn) return { found: false }
190
+ const before = document.querySelectorAll('.wb-sub-item').length
191
+ delBtn.click()
192
+ await new Promise(r => setTimeout(r, 600))
193
+ window.fetch = realFetch
194
+ const after = document.querySelectorAll('.wb-sub-item').length
195
+ obs.disconnect()
196
+ return { found: true, flips, before, after }
197
+ })
198
+ log('× 按钮 → class 翻转:', delResult.flips, '行数:', delResult.before, '→', delResult.after)
199
+ if (delResult.found && delResult.after === delResult.before - 1) log('✅ × 删除按钮:行被删除,无冒泡副作用')
200
+ await snapshot(page, '05-after-del')
201
+
202
+ // ── (5) 键盘 Tab + Enter / Space ─────────────────────
203
+ // 通过标题定位 sub,避开 Vue 重渲染时 activeElement 丢失问题
204
+ const kbdRow = page.locator('.wb-sub-item.is-collapsed .wb-sub-item__row--compact').first()
205
+ if (await kbdRow.count()) {
206
+ const titleBefore = (await kbdRow.locator('.wb-sub-item__title-compact').first().textContent())?.trim()
207
+ log('键盘测试目标标题:', titleBefore?.slice(0, 30))
208
+ await kbdRow.focus()
209
+ await page.waitForTimeout(200)
210
+ const focused = await page.evaluate(() => document.activeElement?.classList?.contains('wb-sub-item__row--compact'))
211
+ log('行获得焦点:', focused)
212
+ await snapshot(page, '06-focused')
213
+
214
+ await page.keyboard.press('Enter')
215
+ await page.waitForTimeout(500)
216
+ const afterEnter = await page.evaluate((title) => {
217
+ const items = Array.from(document.querySelectorAll('.wb-sub-item'))
218
+ const t = (title || '').trim()
219
+ for (const it of items) {
220
+ const compact = it.querySelector('.wb-sub-item__title-compact')
221
+ const input = it.querySelector('input.wb-input')
222
+ const text = (compact?.textContent || input?.value || '').trim()
223
+ if (text === t) return it.classList.contains('is-collapsed')
224
+ }
225
+ return null
226
+ }, titleBefore)
227
+ log('Enter 后该 sub isCollapsed:', afterEnter)
228
+ if (afterEnter === false) log('✅ 键盘 Enter 触发展开 OK')
229
+ else log('❌ 键盘 Enter 未触发展开')
230
+
231
+ // Space 折叠回去(同样按标题定位)
232
+ if (afterEnter === false) {
233
+ // 找标题匹配的那条展开行的 input,定位其所在 wb-sub-item__row
234
+ const handle = await page.evaluateHandle((title) => {
235
+ const t = (title || '').trim()
236
+ const items = Array.from(document.querySelectorAll('.wb-sub-item:not(.is-collapsed)'))
237
+ for (const it of items) {
238
+ const input = it.querySelector('input.wb-input')
239
+ if ((input?.value || '').trim() === t) return it.querySelector('.wb-sub-item__row')
240
+ }
241
+ return null
242
+ }, titleBefore)
243
+ if (handle) {
244
+ const el = handle.asElement()
245
+ if (el) {
246
+ await el.focus()
247
+ await page.waitForTimeout(200)
248
+ await page.keyboard.press(' ')
249
+ await page.waitForTimeout(500)
250
+ const afterSpace = await page.evaluate((title) => {
251
+ const items = Array.from(document.querySelectorAll('.wb-sub-item'))
252
+ const t = (title || '').trim()
253
+ for (const it of items) {
254
+ const compact = it.querySelector('.wb-sub-item__title-compact')
255
+ const input = it.querySelector('input.wb-input')
256
+ const text = (compact?.textContent || input?.value || '').trim()
257
+ if (text === t) return it.classList.contains('is-collapsed')
258
+ }
259
+ return null
260
+ }, titleBefore)
261
+ log('Space 后该 sub isCollapsed:', afterSpace)
262
+ if (afterSpace === true) log('✅ 键盘 Space 触发折叠 OK')
263
+ else log('❌ 键盘 Space 未触发折叠')
264
+ }
265
+ } else {
266
+ log('⚠️ 找不到展开后的行进行 Space 测试')
267
+ }
268
+ }
269
+ await snapshot(page, '07-after-keyboard')
270
+ } else {
271
+ log('ℹ️ 无可测折叠行,跳过键盘测试')
272
+ }
273
+
274
+ log('\n=== 验证总结 ===')
275
+ log('Console errors/warnings:', consoleErrors.length)
276
+ consoleErrors.forEach(e => log(' ·', e))
277
+ log('Page errors:', pageErrors.length)
278
+ pageErrors.forEach(e => log(' ·', e))
279
+
280
+ await browser.close()
281
+ log('done.')
282
+ }
283
+
284
+ main().catch(e => { console.error('FATAL:', e); process.exit(1) })
@@ -0,0 +1 @@
1
+ .md-preview[data-v-d602e5a5]{color:inherit;word-break:break-word;padding:20px 24px;font-size:14px;line-height:1.6}.md-preview[data-v-d602e5a5] h1,.md-preview[data-v-d602e5a5] h2,.md-preview[data-v-d602e5a5] h3,.md-preview[data-v-d602e5a5] h4,.md-preview[data-v-d602e5a5] h5,.md-preview[data-v-d602e5a5] h6{border-bottom:1px solid var(--border-color,#d0d7de);margin-top:24px;margin-bottom:16px;padding-bottom:.3em;font-weight:600}.md-preview[data-v-d602e5a5] h1{font-size:1.8em}.md-preview[data-v-d602e5a5] h2{font-size:1.4em}.md-preview[data-v-d602e5a5] h3{font-size:1.2em}.md-preview[data-v-d602e5a5] p{margin:0 0 14px}.md-preview[data-v-d602e5a5] a{color:var(--link-color,#0969da);text-decoration:none}.md-preview[data-v-d602e5a5] a:hover{text-decoration:underline}.md-preview[data-v-d602e5a5] code{background:var(--code-bg,#f6f8fa);border-radius:4px;padding:2px 5px;font-family:JetBrains Mono,Fira Code,Consolas,monospace;font-size:87%}.md-preview[data-v-d602e5a5] pre{background:var(--code-bg,#f6f8fa);border-radius:6px;margin:0 0 16px;padding:14px 16px;overflow:auto}.md-preview[data-v-d602e5a5] pre code{background:0 0;padding:0;font-size:100%}.md-preview[data-v-d602e5a5] blockquote{border-left:3px solid var(--border-color,#d0d7de);color:var(--text-secondary,#656d76);margin:0 0 16px;padding:0 16px}.md-preview[data-v-d602e5a5] img{border-radius:4px;max-width:100%}.md-preview[data-v-d602e5a5] hr{border:none;border-top:1px solid var(--border-color,#d0d7de);margin:24px 0}.md-preview[data-v-d602e5a5] table{border-collapse:collapse;width:100%;margin-bottom:16px}.md-preview[data-v-d602e5a5] th,.md-preview[data-v-d602e5a5] td{border:1px solid var(--border-color,#d0d7de);padding:6px 13px}.md-preview[data-v-d602e5a5] thead tr{background:var(--code-bg,#f6f8fa)}.md-preview[data-v-d602e5a5] ul,.md-preview[data-v-d602e5a5] ol{margin-bottom:16px;padding-left:2em}.md-preview[data-v-d602e5a5] li{margin:4px 0}.md-segment[data-v-d602e5a5]{display:block}.md-mindmap[data-v-d602e5a5]{border:1px solid var(--border-color,#d0d7de);background:var(--bg-panel,#fff);border-radius:8px;margin:18px 0 28px;overflow:hidden}.md-mindmap-title[data-v-d602e5a5]{color:var(--text-secondary,#656d76);background:var(--code-bg,#f6f8fa);border-bottom:1px solid var(--border-color,#d0d7de);letter-spacing:.5px;padding:8px 12px;font-size:12px;font-weight:600}.md-mindmap-canvas[data-v-d602e5a5]{width:100%;height:480px;display:block}.mindmap-preview[data-v-5988cf1a]{background:var(--bg-container,#fff);flex-direction:column;width:100%;height:100%;display:flex}.mindmap-preview-header[data-v-5988cf1a]{border-bottom:1px solid var(--border-color,#d0d7de);background:var(--bg-panel,#f6f8fa);align-items:center;gap:12px;padding:8px 14px;font-size:12px;display:flex}.mindmap-preview-title[data-v-5988cf1a]{color:var(--text-primary,#1f2328);letter-spacing:.5px;font-weight:600}.mindmap-preview-hint[data-v-5988cf1a]{color:var(--text-secondary,#656d76);font-size:11px}.mindmap-preview-canvas[data-v-5988cf1a]{flex:1;width:100%;min-height:0}.mindmap-preview-canvas[data-v-5988cf1a] .zm-mindmap,.mindmap-preview-canvas[data-v-5988cf1a]>*{width:100%;height:100%}.editor-view[data-v-212f0a2a]{background:var(--bg-container);border:1px solid var(--border-color);width:100%;height:100%;box-shadow:var(--shadow-sm);border-radius:0;display:flex;overflow:hidden}.editor-sidebar[data-v-212f0a2a]{border-right:1px solid var(--border-color);background:var(--bg-panel);flex-direction:column;flex-shrink:0;display:flex;overflow:hidden}.sidebar-header[data-v-212f0a2a]{border-bottom:1px solid var(--border-color);flex-shrink:0;justify-content:space-between;align-items:center;padding:8px 10px 6px;display:flex}.sidebar-title[data-v-212f0a2a]{letter-spacing:.08em;text-transform:uppercase;color:var(--text-secondary);-webkit-user-select:none;user-select:none;font-size:11px;font-weight:700}.sidebar-action-btn[data-v-212f0a2a]{cursor:pointer;color:var(--text-tertiary);border-radius:var(--radius-base);background:0 0;border:none;align-items:center;padding:3px;display:flex}.sidebar-action-btn[data-v-212f0a2a]:hover{color:var(--text-primary);background:var(--bg-hover)}.sidebar-search[data-v-212f0a2a]{border-bottom:1px solid var(--border-color);flex-shrink:0;align-items:center;padding:6px 10px;display:flex;position:relative}.sidebar-search-icon[data-v-212f0a2a]{color:var(--text-tertiary);pointer-events:none;position:absolute;left:18px}.sidebar-search-input[data-v-212f0a2a]{min-width:0;height:26px;color:var(--text-primary);background:var(--bg-input,#ffffff0a);border:1px solid var(--border-color);border-radius:var(--radius-base);outline:none;flex:1;padding:0 24px 0 26px;font-size:12px;transition:border-color .12s,background .12s}.sidebar-search-input[data-v-212f0a2a]::placeholder{color:var(--text-tertiary)}.sidebar-search-input[data-v-212f0a2a]:hover{border-color:var(--text-tertiary)}.sidebar-search-input[data-v-212f0a2a]:focus{border-color:var(--accent-color,#3b82f6);background:var(--bg-panel)}.sidebar-search-clear[data-v-212f0a2a]{cursor:pointer;width:18px;height:18px;color:var(--text-tertiary);background:0 0;border:none;border-radius:50%;justify-content:center;align-items:center;padding:0;transition:background .12s,color .12s;display:flex;position:absolute;right:14px}.sidebar-search-clear[data-v-212f0a2a]:hover{background:var(--bg-hover);color:var(--text-primary)}.tree-name-hit[data-v-212f0a2a]{color:var(--accent-color,#f59e0b);background:#f59e0b26;border-radius:2px;padding:0 1px}.sidebar-tree[data-v-212f0a2a]{flex:1;padding:4px 0;overflow:hidden auto}.tree-node[data-v-212f0a2a]{cursor:pointer;-webkit-user-select:none;user-select:none;height:24px;color:var(--text-primary);white-space:nowrap;border-radius:4px;align-items:center;gap:4px;padding-right:8px;font-size:13px;transition:background .1s;display:flex;overflow:hidden}.tree-node[data-v-212f0a2a]:hover{background:var(--bg-hover)}.tree-node--active[data-v-212f0a2a]{color:var(--color-primary);background:#3b82f61f}.tree-node--selected[data-v-212f0a2a]{outline-offset:-1px;background:#63b3ed26;outline:1px solid #63b3ed59}.tree-arrow[data-v-212f0a2a]{color:var(--text-tertiary);flex-shrink:0;align-items:center;width:12px;transition:transform .15s;display:flex}.tree-arrow.expanded[data-v-212f0a2a]{transform:rotate(90deg)}.tree-arrow-spacer[data-v-212f0a2a]{flex-shrink:0;width:12px}.tree-icon[data-v-212f0a2a]{flex-shrink:0;align-items:center;font-size:14px;line-height:1;display:flex}.tree-icon.mit-icon[data-v-212f0a2a]{fill:currentColor;vertical-align:middle;width:14px;height:14px;display:inline-block}.tree-icon--dir[data-v-212f0a2a]{color:#e8b84b}.tree-name[data-v-212f0a2a]{text-overflow:ellipsis;flex:1;min-width:0;font-size:13px;overflow:hidden}.tree-loading[data-v-212f0a2a]{border:1.5px solid var(--color-primary);border-top-color:#0000;border-radius:50%;flex-shrink:0;width:10px;height:10px;animation:.8s linear infinite spin-212f0a2a;display:inline-block}.tree-empty[data-v-212f0a2a]{color:var(--text-tertiary);text-align:center;padding:24px 12px;font-size:12px}.sidebar-loading[data-v-212f0a2a]{flex:1;justify-content:center;align-items:center;display:flex}.spin-icon[data-v-212f0a2a]{border:2px solid var(--color-primary);border-top-color:#0000;border-radius:50%;width:20px;height:20px;animation:.8s linear infinite spin-212f0a2a;display:inline-block}@keyframes spin-212f0a2a{to{transform:rotate(360deg)}}.editor-resizer[data-v-212f0a2a]{cursor:col-resize;z-index:2;background:0 0;flex-shrink:0;width:6px;transition:background .15s;position:relative}.editor-resizer[data-v-212f0a2a]:after{content:"";background:var(--color-gray-300);border-radius:2px;width:2px;height:32px;transition:background .15s,height .15s;position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)}.editor-resizer[data-v-212f0a2a]:hover{background:#3b82f60f}.editor-resizer[data-v-212f0a2a]:hover:after{background:var(--color-primary);height:48px;box-shadow:0 0 8px #3b82f666}.editor-main[data-v-212f0a2a]{flex-direction:column;flex:1;display:flex;overflow:hidden}.editor-tabs[data-v-212f0a2a]{border-bottom:1px solid var(--border-color);background:var(--bg-panel);scrollbar-width:none;flex-shrink:0;display:flex;overflow:auto hidden}.editor-tabs[data-v-212f0a2a]::-webkit-scrollbar{display:none}.editor-tab[data-v-212f0a2a]{border-right:1px solid var(--border-color);cursor:pointer;height:34px;color:var(--text-secondary);-webkit-user-select:none;user-select:none;flex-shrink:0;align-items:center;gap:6px;min-width:80px;max-width:200px;padding:0 12px;font-size:12.5px;transition:background .1s,color .1s;display:flex}.editor-tab[data-v-212f0a2a]:hover{background:var(--bg-hover);color:var(--text-primary)}.editor-tab.active[data-v-212f0a2a]{background:var(--bg-container);color:var(--text-primary);border-bottom:2px solid var(--color-primary)}.tab-name[data-v-212f0a2a]{text-overflow:ellipsis;white-space:nowrap;flex:1;overflow:hidden}.tab-dirty-dot[data-v-212f0a2a]{background:var(--color-warning);border-radius:50%;flex-shrink:0;width:6px;height:6px}.tab-close[data-v-212f0a2a]{cursor:pointer;color:var(--text-tertiary);opacity:0;background:0 0;border:none;border-radius:3px;flex-shrink:0;align-items:center;padding:2px;transition:opacity .1s,background .1s;display:flex}.editor-tab:hover .tab-close[data-v-212f0a2a],.editor-tab.active .tab-close[data-v-212f0a2a]{opacity:1}.tab-close[data-v-212f0a2a]:hover{background:var(--bg-hover);color:var(--color-danger)}.editor-empty[data-v-212f0a2a]{color:var(--text-tertiary);-webkit-user-select:none;user-select:none;flex-direction:column;flex:1;justify-content:center;align-items:center;gap:10px;font-size:13px;display:flex}.editor-empty p[data-v-212f0a2a]{margin:0}.editor-empty-hint[data-v-212f0a2a]{opacity:.6;font-size:11px}.monaco-container[data-v-212f0a2a]{flex:1;min-width:0;overflow:hidden}.monaco-container.hidden[data-v-212f0a2a]{display:none}.image-tab-placeholder[data-v-212f0a2a]{justify-content:center;align-items:center;gap:var(--spacing-sm);color:var(--text-secondary);background:var(--bg-container);text-align:center;padding:var(--spacing-lg);flex-direction:column;flex:1;display:flex}.image-tab-placeholder-title[data-v-212f0a2a]{font-size:var(--font-size-md);font-weight:var(--font-weight-medium);color:var(--text-primary);word-break:break-all;margin:0}.image-tab-placeholder-hint[data-v-212f0a2a]{font-size:var(--font-size-sm);color:var(--text-tertiary);margin:0}.editor-body[data-v-212f0a2a]{flex:1;min-height:0;display:flex;overflow:hidden}.preview-resizer[data-v-212f0a2a]{cursor:col-resize;z-index:2;background:0 0;flex-shrink:0;order:99;width:6px;margin-left:auto;transition:background .15s;position:relative}.preview-resizer[data-v-212f0a2a]:after{content:"";background:var(--color-gray-300);border-radius:2px;width:2px;height:32px;transition:background .15s,height .15s;position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)}.preview-resizer[data-v-212f0a2a]:hover{background:#3b82f60f}.preview-resizer[data-v-212f0a2a]:hover:after{background:var(--color-primary);height:48px;box-shadow:0 0 8px #3b82f666}.preview-panel[data-v-212f0a2a]{border-left:1px solid var(--border-color);background:var(--bg-panel);flex-direction:column;flex:1 1 0;min-width:200px;display:flex;overflow:hidden}.preview-header[data-v-212f0a2a]{border-bottom:1px solid var(--border-color);background:var(--bg-panel);flex-shrink:0;align-items:center;gap:6px;height:34px;padding:0 10px;display:flex}.preview-title[data-v-212f0a2a]{letter-spacing:.08em;text-transform:uppercase;color:var(--text-secondary);-webkit-user-select:none;user-select:none;font-size:11px;font-weight:700}.preview-ext-badge[data-v-212f0a2a]{color:var(--color-primary);letter-spacing:.04em;background:#3b82f626;border-radius:3px;padding:1px 5px;font-size:10px;font-weight:600}.preview-header-spacer[data-v-212f0a2a]{flex:1}.preview-close-btn[data-v-212f0a2a]{cursor:pointer;color:var(--text-tertiary);border-radius:var(--radius-base);background:0 0;border:none;align-items:center;padding:3px;transition:color .1s,background .1s;display:flex}.preview-close-btn[data-v-212f0a2a]:hover{color:var(--color-danger);background:var(--bg-hover)}.preview-body[data-v-212f0a2a]{flex-direction:column;flex:1;display:flex;overflow:hidden}.preview-iframe[data-v-212f0a2a]{background:0 0;border:none;flex:1;width:100%;height:100%}.preview-markdown[data-v-212f0a2a]{background:var(--bg-container,#fff);width:100%;height:100%;color:var(--text-primary,#1f2328);flex:1;overflow:auto}.preview-mindmap[data-v-212f0a2a]{background:var(--bg-container,#fff);flex-direction:column;flex:1;width:100%;height:100%;display:flex;overflow:hidden}.mindmap-toggle-btn.disabled[data-v-212f0a2a]{opacity:.45;cursor:not-allowed}.mindmap-toggle-btn.active[data-v-212f0a2a]{color:#fff;background:linear-gradient(#6366f1 0%,#4f46e5 100%);border-color:#4f46e5}.preview-image-wrap[data-v-212f0a2a]{background:var(--bg-container);flex:1;justify-content:center;align-items:center;padding:16px;display:flex;overflow:auto}.preview-image-wrap img[data-v-212f0a2a]{object-fit:contain;border-radius:4px;max-width:100%;max-height:100%;box-shadow:0 2px 12px #0003}.editor-tabs-spacer[data-v-212f0a2a]{flex:1}.preview-toggle-btn[data-v-212f0a2a]{cursor:pointer;height:34px;color:var(--text-secondary);white-space:nowrap;border:none;border-left:1px solid var(--border-color);background:0 0;flex-shrink:0;align-items:center;gap:5px;padding:0 10px;font-size:12px;transition:background .1s,color .1s;display:flex}.preview-toggle-btn[data-v-212f0a2a]:hover{background:var(--bg-hover);color:var(--text-primary)}.preview-toggle-btn.active[data-v-212f0a2a]{color:var(--color-primary);background:#3b82f614}.sidebar-actions[data-v-212f0a2a]{align-items:center;gap:2px;display:flex}.tree-inline-input-row[data-v-212f0a2a]{cursor:default;background:var(--bg-hover)}.tree-inline-input[data-v-212f0a2a]{background:var(--bg-container);border:1px solid var(--color-primary);min-width:0;height:20px;color:var(--text-primary);border-radius:3px;outline:none;flex:1;padding:0 5px;font-family:inherit;font-size:12.5px}.ctx-menu[data-v-212f0a2a]{z-index:9999;background:var(--bg-panel);border:1px solid var(--border-color);-webkit-user-select:none;user-select:none;border-radius:6px;min-width:160px;padding:4px;position:fixed;box-shadow:0 8px 24px #00000040}.ctx-menu-item[data-v-212f0a2a]{cursor:pointer;width:100%;color:var(--text-primary);text-align:left;background:0 0;border:none;border-radius:4px;align-items:center;gap:8px;padding:6px 10px;font-size:12.5px;transition:background .1s;display:flex}.ctx-menu-item[data-v-212f0a2a]:hover{background:var(--bg-hover)}.ctx-menu-item--danger[data-v-212f0a2a]{color:var(--color-danger)}.ctx-menu-item--danger[data-v-212f0a2a]:hover{background:#ef44441a}.ctx-menu-sep[data-v-212f0a2a]{background:var(--border-color);height:1px;margin:4px 2px}
@@ -0,0 +1,3 @@
1
+ import{o as e}from"./rolldown-runtime-BM3Ffeng.js";import{Di as t,Gi as n,Gt as r,Hi as i,Ht as a,Jn as o,Jt as s,Ki as c,Kt as ee,Li as l,Ni as u,Oi as te,Ri as d,Si as f,Ui as ne,Ut as re,Wi as p,Wt as ie,Xt as ae,Yt as oe,_i as m,bi as se,ci as h,di as g,hi as _,ji as v,ki as ce,li as le,mi as y,qi as b,qt as ue,si as de,t as fe,un as pe,vi as x,wi as me,xi as S,xt as C,zi as w}from"./vendor-CDAfMQIV.js";import{n as T,t as E}from"./_plugin-vue_export-helper-BMDM_R2D.js";import{a as he,r as ge}from"./index-BaFDF44N.js";var _e=e(a(),1),ve={class:`source-map-view`},ye={class:`sm-toolbar`},be={class:`sm-toolbar-left`},xe={class:`sm-title`},Se={class:`sm-toolbar-center`},Ce=[`placeholder`,`disabled`],we=[`disabled`],Te={class:`sm-toolbar-right`},Ee={class:`sm-body`},De={class:`sm-panel-header sm-panel-header--tabs`},Oe={key:0,class:`sm-badge`},ke={class:`sm-panel-body sm-file-tree`},Ae=[`onClick`],je={key:1,class:`sm-tree-arrow-spacer`},Me={key:2,class:`sm-tree-icon mit-icon`,"aria-hidden":`true`},Ne=[`xlink:href`],Pe={key:3,class:`sm-tree-icon mit-icon`,"aria-hidden":`true`},Fe=[`xlink:href`],Ie=[`title`],Le={key:1,class:`sm-tree-empty`},Re={class:`sm-panel-body sm-outline-body`},ze={class:`sm-badge`,style:{"margin-left":`auto`}},Be=[`onClick`],Ve=[`title`],He={key:0,class:`sm-outline-desc`},Ue={key:1,class:`sm-tree-empty`},We={class:`sm-panel sm-panel-graph`},Ge={key:0,class:`sm-project-info`},Ke={class:`sm-lang-badge`},qe={class:`sm-summary-text`},Je={class:`sm-graph-container`},Ye={class:`sm-layout-btn-wrap`},Xe=[`disabled`],Ze=[`title`],Qe={class:`sm-fn-label`},$e={key:0,class:`sm-fn-desc`},et={key:0,class:`sm-graph-empty`},tt={viewBox:`0 0 24 24`,width:`48`,height:`48`,fill:`none`,stroke:`currentColor`,"stroke-width":`1`,style:{opacity:`0.3`}},nt={key:1,class:`sm-graph-loading`},rt={class:`sm-log-header`},it={key:0,class:`sm-log-indicator`},at={class:`sm-log-body`,ref:`logBodyRef`},ot={key:0,class:`sm-empty`},st={class:`sm-panel-header`},ct={key:0,class:`sm-badge sm-badge-amber`},lt={key:0,class:`sm-node-detail`},ut={class:`sm-node-name`},dt={class:`sm-node-file`},ft={key:0},pt={key:0,class:`sm-node-desc`},mt={class:`sm-panel-body sm-source-body`},ht={key:0,class:`sm-source-overlay`},gt={key:1,class:`sm-source-overlay sm-source-placeholder`},D=E(me({__name:`SourceMapView`,setup(e){let a=i(document.documentElement.getAttribute(`data-theme`)===`dark`?`dark`:`light`);function me(){a.value=document.documentElement.getAttribute(`data-theme`)===`dark`?`dark`:`light`}let E=null,D=he(),O=i(D.currentDirectory||``),k=i(`idle`),A=i([]),_t=0,j=i(null),M=i(null),N=i(`files`),P=i(``),F=i(``),I=i(!1),L=i([]),R=i(new Set),z=i({files:!0,graph:!0,source:!0}),B=i(!1),vt=i(220),V=i(360),H=i(140),U=null,W=0,yt=0,G=0;function K(e,t){U=e,W=t.clientX,yt=t.clientY,G=e===`files`?vt.value:e===`source`?V.value:H.value,document.addEventListener(`mousemove`,bt),document.addEventListener(`mouseup`,q),t.preventDefault()}function bt(e){U&&(U===`files`?vt.value=Math.max(120,Math.min(480,G+e.clientX-W)):U===`source`?V.value=Math.max(200,Math.min(700,G+W-e.clientX)):H.value=Math.max(60,Math.min(500,G+yt-e.clientY)))}function q(){U=null,document.removeEventListener(`mousemove`,bt),document.removeEventListener(`mouseup`,q)}let J=i(null),Y=ne(null),{fitView:xt,setNodes:X,setEdges:St,getNodes:Ct,getEdges:wt,updateNodeInternals:Tt}=ae(),Z=y(()=>k.value===`scanning`||k.value===`analyzing`),Et=y(()=>a.value===`dark`?`#334155`:`#cbd5e1`),Q=y(()=>j.value?.nodes.find(e=>e.id===M.value)??null),Dt=y(()=>{if(!j.value)return[];let e=new Map;for(let t of j.value.nodes){let n=t.subsystem??`__default__`;if(!e.has(n)){let r=j.value.subsystems?.find(e=>e.name===n);e.set(n,{displayName:r?.displayName||t.subsystem||T(`@SRCMAP:默认`),color:t.subsystemColor||kt[0],nodes:[]})}e.get(n).nodes.push(t)}return[...e.entries()].map(([,e])=>e)});function $(e,t=`info`){A.value.push({id:++_t,message:e,type:t,timestamp:Date.now()}),A.value.length>200&&A.value.splice(0,A.value.length-200)}function Ot(e){let t={name:``,path:``,kind:`dir`,children:[],childMap:new Map};for(let n of e){let e=n.trim().split(`/`).filter(Boolean),r=t,i=``;for(let t=0;t<e.length;t++){let n=e[t];i=i?`${i}/${n}`:n;let a=t===e.length-1,o=r.childMap.get(n);o||(o={name:n,path:i,kind:a?`file`:`dir`,children:[],childMap:new Map},r.childMap.set(n,o),r.children.push(o)),r=o}}let n=e=>{e.sort((e,t)=>e.kind===t.kind?e.name.localeCompare(t.name):e.kind===`dir`?-1:1),e.forEach(e=>{e.kind===`dir`&&n(e.children)})};return n(t.children),t.children}let kt=[`#f59e0b`,`#3b82f6`,`#10b981`,`#8b5cf6`];function At(e){return e.subsystemColor?e.subsystemColor:e.subsystemIndex===void 0?e.importance===`high`?`#f59e0b`:e.importance===`low`?`#94a3b8`:`#3b82f6`:kt[e.subsystemIndex%kt.length]}function jt(e){return{ts:`typescript`,tsx:`typescript`,js:`javascript`,mjs:`javascript`,cjs:`javascript`,jsx:`javascript`,vue:`html`,svelte:`html`,html:`html`,py:`python`,java:`java`,go:`go`,rs:`rust`,cpp:`cpp`,cc:`cpp`,cxx:`cpp`,c:`c`,h:`c`,hpp:`cpp`,cs:`csharp`,rb:`ruby`,php:`php`,swift:`swift`,kt:`kotlin`,sh:`shell`,bash:`shell`,json:`json`,yaml:`yaml`,yml:`yaml`,md:`markdown`,css:`css`,scss:`scss`,less:`less`,sql:`sql`}[e.split(`.`).pop()?.toLowerCase()||``]||`plaintext`}function Mt(){!J.value||Y.value||(Y.value=C.create(J.value,{value:``,language:`plaintext`,theme:a.value===`dark`?`vs-dark`:`vs`,readOnly:!0,fontSize:12,lineHeight:19,fontFamily:`'JetBrains Mono', 'Fira Code', Consolas, monospace`,minimap:{enabled:!1},scrollBeyondLastLine:!1,automaticLayout:!0,wordWrap:`off`,padding:{top:8,bottom:8},scrollbar:{verticalScrollbarSize:6,horizontalScrollbarSize:6}}))}l(a,e=>{Y.value&&C.setTheme(e===`dark`?`vs-dark`:`vs`)});function Nt(e,t){let n={};t.forEach(e=>{n[e.source]||(n[e.source]=[]),n[e.source].push(e.target)});let r=new Map;e.forEach(e=>{let t=e.subsystem??`__default`;r.has(t)||r.set(t,[]),r.get(t).push(e)});let i=[],a=0;for(let[,e]of r){let t=new Set(e.map(e=>e.id)),r={},o=e.length?[e[0].id]:[];for(o[0]&&(r[o[0]]=0);o.length;){let e=o.shift();(n[e]||[]).filter(e=>t.has(e)).forEach(t=>{r[t]===void 0&&(r[t]=(r[e]??0)+1,o.push(t))})}let s={};e.forEach(e=>{let t=r[e.id]??0;s[t]=(s[t]||0)+1});let c={};e.forEach(e=>{let t=r[e.id]??0;c[t]=(c[t]??-1)+1;let n=c[t],o=s[t]??1,ee=a*600+n*220-(o-1)*220/2,l=t*110,u=At(e);i.push({id:e.id,type:`default`,position:{x:ee,y:l},label:e.label,class:`sm-fn-node`,data:{...e,_accentColor:u},style:{"--node-accent":u}})}),a++}return{flowNodes:i,flowEdges:t.map((e,t)=>({id:`e_${t}_${e.source}_${e.target}`,source:e.source,target:e.target,type:`smoothstep`,animated:!1,class:`sm-fn-edge`,markerEnd:{type:ee.ArrowClosed}}))}}async function Pt(){let e=Ct.value,n=wt.value;if(e.length!==0){B.value=!0;try{await t(),await new Promise(e=>requestAnimationFrame(()=>e())),Tt(e.map(e=>e.id)),await new Promise(e=>requestAnimationFrame(()=>e()));let r=new Map;e.forEach(e=>{let t=e.data?.subsystem??`__default__`;r.has(t)||r.set(t,[]),r.get(t).push(e)});let i=new Map,a=0;for(let[,e]of r){let t=new _e.default.graphlib.Graph;t.setDefaultEdgeLabel(()=>({})),t.setGraph({rankdir:`TB`,nodesep:55,ranksep:75,marginx:40,marginy:40});let r=new Set(e.map(e=>e.id));e.forEach(e=>{let n=190,r=e.data?.description?68:48,i=document.querySelector(`.vue-flow__node[data-id="${e.id}"]`);i&&i.offsetWidth>0&&(n=i.offsetWidth,r=i.offsetHeight),t.setNode(e.id,{width:n,height:r})}),n.forEach(e=>{r.has(e.source)&&r.has(e.target)&&t.setEdge(e.source,e.target)}),_e.default.layout(t);let o=0;e.forEach(e=>{let n=t.node(e.id);n&&(i.set(e.id,{x:a+n.x-n.width/2,y:n.y-n.height/2}),o=Math.max(o,n.x+n.width/2))}),a+=o+120}X(e.map(e=>({...e,position:i.get(e.id)??e.position}))),await t(),xt({padding:.18})}finally{B.value=!1}}}async function Ft(){if(!O.value.trim()){pe.warning(T(`@SRCMAP:请先输入项目路径`));return}if(!Z.value){A.value=[],j.value=null,M.value=null,P.value=``,F.value=``,X([]),St([]),k.value=`scanning`,$(T(`@SRCMAP:开始分析项目...`),`info`);try{let e=await fetch(`/api/code-analysis/analyze`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({path:O.value})});if(!e.ok){let t=await e.json().catch(()=>({error:`请求失败`}));throw Error(t.error||`HTTP ${e.status}`)}let t=e.body.getReader(),n=new TextDecoder,r=``;k.value=`analyzing`;let i=``,a=[];for(;;){let{done:e,value:o}=await t.read();if(e)break;r+=n.decode(o,{stream:!0});let s=r.split(`
2
+ `);r=s.pop()??``;for(let e of s)if(e.startsWith(`event:`))i=e.slice(6).trim(),a=[];else if(e.startsWith(`data:`))a.push(e.slice(5).trim());else if(e===``){if(i&&a.length){let e=a.join(``);try{let t=JSON.parse(e);It(i,t)}catch{}}i=``,a=[]}}}catch(e){$(`${T(`@SRCMAP:分析失败`)}: ${e.message}`,`error`),k.value=`error`}}}function It(e,n){if(e===`log`)$(n.message,n.type||`info`);else if(e===`files`)L.value=Ot(n.files||[]),L.value.forEach(e=>{e.kind===`dir`&&R.value.add(e.path)});else if(e===`result`){j.value={language:n.language||``,entryFile:n.entryFile||``,entryFunction:n.entryFunction||``,nodes:Array.isArray(n.nodes)?n.nodes:[],edges:Array.isArray(n.edges)?n.edges:[],techStack:Array.isArray(n.techStack)?n.techStack:[],summary:n.summary||``,allFiles:Array.isArray(n.allFiles)?n.allFiles:[],codeFiles:Array.isArray(n.codeFiles)?n.codeFiles:[],subsystems:Array.isArray(n.subsystems)?n.subsystems:void 0};let{flowNodes:e,flowEdges:r}=Nt(j.value.nodes,j.value.edges);X(e),St(r),t(()=>Pt())}else e===`done`&&(n.error?($(`${T(`@SRCMAP:分析失败`)}: ${n.error}`,`error`),k.value=`error`):k.value=`done`)}async function Lt(e){if(!(!e||I.value)&&F.value!==e){I.value=!0,F.value=e,P.value=``;try{let t=await(await fetch(`/api/code-analysis/file-content?path=${encodeURIComponent(O.value)}&file=${encodeURIComponent(e)}`)).json();if(t.error)throw Error(t.error);P.value=t.content||``}catch(e){P.value=`// ${T(`@SRCMAP:加载失败`)}: ${e.message}`}finally{I.value=!1}}}function Rt(e){let t=e.node.data;M.value=t.id,t.file&&Lt(t.file)}function zt(e){R.value.has(e)?R.value.delete(e):R.value.add(e)}let Bt=y(()=>{let e=[];function t(n,r){for(let i of n){let n=R.value.has(i.path);e.push({name:i.name,path:i.path,kind:i.kind,depth:r,expanded:n}),i.kind===`dir`&&n&&t(i.children,r+1)}}return t(L.value,0),e});return l(()=>D.currentDirectory,e=>{e&&!O.value&&(O.value=e)}),l([P,F],([e,t])=>{let n=Y.value;if(!n)return;let r=jt(t||``),i=n.getModel(),a=C.createModel(e||``,r);n.setModel(a),i?.dispose(),n.setScrollPosition({scrollTop:0,scrollLeft:0})}),ce(()=>{Mt(),!O.value&&D.currentDirectory&&(O.value=D.currentDirectory),E=new MutationObserver(()=>me()),E.observe(document.documentElement,{attributes:!0,attributeFilter:[`data-theme`]})}),te(()=>{Y.value?.getModel()?.dispose(),Y.value?.dispose(),q(),E?.disconnect(),E=null}),(e,t)=>{let i=o;return v(),x(`div`,ve,[_(`div`,ye,[_(`div`,be,[t[9]||=_(`svg`,{viewBox:`0 0 24 24`,width:`18`,height:`18`,fill:`none`,stroke:`currentColor`,"stroke-width":`1.8`,class:`sm-icon-map`},[_(`polygon`,{points:`3 6 9 3 15 6 21 3 21 18 15 21 9 18 3 21`}),_(`line`,{x1:`9`,y1:`3`,x2:`9`,y2:`18`}),_(`line`,{x1:`15`,y1:`6`,x2:`15`,y2:`21`})],-1),_(`span`,xe,b(p(T)(`@SRCMAP:源码地图`)),1)]),_(`div`,Se,[w(_(`input`,{"onUpdate:modelValue":t[0]||=e=>O.value=e,class:`sm-path-input`,placeholder:p(T)(`@SRCMAP:输入项目目录路径`),disabled:Z.value,onKeydown:le(Ft,[`enter`])},null,40,Ce),[[de,O.value]]),_(`button`,{class:`sm-btn sm-btn-primary`,disabled:Z.value,onClick:Ft},[Z.value?(v(),x(g,{key:0},[t[10]||=_(`span`,{class:`sm-spinner`},null,-1),S(` `+b(p(T)(`@SRCMAP:分析中...`)),1)],64)):(v(),x(g,{key:1},[S(b(k.value===`done`?p(T)(`@SRCMAP:重新分析`):p(T)(`@SRCMAP:开始分析`)),1)],64))],8,we)]),_(`div`,Te,[f(i,{content:p(T)(`@SRCMAP:文件列表`),placement:`bottom`},{default:d(()=>[_(`button`,{class:n([`sm-panel-btn`,{active:z.value.files}]),onClick:t[1]||=e=>z.value.files=!z.value.files},t[11]||=[_(`svg`,{viewBox:`0 0 24 24`,width:`16`,height:`16`,fill:`none`,stroke:`currentColor`,"stroke-width":`1.8`},[_(`path`,{d:`M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z`})],-1)],2)]),_:1},8,[`content`]),f(i,{content:p(T)(`@SRCMAP:调用图`),placement:`bottom`},{default:d(()=>[_(`button`,{class:n([`sm-panel-btn`,{active:z.value.graph}]),onClick:t[2]||=e=>z.value.graph=!z.value.graph},t[12]||=[_(`svg`,{viewBox:`0 0 24 24`,width:`16`,height:`16`,fill:`none`,stroke:`currentColor`,"stroke-width":`1.8`},[_(`circle`,{cx:`12`,cy:`12`,r:`3`}),_(`circle`,{cx:`3`,cy:`6`,r:`2`}),_(`circle`,{cx:`21`,cy:`6`,r:`2`}),_(`circle`,{cx:`3`,cy:`18`,r:`2`}),_(`circle`,{cx:`21`,cy:`18`,r:`2`}),_(`line`,{x1:`5`,y1:`6`,x2:`9`,y2:`11`}),_(`line`,{x1:`19`,y1:`6`,x2:`15`,y2:`11`}),_(`line`,{x1:`5`,y1:`18`,x2:`9`,y2:`13`}),_(`line`,{x1:`19`,y1:`18`,x2:`15`,y2:`13`})],-1)],2)]),_:1},8,[`content`]),f(i,{content:p(T)(`@SRCMAP:源码面板`),placement:`bottom`},{default:d(()=>[_(`button`,{class:n([`sm-panel-btn`,{active:z.value.source}]),onClick:t[3]||=e=>z.value.source=!z.value.source},t[13]||=[_(`svg`,{viewBox:`0 0 24 24`,width:`16`,height:`16`,fill:`none`,stroke:`currentColor`,"stroke-width":`1.8`},[_(`polyline`,{points:`16 18 22 12 16 6`}),_(`polyline`,{points:`8 6 2 12 8 18`})],-1)],2)]),_:1},8,[`content`])])]),_(`div`,Ee,[w(_(`div`,{class:`sm-panel sm-panel-files`,style:c({width:vt.value+`px`})},[_(`div`,De,[_(`button`,{class:n([`sm-tab-btn`,{active:N.value===`files`}]),onClick:t[4]||=e=>N.value=`files`},[t[14]||=_(`svg`,{viewBox:`0 0 24 24`,width:`12`,height:`12`,fill:`none`,stroke:`currentColor`,"stroke-width":`1.8`},[_(`path`,{d:`M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z`})],-1),S(` `+b(p(T)(`@SRCMAP:文件列表`))+` `,1),j.value?(v(),x(`span`,Oe,b(j.value.allFiles.length),1)):m(``,!0)],2),_(`button`,{class:n([`sm-tab-btn`,{active:N.value===`outline`}]),onClick:t[5]||=e=>N.value=`outline`},[t[15]||=se(`<svg viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" stroke-width="1.8" data-v-71d3569e><line x1="8" y1="6" x2="21" y2="6" data-v-71d3569e></line><line x1="8" y1="12" x2="21" y2="12" data-v-71d3569e></line><line x1="8" y1="18" x2="21" y2="18" data-v-71d3569e></line><line x1="3" y1="6" x2="3.01" y2="6" data-v-71d3569e></line><line x1="3" y1="12" x2="3.01" y2="12" data-v-71d3569e></line><line x1="3" y1="18" x2="3.01" y2="18" data-v-71d3569e></line></svg>`,1),S(` `+b(p(T)(`@SRCMAP:大纲`)),1)],2)]),w(_(`div`,ke,[Bt.value.length>0?(v(!0),x(g,{key:0},u(Bt.value,e=>(v(),x(`div`,{key:e.path,class:n([`sm-tree-node`,{"sm-tree-node--dir":e.kind===`dir`,"sm-tree-node--active":e.kind===`file`&&F.value===e.path}]),style:c({paddingLeft:10+e.depth*14+`px`}),onClick:t=>e.kind===`dir`?zt(e.path):Lt(e.path)},[e.kind===`dir`?(v(),x(`span`,{key:0,class:n([`sm-tree-arrow`,{expanded:e.expanded}])},t[16]||=[_(`svg`,{viewBox:`0 0 24 24`,width:`10`,height:`10`,fill:`none`,stroke:`currentColor`,"stroke-width":`2.5`,"stroke-linecap":`round`,"stroke-linejoin":`round`},[_(`polyline`,{points:`9 18 15 12 9 6`})],-1)],2)):(v(),x(`span`,je)),e.kind===`dir`?(v(),x(`svg`,Me,[_(`use`,{"xlink:href":`#${p(ge)(e.name)||`icon-folder`}`},null,8,Ne)])):(v(),x(`svg`,Pe,[_(`use`,{"xlink:href":`#${p(ge)(e.name)}`},null,8,Fe)])),_(`span`,{class:`sm-tree-name`,title:e.path},b(e.name),9,Ie)],14,Ae))),128)):(v(),x(`div`,Le,b(p(T)(`@SRCMAP:暂无文件,请先开始分析`)),1))],512),[[h,N.value===`files`]]),w(_(`div`,Re,[Dt.value.length>0?(v(!0),x(g,{key:0},u(Dt.value,e=>(v(),x(`div`,{key:e.displayName,class:`sm-outline-group`},[_(`div`,{class:`sm-outline-group-header`,style:c({color:e.color})},[t[17]||=_(`svg`,{viewBox:`0 0 24 24`,width:`8`,height:`8`,fill:`currentColor`},[_(`circle`,{cx:`12`,cy:`12`,r:`6`})],-1),S(` `+b(e.displayName)+` `,1),_(`span`,ze,b(e.nodes.length),1)],4),(v(!0),x(g,null,u(e.nodes,t=>(v(),x(`div`,{key:t.id,class:n([`sm-outline-node`,{"sm-outline-node--active":M.value===t.id}]),onClick:e=>{M.value=t.id,t.file&&Lt(t.file)}},[_(`span`,{class:`sm-outline-dot`,style:c({background:e.color})},null,4),_(`span`,{class:`sm-outline-label`,title:t.file||t.label},b(t.label),9,Ve),t.description?(v(),x(`span`,He,b(t.description.length>18?t.description.slice(0,18)+`…`:t.description),1)):m(``,!0)],10,Be))),128))]))),128)):(v(),x(`div`,Ue,b(p(T)(`@SRCMAP:暂无分析结果`)),1))],512),[[h,N.value===`outline`]])],4),[[h,z.value.files]]),w(_(`div`,{class:`sm-resizer sm-resizer-v`,onMousedown:t[6]||=e=>K(`files`,e)},null,544),[[h,z.value.files&&z.value.graph]]),w(_(`div`,We,[j.value?(v(),x(`div`,Ge,[_(`span`,Ke,b(j.value.language),1),j.value.subsystems&&j.value.subsystems.length>1?(v(!0),x(g,{key:0},u(j.value.subsystems,e=>(v(),x(`span`,{key:e.name,class:`sm-subsystem-tag`,style:c({borderColor:e.color,color:e.color})},`● `+b(e.displayName||e.name),5))),128)):(v(!0),x(g,{key:1},u(j.value.techStack.slice(0,4),e=>(v(),x(`span`,{key:e,class:`sm-tech-tag`},b(e),1))),128)),_(`span`,qe,b(j.value.summary),1)])):m(``,!0),_(`div`,Je,[_(`div`,Ye,[_(`button`,{class:`sm-layout-btn`,disabled:B.value||!j.value,onClick:Pt},[t[18]||=se(`<svg viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" stroke-width="1.8" data-v-71d3569e><rect x="3" y="3" width="7" height="7" rx="1" data-v-71d3569e></rect><rect x="14" y="3" width="7" height="7" rx="1" data-v-71d3569e></rect><rect x="3" y="14" width="7" height="7" rx="1" data-v-71d3569e></rect><rect x="14" y="14" width="7" height="7" rx="1" data-v-71d3569e></rect></svg>`,1),S(` `+b(B.value?p(T)(`@SRCMAP:布局中...`):p(T)(`@SRCMAP:优化布局`)),1)],8,Xe)]),f(p(s),{class:`sm-vue-flow`,"default-viewport":{zoom:1},"min-zoom":.2,"max-zoom":4,"fit-view-on-init":``,onNodeClick:Rt},{"node-default":d(({data:e,label:t})=>[f(p(oe),{type:`target`,position:p(ue).Top},null,8,[`position`]),_(`div`,{class:`sm-fn-inner`,title:`${t}${e?.description?`
3
+ `+e.description:``}`},[_(`div`,Qe,b(t),1),e?.description?(v(),x(`div`,$e,b(e.description.length>22?e.description.slice(0,22)+`…`:e.description),1)):m(``,!0)],8,Ze),f(p(oe),{type:`source`,position:p(ue).Bottom},null,8,[`position`])]),default:d(()=>[f(p(r),{variant:p(ie).Dots,gap:20,size:1,"pattern-color":Et.value},null,8,[`variant`,`pattern-color`]),f(p(re)),f(p(fe),{"node-color":`#3b82f6`,"mask-color":a.value===`dark`?`rgba(0,0,0,0.55)`:`rgba(15,23,42,0.06)`},null,8,[`mask-color`])]),_:1}),!j.value&&!Z.value?(v(),x(`div`,et,[(v(),x(`svg`,tt,t[19]||=[_(`polygon`,{points:`3 6 9 3 15 6 21 3 21 18 15 21 9 18 3 21`},null,-1),_(`line`,{x1:`9`,y1:`3`,x2:`9`,y2:`18`},null,-1),_(`line`,{x1:`15`,y1:`6`,x2:`15`,y2:`21`},null,-1)])),_(`p`,null,b(p(T)(`@SRCMAP:输入项目路径后点击开始分析`)),1)])):m(``,!0),Z.value&&!j.value?(v(),x(`div`,nt,[t[20]||=_(`span`,{class:`sm-spinner sm-spinner-lg`},null,-1),_(`p`,null,b(p(T)(`@SRCMAP:AI 正在分析项目结构...`)),1)])):m(``,!0)]),_(`div`,{class:`sm-resizer sm-resizer-h`,onMousedown:t[7]||=e=>K(`log`,e)},null,32),_(`div`,{class:`sm-log-panel`,style:c({height:H.value+`px`})},[_(`div`,rt,[t[21]||=_(`svg`,{viewBox:`0 0 24 24`,width:`12`,height:`12`,fill:`none`,stroke:`currentColor`,"stroke-width":`1.8`},[_(`polyline`,{points:`4 17 10 11 4 5`}),_(`line`,{x1:`12`,y1:`19`,x2:`20`,y2:`19`})],-1),S(` `+b(p(T)(`@SRCMAP:Agent 日志`))+` `,1),Z.value?(v(),x(`span`,it)):m(``,!0)]),_(`div`,at,[(v(!0),x(g,null,u(A.value.slice(-50),e=>(v(),x(`div`,{key:e.id,class:n([`sm-log-entry`,`sm-log-entry--${e.type}`])},b(e.message),3))),128)),A.value.length===0?(v(),x(`div`,ot,b(p(T)(`@SRCMAP:等待开始分析`)),1)):m(``,!0)],512)],4)],512),[[h,z.value.graph]]),w(_(`div`,{class:`sm-resizer sm-resizer-v`,onMousedown:t[8]||=e=>K(`source`,e)},null,544),[[h,z.value.graph&&z.value.source]]),w(_(`div`,{class:`sm-panel sm-panel-source`,style:c({width:V.value+`px`})},[_(`div`,st,[t[22]||=_(`svg`,{viewBox:`0 0 24 24`,width:`13`,height:`13`,fill:`none`,stroke:`currentColor`,"stroke-width":`1.8`},[_(`polyline`,{points:`16 18 22 12 16 6`}),_(`polyline`,{points:`8 6 2 12 8 18`})],-1),S(` `+b(F.value||p(T)(`@SRCMAP:源码面板`))+` `,1),Q.value?(v(),x(`span`,ct,b(Q.value.label),1)):m(``,!0)]),Q.value?(v(),x(`div`,lt,[_(`div`,ut,b(Q.value.label),1),_(`div`,dt,[S(b(Q.value.file),1),Q.value.line?(v(),x(`span`,ft,` :`+b(Q.value.line),1)):m(``,!0)]),Q.value.description?(v(),x(`div`,pt,b(Q.value.description),1)):m(``,!0)])):m(``,!0),_(`div`,mt,[I.value?(v(),x(`div`,ht,t[23]||=[_(`span`,{class:`sm-spinner`},null,-1)])):m(``,!0),!I.value&&!P.value?(v(),x(`div`,gt,b(p(T)(`@SRCMAP:点击调用图中的节点查看源码`)),1)):m(``,!0),_(`div`,{ref_key:`monacoContainerRef`,ref:J,class:`sm-monaco-container`},null,512)])],4),[[h,z.value.source]])])])}}}),[[`__scopeId`,`data-v-71d3569e`]]);export{D as default};