zen-gitsync 2.17.22 → 2.17.24

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 (58) hide show
  1. package/README.md +10 -8
  2. package/package.json +8 -1
  3. package/src/config.js +34 -3
  4. package/src/ui/public/assets/{AgentEngineSelector-Dtvp5u3j.js → AgentEngineSelector-CvIoGCLD.js} +1 -1
  5. package/src/ui/public/assets/{AgentView-BzfjNz7k.js → AgentView-BrqS78Rt.js} +1 -1
  6. package/src/ui/public/assets/{AppVersionBadge-DAAnAFNp.js → AppVersionBadge-BHxZm7-h.js} +2 -2
  7. package/src/ui/public/assets/{BranchSelector-C20zVkp_.js → BranchSelector-w5ybmWVv.js} +1 -1
  8. package/src/ui/public/assets/{CommitForm-COc-NwA8.js → CommitForm-pRCnAfz6.js} +1 -1
  9. package/src/ui/public/assets/{CommonDialog-B7r-EaIr.js → CommonDialog-qXAsFhNS.js} +1 -1
  10. package/src/ui/public/assets/{EditorView-DnMiWg3T.css → EditorView-C6HFYeEM.css} +1 -1
  11. package/src/ui/public/assets/EditorView-DnuGnX3k.js +1 -0
  12. package/src/ui/public/assets/{FileDiffViewer-7HZ_CW13.js → FileDiffViewer-Bc8j9cQn.js} +1 -1
  13. package/src/ui/public/assets/{FlowExecutionViewer-YKYhtKmd.js → FlowExecutionViewer-CF4HkGu6.js} +1 -1
  14. package/src/ui/public/assets/{FlowOrchestrationWorkspace-Bg5pSrGY.js → FlowOrchestrationWorkspace-a3Zk4BMK.js} +1 -1
  15. package/src/ui/public/assets/{IconButton-CyEyp9_D.js → IconButton-BjIHmE4p.js} +1 -1
  16. package/src/ui/public/assets/{LogList-B0YcWT9U.js → LogList-CqpSAW5T.js} +1 -1
  17. package/src/ui/public/assets/{MindmapView-CJI789Gz.js → MindmapView-CgptM54l.js} +1 -1
  18. package/src/ui/public/assets/{MonitorView-DVrInkz0.js → MonitorView-qb0EPvQW.js} +1 -1
  19. package/src/ui/public/assets/{ProjectStartupButton-BGrtaPiJ.js → ProjectStartupButton-CmankQtk.js} +1 -1
  20. package/src/ui/public/assets/{RecentDirectoriesChat-BsM5qfRX.js → RecentDirectoriesChat-CsHHckPP.js} +1 -1
  21. package/src/ui/public/assets/{RemoteManagerDialog-fGsXehZF.js → RemoteManagerDialog-Cpg4g5KW.js} +1 -1
  22. package/src/ui/public/assets/{RemoteRepoCard-CYXYYtM8.js → RemoteRepoCard-DCc-adLb.js} +1 -1
  23. package/src/ui/public/assets/{SourceMapView-BNyHS4uU.js → SourceMapView-I41XvvZr.js} +1 -1
  24. package/src/ui/public/assets/{SvgIcon-Cgmy9qD6.js → SvgIcon-BB80ERhO.js} +1 -1
  25. package/src/ui/public/assets/{TemplateManager-DUPI3yBg.js → TemplateManager-BB_bEiBB.js} +1 -1
  26. package/src/ui/public/assets/{UserInputNode-DUbPMxsr.js → UserInputNode-y9gz0EKH.js} +1 -1
  27. package/src/ui/public/assets/WorkbenchView-Bkzbu5Sp.css +1 -0
  28. package/src/ui/public/assets/WorkbenchView-DKqriLBP.js +6 -0
  29. package/src/ui/public/assets/_plugin-vue_export-helper-Dd_YmKEO.js +19 -0
  30. package/src/ui/public/assets/{agentConversations-CuFU_pdW.js → agentConversations-CT9E8eQr.js} +1 -1
  31. package/src/ui/public/assets/configStore-BR8vEJCp.js +1 -0
  32. package/src/ui/public/assets/{gitStore-BoFStoL3.js → gitStore-Bz77W-S0.js} +1 -1
  33. package/src/ui/public/assets/index-CsLyZsXy.js +29 -0
  34. package/src/ui/public/assets/index-D2rf9QKB.css +1 -0
  35. package/src/ui/public/assets/{previewSandbox-BzitUMhT.js → previewSandbox-luPexzF4.js} +1 -1
  36. package/src/ui/public/assets/toolsStore-CeI7jM5o.js +1 -0
  37. package/src/ui/public/index.html +12 -12
  38. package/src/ui/public/sounds/CREDITS.txt +32 -0
  39. package/src/ui/public/sounds/task-done.wav +0 -0
  40. package/src/ui/public/sounds/task-error.wav +0 -0
  41. package/src/ui/server/routes/config.js +11 -2
  42. package/src/ui/server/routes/fileOpen.js +35 -6
  43. package/src/ui/server/routes/workbench/executorModels.js +329 -0
  44. package/src/ui/server/routes/workbench/executorModels.test.js +326 -0
  45. package/src/ui/server/routes/workbench/index.js +26 -0
  46. package/src/ui/server/routes/workbench/jobActivity.js +27 -1
  47. package/src/ui/server/routes/workbench/jobActivity.test.js +25 -0
  48. package/src/ui/server/routes/workbench/orchestratorStore.js +4 -1
  49. package/src/ui/server/routes/workbench/projectRegistry.js +9 -1
  50. package/src/ui/server/routes/workbench/projectRegistry.test.js +26 -0
  51. package/src/ui/public/assets/EditorView-CWCzo4aE.js +0 -1
  52. package/src/ui/public/assets/WorkbenchView-AbJMHSJy.js +0 -6
  53. package/src/ui/public/assets/WorkbenchView-D8wqeEiu.css +0 -1
  54. package/src/ui/public/assets/_plugin-vue_export-helper-DBFEfstn.js +0 -19
  55. package/src/ui/public/assets/configStore-6xFSX9E5.js +0 -1
  56. package/src/ui/public/assets/index-BVkLqLHo.js +0 -29
  57. package/src/ui/public/assets/index-DVJITdth.css +0 -1
  58. package/src/ui/public/assets/toolsStore-B49mCaQ_.js +0 -1
