dsh-agy-link 0.3.4 → 0.4.5
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/CHANGELOG.md +99 -1
- package/README.md +1 -1
- package/dist/client.js +1045 -123
- package/dist/index.js +25212 -304
- package/docs/adr-012-account-pool.md +54 -0
- package/docs/market-pr-draft.md +38 -28
- package/docs/spec-account-pool.md +229 -0
- package/package.json +3 -2
package/dist/client.js
CHANGED
|
@@ -5,81 +5,397 @@ window.__ModuleLoader__.load({
|
|
|
5
5
|
var exports = module.exports;
|
|
6
6
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
7
7
|
//#region src/client/index.ts
|
|
8
|
-
const { createElement: h, useState, useEffect } = require("react");
|
|
8
|
+
const { createElement: h, useState, useEffect, useRef } = require("react");
|
|
9
|
+
const reactDom = require("react-dom");
|
|
9
10
|
const win = globalThis;
|
|
11
|
+
const bodyEl = win.document?.body ?? null;
|
|
12
|
+
const portalToBody = (node) => {
|
|
13
|
+
if (bodyEl && reactDom && typeof reactDom.createPortal === "function") return reactDom.createPortal(node, bodyEl);
|
|
14
|
+
return node;
|
|
15
|
+
};
|
|
10
16
|
const name = "dsh-agy-link-client";
|
|
11
17
|
const inject = ["slots"];
|
|
12
18
|
const base = win.location?.origin ?? "";
|
|
19
|
+
let statusCache = null;
|
|
13
20
|
async function getStatus() {
|
|
14
21
|
try {
|
|
15
22
|
const res = await win.fetch?.(base + "/plugins/agy-link/status");
|
|
16
23
|
if (!res || !res.ok) return null;
|
|
17
|
-
|
|
24
|
+
const payload = await res.json();
|
|
25
|
+
statusCache = payload;
|
|
26
|
+
return payload;
|
|
18
27
|
} catch {
|
|
19
28
|
return null;
|
|
20
29
|
}
|
|
21
30
|
}
|
|
22
31
|
async function postJson(path, body) {
|
|
23
32
|
try {
|
|
24
|
-
|
|
33
|
+
const res = await win.fetch?.(base + path, {
|
|
25
34
|
method: "POST",
|
|
26
35
|
headers: { "Content-Type": "application/json" },
|
|
27
36
|
body: JSON.stringify(body)
|
|
28
|
-
})
|
|
37
|
+
});
|
|
38
|
+
if (!res) return null;
|
|
39
|
+
try {
|
|
40
|
+
return await res.json();
|
|
41
|
+
} catch {
|
|
42
|
+
return { ok: res.ok };
|
|
43
|
+
}
|
|
29
44
|
} catch {
|
|
30
45
|
return null;
|
|
31
46
|
}
|
|
32
47
|
}
|
|
48
|
+
/** Global modal state for opening Antigravity console dialog from footer shortcut or header status */
|
|
49
|
+
const agyModalStore = {
|
|
50
|
+
open: false,
|
|
51
|
+
listeners: /* @__PURE__ */ new Set(),
|
|
52
|
+
setOpen(v) {
|
|
53
|
+
agyModalStore.open = v;
|
|
54
|
+
agyModalStore.listeners.forEach((fn) => fn());
|
|
55
|
+
},
|
|
56
|
+
subscribe(fn) {
|
|
57
|
+
agyModalStore.listeners.add(fn);
|
|
58
|
+
return () => {
|
|
59
|
+
agyModalStore.listeners.delete(fn);
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
/** Smart parser for 5-hour rolling window vs weekly cap */
|
|
64
|
+
function formatQuotaWindow(resetTimeStr, remainingFraction) {
|
|
65
|
+
if (!resetTimeStr) return {
|
|
66
|
+
windowType: "5小时额度",
|
|
67
|
+
resetText: "",
|
|
68
|
+
countdownText: "",
|
|
69
|
+
isWeeklyLockout: false
|
|
70
|
+
};
|
|
71
|
+
try {
|
|
72
|
+
const d = new Date(resetTimeStr);
|
|
73
|
+
const diffMs = d.getTime() - Date.now();
|
|
74
|
+
const timeStr = `${d.getHours().toString().padStart(2, "0")}:${d.getMinutes().toString().padStart(2, "0")}`;
|
|
75
|
+
const isWeekly = diffMs > 216e5;
|
|
76
|
+
const windowType = isWeekly ? "周额度" : "5小时额度";
|
|
77
|
+
let countdown = "";
|
|
78
|
+
if (diffMs > 0) {
|
|
79
|
+
const totalMins = Math.ceil(diffMs / 6e4);
|
|
80
|
+
const hours = Math.floor(totalMins / 60);
|
|
81
|
+
const mins = totalMins % 60;
|
|
82
|
+
if (hours >= 24) countdown = `${Math.floor(hours / 24)}天${hours % 24}小时后`;
|
|
83
|
+
else if (hours > 0) countdown = `${hours}小时${mins}分后`;
|
|
84
|
+
else countdown = `${mins}分钟后`;
|
|
85
|
+
} else countdown = "即将重置";
|
|
86
|
+
let resetText = "";
|
|
87
|
+
if (isWeekly) resetText = `${(d.getMonth() + 1).toString().padStart(2, "0")}/${d.getDate().toString().padStart(2, "0")} ${timeStr} (${countdown})`;
|
|
88
|
+
else resetText = `${timeStr} (${countdown})`;
|
|
89
|
+
return {
|
|
90
|
+
windowType,
|
|
91
|
+
resetText,
|
|
92
|
+
countdownText: countdown,
|
|
93
|
+
isWeeklyLockout: isWeekly && (remainingFraction ?? 1) <= .05
|
|
94
|
+
};
|
|
95
|
+
} catch {
|
|
96
|
+
return {
|
|
97
|
+
windowType: "5小时额度",
|
|
98
|
+
resetText: "",
|
|
99
|
+
countdownText: "",
|
|
100
|
+
isWeeklyLockout: false
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
const GLOBAL_CSS = `
|
|
105
|
+
@keyframes agy-spin {
|
|
106
|
+
0% { transform: rotate(0deg); }
|
|
107
|
+
100% { transform: rotate(360deg); }
|
|
108
|
+
}
|
|
109
|
+
.agy-spinner {
|
|
110
|
+
display: inline-block;
|
|
111
|
+
width: 12px;
|
|
112
|
+
height: 12px;
|
|
113
|
+
border: 2px solid currentColor;
|
|
114
|
+
border-top-color: transparent;
|
|
115
|
+
border-radius: 50%;
|
|
116
|
+
animation: agy-spin 0.75s linear infinite;
|
|
117
|
+
vertical-align: -2px;
|
|
118
|
+
margin-right: 5px;
|
|
119
|
+
}
|
|
120
|
+
.agy-pulse-dot {
|
|
121
|
+
display: inline-block;
|
|
122
|
+
width: 8px;
|
|
123
|
+
height: 8px;
|
|
124
|
+
border-radius: 50%;
|
|
125
|
+
}
|
|
126
|
+
.agy-card-hover {
|
|
127
|
+
transition: border-color 0.2s ease, box-shadow 0.2s ease;
|
|
128
|
+
}
|
|
129
|
+
.agy-card-hover:hover {
|
|
130
|
+
border-color: rgba(128,128,128,0.35) !important;
|
|
131
|
+
}
|
|
132
|
+
.agy-btn {
|
|
133
|
+
transition: all 0.15s ease;
|
|
134
|
+
}
|
|
135
|
+
.agy-btn:hover:not(:disabled) {
|
|
136
|
+
filter: brightness(1.12);
|
|
137
|
+
transform: translateY(-0.5px);
|
|
138
|
+
}
|
|
139
|
+
.agy-btn:active:not(:disabled) {
|
|
140
|
+
transform: translateY(0.5px);
|
|
141
|
+
}
|
|
142
|
+
.agy-progress-fill {
|
|
143
|
+
transition: width 0.4s cubic-bezier(0.16, 1, 0.3, 1);
|
|
144
|
+
}
|
|
145
|
+
.agy-modal-backdrop {
|
|
146
|
+
position: fixed;
|
|
147
|
+
inset: 0;
|
|
148
|
+
z-index: 9999;
|
|
149
|
+
background: rgba(0, 0, 0, 0.55);
|
|
150
|
+
backdrop-filter: blur(4px);
|
|
151
|
+
display: flex;
|
|
152
|
+
align-items: center;
|
|
153
|
+
justify-content: center;
|
|
154
|
+
padding: 16px;
|
|
155
|
+
box-sizing: border-box;
|
|
156
|
+
}
|
|
157
|
+
.agy-modal-panel {
|
|
158
|
+
position: relative;
|
|
159
|
+
width: 100%;
|
|
160
|
+
max-width: 640px;
|
|
161
|
+
max-height: min(820px, calc(100vh - 48px));
|
|
162
|
+
background: var(--dsw-specific-menu, #1c1d22);
|
|
163
|
+
border: 1px solid var(--dsw-alias-border-l2, rgba(128,128,128,0.25));
|
|
164
|
+
border-radius: 12px;
|
|
165
|
+
box-shadow: 0 20px 60px rgba(0,0,0,0.5);
|
|
166
|
+
display: flex;
|
|
167
|
+
flex-direction: column;
|
|
168
|
+
overflow: hidden;
|
|
169
|
+
color: inherit;
|
|
170
|
+
font-family: inherit;
|
|
171
|
+
}
|
|
172
|
+
.agy-submodel-row {
|
|
173
|
+
display: flex;
|
|
174
|
+
align-items: center;
|
|
175
|
+
justify-content: space-between;
|
|
176
|
+
padding: 3px 6px;
|
|
177
|
+
font-size: 10.5px;
|
|
178
|
+
border-radius: 4px;
|
|
179
|
+
background: rgba(128,128,128,0.06);
|
|
180
|
+
margin: 2px 0;
|
|
181
|
+
}
|
|
182
|
+
`;
|
|
33
183
|
const S = {
|
|
34
|
-
|
|
184
|
+
container: {
|
|
185
|
+
lineHeight: 1.5,
|
|
186
|
+
fontSize: "13px",
|
|
187
|
+
fontFamily: "inherit",
|
|
188
|
+
color: "inherit"
|
|
189
|
+
},
|
|
190
|
+
headerCard: {
|
|
35
191
|
display: "flex",
|
|
36
192
|
alignItems: "center",
|
|
37
|
-
|
|
38
|
-
|
|
193
|
+
justifyContent: "space-between",
|
|
194
|
+
padding: "12px 14px",
|
|
195
|
+
background: "rgba(128,128,128,0.06)",
|
|
196
|
+
border: "1px solid rgba(128,128,128,0.18)",
|
|
197
|
+
borderRadius: "8px",
|
|
198
|
+
marginBottom: "12px"
|
|
199
|
+
},
|
|
200
|
+
badgePrimary: {
|
|
201
|
+
display: "inline-flex",
|
|
202
|
+
alignItems: "center",
|
|
203
|
+
gap: "3px",
|
|
204
|
+
background: "rgba(59,130,246,0.15)",
|
|
205
|
+
color: "#3b82f6",
|
|
206
|
+
border: "1px solid rgba(59,130,246,0.35)",
|
|
207
|
+
padding: "1px 7px",
|
|
208
|
+
borderRadius: "5px",
|
|
209
|
+
fontSize: "11px",
|
|
210
|
+
fontWeight: 600
|
|
211
|
+
},
|
|
212
|
+
badgeTag: {
|
|
213
|
+
display: "inline-flex",
|
|
214
|
+
alignItems: "center",
|
|
215
|
+
gap: "3px",
|
|
216
|
+
background: "rgba(128,128,128,0.12)",
|
|
217
|
+
color: "inherit",
|
|
218
|
+
border: "1px solid rgba(128,128,128,0.22)",
|
|
219
|
+
padding: "1px 6px",
|
|
220
|
+
borderRadius: "4px",
|
|
221
|
+
fontSize: "11px"
|
|
222
|
+
},
|
|
223
|
+
card: {
|
|
224
|
+
background: "rgba(128,128,128,0.04)",
|
|
225
|
+
border: "1px solid rgba(128,128,128,0.18)",
|
|
226
|
+
borderRadius: "8px",
|
|
227
|
+
padding: "12px 14px",
|
|
228
|
+
marginBottom: "10px"
|
|
229
|
+
},
|
|
230
|
+
cardPrimary: {
|
|
231
|
+
background: "rgba(59,130,246,0.03)",
|
|
232
|
+
border: "1px solid rgba(59,130,246,0.32)",
|
|
233
|
+
borderRadius: "8px",
|
|
234
|
+
padding: "12px 14px",
|
|
235
|
+
marginBottom: "10px"
|
|
236
|
+
},
|
|
237
|
+
quotaBox: {
|
|
238
|
+
background: "rgba(0,0,0,0.14)",
|
|
239
|
+
border: "1px solid rgba(128,128,128,0.14)",
|
|
240
|
+
borderRadius: "6px",
|
|
241
|
+
padding: "8px 12px",
|
|
242
|
+
marginTop: "8px"
|
|
243
|
+
},
|
|
244
|
+
progressBarBg: {
|
|
245
|
+
flex: "1",
|
|
246
|
+
height: "6px",
|
|
247
|
+
borderRadius: "3px",
|
|
248
|
+
background: "rgba(128,128,128,0.2)",
|
|
249
|
+
overflow: "hidden",
|
|
250
|
+
margin: "0 10px"
|
|
39
251
|
},
|
|
40
252
|
btn: {
|
|
253
|
+
display: "inline-flex",
|
|
254
|
+
alignItems: "center",
|
|
255
|
+
justifyContent: "center",
|
|
41
256
|
padding: "4px 10px",
|
|
42
257
|
borderRadius: "6px",
|
|
43
|
-
border: "1px solid rgba(128,128,128,0.
|
|
44
|
-
background: "rgba(128,128,128,0.
|
|
258
|
+
border: "1px solid rgba(128,128,128,0.3)",
|
|
259
|
+
background: "rgba(128,128,128,0.1)",
|
|
45
260
|
color: "inherit",
|
|
46
261
|
cursor: "pointer",
|
|
47
|
-
fontSize: "12px"
|
|
262
|
+
fontSize: "12px",
|
|
263
|
+
fontWeight: 500
|
|
48
264
|
},
|
|
49
265
|
btnPrimary: {
|
|
50
|
-
|
|
266
|
+
display: "inline-flex",
|
|
267
|
+
alignItems: "center",
|
|
268
|
+
justifyContent: "center",
|
|
269
|
+
padding: "5px 12px",
|
|
51
270
|
borderRadius: "6px",
|
|
52
|
-
border: "1px solid rgba(
|
|
53
|
-
background: "rgba(
|
|
271
|
+
border: "1px solid rgba(59,130,246,0.5)",
|
|
272
|
+
background: "rgba(59,130,246,0.22)",
|
|
273
|
+
color: "#3b82f6",
|
|
274
|
+
cursor: "pointer",
|
|
275
|
+
fontSize: "12px",
|
|
276
|
+
fontWeight: 600
|
|
277
|
+
},
|
|
278
|
+
btnDanger: {
|
|
279
|
+
display: "inline-flex",
|
|
280
|
+
alignItems: "center",
|
|
281
|
+
justifyContent: "center",
|
|
282
|
+
padding: "3px 8px",
|
|
283
|
+
borderRadius: "5px",
|
|
284
|
+
border: "1px solid rgba(239,68,68,0.35)",
|
|
285
|
+
background: "rgba(239,68,68,0.1)",
|
|
286
|
+
color: "#ef4444",
|
|
287
|
+
cursor: "pointer",
|
|
288
|
+
fontSize: "11px",
|
|
289
|
+
fontWeight: 500
|
|
290
|
+
},
|
|
291
|
+
btnSm: {
|
|
292
|
+
display: "inline-flex",
|
|
293
|
+
alignItems: "center",
|
|
294
|
+
justifyContent: "center",
|
|
295
|
+
padding: "3px 8px",
|
|
296
|
+
borderRadius: "5px",
|
|
297
|
+
border: "1px solid rgba(128,128,128,0.25)",
|
|
298
|
+
background: "rgba(128,128,128,0.08)",
|
|
54
299
|
color: "inherit",
|
|
55
300
|
cursor: "pointer",
|
|
56
|
-
fontSize: "
|
|
301
|
+
fontSize: "11px",
|
|
302
|
+
fontWeight: 500
|
|
303
|
+
},
|
|
304
|
+
btnSmPrimary: {
|
|
305
|
+
display: "inline-flex",
|
|
306
|
+
alignItems: "center",
|
|
307
|
+
justifyContent: "center",
|
|
308
|
+
padding: "3px 8px",
|
|
309
|
+
borderRadius: "5px",
|
|
310
|
+
border: "1px solid rgba(59,130,246,0.35)",
|
|
311
|
+
background: "rgba(59,130,246,0.15)",
|
|
312
|
+
color: "#3b82f6",
|
|
313
|
+
cursor: "pointer",
|
|
314
|
+
fontSize: "11px",
|
|
315
|
+
fontWeight: 600
|
|
316
|
+
},
|
|
317
|
+
segGroup: {
|
|
318
|
+
display: "inline-flex",
|
|
319
|
+
background: "rgba(128,128,128,0.12)",
|
|
320
|
+
borderRadius: "6px",
|
|
321
|
+
padding: "2px",
|
|
322
|
+
border: "1px solid rgba(128,128,128,0.18)"
|
|
323
|
+
},
|
|
324
|
+
segBtn: {
|
|
325
|
+
padding: "3px 9px",
|
|
326
|
+
borderRadius: "4px",
|
|
327
|
+
border: "none",
|
|
328
|
+
background: "transparent",
|
|
329
|
+
color: "inherit",
|
|
330
|
+
cursor: "pointer",
|
|
331
|
+
fontSize: "11px",
|
|
332
|
+
fontWeight: 500,
|
|
333
|
+
opacity: .75
|
|
334
|
+
},
|
|
335
|
+
segBtnActive: {
|
|
336
|
+
padding: "3px 9px",
|
|
337
|
+
borderRadius: "4px",
|
|
338
|
+
border: "none",
|
|
339
|
+
background: "rgba(59,130,246,0.28)",
|
|
340
|
+
color: "#3b82f6",
|
|
341
|
+
cursor: "pointer",
|
|
342
|
+
fontSize: "11px",
|
|
343
|
+
fontWeight: 600,
|
|
344
|
+
opacity: 1
|
|
57
345
|
},
|
|
58
346
|
input: {
|
|
59
347
|
flex: "1",
|
|
60
|
-
padding: "6px
|
|
348
|
+
padding: "6px 10px",
|
|
61
349
|
borderRadius: "6px",
|
|
62
|
-
border: "1px solid rgba(128,128,128,0.
|
|
63
|
-
background: "
|
|
350
|
+
border: "1px solid rgba(128,128,128,0.3)",
|
|
351
|
+
background: "rgba(0,0,0,0.1)",
|
|
64
352
|
color: "inherit",
|
|
65
|
-
fontSize: "12px"
|
|
353
|
+
fontSize: "12px",
|
|
354
|
+
outline: "none"
|
|
66
355
|
},
|
|
67
356
|
muted: {
|
|
68
357
|
color: "#9aa0a6",
|
|
358
|
+
fontSize: "11px"
|
|
359
|
+
},
|
|
360
|
+
noticeBanner: {
|
|
361
|
+
display: "flex",
|
|
362
|
+
alignItems: "center",
|
|
363
|
+
justifyContent: "space-between",
|
|
364
|
+
padding: "8px 12px",
|
|
365
|
+
borderRadius: "6px",
|
|
366
|
+
marginBottom: "10px",
|
|
69
367
|
fontSize: "12px"
|
|
368
|
+
},
|
|
369
|
+
authModal: {
|
|
370
|
+
background: "rgba(59,130,246,0.06)",
|
|
371
|
+
border: "1px solid rgba(59,130,246,0.3)",
|
|
372
|
+
borderRadius: "8px",
|
|
373
|
+
padding: "12px 14px",
|
|
374
|
+
marginBottom: "12px"
|
|
70
375
|
}
|
|
71
376
|
};
|
|
72
377
|
function apply(ctx) {
|
|
73
|
-
const AgySettingsSection = () => {
|
|
74
|
-
const [status, setStatus] = useState(
|
|
75
|
-
const [
|
|
76
|
-
const [
|
|
77
|
-
const [
|
|
378
|
+
const AgySettingsSection = (props) => {
|
|
379
|
+
const [status, setStatus] = useState(statusCache);
|
|
380
|
+
const [aliasInput, setAliasInput] = useState("");
|
|
381
|
+
const [proxyInputs, setProxyInputs] = useState({});
|
|
382
|
+
const [editingProxyId, setEditingProxyId] = useState(null);
|
|
383
|
+
const [addingAccount, setAddingAccount] = useState(false);
|
|
384
|
+
const [authCodeInput, setAuthCodeInput] = useState("");
|
|
385
|
+
const [loadingAction, setLoadingAction] = useState(null);
|
|
386
|
+
const [toast, setToast] = useState(null);
|
|
387
|
+
const [expandedModels, setExpandedModels] = useState({});
|
|
78
388
|
useEffect(() => {
|
|
79
389
|
let alive = true;
|
|
390
|
+
let lastJson = "";
|
|
80
391
|
const tick = async () => {
|
|
81
392
|
const st = await getStatus();
|
|
82
|
-
if (alive
|
|
393
|
+
if (!alive || !st) return;
|
|
394
|
+
const json = JSON.stringify(st);
|
|
395
|
+
if (json !== lastJson) {
|
|
396
|
+
lastJson = json;
|
|
397
|
+
setStatus(st);
|
|
398
|
+
}
|
|
83
399
|
};
|
|
84
400
|
tick();
|
|
85
401
|
const timer = setInterval(tick, 3e3);
|
|
@@ -88,138 +404,732 @@ window.__ModuleLoader__.load({
|
|
|
88
404
|
clearInterval(timer);
|
|
89
405
|
};
|
|
90
406
|
}, []);
|
|
407
|
+
const flowStartedRef = useRef(false);
|
|
408
|
+
useEffect(() => {
|
|
409
|
+
if (!addingAccount || !flowStartedRef.current) return;
|
|
410
|
+
const pa = status?.poolAuth;
|
|
411
|
+
if (!pa) return;
|
|
412
|
+
if (pa.phase === "done") {
|
|
413
|
+
flowStartedRef.current = false;
|
|
414
|
+
setAddingAccount(false);
|
|
415
|
+
setAuthCodeInput("");
|
|
416
|
+
setAliasInput("");
|
|
417
|
+
showToast(`✅ ${pa.message || "账号已激活入池"}`, "success");
|
|
418
|
+
} else if (pa.phase === "failed") {
|
|
419
|
+
flowStartedRef.current = false;
|
|
420
|
+
showToast(`❌ ${pa.message || "授权失败"}`, "error");
|
|
421
|
+
}
|
|
422
|
+
}, [
|
|
423
|
+
addingAccount,
|
|
424
|
+
status?.poolAuth?.phase,
|
|
425
|
+
status?.poolAuth?.message
|
|
426
|
+
]);
|
|
427
|
+
const showToast = (text, type = "info") => {
|
|
428
|
+
setToast({
|
|
429
|
+
text,
|
|
430
|
+
type,
|
|
431
|
+
id: Date.now()
|
|
432
|
+
});
|
|
433
|
+
setTimeout(() => {
|
|
434
|
+
setToast((prev) => prev?.text === text ? null : prev);
|
|
435
|
+
}, 6e3);
|
|
436
|
+
};
|
|
91
437
|
const refresh = async () => {
|
|
92
438
|
setStatus(await getStatus());
|
|
93
439
|
};
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
440
|
+
const handleBeginAddAccount = async () => {
|
|
441
|
+
setLoadingAction("pool:beginAdd");
|
|
442
|
+
const res = await postJson("/plugins/agy-link/pool/begin-add", { alias: aliasInput.trim() || `备用 Google 账号 ${(status?.pool?.accounts?.length ?? 1) + 1}` });
|
|
443
|
+
setLoadingAction(null);
|
|
444
|
+
if (res && res.ok) {
|
|
445
|
+
flowStartedRef.current = true;
|
|
446
|
+
await refresh();
|
|
447
|
+
if (res.browserOpened) showToast("🌐 浏览器已打开 Google 授权页,完成授权后这里会自动完成", "info");
|
|
448
|
+
else showToast("⚠️ 无法自动打开浏览器,请点击下方链接手动完成授权", "warn");
|
|
449
|
+
} else showToast(`启动 Google 授权失败: ${res?.message || "请检查网络或代理配置"}`, "error");
|
|
100
450
|
};
|
|
101
|
-
const
|
|
102
|
-
if (
|
|
103
|
-
|
|
104
|
-
await postJson("/plugins/agy-link/
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
451
|
+
const handleCompleteAddAccount = async () => {
|
|
452
|
+
if (!authCodeInput.trim()) return;
|
|
453
|
+
setLoadingAction("pool:completeAdd");
|
|
454
|
+
const res = await postJson("/plugins/agy-link/pool/complete-add", { code: authCodeInput.trim() });
|
|
455
|
+
setLoadingAction(null);
|
|
456
|
+
if (res && res.ok) {
|
|
457
|
+
flowStartedRef.current = false;
|
|
458
|
+
setAuthCodeInput("");
|
|
459
|
+
setAliasInput("");
|
|
460
|
+
setAddingAccount(false);
|
|
461
|
+
await refresh();
|
|
462
|
+
showToast(`✅ ${res.message || "成功添加并激活 Google 账号"}`, "success");
|
|
463
|
+
} else showToast(`❌ ${res?.message || res?.error || "授权码验证失败"}`, "error");
|
|
464
|
+
};
|
|
465
|
+
const handleCancelAddAccount = async () => {
|
|
466
|
+
flowStartedRef.current = false;
|
|
467
|
+
await postJson("/plugins/agy-link/pool/cancel-add", {});
|
|
468
|
+
setAuthCodeInput("");
|
|
469
|
+
setAliasInput("");
|
|
470
|
+
setAddingAccount(false);
|
|
471
|
+
await refresh();
|
|
110
472
|
};
|
|
111
473
|
const setCfg = async (key, value) => {
|
|
112
|
-
|
|
474
|
+
setLoadingAction(`config:${key}`);
|
|
113
475
|
await postJson("/plugins/agy-link/config", {
|
|
114
476
|
key,
|
|
115
477
|
value
|
|
116
478
|
});
|
|
117
479
|
await refresh();
|
|
118
|
-
|
|
480
|
+
setLoadingAction(null);
|
|
481
|
+
};
|
|
482
|
+
const setPrimary = async (id) => {
|
|
483
|
+
setLoadingAction(`primary:${id}`);
|
|
484
|
+
await postJson("/plugins/agy-link/pool/primary", { id });
|
|
485
|
+
await refresh();
|
|
486
|
+
setLoadingAction(null);
|
|
487
|
+
showToast("⭐ 已设为主用账号", "success");
|
|
488
|
+
};
|
|
489
|
+
const removeAccount = async (id, alias) => {
|
|
490
|
+
setLoadingAction(`remove:${id}`);
|
|
491
|
+
await postJson("/plugins/agy-link/pool/remove", { id });
|
|
492
|
+
await refresh();
|
|
493
|
+
setLoadingAction(null);
|
|
494
|
+
showToast(`已移除账号: ${alias}`, "info");
|
|
495
|
+
};
|
|
496
|
+
const refreshQuota = async (id) => {
|
|
497
|
+
setLoadingAction(id ? `refresh:${id}` : "refresh:all");
|
|
498
|
+
await postJson("/plugins/agy-link/pool/refresh-quota", { id });
|
|
499
|
+
await refresh();
|
|
500
|
+
setLoadingAction(null);
|
|
501
|
+
showToast("✅ 额度已刷新", "success");
|
|
502
|
+
};
|
|
503
|
+
const saveProxy = async (id) => {
|
|
504
|
+
setLoadingAction(`proxy:${id}`);
|
|
505
|
+
const proxyUrl = proxyInputs[id];
|
|
506
|
+
await postJson("/plugins/agy-link/pool/proxy", {
|
|
507
|
+
id,
|
|
508
|
+
proxyUrl
|
|
509
|
+
});
|
|
510
|
+
setEditingProxyId(null);
|
|
511
|
+
await refresh();
|
|
512
|
+
setLoadingAction(null);
|
|
513
|
+
showToast("💾 代理已保存", "success");
|
|
119
514
|
};
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
515
|
+
const setMode = async (mode) => {
|
|
516
|
+
setLoadingAction(`mode:${mode}`);
|
|
517
|
+
await postJson("/plugins/agy-link/pool/mode", { mode });
|
|
518
|
+
await refresh();
|
|
519
|
+
setLoadingAction(null);
|
|
520
|
+
};
|
|
521
|
+
const clearCooldown = async (id) => {
|
|
522
|
+
setLoadingAction(id ? `clearCooldown:${id}` : "clearCooldown:all");
|
|
523
|
+
await postJson("/plugins/agy-link/pool/clear-cooldown", { id });
|
|
524
|
+
await refresh();
|
|
525
|
+
setLoadingAction(null);
|
|
526
|
+
showToast("⚡ 已清除冷却", "success");
|
|
527
|
+
};
|
|
528
|
+
const toggleExpand = (accId) => {
|
|
529
|
+
setExpandedModels((prev) => ({
|
|
530
|
+
...prev,
|
|
531
|
+
[accId]: !prev[accId]
|
|
532
|
+
}));
|
|
533
|
+
};
|
|
534
|
+
const authPhase = status?.auth?.phase ?? "unknown";
|
|
535
|
+
const pool = status?.pool;
|
|
536
|
+
const accounts = pool?.accounts ?? [];
|
|
537
|
+
const isAuthed = authPhase === "ok" || accounts.length > 0;
|
|
538
|
+
const isBusy = loadingAction !== null;
|
|
539
|
+
const renderSpinner = () => h("span", { className: "agy-spinner" });
|
|
540
|
+
const renderToastBanner = () => {
|
|
541
|
+
if (!toast) return null;
|
|
542
|
+
const typeStyles = {
|
|
543
|
+
success: {
|
|
544
|
+
background: "rgba(16,185,129,0.15)",
|
|
545
|
+
border: "1px solid rgba(16,185,129,0.4)",
|
|
546
|
+
color: "#10b981"
|
|
547
|
+
},
|
|
548
|
+
info: {
|
|
549
|
+
background: "rgba(59,130,246,0.15)",
|
|
550
|
+
border: "1px solid rgba(59,130,246,0.4)",
|
|
551
|
+
color: "#3b82f6"
|
|
552
|
+
},
|
|
553
|
+
warn: {
|
|
554
|
+
background: "rgba(245,158,11,0.15)",
|
|
555
|
+
border: "1px solid rgba(245,158,11,0.4)",
|
|
556
|
+
color: "#f59e0b"
|
|
557
|
+
},
|
|
558
|
+
error: {
|
|
559
|
+
background: "rgba(239,68,68,0.15)",
|
|
560
|
+
border: "1px solid rgba(239,68,68,0.4)",
|
|
561
|
+
color: "#ef4444"
|
|
562
|
+
}
|
|
563
|
+
};
|
|
564
|
+
return h("div", { style: {
|
|
565
|
+
...S.noticeBanner,
|
|
566
|
+
...typeStyles[toast.type]
|
|
567
|
+
} }, h("span", { style: { fontWeight: 500 } }, toast.text), h("button", {
|
|
568
|
+
type: "button",
|
|
569
|
+
style: {
|
|
570
|
+
background: "transparent",
|
|
571
|
+
border: "none",
|
|
572
|
+
color: "inherit",
|
|
573
|
+
cursor: "pointer",
|
|
574
|
+
fontSize: "14px",
|
|
575
|
+
padding: "0 4px",
|
|
576
|
+
opacity: .8
|
|
577
|
+
},
|
|
578
|
+
onClick: () => setToast(null)
|
|
579
|
+
}, "✕"));
|
|
580
|
+
};
|
|
581
|
+
const renderQuotaBar = (label, icon, familyKey, acc) => {
|
|
582
|
+
const info = acc.quotas[familyKey];
|
|
583
|
+
const cd = acc.cooldowns[familyKey];
|
|
584
|
+
const inCooldown = cd && cd.cooldownUntil > Date.now();
|
|
585
|
+
const hasData = typeof info?.remainingFraction === "number" && Number.isFinite(info.remainingFraction);
|
|
586
|
+
let percent = hasData ? 100 : -1;
|
|
587
|
+
let statusLabel = hasData ? "充足" : "未知";
|
|
588
|
+
const windowInfo = formatQuotaWindow(info?.resetTime, info?.remainingFraction);
|
|
589
|
+
if (inCooldown) {
|
|
590
|
+
percent = 0;
|
|
591
|
+
statusLabel = "限流冷却中";
|
|
592
|
+
} else if (hasData) {
|
|
593
|
+
percent = Math.max(0, Math.min(100, Math.round((info?.remainingFraction ?? 0) * 100)));
|
|
594
|
+
statusLabel = percent <= 20 ? "紧张" : percent <= 50 ? "适中" : "充足";
|
|
595
|
+
}
|
|
596
|
+
let barGradient = "linear-gradient(90deg, #10b981, #059669)";
|
|
597
|
+
let textColor = "#10b981";
|
|
598
|
+
let badgeBg = "rgba(16,185,129,0.15)";
|
|
599
|
+
if (percent < 0) {
|
|
600
|
+
barGradient = "rgba(128,128,128,0.25)";
|
|
601
|
+
textColor = "#9aa0a6";
|
|
602
|
+
badgeBg = "rgba(128,128,128,0.12)";
|
|
603
|
+
} else if (percent <= 20 || inCooldown || windowInfo.isWeeklyLockout) {
|
|
604
|
+
barGradient = "linear-gradient(90deg, #ef4444, #dc2626)";
|
|
605
|
+
textColor = "#ef4444";
|
|
606
|
+
badgeBg = "rgba(239,68,68,0.15)";
|
|
607
|
+
} else if (percent <= 50) {
|
|
608
|
+
barGradient = "linear-gradient(90deg, #f59e0b, #d97706)";
|
|
609
|
+
textColor = "#f59e0b";
|
|
610
|
+
badgeBg = "rgba(245,158,11,0.15)";
|
|
611
|
+
}
|
|
612
|
+
return h("div", { style: {
|
|
613
|
+
display: "flex",
|
|
614
|
+
alignItems: "center",
|
|
615
|
+
fontSize: "11px",
|
|
616
|
+
margin: "6px 0",
|
|
617
|
+
flexWrap: "wrap",
|
|
618
|
+
gap: "4px 0"
|
|
619
|
+
} }, h("div", { style: {
|
|
620
|
+
width: "130px",
|
|
621
|
+
display: "inline-flex",
|
|
622
|
+
alignItems: "center",
|
|
623
|
+
gap: "4px",
|
|
624
|
+
fontWeight: 500
|
|
625
|
+
} }, h("span", null, icon), h("span", null, label), hasData ? h("span", { style: {
|
|
626
|
+
fontSize: "9.5px",
|
|
627
|
+
padding: "0 4px",
|
|
628
|
+
borderRadius: "3px",
|
|
629
|
+
background: windowInfo.isWeeklyLockout ? "rgba(239,68,68,0.2)" : "rgba(128,128,128,0.14)",
|
|
630
|
+
color: windowInfo.isWeeklyLockout ? "#ef4444" : "#9aa0a6",
|
|
631
|
+
border: "1px solid rgba(128,128,128,0.2)"
|
|
632
|
+
} }, windowInfo.windowType) : null), h("div", { style: S.progressBarBg }, h("div", {
|
|
633
|
+
className: "agy-progress-fill",
|
|
634
|
+
style: {
|
|
635
|
+
width: `${percent < 0 ? 100 : percent}%`,
|
|
636
|
+
height: "100%",
|
|
637
|
+
background: barGradient,
|
|
638
|
+
borderRadius: "3px"
|
|
639
|
+
}
|
|
640
|
+
})), h("div", { style: {
|
|
641
|
+
display: "inline-flex",
|
|
642
|
+
alignItems: "center",
|
|
643
|
+
gap: "6px",
|
|
644
|
+
minWidth: "150px",
|
|
645
|
+
justifyContent: "flex-end"
|
|
646
|
+
} }, h("span", { style: {
|
|
647
|
+
padding: "1px 6px",
|
|
648
|
+
borderRadius: "4px",
|
|
649
|
+
background: badgeBg,
|
|
650
|
+
color: textColor,
|
|
651
|
+
fontWeight: 600,
|
|
652
|
+
fontSize: "11px"
|
|
653
|
+
} }, percent < 0 ? `— ${statusLabel}` : `${percent}% ${statusLabel}`), windowInfo.resetText ? h("span", { style: {
|
|
654
|
+
...S.muted,
|
|
655
|
+
fontSize: "10px"
|
|
656
|
+
} }, `(${windowInfo.resetText})`) : null));
|
|
657
|
+
};
|
|
658
|
+
const renderedAccountCards = accounts.map((acc) => {
|
|
659
|
+
const isPrimary = acc.id === pool?.primaryAccountId;
|
|
660
|
+
const hasCooldown = Object.entries(acc.cooldowns).some(([, cd]) => cd && cd.cooldownUntil > Date.now());
|
|
661
|
+
const dotColor = !acc.enabled ? "#9aa0a6" : hasCooldown ? "#f59e0b" : "#10b981";
|
|
662
|
+
const isEditingProxy = editingProxyId === acc.id;
|
|
663
|
+
const isExpanded = expandedModels[acc.id] ?? false;
|
|
664
|
+
const cardStyle = isPrimary ? { ...S.cardPrimary } : { ...S.card };
|
|
665
|
+
const googleModels = acc.quotas.google?.models ?? [];
|
|
666
|
+
const anthropicModels = acc.quotas.anthropic?.models ?? [];
|
|
667
|
+
const openaiModels = acc.quotas.openai?.models ?? [];
|
|
668
|
+
const allChildModels = [
|
|
669
|
+
...googleModels.map((m) => ({
|
|
670
|
+
family: "Google",
|
|
671
|
+
model: m
|
|
672
|
+
})),
|
|
673
|
+
...anthropicModels.map((m) => ({
|
|
674
|
+
family: "Anthropic",
|
|
675
|
+
model: m
|
|
676
|
+
})),
|
|
677
|
+
...openaiModels.map((m) => ({
|
|
678
|
+
family: "OpenAI",
|
|
679
|
+
model: m
|
|
680
|
+
}))
|
|
681
|
+
];
|
|
682
|
+
return h("div", {
|
|
683
|
+
key: acc.id,
|
|
684
|
+
className: "agy-card-hover",
|
|
685
|
+
style: cardStyle
|
|
686
|
+
}, h("div", { style: {
|
|
687
|
+
display: "flex",
|
|
688
|
+
alignItems: "center",
|
|
689
|
+
justifyContent: "space-between",
|
|
690
|
+
marginBottom: "6px"
|
|
691
|
+
} }, h("div", { style: {
|
|
692
|
+
display: "flex",
|
|
693
|
+
alignItems: "center",
|
|
694
|
+
gap: "8px",
|
|
695
|
+
flexWrap: "wrap"
|
|
696
|
+
} }, h("span", {
|
|
697
|
+
className: "agy-pulse-dot",
|
|
698
|
+
style: {
|
|
699
|
+
background: dotColor,
|
|
700
|
+
boxShadow: `0 0 8px ${dotColor}88`
|
|
701
|
+
}
|
|
702
|
+
}), h("span", { style: {
|
|
703
|
+
fontWeight: 600,
|
|
704
|
+
fontSize: "13px"
|
|
705
|
+
} }, acc.alias), acc.email ? h("span", { style: {
|
|
706
|
+
...S.badgeTag,
|
|
707
|
+
color: "#3b82f6",
|
|
708
|
+
borderColor: "rgba(59,130,246,0.3)"
|
|
709
|
+
} }, `✉️ ${acc.email}`) : null, isPrimary ? h("span", { style: S.badgePrimary }, "⭐ 主用") : null, acc.proxyUrl ? h("span", { style: {
|
|
710
|
+
...S.badgeTag,
|
|
711
|
+
color: "#10b981",
|
|
712
|
+
borderColor: "rgba(16,185,129,0.3)"
|
|
713
|
+
} }, "🌐 代理") : null), h("div", { style: {
|
|
714
|
+
display: "flex",
|
|
715
|
+
gap: "5px"
|
|
716
|
+
} }, allChildModels.length > 0 ? h("button", {
|
|
717
|
+
type: "button",
|
|
718
|
+
className: "agy-btn",
|
|
719
|
+
style: isExpanded ? { ...S.btnSmPrimary } : S.btnSm,
|
|
720
|
+
onClick: () => toggleExpand(acc.id)
|
|
721
|
+
}, isExpanded ? "收起细分" : "🔍 细分") : null, !isPrimary ? h("button", {
|
|
722
|
+
type: "button",
|
|
723
|
+
className: "agy-btn",
|
|
724
|
+
style: S.btnSm,
|
|
725
|
+
disabled: isBusy,
|
|
726
|
+
onClick: () => void setPrimary(acc.id)
|
|
727
|
+
}, loadingAction === `primary:${acc.id}` ? [renderSpinner(), "设置中"] : "设为主用") : null, h("button", {
|
|
728
|
+
type: "button",
|
|
729
|
+
className: "agy-btn",
|
|
730
|
+
style: isEditingProxy ? { ...S.btnSmPrimary } : S.btnSm,
|
|
731
|
+
disabled: isBusy,
|
|
732
|
+
onClick: () => setEditingProxyId(isEditingProxy ? null : acc.id)
|
|
733
|
+
}, "代理"), accounts.length > 1 ? h("button", {
|
|
734
|
+
type: "button",
|
|
735
|
+
className: "agy-btn",
|
|
736
|
+
style: S.btnDanger,
|
|
737
|
+
disabled: isBusy,
|
|
738
|
+
onClick: () => void removeAccount(acc.id, acc.alias)
|
|
739
|
+
}, loadingAction === `remove:${acc.id}` ? [renderSpinner(), ""] : "🗑️") : null)), h("div", { style: S.quotaBox }, renderQuotaBar("Gemini (Flash/Pro)", "✨", "google", acc), renderQuotaBar("Claude (Sonnet/Opus)", "🧠", "anthropic", acc), renderQuotaBar("GPT-OSS (120B)", "⚡", "openai", acc), isExpanded && allChildModels.length > 0 ? h("div", { style: {
|
|
740
|
+
marginTop: "8px",
|
|
741
|
+
paddingTop: "8px",
|
|
742
|
+
borderTop: "1px solid rgba(128,128,128,0.15)"
|
|
743
|
+
} }, h("div", { style: {
|
|
744
|
+
...S.muted,
|
|
745
|
+
marginBottom: "4px",
|
|
746
|
+
fontWeight: 600
|
|
747
|
+
} }, "📊 单模型明细额度 (5小时/周限额状态):"), allChildModels.map(({ family, model }) => {
|
|
748
|
+
const frac = model.remainingFraction ?? 1;
|
|
749
|
+
const pct = Math.round(frac * 100);
|
|
750
|
+
const w = formatQuotaWindow(model.resetTime, frac);
|
|
751
|
+
const pColor = pct <= 20 ? "#ef4444" : pct <= 50 ? "#f59e0b" : "#10b981";
|
|
752
|
+
return h("div", {
|
|
753
|
+
key: model.modelId,
|
|
754
|
+
className: "agy-submodel-row"
|
|
755
|
+
}, h("span", { style: { fontWeight: 500 } }, `[${family}] ${model.displayName || model.modelId}`), h("div", { style: {
|
|
756
|
+
display: "flex",
|
|
757
|
+
alignItems: "center",
|
|
758
|
+
gap: "6px"
|
|
759
|
+
} }, h("span", { style: {
|
|
760
|
+
color: pColor,
|
|
761
|
+
fontWeight: 600
|
|
762
|
+
} }, `${pct}%`), w.resetText ? h("span", { style: S.muted }, w.resetText) : null));
|
|
763
|
+
})) : null), hasCooldown ? h("div", { style: {
|
|
764
|
+
background: "rgba(245,158,11,0.1)",
|
|
765
|
+
border: "1px solid rgba(245,158,11,0.3)",
|
|
766
|
+
borderRadius: "6px",
|
|
767
|
+
padding: "4px 8px",
|
|
768
|
+
color: "#f59e0b",
|
|
769
|
+
fontSize: "11px",
|
|
770
|
+
marginTop: "6px",
|
|
771
|
+
display: "flex",
|
|
772
|
+
alignItems: "center",
|
|
773
|
+
justifyContent: "space-between"
|
|
774
|
+
} }, h("span", null, "⚠️ 该账号部分模型处于限流冷却中 (已自动切换下个可用账号)"), h("button", {
|
|
775
|
+
type: "button",
|
|
776
|
+
className: "agy-btn",
|
|
777
|
+
style: {
|
|
778
|
+
...S.btnSm,
|
|
779
|
+
color: "#f59e0b",
|
|
780
|
+
borderColor: "rgba(245,158,11,0.4)",
|
|
781
|
+
background: "rgba(245,158,11,0.15)"
|
|
782
|
+
},
|
|
783
|
+
disabled: isBusy,
|
|
784
|
+
onClick: () => void clearCooldown(acc.id)
|
|
785
|
+
}, loadingAction === `clearCooldown:${acc.id}` ? [renderSpinner(), ""] : "⚡ 清除冷却")) : null, isEditingProxy ? h("div", { style: {
|
|
786
|
+
marginTop: "6px",
|
|
787
|
+
padding: "8px 10px",
|
|
788
|
+
background: "rgba(128,128,128,0.06)",
|
|
789
|
+
borderRadius: "6px",
|
|
790
|
+
border: "1px solid rgba(128,128,128,0.15)"
|
|
791
|
+
} }, h("div", { style: {
|
|
792
|
+
display: "flex",
|
|
793
|
+
gap: "6px"
|
|
794
|
+
} }, h("input", {
|
|
795
|
+
style: S.input,
|
|
796
|
+
value: proxyInputs[acc.id] !== void 0 ? proxyInputs[acc.id] : acc.proxyUrl ?? "",
|
|
797
|
+
placeholder: "专属代理 URL (如: http://127.0.0.1:7890,留空则使用全局)",
|
|
798
|
+
onChange: (e) => setProxyInputs({
|
|
799
|
+
...proxyInputs,
|
|
800
|
+
[acc.id]: e.target.value
|
|
801
|
+
})
|
|
802
|
+
}), h("button", {
|
|
803
|
+
type: "button",
|
|
804
|
+
className: "agy-btn",
|
|
805
|
+
style: S.btnPrimary,
|
|
806
|
+
disabled: isBusy,
|
|
807
|
+
onClick: () => void saveProxy(acc.id)
|
|
808
|
+
}, loadingAction === `proxy:${acc.id}` ? [renderSpinner(), "保存"] : "保存"), h("button", {
|
|
809
|
+
type: "button",
|
|
810
|
+
className: "agy-btn",
|
|
811
|
+
style: S.btn,
|
|
812
|
+
onClick: () => setEditingProxyId(null)
|
|
813
|
+
}, "取消"))) : null);
|
|
814
|
+
});
|
|
815
|
+
const poolAuth = status?.poolAuth;
|
|
816
|
+
const flowPhase = poolAuth?.phase ?? "idle";
|
|
817
|
+
const flowActive = flowPhase === "waiting" || flowPhase === "exchanging";
|
|
818
|
+
const addAccountSection = addingAccount ? h("div", { style: S.authModal }, h("div", { style: {
|
|
819
|
+
fontWeight: 600,
|
|
820
|
+
fontSize: "13px",
|
|
821
|
+
marginBottom: "8px"
|
|
822
|
+
} }, "➕ 添加 Google 账号"), !flowActive ? h("div", null, h("div", { style: {
|
|
823
|
+
display: "flex",
|
|
824
|
+
gap: "8px"
|
|
825
|
+
} }, h("input", {
|
|
826
|
+
style: S.input,
|
|
827
|
+
value: aliasInput,
|
|
828
|
+
placeholder: "账号别名 (例如: 备用账号 2)",
|
|
829
|
+
onChange: (e) => setAliasInput(e.target.value)
|
|
830
|
+
}), h("button", {
|
|
831
|
+
type: "button",
|
|
832
|
+
className: "agy-btn",
|
|
833
|
+
style: S.btnPrimary,
|
|
834
|
+
disabled: isBusy,
|
|
835
|
+
onClick: () => void handleBeginAddAccount()
|
|
836
|
+
}, loadingAction === "pool:beginAdd" ? [renderSpinner(), "正在打开浏览器..."] : "🚀 打开浏览器登录"), h("button", {
|
|
837
|
+
type: "button",
|
|
838
|
+
className: "agy-btn",
|
|
839
|
+
style: S.btn,
|
|
840
|
+
onClick: () => handleCancelAddAccount()
|
|
841
|
+
}, "取消")), flowPhase === "failed" && poolAuth?.message ? h("div", { style: {
|
|
124
842
|
...S.muted,
|
|
125
|
-
|
|
126
|
-
margin: "6px 0 12px"
|
|
127
|
-
} }, h("div", null, "agy binary: ", status?.bin ?? "not found", status?.version ? " — v" + status.version : ""), h("div", null, "auth: ", status?.auth?.phase ?? "unknown"), h("div", null, "workspace: ", status?.workspaceRoot ? status.workspaceRoot : "(session cwd / process cwd)"), h("div", null, "models: ", String(status?.catalog?.count ?? 0), " — ", status?.catalog?.source ?? ""), h("div", null, "bindings: ", String(status?.bindings ?? 0)), h("div", null, "last run: ", status?.lastRun ? (status.lastRun.ok ? "ok" : status.lastRun.code) + " — " + status.lastRun.model : "none"));
|
|
128
|
-
const modeRow = h("div", { style: {
|
|
129
|
-
...S.row,
|
|
843
|
+
color: "#ef4444",
|
|
130
844
|
marginTop: "8px"
|
|
131
|
-
} },
|
|
845
|
+
} }, `❌ ${poolAuth.message}`) : null) : h("div", null, h("div", { style: {
|
|
846
|
+
display: "flex",
|
|
847
|
+
alignItems: "center",
|
|
848
|
+
...S.muted,
|
|
849
|
+
marginBottom: "8px",
|
|
850
|
+
lineHeight: 1.5
|
|
851
|
+
} }, renderSpinner(), flowPhase === "exchanging" ? "正在验证授权并激活账号,请稍候..." : "等待浏览器中完成 Google 授权,成功后这里会自动完成。"), poolAuth?.url ? h("div", { style: { marginBottom: "8px" } }, h("a", {
|
|
852
|
+
href: poolAuth.url,
|
|
853
|
+
target: "_blank",
|
|
854
|
+
style: {
|
|
855
|
+
color: "#3b82f6",
|
|
856
|
+
textDecoration: "none",
|
|
857
|
+
fontSize: "12px",
|
|
858
|
+
fontWeight: 500
|
|
859
|
+
}
|
|
860
|
+
}, "👉 若浏览器未打开,请点击此处手动打开 Google 登录页")) : null, h("div", { style: {
|
|
861
|
+
...S.muted,
|
|
862
|
+
marginBottom: "6px",
|
|
863
|
+
fontSize: "10px"
|
|
864
|
+
} }, "自动回调失败时,可粘贴授权码或浏览器地址栏中的完整回调 URL:"), h("div", { style: {
|
|
865
|
+
display: "flex",
|
|
866
|
+
gap: "8px"
|
|
867
|
+
} }, h("input", {
|
|
868
|
+
style: S.input,
|
|
869
|
+
value: authCodeInput,
|
|
870
|
+
placeholder: "授权码 或 http://localhost:51121/oauth-callback?code=... 完整链接",
|
|
871
|
+
onChange: (e) => setAuthCodeInput(e.target.value)
|
|
872
|
+
}), h("button", {
|
|
873
|
+
type: "button",
|
|
874
|
+
className: "agy-btn",
|
|
875
|
+
style: S.btnPrimary,
|
|
876
|
+
disabled: isBusy || !authCodeInput.trim(),
|
|
877
|
+
onClick: () => void handleCompleteAddAccount()
|
|
878
|
+
}, loadingAction === "pool:completeAdd" ? [renderSpinner(), "验证激活中..."] : "✅ 手动激活"), h("button", {
|
|
879
|
+
type: "button",
|
|
880
|
+
className: "agy-btn",
|
|
881
|
+
style: S.btn,
|
|
882
|
+
onClick: () => handleCancelAddAccount()
|
|
883
|
+
}, "取消")))) : null;
|
|
884
|
+
if (status === null) return h("div", { style: {
|
|
885
|
+
...S.container,
|
|
886
|
+
minHeight: "120px",
|
|
887
|
+
display: "flex",
|
|
888
|
+
alignItems: "center",
|
|
889
|
+
justifyContent: "center"
|
|
890
|
+
} }, h("style", null, GLOBAL_CSS), h("span", { style: S.muted }, [renderSpinner(), "正在加载 Antigravity 状态..."]));
|
|
891
|
+
return h("div", { style: S.container }, h("style", null, GLOBAL_CSS), h("div", { style: S.headerCard }, h("div", { style: {
|
|
892
|
+
display: "flex",
|
|
893
|
+
alignItems: "center",
|
|
894
|
+
gap: "8px"
|
|
895
|
+
} }, h("span", {
|
|
896
|
+
className: "agy-pulse-dot",
|
|
897
|
+
style: {
|
|
898
|
+
background: isAuthed ? "#10b981" : "#f59e0b",
|
|
899
|
+
boxShadow: `0 0 8px ${isAuthed ? "#10b981" : "#f59e0b"}`
|
|
900
|
+
}
|
|
901
|
+
}), h("span", { style: {
|
|
902
|
+
fontWeight: 700,
|
|
903
|
+
fontSize: "14px"
|
|
904
|
+
} }, "Antigravity (agy CLI)"), h("span", { style: isAuthed ? {
|
|
905
|
+
...S.badgePrimary,
|
|
906
|
+
color: "#10b981",
|
|
907
|
+
background: "rgba(16,185,129,0.12)",
|
|
908
|
+
borderColor: "rgba(16,185,129,0.3)"
|
|
909
|
+
} : S.badgePrimary }, isAuthed ? "就绪" : "待认证")), h("div", { style: {
|
|
910
|
+
display: "flex",
|
|
911
|
+
gap: "6px"
|
|
912
|
+
} }, !addingAccount ? h("button", {
|
|
913
|
+
type: "button",
|
|
914
|
+
className: "agy-btn",
|
|
915
|
+
style: S.btnPrimary,
|
|
916
|
+
onClick: () => setAddingAccount(true)
|
|
917
|
+
}, "➕ 添加账号") : null, h("button", {
|
|
132
918
|
type: "button",
|
|
133
|
-
|
|
919
|
+
className: "agy-btn",
|
|
920
|
+
style: S.btn,
|
|
921
|
+
disabled: isBusy,
|
|
922
|
+
onClick: () => void refreshQuota()
|
|
923
|
+
}, loadingAction === "refresh:all" ? [renderSpinner(), "刷新中"] : "🔄 刷新额度"))), renderToastBanner(), addAccountSection, renderedAccountCards, h("div", { style: {
|
|
924
|
+
background: "rgba(59,130,246,0.05)",
|
|
925
|
+
border: "1px solid rgba(59,130,246,0.18)",
|
|
926
|
+
borderRadius: "6px",
|
|
927
|
+
padding: "8px 10px",
|
|
928
|
+
marginBottom: "10px",
|
|
929
|
+
fontSize: "11px",
|
|
930
|
+
lineHeight: "1.6"
|
|
931
|
+
} }, h("div", { style: {
|
|
932
|
+
fontWeight: 600,
|
|
933
|
+
color: "#3b82f6",
|
|
934
|
+
marginBottom: "2px",
|
|
935
|
+
display: "flex",
|
|
936
|
+
alignItems: "center",
|
|
937
|
+
gap: "4px"
|
|
938
|
+
} }, "💡 Antigravity 额度机制说明 (5小时额度 & 周额度)"), h("div", { style: { opacity: .85 } }, h("div", null, "• 🕒 5小时短期额度:各模型族按 5 小时滚动窗口刷新配额,日常对话优先消耗此额度。"), h("div", null, "• 📅 周额度 / 总量上限:Google 账号设有周期总量保护;若高频重度使用触及周上限,重置时间将显示为数天后。"), h("div", null, "• 🔄 多账号号池调度:单账号 5 小时额度或周额度受限时,号池自动平滑接力至下一可用账号,无缝保障持续编码。"))), h("div", { style: {
|
|
939
|
+
marginTop: "14px",
|
|
940
|
+
paddingTop: "10px",
|
|
941
|
+
borderTop: "1px solid rgba(128,128,128,0.15)"
|
|
942
|
+
} }, h("div", { style: {
|
|
943
|
+
display: "flex",
|
|
944
|
+
flexDirection: "column",
|
|
945
|
+
gap: "8px"
|
|
946
|
+
} }, h("div", { style: {
|
|
947
|
+
display: "flex",
|
|
948
|
+
alignItems: "center",
|
|
949
|
+
justifyContent: "space-between"
|
|
950
|
+
} }, h("span", { style: S.muted }, "权限模式:"), h("div", { style: S.segGroup }, h("button", {
|
|
951
|
+
type: "button",
|
|
952
|
+
style: status?.permissionMode === "plan" ? S.segBtnActive : S.segBtn,
|
|
134
953
|
onClick: () => void setCfg("permissionMode", "plan")
|
|
135
|
-
}, "plan"), h("button", {
|
|
954
|
+
}, "plan (只读)"), h("button", {
|
|
136
955
|
type: "button",
|
|
137
|
-
style: status?.permissionMode === "accept-edits" ? S.
|
|
956
|
+
style: status?.permissionMode === "accept-edits" ? S.segBtnActive : S.segBtn,
|
|
138
957
|
onClick: () => void setCfg("permissionMode", "accept-edits")
|
|
139
|
-
}, "accept-edits"), h("button", {
|
|
958
|
+
}, "accept-edits (改代码)"), h("button", {
|
|
140
959
|
type: "button",
|
|
141
960
|
style: status?.permissionMode === "skip" ? {
|
|
142
|
-
...S.
|
|
143
|
-
color: "#
|
|
144
|
-
|
|
145
|
-
} : S.
|
|
961
|
+
...S.segBtnActive,
|
|
962
|
+
color: "#ef4444",
|
|
963
|
+
background: "rgba(239,68,68,0.2)"
|
|
964
|
+
} : S.segBtn,
|
|
146
965
|
onClick: () => void setCfg("permissionMode", "skip")
|
|
147
|
-
}, "skip"))
|
|
148
|
-
|
|
966
|
+
}, "skip (全自动免确认)"))), h("div", { style: {
|
|
967
|
+
display: "flex",
|
|
968
|
+
alignItems: "center",
|
|
969
|
+
justifyContent: "space-between"
|
|
970
|
+
} }, h("span", { style: S.muted }, "思考强度:"), h("div", { style: S.segGroup }, h("button", {
|
|
149
971
|
type: "button",
|
|
150
|
-
style: status?.defaultEffort === "
|
|
972
|
+
style: status?.defaultEffort === "" ? S.segBtnActive : S.segBtn,
|
|
973
|
+
onClick: () => void setCfg("defaultEffort", "")
|
|
974
|
+
}, "auto"), h("button", {
|
|
975
|
+
type: "button",
|
|
976
|
+
style: status?.defaultEffort === "low" ? S.segBtnActive : S.segBtn,
|
|
151
977
|
onClick: () => void setCfg("defaultEffort", "low")
|
|
152
978
|
}, "low"), h("button", {
|
|
153
979
|
type: "button",
|
|
154
|
-
style: status?.defaultEffort === "medium" ? S.
|
|
980
|
+
style: status?.defaultEffort === "medium" ? S.segBtnActive : S.segBtn,
|
|
155
981
|
onClick: () => void setCfg("defaultEffort", "medium")
|
|
156
982
|
}, "medium"), h("button", {
|
|
157
983
|
type: "button",
|
|
158
|
-
style: status?.defaultEffort === "high" ? S.
|
|
984
|
+
style: status?.defaultEffort === "high" ? S.segBtnActive : S.segBtn,
|
|
159
985
|
onClick: () => void setCfg("defaultEffort", "high")
|
|
160
|
-
}, "high"), h("
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
}, "
|
|
165
|
-
const skipWarn = h("div", { style: {
|
|
166
|
-
...S.muted,
|
|
167
|
-
marginBottom: "8px"
|
|
168
|
-
} }, "skip runs agy tools with --dangerously-skip-permissions (no approval prompts). plan is the safe read-only default.");
|
|
169
|
-
const authBlock = h("div", null, h("div", { style: {
|
|
170
|
-
fontWeight: 600,
|
|
171
|
-
marginBottom: "4px"
|
|
172
|
-
} }, "Google login required"), h("div", null, "agy is not signed in. Start the login flow, scan the QR or open the URL, then paste the authorization code below."), h("img", {
|
|
173
|
-
src: base + "/plugins/agy-link/qr",
|
|
174
|
-
alt: "auth QR",
|
|
175
|
-
width: 200,
|
|
176
|
-
height: 200,
|
|
177
|
-
style: {
|
|
178
|
-
display: "block",
|
|
179
|
-
margin: "8px auto"
|
|
180
|
-
}
|
|
181
|
-
}), h("div", { style: { wordBreak: "break-all" } }, status?.auth?.url ?? ""), h("div", { style: S.row }, h("input", {
|
|
182
|
-
style: S.input,
|
|
183
|
-
value: code,
|
|
184
|
-
placeholder: "authorization code",
|
|
185
|
-
onChange: (e) => setCode(e.target.value)
|
|
186
|
-
}), h("button", {
|
|
986
|
+
}, "high"))), h("div", { style: {
|
|
987
|
+
display: "flex",
|
|
988
|
+
alignItems: "center",
|
|
989
|
+
justifyContent: "space-between"
|
|
990
|
+
} }, h("span", { style: S.muted }, "号池调度:"), h("div", { style: S.segGroup }, h("button", {
|
|
187
991
|
type: "button",
|
|
188
|
-
style: S.
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
}, "Submit code"), h("button", {
|
|
992
|
+
style: pool?.mode === "sequential" || !pool?.mode ? S.segBtnActive : S.segBtn,
|
|
993
|
+
onClick: () => void setMode("sequential")
|
|
994
|
+
}, "顺次耗尽"), h("button", {
|
|
192
995
|
type: "button",
|
|
193
|
-
style: S.
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
996
|
+
style: pool?.mode === "round-robin" ? S.segBtnActive : S.segBtn,
|
|
997
|
+
onClick: () => void setMode("round-robin")
|
|
998
|
+
}, "轮询均衡"))))));
|
|
999
|
+
};
|
|
1000
|
+
/** Modal dialog container rendered into document.body */
|
|
1001
|
+
const AgyModalDialog = () => {
|
|
1002
|
+
const [isOpen, setIsOpen] = useState(agyModalStore.open);
|
|
1003
|
+
useEffect(() => {
|
|
1004
|
+
return agyModalStore.subscribe(() => {
|
|
1005
|
+
setIsOpen(agyModalStore.open);
|
|
1006
|
+
});
|
|
1007
|
+
}, []);
|
|
1008
|
+
useEffect(() => {
|
|
1009
|
+
if (!isOpen) return;
|
|
1010
|
+
const onKey = (e) => {
|
|
1011
|
+
if (e.key === "Escape") {
|
|
1012
|
+
e.stopPropagation();
|
|
1013
|
+
agyModalStore.setOpen(false);
|
|
1014
|
+
}
|
|
1015
|
+
};
|
|
1016
|
+
win.addEventListener?.("keydown", onKey, true);
|
|
1017
|
+
return () => win.removeEventListener?.("keydown", onKey, true);
|
|
1018
|
+
}, [isOpen]);
|
|
1019
|
+
if (!isOpen) return null;
|
|
1020
|
+
return h("div", {
|
|
1021
|
+
className: "agy-modal-backdrop",
|
|
1022
|
+
onClick: (e) => {
|
|
1023
|
+
if (e.target === e.currentTarget) agyModalStore.setOpen(false);
|
|
1024
|
+
}
|
|
1025
|
+
}, h("div", {
|
|
1026
|
+
className: "agy-modal-panel",
|
|
1027
|
+
role: "dialog",
|
|
1028
|
+
"aria-modal": true
|
|
1029
|
+
}, h("div", { style: {
|
|
200
1030
|
display: "flex",
|
|
201
1031
|
alignItems: "center",
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
1032
|
+
justifyContent: "space-between",
|
|
1033
|
+
padding: "12px 16px",
|
|
1034
|
+
borderBottom: "1px solid rgba(128,128,128,0.2)",
|
|
1035
|
+
background: "rgba(128,128,128,0.05)"
|
|
1036
|
+
} }, h("div", { style: {
|
|
1037
|
+
display: "flex",
|
|
1038
|
+
alignItems: "center",
|
|
1039
|
+
gap: "8px"
|
|
1040
|
+
} }, h("span", { style: { fontSize: "16px" } }, "🪐"), h("strong", { style: { fontSize: "14px" } }, "Antigravity 管理控制台")), h("button", {
|
|
1041
|
+
type: "button",
|
|
1042
|
+
title: "关闭 (Esc)",
|
|
1043
|
+
style: {
|
|
1044
|
+
background: "transparent",
|
|
1045
|
+
border: "none",
|
|
1046
|
+
color: "#9aa0a6",
|
|
1047
|
+
cursor: "pointer",
|
|
1048
|
+
fontSize: "16px",
|
|
1049
|
+
lineHeight: 1,
|
|
1050
|
+
padding: "4px 6px",
|
|
1051
|
+
borderRadius: "4px"
|
|
1052
|
+
},
|
|
1053
|
+
onClick: () => agyModalStore.setOpen(false)
|
|
1054
|
+
}, "✕")), h("div", { style: {
|
|
1055
|
+
overflowY: "auto",
|
|
1056
|
+
padding: "16px",
|
|
1057
|
+
flex: "1"
|
|
1058
|
+
} }, h(AgySettingsSection, {
|
|
1059
|
+
isModal: true,
|
|
1060
|
+
onClose: () => agyModalStore.setOpen(false)
|
|
1061
|
+
}))));
|
|
1062
|
+
};
|
|
1063
|
+
/** Sidebar bottom-left action button + body portal modal */
|
|
1064
|
+
const AgySidebarFooterAction = (props) => {
|
|
1065
|
+
const [status, setStatus] = useState(statusCache);
|
|
1066
|
+
const isWide = props?.wide !== false;
|
|
1067
|
+
useEffect(() => {
|
|
1068
|
+
let alive = true;
|
|
1069
|
+
const tick = async () => {
|
|
1070
|
+
const st = await getStatus();
|
|
1071
|
+
if (!alive || !st) return;
|
|
1072
|
+
setStatus(st);
|
|
1073
|
+
};
|
|
1074
|
+
tick();
|
|
1075
|
+
const timer = setInterval(tick, 3e3);
|
|
1076
|
+
return () => {
|
|
1077
|
+
alive = false;
|
|
1078
|
+
clearInterval(timer);
|
|
1079
|
+
};
|
|
1080
|
+
}, []);
|
|
1081
|
+
const accounts = (status?.pool)?.accounts ?? [];
|
|
1082
|
+
const hasCooldown = accounts.some((a) => Object.entries(a.cooldowns).some(([, cd]) => cd && cd.cooldownUntil > Date.now()));
|
|
1083
|
+
const isAuthed = status?.auth?.phase === "ok" || accounts.length > 0;
|
|
1084
|
+
const dotColor = status === null ? "#9aa0a6" : status.dormantReason ? "#f59e0b" : hasCooldown ? "#f59e0b" : isAuthed ? "#10b981" : "#f59e0b";
|
|
1085
|
+
const button = h("button", {
|
|
1086
|
+
type: "button",
|
|
1087
|
+
title: `Antigravity: ${accounts.length} accounts (${isAuthed ? "就绪" : "待认证"}) · 点击打开管理控制台`,
|
|
1088
|
+
className: "agy-btn",
|
|
1089
|
+
style: {
|
|
1090
|
+
display: "inline-flex",
|
|
1091
|
+
alignItems: "center",
|
|
1092
|
+
gap: "6px",
|
|
1093
|
+
padding: isWide ? "5px 10px" : "6px",
|
|
1094
|
+
border: "1px solid rgba(128,128,128,0.22)",
|
|
1095
|
+
borderRadius: "8px",
|
|
1096
|
+
background: "rgba(128,128,128,0.08)",
|
|
1097
|
+
color: "inherit",
|
|
1098
|
+
cursor: "pointer",
|
|
1099
|
+
fontSize: "12px",
|
|
1100
|
+
lineHeight: 1.4
|
|
1101
|
+
},
|
|
1102
|
+
onClick: () => agyModalStore.setOpen(true)
|
|
1103
|
+
}, h("span", { style: { fontSize: "13px" } }, "🪐"), isWide ? h("span", { style: { fontWeight: 500 } }, "Antigravity") : null, h("span", { style: {
|
|
206
1104
|
display: "inline-block",
|
|
207
|
-
width: "
|
|
208
|
-
height: "
|
|
1105
|
+
width: "7px",
|
|
1106
|
+
height: "7px",
|
|
209
1107
|
borderRadius: "50%",
|
|
210
|
-
background:
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
1108
|
+
background: dotColor,
|
|
1109
|
+
boxShadow: `0 0 6px ${dotColor}`
|
|
1110
|
+
} }), isWide && accounts.length > 0 ? h("span", { style: {
|
|
1111
|
+
fontSize: "10px",
|
|
1112
|
+
opacity: .75,
|
|
1113
|
+
background: "rgba(128,128,128,0.18)",
|
|
1114
|
+
padding: "1px 5px",
|
|
1115
|
+
borderRadius: "10px"
|
|
1116
|
+
} }, `${accounts.length}`) : null);
|
|
1117
|
+
return h("div", { style: { display: "inline-block" } }, button, portalToBody(h(AgyModalDialog, null)));
|
|
215
1118
|
};
|
|
1119
|
+
/** Session Header badge in chat toolbar */
|
|
216
1120
|
const AgySessionStatus = () => {
|
|
217
|
-
const [status, setStatus] = useState(
|
|
1121
|
+
const [status, setStatus] = useState(statusCache);
|
|
218
1122
|
useEffect(() => {
|
|
219
1123
|
let alive = true;
|
|
1124
|
+
let lastJson = "";
|
|
220
1125
|
const tick = async () => {
|
|
221
1126
|
const st = await getStatus();
|
|
222
|
-
if (alive
|
|
1127
|
+
if (!alive || !st) return;
|
|
1128
|
+
const json = JSON.stringify(st);
|
|
1129
|
+
if (json !== lastJson) {
|
|
1130
|
+
lastJson = json;
|
|
1131
|
+
setStatus(st);
|
|
1132
|
+
}
|
|
223
1133
|
};
|
|
224
1134
|
tick();
|
|
225
1135
|
const timer = setInterval(tick, 3e3);
|
|
@@ -228,16 +1138,20 @@ window.__ModuleLoader__.load({
|
|
|
228
1138
|
clearInterval(timer);
|
|
229
1139
|
};
|
|
230
1140
|
}, []);
|
|
231
|
-
const
|
|
232
|
-
const
|
|
1141
|
+
const accounts = (status?.pool)?.accounts ?? [];
|
|
1142
|
+
const hasCooldown = accounts.some((a) => Object.entries(a.cooldowns).some(([, cd]) => cd && cd.cooldownUntil > Date.now()));
|
|
1143
|
+
const isAuthed = status?.auth?.phase === "ok" || accounts.length > 0;
|
|
1144
|
+
const color = status === null ? "#9aa0a6" : status.dormantReason ? "#f59e0b" : hasCooldown ? "#f59e0b" : isAuthed ? "#10b981" : "#f59e0b";
|
|
233
1145
|
return h("button", {
|
|
234
1146
|
type: "button",
|
|
235
|
-
title
|
|
1147
|
+
title: `Antigravity: ${accounts.length} accounts ready · 点击打开控制台`,
|
|
1148
|
+
className: "agy-btn",
|
|
1149
|
+
onClick: () => agyModalStore.setOpen(true),
|
|
236
1150
|
style: {
|
|
237
1151
|
background: "transparent",
|
|
238
|
-
border: "1px solid rgba(128,128,128,0.
|
|
1152
|
+
border: "1px solid rgba(128,128,128,0.25)",
|
|
239
1153
|
borderRadius: "999px",
|
|
240
|
-
cursor: "
|
|
1154
|
+
cursor: "pointer",
|
|
241
1155
|
padding: "2px 8px",
|
|
242
1156
|
fontSize: "11px",
|
|
243
1157
|
lineHeight: 1.5,
|
|
@@ -248,11 +1162,11 @@ window.__ModuleLoader__.load({
|
|
|
248
1162
|
}
|
|
249
1163
|
}, h("span", { style: {
|
|
250
1164
|
display: "inline-block",
|
|
251
|
-
width: "
|
|
252
|
-
height: "
|
|
1165
|
+
width: "7px",
|
|
1166
|
+
height: "7px",
|
|
253
1167
|
borderRadius: "50%",
|
|
254
1168
|
background: color
|
|
255
|
-
} }),
|
|
1169
|
+
} }), `AGY (${accounts.length})`);
|
|
256
1170
|
};
|
|
257
1171
|
ctx.slots.inject("conversation.session.header.actions", () => {
|
|
258
1172
|
return ctx.slots.register({
|
|
@@ -270,6 +1184,14 @@ window.__ModuleLoader__.load({
|
|
|
270
1184
|
label: "Antigravity"
|
|
271
1185
|
}, AgySettingsSection);
|
|
272
1186
|
});
|
|
1187
|
+
ctx.slots.inject("sidebar.footer.action", () => {
|
|
1188
|
+
return ctx.slots.register({
|
|
1189
|
+
name: "sidebar.footer.action",
|
|
1190
|
+
id: "agy-link-sidebar-footer",
|
|
1191
|
+
order: 50,
|
|
1192
|
+
label: "Antigravity"
|
|
1193
|
+
}, AgySidebarFooterAction);
|
|
1194
|
+
});
|
|
273
1195
|
}
|
|
274
1196
|
//#endregion
|
|
275
1197
|
exports.apply = apply;
|