@yesdgq/claude-buddy 1.1.1017 → 1.1.1019
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/assets/config.html +81 -38
- package/assets/config.js +248 -17
- package/assets/libs/style.css +201 -13
- package/dist/index.js +1 -1
- package/package.json +1 -1
package/assets/config.html
CHANGED
|
@@ -79,17 +79,40 @@
|
|
|
79
79
|
<li><strong>{{ t.feature5Title }}</strong>:{{ t.feature5Desc }}</li>
|
|
80
80
|
</ul>
|
|
81
81
|
|
|
82
|
+
<h3 class="command-title">
|
|
83
|
+
<i class="fa fa-book"></i>
|
|
84
|
+
{{ t.installTitle }}
|
|
85
|
+
</h3>
|
|
86
|
+
<ul class="install-list">
|
|
87
|
+
<li
|
|
88
|
+
v-for="cmd in installCommands"
|
|
89
|
+
:key="cmd.command"
|
|
90
|
+
class="command-item"
|
|
91
|
+
:title="t.clickToCopy"
|
|
92
|
+
@click="copyCommand(cmd.command)"
|
|
93
|
+
>
|
|
94
|
+
<i class="fa fa-chevron-right"></i>
|
|
95
|
+
<code class="item-cmd">{{ cmd.command }}</code>
|
|
96
|
+
<span class="item-desc">{{ cmd.desc }}</span>
|
|
97
|
+
</li>
|
|
98
|
+
</ul>
|
|
99
|
+
|
|
82
100
|
<h3 class="command-title">
|
|
83
101
|
<i class="fa fa-book"></i>
|
|
84
102
|
{{ t.commandUsageTitle }}
|
|
85
103
|
</h3>
|
|
86
104
|
<ul class="command-list">
|
|
87
|
-
<li
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
105
|
+
<li
|
|
106
|
+
v-for="cmd in usageCommands"
|
|
107
|
+
:key="cmd.command"
|
|
108
|
+
class="command-item"
|
|
109
|
+
:title="t.clickToCopy"
|
|
110
|
+
@click="copyCommand(cmd.command)"
|
|
111
|
+
>
|
|
112
|
+
<i class="fa fa-chevron-right"></i>
|
|
113
|
+
<code class="item-cmd">{{ cmd.command }}</code>
|
|
114
|
+
<span class="item-desc">{{ cmd.desc }}</span>
|
|
115
|
+
</li>
|
|
93
116
|
</ul>
|
|
94
117
|
</div>
|
|
95
118
|
|
|
@@ -124,16 +147,18 @@
|
|
|
124
147
|
|
|
125
148
|
|
|
126
149
|
<!-- AUTH 配置列表 -->
|
|
127
|
-
<div v-if="!loading">
|
|
128
|
-
<div v-for="(auth, index) in config.AUTH" :key="index"
|
|
129
|
-
:class="['auth-card', { 'auth-card-current': index === config.activeIndex, 'auth-card-dragging': draggingIndex === index, 'auth-card-drag-over': dragOverIndex === index }]"
|
|
130
|
-
|
|
131
|
-
@
|
|
132
|
-
@
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
@
|
|
150
|
+
<div v-if="!loading" :class="{ 'auth-list-ready': authListReady }">
|
|
151
|
+
<div v-for="(auth, index) in config.AUTH" :key="authKeys[index] || index"
|
|
152
|
+
:class="['auth-card', getDragShiftClass(index), { 'auth-card-current': index === config.activeIndex, 'auth-card-dragging': draggingIndex === index, 'auth-card-drag-over': dragOverIndex === index }]"
|
|
153
|
+
:style="getDragShiftStyle(index)"
|
|
154
|
+
@dragover="handleDragOver(index, $event)"
|
|
155
|
+
@dragleave="handleDragLeave($event)"
|
|
156
|
+
@drop="handleDrop(index, $event)">
|
|
157
|
+
<div class="drag-handle"
|
|
158
|
+
draggable="true"
|
|
159
|
+
@dragstart="handleDragStart(index, $event)"
|
|
160
|
+
@drag="handleDrag($event)"
|
|
161
|
+
@dragend="handleDragEnd($event)">
|
|
137
162
|
<i class="fa fa-ellipsis-v"></i>
|
|
138
163
|
</div>
|
|
139
164
|
<div class="auth-header">
|
|
@@ -217,11 +242,29 @@
|
|
|
217
242
|
</button>
|
|
218
243
|
</div>
|
|
219
244
|
</div>
|
|
220
|
-
</div>
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
245
|
+
</div>
|
|
246
|
+
|
|
247
|
+
<div v-if="currentCli === 'Codex'" class="codex-default-model">
|
|
248
|
+
<div class="default-model-header">
|
|
249
|
+
<label>
|
|
250
|
+
<i class="fa fa-terminal"></i>
|
|
251
|
+
{{ t.defaultModelTitle }}
|
|
252
|
+
</label>
|
|
253
|
+
<span class="default-model-chip">Codex</span>
|
|
254
|
+
</div>
|
|
255
|
+
<div class="default-model-input-wrap">
|
|
256
|
+
<i class="fa fa-magic"></i>
|
|
257
|
+
<input
|
|
258
|
+
v-model="auth.DEFAULT_MODEL"
|
|
259
|
+
type="text"
|
|
260
|
+
:placeholder="t.defaultModelPlaceholder"
|
|
261
|
+
>
|
|
262
|
+
</div>
|
|
263
|
+
</div>
|
|
264
|
+
|
|
265
|
+
<!-- 模型管理 -->
|
|
266
|
+
<div v-if="currentCli !== 'Codex'" class="models-section" :class="{ 'is-expanded': showModelSections[index] }">
|
|
267
|
+
<div class="models-header">
|
|
225
268
|
<label>
|
|
226
269
|
<i class="fa fa-cogs"></i>
|
|
227
270
|
{{ t.modelManagement }}
|
|
@@ -267,7 +310,7 @@
|
|
|
267
310
|
</div>
|
|
268
311
|
|
|
269
312
|
<!-- 环境变量配置 -->
|
|
270
|
-
<div class="env-section" :class="{ 'is-expanded': showEnvSections[index] }">
|
|
313
|
+
<div v-if="currentCli !== 'Codex'" class="env-section" :class="{ 'is-expanded': showEnvSections[index] }">
|
|
271
314
|
<div class="env-header">
|
|
272
315
|
<label>
|
|
273
316
|
<i class="fa fa-cogs"></i>
|
|
@@ -315,17 +358,8 @@
|
|
|
315
358
|
</div>
|
|
316
359
|
</div>
|
|
317
360
|
|
|
318
|
-
<!--
|
|
319
|
-
<
|
|
320
|
-
@click="addAuth"
|
|
321
|
-
class="btn btn-primary"
|
|
322
|
-
>
|
|
323
|
-
<i class="fa fa-plus-circle"></i>
|
|
324
|
-
{{ t.addNewConfig }}
|
|
325
|
-
</button>
|
|
326
|
-
|
|
327
|
-
<!-- 保存按钮 -->
|
|
328
|
-
<div class="btn-group">
|
|
361
|
+
<!-- 保存按钮 -->
|
|
362
|
+
<div class="btn-group">
|
|
329
363
|
<button
|
|
330
364
|
@click="saveConfig"
|
|
331
365
|
:disabled="saving"
|
|
@@ -346,11 +380,20 @@
|
|
|
346
380
|
</div>
|
|
347
381
|
</div>
|
|
348
382
|
|
|
349
|
-
<!-- Toast 容器 -->
|
|
350
|
-
<div id="toast-container"></div>
|
|
351
|
-
|
|
352
|
-
<!--
|
|
353
|
-
<button
|
|
383
|
+
<!-- Toast 容器 -->
|
|
384
|
+
<div id="toast-container"></div>
|
|
385
|
+
|
|
386
|
+
<!-- 右下角悬浮添加配置按钮 -->
|
|
387
|
+
<button
|
|
388
|
+
@click="addAuth"
|
|
389
|
+
class="fab-add"
|
|
390
|
+
:title="t.addNewConfig"
|
|
391
|
+
>
|
|
392
|
+
<i class="fa fa-plus"></i>
|
|
393
|
+
</button>
|
|
394
|
+
|
|
395
|
+
<!-- 右下角悬浮保存按钮 -->
|
|
396
|
+
<button
|
|
354
397
|
@click="saveConfig"
|
|
355
398
|
:disabled="saving"
|
|
356
399
|
class="fab-save"
|
package/assets/config.js
CHANGED
|
@@ -11,6 +11,8 @@ createApp({
|
|
|
11
11
|
showTokens: [],
|
|
12
12
|
showEnvSections: [],
|
|
13
13
|
showModelSections: [],
|
|
14
|
+
authKeys: [],
|
|
15
|
+
authListReady: false,
|
|
14
16
|
configPath: '',
|
|
15
17
|
showHelp: false,
|
|
16
18
|
currentCli: 'Claude',
|
|
@@ -25,9 +27,32 @@ createApp({
|
|
|
25
27
|
{ name: 'Codex', label: 'Codex', icon: 'images/gpt.png' },
|
|
26
28
|
{ name: 'Gemini', label: 'Gemini', icon: 'images/gemini.png' }
|
|
27
29
|
],
|
|
30
|
+
// 安装命令列表
|
|
31
|
+
installCommands: [
|
|
32
|
+
{ command: 'npm install -g @yesdgq/claude-buddy -f', desc: '安装包(强制覆盖)' },
|
|
33
|
+
{ command: 'npm update -g @yesdgq/claude-buddy', desc: '更新包,同步更新 claude code' },
|
|
34
|
+
{ command: 'npm install -g @yesdgq/claude-buddy@latest', desc: '更新包到最新版,同步更新 claude code' },
|
|
35
|
+
{ command: 'npm uninstall -g @yesdgq/claude-buddy', desc: '删除包' }
|
|
36
|
+
],
|
|
37
|
+
// 命令使用列表
|
|
38
|
+
usageCommands: [
|
|
39
|
+
{ command: 'ccby config', desc: '打开配置页面,配置您的 API 地址和访问令牌(TOKEN)' },
|
|
40
|
+
{ command: 'ccby', desc: '启动claude code' },
|
|
41
|
+
{ command: 'ccby -r', desc: '查看历史会话' },
|
|
42
|
+
{ command: 'ccby -c', desc: '继续上次会话' },
|
|
43
|
+
{ command: 'ccby --version', desc: '查看Claude-Buddy版本' },
|
|
44
|
+
{ command: 'claude --version', desc: '查看Claude版本' }
|
|
45
|
+
],
|
|
28
46
|
// 拖拽相关状态
|
|
29
47
|
draggingIndex: null,
|
|
30
48
|
dragOverIndex: null,
|
|
49
|
+
dropTargetIndex: null,
|
|
50
|
+
draggedCardHeight: 0,
|
|
51
|
+
draggedCardVisualHeight: 0,
|
|
52
|
+
dragCardRects: [],
|
|
53
|
+
dragStartY: 0,
|
|
54
|
+
dragCurrentY: 0,
|
|
55
|
+
isDropping: false,
|
|
31
56
|
// 版本更新信息
|
|
32
57
|
updateInfo: {
|
|
33
58
|
hasUpdate: false,
|
|
@@ -57,6 +82,7 @@ createApp({
|
|
|
57
82
|
feature4Desc: 'Visual interface, configure in seconds',
|
|
58
83
|
feature5Title: 'Developer First',
|
|
59
84
|
feature5Desc: 'Built for developers who love efficiency',
|
|
85
|
+
installTitle: 'ccby Update/Install',
|
|
60
86
|
commandUsageTitle: 'Command Usage',
|
|
61
87
|
command1: 'Open the configuration page to set your API URL and access token',
|
|
62
88
|
command2: 'Launch Claude Code',
|
|
@@ -73,6 +99,8 @@ createApp({
|
|
|
73
99
|
deleteThisConfig: 'Delete this configuration',
|
|
74
100
|
emptyConfigCannotDelete: 'Empty configuration cannot be deleted',
|
|
75
101
|
modelManagement: 'Model Management',
|
|
102
|
+
defaultModelTitle: 'Default Model',
|
|
103
|
+
defaultModelPlaceholder: 'Enter default GPT model',
|
|
76
104
|
collapse: 'Collapse',
|
|
77
105
|
expand: 'Expand',
|
|
78
106
|
modelNamePlaceholder: 'Model name',
|
|
@@ -108,9 +136,10 @@ createApp({
|
|
|
108
136
|
baseUrlPlaceholder: 'https://api.example.com',
|
|
109
137
|
tokenPlaceholder: 'Enter your API token',
|
|
110
138
|
updateAvailable: 'New version available:',
|
|
111
|
-
clickToCopy: 'Click to copy
|
|
139
|
+
clickToCopy: 'Click to copy command',
|
|
112
140
|
dismiss: 'Dismiss',
|
|
113
|
-
updateCmdCopied: 'Update command copied!'
|
|
141
|
+
updateCmdCopied: 'Update command copied!',
|
|
142
|
+
cmdCopied: 'Command copied!'
|
|
114
143
|
},
|
|
115
144
|
zh: {
|
|
116
145
|
helpTitle: '使用说明',
|
|
@@ -125,6 +154,7 @@ createApp({
|
|
|
125
154
|
feature4Desc: '可视化界面,几秒完成设置',
|
|
126
155
|
feature5Title: '开发者友好',
|
|
127
156
|
feature5Desc: '专为追求效率的开发者打造',
|
|
157
|
+
installTitle: 'ccby更新/安装',
|
|
128
158
|
commandUsageTitle: '命令使用',
|
|
129
159
|
command1: '打开配置页面,配置您的 API 地址和访问令牌(TOKEN)',
|
|
130
160
|
command2: '启动claude code',
|
|
@@ -141,6 +171,8 @@ createApp({
|
|
|
141
171
|
deleteThisConfig: '删除此配置',
|
|
142
172
|
emptyConfigCannotDelete: '空配置不能删除',
|
|
143
173
|
modelManagement: '模型管理',
|
|
174
|
+
defaultModelTitle: '默认模型',
|
|
175
|
+
defaultModelPlaceholder: '输入默认gpt模型',
|
|
144
176
|
collapse: '收起',
|
|
145
177
|
expand: '展开',
|
|
146
178
|
modelNamePlaceholder: '模型名称',
|
|
@@ -176,9 +208,10 @@ createApp({
|
|
|
176
208
|
baseUrlPlaceholder: 'https://api.example.com',
|
|
177
209
|
tokenPlaceholder: '输入您的 API 令牌',
|
|
178
210
|
updateAvailable: '新版本可用:',
|
|
179
|
-
clickToCopy: '
|
|
211
|
+
clickToCopy: '点击复制命令',
|
|
180
212
|
dismiss: '关闭',
|
|
181
|
-
updateCmdCopied: '更新命令已复制!'
|
|
213
|
+
updateCmdCopied: '更新命令已复制!',
|
|
214
|
+
cmdCopied: '命令已复制!'
|
|
182
215
|
}
|
|
183
216
|
}
|
|
184
217
|
};
|
|
@@ -300,6 +333,28 @@ createApp({
|
|
|
300
333
|
});
|
|
301
334
|
},
|
|
302
335
|
|
|
336
|
+
// 通用复制命令方法
|
|
337
|
+
copyCommand(cmd) {
|
|
338
|
+
navigator.clipboard.writeText(cmd).then(() => {
|
|
339
|
+
this.showToast(this.t.cmdCopied);
|
|
340
|
+
}).catch(() => {
|
|
341
|
+
// fallback: 创建临时textarea并选中文本
|
|
342
|
+
const textarea = document.createElement('textarea');
|
|
343
|
+
textarea.value = cmd;
|
|
344
|
+
textarea.style.position = 'fixed';
|
|
345
|
+
textarea.style.opacity = '0';
|
|
346
|
+
document.body.appendChild(textarea);
|
|
347
|
+
textarea.select();
|
|
348
|
+
try {
|
|
349
|
+
document.execCommand('copy');
|
|
350
|
+
this.showToast(this.t.cmdCopied);
|
|
351
|
+
} catch (err) {
|
|
352
|
+
this.showToast('Copy failed', 'error');
|
|
353
|
+
}
|
|
354
|
+
document.body.removeChild(textarea);
|
|
355
|
+
});
|
|
356
|
+
},
|
|
357
|
+
|
|
303
358
|
// 重置服务器倒计时
|
|
304
359
|
async resetTimer() {
|
|
305
360
|
try {
|
|
@@ -388,6 +443,7 @@ createApp({
|
|
|
388
443
|
cliType = this.currentCli.toLowerCase();
|
|
389
444
|
}
|
|
390
445
|
this.loading = true;
|
|
446
|
+
this.authListReady = false;
|
|
391
447
|
this.error = null;
|
|
392
448
|
try {
|
|
393
449
|
const response = await axios.get('/api/config', {
|
|
@@ -416,6 +472,7 @@ createApp({
|
|
|
416
472
|
name: '',
|
|
417
473
|
BASE_URL: '',
|
|
418
474
|
TOKEN: '',
|
|
475
|
+
DEFAULT_MODEL: '',
|
|
419
476
|
MODELS: [],
|
|
420
477
|
ENV: []
|
|
421
478
|
}];
|
|
@@ -430,12 +487,16 @@ createApp({
|
|
|
430
487
|
if (!auth.MODELS) {
|
|
431
488
|
auth.MODELS = [];
|
|
432
489
|
}
|
|
490
|
+
if (cliType === 'codex' && typeof auth.DEFAULT_MODEL !== 'string') {
|
|
491
|
+
auth.DEFAULT_MODEL = '';
|
|
492
|
+
}
|
|
433
493
|
});
|
|
434
494
|
|
|
435
495
|
// 先设置辅助数组,再设置 config,确保 Vue 响应式正确更新
|
|
436
496
|
this.showTokens = new Array(config.AUTH.length).fill(false);
|
|
437
497
|
this.showEnvSections = new Array(config.AUTH.length).fill(false);
|
|
438
498
|
this.showModelSections = new Array(config.AUTH.length).fill(false);
|
|
499
|
+
this.authKeys = config.AUTH.map(() => this.createAuthKey());
|
|
439
500
|
|
|
440
501
|
// 最后更新 config
|
|
441
502
|
this.config = config;
|
|
@@ -447,6 +508,11 @@ createApp({
|
|
|
447
508
|
this.showToast(this.t.configLoadFailed + ': ' + (err.response?.data?.error || err.message), 'error');
|
|
448
509
|
} finally {
|
|
449
510
|
this.loading = false;
|
|
511
|
+
this.$nextTick(() => {
|
|
512
|
+
requestAnimationFrame(() => {
|
|
513
|
+
this.authListReady = true;
|
|
514
|
+
});
|
|
515
|
+
});
|
|
450
516
|
}
|
|
451
517
|
},
|
|
452
518
|
|
|
@@ -455,6 +521,7 @@ createApp({
|
|
|
455
521
|
this.error = null;
|
|
456
522
|
this.successMessage = null;
|
|
457
523
|
try {
|
|
524
|
+
this.normalizeCodexDefaults();
|
|
458
525
|
// 创建配置副本,过滤掉空配置项
|
|
459
526
|
const configToSave = {
|
|
460
527
|
...this.config,
|
|
@@ -483,17 +550,28 @@ createApp({
|
|
|
483
550
|
}
|
|
484
551
|
},
|
|
485
552
|
|
|
553
|
+
normalizeCodexDefaults() {
|
|
554
|
+
if (this.currentCli !== 'Codex') {
|
|
555
|
+
return;
|
|
556
|
+
}
|
|
557
|
+
this.config.AUTH.forEach(auth => {
|
|
558
|
+
auth.DEFAULT_MODEL = (auth.DEFAULT_MODEL || '').trim();
|
|
559
|
+
});
|
|
560
|
+
},
|
|
561
|
+
|
|
486
562
|
addAuth() {
|
|
487
563
|
this.config.AUTH.push({
|
|
488
564
|
name: '',
|
|
489
565
|
BASE_URL: '',
|
|
490
566
|
TOKEN: '',
|
|
567
|
+
DEFAULT_MODEL: '',
|
|
491
568
|
MODELS: [],
|
|
492
569
|
ENV: []
|
|
493
570
|
});
|
|
494
571
|
this.showTokens.push(false);
|
|
495
572
|
this.showEnvSections.push(false);
|
|
496
573
|
this.showModelSections.push(false);
|
|
574
|
+
this.authKeys.push(this.createAuthKey());
|
|
497
575
|
// 重置服务器倒计时
|
|
498
576
|
this.resetTimer();
|
|
499
577
|
},
|
|
@@ -517,6 +595,7 @@ createApp({
|
|
|
517
595
|
const deletedTokenState = this.showTokens[index];
|
|
518
596
|
const deletedEnvState = this.showEnvSections[index];
|
|
519
597
|
const deletedModelState = this.showModelSections[index];
|
|
598
|
+
const deletedAuthKey = this.authKeys[index];
|
|
520
599
|
const oldActiveIndex = this.config.activeIndex;
|
|
521
600
|
|
|
522
601
|
// 从列表中删除
|
|
@@ -524,6 +603,7 @@ createApp({
|
|
|
524
603
|
this.showTokens.splice(index, 1);
|
|
525
604
|
this.showEnvSections.splice(index, 1);
|
|
526
605
|
this.showModelSections.splice(index, 1);
|
|
606
|
+
this.authKeys.splice(index, 1);
|
|
527
607
|
|
|
528
608
|
// 调整 activeIndex:如果删除的是当前使用的配置,或者删除的配置在当前使用配置之前
|
|
529
609
|
if (index === this.config.activeIndex) {
|
|
@@ -541,17 +621,20 @@ createApp({
|
|
|
541
621
|
name: '',
|
|
542
622
|
BASE_URL: '',
|
|
543
623
|
TOKEN: '',
|
|
624
|
+
DEFAULT_MODEL: '',
|
|
544
625
|
MODELS: [],
|
|
545
626
|
ENV: []
|
|
546
627
|
});
|
|
547
628
|
this.showTokens.push(false);
|
|
548
629
|
this.showEnvSections.push(false);
|
|
549
630
|
this.showModelSections.push(false);
|
|
631
|
+
this.authKeys.push(this.createAuthKey());
|
|
550
632
|
this.config.activeIndex = 0;
|
|
551
633
|
}
|
|
552
634
|
|
|
553
635
|
// 立即保存配置,过滤掉空配置项(但保留至少一个配置)
|
|
554
636
|
try {
|
|
637
|
+
this.normalizeCodexDefaults();
|
|
555
638
|
const filteredAuth = this.config.AUTH.filter(auth => !this.isEmptyConfig(auth));
|
|
556
639
|
const configToSave = {
|
|
557
640
|
...this.config,
|
|
@@ -572,6 +655,7 @@ createApp({
|
|
|
572
655
|
this.showTokens.splice(index, 0, deletedTokenState);
|
|
573
656
|
this.showEnvSections.splice(index, 0, deletedEnvState);
|
|
574
657
|
this.showModelSections.splice(index, 0, deletedModelState);
|
|
658
|
+
this.authKeys.splice(index, 0, deletedAuthKey);
|
|
575
659
|
this.config.activeIndex = oldActiveIndex;
|
|
576
660
|
}
|
|
577
661
|
});
|
|
@@ -588,6 +672,7 @@ createApp({
|
|
|
588
672
|
|
|
589
673
|
// 立即保存配置
|
|
590
674
|
try {
|
|
675
|
+
this.normalizeCodexDefaults();
|
|
591
676
|
const configToSave = {
|
|
592
677
|
...this.config,
|
|
593
678
|
AUTH: this.config.AUTH.filter(auth => !this.isEmptyConfig(auth))
|
|
@@ -650,7 +735,12 @@ createApp({
|
|
|
650
735
|
|
|
651
736
|
// 判断是否为空配置
|
|
652
737
|
isEmptyConfig(auth) {
|
|
653
|
-
|
|
738
|
+
const hasDefaultModel = this.currentCli === 'Codex' && auth.DEFAULT_MODEL && auth.DEFAULT_MODEL.trim();
|
|
739
|
+
return !auth.name && !auth.BASE_URL && !auth.TOKEN && !hasDefaultModel && (!auth.ENV || auth.ENV.length === 0) && (!auth.MODELS || auth.MODELS.length === 0);
|
|
740
|
+
},
|
|
741
|
+
|
|
742
|
+
createAuthKey() {
|
|
743
|
+
return `auth-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
654
744
|
},
|
|
655
745
|
|
|
656
746
|
// 切换使用说明显示/隐藏
|
|
@@ -658,6 +748,41 @@ createApp({
|
|
|
658
748
|
this.showHelp = !this.showHelp;
|
|
659
749
|
},
|
|
660
750
|
|
|
751
|
+
getDragShiftClass(index) {
|
|
752
|
+
if (this.draggingIndex === null || this.dropTargetIndex === null || this.isDropping || index === this.draggingIndex) {
|
|
753
|
+
return '';
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
if (this.draggingIndex < this.dropTargetIndex && index > this.draggingIndex && index <= this.dropTargetIndex) {
|
|
757
|
+
return 'auth-card-shift-up';
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
if (this.draggingIndex > this.dropTargetIndex && index >= this.dropTargetIndex && index < this.draggingIndex) {
|
|
761
|
+
return 'auth-card-shift-down';
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
return '';
|
|
765
|
+
},
|
|
766
|
+
|
|
767
|
+
getDragShiftStyle(index) {
|
|
768
|
+
if (index === this.draggingIndex) {
|
|
769
|
+
const offset = this.dragCurrentY - this.dragStartY;
|
|
770
|
+
return {
|
|
771
|
+
'--drag-follow-offset': `${offset}px`
|
|
772
|
+
};
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
const shiftClass = this.getDragShiftClass(index);
|
|
776
|
+
if (!shiftClass || !this.draggedCardHeight) {
|
|
777
|
+
return {};
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
const offset = shiftClass === 'auth-card-shift-up' ? -this.draggedCardHeight : this.draggedCardHeight;
|
|
781
|
+
return {
|
|
782
|
+
'--drag-shift-offset': `${offset}px`
|
|
783
|
+
};
|
|
784
|
+
},
|
|
785
|
+
|
|
661
786
|
// 选择 CLI 类型
|
|
662
787
|
async selectCli(cliName) {
|
|
663
788
|
if (cliName === 'Gemini') {
|
|
@@ -672,25 +797,105 @@ createApp({
|
|
|
672
797
|
// 拖拽开始
|
|
673
798
|
handleDragStart(index, event) {
|
|
674
799
|
this.draggingIndex = index;
|
|
800
|
+
this.dragOverIndex = null;
|
|
801
|
+
this.dropTargetIndex = null;
|
|
802
|
+
this.isDropping = false;
|
|
803
|
+
const card = event.currentTarget.closest('.auth-card');
|
|
804
|
+
const cardStyle = window.getComputedStyle(card);
|
|
805
|
+
this.draggedCardHeight = card.offsetHeight + parseFloat(cardStyle.marginBottom || 0);
|
|
806
|
+
this.draggedCardVisualHeight = card.offsetHeight;
|
|
807
|
+
this.dragCardRects = Array.from(document.querySelectorAll('.auth-card')).map(cardEl => {
|
|
808
|
+
const rect = cardEl.getBoundingClientRect();
|
|
809
|
+
return {
|
|
810
|
+
top: rect.top,
|
|
811
|
+
bottom: rect.bottom,
|
|
812
|
+
height: rect.height
|
|
813
|
+
};
|
|
814
|
+
});
|
|
815
|
+
this.dragStartY = event.clientY;
|
|
816
|
+
this.dragCurrentY = event.clientY;
|
|
675
817
|
event.dataTransfer.effectAllowed = 'move';
|
|
676
818
|
// 设置拖拽时的透明度
|
|
677
819
|
event.target.style.opacity = '0.5';
|
|
678
820
|
},
|
|
679
821
|
|
|
822
|
+
handleDrag(event) {
|
|
823
|
+
if (this.draggingIndex === null || event.clientY === 0) {
|
|
824
|
+
return;
|
|
825
|
+
}
|
|
826
|
+
this.dragCurrentY = event.clientY;
|
|
827
|
+
this.updateDropTargetByY(event.clientY);
|
|
828
|
+
},
|
|
829
|
+
|
|
680
830
|
// 拖拽结束
|
|
681
831
|
handleDragEnd(event) {
|
|
832
|
+
if (this.isDropping) {
|
|
833
|
+
event.target.style.opacity = '1';
|
|
834
|
+
return;
|
|
835
|
+
}
|
|
682
836
|
this.draggingIndex = null;
|
|
683
837
|
this.dragOverIndex = null;
|
|
838
|
+
this.dropTargetIndex = null;
|
|
839
|
+
this.draggedCardHeight = 0;
|
|
840
|
+
this.draggedCardVisualHeight = 0;
|
|
841
|
+
this.dragCardRects = [];
|
|
842
|
+
this.dragStartY = 0;
|
|
843
|
+
this.dragCurrentY = 0;
|
|
844
|
+
this.isDropping = false;
|
|
684
845
|
event.target.style.opacity = '1';
|
|
685
846
|
},
|
|
686
847
|
|
|
687
848
|
// 拖拽经过
|
|
688
849
|
handleDragOver(index, event) {
|
|
689
850
|
event.preventDefault();
|
|
690
|
-
if (this.draggingIndex === null
|
|
851
|
+
if (this.draggingIndex === null) {
|
|
852
|
+
return;
|
|
853
|
+
}
|
|
854
|
+
if (this.draggingIndex === index) {
|
|
855
|
+
return;
|
|
856
|
+
}
|
|
857
|
+
if (event.clientY !== 0) {
|
|
858
|
+
this.dragCurrentY = event.clientY;
|
|
859
|
+
}
|
|
860
|
+
this.updateDropTargetByY(event.clientY);
|
|
861
|
+
if (this.dragOverIndex !== index) {
|
|
862
|
+
this.dragOverIndex = index;
|
|
863
|
+
}
|
|
864
|
+
},
|
|
865
|
+
|
|
866
|
+
updateDropTargetByY(clientY) {
|
|
867
|
+
if (this.draggingIndex === null || clientY === 0) {
|
|
691
868
|
return;
|
|
692
869
|
}
|
|
693
|
-
|
|
870
|
+
|
|
871
|
+
const dragOffset = clientY - this.dragStartY;
|
|
872
|
+
const draggedRect = this.dragCardRects[this.draggingIndex];
|
|
873
|
+
if (!draggedRect) {
|
|
874
|
+
return;
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
const draggedTop = draggedRect.top + dragOffset;
|
|
878
|
+
const draggedBottom = draggedTop + this.draggedCardVisualHeight;
|
|
879
|
+
|
|
880
|
+
const crossedIndexes = this.dragCardRects
|
|
881
|
+
.map((rect, index) => ({ rect, index }))
|
|
882
|
+
.filter(({ index }) => index !== this.draggingIndex)
|
|
883
|
+
.filter(({ rect, index }) => {
|
|
884
|
+
const targetMiddle = rect.top + rect.height / 2;
|
|
885
|
+
return this.draggingIndex < index
|
|
886
|
+
? draggedBottom > targetMiddle
|
|
887
|
+
: draggedTop < targetMiddle;
|
|
888
|
+
})
|
|
889
|
+
.map(({ index }) => index);
|
|
890
|
+
|
|
891
|
+
const nextTargetIndex = this.draggingIndex < (crossedIndexes[0] ?? this.draggingIndex)
|
|
892
|
+
? crossedIndexes[crossedIndexes.length - 1]
|
|
893
|
+
: crossedIndexes[0];
|
|
894
|
+
|
|
895
|
+
const normalizedTargetIndex = nextTargetIndex === undefined ? null : nextTargetIndex;
|
|
896
|
+
if (this.dropTargetIndex !== normalizedTargetIndex) {
|
|
897
|
+
this.dropTargetIndex = normalizedTargetIndex;
|
|
898
|
+
}
|
|
694
899
|
},
|
|
695
900
|
|
|
696
901
|
// 拖拽离开
|
|
@@ -701,10 +906,13 @@ createApp({
|
|
|
701
906
|
// 放下
|
|
702
907
|
async handleDrop(index, event) {
|
|
703
908
|
event.preventDefault();
|
|
704
|
-
|
|
909
|
+
const targetIndex = this.dropTargetIndex ?? index;
|
|
910
|
+
if (this.draggingIndex === null || this.draggingIndex === targetIndex) {
|
|
705
911
|
this.dragOverIndex = null;
|
|
912
|
+
this.dropTargetIndex = null;
|
|
706
913
|
return;
|
|
707
914
|
}
|
|
915
|
+
this.isDropping = true;
|
|
708
916
|
|
|
709
917
|
// 保存原始数据,以便失败时恢复
|
|
710
918
|
const oldAuth = [...this.config.AUTH];
|
|
@@ -712,41 +920,63 @@ createApp({
|
|
|
712
920
|
const oldShowTokens = [...this.showTokens];
|
|
713
921
|
const oldShowEnvSections = [...this.showEnvSections];
|
|
714
922
|
const oldShowModelSections = [...this.showModelSections];
|
|
923
|
+
const oldAuthKeys = [...this.authKeys];
|
|
715
924
|
|
|
716
925
|
// 移动配置项
|
|
717
926
|
const [movedAuth] = this.config.AUTH.splice(this.draggingIndex, 1);
|
|
718
|
-
this.config.AUTH.splice(
|
|
927
|
+
this.config.AUTH.splice(targetIndex, 0, movedAuth);
|
|
719
928
|
|
|
720
929
|
// 同步移动显示状态
|
|
721
930
|
const [movedToken] = this.showTokens.splice(this.draggingIndex, 1);
|
|
722
|
-
this.showTokens.splice(
|
|
931
|
+
this.showTokens.splice(targetIndex, 0, movedToken);
|
|
723
932
|
|
|
724
933
|
const [movedEnv] = this.showEnvSections.splice(this.draggingIndex, 1);
|
|
725
|
-
this.showEnvSections.splice(
|
|
934
|
+
this.showEnvSections.splice(targetIndex, 0, movedEnv);
|
|
726
935
|
|
|
727
936
|
const [movedModel] = this.showModelSections.splice(this.draggingIndex, 1);
|
|
728
|
-
this.showModelSections.splice(
|
|
937
|
+
this.showModelSections.splice(targetIndex, 0, movedModel);
|
|
938
|
+
|
|
939
|
+
const [movedAuthKey] = this.authKeys.splice(this.draggingIndex, 1);
|
|
940
|
+
this.authKeys.splice(targetIndex, 0, movedAuthKey);
|
|
729
941
|
|
|
730
942
|
// 调整 activeIndex
|
|
731
943
|
if (this.draggingIndex === this.config.activeIndex) {
|
|
732
944
|
// 拖拽的是当前使用的配置,直接移动到目标位置
|
|
733
|
-
this.config.activeIndex =
|
|
734
|
-
} else if (this.draggingIndex < this.config.activeIndex &&
|
|
945
|
+
this.config.activeIndex = targetIndex;
|
|
946
|
+
} else if (this.draggingIndex < this.config.activeIndex && targetIndex >= this.config.activeIndex) {
|
|
735
947
|
// 从前向后拖拽,经过当前使用的配置
|
|
736
948
|
// activeIndex 位置的元素被向前移了一位,所以需要减 1
|
|
737
949
|
this.config.activeIndex--;
|
|
738
|
-
} else if (this.draggingIndex > this.config.activeIndex &&
|
|
950
|
+
} else if (this.draggingIndex > this.config.activeIndex && targetIndex <= this.config.activeIndex) {
|
|
739
951
|
// 从后向前拖拽,经过当前使用的配置
|
|
740
952
|
// activeIndex 位置的元素被向后移了一位,所以需要加 1
|
|
741
953
|
this.config.activeIndex++;
|
|
742
954
|
}
|
|
743
955
|
|
|
744
|
-
|
|
745
|
-
this.draggingIndex = null;
|
|
956
|
+
this.draggingIndex = targetIndex;
|
|
746
957
|
this.dragOverIndex = null;
|
|
958
|
+
this.dropTargetIndex = null;
|
|
959
|
+
this.dragCurrentY = this.dragStartY;
|
|
960
|
+
|
|
961
|
+
// 先完成 DOM 重排,下一帧再清理拖拽状态,避免松手时出现明显回弹刷新。
|
|
962
|
+
requestAnimationFrame(() => {
|
|
963
|
+
if (!this.isDropping || this.draggingIndex !== targetIndex) {
|
|
964
|
+
return;
|
|
965
|
+
}
|
|
966
|
+
this.draggingIndex = null;
|
|
967
|
+
this.dragOverIndex = null;
|
|
968
|
+
this.dropTargetIndex = null;
|
|
969
|
+
this.draggedCardHeight = 0;
|
|
970
|
+
this.draggedCardVisualHeight = 0;
|
|
971
|
+
this.dragCardRects = [];
|
|
972
|
+
this.dragStartY = 0;
|
|
973
|
+
this.dragCurrentY = 0;
|
|
974
|
+
this.isDropping = false;
|
|
975
|
+
});
|
|
747
976
|
|
|
748
977
|
// 保存配置
|
|
749
978
|
try {
|
|
979
|
+
this.normalizeCodexDefaults();
|
|
750
980
|
const configToSave = {
|
|
751
981
|
...this.config,
|
|
752
982
|
AUTH: this.config.AUTH.filter(auth => !this.isEmptyConfig(auth))
|
|
@@ -766,6 +996,7 @@ createApp({
|
|
|
766
996
|
this.showTokens = oldShowTokens;
|
|
767
997
|
this.showEnvSections = oldShowEnvSections;
|
|
768
998
|
this.showModelSections = oldShowModelSections;
|
|
999
|
+
this.authKeys = oldAuthKeys;
|
|
769
1000
|
}
|
|
770
1001
|
}
|
|
771
1002
|
},
|