@@ -0,0 +1,326 @@
1
+ // 执行器模型探测的单测。
2
+ //
3
+ // 为什么这些用例值得写:这个模块的全部价值是"读三种格式的配置文件",
4
+ // 而三种格式的解析都是**手写的**(不引 TOML / JSONC 依赖,理由见模块注释)——
5
+ // 手写解析最容易在两个地方悄悄错:注释/空行的截断、以及多文件兜底被丢掉。
6
+ // 这两处都已经真出过 bug(见下面标 [回归] 的用例),所以它们必须钉住。
7
+ //
8
+ // 所有用例都在**临时沙箱 home** 里跑,绝不读真实 ~/ —— 否则用例会随开发机
9
+ // 装没装 claude、配没配模型而时红时绿。
10
+ import { test } from 'node:test'
11
+ import assert from 'node:assert/strict'
12
+ import fs from 'fs/promises'
13
+ import os from 'os'
14
+ import path from 'path'
15
+ import {
16
+ readTomlTopLevel,
17
+ stripJsonComments,
18
+ detectExecutorModels,
19
+ formatExecutorModel,
20
+ } from './executorModels.js'
21
+
22
+ const sandboxes = []
23
+
24
+ /** 造一个只属于本用例的 home 目录,按 {相对路径: 内容} 落文件 */
25
+ async function makeHome(files = {}) {
26
+ const home = await fs.mkdtemp(path.join(os.tmpdir(), 'zen-executor-models-'))
27
+ sandboxes.push(home)
28
+ for (const [rel, content] of Object.entries(files)) {
29
+ const full = path.join(home, rel)
30
+ await fs.mkdir(path.dirname(full), { recursive: true })
31
+ await fs.writeFile(full, content, 'utf8')
32
+ }
33
+ return home
34
+ }
35
+
36
+ test.after(async () => {
37
+ for (const home of sandboxes) await fs.rm(home, { recursive: true, force: true })
38
+ })
39
+
40
+ // ── readTomlTopLevel ─────────────────────────────────────────────────────────
41
+
42
+ test('readTomlTopLevel: 注释行与空行不截断顶层读取 [回归]', () => {
43
+ // 老实现把「空行 / 注释行 / 段头」三类一起 break,于是文件第一行是注释时
44
+ // 顶层 model 整个读空 —— 表现为界面上 codex 永远显示"未配置"。
45
+ const toml = [
46
+ '# codex 配置说明',
47
+ '# 第二行注释',
48
+ 'model = "gpt-6-astra"',
49
+ '',
50
+ 'model_provider = "custom"',
51
+ '',
52
+ '[model_providers.custom]',
53
+ 'name = "kakouai"',
54
+ ].join('\n')
55
+ assert.deepEqual(readTomlTopLevel(toml, ['model', 'model_provider']), {
56
+ model: 'gpt-6-astra',
57
+ model_provider: 'custom',
58
+ })
59
+ })
60
+
61
+ test('readTomlTopLevel: 段内的同名键不会被当成顶层值', () => {
62
+ const toml = 'model = "top"\n\n[projects."c:/x"]\nmodel = "inner"\n'
63
+ assert.equal(readTomlTopLevel(toml, ['model']).model, 'top')
64
+ })
65
+
66
+ test('readTomlTopLevel: 只取白名单里的键(其余含密钥的键一律不取)', () => {
67
+ const toml = 'model = "m"\nexperimental_bearer_token = "tok"\napi_key = "k"\n'
68
+ assert.deepEqual(readTomlTopLevel(toml, ['model']), { model: 'm' })
69
+ })
70
+
71
+ test('readTomlTopLevel: 行尾注释不污染值;单引号值也认', () => {
72
+ const toml = "model = 'm-1' # 备注\nmodel_provider = \"custom\"#紧贴的注释\n"
73
+ assert.deepEqual(readTomlTopLevel(toml, ['model', 'model_provider']), {
74
+ model: 'm-1',
75
+ model_provider: 'custom',
76
+ })
77
+ })
78
+
79
+ test('readTomlTopLevel: 段头里带方括号的路径不被误判成值', () => {
80
+ const toml = 'model = "m"\n\n[projects.\'c:\\weird[path]\']\nmodel = "inner"\n'
81
+ assert.equal(readTomlTopLevel(toml, ['model']).model, 'm')
82
+ })
83
+
84
+ // ── stripJsonComments ────────────────────────────────────────────────────────
85
+
86
+ test('stripJsonComments: 字符串里的 // 不被当注释(base_url 场景)', () => {
87
+ const jsonc = '{ "url": "https://api.example.com/v1", // 注释\n "n": 1 }'
88
+ assert.equal(JSON.parse(stripJsonComments(jsonc)).url, 'https://api.example.com/v1')
89
+ })
90
+
91
+ test('stripJsonComments: 剥块注释与尾逗号后仍能 JSON.parse', () => {
92
+ const jsonc = '{\n /* 块\n 注释 */\n "model": "a/b",\n}'
93
+ assert.deepEqual(JSON.parse(stripJsonComments(jsonc)), { model: 'a/b' })
94
+ })
95
+
96
+ test('stripJsonComments: 转义引号不影响"在不在字符串里"的判断', () => {
97
+ const jsonc = '{ "s": "a\\"//b", "n": 1 } // 尾注释'
98
+ assert.deepEqual(JSON.parse(stripJsonComments(jsonc)), { s: 'a"//b', n: 1 })
99
+ })
100
+
101
+ // ── detectExecutorModels(沙箱 home)─────────────────────────────────────────
102
+
103
+ const CLAUDE_JSON = JSON.stringify({
104
+ env: {
105
+ ANTHROPIC_BASE_URL: 'http://127.0.0.1:15721',
106
+ ANTHROPIC_DEFAULT_SONNET_MODEL: 'claude-sonnet-5[1M]',
107
+ ANTHROPIC_DEFAULT_SONNET_MODEL_NAME: 'deepseek-v4.1-flash',
108
+ ANTHROPIC_API_KEY: 'sk-should-never-leak',
109
+ },
110
+ })
111
+ const CODEX_TOML = [
112
+ '# 顶层注释',
113
+ 'model = "gpt-6-astra"',
114
+ 'model_provider = "custom"',
115
+ '',
116
+ '[model_providers.custom]',
117
+ 'name = "kakouai"',
118
+ 'experimental_bearer_token = "tok-should-never-leak"',
119
+ '',
120
+ '[projects."c:/x"]',
121
+ 'model = "inner-model"',
122
+ ].join('\n')
123
+ const OPENCODE_JSONC = '{\n // 注释\n "model": "anthropic/claude-x",\n}'
124
+
125
+ test('detectExecutorModels: 三家配置齐全时各自读出模型', async () => {
126
+ const home = await makeHome({
127
+ '.claude/settings.json': CLAUDE_JSON,
128
+ '.codex/config.toml': CODEX_TOML,
129
+ '.config/opencode/opencode.jsonc': OPENCODE_JSONC,
130
+ })
131
+ const out = await detectExecutorModels({ homeDir: home })
132
+
133
+ assert.deepEqual(out.claude, {
134
+ model: 'claude-sonnet-5[1M]',
135
+ display: 'deepseek-v4.1-flash',
136
+ provider: 'http://127.0.0.1:15721',
137
+ })
138
+ // 顶层注释不再截断 → model_provider 读得到 → 服务商名也能查出来
139
+ assert.deepEqual(out.codex, { model: 'gpt-6-astra', display: null, provider: 'kakouai' })
140
+ // 只有 .jsonc(没有 .json)时也要读到:早先那个 promise 被解构丢掉,这里是死代码
141
+ assert.deepEqual(out.opencode, {
142
+ model: 'anthropic/claude-x', display: null, provider: null, source: 'config',
143
+ })
144
+ })
145
+
146
+ test('detectExecutorModels: 只有 .jsonc 时 opencode 仍读得到 [回归]', async () => {
147
+ const home = await makeHome({ '.config/opencode/opencode.jsonc': OPENCODE_JSONC })
148
+ const out = await detectExecutorModels({ homeDir: home })
149
+ assert.equal(out.opencode?.model, 'anthropic/claude-x')
150
+ })
151
+
152
+ test('detectExecutorModels: .json 与 .jsonc 同时存在时 .json 优先', async () => {
153
+ const home = await makeHome({
154
+ '.config/opencode/opencode.json': '{"model":"from-json"}',
155
+ '.config/opencode/opencode.jsonc': '{"model":"from-jsonc"}',
156
+ })
157
+ const out = await detectExecutorModels({ homeDir: home })
158
+ assert.equal(out.opencode?.model, 'from-json')
159
+ })
160
+
161
+ test('detectExecutorModels: opencode 的 model 是对象时带上 variant', async () => {
162
+ const home = await makeHome({
163
+ '.config/opencode/opencode.json': '{"model":{"model":"openai/gpt-y","variant":"high"}}',
164
+ })
165
+ const out = await detectExecutorModels({ homeDir: home })
166
+ assert.equal(out.opencode?.model, 'openai/gpt-y (high)')
167
+ })
168
+
169
+ // ── opencode 的第二处来源:TUI 状态 ──────────────────────────────────────────
170
+ //
171
+ // opencode 的 TUI 允许直接选模型,而它把选择**存在自己的 state 里、不回写配置文件**。
172
+ // 只读配置文件的话,对一个明明在跑的模型显示"未在配置中指定" —— 用户会去翻配置,
173
+ // 配置里确实没有,白折腾一场。这组用例钉住"配置文件优先、没有则退回 state"。
174
+
175
+ const OPENCODE_STATE = JSON.stringify({
176
+ recent: [
177
+ { providerID: 'opencode-go', modelID: 'space-bunny-free' },
178
+ { providerID: 'opencode-go', modelID: 'deepseek-v4.1-flash' },
179
+ ],
180
+ favorite: [],
181
+ variant: { 'opencode-go/space-bunny-free': 'max', 'opencode-go/deepseek-v4.1-flash': 'max' },
182
+ })
183
+
184
+ test('detectExecutorModels: 配置里没写 model 时,退回 TUI 状态里的「最近使用」', async () => {
185
+ const home = await makeHome({
186
+ // 本机真实情形:配置文件里只有一个 $schema
187
+ '.config/opencode/opencode.jsonc': '{\n "$schema": "https://opencode.ai/config.json"\n}',
188
+ '.local/state/opencode/model.json': OPENCODE_STATE,
189
+ })
190
+ const out = await detectExecutorModels({ homeDir: home })
191
+ assert.deepEqual(out.opencode, {
192
+ model: 'opencode-go/space-bunny-free (max)', // recent[0] + variant 表里的 max
193
+ display: null,
194
+ provider: null, // 模型名本身已是 provider/model,不再重复报一次
195
+ source: 'state',
196
+ })
197
+ })
198
+
199
+ test('detectExecutorModels: 配置文件里写了 model 就以它为准(state 只是兜底)', async () => {
200
+ const home = await makeHome({
201
+ '.config/opencode/opencode.json': '{"model":"anthropic/claude-x"}',
202
+ '.local/state/opencode/model.json': OPENCODE_STATE,
203
+ })
204
+ const out = await detectExecutorModels({ homeDir: home })
205
+ assert.deepEqual(out.opencode, {
206
+ model: 'anthropic/claude-x', display: null, provider: null, source: 'config',
207
+ })
208
+ })
209
+
210
+ test('detectExecutorModels: recent 项自带 variant 时优先于顶层 variant 表', async () => {
211
+ const home = await makeHome({
212
+ '.config/opencode/opencode.jsonc': '',
213
+ '.local/state/opencode/model.json': JSON.stringify({
214
+ recent: [{ providerID: 'p', modelID: 'm', variant: 'own' }],
215
+ variant: { 'p/m': 'mapped' },
216
+ }),
217
+ })
218
+ const out = await detectExecutorModels({ homeDir: home })
219
+ assert.equal(out.opencode?.model, 'p/m (own)')
220
+ })
221
+
222
+ test('detectExecutorModels: state 里没有 recent / 缺字段时仍是 null(不猜)', async () => {
223
+ const empty = await makeHome({ '.local/state/opencode/model.json': '{"recent":[],"favorite":[]}' })
224
+ assert.equal((await detectExecutorModels({ homeDir: empty })).opencode, null)
225
+
226
+ const partial = await makeHome({
227
+ '.local/state/opencode/model.json': '{"recent":[{"providerID":"p"}]}',
228
+ })
229
+ assert.equal((await detectExecutorModels({ homeDir: partial })).opencode, null)
230
+ })
231
+
232
+ test('detectExecutorModels: 两处都没有 model 才是真的"未在配置中指定"', async () => {
233
+ const home = await makeHome({ '.config/opencode/opencode.json': '{"model":""}' })
234
+ assert.equal((await detectExecutorModels({ homeDir: home })).opencode, null)
235
+ })
236
+
237
+ test('detectExecutorModels: claude 显式 ANTHROPIC_MODEL 优先于档位别名', async () => {
238
+ const home = await makeHome({
239
+ '.claude/settings.json': JSON.stringify({
240
+ env: {
241
+ ANTHROPIC_MODEL: 'my-explicit-model',
242
+ ANTHROPIC_BASE_URL: 'http://proxy.local',
243
+ ANTHROPIC_DEFAULT_SONNET_MODEL: 'sonnet-alias',
244
+ ANTHROPIC_DEFAULT_SONNET_MODEL_NAME: 'sonnet-real',
245
+ },
246
+ }),
247
+ })
248
+ const out = await detectExecutorModels({ homeDir: home })
249
+ // 显式指定时没有"别名背后"的第二层,但 base_url 仍要带出来
250
+ assert.deepEqual(out.claude, {
251
+ model: 'my-explicit-model',
252
+ display: null,
253
+ provider: 'http://proxy.local',
254
+ })
255
+ })
256
+
257
+ test('detectExecutorModels: 一个配置文件都没有时三项全 null 且不抛', async () => {
258
+ const home = await makeHome()
259
+ const out = await detectExecutorModels({ homeDir: home })
260
+ assert.deepEqual(out, { claude: null, codex: null, opencode: null })
261
+ })
262
+
263
+ test('detectExecutorModels: 配置残缺/是目录/内容不是 JSON 时只让自己变 null', async () => {
264
+ const home = await makeHome({
265
+ '.claude/settings.json': '{ 这不是 JSON', // 解析失败 → null
266
+ '.codex/config.toml': '# 只有注释,没有 model\n', // 无 model → null
267
+ '.config/opencode/opencode.json': '{"model":""}', // 空串 → null
268
+ })
269
+ const out = await detectExecutorModels({ homeDir: home })
270
+ assert.deepEqual(out, { claude: null, codex: null, opencode: null })
271
+ })
272
+
273
+ test('detectExecutorModels: 返回值里绝不出现任何密钥', async () => {
274
+ const home = await makeHome({
275
+ '.claude/settings.json': CLAUDE_JSON,
276
+ '.codex/config.toml': CODEX_TOML,
277
+ })
278
+ const serialized = JSON.stringify(await detectExecutorModels({ homeDir: home }))
279
+ assert.equal(serialized.includes('sk-should-never-leak'), false)
280
+ assert.equal(serialized.includes('tok-should-never-leak'), false)
281
+ })
282
+
283
+ // ── formatExecutorModel ──────────────────────────────────────────────────────
284
+
285
+ test('formatExecutorModel: display 优先,别名落到 detail', () => {
286
+ assert.deepEqual(
287
+ formatExecutorModel({
288
+ model: 'claude-sonnet-5[1M]',
289
+ display: 'deepseek-v4.1-flash',
290
+ provider: 'http://127.0.0.1:15721',
291
+ }),
292
+ { name: 'deepseek-v4.1-flash', detail: 'claude-sonnet-5[1M]', provider: 'http://127.0.0.1:15721', source: null },
293
+ )
294
+ })
295
+
296
+ test('formatExecutorModel: 两个名字相同则不重复进 detail', () => {
297
+ assert.deepEqual(
298
+ formatExecutorModel({ model: 'same', display: 'same', provider: null }),
299
+ { name: 'same', detail: null, provider: null, source: null },
300
+ )
301
+ })
302
+
303
+ test('formatExecutorModel: 只有别名时就用别名,detail 为空', () => {
304
+ assert.deepEqual(
305
+ formatExecutorModel({ model: 'only-alias', display: null, provider: null }),
306
+ { name: 'only-alias', detail: null, provider: null, source: null },
307
+ )
308
+ })
309
+
310
+ test('formatExecutorModel: 来源(config / state)照实透传出去', () => {
311
+ assert.equal(
312
+ formatExecutorModel({ model: 'm', display: null, provider: null, source: 'state' }).source,
313
+ 'state',
314
+ )
315
+ assert.equal(
316
+ formatExecutorModel({ model: 'm', display: null, provider: null, source: 'config' }).source,
317
+ 'config',
318
+ )
319
+ })
320
+
321
+ test('formatExecutorModel: null / 空对象一律 null(前端据此走"未在配置中指定")', () => {
322
+ assert.equal(formatExecutorModel(null), null)
323
+ assert.equal(formatExecutorModel(undefined), null)
324
+ assert.equal(formatExecutorModel({ model: null, display: null, provider: null }), null)
325
+ assert.equal(formatExecutorModel({ model: '', display: '', provider: null }), null)
326
+ })
@@ -129,6 +129,7 @@ import {
129
129
  buildRunningFacts,
130
130
  generateProgressReport,
131
131
  } from './progressReport.js';
132
+ import { detectExecutorModels, formatExecutorModel } from './executorModels.js';
132
133
 
