dsh-agy-link 0.4.44 → 0.4.49

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.
package/dist/client.js CHANGED
@@ -8,12 +8,16 @@ window.__ModuleLoader__.load({
8
8
  const BRAND_PATHS = {
9
9
  gemini: "M11.04 19.32Q12 21.51 12 24q0-2.49.93-4.68.96-2.19 2.58-3.81t3.81-2.55Q21.51 12 24 12q-2.49 0-4.68-.93a12.3 12.3 0 0 1-3.81-2.58 12.3 12.3 0 0 1-2.58-3.81Q12 2.49 12 0q0 2.49-.96 4.68-.93 2.19-2.55 3.81a12.3 12.3 0 0 1-3.81 2.58Q2.49 12 0 12q2.49 0 4.68.96 2.19.93 3.81 2.55t2.55 3.81",
10
10
  claude: "m4.7144 15.9555 4.7174-2.6471.079-.2307-.079-.1275h-.2307l-.7893-.0486-2.6956-.0729-2.3375-.0971-2.2646-.1214-.5707-.1215-.5343-.7042.0546-.3522.4797-.3218.686.0608 1.5179.1032 2.2767.1578 1.6514.0972 2.4468.255h.3886l.0546-.1579-.1336-.0971-.1032-.0972L6.973 9.8356l-2.55-1.6879-1.3356-.9714-.7225-.4918-.3643-.4614-.1578-1.0078.6557-.7225.8803.0607.2246.0607.8925.686 1.9064 1.4754 2.4893 1.8336.3643.3035.1457-.1032.0182-.0728-.164-.2733-1.3539-2.4467-1.445-2.4893-.6435-1.032-.17-.6194c-.0607-.255-.1032-.4674-.1032-.7285L6.287.1335 6.6997 0l.9957.1336.419.3642.6192 1.4147 1.0018 2.2282 1.5543 3.0296.4553.8985.2429.8318.091.255h.1579v-.1457l.1275-1.706.2368-2.0947.2307-2.6957.0789-.7589.3764-.9107.7468-.4918.5828.2793.4797.686-.0668.4433-.2853 1.8517-.5586 2.9021-.3643 1.9429h.2125l.2429-.2429.9835-1.3053 1.6514-2.0643.7286-.8196.85-.9046.5464-.4311h1.0321l.759 1.1293-.34 1.1657-1.0625 1.3478-.8804 1.1414-1.2628 1.7-.7893 1.36.0729.1093.1882-.0183 2.8535-.607 1.5421-.2794 1.8396-.3157.8318.3886.091.3946-.3278.8075-1.967.4857-2.3072.4614-3.4364.8136-.0425.0304.0486.0607 1.5482.1457.6618.0364h1.621l3.0175.2247.7892.522.4736.6376-.079.4857-1.2142.6193-1.6393-.3886-3.825-.9107-1.3113-.3279h-.1822v.1093l1.0929 1.0686 2.0035 1.8092 2.5075 2.3314.1275.5768-.3218.4554-.34-.0486-2.2039-1.6575-.85-.7468-1.9246-1.621h-.1275v.17l.4432.6496 2.3436 3.5214.1214 1.0807-.17.3521-.6071.2125-.6679-.1214-1.3721-1.9246L14.38 17.959l-1.1414-1.9428-.1397.079-.674 7.2552-.3156.3703-.7286.2793-.6071-.4614-.3218-.7468.3218-1.4753.3886-1.9246.3157-1.53.2853-1.9004.17-.6314-.0121-.0425-.1397.0182-1.4328 1.9672-2.1796 2.9446-1.7243 1.8456-.4128.164-.7164-.3704.0667-.6618.4008-.5889 2.386-3.0357 1.4389-1.882.929-1.0868-.0062-.1579h-.0546l-6.3385 4.1164-1.1293.1457-.4857-.4554.0608-.7467.2307-.2429 1.9064-1.3114Z",
11
- openai: "M22.2819 9.8211a5.9847 5.9847 0 0 0-.5157-4.9108 6.0462 6.0462 0 0 0-6.5098-2.9A6.0651 6.0651 0 0 0 4.9807 4.1818a5.9847 5.9847 0 0 0-3.9977 2.9 6.0462 6.0462 0 0 0 .7427 7.0966 5.98 5.98 0 0 0 .511 4.9107 6.051 6.051 0 0 0 6.5146 2.9001A5.9847 5.9847 0 0 0 13.2599 24a6.0557 6.0557 0 0 0 5.7718-4.2058 5.9894 5.9894 0 0 0 3.9977-2.9001 6.0557 6.0557 0 0 0-.7475-7.0729zm-9.022 12.6081a4.4755 4.4755 0 0 1-2.8764-1.0408l.1419-.0804 4.7783-2.7582a.7948.7948 0 0 0 .3927-.6813v-6.7369l2.02 1.1686a.071.071 0 0 1 .038.052v5.5826a4.504 4.504 0 0 1-4.4945 4.4944zm-9.6607-4.1254a4.4708 4.4708 0 0 1-.5346-3.0137l.142.0852 4.783 2.7582a.7712.7712 0 0 0 .7806 0l5.8428-3.3685v2.3324a.0804.0804 0 0 1-.0332.0615L9.74 19.9502a4.4992 4.4992 0 0 1-6.1408-1.6464zM2.3408 7.8956a4.485 4.485 0 0 1 2.3655-1.9728V11.6a.7664.7664 0 0 0 .3879.6765l5.8144 3.3543-2.0201 1.1685a.0757.0757 0 0 1-.071 0l-4.8303-2.7865A4.504 4.504 0 0 1 2.3408 7.872zm16.5963 3.8558L13.1038 8.364 15.1192 7.2a.0757.0757 0 0 1 .071 0l4.8303 2.7913a4.4944 4.4944 0 0 1-.6765 8.1042v-5.6772a.79.79 0 0 0-.407-.667zm2.0107-3.0231l-.142-.0852-4.7735-2.7818a.7759.7759 0 0 0-.7854 0L9.409 9.2297V6.8974a.0662.0662 0 0 1 .0284-.0615l4.8303-2.7866a4.4992 4.4992 0 0 1 6.6802 4.66zM8.3065 12.863l-2.02-1.1638a.0804.0804 0 0 1-.038-.0567V6.0742a4.4992 4.4992 0 0 1 7.3757-3.4537l-.142.0805L8.704 5.459a.7948.7948 0 0 0-.3927.6813zm1.0976-2.3654l2.602-1.4998 2.6069 1.4998v2.9994l-2.5974 1.4997-2.6067-1.4997Z"
11
+ openai: "M22.2819 9.8211a5.9847 5.9847 0 0 0-.5157-4.9108 6.0462 6.0462 0 0 0-6.5098-2.9A6.0651 6.0651 0 0 0 4.9807 4.1818a5.9847 5.9847 0 0 0-3.9977 2.9 6.0462 6.0462 0 0 0 .7427 7.0966 5.98 5.98 0 0 0 .511 4.9107 6.051 6.051 0 0 0 6.5146 2.9001A5.9847 5.9847 0 0 0 13.2599 24a6.0557 6.0557 0 0 0 5.7718-4.2058 5.9894 5.9894 0 0 0 3.9977-2.9001 6.0557 6.0557 0 0 0-.7475-7.0729zm-9.022 12.6081a4.4755 4.4755 0 0 1-2.8764-1.0408l.1419-.0804 4.7783-2.7582a.7948.7948 0 0 0 .3927-.6813v-6.7369l2.02 1.1686a.071.071 0 0 1 .038.052v5.5826a4.504 4.504 0 0 1-4.4945 4.4944zm-9.6607-4.1254a4.4708 4.4708 0 0 1-.5346-3.0137l.142.0852 4.783 2.7582a.7712.7712 0 0 0 .7806 0l5.8428-3.3685v2.3324a.0804.0804 0 0 1-.0332.0615L9.74 19.9502a4.4992 4.4992 0 0 1-6.1408-1.6464zM2.3408 7.8956a4.485 4.485 0 0 1 2.3655-1.9728V11.6a.7664.7664 0 0 0 .3879.6765l5.8144 3.3543-2.0201 1.1685a.0757.0757 0 0 1-.071 0l-4.8303-2.7865A4.504 4.504 0 0 1 2.3408 7.872zm16.5963 3.8558L13.1038 8.364 15.1192 7.2a.0757.0757 0 0 1 .071 0l4.8303 2.7913a4.4944 4.4944 0 0 1-.6765 8.1042v-5.6772a.79.79 0 0 0-.407-.667zm2.0107-3.0231l-.142-.0852-4.7735-2.7818a.7759.7759 0 0 0-.7854 0L9.409 9.2297V6.8974a.0662.0662 0 0 1 .0284-.0615l4.8303-2.7866a4.4992 4.4992 0 0 1 6.6802 4.66zM8.3065 12.863l-2.02-1.1638a.0804.0804 0 0 1-.038-.0567V6.0742a4.4992 4.4992 0 0 1 7.3757-3.4537l-.142.0805L8.704 5.459a.7948.7948 0 0 0-.3927.6813zm1.0976-2.3654l2.602-1.4998 2.6069 1.4998v2.9994l-2.5974 1.4997-2.6067-1.4997Z",
12
+ chrome: "M12 0C8.21 0 4.831 1.757 2.632 4.501l3.953 6.848A5.454 5.454 0 0 1 12 6.545h10.73c-.911-3.794-4.321-6.545-8.384-6.545zM1.986 6.368A11.95 11.95 0 0 0 0 12c0 6.096 4.542 11.134 10.428 11.89l3.953-6.848a5.454 5.454 0 0 1-5.836-2.585L1.986 6.368zm14.569 5.632a5.454 5.454 0 0 1-1.127 3.327l-5.365 9.293C10.784 24.873 11.385 25 12 25c6.627 0 12-5.373 12-12 0-.341-.014-.679-.042-1.013H12v.013h4.555z",
13
+ edge: "M23.95 13.91c-.13-1.68-.78-3.23-1.83-4.47-.98-1.16-2.29-1.99-3.76-2.4-.64-.18-1.3-.28-1.97-.29-2.09-.03-4.08.74-5.63 2.16-1.57 1.44-2.48 3.44-2.55 5.56.08 1.47.66 2.87 1.66 3.96.99 1.09 2.37 1.76 3.86 1.88 1.34.1 2.69-.26 3.82-1.02.44-.3.84-.66 1.18-1.07l2.87 2.87c-1.35 1.44-3.04 2.51-4.92 3.12-1.89.6-3.9.7-5.84.28-2.67-.58-5.06-2.06-6.75-4.17-1.68-2.12-2.59-4.78-2.57-7.51.02-2.85 1.04-5.6 2.89-7.77C6.88 4.31 9.53 2.85 12.44 2.38c2.19-.35 4.43-.2 6.55.45 2.13.65 4.04 1.83 5.58 3.44l-2.9 2.9c-1.12-1.19-2.52-2.06-4.07-2.54-1.55-.47-3.19-.57-4.78-.3-2.14.36-4.08 1.45-5.46 3.06-1.38 1.62-2.13 3.69-2.13 5.84 0 2.05.69 4.04 1.98 5.62 1.28 1.58 3.1 2.64 5.12 3.01 1.47.27 2.99.17 4.42-.31 1.43-.48 2.7-1.33 3.69-2.46l-4.13-4.13c-.37.28-.8.48-1.26.58-.46.1-.94.09-1.4-.02-.57-.14-1.08-.47-1.46-.93-.38-.46-.59-1.04-.59-1.64.03-.84.4-1.63 1.03-2.2.63-.57 1.45-.88 2.3-.87.87.01 1.7.35 2.32.95.62.61.98 1.43 1 2.3h4.67z"
12
14
  };
13
15
  const BRAND_COLORS = {
14
16
  gemini: "#8E75B2",
15
17
  claude: "#D97757",
16
- openai: "#10a37f"
18
+ openai: "#10a37f",
19
+ chrome: "#4285F4",
20
+ edge: "#0078D7"
17
21
  };
18
22
  const UI_PATHS = {
19
23
  trash: "M3 6h18M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2M10 11v6M14 11v6",
@@ -28,7 +32,10 @@ window.__ModuleLoader__.load({
28
32
  check: "M20 6L9 17l-5-5",
29
33
  x: "M18 6L6 18M6 6l12 12",
30
34
  externalLink: "M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6M15 3h6v6M10 14L21 3",
31
- alert: "M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0zM12 9v4M12 17h.01"
35
+ alert: "M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0zM12 9v4M12 17h.01",
36
+ move: "M9 5h.01M9 12h.01M9 19h.01M15 5h.01M15 12h.01M15 19h.01",
37
+ arrowUp: "M12 19V5M5 12l7-7 7 7",
38
+ arrowDown: "M12 5v14M19 12l-7 7-7-7"
32
39
  };
33
40
  //#endregion
34
41
  //#region src/client/locales.ts
@@ -39,6 +46,25 @@ window.__ModuleLoader__.load({
39
46
  "section.label": "Antigravity",
40
47
  "code.title": "代码",
41
48
  "code.inspect": "检查",
49
+ "tv.command": "命令",
50
+ "tv.write": "写入",
51
+ "tv.edit": "编辑",
52
+ "tv.read": "读取",
53
+ "tv.search": "搜索",
54
+ "tv.webSearch": "网页搜索",
55
+ "tv.list": "列表",
56
+ "tv.delete": "删除",
57
+ "tv.tool": "工具",
58
+ "tv.noOutput": "(无输出)",
59
+ "tv.noMatches": "(无匹配)",
60
+ "tv.noChanges": "(无文本变更)",
61
+ "tv.emptyDir": "(空目录)",
62
+ "tv.noDetail": "(无详情)",
63
+ "tv.hint.expandInspect": "点击展开 · 右键检查",
64
+ "tv.hint.collapseInspect": "点击收起 · 右键检查",
65
+ "tv.hint.expand": "点击展开",
66
+ "tv.hint.collapse": "点击收起",
67
+ "tv.diffMore": "… 其余 {count} 行未渲染",
42
68
  "code.copy": "复制",
43
69
  "code.copied": "已复制",
44
70
  "console.title": "Antigravity 管理控制台",
@@ -52,6 +78,7 @@ window.__ModuleLoader__.load({
52
78
  "account.add": "添加 Google 账号",
53
79
  "account.aliasDefault": "备用 Google 账号 {number}",
54
80
  "account.defaultAlias": "主账号(系统登录)",
81
+ "account.proxyPlaceholder": "专属代理 URL(如 http://127.0.0.1:7890,留空则继承全局)",
55
82
  "account.aliasPlaceholder": "账号别名(例如:备用账号 2)",
56
83
  "account.removeTitle": "移除此账号",
57
84
  "aria.aliasInput": "账号别名",
@@ -62,6 +89,9 @@ window.__ModuleLoader__.load({
62
89
  "account.settingPrimary": "设置中",
63
90
  "account.details": "明细",
64
91
  "account.collapse": "收起",
92
+ "account.expandOthers": "展开查看 Claude & GPT-OSS 模型额度",
93
+ "account.collapseOthers": "收起 Claude & GPT-OSS 模型额度",
94
+ "account.otherModels": "Claude / GPT 额度",
65
95
  "account.sync": "同步",
66
96
  "account.syncing": "同步中",
67
97
  "account.syncTitle": "刷新此账号(换号后立即同步 email / 额度 / 模型)",
@@ -127,13 +157,46 @@ window.__ModuleLoader__.load({
127
157
  "header.refreshQuota": "刷新额度",
128
158
  "header.refreshing": "刷新中",
129
159
  "console.close": "关闭(Esc)",
130
- "header.badgeTitle": "Antigravity:{count} 个账号已就绪 · 点击打开控制台"
160
+ "header.badgeTitle": "Antigravity:{count} 个账号已就绪 · 点击打开控制台",
161
+ "header.badgeLoading": "Antigravity · 点击打开控制台",
162
+ "account.type.agy": "Google Antigravity (CLI / OAuth)",
163
+ "account.loginType": "登录方式:",
164
+ "account.dragHandle": "拖拽调整优先级",
165
+ "account.moveUp": "上移优先级",
166
+ "account.moveDown": "下移优先级",
167
+ "account.reordered": "账号优先级顺序已更新",
168
+ "account.collapseAll": "全部折叠",
169
+ "account.expandAll": "全部展开",
170
+ "account.cardCollapse": "收起详情",
171
+ "account.cardExpand": "展开详情",
172
+ "account.draggingTarget": "放置至此成为优先级 #{num}",
173
+ "account.dragBanner": "正在调整优先级:将【{alias}】移动至 #{num}",
174
+ "account.dragHint": "拖拽卡片可实时调整调度优先级"
131
175
  };
132
176
  const en = {
133
177
  "section.label": "Antigravity",
134
178
  "console.title": "Antigravity Management Console",
135
179
  "code.title": "Code",
136
180
  "code.inspect": "Inspect",
181
+ "tv.command": "Command",
182
+ "tv.write": "Write",
183
+ "tv.edit": "Edit",
184
+ "tv.read": "Read",
185
+ "tv.search": "Search",
186
+ "tv.webSearch": "Web Search",
187
+ "tv.list": "List",
188
+ "tv.delete": "Delete",
189
+ "tv.tool": "Tool",
190
+ "tv.noOutput": "(no output)",
191
+ "tv.noMatches": "(no matches)",
192
+ "tv.noChanges": "(no textual changes)",
193
+ "tv.emptyDir": "(empty directory)",
194
+ "tv.noDetail": "(no detail)",
195
+ "tv.hint.expandInspect": "Click to expand · right-click to inspect",
196
+ "tv.hint.collapseInspect": "Click to collapse · right-click to inspect",
197
+ "tv.hint.expand": "Click to expand",
198
+ "tv.hint.collapse": "Click to collapse",
199
+ "tv.diffMore": "… {count} more lines not rendered",
137
200
  "code.copy": "Copy",
138
201
  "code.copied": "Copied",
139
202
  "status.ready": "Ready",
@@ -146,6 +209,7 @@ window.__ModuleLoader__.load({
146
209
  "account.add": "Add Google account",
147
210
  "account.aliasDefault": "Backup Google account {number}",
148
211
  "account.defaultAlias": "Primary account (system sign-in)",
212
+ "account.proxyPlaceholder": "Dedicated proxy URL (e.g. http://127.0.0.1:7890; leave blank to inherit global)",
149
213
  "account.aliasPlaceholder": "Account alias (for example: Backup account 2)",
150
214
  "account.removeTitle": "Remove this account",
151
215
  "aria.aliasInput": "Account alias",
@@ -156,6 +220,9 @@ window.__ModuleLoader__.load({
156
220
  "account.settingPrimary": "Setting...",
157
221
  "account.details": "Details",
158
222
  "account.collapse": "Collapse",
223
+ "account.expandOthers": "Expand Claude & GPT-OSS quotas",
224
+ "account.collapseOthers": "Collapse Claude & GPT-OSS quotas",
225
+ "account.otherModels": "Claude / GPT quotas",
159
226
  "account.sync": "Sync",
160
227
  "account.syncing": "Syncing...",
161
228
  "account.syncTitle": "Refresh this account (sync email, quota, and models after switching accounts)",
@@ -221,13 +288,46 @@ window.__ModuleLoader__.load({
221
288
  "header.refreshQuota": "Refresh quota",
222
289
  "header.refreshing": "Refreshing...",
223
290
  "console.close": "Close (Esc)",
224
- "header.badgeTitle": "Antigravity: {count} accounts ready · click to open the console"
291
+ "header.badgeTitle": "Antigravity: {count} accounts ready · click to open the console",
292
+ "header.badgeLoading": "Antigravity · click to open the console",
293
+ "account.type.agy": "Google Antigravity (CLI / OAuth)",
294
+ "account.loginType": "Login Method:",
295
+ "account.dragHandle": "Drag to reorder priority",
296
+ "account.moveUp": "Move up priority",
297
+ "account.moveDown": "Move down priority",
298
+ "account.reordered": "Account priorities reordered successfully",
299
+ "account.collapseAll": "Collapse all",
300
+ "account.expandAll": "Expand all",
301
+ "account.cardCollapse": "Collapse",
302
+ "account.cardExpand": "Expand details",
303
+ "account.draggingTarget": "Drop here to set priority #{num}",
304
+ "account.dragBanner": "Reordering: moving [{alias}] to priority #{num}",
305
+ "account.dragHint": "Drag cards to reorder scheduling priority in real time"
225
306
  };
226
307
  const ptBR = {
227
308
  "section.label": "Antigravity",
228
309
  "console.title": "Console de gerenciamento do Antigravity",
229
310
  "code.title": "Código",
230
311
  "code.inspect": "Inspecionar",
312
+ "tv.command": "Comando",
313
+ "tv.write": "Escrever",
314
+ "tv.edit": "Editar",
315
+ "tv.read": "Ler",
316
+ "tv.search": "Buscar",
317
+ "tv.webSearch": "Busca na web",
318
+ "tv.list": "Listar",
319
+ "tv.delete": "Excluir",
320
+ "tv.tool": "Ferramenta",
321
+ "tv.noOutput": "(sem saída)",
322
+ "tv.noMatches": "(nenhuma correspondência)",
323
+ "tv.noChanges": "(sem alterações de texto)",
324
+ "tv.emptyDir": "(diretório vazio)",
325
+ "tv.noDetail": "(sem detalhes)",
326
+ "tv.hint.expandInspect": "Clique para expandir · clique direito para inspecionar",
327
+ "tv.hint.collapseInspect": "Clique para recolher · clique direito para inspecionar",
328
+ "tv.hint.expand": "Clique para expandir",
329
+ "tv.hint.collapse": "Clique para recolher",
330
+ "tv.diffMore": "… {count} linhas restantes não renderizadas",
231
331
  "code.copy": "Copiar",
232
332
  "code.copied": "Copiado",
233
333
  "status.ready": "Pronto",
@@ -240,6 +340,7 @@ window.__ModuleLoader__.load({
240
340
  "account.add": "Adicionar conta Google",
241
341
  "account.aliasDefault": "Conta Google reserva {number}",
242
342
  "account.defaultAlias": "Conta principal (login do sistema)",
343
+ "account.proxyPlaceholder": "URL de proxy dedicada (ex.: http://127.0.0.1:7890; deixe vazio para herdar a global)",
243
344
  "account.aliasPlaceholder": "Apelido da conta (por exemplo: Conta reserva 2)",
244
345
  "account.removeTitle": "Remover esta conta",
245
346
  "aria.aliasInput": "Apelido da conta",
@@ -250,6 +351,9 @@ window.__ModuleLoader__.load({
250
351
  "account.settingPrimary": "Definindo...",
251
352
  "account.details": "Detalhes",
252
353
  "account.collapse": "Recolher",
354
+ "account.expandOthers": "Expandir cotas Claude e GPT-OSS",
355
+ "account.collapseOthers": "Recolher cotas Claude e GPT-OSS",
356
+ "account.otherModels": "Cotas Claude / GPT",
253
357
  "account.sync": "Sincronizar",
254
358
  "account.syncing": "Sincronizando...",
255
359
  "account.syncTitle": "Atualizar esta conta (sincroniza e-mail, cota e modelos depois de trocar de conta)",
@@ -315,13 +419,46 @@ window.__ModuleLoader__.load({
315
419
  "header.refreshQuota": "Atualizar cota",
316
420
  "header.refreshing": "Atualizando...",
317
421
  "console.close": "Fechar (Esc)",
318
- "header.badgeTitle": "Antigravity: {count} contas prontas · clique para abrir o console"
422
+ "header.badgeTitle": "Antigravity: {count} contas prontas · clique para abrir o console",
423
+ "header.badgeLoading": "Antigravity · clique para abrir o console",
424
+ "account.type.agy": "Google Antigravity (CLI / OAuth)",
425
+ "account.loginType": "Tipo de login:",
426
+ "account.dragHandle": "Arrastar para reordenar prioridade",
427
+ "account.moveUp": "Subir prioridade",
428
+ "account.moveDown": "Descer prioridade",
429
+ "account.reordered": "Prioridade das contas reordenada com sucesso",
430
+ "account.collapseAll": "Recolher todos",
431
+ "account.expandAll": "Expandir todos",
432
+ "account.cardCollapse": "Recolher",
433
+ "account.cardExpand": "Expandir detalhes",
434
+ "account.draggingTarget": "Soltar aqui para prioridade #{num}",
435
+ "account.dragBanner": "Reordenando: movendo [{alias}] para #{num}",
436
+ "account.dragHint": "Arraste os cartões para reordenar a prioridade em tempo real"
319
437
  };
320
438
  const es = {
321
439
  "section.label": "Antigravity",
322
440
  "console.title": "Consola de administración de Antigravity",
323
441
  "code.title": "Código",
324
442
  "code.inspect": "Inspeccionar",
443
+ "tv.command": "Comando",
444
+ "tv.write": "Escribir",
445
+ "tv.edit": "Editar",
446
+ "tv.read": "Leer",
447
+ "tv.search": "Buscar",
448
+ "tv.webSearch": "Búsqueda web",
449
+ "tv.list": "Listar",
450
+ "tv.delete": "Eliminar",
451
+ "tv.tool": "Herramienta",
452
+ "tv.noOutput": "(sin salida)",
453
+ "tv.noMatches": "(sin coincidencias)",
454
+ "tv.noChanges": "(sin cambios de texto)",
455
+ "tv.emptyDir": "(directorio vacío)",
456
+ "tv.noDetail": "(sin detalles)",
457
+ "tv.hint.expandInspect": "Clic para expandir · clic derecho para inspeccionar",
458
+ "tv.hint.collapseInspect": "Clic para contraer · clic derecho para inspeccionar",
459
+ "tv.hint.expand": "Clic para expandir",
460
+ "tv.hint.collapse": "Clic para contraer",
461
+ "tv.diffMore": "… {count} líneas restantes no renderizadas",
325
462
  "code.copy": "Copiar",
326
463
  "code.copied": "Copiado",
327
464
  "status.ready": "Listo",
@@ -334,6 +471,7 @@ window.__ModuleLoader__.load({
334
471
  "account.add": "Añadir cuenta de Google",
335
472
  "account.aliasDefault": "Cuenta de Google de respaldo {number}",
336
473
  "account.defaultAlias": "Cuenta principal (inicio de sesión del sistema)",
474
+ "account.proxyPlaceholder": "URL de proxy dedicada (p. ej., http://127.0.0.1:7890; déjalo vacío para heredar la global)",
337
475
  "account.aliasPlaceholder": "Alias de la cuenta (por ejemplo: Cuenta de respaldo 2)",
338
476
  "account.removeTitle": "Eliminar esta cuenta",
339
477
  "aria.aliasInput": "Alias de la cuenta",
@@ -344,6 +482,9 @@ window.__ModuleLoader__.load({
344
482
  "account.settingPrimary": "Configurando...",
345
483
  "account.details": "Detalles",
346
484
  "account.collapse": "Contraer",
485
+ "account.expandOthers": "Expandir cuotas Claude y GPT-OSS",
486
+ "account.collapseOthers": "Contraer cuotas Claude y GPT-OSS",
487
+ "account.otherModels": "Cuotas Claude / GPT",
347
488
  "account.sync": "Sincronizar",
348
489
  "account.syncing": "Sincronizando...",
349
490
  "account.syncTitle": "Actualizar esta cuenta (sincroniza correo, cuota y modelos tras cambiar de cuenta)",
@@ -409,9 +550,729 @@ window.__ModuleLoader__.load({
409
550
  "header.refreshQuota": "Actualizar cuota",
410
551
  "header.refreshing": "Actualizando...",
411
552
  "console.close": "Cerrar (Esc)",
412
- "header.badgeTitle": "Antigravity: {count} cuentas listas · haga clic para abrir la consola"
553
+ "header.badgeTitle": "Antigravity: {count} cuentas listas · haga clic para abrir la consola",
554
+ "header.badgeLoading": "Antigravity · haga clic para abrir la consola",
555
+ "account.type.agy": "Google Antigravity (CLI / OAuth)",
556
+ "account.loginType": "Tipo de inicio de sesión:",
557
+ "account.dragHandle": "Arrastrar para reordenar prioridad",
558
+ "account.moveUp": "Subir prioridad",
559
+ "account.moveDown": "Bajar prioridad",
560
+ "account.reordered": "Prioridad de cuentas reordenada con éxito",
561
+ "account.collapseAll": "Contraer todo",
562
+ "account.expandAll": "Expandir todo",
563
+ "account.cardCollapse": "Contraer",
564
+ "account.cardExpand": "Expandir detalles",
565
+ "account.draggingTarget": "Soltar aquí para prioridad #{num}",
566
+ "account.dragBanner": "Reordenando: moviendo [{alias}] a #{num}",
567
+ "account.dragHint": "Arrastre las tarjetas para reordenar la prioridad en tiempo real"
413
568
  };
414
569
  //#endregion
570
+ //#region src/client/decisions.ts
571
+ const DECISION_CSS = `
572
+ .agy-decision-container {
573
+ margin: 10px 0 6px 0;
574
+ padding: 10px 12px;
575
+ border-radius: 8px;
576
+ background: var(--dsw-alias-bg-l2, rgba(127, 127, 127, 0.07));
577
+ border: 1px dashed var(--dsw-alias-border-l3, rgba(127, 127, 127, 0.3));
578
+ }
579
+
580
+ .agy-decision-header {
581
+ font-size: 12px;
582
+ font-weight: 600;
583
+ color: var(--dsw-alias-label-secondary, #aaa);
584
+ margin-bottom: 8px;
585
+ display: flex;
586
+ align-items: center;
587
+ gap: 6px;
588
+ }
589
+
590
+ .agy-decision-chips {
591
+ display: flex;
592
+ flex-wrap: wrap;
593
+ gap: 8px;
594
+ }
595
+
596
+ .agy-decision-chip {
597
+ background: var(--dsw-alias-bg-l1, rgba(255, 255, 255, 0.05));
598
+ border: 1px solid var(--dsw-alias-border-l2, rgba(127, 127, 127, 0.25));
599
+ color: var(--dsw-alias-label-primary, #eee);
600
+ padding: 5px 12px;
601
+ border-radius: 999px;
602
+ cursor: pointer;
603
+ font-size: 12px;
604
+ font-weight: 500;
605
+ display: inline-flex;
606
+ align-items: center;
607
+ gap: 6px;
608
+ transition: all 0.15s ease;
609
+ user-select: none;
610
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
611
+ text-align: left;
612
+ max-width: 100%;
613
+ }
614
+
615
+ .agy-decision-chip:hover {
616
+ background: var(--dsw-alias-bg-hover, rgba(127, 127, 127, 0.18));
617
+ border-color: var(--dsw-alias-border-l4, rgba(127, 127, 127, 0.5));
618
+ transform: translateY(-1px);
619
+ box-shadow: 0 2px 6px rgba(0, 0, 0, 0.18);
620
+ }
621
+
622
+ .agy-decision-chip:active {
623
+ transform: translateY(0);
624
+ }
625
+
626
+ .agy-decision-chip.selected {
627
+ background: rgba(16, 185, 129, 0.15);
628
+ border-color: #10b981;
629
+ color: #10b981;
630
+ }
631
+
632
+ .agy-decision-chip-num {
633
+ font-weight: 700;
634
+ opacity: 0.8;
635
+ }
636
+
637
+ .agy-decision-chip-text {
638
+ overflow: hidden;
639
+ text-overflow: ellipsis;
640
+ white-space: nowrap;
641
+ }
642
+ `;
643
+ function injectStyles$3() {
644
+ if (typeof document === "undefined") return;
645
+ const styleId = "dsh-agy-link-decisions-css";
646
+ if (document.getElementById(styleId) === null) {
647
+ const st = document.createElement("style");
648
+ st.id = styleId;
649
+ st.textContent = DECISION_CSS;
650
+ const host = document.head ?? document.documentElement;
651
+ if (host) host.appendChild(st);
652
+ }
653
+ }
654
+ /**
655
+ * Insert text directly into DSH's chat composer
656
+ */
657
+ function insertTextIntoChatComposer(text) {
658
+ if (typeof document === "undefined" || !text) return false;
659
+ const inputEl = document.querySelector("[data-composer-input]") ?? document.querySelector("[role=\"textbox\"][contenteditable=\"true\"]") ?? document.querySelector("textarea");
660
+ if (!inputEl) return false;
661
+ inputEl.focus();
662
+ if (inputEl instanceof HTMLTextAreaElement || inputEl instanceof HTMLInputElement) {
663
+ const start = inputEl.selectionStart ?? inputEl.value.length;
664
+ const end = inputEl.selectionEnd ?? inputEl.value.length;
665
+ inputEl.setRangeText(text, start, end, "end");
666
+ inputEl.dispatchEvent(new Event("input", { bubbles: true }));
667
+ inputEl.dispatchEvent(new Event("change", { bubbles: true }));
668
+ return true;
669
+ }
670
+ try {
671
+ if (document.execCommand("insertText", false, text)) return true;
672
+ } catch {}
673
+ const existing = inputEl.textContent?.trim() ?? "";
674
+ inputEl.textContent = existing ? `${existing} ${text}` : text;
675
+ inputEl.dispatchEvent(new InputEvent("input", {
676
+ bubbles: true,
677
+ inputType: "insertText",
678
+ data: text
679
+ }));
680
+ return true;
681
+ }
682
+ /** Check if text header suggests an actionable decision or choice prompt */
683
+ const DECISION_HEADER_REGEX = /(?:请(?:您)?(?:拍板|指示|决定|选择|决策)|decision|next\s+step|choose|拍板|选择方案)/i;
684
+ /** Extract numbered options or choice items from a decision list */
685
+ function extractOptionsFromList(container) {
686
+ const options = [];
687
+ const listItems = container.querySelectorAll("li");
688
+ if (listItems.length > 0) for (let i = 0; i < listItems.length; i++) {
689
+ const li = listItems[i];
690
+ if (!li) continue;
691
+ const text = (li.textContent ?? "").trim();
692
+ if (!text) continue;
693
+ const m = text.match(/^([0-9]+[、..:]\s*)?(.*?)(?:[::—\-·]|\n|$)/);
694
+ const num = m?.[1]?.trim() ?? `${i + 1}.`;
695
+ let title = m?.[2]?.trim() || text.slice(0, 50);
696
+ if (title.length > 40) title = title.slice(0, 40) + "…";
697
+ options.push({
698
+ num,
699
+ title,
700
+ fullText: text
701
+ });
702
+ }
703
+ return options;
704
+ }
705
+ /**
706
+ * Scan DOM for decision prompts and mount interactive choice chips
707
+ */
708
+ function scanAndEnhanceDecisions() {
709
+ if (typeof document === "undefined") return;
710
+ const markdownContainers = document.querySelectorAll("div[class*=\"_markdown_\"], [data-markdown-variant]");
711
+ for (let i = 0; i < markdownContainers.length; i++) {
712
+ const container = markdownContainers[i];
713
+ if (!container || container.dataset.agyDecisionsScanned === "true") continue;
714
+ const headings = container.querySelectorAll("h2, h3, h4, p > strong");
715
+ for (let j = 0; j < headings.length; j++) {
716
+ const heading = headings[j];
717
+ if (!heading || heading.dataset.agyDecisionAttached === "true") continue;
718
+ const headingText = (heading.textContent ?? "").trim();
719
+ if (!DECISION_HEADER_REGEX.test(headingText)) continue;
720
+ let sibling = heading.nextElementSibling;
721
+ let targetList = null;
722
+ while (sibling) {
723
+ if (sibling.tagName === "OL" || sibling.tagName === "UL") {
724
+ targetList = sibling;
725
+ break;
726
+ }
727
+ if (/^H[1-4]$/.test(sibling.tagName)) break;
728
+ sibling = sibling.nextElementSibling;
729
+ }
730
+ if (!targetList) continue;
731
+ const options = extractOptionsFromList(targetList);
732
+ if (options.length === 0) continue;
733
+ heading.dataset.agyDecisionAttached = "true";
734
+ const box = document.createElement("div");
735
+ box.className = "agy-decision-container";
736
+ const header = document.createElement("div");
737
+ header.className = "agy-decision-header";
738
+ header.innerHTML = "<span>💬 点击选项快速填入:</span>";
739
+ box.appendChild(header);
740
+ const chips = document.createElement("div");
741
+ chips.className = "agy-decision-chips";
742
+ for (const opt of options) {
743
+ const chip = document.createElement("button");
744
+ chip.type = "button";
745
+ chip.className = "agy-decision-chip";
746
+ chip.title = `点击填入: ${opt.fullText}`;
747
+ const numSpan = document.createElement("span");
748
+ numSpan.className = "agy-decision-chip-num";
749
+ numSpan.textContent = opt.num ?? "👉";
750
+ const textSpan = document.createElement("span");
751
+ textSpan.className = "agy-decision-chip-text";
752
+ textSpan.textContent = opt.title;
753
+ chip.appendChild(numSpan);
754
+ chip.appendChild(textSpan);
755
+ chip.onclick = (e) => {
756
+ e.stopPropagation();
757
+ const cleanText = opt.fullText.replace(/^[0-9]+[、..:]\s*/, "").trim();
758
+ insertTextIntoChatComposer(`我选择 ${opt.num ? opt.num + " " : ""}${cleanText}`);
759
+ chip.classList.add("selected");
760
+ setTimeout(() => chip.classList.remove("selected"), 1200);
761
+ };
762
+ chips.appendChild(chip);
763
+ }
764
+ box.appendChild(chips);
765
+ targetList.parentNode?.insertBefore(box, targetList.nextSibling);
766
+ }
767
+ }
768
+ }
769
+ let decisionChipsDispose = null;
770
+ function installDecisionChips() {
771
+ if (typeof window === "undefined" || typeof document === "undefined") return () => {};
772
+ decisionChipsDispose?.();
773
+ injectStyles$3();
774
+ const observer = new MutationObserver(() => {
775
+ scanAndEnhanceDecisions();
776
+ });
777
+ const setup = () => {
778
+ if (document.body) {
779
+ observer.observe(document.body, {
780
+ childList: true,
781
+ subtree: true
782
+ });
783
+ scanAndEnhanceDecisions();
784
+ }
785
+ };
786
+ if (document.readyState === "loading") window.addEventListener("DOMContentLoaded", setup, { once: true });
787
+ else setup();
788
+ const dispose = () => {
789
+ observer.disconnect();
790
+ window.removeEventListener("DOMContentLoaded", setup);
791
+ if (decisionChipsDispose === dispose) decisionChipsDispose = null;
792
+ };
793
+ decisionChipsDispose = dispose;
794
+ return dispose;
795
+ }
796
+ //#endregion
797
+ //#region src/client/images.ts
798
+ let lightboxOverlay = null;
799
+ let lightboxScale = 1;
800
+ let lightboxTranslateX = 0;
801
+ let lightboxTranslateY = 0;
802
+ let isDraggingLightbox = false;
803
+ let dragStartX = 0;
804
+ let dragStartY = 0;
805
+ const IMAGE_CSS = `
806
+ .agy-image-card {
807
+ display: inline-flex;
808
+ flex-direction: column;
809
+ margin: 6px 0;
810
+ max-width: 100%;
811
+ border-radius: 8px;
812
+ overflow: hidden;
813
+ border: 1px solid var(--dsw-alias-border-l2, rgba(127, 127, 127, 0.25));
814
+ background: var(--dsw-alias-bg-l1, rgba(255, 255, 255, 0.03));
815
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
816
+ transition: transform 0.15s ease, box-shadow 0.15s ease;
817
+ vertical-align: middle;
818
+ }
819
+
820
+ .agy-image-card:hover {
821
+ border-color: var(--dsw-alias-border-l3, rgba(127, 127, 127, 0.45));
822
+ box-shadow: 0 4px 14px rgba(0, 0, 0, 0.25);
823
+ }
824
+
825
+ .agy-image-thumb-wrap {
826
+ position: relative;
827
+ overflow: hidden;
828
+ max-height: 480px;
829
+ cursor: zoom-in;
830
+ display: flex;
831
+ align-items: center;
832
+ justify-content: center;
833
+ background: repeating-conic-gradient(rgba(127,127,127,0.08) 0% 25%, transparent 0% 50%) 50% / 16px 16px;
834
+ }
835
+
836
+ .agy-image-thumb {
837
+ display: block;
838
+ max-width: 100%;
839
+ max-height: 480px;
840
+ height: auto;
841
+ object-fit: contain;
842
+ transition: transform 0.2s ease;
843
+ }
844
+
845
+ .agy-image-card:hover .agy-image-thumb {
846
+ transform: scale(1.015);
847
+ }
848
+
849
+ .agy-image-caption {
850
+ padding: 6px 10px;
851
+ font-size: 11.5px;
852
+ color: var(--dsw-alias-label-secondary, #aaa);
853
+ background: var(--dsw-alias-bg-l2, rgba(127, 127, 127, 0.08));
854
+ border-top: 1px solid var(--dsw-alias-border-l2, rgba(127, 127, 127, 0.15));
855
+ display: flex;
856
+ align-items: center;
857
+ justify-content: space-between;
858
+ gap: 8px;
859
+ overflow: hidden;
860
+ text-overflow: ellipsis;
861
+ white-space: nowrap;
862
+ }
863
+
864
+ .agy-image-link-chip {
865
+ display: inline-flex;
866
+ align-items: center;
867
+ gap: 4px;
868
+ padding: 2px 6px;
869
+ margin: 0 2px;
870
+ border-radius: 4px;
871
+ font-size: 12px;
872
+ background: var(--dsw-alias-bg-l2, rgba(127, 127, 127, 0.1));
873
+ border: 1px solid var(--dsw-alias-border-l2, rgba(127, 127, 127, 0.2));
874
+ color: var(--dsw-alias-label-primary, #ddd);
875
+ cursor: pointer;
876
+ text-decoration: none;
877
+ transition: all 0.15s ease;
878
+ }
879
+
880
+ .agy-image-link-chip:hover {
881
+ background: var(--dsw-alias-bg-hover, rgba(127, 127, 127, 0.2));
882
+ border-color: var(--dsw-alias-border-l3, rgba(127, 127, 127, 0.35));
883
+ }
884
+
885
+ /* Lightbox Modal */
886
+ .agy-lightbox-backdrop {
887
+ position: fixed;
888
+ inset: 0;
889
+ z-index: 999999;
890
+ background: rgba(0, 0, 0, 0.85);
891
+ backdrop-filter: blur(8px);
892
+ display: flex;
893
+ flex-direction: column;
894
+ user-select: none;
895
+ opacity: 0;
896
+ transition: opacity 0.2s ease;
897
+ }
898
+
899
+ .agy-lightbox-backdrop.open {
900
+ opacity: 1;
901
+ }
902
+
903
+ .agy-lightbox-header {
904
+ height: 48px;
905
+ display: flex;
906
+ align-items: center;
907
+ justify-content: space-between;
908
+ padding: 0 16px;
909
+ background: rgba(20, 20, 20, 0.65);
910
+ border-bottom: 1px solid rgba(255, 255, 255, 0.1);
911
+ color: #fff;
912
+ font-size: 13px;
913
+ }
914
+
915
+ .agy-lightbox-title {
916
+ font-weight: 500;
917
+ overflow: hidden;
918
+ text-overflow: ellipsis;
919
+ white-space: nowrap;
920
+ max-width: 60%;
921
+ }
922
+
923
+ .agy-lightbox-actions {
924
+ display: flex;
925
+ align-items: center;
926
+ gap: 8px;
927
+ }
928
+
929
+ .agy-lightbox-btn {
930
+ background: rgba(255, 255, 255, 0.1);
931
+ border: 1px solid rgba(255, 255, 255, 0.15);
932
+ color: #fff;
933
+ border-radius: 6px;
934
+ padding: 4px 10px;
935
+ cursor: pointer;
936
+ font-size: 12px;
937
+ display: inline-flex;
938
+ align-items: center;
939
+ gap: 4px;
940
+ transition: all 0.15s ease;
941
+ }
942
+
943
+ .agy-lightbox-btn:hover {
944
+ background: rgba(255, 255, 255, 0.2);
945
+ }
946
+
947
+ .agy-lightbox-btn-close {
948
+ background: rgba(239, 68, 68, 0.25);
949
+ border-color: rgba(239, 68, 68, 0.4);
950
+ }
951
+
952
+ .agy-lightbox-btn-close:hover {
953
+ background: rgba(239, 68, 68, 0.45);
954
+ }
955
+
956
+ .agy-lightbox-body {
957
+ flex: 1;
958
+ position: relative;
959
+ overflow: hidden;
960
+ display: flex;
961
+ align-items: center;
962
+ justify-content: center;
963
+ cursor: grab;
964
+ }
965
+
966
+ .agy-lightbox-body.grabbing {
967
+ cursor: grabbing;
968
+ }
969
+
970
+ .agy-lightbox-img-wrap {
971
+ transform-origin: center center;
972
+ transition: transform 0.05s ease-out;
973
+ display: inline-block;
974
+ }
975
+
976
+ .agy-lightbox-img {
977
+ max-width: 90vw;
978
+ max-height: 85vh;
979
+ object-fit: contain;
980
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5);
981
+ border-radius: 4px;
982
+ pointer-events: none;
983
+ }
984
+ `;
985
+ function injectStyles$2() {
986
+ if (typeof document === "undefined") return;
987
+ const styleId = "dsh-agy-link-images-css";
988
+ if (document.getElementById(styleId) === null) {
989
+ const st = document.createElement("style");
990
+ st.id = styleId;
991
+ st.textContent = IMAGE_CSS;
992
+ const host = document.head ?? document.documentElement;
993
+ if (host) host.appendChild(st);
994
+ }
995
+ }
996
+ function getOrigin() {
997
+ if (typeof window !== "undefined" && window.location?.origin) return window.location.origin;
998
+ return "";
999
+ }
1000
+ /** Build media URL from path */
1001
+ function buildMediaUrl(rawPath) {
1002
+ return `${getOrigin()}/plugins/agy-link/media?path=${encodeURIComponent(rawPath.trim())}`;
1003
+ }
1004
+ /**
1005
+ * Open fullscreen Lightbox modal
1006
+ */
1007
+ function openLightbox(src, title = "Image Preview", rawPath) {
1008
+ if (typeof document === "undefined") return;
1009
+ injectStyles$2();
1010
+ if (lightboxOverlay) {
1011
+ lightboxOverlay.remove();
1012
+ lightboxOverlay = null;
1013
+ }
1014
+ lightboxScale = 1;
1015
+ lightboxTranslateX = 0;
1016
+ lightboxTranslateY = 0;
1017
+ const overlay = document.createElement("div");
1018
+ overlay.className = "agy-lightbox-backdrop";
1019
+ const header = document.createElement("div");
1020
+ header.className = "agy-lightbox-header";
1021
+ const titleEl = document.createElement("div");
1022
+ titleEl.className = "agy-lightbox-title";
1023
+ titleEl.textContent = title;
1024
+ header.appendChild(titleEl);
1025
+ const actions = document.createElement("div");
1026
+ actions.className = "agy-lightbox-actions";
1027
+ const btnZoomIn = document.createElement("button");
1028
+ btnZoomIn.className = "agy-lightbox-btn";
1029
+ btnZoomIn.textContent = "🔍+";
1030
+ btnZoomIn.title = "放大 / Zoom In";
1031
+ const btnZoomOut = document.createElement("button");
1032
+ btnZoomOut.className = "agy-lightbox-btn";
1033
+ btnZoomOut.textContent = "🔍-";
1034
+ btnZoomOut.title = "缩小 / Zoom Out";
1035
+ const btnReset = document.createElement("button");
1036
+ btnReset.className = "agy-lightbox-btn";
1037
+ btnReset.textContent = "1:1";
1038
+ btnReset.title = "实际大小 / Reset";
1039
+ actions.appendChild(btnZoomIn);
1040
+ actions.appendChild(btnZoomOut);
1041
+ actions.appendChild(btnReset);
1042
+ if (rawPath) {
1043
+ const btnCopyPath = document.createElement("button");
1044
+ btnCopyPath.className = "agy-lightbox-btn";
1045
+ btnCopyPath.textContent = "📋 复制路径";
1046
+ btnCopyPath.onclick = (e) => {
1047
+ e.stopPropagation();
1048
+ if (navigator.clipboard) navigator.clipboard.writeText(rawPath).then(() => {
1049
+ btnCopyPath.textContent = "✓ 已复制";
1050
+ setTimeout(() => {
1051
+ btnCopyPath.textContent = "📋 复制路径";
1052
+ }, 1500);
1053
+ }).catch(() => {});
1054
+ };
1055
+ actions.appendChild(btnCopyPath);
1056
+ }
1057
+ const btnClose = document.createElement("button");
1058
+ btnClose.className = "agy-lightbox-btn agy-lightbox-btn-close";
1059
+ btnClose.textContent = "✕ 关闭";
1060
+ actions.appendChild(btnClose);
1061
+ header.appendChild(actions);
1062
+ overlay.appendChild(header);
1063
+ const body = document.createElement("div");
1064
+ body.className = "agy-lightbox-body";
1065
+ const imgWrap = document.createElement("div");
1066
+ imgWrap.className = "agy-lightbox-img-wrap";
1067
+ const img = document.createElement("img");
1068
+ img.className = "agy-lightbox-img";
1069
+ img.src = src;
1070
+ img.alt = title;
1071
+ imgWrap.appendChild(img);
1072
+ body.appendChild(imgWrap);
1073
+ overlay.appendChild(body);
1074
+ const updateTransform = () => {
1075
+ imgWrap.style.transform = `translate(${lightboxTranslateX}px, ${lightboxTranslateY}px) scale(${lightboxScale})`;
1076
+ };
1077
+ btnZoomIn.onclick = (e) => {
1078
+ e.stopPropagation();
1079
+ lightboxScale = Math.min(lightboxScale * 1.3, 5);
1080
+ updateTransform();
1081
+ };
1082
+ btnZoomOut.onclick = (e) => {
1083
+ e.stopPropagation();
1084
+ lightboxScale = Math.max(lightboxScale / 1.3, .2);
1085
+ updateTransform();
1086
+ };
1087
+ btnReset.onclick = (e) => {
1088
+ e.stopPropagation();
1089
+ lightboxScale = 1;
1090
+ lightboxTranslateX = 0;
1091
+ lightboxTranslateY = 0;
1092
+ updateTransform();
1093
+ };
1094
+ const closeLightbox = () => {
1095
+ window.removeEventListener("keydown", onKeyDown);
1096
+ overlay.classList.remove("open");
1097
+ setTimeout(() => {
1098
+ overlay.remove();
1099
+ if (lightboxOverlay === overlay) lightboxOverlay = null;
1100
+ }, 200);
1101
+ };
1102
+ btnClose.onclick = (e) => {
1103
+ e.stopPropagation();
1104
+ closeLightbox();
1105
+ };
1106
+ overlay.onclick = (e) => {
1107
+ if (e.target === overlay || e.target === body) closeLightbox();
1108
+ };
1109
+ const onKeyDown = (e) => {
1110
+ if (e.key === "Escape") closeLightbox();
1111
+ };
1112
+ window.addEventListener("keydown", onKeyDown);
1113
+ body.onmousedown = (e) => {
1114
+ isDraggingLightbox = true;
1115
+ dragStartX = e.clientX - lightboxTranslateX;
1116
+ dragStartY = e.clientY - lightboxTranslateY;
1117
+ body.classList.add("grabbing");
1118
+ };
1119
+ const onMouseMove = (e) => {
1120
+ if (!isDraggingLightbox) return;
1121
+ lightboxTranslateX = e.clientX - dragStartX;
1122
+ lightboxTranslateY = e.clientY - dragStartY;
1123
+ updateTransform();
1124
+ };
1125
+ const onMouseUp = () => {
1126
+ if (isDraggingLightbox) {
1127
+ isDraggingLightbox = false;
1128
+ body.classList.remove("grabbing");
1129
+ }
1130
+ };
1131
+ window.addEventListener("mousemove", onMouseMove);
1132
+ window.addEventListener("mouseup", onMouseUp);
1133
+ body.onwheel = (e) => {
1134
+ e.preventDefault();
1135
+ const factor = e.deltaY < 0 ? 1.15 : .87;
1136
+ lightboxScale = Math.max(.2, Math.min(lightboxScale * factor, 5));
1137
+ updateTransform();
1138
+ };
1139
+ document.body.appendChild(overlay);
1140
+ lightboxOverlay = overlay;
1141
+ requestAnimationFrame(() => overlay.classList.add("open"));
1142
+ }
1143
+ /** Check if a URL or path is an image */
1144
+ function isImageFile(pathOrUrl) {
1145
+ if (!pathOrUrl || typeof pathOrUrl !== "string") return false;
1146
+ const clean = pathOrUrl.split(/[?#]/)[0] ?? "";
1147
+ return /\.(png|jpe?g|webp|gif|svg|bmp|ico|avif)$/i.test(clean);
1148
+ }
1149
+ /** Get React props of a DOM element */
1150
+ function getReactProps(el) {
1151
+ const key = Object.keys(el).find((k) => k.startsWith("__reactProps"));
1152
+ if (!key) return null;
1153
+ return el[key];
1154
+ }
1155
+ /**
1156
+ * Scan DOM for imageAlt spans and enhance them with live rendered images
1157
+ */
1158
+ function scanAndEnhanceImages() {
1159
+ if (typeof document === "undefined") return;
1160
+ const origin = getOrigin();
1161
+ if (origin && !origin.includes("127.0.0.1") && !origin.includes("localhost")) {
1162
+ const images = document.querySelectorAll("img[src*=\"/plugins/agy-link/media\"]");
1163
+ for (let i = 0; i < images.length; i++) {
1164
+ const img = images[i];
1165
+ if (img && img.src.includes("127.0.0.1")) {
1166
+ const u = new URL(img.src);
1167
+ img.src = `${origin}${u.pathname}${u.search}`;
1168
+ }
1169
+ }
1170
+ }
1171
+ const markdownContainers = document.querySelectorAll("div[class*=\"_markdown_\"], [data-markdown-variant]");
1172
+ for (let i = 0; i < markdownContainers.length; i++) {
1173
+ const container = markdownContainers[i];
1174
+ if (!container) continue;
1175
+ const altSpans = container.querySelectorAll("span[class*=\"imageAlt\"], span[class*=\"_imageAlt_\"]");
1176
+ if (altSpans.length === 0) continue;
1177
+ const props = getReactProps(container);
1178
+ const mdText = typeof props?.text === "string" ? props.text : "";
1179
+ if (!mdText) continue;
1180
+ const imgMatches = [];
1181
+ const re = /!\[(.*?)\]\((file:\/\/\/[^\s)]+|file:\/\/[^\s)]+|[A-Za-z]:[\\/][^\s)]+|\/home\/[^\s)]+|\/mnt\/[a-zA-Z]\/[^\s)]+|\\\\wsl\.localhost\\[^\s)]+)\)/g;
1182
+ let m;
1183
+ while ((m = re.exec(mdText)) !== null) imgMatches.push({
1184
+ alt: m[1] ?? "",
1185
+ target: m[2] ?? ""
1186
+ });
1187
+ if (imgMatches.length === 0) continue;
1188
+ for (let j = 0; j < altSpans.length; j++) {
1189
+ const span = altSpans[j];
1190
+ if (!span || span.dataset.agyEnhanced === "true") continue;
1191
+ const altText = (span.textContent ?? "").trim();
1192
+ const match = imgMatches.find((im) => im.alt.trim() === altText) ?? imgMatches[j];
1193
+ if (!match) continue;
1194
+ span.dataset.agyEnhanced = "true";
1195
+ const mediaUrl = buildMediaUrl(match.target);
1196
+ const card = document.createElement("div");
1197
+ card.className = "agy-image-card";
1198
+ const thumbWrap = document.createElement("div");
1199
+ thumbWrap.className = "agy-image-thumb-wrap";
1200
+ thumbWrap.title = `${altText || "Image"} (点击全屏放大)`;
1201
+ const img = document.createElement("img");
1202
+ img.className = "agy-image-thumb";
1203
+ img.src = mediaUrl;
1204
+ img.alt = altText || "Preview";
1205
+ img.loading = "lazy";
1206
+ img.onerror = () => {
1207
+ card.style.display = "none";
1208
+ span.style.display = "";
1209
+ };
1210
+ thumbWrap.appendChild(img);
1211
+ card.appendChild(thumbWrap);
1212
+ if (altText) {
1213
+ const caption = document.createElement("div");
1214
+ caption.className = "agy-image-caption";
1215
+ caption.textContent = altText;
1216
+ card.appendChild(caption);
1217
+ }
1218
+ thumbWrap.onclick = (e) => {
1219
+ e.stopPropagation();
1220
+ openLightbox(mediaUrl, altText || "Image Preview", match.target);
1221
+ };
1222
+ span.style.display = "none";
1223
+ span.parentNode?.insertBefore(card, span);
1224
+ }
1225
+ }
1226
+ const links = document.querySelectorAll("a[href^=\"file://\"]");
1227
+ for (let i = 0; i < links.length; i++) {
1228
+ const link = links[i];
1229
+ if (!link || link.dataset.agyLinkEnhanced === "true") continue;
1230
+ const href = link.href ?? "";
1231
+ if (!isImageFile(href)) continue;
1232
+ link.dataset.agyLinkEnhanced = "true";
1233
+ const mediaUrl = buildMediaUrl(href);
1234
+ const linkText = link.textContent ?? "Image";
1235
+ link.onclick = (e) => {
1236
+ e.preventDefault();
1237
+ e.stopPropagation();
1238
+ openLightbox(mediaUrl, linkText, href);
1239
+ };
1240
+ if (!link.querySelector(".agy-image-link-icon")) {
1241
+ const badge = document.createElement("span");
1242
+ badge.className = "agy-image-link-chip agy-image-link-icon";
1243
+ badge.textContent = "🖼️ 预览";
1244
+ link.appendChild(badge);
1245
+ }
1246
+ }
1247
+ }
1248
+ let imageEnhancerDispose = null;
1249
+ function installImageEnhancer() {
1250
+ if (typeof window === "undefined" || typeof document === "undefined") return () => {};
1251
+ imageEnhancerDispose?.();
1252
+ injectStyles$2();
1253
+ const observer = new MutationObserver(() => {
1254
+ scanAndEnhanceImages();
1255
+ });
1256
+ const setup = () => {
1257
+ if (document.body) {
1258
+ observer.observe(document.body, {
1259
+ childList: true,
1260
+ subtree: true
1261
+ });
1262
+ scanAndEnhanceImages();
1263
+ }
1264
+ };
1265
+ if (document.readyState === "loading") window.addEventListener("DOMContentLoaded", setup, { once: true });
1266
+ else setup();
1267
+ const dispose = () => {
1268
+ observer.disconnect();
1269
+ window.removeEventListener("DOMContentLoaded", setup);
1270
+ if (imageEnhancerDispose === dispose) imageEnhancerDispose = null;
1271
+ };
1272
+ imageEnhancerDispose = dispose;
1273
+ return dispose;
1274
+ }
1275
+ //#endregion
415
1276
  //#region src/client/toolview.ts
416
1277
  function react() {
417
1278
  const mod = require("react");
@@ -604,6 +1465,40 @@ window.__ModuleLoader__.load({
604
1465
  fill: "currentColor"
605
1466
  }));
606
1467
  }
1468
+ function IconQuestionOutline16(size = 14, className = "") {
1469
+ return hx("svg", {
1470
+ width: size,
1471
+ height: size,
1472
+ className: className || void 0,
1473
+ viewBox: "0 0 16 16",
1474
+ fill: "none",
1475
+ style: {
1476
+ display: "block",
1477
+ flexShrink: 0
1478
+ },
1479
+ "aria-hidden": true
1480
+ }, hx("path", {
1481
+ d: "M8 1C4.134 1 1 3.91 1 7.5c0 1.547.575 2.973 1.559 4.093L1.13 14.54a.5.5 0 00.627.63l3.238-1.079C5.875 14.316 6.91 14.5 8 14.5c3.866 0 7-2.91 7-6.5S11.866 1 8 1zm0 10.5a.75.75 0 110-1.5.75.75 0 010 1.5zm1.09-3.41c-.42.34-.59.54-.59.91H7.5v-.5c0-.66.36-1.12.82-1.48.44-.35.68-.61.68-1.02 0-.55-.45-1-1-1s-1 .45-1 1H6c0-1.1.9-2 2-2s2 .9 2 2c0 .73-.39 1.25-.91 1.67z",
1482
+ fill: "currentColor"
1483
+ }));
1484
+ }
1485
+ function IconImageOutline16(size = 14, className = "") {
1486
+ return hx("svg", {
1487
+ width: size,
1488
+ height: size,
1489
+ className: className || void 0,
1490
+ viewBox: "0 0 16 16",
1491
+ fill: "none",
1492
+ style: {
1493
+ display: "block",
1494
+ flexShrink: 0
1495
+ },
1496
+ "aria-hidden": true
1497
+ }, hx("path", {
1498
+ d: "M14 3H2a1 1 0 00-1 1v8a1 1 0 001 1h12a1 1 0 001-1V4a1 1 0 00-1-1zm-1 8H3l2.5-3.2 1.8 2.3 2.7-3.5L13 11zM5.5 6a1.5 1.5 0 110-3 1.5 1.5 0 010 3z",
1499
+ fill: "currentColor"
1500
+ }));
1501
+ }
607
1502
  function toolIconFor(kind, size = 14) {
608
1503
  switch (kind) {
609
1504
  case "terminal": return IconApiOutline14(size);
@@ -612,6 +1507,8 @@ window.__ModuleLoader__.load({
612
1507
  case "read": return IconBrowseOutline16(size);
613
1508
  case "search":
614
1509
  case "list": return IconSearchOutline16(size);
1510
+ case "question": return IconQuestionOutline16(size);
1511
+ case "image": return IconImageOutline16(size);
615
1512
  default: return IconCodeOutline16(size);
616
1513
  }
617
1514
  }
@@ -705,7 +1602,9 @@ window.__ModuleLoader__.load({
705
1602
  "grep_search",
706
1603
  "search",
707
1604
  "search_file_content",
708
- "grep"
1605
+ "grep",
1606
+ "search_web",
1607
+ "web_search"
709
1608
  ]);
710
1609
  const LIST_TOOLS = /* @__PURE__ */ new Set(["list_dir", "ls"]);
711
1610
  const DELETE_TOOLS = /* @__PURE__ */ new Set([
@@ -713,6 +1612,16 @@ window.__ModuleLoader__.load({
713
1612
  "remove_file",
714
1613
  "rm"
715
1614
  ]);
1615
+ const QUESTION_TOOLS = /* @__PURE__ */ new Set([
1616
+ "ask_question",
1617
+ "ask",
1618
+ "question"
1619
+ ]);
1620
+ const IMAGE_TOOLS = /* @__PURE__ */ new Set([
1621
+ "generate_image",
1622
+ "create_image",
1623
+ "render_image"
1624
+ ]);
716
1625
  function mirrorCardModel(block, cwd, home) {
717
1626
  const info = mirrorInfo(block);
718
1627
  const out = resultText(block);
@@ -724,6 +1633,33 @@ window.__ModuleLoader__.load({
724
1633
  };
725
1634
  const { tool, input } = info;
726
1635
  const desc = pick(input, "Description", "description", "toolAction", "toolSummary", "Instruction", "instruction");
1636
+ if (QUESTION_TOOLS.has(tool)) {
1637
+ const questionsRaw = input.questions ?? input.Questions;
1638
+ const questions = Array.isArray(questionsRaw) ? questionsRaw : [];
1639
+ return {
1640
+ kind: "question",
1641
+ tool,
1642
+ title: questions[0]?.question ?? desc ?? "Ask Question",
1643
+ output: out,
1644
+ questions,
1645
+ raw: parsedArgsRaw(block)
1646
+ };
1647
+ }
1648
+ if (IMAGE_TOOLS.has(tool)) {
1649
+ const prompt = pick(input, "Prompt", "prompt") ?? "";
1650
+ const name = pick(input, "ImageName", "image_name", "name") ?? "image";
1651
+ const ratio = pick(input, "AspectRatio", "aspect_ratio") ?? "1:1";
1652
+ return {
1653
+ kind: "image",
1654
+ tool,
1655
+ title: desc ?? `Generate Image · ${name}`,
1656
+ prompt,
1657
+ imageName: name,
1658
+ aspectRatio: ratio,
1659
+ output: out,
1660
+ raw: parsedArgsRaw(block)
1661
+ };
1662
+ }
727
1663
  if (TERMINAL_TOOLS.has(tool)) {
728
1664
  const command = pick(input, "CommandLine", "command_line", "command", "cmd", "Cmd") ?? JSON.stringify(input);
729
1665
  const cwdVal = pick(input, "Cwd", "cwd", "WorkingDirectory", "working_directory") ?? cwd;
@@ -757,12 +1693,17 @@ window.__ModuleLoader__.load({
757
1693
  const path = pick(input, "AbsolutePath", "absolute_path", "TargetFile", "target_file", "path", "file_path", "filename", "Path", "FilePath", "FileName", "targetFile") ?? "";
758
1694
  const offset = num(input, "offset", "Offset") ?? num(input, "StartLine", "start_line");
759
1695
  const line = offset !== void 0 ? offset > 0 && (tool === "view_file" || tool === "read_file") ? offset : offset + 1 : void 0;
1696
+ const isImg = isImageFile(path);
760
1697
  return {
761
- kind: "read",
1698
+ kind: isImg ? "image" : "read",
762
1699
  tool,
763
1700
  title: desc ? `${desc} · ${path}` : path !== "" ? "Read " + path : "Read",
764
1701
  path,
765
1702
  output: out,
1703
+ isImage: isImg,
1704
+ imagePath: isImg ? path : void 0,
1705
+ prompt: desc,
1706
+ imageName: path.split(/[/\\]/).pop() || "image",
766
1707
  ...path !== "" ? { location: {
767
1708
  path,
768
1709
  ...line !== void 0 ? { line } : {}
@@ -833,15 +1774,32 @@ window.__ModuleLoader__.load({
833
1774
  function cls(...names) {
834
1775
  return names.filter((n) => typeof n === "string" && n !== "").join(" ");
835
1776
  }
836
- function displayTitle(model) {
1777
+ /**
1778
+ * Locale lookup with an English fallback: the mirror card renders through
1779
+ * host-provided `t` when the registration declares the namespace, but tests
1780
+ * and hosts without the seat call it bare. The old literals were mixed
1781
+ * 中文 and English as BOTH fallback and primary, so non-Chinese users saw
1782
+ * Chinese buttons on mirror cards.
1783
+ */
1784
+ function tr(t, key, fallback, params) {
1785
+ if (typeof t === "function") try {
1786
+ const v = t(key, params);
1787
+ if (typeof v === "string" && v !== "" && v !== key) return v;
1788
+ } catch {}
1789
+ if (params !== void 0 && params.count !== void 0 && fallback.includes("{count}")) return fallback.replace("{count}", String(params.count));
1790
+ return fallback;
1791
+ }
1792
+ function displayTitle(model, t) {
837
1793
  switch (model.kind) {
838
- case "terminal": return "Command";
839
- case "diff": return model.tool === "write_to_file" || model.tool === "write_file" || model.tool === "create_file" ? "Write" : "Edit";
840
- case "read": return "Read";
841
- case "search": return "Search";
842
- case "list": return "List";
843
- case "delete": return "Delete";
844
- default: return model.tool ? model.tool.replace(/_/g, " ") : "Tool";
1794
+ case "terminal": return tr(t, "tv.command", "Command");
1795
+ case "diff": return model.tool === "write_to_file" || model.tool === "write_file" || model.tool === "create_file" ? tr(t, "tv.write", "Write") : tr(t, "tv.edit", "Edit");
1796
+ case "read": return tr(t, "tv.read", "Read");
1797
+ case "search": return model.tool === "search_web" || model.tool === "web_search" ? tr(t, "tv.webSearch", "Web Search") : tr(t, "tv.search", "Search");
1798
+ case "list": return tr(t, "tv.list", "List");
1799
+ case "delete": return tr(t, "tv.delete", "Delete");
1800
+ case "question": return tr(t, "tv.question", "Question");
1801
+ case "image": return tr(t, "tv.image", "Image");
1802
+ default: return model.tool ? model.tool.replace(/_/g, " ") : tr(t, "tv.tool", "Tool");
845
1803
  }
846
1804
  }
847
1805
  function displaySummary(model, cwd, home) {
@@ -851,8 +1809,10 @@ window.__ModuleLoader__.load({
851
1809
  const rawPath = model.path ?? "";
852
1810
  return rawPath !== "" ? relativize(rawPath, cwd, home) : model.title;
853
1811
  }
854
- if (model.kind === "search") return model.title.replace(/^Search\s*/i, "") || model.title;
1812
+ if (model.kind === "search") return model.title.replace(/^(Web search|Search)\s*/i, "") || model.title;
855
1813
  if (model.kind === "list") return model.title.replace(/^List\s*(directory\s*)?/i, "") || model.title;
1814
+ if (model.kind === "question") return model.title;
1815
+ if (model.kind === "image") return model.prompt || model.title;
856
1816
  return model.title;
857
1817
  }
858
1818
  function diffStat(model) {
@@ -865,16 +1825,18 @@ window.__ModuleLoader__.load({
865
1825
  if (oldLines === 0) return `+${newLines}`;
866
1826
  return `+${newLines} -${oldLines}`;
867
1827
  }
868
- function CopyButton({ text, labels }) {
1828
+ function CopyButton({ text, labels, t }) {
869
1829
  const [copied, setCopied] = react().useState(false);
870
- const copyLabel = labels?.[0] ?? "复制";
871
- const copiedLabel = labels?.[1] ?? "已复制";
1830
+ const copyLabel = labels?.[0] ?? tr(t, "code.copy", "Copy");
1831
+ const copiedLabel = labels?.[1] ?? tr(t, "code.copied", "Copied");
872
1832
  const onCopy = (e) => {
873
1833
  e.stopPropagation();
874
1834
  if (typeof navigator !== "undefined" && navigator.clipboard) navigator.clipboard.writeText(text).then(() => {
875
1835
  setCopied(true);
876
1836
  setTimeout(() => setCopied(false), 1500);
877
- }).catch(() => {});
1837
+ }).catch(() => {
1838
+ setCopied(false);
1839
+ });
878
1840
  };
879
1841
  return hx("button", {
880
1842
  type: "button",
@@ -883,39 +1845,146 @@ window.__ModuleLoader__.load({
883
1845
  title: copied ? copiedLabel : copyLabel
884
1846
  }, copied ? IconCheckOutline14(11) : IconCopyOutline16(11), hx("span", null, copied ? copiedLabel : copyLabel));
885
1847
  }
886
- function DiffBody({ model }) {
1848
+ /** Per-side render cap: a 5000-line write_to_file used to build 10k DOM rows
1849
+ * inside a max-height:280px pane (99% of them invisible). */
1850
+ const DIFF_MAX_LINES = 400;
1851
+ function DiffBody({ model, t }) {
887
1852
  const h = hx;
888
1853
  const oldText = model.oldText ?? "";
889
1854
  const newText = model.newText ?? "";
890
1855
  const rows = [];
1856
+ const moreRow = (count, key) => h("div", {
1857
+ key,
1858
+ className: "agy-tv-diff-line",
1859
+ style: { color: "var(--dsw-alias-label-tertiary)" }
1860
+ }, tr(t, "tv.diffMore", "… {count} more lines not rendered", { count }));
891
1861
  if (oldText !== "") {
892
1862
  const lines = oldText.split("\n");
893
- for (let i = 0; i < lines.length; i++) {
1863
+ const shown = Math.min(lines.length, DIFF_MAX_LINES);
1864
+ for (let i = 0; i < shown; i++) {
894
1865
  const line = lines[i];
895
1866
  rows.push(h("div", {
896
1867
  key: "del-" + i,
897
1868
  className: "agy-tv-diff-line agy-tv-diff-del"
898
1869
  }, h("span", { className: "agy-tv-diff-marker" }, "-"), h("span", { style: { flex: 1 } }, line === "" ? " " : line)));
899
1870
  }
1871
+ if (lines.length > shown) rows.push(moreRow(lines.length - shown, "del-more"));
900
1872
  }
901
1873
  if (newText !== "") {
902
1874
  const lines = newText.split("\n");
903
- for (let i = 0; i < lines.length; i++) {
1875
+ const shown = Math.min(lines.length, DIFF_MAX_LINES);
1876
+ for (let i = 0; i < shown; i++) {
904
1877
  const line = lines[i];
905
1878
  rows.push(h("div", {
906
1879
  key: "add-" + i,
907
1880
  className: "agy-tv-diff-line agy-tv-diff-add"
908
1881
  }, h("span", { className: "agy-tv-diff-marker" }, "+"), h("span", { style: { flex: 1 } }, line === "" ? " " : line)));
909
1882
  }
1883
+ if (lines.length > shown) rows.push(moreRow(lines.length - shown, "add-more"));
910
1884
  }
911
1885
  if (rows.length === 0) rows.push(h("div", {
912
1886
  key: "empty",
913
1887
  className: "agy-tv-diff-line",
914
1888
  style: { color: "var(--dsw-alias-label-tertiary)" }
915
- }, "(no textual changes)"));
1889
+ }, tr(t, "tv.noChanges", "(no textual changes)")));
916
1890
  return h("div", { className: "agy-tv-diff-list" }, ...rows);
917
1891
  }
918
- function CardBody({ model, openFile, cwd, home, inspect }) {
1892
+ function QuestionCardBody({ model }) {
1893
+ const h = hx;
1894
+ const questions = model.questions ?? [];
1895
+ const [selectedMap, setSelectedMap] = react().useState({});
1896
+ const [inserted, setInserted] = react().useState(false);
1897
+ const toggleOption = (qIdx, opt, isMulti) => {
1898
+ setSelectedMap((prev) => {
1899
+ const next = { ...prev };
1900
+ const current = new Set(next[qIdx] ?? []);
1901
+ if (isMulti) {
1902
+ if (current.has(opt)) current.delete(opt);
1903
+ else current.add(opt);
1904
+ } else {
1905
+ current.clear();
1906
+ current.add(opt);
1907
+ }
1908
+ next[qIdx] = current;
1909
+ return next;
1910
+ });
1911
+ };
1912
+ const onInsert = () => {
1913
+ const parts = [];
1914
+ for (let i = 0; i < questions.length; i++) {
1915
+ const sel = Array.from(selectedMap[i] ?? []);
1916
+ if (sel.length > 0) parts.push(sel.join("; "));
1917
+ }
1918
+ if (parts.length > 0) {
1919
+ insertTextIntoChatComposer(parts.join("\n"));
1920
+ setInserted(true);
1921
+ setTimeout(() => setInserted(false), 1500);
1922
+ }
1923
+ };
1924
+ const qNodes = [];
1925
+ for (let i = 0; i < questions.length; i++) {
1926
+ const q = questions[i];
1927
+ if (!q) continue;
1928
+ const options = q.options ?? [];
1929
+ const isMulti = q.is_multi_select === true;
1930
+ const sel = selectedMap[i] ?? /* @__PURE__ */ new Set();
1931
+ const optNodes = options.map((opt, oIdx) => {
1932
+ const isChecked = sel.has(opt);
1933
+ return h("button", {
1934
+ key: "opt-" + oIdx,
1935
+ type: "button",
1936
+ className: cls("agy-tv-q-opt", isChecked && "selected"),
1937
+ onClick: (e) => {
1938
+ e.stopPropagation();
1939
+ toggleOption(i, opt, isMulti);
1940
+ }
1941
+ }, h("span", { className: cls("agy-tv-q-check", isMulti ? "checkbox" : "radio", isChecked && "checked") }), h("span", { className: "agy-tv-q-opt-text" }, opt));
1942
+ });
1943
+ qNodes.push(h("div", {
1944
+ key: "q-" + i,
1945
+ className: "agy-tv-q-item"
1946
+ }, h("div", { className: "agy-tv-q-prompt" }, q.question || "Please select:"), h("div", { className: "agy-tv-q-opts" }, ...optNodes)));
1947
+ }
1948
+ const hasSelection = Object.values(selectedMap).some((s) => s.size > 0);
1949
+ return h("div", { className: "agy-tv-question-wrap" }, ...qNodes, h("div", { className: "agy-tv-q-footer" }, h("button", {
1950
+ type: "button",
1951
+ className: cls("agy-tv-q-insert-btn", hasSelection && "active"),
1952
+ disabled: !hasSelection,
1953
+ onClick: (e) => {
1954
+ e.stopPropagation();
1955
+ onInsert();
1956
+ }
1957
+ }, inserted ? "✓ 已填入对话框" : "💬 填入对话框 / Insert into Chat"), model.output ? h("div", { className: "agy-tv-q-answered" }, `已记录: ${model.output}`) : null));
1958
+ }
1959
+ function ImageCardBody({ model }) {
1960
+ const h = hx;
1961
+ const prompt = model.prompt || "";
1962
+ const name = model.imageName || "image";
1963
+ const ratio = model.aspectRatio || "1:1";
1964
+ const path = model.imagePath || model.path || "";
1965
+ const mediaUrl = path ? buildMediaUrl(path) : null;
1966
+ return h("div", { className: "agy-tv-img-wrap" }, h("div", { className: "agy-tv-img-meta" }, h("div", {
1967
+ className: "agy-tv-img-prompt",
1968
+ title: prompt || name
1969
+ }, prompt ? `"${prompt}"` : name), h("div", { className: "agy-tv-img-badges" }, h("span", { className: "agy-tv-img-badge" }, ratio), ...path ? [h("span", {
1970
+ key: "path",
1971
+ className: "agy-tv-img-badge",
1972
+ title: path
1973
+ }, path.split(/[/\\]/).pop())] : [])), mediaUrl ? h("div", {
1974
+ className: "agy-tv-img-preview-box",
1975
+ onClick: (e) => {
1976
+ e.stopPropagation();
1977
+ openLightbox(mediaUrl, prompt || name, path);
1978
+ },
1979
+ title: "点击放大预览 / Click to open lightbox"
1980
+ }, h("img", {
1981
+ className: "agy-tv-img-thumb",
1982
+ src: mediaUrl,
1983
+ alt: prompt || name,
1984
+ loading: "lazy"
1985
+ })) : null);
1986
+ }
1987
+ function CardBody({ model, openFile, cwd, home, inspect, t }) {
919
1988
  const h = hx;
920
1989
  const inspectNode = inspect !== void 0 ? h("div", {
921
1990
  key: "ins",
@@ -924,10 +1993,10 @@ window.__ModuleLoader__.load({
924
1993
  type: "button",
925
1994
  className: "agy-tv-inspect-btn",
926
1995
  onClick: inspect
927
- }, IconInspectOutline12(12), "检查")) : null;
1996
+ }, IconInspectOutline12(12), tr(t, "code.inspect", "Inspect"))) : null;
928
1997
  switch (model.kind) {
929
1998
  case "terminal": {
930
- const rawBody = model.output !== void 0 && model.output !== "" ? trimTrailingBlankLines(model.output) : "(no output)";
1999
+ const rawBody = model.output !== void 0 && model.output !== "" ? trimTrailingBlankLines(model.output) : tr(t, "tv.noOutput", "(no output)");
931
2000
  const copyText = model.output !== void 0 && model.output !== "" ? model.output : model.command ?? "";
932
2001
  return h("div", { className: "agy-tv-card" }, h("div", { className: "agy-tv-card-header" }, h("div", { className: "agy-tv-card-header-left" }, h("span", { className: "agy-tv-prompt-glyph" }, "❯"), ...model.cwd ? [h("span", {
933
2002
  key: "cwd",
@@ -935,7 +2004,10 @@ window.__ModuleLoader__.load({
935
2004
  }, model.cwd)] : [], h("span", {
936
2005
  className: "agy-tv-card-cmd",
937
2006
  title: model.command
938
- }, model.command ?? "")), h(CopyButton, { text: copyText })), h("pre", { className: "agy-tv-card-content" }, clip(rawBody, 16e3)), inspectNode);
2007
+ }, model.command ?? "")), h(CopyButton, {
2008
+ text: copyText,
2009
+ t
2010
+ })), h("pre", { className: "agy-tv-card-content" }, clip(rawBody, 16e3)), inspectNode);
939
2011
  }
940
2012
  case "diff": {
941
2013
  const pathLabel = relativize(model.path ?? "file", cwd, home);
@@ -948,38 +2020,53 @@ window.__ModuleLoader__.load({
948
2020
  model,
949
2021
  openFile,
950
2022
  cwd,
951
- home
2023
+ home,
2024
+ t
952
2025
  }), inspectNode);
953
2026
  }
954
2027
  case "read": {
955
2028
  const pathLabel = relativize(model.path ?? "file", cwd, home);
956
- const rawBody = model.output !== void 0 && model.output !== "" ? trimTrailingBlankLines(model.output) : "(no output)";
957
- return h("div", { className: "agy-tv-card" }, h("div", { className: "agy-tv-card-header" }, h("div", { className: "agy-tv-card-header-left" }, IconBrowseOutline16(13), h("span", { className: "agy-tv-card-path" }, pathLabel)), h(CopyButton, { text: model.output ?? "" })), h("pre", { className: "agy-tv-card-content" }, clip(rawBody, 16e3)), inspectNode);
2029
+ const rawBody = model.output !== void 0 && model.output !== "" ? trimTrailingBlankLines(model.output) : tr(t, "tv.noOutput", "(no output)");
2030
+ return h("div", { className: "agy-tv-card" }, h("div", { className: "agy-tv-card-header" }, h("div", { className: "agy-tv-card-header-left" }, IconBrowseOutline16(13), h("span", { className: "agy-tv-card-path" }, pathLabel)), h(CopyButton, {
2031
+ text: model.output ?? "",
2032
+ t
2033
+ })), h("pre", { className: "agy-tv-card-content" }, clip(rawBody, 16e3)), inspectNode);
958
2034
  }
959
2035
  case "search": {
960
- const rawBody = model.output !== void 0 && model.output !== "" ? trimTrailingBlankLines(model.output) : "(no matches)";
961
- return h("div", { className: "agy-tv-card" }, h("div", { className: "agy-tv-card-header" }, h("div", { className: "agy-tv-card-header-left" }, IconSearchOutline16(13), h("span", { className: "agy-tv-card-path" }, model.title)), h(CopyButton, { text: model.output ?? "" })), h("pre", { className: "agy-tv-card-content" }, clip(rawBody, 16e3)), inspectNode);
2036
+ const rawBody = model.output !== void 0 && model.output !== "" ? trimTrailingBlankLines(model.output) : tr(t, "tv.noMatches", "(no matches)");
2037
+ return h("div", { className: "agy-tv-card" }, h("div", { className: "agy-tv-card-header" }, h("div", { className: "agy-tv-card-header-left" }, IconSearchOutline16(13), h("span", { className: "agy-tv-card-path" }, model.title)), h(CopyButton, {
2038
+ text: model.output ?? "",
2039
+ t
2040
+ })), h("pre", { className: "agy-tv-card-content" }, clip(rawBody, 16e3)), inspectNode);
962
2041
  }
963
2042
  case "list": {
964
- const rawBody = model.output !== void 0 && model.output !== "" ? trimTrailingBlankLines(model.output) : "(empty directory)";
965
- return h("div", { className: "agy-tv-card" }, h("div", { className: "agy-tv-card-header" }, h("div", { className: "agy-tv-card-header-left" }, IconBrowseOutline16(13), h("span", { className: "agy-tv-card-path" }, model.title)), h(CopyButton, { text: model.output ?? "" })), h("pre", { className: "agy-tv-card-content" }, clip(rawBody, 16e3)), inspectNode);
2043
+ const rawBody = model.output !== void 0 && model.output !== "" ? trimTrailingBlankLines(model.output) : tr(t, "tv.emptyDir", "(empty directory)");
2044
+ return h("div", { className: "agy-tv-card" }, h("div", { className: "agy-tv-card-header" }, h("div", { className: "agy-tv-card-header-left" }, IconBrowseOutline16(13), h("span", { className: "agy-tv-card-path" }, model.title)), h(CopyButton, {
2045
+ text: model.output ?? "",
2046
+ t
2047
+ })), h("pre", { className: "agy-tv-card-content" }, clip(rawBody, 16e3)), inspectNode);
966
2048
  }
2049
+ case "question": return h("div", { className: "agy-tv-card" }, h("div", { className: "agy-tv-card-header" }, h("div", { className: "agy-tv-card-header-left" }, IconQuestionOutline16(13), h("span", { className: "agy-tv-card-path" }, model.title)), h(CopyButton, { text: model.output ?? model.raw ?? "" })), h(QuestionCardBody, { model }), inspectNode);
2050
+ case "image": return h("div", { className: "agy-tv-card" }, h("div", { className: "agy-tv-card-header" }, h("div", { className: "agy-tv-card-header-left" }, IconImageOutline16(13), h("span", { className: "agy-tv-card-path" }, model.imageName || "Image")), h(CopyButton, { text: model.prompt || (model.raw ?? "") })), h(ImageCardBody, { model }), inspectNode);
967
2051
  default: {
968
2052
  const copyText = model.output !== void 0 && model.output !== "" ? model.output : model.raw ?? "";
969
- const rawBody = model.output !== void 0 && model.output !== "" ? trimTrailingBlankLines(model.output) : model.raw !== void 0 && model.raw !== "" ? clip(model.raw, 4e3) : "(no detail)";
970
- return h("div", { className: "agy-tv-card" }, h("div", { className: "agy-tv-card-header" }, h("div", { className: "agy-tv-card-header-left" }, IconCodeOutline16(13), h("span", { className: "agy-tv-card-path" }, model.tool || "Tool")), h(CopyButton, { text: copyText })), h("pre", { className: "agy-tv-card-content" }, clip(rawBody, 16e3)), inspectNode);
2053
+ const rawBody = model.output !== void 0 && model.output !== "" ? trimTrailingBlankLines(model.output) : model.raw !== void 0 && model.raw !== "" ? clip(model.raw, 4e3) : tr(t, "tv.noDetail", "(no detail)");
2054
+ return h("div", { className: "agy-tv-card" }, h("div", { className: "agy-tv-card-header" }, h("div", { className: "agy-tv-card-header-left" }, IconCodeOutline16(13), h("span", { className: "agy-tv-card-path" }, model.tool || "Tool")), h(CopyButton, {
2055
+ text: copyText,
2056
+ t
2057
+ })), h("pre", { className: "agy-tv-card-content" }, clip(rawBody, 16e3)), inspectNode);
971
2058
  }
972
2059
  }
973
2060
  }
974
2061
  function AgyMirrorToolView(props) {
975
2062
  const [open, setOpen] = useToggle(false);
976
2063
  const h = hx;
977
- const { block, cwd, home, openFile, inspect } = props;
2064
+ const { block, cwd, home, openFile, inspect, t } = props;
978
2065
  const settle = isSettled(block);
979
2066
  const err = isError(block);
980
2067
  const model = mirrorCardModel(block, cwd, home);
981
2068
  const state = !settle ? "run" : err ? "err" : "ok";
982
- const title = displayTitle(model);
2069
+ const title = displayTitle(model, t);
983
2070
  const summary = displaySummary(model, cwd, home);
984
2071
  const stat = diffStat(model);
985
2072
  const preview = !open && model.kind !== "diff" ? previewLine(model, cwd, home) : void 0;
@@ -1016,7 +2103,7 @@ window.__ModuleLoader__.load({
1016
2103
  setOpen();
1017
2104
  }
1018
2105
  },
1019
- title: inspect !== void 0 ? open ? "Click to collapse · right-click to inspect" : "Click to expand · right-click to inspect" : open ? "Click to collapse" : "Click to expand",
2106
+ title: inspect !== void 0 ? open ? tr(t, "tv.hint.collapseInspect", "Click to collapse · right-click to inspect") : tr(t, "tv.hint.expandInspect", "Click to expand · right-click to inspect") : open ? tr(t, "tv.hint.collapse", "Click to collapse") : tr(t, "tv.hint.expand", "Click to expand"),
1020
2107
  onContextMenu: inspect !== void 0 ? (e) => {
1021
2108
  e.preventDefault();
1022
2109
  inspect();
@@ -1044,7 +2131,8 @@ window.__ModuleLoader__.load({
1044
2131
  openFile,
1045
2132
  cwd,
1046
2133
  home,
1047
- inspect
2134
+ inspect,
2135
+ t
1048
2136
  }))] : []);
1049
2137
  }
1050
2138
  /** Extract the agy mirror cursor (and tool name) from a run_code program. */
@@ -1116,7 +2204,7 @@ window.__ModuleLoader__.load({
1116
2204
  setOpen();
1117
2205
  }
1118
2206
  },
1119
- title: inspect !== void 0 ? open ? "Click to collapse · right-click to inspect" : "Click to expand · right-click to inspect" : open ? "Click to collapse" : "Click to expand",
2207
+ title: inspect !== void 0 ? open ? tr(t, "tv.hint.collapseInspect", "Click to collapse · right-click to inspect") : tr(t, "tv.hint.expandInspect", "Click to expand · right-click to inspect") : open ? tr(t, "tv.hint.collapse", "Click to collapse") : tr(t, "tv.hint.expand", "Click to expand"),
1120
2208
  onContextMenu: inspect !== void 0 ? (e) => {
1121
2209
  e.preventDefault();
1122
2210
  inspect();
@@ -1159,18 +2247,21 @@ window.__ModuleLoader__.load({
1159
2247
  const block = typed?.block;
1160
2248
  const mirror = parseAgyMirrorFromCode(block !== void 0 ? parsedArgsRaw(block) : "{}");
1161
2249
  if (mirror === null) return AgyCodeToolView(typed);
1162
- return AgyMirrorToolView({ block: {
1163
- ...block,
1164
- call: {
1165
- ...block?.call ?? {},
1166
- name: "agy_tool",
1167
- argsRaw: JSON.stringify({
1168
- run: mirror.run,
1169
- step: mirror.step,
1170
- ...mirror.tool !== void 0 ? { tool: mirror.tool } : {}
1171
- })
1172
- }
1173
- } });
2250
+ return AgyMirrorToolView({
2251
+ block: {
2252
+ ...block,
2253
+ call: {
2254
+ ...block?.call ?? {},
2255
+ name: "agy_tool",
2256
+ argsRaw: JSON.stringify({
2257
+ run: mirror.run,
2258
+ step: mirror.step,
2259
+ ...mirror.tool !== void 0 ? { tool: mirror.tool } : {}
2260
+ })
2261
+ }
2262
+ },
2263
+ t: typed?.t
2264
+ });
1174
2265
  }
1175
2266
  function installAgyToolView(ctx) {
1176
2267
  ctx.slots.inject("tool.call.toolview", () => {
@@ -1178,7 +2269,8 @@ window.__ModuleLoader__.load({
1178
2269
  name: "tool.call.toolview",
1179
2270
  key: "agy_tool",
1180
2271
  id: "agy-tool-view",
1181
- label: "Antigravity tool"
2272
+ label: "Antigravity tool",
2273
+ locale: NS
1182
2274
  }, AgyMirrorToolView);
1183
2275
  const d2 = ctx.slots.register({
1184
2276
  name: "tool.call.toolview",
@@ -1548,20 +2640,170 @@ window.__ModuleLoader__.load({
1548
2640
  align-self: flex-start;
1549
2641
  align-items: center;
1550
2642
  gap: 4px;
1551
- margin: 4px 0 2px 4px;
1552
- padding: 2px 8px;
2643
+ margin: 4px 0 2px 4px;
2644
+ padding: 2px 8px;
2645
+ font-size: 11px;
2646
+ line-height: 16px;
2647
+ transition: opacity 0.1s;
2648
+ display: inline-flex;
2649
+ }
2650
+ .agy-tv-root:hover .agy-tv-inspect-btn,
2651
+ .agy-tv-inspect-btn:focus-visible {
2652
+ opacity: 1;
2653
+ }
2654
+ .agy-tv-inspect-btn:hover {
2655
+ background: var(--dsw-alias-interactive-bg-hover-solid, #f1f5f9);
2656
+ color: var(--dsw-alias-label-primary, #0f172a);
2657
+ }
2658
+
2659
+ /* Question Card Styles */
2660
+ .agy-tv-question-wrap {
2661
+ padding: 10px 14px;
2662
+ display: flex;
2663
+ flex-direction: column;
2664
+ gap: 10px;
2665
+ font-family: inherit;
2666
+ }
2667
+ .agy-tv-q-item {
2668
+ display: flex;
2669
+ flex-direction: column;
2670
+ gap: 6px;
2671
+ }
2672
+ .agy-tv-q-prompt {
2673
+ font-size: 13px;
2674
+ font-weight: 600;
2675
+ color: var(--dsw-alias-label-primary, #0f172a);
2676
+ }
2677
+ .agy-tv-q-opts {
2678
+ display: flex;
2679
+ flex-direction: column;
2680
+ gap: 6px;
2681
+ }
2682
+ .agy-tv-q-opt {
2683
+ display: flex;
2684
+ align-items: center;
2685
+ gap: 8px;
2686
+ padding: 6px 10px;
2687
+ border-radius: 6px;
2688
+ background: var(--dsw-alias-bg-l2, rgba(127, 127, 127, 0.08));
2689
+ border: 1px solid var(--dsw-alias-border-l2, rgba(127, 127, 127, 0.2));
2690
+ color: var(--dsw-alias-label-primary, #334155);
2691
+ cursor: pointer;
2692
+ text-align: left;
2693
+ font-size: 12.5px;
2694
+ transition: all 0.15s ease;
2695
+ }
2696
+ .agy-tv-q-opt:hover {
2697
+ background: var(--dsw-alias-bg-hover, rgba(127, 127, 127, 0.15));
2698
+ border-color: var(--dsw-alias-border-l3, rgba(127, 127, 127, 0.35));
2699
+ }
2700
+ .agy-tv-q-opt.selected {
2701
+ background: rgba(16, 185, 129, 0.12);
2702
+ border-color: #10b981;
2703
+ color: #10b981;
2704
+ }
2705
+ .agy-tv-q-check {
2706
+ width: 14px;
2707
+ height: 14px;
2708
+ border: 1.5px solid currentColor;
2709
+ display: inline-flex;
2710
+ align-items: center;
2711
+ justify-content: center;
2712
+ flex-shrink: 0;
2713
+ }
2714
+ .agy-tv-q-check.radio {
2715
+ border-radius: 50%;
2716
+ }
2717
+ .agy-tv-q-check.checkbox {
2718
+ border-radius: 3px;
2719
+ }
2720
+ .agy-tv-q-check.checked:after {
2721
+ content: "";
2722
+ width: 6px;
2723
+ height: 6px;
2724
+ background: currentColor;
2725
+ border-radius: inherit;
2726
+ }
2727
+ .agy-tv-q-footer {
2728
+ display: flex;
2729
+ align-items: center;
2730
+ justify-content: space-between;
2731
+ padding-top: 6px;
2732
+ border-top: 1px solid var(--dsw-alias-border-l2, rgba(127, 127, 127, 0.15));
2733
+ }
2734
+ .agy-tv-q-insert-btn {
2735
+ padding: 5px 12px;
2736
+ border-radius: 6px;
2737
+ font-size: 12px;
2738
+ font-weight: 600;
2739
+ background: rgba(16, 185, 129, 0.15);
2740
+ border: 1px solid #10b981;
2741
+ color: #10b981;
2742
+ cursor: pointer;
2743
+ transition: all 0.15s ease;
2744
+ }
2745
+ .agy-tv-q-insert-btn:disabled {
2746
+ opacity: 0.4;
2747
+ cursor: not-allowed;
2748
+ }
2749
+ .agy-tv-q-insert-btn:not(:disabled):hover {
2750
+ background: #10b981;
2751
+ color: #fff;
2752
+ }
2753
+ .agy-tv-q-answered {
2754
+ font-size: 11px;
2755
+ color: var(--dsw-alias-label-tertiary, #888);
2756
+ }
2757
+
2758
+ /* Image Card Styles */
2759
+ .agy-tv-img-wrap {
2760
+ padding: 10px 14px;
2761
+ display: flex;
2762
+ flex-direction: column;
2763
+ gap: 8px;
2764
+ }
2765
+ .agy-tv-img-meta {
2766
+ display: flex;
2767
+ align-items: center;
2768
+ justify-content: space-between;
2769
+ gap: 8px;
2770
+ }
2771
+ .agy-tv-img-prompt {
2772
+ font-size: 12.5px;
2773
+ color: var(--dsw-alias-label-primary, #334155);
2774
+ font-style: italic;
2775
+ overflow: hidden;
2776
+ text-overflow: ellipsis;
2777
+ white-space: nowrap;
2778
+ }
2779
+ .agy-tv-img-badges {
2780
+ display: flex;
2781
+ gap: 4px;
2782
+ flex-shrink: 0;
2783
+ }
2784
+ .agy-tv-img-badge {
1553
2785
  font-size: 11px;
1554
- line-height: 16px;
1555
- transition: opacity 0.1s;
1556
- display: inline-flex;
2786
+ padding: 1px 6px;
2787
+ border-radius: 4px;
2788
+ background: var(--dsw-alias-bg-l2, rgba(127, 127, 127, 0.1));
2789
+ border: 1px solid var(--dsw-alias-border-l2, rgba(127, 127, 127, 0.2));
2790
+ color: var(--dsw-alias-label-secondary, #888);
1557
2791
  }
1558
- .agy-tv-root:hover .agy-tv-inspect-btn,
1559
- .agy-tv-inspect-btn:focus-visible {
1560
- opacity: 1;
2792
+ .agy-tv-img-preview-box {
2793
+ max-height: 320px;
2794
+ overflow: hidden;
2795
+ border-radius: 6px;
2796
+ border: 1px solid var(--dsw-alias-border-l2, rgba(127, 127, 127, 0.2));
2797
+ display: flex;
2798
+ align-items: center;
2799
+ justify-content: center;
2800
+ background: repeating-conic-gradient(rgba(127,127,127,0.08) 0% 25%, transparent 0% 50%) 50% / 16px 16px;
2801
+ cursor: zoom-in;
1561
2802
  }
1562
- .agy-tv-inspect-btn:hover {
1563
- background: var(--dsw-alias-interactive-bg-hover-solid, #f1f5f9);
1564
- color: var(--dsw-alias-label-primary, #0f172a);
2803
+ .agy-tv-img-thumb {
2804
+ max-width: 100%;
2805
+ max-height: 320px;
2806
+ object-fit: contain;
1565
2807
  }
1566
2808
  `;
1567
2809
  const gDoc = globalThis.document;
@@ -1601,12 +2843,25 @@ div[data-agy-mermaid-svg][data-agy-mermaid-error] {
1601
2843
  white-space: pre-wrap;
1602
2844
  padding: 10px 12px;
1603
2845
  }
2846
+ /* Prevent Mermaid's internal error DOM or leaked temporary nodes from escaping into document.body.
2847
+ The generic .error-icon/.error-text classes are scoped to OUR mounts and the
2848
+ leaked-body id shapes only — unqualified, they hid host UI using the same
2849
+ class names. */
2850
+ div[data-agy-mermaid-svg] .error-icon,
2851
+ div[data-agy-mermaid-svg] .error-text,
2852
+ body > [id^="dagy-mmd"] .error-icon,
2853
+ body > [id^="iagy-mmd"] .error-icon {
2854
+ display: none !important;
2855
+ }
1604
2856
  `;
1605
- const CDN_URLS = [
1606
- "/plugins/agy-link/mermaid.min.js",
1607
- "https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.min.js",
1608
- "https://unpkg.com/mermaid@11/dist/mermaid.min.js"
1609
- ];
2857
+ /**
2858
+ * The bundle ships mermaid.min.js and serves it from the plugin route. There
2859
+ * is deliberately NO public-CDN fallback: a floating-version, unsigned
2860
+ * script executing inside the DSH web origin (with reach into every local
2861
+ * HTTP API) is a supply-chain hazard that outweighs offline convenience.
2862
+ * When the local asset is missing the source block simply stays visible.
2863
+ */
2864
+ const CDN_URLS = ["/plugins/agy-link/mermaid.min.js"];
1610
2865
  let loadPromise = null;
1611
2866
  let loadFailedAt = 0;
1612
2867
  const LOAD_RETRY_MS = 6e4;
@@ -1624,9 +2879,13 @@ div[data-agy-mermaid-svg][data-agy-mermaid-error] {
1624
2879
  const s = document.createElement("script");
1625
2880
  s.src = src;
1626
2881
  s.async = true;
2882
+ let settled = false;
1627
2883
  const done = (ok) => {
2884
+ if (settled) return;
2885
+ settled = true;
1628
2886
  s.removeEventListener("load", onLoad);
1629
2887
  s.removeEventListener("error", onError);
2888
+ if (!ok) s.remove();
1630
2889
  resolve(ok);
1631
2890
  };
1632
2891
  const onLoad = () => done(true);
@@ -1649,7 +2908,8 @@ div[data-agy-mermaid-svg][data-agy-mermaid-error] {
1649
2908
  startOnLoad: false,
1650
2909
  securityLevel: "strict",
1651
2910
  theme: "neutral",
1652
- fontFamily: "inherit"
2911
+ fontFamily: "inherit",
2912
+ suppressErrorRendering: true
1653
2913
  });
1654
2914
  } catch {}
1655
2915
  return window.mermaid;
@@ -1731,6 +2991,16 @@ div[data-agy-mermaid-svg][data-agy-mermaid-error] {
1731
2991
  function readSource(block) {
1732
2992
  return ((block.querySelector("[data-code-block-content] code") ?? block.querySelector("[data-code-block-content] pre") ?? block.querySelector("[data-code-block-content]"))?.textContent ?? "").trim();
1733
2993
  }
2994
+ function cleanupTempElements(id) {
2995
+ if (typeof document === "undefined") return;
2996
+ if (id) {
2997
+ document.getElementById("d" + id)?.remove();
2998
+ document.getElementById("i" + id)?.remove();
2999
+ document.getElementById(id)?.remove();
3000
+ }
3001
+ const stray = document.querySelectorAll("body > [id^=\"dagy-mmd\"], body > [id^=\"iagy-mmd\"], body > [id^=\"d_agy-mmd\"], body > [id^=\"i_agy-mmd\"], body > [id^=\"agy-mmd-\"]");
3002
+ for (let i = 0; i < stray.length; i++) stray[i]?.remove();
3003
+ }
1734
3004
  async function renderInto(block, source) {
1735
3005
  block.querySelector("[data-agy-mermaid-svg]")?.remove();
1736
3006
  const mount = document.createElement("div");
@@ -1743,28 +3013,32 @@ div[data-agy-mermaid-svg][data-agy-mermaid-error] {
1743
3013
  mount.textContent = "Mermaid renderer unavailable (offline). Diagram source stays below.";
1744
3014
  return;
1745
3015
  }
3016
+ renderSeq += 1;
3017
+ const id = "agy-mmd-" + renderSeq;
1746
3018
  try {
1747
- renderSeq += 1;
1748
- const id = "agy-mmd-" + renderSeq;
1749
- const { svg } = await api.render(id, source);
3019
+ if (typeof api.parse === "function") await api.parse(source);
3020
+ const { svg } = await api.render(id, source, mount);
1750
3021
  mount.innerHTML = svg;
1751
3022
  block.setAttribute("data-agy-mermaid", "done");
1752
3023
  } catch (e) {
1753
3024
  block.setAttribute("data-agy-mermaid", "error");
1754
3025
  mount.setAttribute("data-agy-mermaid-error", "1");
1755
3026
  mount.textContent = "Mermaid render failed: " + (e instanceof Error ? e.message : String(e));
3027
+ } finally {
3028
+ cleanupTempElements(id);
1756
3029
  }
1757
3030
  }
1758
- let scanTimer = null;
1759
- function scheduleScan() {
1760
- if (scanTimer !== null) return;
1761
- scanTimer = setTimeout(() => {
1762
- scanTimer = null;
3031
+ let scanTimer$1 = null;
3032
+ function scheduleScan$1() {
3033
+ if (scanTimer$1 !== null) return;
3034
+ scanTimer$1 = setTimeout(() => {
3035
+ scanTimer$1 = null;
1763
3036
  scan();
1764
3037
  }, 150);
1765
3038
  }
1766
3039
  function scan() {
1767
3040
  if (typeof document === "undefined") return;
3041
+ cleanupTempElements();
1768
3042
  const blocks = document.querySelectorAll(".md-code-block");
1769
3043
  for (let i = 0; i < blocks.length; i++) {
1770
3044
  const block = blocks[i];
@@ -1779,14 +3053,25 @@ div[data-agy-mermaid-svg][data-agy-mermaid-error] {
1779
3053
  renderInto(block, source);
1780
3054
  }
1781
3055
  }
3056
+ let activeObserver$1 = null;
3057
+ let domContentLoadedHandler$1 = null;
3058
+ /**
3059
+ * Install the mermaid overlay. Returns a disposer that disconnects the
3060
+ * MutationObserver and cancels pending setup — wrap it in ctx.effect so a
3061
+ * plugin reload cannot stack observers over the same document.
3062
+ */
1782
3063
  function installMermaidRendering() {
1783
- if (typeof window === "undefined" || typeof document === "undefined") return;
3064
+ if (typeof window === "undefined" || typeof document === "undefined") return () => {};
3065
+ disposeMermaidRendering();
1784
3066
  injectStyles$1();
3067
+ cleanupTempElements();
1785
3068
  const observer = new MutationObserver(() => {
1786
- scheduleScan();
3069
+ scheduleScan$1();
1787
3070
  });
3071
+ activeObserver$1 = observer;
1788
3072
  const setup = () => {
1789
3073
  if (document.body) {
3074
+ cleanupTempElements();
1790
3075
  observer.observe(document.body, {
1791
3076
  childList: true,
1792
3077
  subtree: true
@@ -1794,8 +3079,26 @@ div[data-agy-mermaid-svg][data-agy-mermaid-error] {
1794
3079
  scan();
1795
3080
  }
1796
3081
  };
1797
- if (document.readyState === "loading") window.addEventListener("DOMContentLoaded", setup, { once: true });
1798
- else setup();
3082
+ if (document.readyState === "loading") {
3083
+ domContentLoadedHandler$1 = setup;
3084
+ window.addEventListener("DOMContentLoaded", setup, { once: true });
3085
+ } else setup();
3086
+ return disposeMermaidRendering;
3087
+ }
3088
+ /** Disconnect the observer and cancel pending setup work. */
3089
+ function disposeMermaidRendering() {
3090
+ if (activeObserver$1 !== null) {
3091
+ activeObserver$1.disconnect();
3092
+ activeObserver$1 = null;
3093
+ }
3094
+ if (domContentLoadedHandler$1 !== null) {
3095
+ window.removeEventListener("DOMContentLoaded", domContentLoadedHandler$1);
3096
+ domContentLoadedHandler$1 = null;
3097
+ }
3098
+ if (scanTimer$1 !== null) {
3099
+ clearTimeout(scanTimer$1);
3100
+ scanTimer$1 = null;
3101
+ }
1799
3102
  }
1800
3103
  //#endregion
1801
3104
  //#region src/client/reasoning.ts
@@ -1857,9 +3160,16 @@ div[data-variant="think"] [class*="thinkBody"] {
1857
3160
  * Try to expand a thinking row once when its body contains real prose
1858
3161
  * (not a bare [agy thinking turn · N tokens] chip). Uses the host's own
1859
3162
  * disclosure control when present — never mutates thinkBody children.
3163
+ *
3164
+ * A row the user manually COLLAPSED is recorded in a WeakSet keyed by the
3165
+ * row element; React keeps the same element identity across its own
3166
+ * re-renders, so the scanner stops re-expanding it (the old data attribute
3167
+ * survived only until React rebuilt the node).
1860
3168
  */
3169
+ const userCollapsed = typeof WeakSet !== "undefined" ? /* @__PURE__ */ new WeakSet() : null;
1861
3170
  function tryExpandThinkRow(root) {
1862
3171
  if (root.dataset.agyExpanded === "true") return;
3172
+ if (userCollapsed?.has(root) === true) return;
1863
3173
  const body = root.querySelector("[class*=\"thinkBody\"]");
1864
3174
  if (!body) return;
1865
3175
  const text = (body.textContent ?? "").trim();
@@ -1871,10 +3181,15 @@ div[data-variant="think"] [class*="thinkBody"] {
1871
3181
  return;
1872
3182
  }
1873
3183
  const toggle = root.querySelector("[data-disclosure-row]") ?? root.querySelector("[class*=\"disclosure\"]") ?? root.querySelector("button");
1874
- if (toggle) try {
1875
- toggle.click();
1876
- root.dataset.agyExpanded = "true";
1877
- } catch {}
3184
+ if (toggle) {
3185
+ userCollapsed?.add(root);
3186
+ try {
3187
+ toggle.click();
3188
+ root.dataset.agyExpanded = "true";
3189
+ } catch {
3190
+ userCollapsed?.delete(root);
3191
+ }
3192
+ }
1878
3193
  }
1879
3194
  function scanThinkingRows() {
1880
3195
  if (typeof document === "undefined") return;
@@ -1884,12 +3199,29 @@ div[data-variant="think"] [class*="thinkBody"] {
1884
3199
  if (row) tryExpandThinkRow(row);
1885
3200
  }
1886
3201
  }
3202
+ let scanTimer = null;
3203
+ function scheduleScan() {
3204
+ if (scanTimer !== null) return;
3205
+ scanTimer = setTimeout(() => {
3206
+ scanTimer = null;
3207
+ scanThinkingRows();
3208
+ }, 200);
3209
+ }
3210
+ let activeObserver = null;
3211
+ let domContentLoadedHandler = null;
3212
+ /**
3213
+ * Install the auto-expand reasoning overlay. Returns a disposer that
3214
+ * disconnects the MutationObserver — wrap it in ctx.effect so a plugin
3215
+ * reload cannot stack observers.
3216
+ */
1887
3217
  function installAutoExpandReasoning() {
1888
- if (typeof window === "undefined" || typeof document === "undefined") return;
3218
+ if (typeof window === "undefined" || typeof document === "undefined") return () => {};
3219
+ disposeAutoExpandReasoning();
1889
3220
  injectStyles();
1890
3221
  const observer = new MutationObserver(() => {
1891
- scanThinkingRows();
3222
+ scheduleScan();
1892
3223
  });
3224
+ activeObserver = observer;
1893
3225
  const setupObserver = () => {
1894
3226
  if (document.body) {
1895
3227
  observer.observe(document.body, {
@@ -1899,26 +3231,57 @@ div[data-variant="think"] [class*="thinkBody"] {
1899
3231
  scanThinkingRows();
1900
3232
  }
1901
3233
  };
1902
- if (document.readyState === "loading") window.addEventListener("DOMContentLoaded", setupObserver, { once: true });
1903
- else setupObserver();
3234
+ if (document.readyState === "loading") {
3235
+ domContentLoadedHandler = setupObserver;
3236
+ window.addEventListener("DOMContentLoaded", setupObserver, { once: true });
3237
+ } else setupObserver();
3238
+ return disposeAutoExpandReasoning;
3239
+ }
3240
+ /** Disconnect the observer and cancel pending work. */
3241
+ function disposeAutoExpandReasoning() {
3242
+ if (activeObserver !== null) {
3243
+ activeObserver.disconnect();
3244
+ activeObserver = null;
3245
+ }
3246
+ if (domContentLoadedHandler !== null) {
3247
+ window.removeEventListener("DOMContentLoaded", domContentLoadedHandler);
3248
+ domContentLoadedHandler = null;
3249
+ }
3250
+ if (scanTimer !== null) {
3251
+ clearTimeout(scanTimer);
3252
+ scanTimer = null;
3253
+ }
1904
3254
  }
1905
3255
  //#endregion
1906
3256
  //#region src/client/index.ts
1907
3257
  const { createElement: h, useState, useEffect, useRef } = require("react");
1908
3258
  const reactDom = require("react-dom");
1909
3259
  const win = globalThis;
1910
- const bodyEl = win.document?.body ?? null;
1911
3260
  const portalToBody = (node) => {
1912
- if (bodyEl && reactDom && typeof reactDom.createPortal === "function") return reactDom.createPortal(node, bodyEl);
3261
+ const body = win.document?.body ?? (typeof document !== "undefined" ? document.body : null);
3262
+ if (body && reactDom && typeof reactDom.createPortal === "function") return reactDom.createPortal(node, body);
1913
3263
  return node;
1914
3264
  };
1915
3265
  const name = "dsh-agy-link-client";
1916
3266
  const inject = ["slots", "locale"];
1917
3267
  const base = win.location?.origin ?? "";
1918
- let statusCache = null;
3268
+ const STATUS_CACHE_KEY = "dsh_agy_status_cache";
3269
+ function loadInitialStatusCache() {
3270
+ try {
3271
+ const raw = win.localStorage?.getItem(STATUS_CACHE_KEY);
3272
+ if (raw) {
3273
+ const parsed = JSON.parse(raw);
3274
+ if (parsed && typeof parsed === "object") return parsed;
3275
+ }
3276
+ } catch {}
3277
+ return null;
3278
+ }
3279
+ let statusCache = loadInitialStatusCache();
1919
3280
  async function getStatus() {
1920
3281
  try {
1921
- const res = await win.fetch?.(base + "/plugins/agy-link/status");
3282
+ const init = {};
3283
+ if (typeof AbortSignal !== "undefined" && typeof AbortSignal.timeout === "function") init.signal = AbortSignal.timeout(5e3);
3284
+ const res = await win.fetch?.(base + "/plugins/agy-link/status", init);
1922
3285
  if (!res || !res.ok) return null;
1923
3286
  const payload = await res.json();
1924
3287
  statusCache = payload;
@@ -1936,7 +3299,12 @@ div[data-variant="think"] [class*="thinkBody"] {
1936
3299
  });
1937
3300
  if (!res) return null;
1938
3301
  try {
1939
- return await res.json();
3302
+ const payload = await res.json();
3303
+ if (!res.ok && payload !== null && typeof payload === "object") return {
3304
+ ok: false,
3305
+ ...payload
3306
+ };
3307
+ return payload;
1940
3308
  } catch {
1941
3309
  return { ok: res.ok };
1942
3310
  }
@@ -1944,6 +3312,72 @@ div[data-variant="think"] [class*="thinkBody"] {
1944
3312
  return null;
1945
3313
  }
1946
3314
  }
3315
+ const statusListeners = /* @__PURE__ */ new Set();
3316
+ let statusPollTimer = null;
3317
+ let statusFetchSeq = 0;
3318
+ function isStatusEqual(a, b) {
3319
+ if (a === b) return true;
3320
+ if (a === null || b === null) return false;
3321
+ try {
3322
+ return JSON.stringify(a) === JSON.stringify(b);
3323
+ } catch {
3324
+ return false;
3325
+ }
3326
+ }
3327
+ function notifyStatus(st) {
3328
+ statusCache = st;
3329
+ try {
3330
+ win.localStorage?.setItem(STATUS_CACHE_KEY, JSON.stringify(st));
3331
+ } catch {}
3332
+ for (const fn of [...statusListeners]) try {
3333
+ fn(st);
3334
+ } catch {}
3335
+ }
3336
+ async function statusPollTick() {
3337
+ const seq = ++statusFetchSeq;
3338
+ const st = await getStatus();
3339
+ if (st !== null && seq === statusFetchSeq) {
3340
+ if (!isStatusEqual(statusCache, st)) notifyStatus(st);
3341
+ }
3342
+ }
3343
+ function ensureStatusPolling() {
3344
+ if (statusPollTimer !== null) return;
3345
+ statusPollTick();
3346
+ statusPollTimer = setInterval(() => {
3347
+ statusPollTick();
3348
+ }, 3e3);
3349
+ }
3350
+ function stopStatusPollingIfIdle() {
3351
+ if (statusListeners.size === 0 && statusPollTimer !== null) {
3352
+ clearInterval(statusPollTimer);
3353
+ statusPollTimer = null;
3354
+ }
3355
+ }
3356
+ /**
3357
+ * Subscribe a component to the shared status poll. Returns the latest
3358
+ * snapshot plus a manual refresh that keeps the last good snapshot on a
3359
+ * transient failure (a failed refresh used to flip the whole panel back to
3360
+ * its loading state).
3361
+ */
3362
+ function useAgyStatus() {
3363
+ const [status, setStatus] = useState(statusCache);
3364
+ useEffect(() => {
3365
+ statusListeners.add(setStatus);
3366
+ ensureStatusPolling();
3367
+ return () => {
3368
+ statusListeners.delete(setStatus);
3369
+ stopStatusPollingIfIdle();
3370
+ };
3371
+ }, []);
3372
+ const refresh = async () => {
3373
+ const st = await getStatus();
3374
+ if (st !== null) notifyStatus(st);
3375
+ };
3376
+ return {
3377
+ status,
3378
+ refresh
3379
+ };
3380
+ }
1947
3381
  /** Global modal state for opening Antigravity console dialog from footer shortcut or header status */
1948
3382
  const agyModalStore = {
1949
3383
  open: false,
@@ -1960,6 +3394,21 @@ div[data-variant="think"] [class*="thinkBody"] {
1960
3394
  }
1961
3395
  };
1962
3396
  /**
3397
+ * Singleton modal instance manager: ensures at most one modal dialog is
3398
+ * rendered into document.body at any time even when multiple session headers mount.
3399
+ */
3400
+ let primaryModalInstanceId = null;
3401
+ const modalInstanceIds = /* @__PURE__ */ new Set();
3402
+ function registerModalInstance(id) {
3403
+ modalInstanceIds.add(id);
3404
+ if (primaryModalInstanceId === null || !modalInstanceIds.has(primaryModalInstanceId)) primaryModalInstanceId = id;
3405
+ return primaryModalInstanceId === id;
3406
+ }
3407
+ function unregisterModalInstance(id) {
3408
+ modalInstanceIds.delete(id);
3409
+ if (primaryModalInstanceId === id) primaryModalInstanceId = modalInstanceIds.values().next().value ?? null;
3410
+ }
3411
+ /**
1963
3412
  * Format reset timestamp into readable date/time + relative countdown.
1964
3413
  */
1965
3414
  function formatQuotaWindow(t, resetTimeStr) {
@@ -2333,12 +3782,13 @@ body.dark,
2333
3782
  border-radius: 50%;
2334
3783
  }
2335
3784
  .agy-card-hover {
2336
- transition: border-color 0.2s ease, box-shadow 0.2s ease, transform 0.15s ease;
3785
+ transition: border-color 0.2s ease, box-shadow 0.2s ease, transform 0.18s cubic-bezier(0.16, 1, 0.3, 1), opacity 0.2s ease;
2337
3786
  }
2338
3787
  .agy-card-hover:hover {
2339
3788
  border-color: var(--agy-border-card-primary, #3b82f6) !important;
2340
3789
  box-shadow: var(--agy-shadow-card-hover) !important;
2341
3790
  }
3791
+
2342
3792
  .agy-btn {
2343
3793
  transition: all 0.15s ease;
2344
3794
  user-select: none;
@@ -2393,6 +3843,17 @@ body.dark,
2393
3843
  color: var(--agy-text-primary);
2394
3844
  }
2395
3845
  `;
3846
+ function injectGlobalStyles() {
3847
+ if (typeof document === "undefined") return;
3848
+ const styleId = "dsh-agy-link-global-css";
3849
+ if (document.getElementById(styleId) === null) {
3850
+ const st = document.createElement("style");
3851
+ st.id = styleId;
3852
+ st.textContent = GLOBAL_CSS;
3853
+ const host = document.head ?? document.documentElement;
3854
+ if (host) host.appendChild(st);
3855
+ }
3856
+ }
2396
3857
  const S = {
2397
3858
  container: {
2398
3859
  lineHeight: 1.5,
@@ -2463,27 +3924,29 @@ body.dark,
2463
3924
  card: {
2464
3925
  background: "var(--agy-bg-card)",
2465
3926
  border: "1px solid var(--agy-border-card)",
2466
- borderRadius: "10px",
2467
- padding: "14px 16px",
2468
- marginBottom: "12px",
3927
+ borderRadius: "8px",
3928
+ padding: "10px 14px",
3929
+ marginBottom: "8px",
2469
3930
  color: "var(--agy-text-primary)",
2470
- boxShadow: "var(--agy-shadow-card)"
3931
+ boxShadow: "var(--agy-shadow-card)",
3932
+ transition: "all 0.18s cubic-bezier(0.16, 1, 0.3, 1)"
2471
3933
  },
2472
3934
  cardPrimary: {
2473
- background: "var(--agy-bg-card-primary)",
2474
- border: "1.5px solid var(--agy-border-card-primary)",
2475
- borderRadius: "10px",
2476
- padding: "14px 16px",
2477
- marginBottom: "12px",
2478
- boxShadow: "var(--agy-shadow-card-primary)",
2479
- color: "var(--agy-text-primary)"
3935
+ background: "var(--agy-bg-card)",
3936
+ border: "1px solid var(--agy-border-card)",
3937
+ borderRadius: "8px",
3938
+ padding: "10px 14px",
3939
+ marginBottom: "8px",
3940
+ color: "var(--agy-text-primary)",
3941
+ boxShadow: "var(--agy-shadow-card)",
3942
+ transition: "all 0.18s cubic-bezier(0.16, 1, 0.3, 1)"
2480
3943
  },
2481
3944
  quotaBox: {
2482
3945
  background: "var(--agy-bg-box)",
2483
3946
  border: "1px solid var(--agy-border-box)",
2484
3947
  borderRadius: "8px",
2485
3948
  padding: "8px 10px",
2486
- marginTop: "10px"
3949
+ marginTop: "8px"
2487
3950
  },
2488
3951
  progressBarBg: {
2489
3952
  flex: "1",
@@ -2653,8 +4116,9 @@ body.dark,
2653
4116
  return locale.bind(NS);
2654
4117
  };
2655
4118
  const AgySettingsSection = (props) => {
4119
+ injectGlobalStyles();
2656
4120
  const t = useAgyTranslation();
2657
- const [status, setStatus] = useState(statusCache);
4121
+ const { status, refresh } = useAgyStatus();
2658
4122
  const [aliasInput, setAliasInput] = useState("");
2659
4123
  const [proxyInputs, setProxyInputs] = useState({});
2660
4124
  const [editingProxyId, setEditingProxyId] = useState(null);
@@ -2663,24 +4127,11 @@ body.dark,
2663
4127
  const [loadingAction, setLoadingAction] = useState(null);
2664
4128
  const [toast, setToast] = useState(null);
2665
4129
  const [expandedModels, setExpandedModels] = useState({});
4130
+ const [addProxyInput, setAddProxyInput] = useState("");
4131
+ const [, setTimeTick] = useState(0);
2666
4132
  useEffect(() => {
2667
- let alive = true;
2668
- let lastJson = "";
2669
- const tick = async () => {
2670
- const st = await getStatus();
2671
- if (!alive || !st) return;
2672
- const json = JSON.stringify(st);
2673
- if (json !== lastJson) {
2674
- lastJson = json;
2675
- setStatus(st);
2676
- }
2677
- };
2678
- tick();
2679
- const timer = setInterval(tick, 3e3);
2680
- return () => {
2681
- alive = false;
2682
- clearInterval(timer);
2683
- };
4133
+ const timer = setInterval(() => setTimeTick((n) => n + 1), 3e4);
4134
+ return () => clearInterval(timer);
2684
4135
  }, []);
2685
4136
  const flowStartedRef = useRef(false);
2686
4137
  const safeMessage = (code) => {
@@ -2696,6 +4147,7 @@ body.dark,
2696
4147
  setAddingAccount(false);
2697
4148
  setAuthCodeInput("");
2698
4149
  setAliasInput("");
4150
+ setAddProxyInput("");
2699
4151
  showToast(safeMessage(pa.code), "success");
2700
4152
  } else if (pa.phase === "failed") {
2701
4153
  flowStartedRef.current = false;
@@ -2706,23 +4158,33 @@ body.dark,
2706
4158
  status?.poolAuth?.phase,
2707
4159
  status?.poolAuth?.code
2708
4160
  ]);
4161
+ const toastTimerRef = useRef(null);
4162
+ useEffect(() => () => {
4163
+ if (toastTimerRef.current !== null) clearTimeout(toastTimerRef.current);
4164
+ }, []);
2709
4165
  const showToast = (text, type = "info") => {
2710
4166
  setToast({
2711
4167
  text,
2712
4168
  type,
2713
4169
  id: Date.now()
2714
4170
  });
2715
- setTimeout(() => {
2716
- setToast((prev) => prev?.text === text ? null : prev);
4171
+ if (toastTimerRef.current !== null) clearTimeout(toastTimerRef.current);
4172
+ toastTimerRef.current = setTimeout(() => {
4173
+ toastTimerRef.current = null;
4174
+ setToast((prev) => prev !== null && prev.text === text ? null : prev);
2717
4175
  }, 6e3);
2718
4176
  };
2719
- const refresh = async () => {
2720
- setStatus(await getStatus());
4177
+ const actionFailed = () => {
4178
+ showToast(t("api.genericError"), "error");
2721
4179
  };
2722
4180
  const handleBeginAddAccount = async () => {
2723
4181
  setLoadingAction("pool:beginAdd");
2724
4182
  const alias = aliasInput.trim();
2725
- const res = await postJson("/plugins/agy-link/pool/begin-add", alias ? { alias } : {});
4183
+ const proxyUrl = addProxyInput.trim();
4184
+ const res = await postJson("/plugins/agy-link/pool/begin-add", {
4185
+ ...alias ? { alias } : {},
4186
+ ...proxyUrl ? { proxyUrl } : {}
4187
+ });
2726
4188
  setLoadingAction(null);
2727
4189
  if (res && res.ok) {
2728
4190
  flowStartedRef.current = true;
@@ -2740,6 +4202,7 @@ body.dark,
2740
4202
  flowStartedRef.current = false;
2741
4203
  setAuthCodeInput("");
2742
4204
  setAliasInput("");
4205
+ setAddProxyInput("");
2743
4206
  setAddingAccount(false);
2744
4207
  await refresh();
2745
4208
  showToast(safeMessage(res.code), "success");
@@ -2750,41 +4213,87 @@ body.dark,
2750
4213
  await postJson("/plugins/agy-link/pool/cancel-add", {});
2751
4214
  setAuthCodeInput("");
2752
4215
  setAliasInput("");
4216
+ setAddProxyInput("");
2753
4217
  setAddingAccount(false);
2754
4218
  await refresh();
2755
4219
  };
4220
+ const reorderAccounts = async (newIds) => {
4221
+ setLoadingAction("pool:reorder");
4222
+ const res = await postJson("/plugins/agy-link/pool/reorder", { ids: newIds });
4223
+ setLoadingAction(null);
4224
+ if (res && res.ok) {
4225
+ await refresh();
4226
+ showToast(t("account.reordered"), "success");
4227
+ } else actionFailed();
4228
+ };
4229
+ const moveAccount = async (accId, delta) => {
4230
+ const ids = (status?.pool?.accounts ?? []).map((a) => a.id);
4231
+ const idx = ids.indexOf(accId);
4232
+ if (idx === -1) return;
4233
+ const newIdx = idx + delta;
4234
+ if (newIdx < 0 || newIdx >= ids.length) return;
4235
+ const [moved] = ids.splice(idx, 1);
4236
+ if (moved) ids.splice(newIdx, 0, moved);
4237
+ await reorderAccounts(ids);
4238
+ };
2756
4239
  const setCfg = async (key, value) => {
2757
4240
  setLoadingAction(`config:${key}`);
2758
- await postJson("/plugins/agy-link/config", {
4241
+ const res = await postJson("/plugins/agy-link/config", {
2759
4242
  key,
2760
4243
  value
2761
4244
  });
4245
+ if (res === null || res?.ok === false) {
4246
+ setLoadingAction(null);
4247
+ actionFailed();
4248
+ return;
4249
+ }
2762
4250
  await refresh();
2763
4251
  setLoadingAction(null);
2764
4252
  };
4253
+ const hiddenPendingRef = useRef(null);
2765
4254
  const toggleModelHidden = async (id, hide) => {
2766
- const next = new Set(status?.hiddenModels ?? []);
4255
+ const next = new Set(hiddenPendingRef.current ?? status?.hiddenModels ?? []);
2767
4256
  if (hide) next.add(id);
2768
4257
  else next.delete(id);
2769
- await setCfg("hiddenModels", Array.from(next));
4258
+ hiddenPendingRef.current = next;
4259
+ try {
4260
+ await setCfg("hiddenModels", Array.from(next));
4261
+ } finally {
4262
+ if (hiddenPendingRef.current === next) hiddenPendingRef.current = null;
4263
+ }
2770
4264
  };
2771
4265
  const setPrimary = async (id) => {
2772
4266
  setLoadingAction(`primary:${id}`);
2773
- await postJson("/plugins/agy-link/pool/primary", { id });
4267
+ const res = await postJson("/plugins/agy-link/pool/primary", { id });
4268
+ if (res === null || res?.ok === false) {
4269
+ setLoadingAction(null);
4270
+ actionFailed();
4271
+ return;
4272
+ }
2774
4273
  await refresh();
2775
4274
  setLoadingAction(null);
2776
4275
  showToast(t("account.setPrimary"), "success");
2777
4276
  };
2778
4277
  const removeAccount = async (id, alias) => {
2779
4278
  setLoadingAction(`remove:${id}`);
2780
- await postJson("/plugins/agy-link/pool/remove", { id });
4279
+ const res = await postJson("/plugins/agy-link/pool/remove", { id });
4280
+ if (res === null || res?.ok === false) {
4281
+ setLoadingAction(null);
4282
+ actionFailed();
4283
+ return;
4284
+ }
2781
4285
  await refresh();
2782
4286
  setLoadingAction(null);
2783
4287
  showToast(t("account.removed", { alias }), "info");
2784
4288
  };
2785
4289
  const refreshQuota = async (id) => {
2786
4290
  setLoadingAction(id ? `refresh:${id}` : "refresh:all");
2787
- await postJson("/plugins/agy-link/pool/refresh-quota", { id });
4291
+ const res = await postJson("/plugins/agy-link/pool/refresh-quota", { id });
4292
+ if (res === null || res?.ok === false) {
4293
+ setLoadingAction(null);
4294
+ actionFailed();
4295
+ return;
4296
+ }
2788
4297
  await refresh();
2789
4298
  setLoadingAction(null);
2790
4299
  showToast(t("quota.refreshed"), "success");
@@ -2792,10 +4301,15 @@ body.dark,
2792
4301
  const saveProxy = async (id) => {
2793
4302
  setLoadingAction(`proxy:${id}`);
2794
4303
  const proxyUrl = proxyInputs[id];
2795
- await postJson("/plugins/agy-link/pool/proxy", {
4304
+ const res = await postJson("/plugins/agy-link/pool/proxy", {
2796
4305
  id,
2797
4306
  proxyUrl
2798
4307
  });
4308
+ if (res === null || res?.ok === false) {
4309
+ setLoadingAction(null);
4310
+ actionFailed();
4311
+ return;
4312
+ }
2799
4313
  setEditingProxyId(null);
2800
4314
  await refresh();
2801
4315
  setLoadingAction(null);
@@ -2803,13 +4317,23 @@ body.dark,
2803
4317
  };
2804
4318
  const setMode = async (mode) => {
2805
4319
  setLoadingAction(`mode:${mode}`);
2806
- await postJson("/plugins/agy-link/pool/mode", { mode });
4320
+ const res = await postJson("/plugins/agy-link/pool/mode", { mode });
4321
+ if (res === null || res?.ok === false) {
4322
+ setLoadingAction(null);
4323
+ actionFailed();
4324
+ return;
4325
+ }
2807
4326
  await refresh();
2808
4327
  setLoadingAction(null);
2809
4328
  };
2810
4329
  const clearCooldown = async (id) => {
2811
4330
  setLoadingAction(id ? `clearCooldown:${id}` : "clearCooldown:all");
2812
- await postJson("/plugins/agy-link/pool/clear-cooldown", { id });
4331
+ const res = await postJson("/plugins/agy-link/pool/clear-cooldown", { id });
4332
+ if (res === null || res?.ok === false) {
4333
+ setLoadingAction(null);
4334
+ actionFailed();
4335
+ return;
4336
+ }
2813
4337
  await refresh();
2814
4338
  setLoadingAction(null);
2815
4339
  showToast(t("account.clearCooldown"), "success");
@@ -2980,9 +4504,15 @@ body.dark,
2980
4504
  const dotColor = !acc.enabled ? "#64748b" : isAuthRequired ? "#ef4444" : hasCooldown ? "#f59e0b" : "#10b981";
2981
4505
  const isEditingProxy = editingProxyId === acc.id;
2982
4506
  const isExpanded = expandedModels[acc.id] ?? false;
2983
- const cardStyle = isPrimary ? { ...S.cardPrimary } : { ...S.card };
4507
+ const accIndex = accounts.indexOf(acc);
4508
+ const cardStyle = {
4509
+ ...S.card,
4510
+ cursor: "default",
4511
+ padding: "10px 14px",
4512
+ marginBottom: "8px"
4513
+ };
2984
4514
  const legacyDefault = acc.defaultAlias === void 0 && acc.systemHome && acc.alias === "主账号 (系统登录)";
2985
- const displayAlias = acc.defaultAlias || legacyDefault ? acc.systemHome ? t("account.defaultAlias") : t("account.aliasDefault", { number: accounts.indexOf(acc) + 1 }) : acc.alias;
4515
+ const displayAlias = acc.defaultAlias || legacyDefault ? acc.systemHome ? t("account.defaultAlias") : t("account.aliasDefault", { number: accIndex + 1 }) : acc.alias;
2986
4516
  const googleModels = acc.quotas.google?.models ?? [];
2987
4517
  const anthropicModels = acc.quotas.anthropic?.models ?? [];
2988
4518
  const openaiModels = acc.quotas.openai?.models ?? [];
@@ -3008,155 +4538,278 @@ body.dark,
3008
4538
  display: "flex",
3009
4539
  alignItems: "center",
3010
4540
  justifyContent: "space-between",
4541
+ userSelect: "none",
4542
+ gap: "8px",
3011
4543
  marginBottom: "8px"
3012
4544
  } }, h("div", { style: {
3013
4545
  display: "flex",
3014
4546
  alignItems: "center",
3015
- gap: "8px",
3016
- flexWrap: "wrap"
4547
+ gap: "6px",
4548
+ flexWrap: "wrap",
4549
+ flex: "1",
4550
+ minWidth: 0
3017
4551
  } }, h("span", {
4552
+ style: {
4553
+ ...S.badgeTag,
4554
+ fontWeight: 700,
4555
+ fontSize: "10.5px",
4556
+ padding: "1px 5px",
4557
+ background: isPrimary ? "var(--agy-badge-primary-bg)" : "var(--agy-badge-tag-bg)",
4558
+ color: isPrimary ? "var(--agy-badge-primary-text)" : "var(--agy-badge-tag-text)",
4559
+ borderColor: isPrimary ? "var(--agy-badge-primary-border)" : "var(--agy-badge-tag-border)"
4560
+ },
4561
+ title: `优先级 #${accIndex + 1}`
4562
+ }, `#${accIndex + 1}`), accounts.length > 1 ? h("div", { style: {
4563
+ display: "inline-flex",
4564
+ gap: "2px",
4565
+ alignItems: "center"
4566
+ } }, accIndex > 0 ? h("button", {
4567
+ type: "button",
4568
+ className: "agy-btn",
4569
+ style: {
4570
+ ...S.btnSm,
4571
+ padding: "1px 4px",
4572
+ fontSize: "10px",
4573
+ lineHeight: 1,
4574
+ height: "18px"
4575
+ },
4576
+ title: t("account.moveUp"),
4577
+ onClick: () => void moveAccount(acc.id, -1)
4578
+ }, uiIcon("arrowUp", 10)) : null, accIndex < accounts.length - 1 ? h("button", {
4579
+ type: "button",
4580
+ className: "agy-btn",
4581
+ style: {
4582
+ ...S.btnSm,
4583
+ padding: "1px 4px",
4584
+ fontSize: "10px",
4585
+ lineHeight: 1,
4586
+ height: "18px"
4587
+ },
4588
+ title: t("account.moveDown"),
4589
+ onClick: () => void moveAccount(acc.id, 1)
4590
+ }, uiIcon("arrowDown", 10)) : null) : null, h("span", {
3018
4591
  className: "agy-pulse-dot",
3019
4592
  style: {
3020
4593
  background: dotColor,
3021
- boxShadow: `0 0 8px ${dotColor}aa`
4594
+ boxShadow: `0 0 6px ${dotColor}aa`,
4595
+ marginLeft: "1px"
3022
4596
  }
3023
- }), h("span", { style: {
3024
- fontWeight: 700,
3025
- fontSize: "13.5px",
3026
- color: "var(--agy-text-primary)"
3027
- } }, displayAlias), isAuthRequired ? h("span", { style: {
4597
+ }), h("span", {
4598
+ style: {
4599
+ fontWeight: 700,
4600
+ fontSize: "13px",
4601
+ color: "var(--agy-text-primary)",
4602
+ whiteSpace: "nowrap"
4603
+ },
4604
+ title: displayAlias
4605
+ }, displayAlias), h("span", { style: {
4606
+ ...S.badgeTag,
4607
+ background: "rgba(59, 130, 246, 0.12)",
4608
+ color: "#3b82f6",
4609
+ borderColor: "rgba(59, 130, 246, 0.35)",
4610
+ fontSize: "10px",
4611
+ padding: "1px 5px",
4612
+ fontWeight: 700
4613
+ } }, "AGY"), isAuthRequired ? h("span", { style: {
3028
4614
  ...S.badgeTag,
3029
4615
  background: "var(--agy-badge-email-bg)",
3030
4616
  color: "#ef4444",
3031
4617
  borderColor: "#fca5a5",
3032
- gap: "4px",
4618
+ gap: "3px",
4619
+ fontSize: "10px",
4620
+ padding: "1px 5px",
3033
4621
  fontWeight: 700
3034
- } }, uiIcon("alert", 11, "#ef4444"), t("status.authRequired")) : null, acc.email ? h("span", { style: {
4622
+ } }, uiIcon("alert", 10, "#ef4444"), t("status.authRequired")) : null, hasCooldown ? h("span", { style: {
3035
4623
  ...S.badgeTag,
3036
- background: "var(--agy-badge-email-bg)",
3037
- color: "var(--agy-badge-email-text)",
3038
- borderColor: "var(--agy-badge-email-border)",
3039
- gap: "5px"
3040
- } }, uiIcon("mail", 11, "var(--agy-badge-email-text)"), acc.email) : null, isPrimary ? h("span", { style: {
4624
+ background: "rgba(245, 158, 11, 0.15)",
4625
+ color: "#f59e0b",
4626
+ borderColor: "rgba(245, 158, 11, 0.4)",
4627
+ gap: "3px",
4628
+ fontSize: "10px",
4629
+ padding: "1px 5px",
4630
+ fontWeight: 700
4631
+ } }, uiIcon("alert", 10, "#f59e0b"), "冷却中") : null, acc.email ? h("span", {
4632
+ style: {
4633
+ ...S.badgeTag,
4634
+ background: "var(--agy-badge-email-bg)",
4635
+ color: "var(--agy-badge-email-text)",
4636
+ borderColor: "var(--agy-badge-email-border)",
4637
+ gap: "4px",
4638
+ fontSize: "11px",
4639
+ padding: "1px 6px",
4640
+ whiteSpace: "nowrap"
4641
+ },
4642
+ title: acc.email
4643
+ }, uiIcon("mail", 10, "var(--agy-badge-email-text)"), acc.email) : null, isPrimary ? h("span", { style: {
3041
4644
  ...S.badgePrimary,
3042
- gap: "4px"
4645
+ gap: "3px",
4646
+ fontSize: "10.5px",
4647
+ padding: "1px 5px"
3043
4648
  } }, uiIcon("star", 10, "var(--agy-badge-primary-text)"), t("status.primary")) : null, isInUse ? h("span", {
3044
4649
  style: {
3045
4650
  ...S.badgeTag,
3046
4651
  background: "rgba(16, 185, 129, 0.12)",
3047
4652
  color: "#10b981",
3048
4653
  borderColor: "rgba(16, 185, 129, 0.45)",
3049
- gap: "4px",
4654
+ gap: "3px",
4655
+ fontSize: "10px",
4656
+ padding: "1px 5px",
3050
4657
  fontWeight: 700
3051
4658
  },
3052
4659
  title: t("status.inUse")
3053
- }, uiIcon("zap", 11, "#10b981"), t("status.inUse")) : null, acc.proxyUrl ? h("span", { style: {
4660
+ }, uiIcon("zap", 10, "#10b981"), t("status.inUse")) : null, acc.proxyUrl ? h("span", { style: {
3054
4661
  ...S.badgeTag,
3055
4662
  background: "var(--agy-badge-proxy-bg)",
3056
4663
  color: "var(--agy-badge-proxy-text)",
3057
4664
  borderColor: "var(--agy-badge-proxy-border)",
3058
- gap: "5px"
3059
- } }, uiIcon("globe", 11, "var(--agy-badge-proxy-text)"), t("status.proxy")) : null), h("div", { style: {
4665
+ gap: "4px",
4666
+ fontSize: "10px",
4667
+ padding: "1px 5px"
4668
+ } }, uiIcon("globe", 10, "var(--agy-badge-proxy-text)"), t("status.proxy")) : null), h("div", { style: {
3060
4669
  display: "flex",
3061
- gap: "5px",
3062
- alignItems: "center"
3063
- } }, allChildModels.length > 0 ? h("button", {
4670
+ gap: "4px",
4671
+ alignItems: "center",
4672
+ flexShrink: 0
4673
+ } }, h("button", {
3064
4674
  type: "button",
3065
4675
  className: "agy-btn",
3066
4676
  style: isExpanded ? {
3067
4677
  ...S.btnSmPrimary,
3068
- gap: "3px"
4678
+ gap: "3px",
4679
+ fontSize: "11px",
4680
+ padding: "2px 6px"
3069
4681
  } : {
3070
4682
  ...S.btnSm,
3071
- gap: "3px"
4683
+ gap: "3px",
4684
+ fontSize: "11px",
4685
+ padding: "2px 6px"
3072
4686
  },
3073
4687
  onClick: () => toggleExpand(acc.id)
3074
- }, isExpanded ? [uiIcon("chevronUp", 11), ` ${t("account.collapse")}`] : [uiIcon("chevronDown", 11), ` ${t("account.details")}`]) : null, h("button", {
4688
+ }, isExpanded ? [uiIcon("chevronUp", 10), ` ${t("account.collapse")}`] : [uiIcon("chevronDown", 10), ` ${t("account.otherModels")}`]), h("button", {
3075
4689
  type: "button",
3076
4690
  className: "agy-btn",
3077
4691
  style: {
3078
4692
  ...S.btnSm,
3079
- gap: "4px"
4693
+ gap: "3px",
4694
+ fontSize: "11px",
4695
+ padding: "2px 6px"
3080
4696
  },
3081
4697
  title: t("account.syncTitle"),
3082
4698
  disabled: isBusy,
3083
4699
  onClick: () => void refreshQuota(acc.id)
3084
- }, loadingAction === `refresh:${acc.id}` ? [renderSpinner(), t("account.syncing")] : [uiIcon("refresh", 11), ` ${t("account.sync")}`]), !isPrimary ? h("button", {
4700
+ }, loadingAction === `refresh:${acc.id}` ? [renderSpinner(), t("account.syncing")] : [uiIcon("refresh", 10), ` ${t("account.sync")}`]), !isPrimary ? h("button", {
3085
4701
  type: "button",
3086
4702
  className: "agy-btn",
3087
4703
  style: {
3088
4704
  ...S.btnSm,
3089
- gap: "4px"
4705
+ gap: "3px",
4706
+ fontSize: "11px",
4707
+ padding: "2px 6px"
3090
4708
  },
3091
4709
  disabled: isBusy,
3092
4710
  onClick: () => void setPrimary(acc.id)
3093
- }, loadingAction === `primary:${acc.id}` ? [renderSpinner(), t("account.settingPrimary")] : [uiIcon("star", 11), ` ${t("account.setPrimary")}`]) : null, h("button", {
4711
+ }, loadingAction === `primary:${acc.id}` ? [renderSpinner(), t("account.settingPrimary")] : [uiIcon("star", 10), ` ${t("account.setPrimary")}`]) : null, h("button", {
3094
4712
  type: "button",
3095
4713
  className: "agy-btn",
3096
4714
  style: isEditingProxy ? {
3097
4715
  ...S.btnSmPrimary,
3098
- gap: "4px"
4716
+ gap: "3px",
4717
+ fontSize: "11px",
4718
+ padding: "2px 6px"
3099
4719
  } : {
3100
4720
  ...S.btnSm,
3101
- gap: "4px"
4721
+ gap: "3px",
4722
+ fontSize: "11px",
4723
+ padding: "2px 6px"
3102
4724
  },
3103
4725
  disabled: isBusy,
3104
4726
  onClick: () => setEditingProxyId(isEditingProxy ? null : acc.id)
3105
- }, [uiIcon("globe", 11), ` ${t("proxy.button")}`]), accounts.length > 1 ? h("button", {
4727
+ }, [uiIcon("globe", 10), ` ${t("proxy.button")}`]), accounts.length > 1 ? h("button", {
3106
4728
  type: "button",
3107
4729
  className: "agy-btn",
3108
4730
  style: {
3109
4731
  ...S.btnDanger,
3110
- padding: "3px 7px"
4732
+ padding: "2px 6px",
4733
+ fontSize: "11px"
3111
4734
  },
3112
4735
  title: t("account.removeTitle"),
3113
4736
  "aria-label": t("account.removeTitle"),
3114
4737
  disabled: isBusy,
3115
4738
  onClick: () => void removeAccount(acc.id, displayAlias)
3116
- }, loadingAction === `remove:${acc.id}` ? renderSpinner() : uiIcon("trash", 12, "var(--agy-danger-text)")) : null)), h("div", { style: S.quotaBox }, renderQuotaBar("Gemini", "google", acc), renderQuotaBar("Claude", "anthropic", acc), renderQuotaBar("GPT-OSS", "openai", acc), isExpanded && allChildModels.length > 0 ? h("div", { style: {
3117
- marginTop: "8px",
3118
- paddingTop: "8px",
3119
- borderTop: "1px solid var(--agy-border-box)"
3120
- } }, h("div", { style: {
3121
- color: "var(--agy-text-secondary)",
3122
- marginBottom: "6px",
3123
- fontWeight: 600,
3124
- fontSize: "11px"
3125
- } }, t("account.models")), allChildModels.map(({ family, model }) => {
3126
- const frac = model.remainingFraction ?? 1;
3127
- const pct = Math.round(frac * 100);
3128
- const w = formatQuotaWindow(t, model.resetTime);
3129
- const pColor = pct <= 20 ? "var(--agy-quota-low-text)" : pct <= 50 ? "var(--agy-quota-med-text)" : "var(--agy-quota-high-text)";
3130
- return h("div", {
3131
- key: model.modelId,
3132
- className: "agy-submodel-row"
3133
- }, h("span", { style: {
3134
- fontWeight: 500,
3135
- color: "var(--agy-text-primary)"
3136
- } }, `[${family}] ${model.displayName || model.modelId}`), h("div", { style: {
4739
+ }, loadingAction === `remove:${acc.id}` ? renderSpinner() : uiIcon("trash", 11, "var(--agy-danger-text)")) : null)), h("div", {
4740
+ key: "agy-body",
4741
+ style: S.quotaBox
4742
+ }, renderQuotaBar("Gemini", "google", acc), h("button", {
4743
+ type: "button",
4744
+ className: "agy-btn",
4745
+ style: {
4746
+ width: "100%",
3137
4747
  display: "flex",
3138
4748
  alignItems: "center",
3139
- gap: "8px"
3140
- } }, h("span", { style: {
3141
- color: pColor,
3142
- fontWeight: 700
3143
- } }, `${pct}%`), w.resetText ? h("span", { style: {
3144
- color: "var(--agy-text-tertiary)",
3145
- fontSize: "10.5px"
3146
- } }, `↻ ${w.resetText}`) : null));
3147
- })) : null), hasCooldown ? h("div", { style: {
3148
- background: "var(--agy-warn-bg)",
3149
- border: "1px solid var(--agy-warn-border)",
3150
- borderRadius: "7px",
3151
- padding: "6px 10px",
3152
- color: "var(--agy-warn-text)",
3153
- fontSize: "11.5px",
3154
- fontWeight: 600,
3155
- marginTop: "8px",
3156
- display: "flex",
3157
- alignItems: "center",
3158
- justifyContent: "space-between"
3159
- } }, h("div", { style: {
4749
+ justifyContent: "center",
4750
+ gap: "5px",
4751
+ marginTop: "6px",
4752
+ padding: "5px 10px",
4753
+ fontSize: "11px",
4754
+ fontWeight: 600,
4755
+ cursor: "pointer",
4756
+ color: isExpanded ? "var(--dsw-alias-state-business-primary, #3b82f6)" : "var(--agy-text-secondary)",
4757
+ background: "var(--agy-bg-subbox)",
4758
+ border: "1px dashed var(--agy-border-box)",
4759
+ borderRadius: "6px"
4760
+ },
4761
+ onClick: () => toggleExpand(acc.id)
4762
+ }, uiIcon(isExpanded ? "chevronUp" : "chevronDown", 10), h("span", null, isExpanded ? t("account.collapseOthers") : t("account.expandOthers"))), isExpanded ? [
4763
+ renderQuotaBar("Claude", "anthropic", acc),
4764
+ renderQuotaBar("GPT-OSS", "openai", acc),
4765
+ allChildModels.length > 0 ? h("div", { style: {
4766
+ marginTop: "8px",
4767
+ paddingTop: "8px",
4768
+ borderTop: "1px solid var(--agy-border-box)"
4769
+ } }, h("div", { style: {
4770
+ color: "var(--agy-text-secondary)",
4771
+ marginBottom: "6px",
4772
+ fontWeight: 600,
4773
+ fontSize: "11px"
4774
+ } }, t("account.models")), allChildModels.map(({ family, model }) => {
4775
+ const frac = model.remainingFraction ?? 1;
4776
+ const pct = Math.round(frac * 100);
4777
+ const w = formatQuotaWindow(t, model.resetTime);
4778
+ const pColor = pct <= 20 ? "var(--agy-quota-low-text)" : pct <= 50 ? "var(--agy-quota-med-text)" : "var(--agy-quota-high-text)";
4779
+ return h("div", {
4780
+ key: `${family}-${model.modelId}`,
4781
+ className: "agy-submodel-row"
4782
+ }, h("span", { style: {
4783
+ fontWeight: 500,
4784
+ color: "var(--agy-text-primary)"
4785
+ } }, `[${family}] ${model.displayName || model.modelId}`), h("div", { style: {
4786
+ display: "flex",
4787
+ alignItems: "center",
4788
+ gap: "8px"
4789
+ } }, h("span", { style: {
4790
+ color: pColor,
4791
+ fontWeight: 700
4792
+ } }, `${pct}%`), w.resetText ? h("span", { style: {
4793
+ color: "var(--agy-text-tertiary)",
4794
+ fontSize: "10.5px"
4795
+ } }, `↻ ${w.resetText}`) : null));
4796
+ })) : null
4797
+ ] : null), hasCooldown ? h("div", {
4798
+ key: "cd-body",
4799
+ style: {
4800
+ background: "var(--agy-warn-bg)",
4801
+ border: "1px solid var(--agy-warn-border)",
4802
+ borderRadius: "7px",
4803
+ padding: "6px 10px",
4804
+ color: "var(--agy-warn-text)",
4805
+ fontSize: "11.5px",
4806
+ fontWeight: 600,
4807
+ marginTop: "8px",
4808
+ display: "flex",
4809
+ alignItems: "center",
4810
+ justifyContent: "space-between"
4811
+ }
4812
+ }, h("div", { style: {
3160
4813
  display: "flex",
3161
4814
  alignItems: "center",
3162
4815
  gap: "6px"
@@ -3172,13 +4825,16 @@ body.dark,
3172
4825
  },
3173
4826
  disabled: isBusy,
3174
4827
  onClick: () => void clearCooldown(acc.id)
3175
- }, loadingAction === `clearCooldown:${acc.id}` ? [renderSpinner(), ""] : [uiIcon("zap", 11, "var(--agy-warn-btn-text)"), ` ${t("account.clearCooldown")}`])) : null, isEditingProxy ? h("div", { style: {
3176
- marginTop: "8px",
3177
- padding: "10px 12px",
3178
- background: "var(--agy-bg-subbox)",
3179
- borderRadius: "7px",
3180
- border: "1px solid var(--agy-border-subbox)"
3181
- } }, h("div", { style: {
4828
+ }, loadingAction === `clearCooldown:${acc.id}` ? [renderSpinner(), ""] : [uiIcon("zap", 11, "var(--agy-warn-btn-text)"), ` ${t("account.clearCooldown")}`])) : null, isEditingProxy ? h("div", {
4829
+ key: "proxy-body",
4830
+ style: {
4831
+ marginTop: "8px",
4832
+ padding: "10px 12px",
4833
+ background: "var(--agy-bg-subbox)",
4834
+ borderRadius: "7px",
4835
+ border: "1px solid var(--agy-border-subbox)"
4836
+ }
4837
+ }, h("div", { style: {
3182
4838
  display: "flex",
3183
4839
  gap: "8px"
3184
4840
  } }, h("input", {
@@ -3211,23 +4867,40 @@ body.dark,
3211
4867
  role: "group",
3212
4868
  "aria-label": t("account.add")
3213
4869
  }, h("div", { style: {
4870
+ display: "flex",
4871
+ alignItems: "center",
4872
+ justifyContent: "space-between",
4873
+ marginBottom: "12px",
4874
+ flexWrap: "wrap",
4875
+ gap: "8px"
4876
+ } }, h("div", { style: {
3214
4877
  fontWeight: 700,
3215
4878
  fontSize: "13.5px",
3216
- marginBottom: "10px",
3217
4879
  display: "flex",
3218
4880
  alignItems: "center",
3219
4881
  gap: "6px",
3220
4882
  color: "var(--agy-text-primary)"
3221
- } }, uiIcon("plus", 13, "var(--agy-btn-primary-bg)"), t("account.add")), !flowActive ? h("div", null, h("div", { style: {
4883
+ } }, uiIcon("plus", 13, "var(--agy-btn-primary-bg)"), t("account.add"))), !flowActive ? h("div", null, h("div", { style: {
3222
4884
  display: "flex",
3223
- gap: "8px"
4885
+ flexDirection: "column",
4886
+ gap: "8px",
4887
+ marginBottom: "12px"
3224
4888
  } }, h("input", {
3225
4889
  style: S.input,
3226
4890
  value: aliasInput,
3227
4891
  placeholder: t("account.aliasPlaceholder"),
3228
4892
  "aria-label": t("aria.aliasInput"),
3229
4893
  onChange: (e) => setAliasInput(e.target.value)
3230
- }), h("button", {
4894
+ }), h("input", {
4895
+ style: S.input,
4896
+ value: addProxyInput,
4897
+ placeholder: t("account.proxyPlaceholder"),
4898
+ "aria-label": t("aria.proxyInput"),
4899
+ onChange: (e) => setAddProxyInput(e.target.value)
4900
+ })), h("div", { style: {
4901
+ display: "flex",
4902
+ gap: "8px"
4903
+ } }, h("button", {
3231
4904
  type: "button",
3232
4905
  className: "agy-btn",
3233
4906
  style: {
@@ -3303,8 +4976,8 @@ body.dark,
3303
4976
  display: "flex",
3304
4977
  alignItems: "center",
3305
4978
  justifyContent: "center"
3306
- } }, h("style", null, GLOBAL_CSS), h("span", { style: S.muted }, [renderSpinner(), t("status.loading")]));
3307
- return h("div", { style: S.container }, h("style", null, GLOBAL_CSS), h("div", { style: S.headerCard }, h("div", { style: {
4979
+ } }, h("span", { style: S.muted }, [renderSpinner(), t("status.loading")]));
4980
+ return h("div", { style: S.container }, h("div", { style: S.headerCard }, h("div", { style: {
3308
4981
  display: "flex",
3309
4982
  alignItems: "center",
3310
4983
  gap: "8px"
@@ -3333,7 +5006,21 @@ body.dark,
3333
5006
  },
3334
5007
  disabled: isBusy,
3335
5008
  onClick: () => void refreshQuota()
3336
- }, loadingAction === "refresh:all" ? [renderSpinner(), t("header.refreshing")] : [uiIcon("refresh", 12, "var(--agy-text-btn)"), ` ${t("header.refreshQuota")}`]))), renderToastBanner(), addAccountSection, renderedAccountCards, h("div", { style: {
5009
+ }, loadingAction === "refresh:all" ? [renderSpinner(), t("header.refreshing")] : [uiIcon("refresh", 12, "var(--agy-text-btn)"), ` ${t("header.refreshQuota")}`]))), renderToastBanner(), addAccountSection, h("div", { style: {
5010
+ display: "flex",
5011
+ alignItems: "center",
5012
+ justifyContent: "space-between",
5013
+ marginBottom: "8px",
5014
+ padding: "0 2px"
5015
+ } }, h("div", { style: {
5016
+ display: "flex",
5017
+ alignItems: "center",
5018
+ gap: "6px"
5019
+ } }, h("span", { style: {
5020
+ fontWeight: 700,
5021
+ fontSize: "12.5px",
5022
+ color: "var(--agy-text-primary)"
5023
+ } }, `账号池 (${accounts.length})`))), renderedAccountCards, h("div", { style: {
3337
5024
  marginTop: "16px",
3338
5025
  paddingTop: "12px",
3339
5026
  borderTop: "1px solid var(--agy-border-divider)"
@@ -3436,9 +5123,19 @@ body.dark,
3436
5123
  };
3437
5124
  /** Modal dialog container rendered into document.body */
3438
5125
  const AgyModalDialog = () => {
5126
+ const instanceIdRef = useRef(null);
5127
+ if (instanceIdRef.current === null) instanceIdRef.current = "modal_" + Math.random().toString(36).slice(2);
5128
+ const isPrimary = registerModalInstance(instanceIdRef.current);
5129
+ useEffect(() => {
5130
+ const id = instanceIdRef.current;
5131
+ return () => {
5132
+ unregisterModalInstance(id);
5133
+ };
5134
+ }, []);
3439
5135
  const t = useAgyTranslation();
3440
5136
  const [isOpen, setIsOpen] = useState(agyModalStore.open);
3441
5137
  useEffect(() => {
5138
+ injectGlobalStyles();
3442
5139
  return agyModalStore.subscribe(() => {
3443
5140
  setIsOpen(agyModalStore.open);
3444
5141
  });
@@ -3454,7 +5151,7 @@ body.dark,
3454
5151
  win.addEventListener?.("keydown", onKey, true);
3455
5152
  return () => win.removeEventListener?.("keydown", onKey, true);
3456
5153
  }, [isOpen]);
3457
- if (!isOpen) return null;
5154
+ if (!isOpen || !isPrimary) return null;
3458
5155
  return h("div", {
3459
5156
  className: "agy-modal-backdrop",
3460
5157
  onClick: (e) => {
@@ -3512,39 +5209,26 @@ body.dark,
3512
5209
  /** Session Header badge in chat toolbar */
3513
5210
  const AgySessionStatus = () => {
3514
5211
  const t = useAgyTranslation();
3515
- const [status, setStatus] = useState(statusCache);
5212
+ const { status } = useAgyStatus();
5213
+ const [, setTimeTick] = useState(0);
3516
5214
  useEffect(() => {
3517
- let alive = true;
3518
- let lastJson = "";
3519
- const tick = async () => {
3520
- const st = await getStatus();
3521
- if (!alive || !st) return;
3522
- const json = JSON.stringify(st);
3523
- if (json !== lastJson) {
3524
- lastJson = json;
3525
- setStatus(st);
3526
- }
3527
- };
3528
- tick();
3529
- const timer = setInterval(tick, 3e3);
3530
- return () => {
3531
- alive = false;
3532
- clearInterval(timer);
3533
- };
5215
+ const timer = setInterval(() => setTimeTick((n) => n + 1), 3e4);
5216
+ return () => clearInterval(timer);
3534
5217
  }, []);
3535
5218
  const pool = status?.pool;
3536
5219
  const accounts = pool?.accounts ?? [];
3537
5220
  const rawActiveId = pool?.activeAccountId ?? pool?.lastActiveAccountId ?? pool?.activeAccountIds?.google ?? pool?.primaryAccountId;
3538
5221
  const activeAcc = accounts.find((a) => a.id === rawActiveId && a.enabled && !a.authRequired) ?? accounts.find((a) => a.enabled && !a.authRequired) ?? accounts[0];
3539
- const activeHasCooldown = activeAcc ? Object.entries(activeAcc.cooldowns).some(([, cd]) => cd && cd.cooldownUntil > Date.now()) : false;
3540
- const allInCooldown = accounts.length > 0 && accounts.every((a) => !a.enabled || a.authRequired || Object.entries(a.cooldowns).some(([, cd]) => cd && cd.cooldownUntil > Date.now()));
5222
+ const activeHasCooldown = activeAcc ? Object.entries(activeAcc.cooldowns ?? {}).some(([, cd]) => cd && cd.cooldownUntil > Date.now()) : false;
5223
+ const allInCooldown = accounts.length > 0 && accounts.every((a) => !a.enabled || a.authRequired || Object.entries(a.cooldowns ?? {}).some(([, cd]) => cd && cd.cooldownUntil > Date.now()));
3541
5224
  const isAuthed = status?.auth?.phase === "ok" || accounts.length > 0;
3542
5225
  const color = status === null ? "#64748b" : status.dormantReason ? "#f59e0b" : allInCooldown ? "#f59e0b" : activeHasCooldown ? "#f59e0b" : isAuthed ? "#10b981" : "#f59e0b";
3543
5226
  const legacyDefault = activeAcc && activeAcc.defaultAlias === void 0 && activeAcc.systemHome && activeAcc.alias === "主账号 (系统登录)";
3544
5227
  const displayAlias = activeAcc ? activeAcc.defaultAlias || legacyDefault ? activeAcc.systemHome ? t("account.defaultAlias") : t("account.aliasDefault", { number: accounts.indexOf(activeAcc) + 1 }) : activeAcc.alias : "";
3545
- const activeDetail = activeAcc ? ` · ${t("status.inUse")}: ${displayAlias}${activeAcc.email ? ` (${activeAcc.email})` : ""}` : "";
3546
- const badgeTitle = `${t("header.badgeTitle", { count: accounts.length })}${activeDetail}`;
3547
- const buttonLabel = displayAlias ? `AGY · ${displayAlias}` : `AGY (${accounts.length})`;
5228
+ const activeEligible = activeAcc !== void 0 && activeAcc.enabled && !activeAcc.authRequired;
5229
+ const activeDetail = activeAcc !== void 0 && activeEligible ? ` · ${t("status.inUse")}: ${displayAlias}${activeAcc.email ? ` (${activeAcc.email})` : ""}` : "";
5230
+ const badgeTitle = status === null ? t("header.badgeLoading") : `${t("header.badgeTitle", { count: accounts.length })}${activeDetail}`;
5231
+ const buttonLabel = status === null ? "AGY" : displayAlias ? `AGY · ${displayAlias}` : `AGY (${accounts.length})`;
3548
5232
  const badge = h("button", {
3549
5233
  type: "button",
3550
5234
  title: badgeTitle,
@@ -3593,8 +5277,10 @@ body.dark,
3593
5277
  }, AgySettingsSection);
3594
5278
  });
3595
5279
  installAgyToolView(ctx);
3596
- installAutoExpandReasoning();
3597
- installMermaidRendering();
5280
+ ctx.effect(() => installAutoExpandReasoning());
5281
+ ctx.effect(() => installMermaidRendering());
5282
+ ctx.effect(() => installImageEnhancer());
5283
+ ctx.effect(() => installDecisionChips());
3598
5284
  }
3599
5285
  //#endregion
3600
5286
  exports.apply = apply;