133
134
  /**
134
135
  * 「任务结束 → 刷快照」的监听器。存成模块级的,是为了在重复装配路由时
@@ -1497,6 +1498,31 @@ ${subSummaries.map((s, i) => `\n### [${i + 1}] ${s.name} (${s.root})\n${s.summar
1497
1498
  }
1498
1499
  }));
1499
1500
 
1501
+ // ════════════════════════════════════════════════════════════════════════
1502
+ // §20. 执行器模型探测(只读)
1503
+ // ════════════════════════════════════════════════════════════════════════
1504
+ /**
1505
+ * 「这三个执行器现在实际在用什么模型」。
1506
+ *
1507
+ * 为什么需要这个端点:工作台派任务**一律不传 --model**(见 executorModels.js 的
1508
+ * 长注释),模型完全跟随各自 CLI 的配置文件。副作用是界面上一个执行器的模型都
1509
+ * 看不到 —— 用户想确认"这活到底是哪个模型跑的"只能去翻三个不同格式的配置文件。
1510
+ * 这个端点把那件事收口到一次请求里。
1511
+ *
1512
+ * 无参数、服务端不算缓存:三个文件都很小,每次实读比让前端踩脏缓存划算;
1513
+ * 前端那边(stores/toolsStore)自己按分钟级 TTL + 并发去重压请求频率。
1514
+ * 返回值里某项为 null = 那个 CLI 没在配置里写模型,前端照实显示「未在配置中指定」。
1515
+ * key 恒为 claude / codex / opencode 三个,与 TASK_EXECUTOR_OPTIONS 对齐。
1516
+ */
1517
+ app.get('/api/workbench/executor-models', asyncRoute(async (_req, res) => {
1518
+ const detected = await detectExecutorModels();
1519
+ const models = {};
1520
+ for (const [executor, info] of Object.entries(detected)) {
1521
+ models[executor] = formatExecutorModel(info);
1522
+ }
1523
+ res.json({ success: true, checkedAt: nowIso(), models });
1524
+ }));
1525
+
1500
1526
  // 暂存区兜底清理:粘了图却没派发就关页面的痕迹,不该永久占着磁盘。
1501
1527
  // 不上定时器 —— 暂存区的生命周期本来就是"几分钟内被派发掉",启动清一次足够。
1502
1528
  cleanupDispatchStaging().catch(err => {
@@ -183,6 +183,25 @@ export function isLiveJob(job) {
183
183
  return !!job && (job.status === 'running' || job.status === 'pending');
184
184
  }
185
185
 
186
+ /**
187
+ * 本轮跑的是哪个执行器(`claude` | `opencode` | `codex`)。
188
+ *
189
+ * 只在**认出是已知执行器**时才回值:job.agent 来自 jobs.json(会被手工编辑、也读过
190
+ * 别实例写的旧记录),回一个前端不认识的字符串只会让卡片上的图标位空着或显示错品牌。
191
+ * 认不出 → 空串,调用方据此不渲染图标,而不是回落成"猜一个 claude"——
192
+ * 猜错的执行器比不显示更糟。
193
+ *
194
+ * 这里**刻意不 import taskRunner 的 TASK_EXECUTORS**:taskRunner 拉起进程、依赖一堆
195
+ * 运行时模块,而本模块被 projectRegistry(5s 轮询的看板)直接引用,没必要为了三个
196
+ * 字符串把整条执行链拖进来。新增执行器时这里和 taskRunner 一起改。
197
+ */
198
+ const KNOWN_EXECUTORS = ['claude', 'opencode', 'codex'];
199
+
200
+ export function jobAgent(job) {
201
+ const v = job && typeof job.agent === 'string' ? job.agent.trim().toLowerCase() : '';
202
+ return KNOWN_EXECUTORS.includes(v) ? v : '';
203
+ }
204
+
186
205
  /**
187
206
  * 单条 job → 卡片 / 报告可用的活动摘要。非 running/pending 返回 null(调用方据此不显示)。
188
207
  *
@@ -199,8 +218,14 @@ export function buildLiveActivity(job, now = Date.now()) {
199
218
  jobId: job.id || '',
200
219
  status: job.status,
201
220
  /** 本轮执行器(claude | opencode | codex)。老记录没有这个字段 → 空串 */
202
- agent: typeof job.agent === 'string' ? job.agent : '',
221
+ agent: jobAgent(job),
203
222
  startedAt: job.startedAt || null,
223
+ /**
224
+ * 子进程 PID。原来只有左栏那个「执行监控」面板会显示它,面板去掉之后
225
+ * 卡片就是唯一的落点 —— 真要手动 kill 或排查僵尸 claude 进程时,
226
+ * 这是卡片上唯一能指认"到底是哪个进程"的字段。
227
+ */
228
+ pid: job.pid || null,
204
229
  elapsedMs: Number.isFinite(started) ? Math.max(0, now - started) : 0,
205
230
  /** 见 taskRunner 的 MAX_TOOL_CALLS:超上限的调用不再入数组,所以这是"至少这么多次" */
206
231
  toolCallCount: Array.isArray(job.toolCalls) ? job.toolCalls.length : 0,
@@ -249,6 +274,7 @@ export const __testables = {
249
274
  describeLastTool,
250
275
  describeToolMix,
251
276
  silentMsOf,
277
+ jobAgent,
252
278
  buildLiveActivity,
253
279
  pickLiveActivity,
254
280
  };
@@ -31,6 +31,7 @@ import {
31
31
  describeLastTool,
32
32
  describeToolMix,
33
33
  silentMsOf,
34
+ jobAgent,
34
35
  buildLiveActivity,
35
36
  pickLiveActivity,
36
37
  TOOL_MIX_WINDOW,
@@ -171,6 +172,30 @@ test('silentMsOf 到阈值才算静默,不到 / 没这个字段一律 null', (
171
172
  assert.equal(silentMsOf(null, base), null);
172
173
  });
173
174
 
175
+ // ── jobAgent(看板卡片上那个执行器图标该画谁) ────────────────────────────
176
+ // 认不出时必须回空串,不能回落成某个品牌:卡片上画一个错的执行器标,
177
+ // 用户会以为这条是 Claude 跑的,而它可能是 Codex —— 这比不画图标糟得多。
178
+
179
+ test('jobAgent:认识的执行器原样返回', () => {
180
+ assert.equal(jobAgent({ agent: 'claude' }), 'claude');
181
+ assert.equal(jobAgent({ agent: 'opencode' }), 'opencode');
182
+ assert.equal(jobAgent({ agent: 'codex' }), 'codex');
183
+ });
184
+
185
+ test('jobAgent:大小写与首尾空白归一后再认(jobs.json 会被手工编辑)', () => {
186
+ assert.equal(jobAgent({ agent: ' Codex ' }), 'codex');
187
+ assert.equal(jobAgent({ agent: 'CLAUDE' }), 'claude');
188
+ });
189
+
190
+ test('jobAgent:认不出的一律空串,不猜', () => {
191
+ assert.equal(jobAgent({ agent: 'gemini' }), '');
192
+ assert.equal(jobAgent({ agent: '' }), '');
193
+ assert.equal(jobAgent({}), ''); // 老记录没有 agent 字段
194
+ assert.equal(jobAgent({ agent: 123 }), '');
195
+ assert.equal(jobAgent(null), '');
196
+ assert.equal(jobAgent(undefined), '');
197
+ });
198
+
174
199
  // ── buildLiveActivity ───────────────────────────────────────────────────
175
200
 
176
201
  const START = '2026-09-28T10:00:00.000Z';
@@ -421,12 +421,15 @@ export async function appendReport(entry) {
421
421
  }
422
422
 
423
423
  /**
424
- * 正在执行的执行体清单(看板左栏「执行监控」用)。
424
+ * 正在执行的执行体清单(右栏控制台顶部的「{n} 个执行中」计数用)。
425
425
  *
426
426
  * 一行 = 一个活跃 job(不是任务):job 只是执行进程,
427
427
  * 所以文案上叫「执行」而不是「Agent」—— 不要把正在跑的 claude 进程
428
428
  * 渲染成一个并不存在的"智能体集群"。
429
429
  *
430
+ * 它曾有第二个消费者:看板左栏那块「执行监控」面板(2026-09-29 已删,
431
+ * 字段与中间「进行中」卡片重合,只剩 PID 有增量 —— 那个已并到卡片上)。
432
+ *
430
433
  * @param {{ jobs?: object[], tasks?: object[] }} input
431
434
  */
432
435
  export function buildRunningAgents({ jobs = [], tasks = [] } = {}) {
@@ -34,7 +34,7 @@
34
34
  // - Git 状态复用 utils/directoryGitState.js 的批量探测(带并发上限 + TTL 缓存 + 超时)。
35
35
 
36
36
  import { probeDirectoryGitStates } from '../../utils/directoryGitState.js';
37
- import { pickLiveActivity, tailExcerpt } from './jobActivity.js';
37
+ import { pickLiveActivity, tailExcerpt, jobAgent } from './jobActivity.js';
38
38
 
39
39
  /** 看板列,数组顺序即列顺序 */
40
40
  export const TASK_COLUMNS = ['todo', 'doing', 'done'];
@@ -336,6 +336,14 @@ export function decorateTaskForBoard(task, jobsForTask = [], { now = Date.now()
336
336
  * 也不能把上一次的旧话当成这次的结果摆出来。
337
337
  */
338
338
  lastReply: live ? null : tailExcerpt(last ? last.output : ''),
339
+ /**
340
+ * 最近一条 job 用的执行器(卡片上的品牌图标;认不出 / 从没跑过 → '')。
341
+ * 与 live.agent 的分工:那个只在**跑的时候**有值,跑完就断——而"这条是 Claude 还是
342
+ * Codex 跑的"在跑完之后同样要能回答(卡片上那句收尾的话究竟是谁说的)。
343
+ * 两条的校验口径同一份(jobActivity 的 jobAgent),所以同一张卡片上不会出现
344
+ * "跑着显示 A、跑完变成 B"。
345
+ */
346
+ lastJobAgent: jobAgent(last),
339
347
  lastJobStatus: last ? last.status : null,
340
348
  // 最近一条 job 的结束时间 = 这张卡片"跑完"的时刻。
341
349
  // 看板的「已完成」列要按完成时间倒序排(最新完成的在最上边),而 updatedAt 撑不起这个排序:
@@ -209,6 +209,32 @@ test('decorateTaskForBoard: 只回卡片需要的字段', () => {
209
209
  // 没跑过就没有完成时间——看板「已完成」列的排序靠它,空值必须显式是 null
210
210
  // 而不是 undefined(前端 doneAt 的回退链要能一路退到 updatedAt)
211
211
  assert.equal(card.lastJobEndedAt, null);
212
+ // 执行器同理:从没跑过时是空串(前端据此不画品牌图标),不是 undefined
213
+ assert.equal(card.lastJobAgent, '');
214
+ });
215
+
216
+ // ── 卡片上的执行器图标 ──────────────────────────────────────────────
217
+ // live.agent 只在**跑的时候**有值,跑完就断;而"这条是 Claude 还是 Codex 跑的"
218
+ // 在跑完之后同样要能回答(卡片上那句收尾的话是谁说的)。
219
+
220
+ test('decorateTaskForBoard: lastJobAgent 取最近一条 job 的执行器', () => {
221
+ const base = { id: 't1', title: '', desc: '', createdAt: '2025-12-31T00:00:00Z' };
222
+ const jobs = [
223
+ { ...job('j1', 't1', 'done', '2026-01-01T00:00:00Z', '2026-01-01T00:10:00Z'), agent: 'claude' },
224
+ { ...job('j2', 't1', 'done', '2026-01-03T00:00:00Z', '2026-01-03T00:20:00Z'), agent: 'codex' },
225
+ ];
226
+ // 最新的那条(j2)说了算,不是先跑的 j1
227
+ assert.equal(decorateTaskForBoard(base, jobs).lastJobAgent, 'codex');
228
+ });
229
+
230
+ test('decorateTaskForBoard: 执行器认不出时 lastJobAgent 是空串(前端不画图标)', () => {
231
+ const base = { id: 't1', title: '', desc: '', createdAt: '2025-12-31T00:00:00Z' };
232
+ // 老记录可能整条都没有 agent 字段
233
+ const legacy = [job('j1', 't1', 'done', '2026-01-01T00:00:00Z', '2026-01-01T00:10:00Z')];
234
+ assert.equal(decorateTaskForBoard(base, legacy).lastJobAgent, '');
235
+ // jobs.json 会被手工编辑,写了个不认识的执行器也不能透出去换成一个错的品牌标
236
+ const bogus = [{ ...job('j1', 't1', 'done', '2026-01-01T00:00:00Z'), agent: 'gemini' }];
237
+ assert.equal(decorateTaskForBoard(base, bogus).lastJobAgent, '');
212
238
  });
213
239
 
214
240
  test('decorateTaskForBoard: lastJobEndedAt 取最近一条 job 的结束时间(已完成列排序用)', () => {
@@ -1 +0,0 @@
1
- import"./rolldown-runtime-C0FnF6B9.js";import{An as e,Cn as t,Dn as n,En as r,Er as i,H as a,Jn as o,Kn as s,Mn as c,Pn as ee,Pr as l,Qn as te,Qr as u,Rn as d,Tn as f,Un as p,Xn as m,Yr as h,_n as ne,a as g,br as _,cr as re,gn as ie,hn as v,jn as y,ln as ae,lr as b,or as x,pr as S,qr as C,vn as oe,wn as w,xn as se,xr as ce,yn as T}from"./element-plus-Bx3j6vzG.js";import{o as le,r as E,t as ue}from"./vendor-CyWsJd6S.js";import{n as D,t as O}from"./_plugin-vue_export-helper-DBFEfstn.js";import{t as de}from"./SvgIcon-Cgmy9qD6.js";import{t as fe}from"./configStore-6xFSX9E5.js";import{n as pe,t as me}from"./fileIcon-B5p5pGN9.js";import{a as he,i as ge,n as _e,r as ve,s as k,t as ye}from"./previewSandbox-BzitUMhT.js";import{a as be,c as xe,l as Se,n as Ce,o as we,r as A,s as Te}from"./monaco-DkJuNcCr.js";import{t as Ee}from"./useThemeObserver-DYxFoHh1.js";import{r as De,t as j}from"./flow-mindmap-OPE_D4da.js";import{r as Oe}from"./index-BVkLqLHo.js";import{a as ke,i as Ae,n as je,o as Me,r as Ne,s as Pe,t as Fe}from"./agentConversations-CuFU_pdW.js";import{n as Ie,t as M}from"./AgentEngineSelector-Dtvp5u3j.js";function Le(e){return{ts:`typescript`,tsx:`typescript`,js:`javascript`,mjs:`javascript`,cjs:`javascript`,jsx:`javascript`,vue:`html`,html:`html`,htm:`html`,css:`css`,scss:`scss`,less:`less`,json:`json`,jsonc:`json`,md:`markdown`,markdown:`markdown`,yaml:`yaml`,yml:`yaml`,toml:`ini`,xml:`xml`,svg:`xml`,sh:`shell`,bash:`shell`,zsh:`shell`,fish:`shell`,ps1:`powershell`,py:`python`,go:`go`,rs:`rust`,java:`java`,kt:`kotlin`,rb:`ruby`,php:`php`,c:`c`,cpp:`cpp`,h:`cpp`,cs:`csharp`,sql:`sql`,graphql:`graphql`,gql:`graphql`,txt:`plaintext`,log:`plaintext`,ini:`ini`,env:`ini`,gitignore:`ini`,dockerfile:`dockerfile`}[e.toLowerCase()]??`plaintext`}d(),S();var N={class:`mindmap-preview`},P={class:`mindmap-preview-canvas`},F=O(ee({__name:`MindmapPreview`,props:{content:{}},setup(e){let r=e,a=t(()=>j(r.content||``,`未命名文档`));return(e,t)=>(m(),n(`div`,N,[t[0]||=w(`div`,{class:`mindmap-preview-header`},[w(`span`,{class:`mindmap-preview-title`},`整篇导图`),w(`span`,{class:`mindmap-preview-hint`},`基于整篇 Markdown 自动生成 · 阅读模式`)],-1),w(`div`,P,[c(i(De),{data:a.value,"preview-mode":``},null,8,[`data`])])]))}}),[[`__scopeId`,`data-v-d2fe328b`]]);d(),S(),l(),ae();var Re={class:`agent-panel-header`},ze=[`title`,`aria-label`],Be={class:`agent-panel-title`},Ve={key:1,class:`agent-panel-title`},He=[`title`,`aria-label`],Ue=[`title`],We={class:`agent-panel-convs`},Ge=[`data-theme`],I=[`title`],L={class:`agent-context-att-name`},R=[`title`,`aria-label`],Ke=O(ee({__name:`EditorAgentPanel`,props:{activeFilePath:{},activeFileName:{},active:{type:Boolean}},emits:[`close`],setup(a,{emit:ee}){let l=a,te=ee,{theme:d}=Ee(),h=t(()=>d.value===`dark`?`dark`:`light`),{sessions:ne,sessionsLoading:g,currentSessionId:re,messages:v,isStreaming:y,pendingQuestion:ae,answeringQuestion:S,isSessionGenerating:oe,currentEngine:ce,pendingEngine:O,isEngineLocked:de,pickEngine:fe,loadSessions:pe,loadSession:me,deleteSession:he,renameSession:ge,newSession:_e,sendMessage:ve,answerQuestion:k,stop:ye}=Pe(),be=t(()=>de.value?ce.value:O.value);function xe(e){de.value||fe(e)}let Se=t(()=>ke(ne.value,oe)),Ce=Ne(),we=Ae(),A=_(null),Te=_(null),De=_(null),j=_(null),Oe=_(null),{width:Le,narrow:N}=Ie(Oe),P=_(`chat`),F=_(null),Ke=_(!1),z=null,qe=t(()=>!!l.activeFileName&&!Ke.value);function Je(e){return Array.from(e.children).find(e=>e.classList?.contains(`acu-input-attachments`))??null}function Ye(){let e=Te.value?.$el;if(!e)return;let t=e.querySelector(`.agent-context-slot`);if(!qe.value){if(!t)return;F.value=null,p(()=>t.remove());return}let n=F.value&&e.contains(F.value)?F.value:t;n||(n=document.createElement(`div`),n.className=`agent-context-slot`);let r=Je(e),i=r??e;n.parentElement!==i&&i.insertBefore(n,r?r.firstChild:e.firstChild),F.value=n}function Xe(e=!0){A.value?.scrollToBottom(e)}async function Ze(e){let t=Ke.value?void 0:l.activeFilePath||void 0;await ve(e.text,e.files,{openFilePath:t}),await p(),Xe()}function Qe(e){me(e),P.value=`chat`}function $e(){_e(),P.value=`chat`}x(()=>v.value.length,()=>{p(()=>Xe(!1))});function et(){let e=j.value;e&&typeof MutationObserver<`u`&&(z?.disconnect(),z=new MutationObserver(()=>Ye()),z.observe(e,{childList:!0,subtree:!0}))}return x(()=>l.active,e=>{e&&(P.value=`chat`,pe().catch(()=>{}),p(()=>{Ye(),Xe(!1)}))}),x(N,e=>{e||(P.value=`chat`)}),x(()=>l.activeFilePath,()=>{Ke.value=!1}),x(qe,e=>{e&&p(Ye)}),o(()=>{pe().catch(()=>{}),p(()=>{Ye(),et()})}),s(()=>{z?.disconnect(),z=null,F.value=null}),(t,o)=>(m(),n(`div`,{ref_key:`rootRef`,ref:Oe,class:`editor-agent-panel`},[w(`div`,Re,[i(N)&&P.value===`list`?(m(),n(T,{key:0},[w(`button`,{class:`agent-panel-icon-btn agent-panel-back-btn`,title:i(D)(`@EDITOR:返回对话`),"aria-label":i(D)(`@EDITOR:返回对话`),onClick:o[0]||=e=>P.value=`chat`},[...o[4]||=[w(`svg`,{viewBox:`0 0 24 24`,width:`13`,height:`13`,fill:`none`,stroke:`currentColor`,"stroke-width":`2`,"stroke-linecap":`round`,"stroke-linejoin":`round`},[w(`line`,{x1:`19`,y1:`12`,x2:`5`,y2:`12`}),w(`polyline`,{points:`12 19 5 12 12 5`})],-1)]],8,ze),w(`span`,Be,u(i(D)(`@EDITOR:会话列表`)),1)],64)):(m(),n(`span`,Ve,u(i(D)(`@EDITOR:g ai 对话`)),1)),o[7]||=w(`div`,{class:`agent-panel-spacer`},null,-1),!i(N)||P.value===`chat`?(m(),f(M,{key:2,class:`agent-panel-engine`,engine:be.value,locked:i(de),onSelect:xe},null,8,[`engine`,`locked`])):r(``,!0),i(N)&&P.value===`chat`?(m(),n(`button`,{key:3,class:`agent-panel-icon-btn agent-panel-list-btn`,title:i(D)(`@EDITOR:会话列表`),"aria-label":i(D)(`@EDITOR:会话列表`),onClick:o[1]||=e=>P.value=`list`},[...o[5]||=[e(`<svg viewBox="0 0 24 24" width="13" height="13" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" data-v-d910c906><line x1="8" y1="6" x2="21" y2="6" data-v-d910c906></line><line x1="8" y1="12" x2="21" y2="12" data-v-d910c906></line><line x1="8" y1="18" x2="21" y2="18" data-v-d910c906></line><line x1="3" y1="6" x2="3.01" y2="6" data-v-d910c906></line><line x1="3" y1="12" x2="3.01" y2="12" data-v-d910c906></line><line x1="3" y1="18" x2="3.01" y2="18" data-v-d910c906></line></svg>`,1)]],8,He)):r(``,!0),w(`button`,{class:`agent-panel-icon-btn`,title:i(D)(`@EDITOR:关闭对话`),onClick:o[2]||=e=>te(`close`)},[...o[6]||=[w(`svg`,{viewBox:`0 0 24 24`,width:`13`,height:`13`,fill:`none`,stroke:`currentColor`,"stroke-width":`2.5`,"stroke-linecap":`round`},[w(`line`,{x1:`18`,y1:`6`,x2:`6`,y2:`18`}),w(`line`,{x1:`6`,y1:`6`,x2:`18`,y2:`18`})],-1)]],8,Ue)]),w(`div`,{class:C([`agent-panel-body`,{"is-wide":!i(N)}])},[b(w(`div`,We,[c(i(E),{"show-search":!0,items:Se.value,"active-id":i(re),loading:i(g),labels:i(Ce),onSelect:Qe,onNew:$e,onRename:i(ge),onDelete:i(he)},null,8,[`items`,`active-id`,`loading`,`labels`,`onRename`,`onDelete`])],512),[[ie,!i(N)||P.value===`list`]]),b(w(`div`,{ref_key:`chatHostRef`,ref:De,class:`agent-panel-chat`},[c(i(ue),{ref_key:`chatRef`,ref:A,"show-input":!1,messages:i(v),"assistant-name":i(je),"assistant-avatar":i(Fe),theme:h.value,question:i(ae),"question-submitting":i(S),"question-labels":i(we),onAnswer:i(k)},null,8,[`messages`,`assistant-name`,`assistant-avatar`,`theme`,`question`,`question-submitting`,`question-labels`,`onAnswer`])],512),[[ie,!i(N)||P.value===`chat`]]),i(Le)>0?(m(),n(`div`,{key:0,ref_key:`composerRef`,ref:j,class:`agent-panel-composer acu-root`,"data-theme":h.value},[c(i(le),{ref_key:`inputRef`,ref:Te,disabled:i(y),generating:i(y),placeholder:i(y)?i(D)(`@AGENT:正在生成中...`):i(D)(`@AGENT:输入消息,Enter 发送`),"upload-config":{accept:i(Me)},onSend:Ze,onStop:i(ye)},null,8,[`disabled`,`generating`,`placeholder`,`upload-config`,`onStop`]),F.value?(m(),f(se,{key:0,to:F.value},[qe.value?(m(),n(`div`,{key:0,class:`agent-context-att`,title:a.activeFilePath||``},[o[9]||=w(`svg`,{class:`agent-context-att-icon`,viewBox:`0 0 24 24`,width:`14`,height:`14`,fill:`none`,stroke:`currentColor`,"stroke-width":`1.7`,"stroke-linecap":`round`,"stroke-linejoin":`round`,"aria-hidden":`true`},[w(`path`,{d:`M14 3H7a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V8z`}),w(`polyline`,{points:`14 3 14 8 19 8`})],-1),w(`span`,L,u(a.activeFileName),1),w(`button`,{type:`button`,class:`agent-context-att-remove`,title:i(D)(`@EDITOR:不引用当前文档`),"aria-label":i(D)(`@EDITOR:不引用当前文档`),onClick:o[3]||=e=>Ke.value=!0},[...o[8]||=[w(`svg`,{viewBox:`0 0 24 24`,width:`10`,height:`10`,fill:`none`,stroke:`currentColor`,"stroke-width":`3`,"stroke-linecap":`round`,"aria-hidden":`true`},[w(`line`,{x1:`18`,y1:`6`,x2:`6`,y2:`18`}),w(`line`,{x1:`6`,y1:`6`,x2:`18`,y2:`18`})],-1)]],8,R)],8,I)):r(``,!0)],8,[`to`])):r(``,!0)],8,Ge)):r(``,!0)],2)],512))}}),[[`__scopeId`,`data-v-d910c906`]]);d(),S(),l(),ae();var z={class:`sidebar-header`},qe={class:`sidebar-title`},Je={class:`sidebar-actions`},Ye={class:`sidebar-search`},Xe=[`placeholder`,`title`],Ze=[`title`,`aria-label`],Qe={key:0,class:`sidebar-tree`},$e={key:1,class:`tree-arrow-spacer`},et={class:`tree-icon mit-icon`,"aria-hidden":`true`},tt=[`xlink:href`],nt=[`onClick`,`onContextmenu`],rt={key:1,class:`tree-arrow-spacer`},it={key:2,class:`tree-icon mit-icon`,"aria-hidden":`true`},at=[`xlink:href`],ot={key:3,class:`tree-icon mit-icon`,"aria-hidden":`true`},st=[`xlink:href`],ct=[`title`],lt={key:0,class:`tree-name-hit`},ut={key:4,class:`tree-loading`},dt={key:0,viewBox:`0 0 24 24`,width:`13`,height:`13`,fill:`none`,stroke:`currentColor`,"stroke-width":`2`,"stroke-linecap":`round`,"stroke-linejoin":`round`,style:{"flex-shrink":`0`,color:`var(--color-warning)`}},ft={key:1,viewBox:`0 0 24 24`,width:`13`,height:`13`,fill:`none`,stroke:`currentColor`,"stroke-width":`2`,"stroke-linecap":`round`,"stroke-linejoin":`round`,style:{"flex-shrink":`0`,color:`var(--text-tertiary)`}},pt=[`placeholder`],mt={key:0,class:`tree-node tree-inline-input-row`,style:{paddingLeft:`12px`}},ht={key:0,viewBox:`0 0 24 24`,width:`13`,height:`13`,fill:`none`,stroke:`currentColor`,"stroke-width":`2`,"stroke-linecap":`round`,"stroke-linejoin":`round`,style:{"flex-shrink":`0`,color:`var(--color-warning)`}},gt={key:1,viewBox:`0 0 24 24`,width:`13`,height:`13`,fill:`none`,stroke:`currentColor`,"stroke-width":`2`,"stroke-linecap":`round`,"stroke-linejoin":`round`,style:{"flex-shrink":`0`,color:`var(--text-tertiary)`}},_t=[`placeholder`],vt={key:1,class:`tree-empty`},yt={key:1,class:`sidebar-loading`},bt={class:`editor-main`},xt={key:0,class:`editor-tabs`},St=[`onClick`,`title`],Ct={class:`tab-name`},wt={key:0,class:`tab-dirty-dot`},Tt=[`onClick`],Et=[`title`],Dt=[`disabled`,`title`],Ot=[`title`],kt={key:1,class:`editor-empty`},At={viewBox:`0 0 24 24`,width:`48`,height:`48`,fill:`none`,stroke:`currentColor`,"stroke-width":`1.2`,"stroke-linecap":`round`,"stroke-linejoin":`round`,style:{opacity:`.3`}},jt={class:`editor-empty-hint`},Mt={class:`editor-body`},Nt={key:0,class:`image-tab-placeholder`},Pt={viewBox:`0 0 24 24`,width:`56`,height:`56`,fill:`none`,stroke:`currentColor`,"stroke-width":`1.2`,"stroke-linecap":`round`,"stroke-linejoin":`round`,style:{opacity:`.35`}},Ft={class:`image-tab-placeholder-title`},It={class:`image-tab-placeholder-hint`},Lt={class:`preview-header`},Rt={class:`preview-title`},zt={class:`preview-ext-badge`},Bt=[`title`],Vt={class:`preview-body`},Ht=[`sandbox`,`srcdoc`],Ut=6e4,Wt=O(ee({__name:`EditorView`,props:{pendingFilePath:{}},setup(ee){self.MonacoEnvironment={getWorker(e,t){return t===`json`?new xe:t===`css`||t===`scss`||t===`less`?new Te:t===`html`||t===`handlebars`||t===`razor`?new we:t===`typescript`||t===`javascript`?new be:new Se}};let l=ee,d=fe(),ae=Oe(),S=_([]),le=_(!1),E=_(``),ue=_(``),O=null;function De(){E.value=``,ue.value=``,O&&=(clearTimeout(O),null)}x(E,e=>{O&&clearTimeout(O),O=setTimeout(()=>{ue.value=e,O=null},180)}),s(()=>{O&&clearTimeout(O),Ae()});let j=null;function ke(){Ae(),d.ui.fileTreeAutoRefresh&&(j=setInterval(()=>{document.hidden||d.currentDirectory&&(E.value.trim()||M())},Ut))}function Ae(){j&&=(clearInterval(j),null)}x(()=>d.currentDirectory,async()=>{if(Yt=!1,ke(),await Me(),I.value.some(e=>e.isDirty)){Yt=!0;return}I.value=[],L.value=null,tn(),await Qt()}),x(()=>d.ui.fileTreeAutoRefresh,()=>{ke()});async function je(e,t=0){let n=await(await fetch(`/api/browse_directory?path=${encodeURIComponent(e)}`)).json();return n.success?n.items.map(e=>({...e,depth:t,expanded:!1,loading:!1,children:(e.type,void 0)})):[]}async function Me(){let e=d.currentDirectory;if(e){le.value=!0;try{S.value=await je(e,0)}finally{le.value=!1}}}function Ne(e,t=new Set){for(let n of e)n.type===`directory`&&n.expanded&&(t.add(n.path),n.children&&Ne(n.children,t));return t}function Pe(e,t=0){return(e||[]).map(e=>{let n={name:e.name,path:e.path,type:e.type,depth:t,expanded:!1,loading:!1};return n.type===`directory`&&Array.isArray(e.children)&&(n.children=Pe(e.children,t+1),n.expanded=!0),n})}async function Fe(e){let t=d.currentDirectory;if(!t)return null;try{let n=await(await fetch(`/api/browse_directory_tree`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({path:t,expand:e})})).json().catch(()=>null);return!n?.success||!Array.isArray(n.items)?null:Pe(n.items,0)}catch{return null}}function Ie(e,t){let n=new Map(e.map(e=>[e.path,e])),r=e.length!==t.length;if(!r){for(let n=0;n<t.length;n++)if(e[n].path!==t[n].path){r=!0;break}}for(let e=0;e<t.length;e++){let i=t[e],a=n.get(i.path);a&&(t[e]=a,a.name!==i.name&&(a.name=i.name,r=!0),a.type===`directory`&&a.expanded&&Array.isArray(i.children)&&(Array.isArray(a.children)?Ie(a.children,i.children)&&(a.children=i.children,r=!0):(a.children=i.children,r=!0)))}return r}async function M(e={}){if(!d.currentDirectory)return;let t=await Fe([...Ne(S.value)]);if(!t){e.notifyOnError&&g.error(D(`@EDITOR:刷新失败`));return}if(S.value.length===0){S.value=t;return}Ie(S.value,t)&&(S.value=t)}async function N(e){if(e.type===`directory`){if(e.expanded)e.expanded=!1;else{if(!e.children){e.loading=!0;try{e.children=await je(e.path,e.depth+1)}finally{e.loading=!1}}e.expanded=!0}}}function P(e){let t=[];for(let n of e)t.push(n),n.type===`directory`&&n.expanded&&n.children&&t.push(...P(n.children));return t}let Re=t(()=>ue.value.trim().toLowerCase());function ze(e,t){if(!t)return e;let n=[];for(let r of e){let e=r.children?ze(r.children,t):[],i=r.name.toLowerCase().includes(t);r.type===`directory`?(i||e.length>0)&&(r.expanded=!0,r.children!==void 0&&(r.children=e),n.push(r)):i&&n.push(r)}return n}let Be=t(()=>ze(S.value,Re.value)),Ve=t(()=>Re.value.length>0&&Be.value.length===0);function He(e){let t=ue.value.trim();if(!t)return[{text:e,hit:!1}];let n=e.toLowerCase(),r=t.toLowerCase(),i=[],a=0,o=n.indexOf(r,a);for(;o!==-1;)o>a&&i.push({text:e.slice(a,o),hit:!1}),i.push({text:e.slice(o,o+r.length),hit:!0}),a=o+r.length,o=n.indexOf(r,a);return a<e.length&&i.push({text:e.slice(a),hit:!1}),i}let Ue=_(null);function We(){let e=Ue.value;e&&(e.focus(),e.select())}function Ge(){E.value?(De(),Ue.value?.focus()):(Ue.value?.blur(),B.value?.focus())}let I=_([]),L=_(null),R=_(null);async function Wt(e){if(R.value=e,e.type!==`file`){N(e);return}let t=I.value.find(t=>t.path===e.path);if(console.log(`[debug-open-in-editor] EditorView openFile existing tab check:`,{path:e.path,found:!!t,tabsCount:I.value.length}),t){L.value=e.path,p(()=>rn());return}let n=e.name.split(`.`).pop()||``;if(Hn.has(n.toLowerCase())){let t={path:e.path,name:e.name,content:``,originalContent:``,isDirty:!1,language:`plaintext`};I.value.push(t),L.value=e.path,J.value=!0;return}if(k(e.name)){let t={path:e.path,name:e.name,content:``,originalContent:``,isDirty:!1,language:`office`};I.value.push(t),L.value=e.path,Y.value=!1,J.value=!0;return}try{let t=await fetch(`/api/editor/file?path=${encodeURIComponent(e.path)}`),r=await t.json();if(console.log(`[debug-open-in-editor] EditorView fetch /api/editor/file response:`,{path:e.path,status:t.status,data:r}),!r.success){g.error(`${D(`@EDITOR:打开文件失败: `)}${r.error}`);return}let i=Le(n),a={path:e.path,name:e.name,content:r.content,originalContent:r.content,isDirty:!1,language:i};console.log(`[debug-open-in-editor] EditorView pushing tab:`,{path:a.path,name:a.name,contentLen:a.content.length,lang:a.language}),I.value.push(a),L.value=e.path,console.log(`[debug-open-in-editor] EditorView tabs after push:`,I.value.length,`activeTabPath=`,L.value)}catch(e){console.log(`[debug-open-in-editor] EditorView fetch threw:`,e),g.error(`${D(`@EDITOR:打开文件失败: `)}${e.message}`)}}x(()=>l.pendingFilePath,async e=>{if(console.log(`[debug-open-in-editor] EditorView watcher fired, filePath=`,e),!e)return;await p();let t=e.split(/[\\/]/).pop()||e;console.log(`[debug-open-in-editor] EditorView calling openFile, name=`,t,`path=`,e),await Wt({name:t,path:e,type:`file`,depth:0}),console.log(`[debug-open-in-editor] EditorView openFile returned`)},{immediate:!0});function Gt(e,t){t.stopPropagation();let n=I.value.findIndex(t=>t.path===e);n!==-1&&(!I.value[n].isDirty||confirm(D(`@EDITOR:文件未保存,确认关闭?`)))&&(I.value.splice(n,1),L.value===e&&(L.value=I.value[Math.min(n,I.value.length-1)]?.path??null))}let Kt=()=>I.value.find(e=>e.path===L.value)??null,qt=t(()=>({expandedDirs:[...Ne(S.value)],tabs:I.value.map(e=>e.path),activeTab:L.value})),Jt=!1,Yt=!1;function Xt(){Yt&&!Jt&&d.isUiLoaded&&d.saveEditorWorkspace(qt.value)}x(qt,()=>Xt());async function Zt(e){if(I.value.some(t=>t.path===e))return;let t=e.split(/[\\/]/).pop()||e,n=(t.split(`.`).pop()||``).toLowerCase();if(Hn.has(n)){I.value.push({path:e,name:t,content:``,originalContent:``,isDirty:!1,language:`plaintext`});return}if(k(t)){I.value.push({path:e,name:t,content:``,originalContent:``,isDirty:!1,language:`office`});return}try{let r=await(await fetch(`/api/editor/file?path=${encodeURIComponent(e)}`)).json();if(!r.success)return;let i=V.get(e);i&&!i.isDisposed()&&B.value?.getModel()!==i&&(i.dispose(),V.delete(e)),I.value.push({path:e,name:t,content:r.content,originalContent:r.content,isDirty:!1,language:Le(n)})}catch{}}async function Qt(){try{for(let e=0;e<100&&!d.isUiLoaded;e++)await new Promise(e=>setTimeout(e,50));if(!d.isUiLoaded)return;let e=d.getEditorWorkspace();if(!e)return;Jt=!0;try{if(e.expandedDirs.length>0){let t=await Fe(e.expandedDirs);t&&(S.value=t)}for(let t of e.tabs)await Zt(t);if(!l.pendingFilePath&&e.activeTab&&I.value.some(t=>t.path===e.activeTab)){L.value=e.activeTab;let t=Kt(),n=(t?.name.split(`.`).pop()||``).toLowerCase();t&&(Hn.has(n)||k(t.name))&&(J.value=!0)}}finally{Jt=!1}}finally{Yt=!0,Xt()}}let $t=_(null),B=ce(null),V=new Map;function en(e){let t=V.get(e.path);return(!t||t.isDisposed())&&(t=A.createModel(e.content,e.language,Ce.file(e.path)),V.set(e.path,t)),t}function tn(){B.value?.setModel(null);for(let e of V.values())e.isDisposed()||e.dispose();V.clear()}function nn(){if(!$t.value)return;let e=document.documentElement.getAttribute(`data-theme`)===`dark`;B.value=A.create($t.value,{theme:e?`vs-dark`:`vs`,fontSize:13,lineHeight:20,fontFamily:`'JetBrains Mono', 'Fira Code', 'Cascadia Code', Consolas, monospace`,minimap:{enabled:!0},scrollBeyondLastLine:!1,renderWhitespace:`selection`,automaticLayout:!0,tabSize:2,wordWrap:`off`,smoothScrolling:!0,cursorBlinking:`smooth`,padding:{top:8,bottom:8},editContext:!1}),B.value.onDidChangeModelContent(()=>{let e=Kt();if(!e)return;let t=B.value.getValue();e.content=t,e.isDirty=t!==e.originalContent}),B.value.onDidBlurEditorText(()=>{d.ui.editorAutoSave&&Kt()?.isDirty&&sn(!1),requestAnimationFrame(()=>{if(!H.value||H.value.offsetParent===null)return;let e=document.activeElement;e&&(e.tagName===`INPUT`||e.tagName===`TEXTAREA`||e.isContentEditable||e.classList?.contains(`native-edit-context`)||e.classList?.contains(`monaco-editor`))||rn()})})}x(L,e=>{if(!B.value||!e)return;let t=I.value.find(t=>t.path===e);if(!t)return;let n=en(t);B.value.setModel(n),p(()=>rn())});function rn(){if(!B.value)return;B.value.focus();let e=document.querySelector(`.monaco-editor .native-edit-context`);e&&document.activeElement!==e&&requestAnimationFrame(()=>e.focus?.())}x(()=>I.value.filter(e=>e.isDirty).length,e=>{ae.setDirtyCount(e)},{immediate:!0}),s(()=>ae.setDirtyCount(0)),Ee(()=>{let e=document.documentElement.getAttribute(`data-theme`)===`dark`;A.setTheme(e?`vs-dark`:`vs`)});let an=null;function on(){if(an||!H.value)return;let e=[H.value],t=H.value.parentElement;for(;t;){if(t.classList.contains(`view-pane`)||t.classList.contains(`editor-pane`)){e.push(t);break}t=t.parentElement}an=new MutationObserver(()=>{H.value&&H.value.offsetParent!==null&&L.value&&B.value&&requestAnimationFrame(()=>{let e=document.activeElement;e&&(e.tagName===`INPUT`||e.tagName===`TEXTAREA`||e.isContentEditable||e.classList?.contains(`native-edit-context`)||e.classList?.contains(`monaco-editor`))||rn()})});let n=an;e.forEach(e=>n.observe(e,{attributes:!0,attributeFilter:[`style`,`class`,`hidden`]}))}o(async()=>{nn(),await Me(),await Qt(),on(),ke(),L.value&&p(()=>rn())}),s(()=>{an?.disconnect(),an=null,B.value?.dispose(),V.forEach(e=>e.dispose()),V.clear()});async function sn(e=!1){let t=Kt();if(t)try{let n=await(await fetch(`/api/editor/file`,{method:`PUT`,headers:{"Content-Type":`application/json`},body:JSON.stringify({path:t.path,content:t.content})})).json();if(!n.success){g.error(`${D(`@EDITOR:保存失败: `)}${n.error}`);return}t.originalContent=t.content,t.isDirty=!1,e||g.success(D(`@EDITOR:已保存`))}catch(e){g.error(`${D(`@EDITOR:保存失败: `)}${e.message}`)}}function cn(e){let t=e.ctrlKey||e.metaKey;if(t&&e.key.toLowerCase()===`f`){e.preventDefault(),We();return}t&&e.key===`s`&&(e.preventDefault(),sn())}o(()=>window.addEventListener(`keydown`,cn)),s(()=>window.removeEventListener(`keydown`,cn));let H=_(null),ln=_(220),un=!1,dn=0,fn=0;function pn(e){un=!0,dn=e.clientX,fn=ln.value,document.addEventListener(`mousemove`,mn),document.addEventListener(`mouseup`,hn),e.preventDefault()}function mn(e){if(!un)return;let t=e.clientX-dn;ln.value=Math.max(140,Math.min(400,fn+t))}function hn(){un=!1,document.removeEventListener(`mousemove`,mn),document.removeEventListener(`mouseup`,hn)}function gn(e){return e.type===`directory`?pe(e.name):me(e.name)}let U=_(null),W=_(null),G=_(null),_n=_(null);function vn(){let e=R.value;return e?e.type===`directory`?{parentPath:e.path,afterNode:e,depth:e.depth+1}:{parentPath:e.path.includes(`/`)||e.path.includes(`\\`)?e.path.replace(/[\\/][^\\/]+$/,``):d.currentDirectory??``,afterNode:e,depth:e.depth}:{parentPath:d.currentDirectory??``,afterNode:null,depth:0}}async function yn(){let e=vn();R.value?.type===`directory`&&!R.value.expanded&&await N(R.value),U.value={...e,kind:`file`,value:``},await p(),G.value?.focus()}async function bn(){let e=vn();R.value?.type===`directory`&&!R.value.expanded&&await N(R.value),U.value={...e,kind:`directory`,value:``},await p(),G.value?.focus()}function xn(){U.value=null}async function Sn(){let e=U.value;if(!e||!e.value.trim()){U.value=null;return}let t=e.value.trim(),n=e.parentPath.replace(/[/\\]$/,``)+`/`+t;if(e.kind===`file`){let e=await(await fetch(`/api/editor/file`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({path:n})})).json();if(!e.success){g.error(e.error);return}g.success(D(`@EDITOR:创建成功`))}else{let e=await(await fetch(`/api/editor/directory`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({path:n})})).json();if(!e.success){g.error(e.error);return}g.success(D(`@EDITOR:创建成功`))}U.value=null,await M()}function Cn(e){e.key===`Enter`&&(e.preventDefault(),Sn()),e.key===`Escape`&&(e.preventDefault(),xn())}let K=_(null),wn=_(null);function Tn(e,t){e.preventDefault(),K.value={x:e.clientX,y:e.clientY,node:t}}function q(){K.value=null}async function En(){let e=K.value?.node;q(),e&&(R.value=e),e?.type===`directory`&&!e.expanded&&await N(e);let t=vn();U.value={...t,kind:`file`,value:``},await p(),G.value?.focus()}async function Dn(){let e=K.value?.node;q(),e&&(R.value=e),e?.type===`directory`&&!e.expanded&&await N(e);let t=vn();U.value={...t,kind:`directory`,value:``},await p(),G.value?.focus()}async function On(){let e=K.value?.node;q(),e&&(W.value={node:e,value:e.name},await p(),_n.value?.focus(),_n.value?.select())}async function kn(){let e=W.value;if(!e||!e.value.trim()||e.value.trim()===e.node.name){W.value=null;return}let t=e.value.trim(),n=e.node.path.replace(/[\\/][^\\/]+$/,``).replace(/[/\\]$/,``)+`/`+t,r=await(await fetch(`/api/editor/rename`,{method:`PUT`,headers:{"Content-Type":`application/json`},body:JSON.stringify({oldPath:e.node.path,newPath:n})})).json();if(!r.success){g.error(r.error);return}let i=I.value.find(t=>t.path===e.node.path);i&&(i.path=n,i.name=t,L.value===e.node.path&&(L.value=n)),W.value=null,await M()}function An(e){e.key===`Enter`&&(e.preventDefault(),kn()),e.key===`Escape`&&(e.preventDefault(),W.value=null)}async function jn(){let e=K.value?.node;if(q(),!e)return;let t=e.type===`directory`?D(`@EDITOR:文件夹`):D(`@EDITOR:文件`);if(!confirm(`${D(`@EDITOR:确认删除`)} ${t} "${e.name}"?`))return;let n=await(await fetch(`/api/editor/entry?path=${encodeURIComponent(e.path)}`,{method:`DELETE`})).json();if(!n.success){g.error(n.error);return}let r=I.value.findIndex(t=>t.path===e.path);r!==-1&&(I.value.splice(r,1),L.value===e.path&&(L.value=I.value[Math.max(0,r-1)]?.path??null)),g.success(D(`@EDITOR:已删除`)),await M()}async function Mn(){let e=K.value?.node;if(q(),e)try{let t=await(await fetch(`/api/editor/reveal`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({path:e.path})})).json();t.success||g.error(t.error)}catch(e){g.error(e?.message||String(e))}}function Nn(e){let t=d.currentDirectory;if(!t)return e;let n=e=>e.replace(/\\/g,`/`).replace(/\/+$/,``),r=n(t),i=n(e);return i===r?`.`:i.startsWith(r+`/`)?i.slice(r.length+1):i}async function Pn(e){try{if(navigator.clipboard&&window.isSecureContext)return await navigator.clipboard.writeText(e),!0}catch{}try{let t=document.createElement(`textarea`);t.value=e,t.style.position=`fixed`,t.style.left=`-9999px`,document.body.appendChild(t),t.select();let n=document.execCommand(`copy`);return document.body.removeChild(t),n}catch{return!1}}async function Fn(){let e=K.value?.node;q(),e&&(await Pn(e.path)?g.success(D(`@EDITOR:已复制到剪贴板`)):g.error(D(`@EDITOR:复制失败`)))}async function In(){let e=K.value?.node;q(),e&&(await Pn(Nn(e.path))?g.success(D(`@EDITOR:已复制到剪贴板`)):g.error(D(`@EDITOR:复制失败`)))}async function Ln(){let e=K.value?.node;if(q(),e)try{let t=await(await fetch(`/api/editor/open-in-vscode`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({path:e.path})})).json();t.success||g.error(t.error||D(`@EDITOR:VSCode 未安装`))}catch(e){g.error(e?.message||String(e))}}let Rn=new Set([`html`,`htm`]),zn=t(()=>{let e=K.value?.node;if(!e||e.type!==`file`)return!1;let t=e.name.slice(e.name.lastIndexOf(`.`)+1).toLowerCase();return Rn.has(t)});async function Bn(){let e=K.value?.node;if(q(),e)try{let t=await(await fetch(`/api/editor/open-in-browser`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({path:e.path})})).json();t.success||g.error(t.error||D(`@EDITOR:浏览器打开失败`))}catch(e){g.error(e?.message||String(e))}}o(()=>document.addEventListener(`click`,q)),s(()=>document.removeEventListener(`click`,q));let Vn=new Set([`md`,`html`,`htm`,`svg`]),Hn=new Set([`png`,`jpg`,`jpeg`,`gif`,`webp`,`ico`,`bmp`]),J=_(!1),Y=_(!1),X=_(!1),Un=_(!1),Wn=_(0),Z=t(()=>I.value.find(e=>e.path===L.value)??null),Gn=t(()=>Z.value?.language===`plaintext`&&Z.value?.content===``),Kn=t(()=>Z.value?.language===`office`),Q=t(()=>(Z.value?.name??``).split(`.`).pop()?.toLowerCase()??``),qn=t(()=>Vn.has(Q.value)||Hn.has(Q.value)||k(Q.value)),Jn=t(()=>((Z.value?.content??``).match(/^#{1,6}\s+/gm)||[]).length),$=t(()=>Q.value===`md`&&Jn.value>0);x(L,()=>{J.value&&!qn.value&&(J.value=!1),Y.value&&!$.value&&(Y.value=!1)});function Yn(){qn.value&&(J.value||(Y.value=!1,X.value=!1),J.value=!J.value)}function Xn(){$.value&&(Y.value||(J.value=!1,X.value=!1),Y.value=!Y.value)}function Zn(){X.value||(J.value=!1,Y.value=!1),X.value=!X.value,Un.value=!0}let Qn=t(()=>{let e=Z.value;if(!e)return``;let t=Q.value,n=document.documentElement.getAttribute(`data-theme`)===`light`?`#ffffff`:`#0d1117`;return t===`html`||t===`htm`?_e(e.content):t===`svg`?`<!DOCTYPE html><html><head><meta charset="utf-8"><style>html,body{margin:0;padding:0;background:${n};display:flex;align-items:center;justify-content:center;min-height:100vh;}svg{max-width:100%;max-height:90vh;}</style></head><body>${e.content}</body></html>`:``}),$n=!1,er=0,tr=0;function nr(e){$n=!0,er=e.clientX,tr=Wn.value,document.addEventListener(`mousemove`,rr),document.addEventListener(`mouseup`,ir),e.preventDefault()}function rr(e){if(!$n)return;let t=e.clientX-er;Wn.value=Math.max(200,Math.min(900,tr+t))}function ir(){$n=!1,document.removeEventListener(`mousemove`,rr),document.removeEventListener(`mouseup`,ir)}return(t,o)=>(m(),n(T,null,[w(`div`,{class:`editor-view`,ref_key:`containerRef`,ref:H},[w(`div`,{class:`editor-sidebar`,style:h({width:ln.value+`px`})},[w(`div`,z,[w(`span`,qe,u(i(D)(`@EDITOR:资源管理器`)),1),w(`div`,Je,[c(i(a),{content:i(D)(`@EDITOR:新建文件`),placement:`bottom`,"show-after":300},{default:re(()=>[w(`button`,{class:`sidebar-action-btn`,onClick:yn},[...o[11]||=[w(`svg`,{viewBox:`0 0 24 24`,width:`14`,height:`14`,fill:`none`,stroke:`currentColor`,"stroke-width":`2`,"stroke-linecap":`round`,"stroke-linejoin":`round`},[w(`path`,{d:`M14.5 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7.5L14.5 2z`}),w(`polyline`,{points:`14 2 14 8 20 8`}),w(`line`,{x1:`12`,y1:`13`,x2:`12`,y2:`19`}),w(`line`,{x1:`9`,y1:`16`,x2:`15`,y2:`16`})],-1)]])]),_:1},8,[`content`]),c(i(a),{content:i(D)(`@EDITOR:新建文件夹`),placement:`bottom`,"show-after":300},{default:re(()=>[w(`button`,{class:`sidebar-action-btn`,onClick:bn},[...o[12]||=[w(`svg`,{viewBox:`0 0 24 24`,width:`14`,height:`14`,fill:`none`,stroke:`currentColor`,"stroke-width":`2`,"stroke-linecap":`round`,"stroke-linejoin":`round`},[w(`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`}),w(`line`,{x1:`12`,y1:`11`,x2:`12`,y2:`17`}),w(`line`,{x1:`9`,y1:`14`,x2:`15`,y2:`14`})],-1)]])]),_:1},8,[`content`]),c(i(a),{content:i(D)(`@EDITOR:刷新`),placement:`bottom`,"show-after":300},{default:re(()=>[w(`button`,{class:`sidebar-action-btn`,onClick:o[0]||=e=>M({notifyOnError:!0})},[...o[13]||=[w(`svg`,{viewBox:`0 0 24 24`,width:`14`,height:`14`,fill:`none`,stroke:`currentColor`,"stroke-width":`2`,"stroke-linecap":`round`,"stroke-linejoin":`round`},[w(`polyline`,{points:`1 4 1 10 7 10`}),w(`polyline`,{points:`23 20 23 14 17 14`}),w(`path`,{d:`M20.49 9A9 9 0 0 0 5.64 5.64L1 10m22 4l-4.64 4.36A9 9 0 0 1 3.51 15`})],-1)]])]),_:1},8,[`content`])])]),w(`div`,Ye,[o[15]||=w(`svg`,{class:`sidebar-search-icon`,viewBox:`0 0 24 24`,width:`13`,height:`13`,fill:`none`,stroke:`currentColor`,"stroke-width":`2`,"stroke-linecap":`round`,"stroke-linejoin":`round`,"aria-hidden":`true`},[w(`circle`,{cx:`11`,cy:`11`,r:`7`}),w(`line`,{x1:`21`,y1:`21`,x2:`16.65`,y2:`16.65`})],-1),b(w(`input`,{ref_key:`searchInputRef`,ref:Ue,"onUpdate:modelValue":o[1]||=e=>E.value=e,type:`text`,class:`sidebar-search-input`,placeholder:i(D)(`@EDITOR:搜索文件...`),title:i(D)(`@EDITOR:搜索文件...`),spellcheck:`false`,autocomplete:`off`,onKeydown:ne(Ge,[`escape`])},null,40,Xe),[[v,E.value]]),E.value?(m(),n(`button`,{key:0,class:`sidebar-search-clear`,title:i(D)(`@EDITOR:清除搜索`),"aria-label":i(D)(`@EDITOR:清除搜索`),onClick:De},[...o[14]||=[w(`svg`,{viewBox:`0 0 24 24`,width:`12`,height:`12`,fill:`none`,stroke:`currentColor`,"stroke-width":`2.5`,"stroke-linecap":`round`,"stroke-linejoin":`round`,"aria-hidden":`true`},[w(`line`,{x1:`18`,y1:`6`,x2:`6`,y2:`18`}),w(`line`,{x1:`6`,y1:`6`,x2:`18`,y2:`18`})],-1)]],8,Ze)):r(``,!0)]),le.value?(m(),n(`div`,yt,[...o[24]||=[w(`span`,{class:`spin-icon`},null,-1)]])):(m(),n(`div`,Qe,[(m(!0),n(T,null,te(P(Be.value),e=>(m(),n(T,{key:e.path},[W.value&&W.value.node.path===e.path?(m(),n(`div`,{key:0,class:`tree-node tree-inline-input-row`,style:h({paddingLeft:12+e.depth*14+`px`})},[e.type===`directory`?(m(),n(`span`,{key:0,class:C([`tree-arrow`,{expanded:e.expanded}])},[...o[16]||=[w(`svg`,{viewBox:`0 0 24 24`,width:`10`,height:`10`,fill:`none`,stroke:`currentColor`,"stroke-width":`2.5`,"stroke-linecap":`round`,"stroke-linejoin":`round`},[w(`polyline`,{points:`9 18 15 12 9 6`})],-1)]],2)):(m(),n(`span`,$e)),(m(),n(`svg`,et,[w(`use`,{"xlink:href":`#${gn(e)}`},null,8,tt)])),b(w(`input`,{ref_for:!0,ref_key:`renameInputRef`,ref:_n,"onUpdate:modelValue":o[2]||=e=>W.value.value=e,onKeydown:An,onBlur:kn,onClick:o[3]||=oe(()=>{},[`stop`])},null,544),[[v,W.value.value]])],4)):(m(),n(`div`,{key:1,class:C([`tree-node`,{"tree-node--dir":e.type===`directory`,"tree-node--file":e.type===`file`,"tree-node--active":L.value===e.path,"tree-node--selected":R.value?.path===e.path&&L.value!==e.path}]),style:h({paddingLeft:12+e.depth*14+`px`}),onClick:t=>Wt(e),onContextmenu:t=>Tn(t,e)},[e.type===`directory`?(m(),n(`span`,{key:0,class:C([`tree-arrow`,{expanded:e.expanded}])},[...o[17]||=[w(`svg`,{viewBox:`0 0 24 24`,width:`10`,height:`10`,fill:`none`,stroke:`currentColor`,"stroke-width":`2.5`,"stroke-linecap":`round`,"stroke-linejoin":`round`},[w(`polyline`,{points:`9 18 15 12 9 6`})],-1)]],2)):(m(),n(`span`,rt)),e.type===`directory`?(m(),n(`svg`,it,[w(`use`,{"xlink:href":`#${gn(e)}`},null,8,at)])):(m(),n(`svg`,ot,[w(`use`,{"xlink:href":`#${gn(e)}`},null,8,st)])),w(`span`,{class:`tree-name`,title:e.path},[(m(!0),n(T,null,te(He(e.name),(e,t)=>(m(),n(T,{key:t},[e.hit?(m(),n(`span`,lt,u(e.text),1)):(m(),n(T,{key:1},[y(u(e.text),1)],64))],64))),128))],8,ct),e.loading?(m(),n(`span`,ut)):r(``,!0)],46,nt)),U.value&&U.value.afterNode?.path===e.path?(m(),n(`div`,{key:2,class:`tree-node tree-inline-input-row`,style:h({paddingLeft:12+U.value.depth*14+`px`})},[o[20]||=w(`span`,{class:`tree-arrow-spacer`},null,-1),U.value.kind===`directory`?(m(),n(`svg`,dt,[...o[18]||=[w(`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`},null,-1)]])):(m(),n(`svg`,ft,[...o[19]||=[w(`path`,{d:`M14.5 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7.5L14.5 2z`},null,-1),w(`polyline`,{points:`14 2 14 8 20 8`},null,-1)]])),b(w(`input`,{ref_for:!0,ref_key:`inlineInputRef`,ref:G,class:`tree-inline-input`,"onUpdate:modelValue":o[4]||=e=>U.value.value=e,placeholder:U.value.kind===`file`?i(D)(`@EDITOR:输入文件名`):i(D)(`@EDITOR:输入文件夹名`),onKeydown:Cn,onBlur:xn,onClick:o[5]||=oe(()=>{},[`stop`])},null,40,pt),[[v,U.value.value]])],4)):r(``,!0)],64))),128)),U.value&&U.value.afterNode===null?(m(),n(`div`,mt,[o[23]||=w(`span`,{class:`tree-arrow-spacer`},null,-1),U.value.kind===`directory`?(m(),n(`svg`,ht,[...o[21]||=[w(`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`},null,-1)]])):(m(),n(`svg`,gt,[...o[22]||=[w(`path`,{d:`M14.5 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7.5L14.5 2z`},null,-1),w(`polyline`,{points:`14 2 14 8 20 8`},null,-1)]])),b(w(`input`,{ref_key:`inlineInputRef`,ref:G,class:`tree-inline-input`,"onUpdate:modelValue":o[6]||=e=>U.value.value=e,placeholder:U.value.kind===`file`?i(D)(`@EDITOR:输入文件名`):i(D)(`@EDITOR:输入文件夹名`),onKeydown:Cn,onBlur:xn,onClick:o[7]||=oe(()=>{},[`stop`])},null,40,_t),[[v,U.value.value]])])):r(``,!0),!U.value&&(Ve.value||S.value.length===0)?(m(),n(`div`,vt,u(Ve.value?i(D)(`@EDITOR:未找到匹配文件`):i(D)(`@EDITOR:暂无文件`)),1)):r(``,!0)]))],4),w(`div`,{class:`editor-resizer`,onMousedown:pn},null,32),w(`div`,bt,[I.value.length>0?(m(),n(`div`,xt,[(m(!0),n(T,null,te(I.value,e=>(m(),n(`div`,{key:e.path,class:C([`editor-tab`,{active:L.value===e.path,dirty:e.isDirty}]),onClick:t=>L.value=e.path,title:e.path},[w(`span`,Ct,u(e.name),1),e.isDirty?(m(),n(`span`,wt)):r(``,!0),w(`button`,{class:`tab-close`,onClick:t=>Gt(e.path,t)},[...o[25]||=[w(`svg`,{viewBox:`0 0 24 24`,width:`12`,height:`12`,fill:`none`,stroke:`currentColor`,"stroke-width":`2.5`,"stroke-linecap":`round`},[w(`line`,{x1:`18`,y1:`6`,x2:`6`,y2:`18`}),w(`line`,{x1:`6`,y1:`6`,x2:`18`,y2:`18`})],-1)]],8,Tt)],10,St))),128)),o[29]||=w(`div`,{class:`editor-tabs-spacer`},null,-1),qn.value?(m(),n(`button`,{key:0,class:C([`preview-toggle-btn`,{active:J.value}]),title:J.value?i(D)(`@EDITOR:关闭预览`):i(D)(`@EDITOR:打开预览`),onClick:Yn},[o[26]||=w(`svg`,{viewBox:`0 0 24 24`,width:`14`,height:`14`,fill:`none`,stroke:`currentColor`,"stroke-width":`2`,"stroke-linecap":`round`,"stroke-linejoin":`round`},[w(`path`,{d:`M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z`}),w(`circle`,{cx:`12`,cy:`12`,r:`3`})],-1),w(`span`,null,u(i(D)(`@EDITOR:预览`)),1)],10,Et)):r(``,!0),$.value?(m(),n(`button`,{key:1,class:C([`preview-toggle-btn mindmap-toggle-btn`,{active:Y.value,disabled:!$.value}]),disabled:!$.value,title:$.value?Y.value?i(D)(`@EDITOR:关闭思维导图`):i(D)(`@EDITOR:打开思维导图`):i(D)(`@EDITOR:需要至少一个 # 标题`),onClick:Xn},[o[27]||=e(`<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" data-v-9f0417f2><circle cx="12" cy="12" r="2" data-v-9f0417f2></circle><circle cx="5" cy="6" r="1.6" data-v-9f0417f2></circle><circle cx="19" cy="6" r="1.6" data-v-9f0417f2></circle><circle cx="5" cy="18" r="1.6" data-v-9f0417f2></circle><circle cx="19" cy="18" r="1.6" data-v-9f0417f2></circle><line x1="11" y1="11" x2="6.4" y2="7" data-v-9f0417f2></line><line x1="13" y1="11" x2="17.6" y2="7" data-v-9f0417f2></line><line x1="11" y1="13" x2="6.4" y2="17" data-v-9f0417f2></line><line x1="13" y1="13" x2="17.6" y2="17" data-v-9f0417f2></line></svg>`,1),w(`span`,null,u(i(D)(`@EDITOR:思维导图`)),1)],10,Dt)):r(``,!0),w(`button`,{class:C([`preview-toggle-btn agent-toggle-btn`,{active:X.value}]),title:X.value?i(D)(`@EDITOR:关闭 g ai 对话`):i(D)(`@EDITOR:打开 g ai 对话`),onClick:Zn},[o[28]||=w(`svg`,{viewBox:`0 0 24 24`,width:`14`,height:`14`,fill:`none`,stroke:`currentColor`,"stroke-width":`2`,"stroke-linecap":`round`,"stroke-linejoin":`round`},[w(`path`,{d:`M12 3l1.9 4.6L18.5 9.5l-4.6 1.9L12 16l-1.9-4.6L5.5 9.5l4.6-1.9z`}),w(`path`,{d:`M18 16.5l.9 2.1 2.1.9-2.1.9-.9 2.1-.9-2.1-2.1-.9 2.1-.9z`})],-1),w(`span`,null,u(i(D)(`@EDITOR:g ai 对话`)),1)],10,Ot)])):r(``,!0),I.value.length===0?(m(),n(`div`,kt,[(m(),n(`svg`,At,[...o[30]||=[w(`path`,{d:`M14.5 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7.5L14.5 2z`},null,-1),w(`polyline`,{points:`14 2 14 8 20 8`},null,-1)]])),w(`p`,null,u(i(D)(`@EDITOR:从左侧选择文件打开`)),1),w(`p`,jt,u(i(D)(`@EDITOR:Ctrl+S 保存`)),1)])):r(``,!0),w(`div`,Mt,[w(`div`,{class:C([`monaco-container`,{hidden:I.value.length===0||Gn.value||Kn.value}]),ref_key:`editorContainerRef`,ref:$t},null,2),Gn.value&&I.value.length>0&&!J.value&&!Y.value?(m(),n(`div`,Nt,[(m(),n(`svg`,Pt,[...o[31]||=[w(`rect`,{x:`3`,y:`3`,width:`18`,height:`18`,rx:`2`},null,-1),w(`circle`,{cx:`9`,cy:`9`,r:`2`},null,-1),w(`path`,{d:`m21 15-5-5L5 21`},null,-1)]])),w(`p`,Ft,u(Z.value?.name),1),w(`p`,It,u(i(D)(`@EDITOR:这是图片文件,已在右侧预览面板中打开`)),1)])):r(``,!0),(J.value||Y.value||X.value)&&I.value.length>0?(m(),n(`div`,{key:1,class:`preview-resizer`,onMousedown:nr},null,32)):r(``,!0),Un.value?b((m(),f(Ke,{key:2,active:X.value&&I.value.length>0,"active-file-path":L.value,"active-file-name":Z.value?.name??``,style:h({flexBasis:Wn.value+`px`}),onClose:o[8]||=e=>X.value=!1},null,8,[`active`,`active-file-path`,`active-file-name`,`style`])),[[ie,X.value&&I.value.length>0]]):r(``,!0),(J.value||Y.value)&&I.value.length>0?(m(),n(`div`,{key:3,class:`preview-panel`,style:h({flexBasis:Wn.value+`px`})},[w(`div`,Lt,[w(`span`,Rt,u(Y.value?i(D)(`@EDITOR:思维导图`):i(D)(`@EDITOR:预览`)),1),w(`span`,zt,u(Q.value.toUpperCase()),1),o[33]||=w(`div`,{class:`preview-header-spacer`},null,-1),w(`button`,{class:`preview-close-btn`,title:Y.value?i(D)(`@EDITOR:关闭思维导图`):i(D)(`@EDITOR:关闭预览`),onClick:o[9]||=e=>Y.value?Y.value=!1:J.value=!1},[...o[32]||=[w(`svg`,{viewBox:`0 0 24 24`,width:`13`,height:`13`,fill:`none`,stroke:`currentColor`,"stroke-width":`2.5`,"stroke-linecap":`round`},[w(`line`,{x1:`18`,y1:`6`,x2:`6`,y2:`18`}),w(`line`,{x1:`6`,y1:`6`,x2:`18`,y2:`18`})],-1)]],8,Bt)]),w(`div`,Vt,[Y.value&&Q.value===`md`&&Z.value?(m(),f(F,{key:0,content:Z.value.content,class:`preview-mindmap`},null,8,[`content`])):J.value&&Q.value===`md`&&Z.value?(m(),f(ge,{key:Z.value.path,content:Z.value.content,class:`preview-markdown`},null,8,[`content`])):J.value&&[`html`,`htm`,`svg`].includes(Q.value)?(m(),n(`iframe`,{key:2,class:`preview-iframe`,sandbox:i(ye),srcdoc:Qn.value},null,8,Ht)):J.value&&i(Hn).has(Q.value)&&Z.value?(m(),f(he,{key:3,"file-path":Z.value.path,"file-name":Z.value.name},null,8,[`file-path`,`file-name`])):J.value&&Kn.value&&Z.value?(m(),f(ve,{key:4,"file-path":Z.value.path},null,8,[`file-path`])):r(``,!0)])],4)):r(``,!0)])])],512),(m(),f(se,{to:`body`},[K.value?(m(),n(`div`,{key:0,ref_key:`ctxMenuRef`,ref:wn,class:`ctx-menu`,style:h({left:K.value.x+`px`,top:K.value.y+`px`}),onClick:o[10]||=oe(()=>{},[`stop`])},[w(`button`,{class:`ctx-menu-item`,onClick:En},[o[34]||=w(`svg`,{viewBox:`0 0 24 24`,width:`13`,height:`13`,fill:`none`,stroke:`currentColor`,"stroke-width":`2`,"stroke-linecap":`round`,"stroke-linejoin":`round`},[w(`path`,{d:`M14.5 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7.5L14.5 2z`}),w(`polyline`,{points:`14 2 14 8 20 8`}),w(`line`,{x1:`12`,y1:`13`,x2:`12`,y2:`19`}),w(`line`,{x1:`9`,y1:`16`,x2:`15`,y2:`16`})],-1),y(` `+u(i(D)(`@EDITOR:新建文件`)),1)]),w(`button`,{class:`ctx-menu-item`,onClick:Dn},[o[35]||=w(`svg`,{viewBox:`0 0 24 24`,width:`13`,height:`13`,fill:`none`,stroke:`currentColor`,"stroke-width":`2`,"stroke-linecap":`round`,"stroke-linejoin":`round`},[w(`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`}),w(`line`,{x1:`12`,y1:`11`,x2:`12`,y2:`17`}),w(`line`,{x1:`9`,y1:`14`,x2:`15`,y2:`14`})],-1),y(` `+u(i(D)(`@EDITOR:新建文件夹`)),1)]),o[42]||=w(`div`,{class:`ctx-menu-sep`},null,-1),w(`button`,{class:`ctx-menu-item`,onClick:Fn},[o[36]||=w(`svg`,{viewBox:`0 0 24 24`,width:`13`,height:`13`,fill:`none`,stroke:`currentColor`,"stroke-width":`2`,"stroke-linecap":`round`,"stroke-linejoin":`round`},[w(`rect`,{x:`9`,y:`9`,width:`13`,height:`13`,rx:`2`,ry:`2`}),w(`path`,{d:`M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1`})],-1),y(` `+u(i(D)(`@EDITOR:复制绝对路径`)),1)]),w(`button`,{class:`ctx-menu-item`,onClick:In},[o[37]||=w(`svg`,{viewBox:`0 0 24 24`,width:`13`,height:`13`,fill:`none`,stroke:`currentColor`,"stroke-width":`2`,"stroke-linecap":`round`,"stroke-linejoin":`round`},[w(`path`,{d:`M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71`}),w(`path`,{d:`M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71`})],-1),y(` `+u(i(D)(`@EDITOR:复制相对路径`)),1)]),o[43]||=w(`div`,{class:`ctx-menu-sep`},null,-1),zn.value?(m(),n(`button`,{key:0,class:`ctx-menu-item`,onClick:Bn},[o[38]||=w(`svg`,{viewBox:`0 0 24 24`,width:`13`,height:`13`,fill:`none`,stroke:`currentColor`,"stroke-width":`2`,"stroke-linecap":`round`,"stroke-linejoin":`round`},[w(`circle`,{cx:`12`,cy:`12`,r:`9`}),w(`line`,{x1:`3`,y1:`12`,x2:`21`,y2:`12`}),w(`path`,{d:`M12 3a13 13 0 0 1 3.5 9 13 13 0 0 1-3.5 9 13 13 0 0 1-3.5-9A13 13 0 0 1 12 3z`})],-1),y(` `+u(i(D)(`@EDITOR:在浏览器中打开`)),1)])):r(``,!0),w(`button`,{class:`ctx-menu-item`,onClick:Mn},[o[39]||=w(`svg`,{viewBox:`0 0 24 24`,width:`13`,height:`13`,fill:`none`,stroke:`currentColor`,"stroke-width":`2`,"stroke-linecap":`round`,"stroke-linejoin":`round`},[w(`path`,{d:`M3 7a2 2 0 0 1 2-2h4l2 2h8a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z`}),w(`path`,{d:`M3 7v10a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V9a2 2 0 0 0-2-2`})],-1),y(` `+u(i(D)(`@EDITOR:在资源管理器中打开`)),1)]),w(`button`,{class:`ctx-menu-item`,onClick:Ln},[c(de,{"icon-class":`vscode`,"class-name":`ctx-menu-icon`}),y(` `+u(i(D)(`@EDITOR:在 VSCode 中打开`)),1)]),w(`button`,{class:`ctx-menu-item`,onClick:On},[o[40]||=w(`svg`,{viewBox:`0 0 24 24`,width:`13`,height:`13`,fill:`none`,stroke:`currentColor`,"stroke-width":`2`,"stroke-linecap":`round`,"stroke-linejoin":`round`},[w(`path`,{d:`M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7`}),w(`path`,{d:`M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z`})],-1),y(` `+u(i(D)(`@EDITOR:重命名`)),1)]),w(`button`,{class:`ctx-menu-item ctx-menu-item--danger`,onClick:jn},[o[41]||=w(`svg`,{viewBox:`0 0 24 24`,width:`13`,height:`13`,fill:`none`,stroke:`currentColor`,"stroke-width":`2`,"stroke-linecap":`round`,"stroke-linejoin":`round`},[w(`polyline`,{points:`3 6 5 6 21 6`}),w(`path`,{d:`M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6`}),w(`path`,{d:`M10 11v6`}),w(`path`,{d:`M14 11v6`}),w(`path`,{d:`M9 6V4a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2`})],-1),y(` `+u(i(D)(`@EDITOR:删除`)),1)])],4)):r(``,!0)]))],64))}}),[[`__scopeId`,`data-v-9f0417f2`]]);export{Wt as default};