@yesdgq/claude-buddy 1.1.1018 → 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.
@@ -147,16 +147,18 @@
147
147
 
148
148
 
149
149
  <!-- AUTH 配置列表 -->
150
- <div v-if="!loading">
151
- <div v-for="(auth, index) in config.AUTH" :key="index"
152
- :class="['auth-card', { 'auth-card-current': index === config.activeIndex, 'auth-card-dragging': draggingIndex === index, 'auth-card-drag-over': dragOverIndex === index }]"
153
- @dragover="handleDragOver(index, $event)"
154
- @dragleave="handleDragLeave($event)"
155
- @drop="handleDrop(index, $event)">
156
- <div class="drag-handle"
157
- draggable="true"
158
- @dragstart="handleDragStart(index, $event)"
159
- @dragend="handleDragEnd($event)">
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)">
160
162
  <i class="fa fa-ellipsis-v"></i>
161
163
  </div>
162
164
  <div class="auth-header">
@@ -240,11 +242,29 @@
240
242
  </button>
241
243
  </div>
242
244
  </div>
243
- </div>
244
-
245
- <!-- 模型管理 -->
246
- <div class="models-section" :class="{ 'is-expanded': showModelSections[index] }">
247
- <div class="models-header">
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">
248
268
  <label>
249
269
  <i class="fa fa-cogs"></i>
250
270
  {{ t.modelManagement }}
@@ -290,7 +310,7 @@
290
310
  </div>
291
311
 
292
312
  <!-- 环境变量配置 -->
293
- <div class="env-section" :class="{ 'is-expanded': showEnvSections[index] }">
313
+ <div v-if="currentCli !== 'Codex'" class="env-section" :class="{ 'is-expanded': showEnvSections[index] }">
294
314
  <div class="env-header">
295
315
  <label>
296
316
  <i class="fa fa-cogs"></i>
@@ -338,17 +358,8 @@
338
358
  </div>
339
359
  </div>
340
360
 
341
- <!-- 添加新配置按钮 -->
342
- <button
343
- @click="addAuth"
344
- class="btn btn-primary"
345
- >
346
- <i class="fa fa-plus-circle"></i>
347
- {{ t.addNewConfig }}
348
- </button>
349
-
350
- <!-- 保存按钮 -->
351
- <div class="btn-group">
361
+ <!-- 保存按钮 -->
362
+ <div class="btn-group">
352
363
  <button
353
364
  @click="saveConfig"
354
365
  :disabled="saving"
@@ -369,11 +380,20 @@
369
380
  </div>
370
381
  </div>
371
382
 
372
- <!-- Toast 容器 -->
373
- <div id="toast-container"></div>
374
-
375
- <!-- 右下角悬浮保存按钮 -->
376
- <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
377
397
  @click="saveConfig"
378
398
  :disabled="saving"
379
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',
@@ -44,6 +46,13 @@ createApp({
44
46
  // 拖拽相关状态
45
47
  draggingIndex: null,
46
48
  dragOverIndex: null,
49
+ dropTargetIndex: null,
50
+ draggedCardHeight: 0,
51
+ draggedCardVisualHeight: 0,
52
+ dragCardRects: [],
53
+ dragStartY: 0,
54
+ dragCurrentY: 0,
55
+ isDropping: false,
47
56
  // 版本更新信息
48
57
  updateInfo: {
49
58
  hasUpdate: false,
@@ -90,6 +99,8 @@ createApp({
90
99
  deleteThisConfig: 'Delete this configuration',
91
100
  emptyConfigCannotDelete: 'Empty configuration cannot be deleted',
92
101
  modelManagement: 'Model Management',
102
+ defaultModelTitle: 'Default Model',
103
+ defaultModelPlaceholder: 'Enter default GPT model',
93
104
  collapse: 'Collapse',
94
105
  expand: 'Expand',
95
106
  modelNamePlaceholder: 'Model name',
@@ -160,6 +171,8 @@ createApp({
160
171
  deleteThisConfig: '删除此配置',
161
172
  emptyConfigCannotDelete: '空配置不能删除',
162
173
  modelManagement: '模型管理',
174
+ defaultModelTitle: '默认模型',
175
+ defaultModelPlaceholder: '输入默认gpt模型',
163
176
  collapse: '收起',
164
177
  expand: '展开',
165
178
  modelNamePlaceholder: '模型名称',
@@ -430,6 +443,7 @@ createApp({
430
443
  cliType = this.currentCli.toLowerCase();
431
444
  }
432
445
  this.loading = true;
446
+ this.authListReady = false;
433
447
  this.error = null;
434
448
  try {
435
449
  const response = await axios.get('/api/config', {
@@ -458,6 +472,7 @@ createApp({
458
472
  name: '',
459
473
  BASE_URL: '',
460
474
  TOKEN: '',
475
+ DEFAULT_MODEL: '',
461
476
  MODELS: [],
462
477
  ENV: []
463
478
  }];
@@ -472,12 +487,16 @@ createApp({
472
487
  if (!auth.MODELS) {
473
488
  auth.MODELS = [];
474
489
  }
490
+ if (cliType === 'codex' && typeof auth.DEFAULT_MODEL !== 'string') {
491
+ auth.DEFAULT_MODEL = '';
492
+ }
475
493
  });
476
494
 
477
495
  // 先设置辅助数组,再设置 config,确保 Vue 响应式正确更新
478
496
  this.showTokens = new Array(config.AUTH.length).fill(false);
479
497
  this.showEnvSections = new Array(config.AUTH.length).fill(false);
480
498
  this.showModelSections = new Array(config.AUTH.length).fill(false);
499
+ this.authKeys = config.AUTH.map(() => this.createAuthKey());
481
500
 
482
501
  // 最后更新 config
483
502
  this.config = config;
@@ -489,6 +508,11 @@ createApp({
489
508
  this.showToast(this.t.configLoadFailed + ': ' + (err.response?.data?.error || err.message), 'error');
490
509
  } finally {
491
510
  this.loading = false;
511
+ this.$nextTick(() => {
512
+ requestAnimationFrame(() => {
513
+ this.authListReady = true;
514
+ });
515
+ });
492
516
  }
493
517
  },
494
518
 
@@ -497,6 +521,7 @@ createApp({
497
521
  this.error = null;
498
522
  this.successMessage = null;
499
523
  try {
524
+ this.normalizeCodexDefaults();
500
525
  // 创建配置副本,过滤掉空配置项
501
526
  const configToSave = {
502
527
  ...this.config,
@@ -525,17 +550,28 @@ createApp({
525
550
  }
526
551
  },
527
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
+
528
562
  addAuth() {
529
563
  this.config.AUTH.push({
530
564
  name: '',
531
565
  BASE_URL: '',
532
566
  TOKEN: '',
567
+ DEFAULT_MODEL: '',
533
568
  MODELS: [],
534
569
  ENV: []
535
570
  });
536
571
  this.showTokens.push(false);
537
572
  this.showEnvSections.push(false);
538
573
  this.showModelSections.push(false);
574
+ this.authKeys.push(this.createAuthKey());
539
575
  // 重置服务器倒计时
540
576
  this.resetTimer();
541
577
  },
@@ -559,6 +595,7 @@ createApp({
559
595
  const deletedTokenState = this.showTokens[index];
560
596
  const deletedEnvState = this.showEnvSections[index];
561
597
  const deletedModelState = this.showModelSections[index];
598
+ const deletedAuthKey = this.authKeys[index];
562
599
  const oldActiveIndex = this.config.activeIndex;
563
600
 
564
601
  // 从列表中删除
@@ -566,6 +603,7 @@ createApp({
566
603
  this.showTokens.splice(index, 1);
567
604
  this.showEnvSections.splice(index, 1);
568
605
  this.showModelSections.splice(index, 1);
606
+ this.authKeys.splice(index, 1);
569
607
 
570
608
  // 调整 activeIndex:如果删除的是当前使用的配置,或者删除的配置在当前使用配置之前
571
609
  if (index === this.config.activeIndex) {
@@ -583,17 +621,20 @@ createApp({
583
621
  name: '',
584
622
  BASE_URL: '',
585
623
  TOKEN: '',
624
+ DEFAULT_MODEL: '',
586
625
  MODELS: [],
587
626
  ENV: []
588
627
  });
589
628
  this.showTokens.push(false);
590
629
  this.showEnvSections.push(false);
591
630
  this.showModelSections.push(false);
631
+ this.authKeys.push(this.createAuthKey());
592
632
  this.config.activeIndex = 0;
593
633
  }
594
634
 
595
635
  // 立即保存配置,过滤掉空配置项(但保留至少一个配置)
596
636
  try {
637
+ this.normalizeCodexDefaults();
597
638
  const filteredAuth = this.config.AUTH.filter(auth => !this.isEmptyConfig(auth));
598
639
  const configToSave = {
599
640
  ...this.config,
@@ -614,6 +655,7 @@ createApp({
614
655
  this.showTokens.splice(index, 0, deletedTokenState);
615
656
  this.showEnvSections.splice(index, 0, deletedEnvState);
616
657
  this.showModelSections.splice(index, 0, deletedModelState);
658
+ this.authKeys.splice(index, 0, deletedAuthKey);
617
659
  this.config.activeIndex = oldActiveIndex;
618
660
  }
619
661
  });
@@ -630,6 +672,7 @@ createApp({
630
672
 
631
673
  // 立即保存配置
632
674
  try {
675
+ this.normalizeCodexDefaults();
633
676
  const configToSave = {
634
677
  ...this.config,
635
678
  AUTH: this.config.AUTH.filter(auth => !this.isEmptyConfig(auth))
@@ -692,7 +735,12 @@ createApp({
692
735
 
693
736
  // 判断是否为空配置
694
737
  isEmptyConfig(auth) {
695
- return !auth.name && !auth.BASE_URL && !auth.TOKEN && (!auth.ENV || auth.ENV.length === 0) && (!auth.MODELS || auth.MODELS.length === 0);
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)}`;
696
744
  },
697
745
 
698
746
  // 切换使用说明显示/隐藏
@@ -700,9 +748,44 @@ createApp({
700
748
  this.showHelp = !this.showHelp;
701
749
  },
702
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
+
703
786
  // 选择 CLI 类型
704
787
  async selectCli(cliName) {
705
- if (cliName === 'Gemini' || cliName === 'Codex') {
788
+ if (cliName === 'Gemini') {
706
789
  this.showToast(this.t.notAvailableYet, 'error');
707
790
  this.resetTimer();
708
791
  return;
@@ -714,25 +797,105 @@ createApp({
714
797
  // 拖拽开始
715
798
  handleDragStart(index, event) {
716
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;
717
817
  event.dataTransfer.effectAllowed = 'move';
718
818
  // 设置拖拽时的透明度
719
819
  event.target.style.opacity = '0.5';
720
820
  },
721
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
+
722
830
  // 拖拽结束
723
831
  handleDragEnd(event) {
832
+ if (this.isDropping) {
833
+ event.target.style.opacity = '1';
834
+ return;
835
+ }
724
836
  this.draggingIndex = null;
725
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;
726
845
  event.target.style.opacity = '1';
727
846
  },
728
847
 
729
848
  // 拖拽经过
730
849
  handleDragOver(index, event) {
731
850
  event.preventDefault();
732
- if (this.draggingIndex === null || this.draggingIndex === index) {
851
+ if (this.draggingIndex === null) {
733
852
  return;
734
853
  }
735
- this.dragOverIndex = index;
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) {
868
+ return;
869
+ }
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
+ }
736
899
  },
737
900
 
738
901
  // 拖拽离开
@@ -743,10 +906,13 @@ createApp({
743
906
  // 放下
744
907
  async handleDrop(index, event) {
745
908
  event.preventDefault();
746
- if (this.draggingIndex === null || this.draggingIndex === index) {
909
+ const targetIndex = this.dropTargetIndex ?? index;
910
+ if (this.draggingIndex === null || this.draggingIndex === targetIndex) {
747
911
  this.dragOverIndex = null;
912
+ this.dropTargetIndex = null;
748
913
  return;
749
914
  }
915
+ this.isDropping = true;
750
916
 
751
917
  // 保存原始数据,以便失败时恢复
752
918
  const oldAuth = [...this.config.AUTH];
@@ -754,41 +920,63 @@ createApp({
754
920
  const oldShowTokens = [...this.showTokens];
755
921
  const oldShowEnvSections = [...this.showEnvSections];
756
922
  const oldShowModelSections = [...this.showModelSections];
923
+ const oldAuthKeys = [...this.authKeys];
757
924
 
758
925
  // 移动配置项
759
926
  const [movedAuth] = this.config.AUTH.splice(this.draggingIndex, 1);
760
- this.config.AUTH.splice(index, 0, movedAuth);
927
+ this.config.AUTH.splice(targetIndex, 0, movedAuth);
761
928
 
762
929
  // 同步移动显示状态
763
930
  const [movedToken] = this.showTokens.splice(this.draggingIndex, 1);
764
- this.showTokens.splice(index, 0, movedToken);
931
+ this.showTokens.splice(targetIndex, 0, movedToken);
765
932
 
766
933
  const [movedEnv] = this.showEnvSections.splice(this.draggingIndex, 1);
767
- this.showEnvSections.splice(index, 0, movedEnv);
934
+ this.showEnvSections.splice(targetIndex, 0, movedEnv);
768
935
 
769
936
  const [movedModel] = this.showModelSections.splice(this.draggingIndex, 1);
770
- this.showModelSections.splice(index, 0, movedModel);
937
+ this.showModelSections.splice(targetIndex, 0, movedModel);
938
+
939
+ const [movedAuthKey] = this.authKeys.splice(this.draggingIndex, 1);
940
+ this.authKeys.splice(targetIndex, 0, movedAuthKey);
771
941
 
772
942
  // 调整 activeIndex
773
943
  if (this.draggingIndex === this.config.activeIndex) {
774
944
  // 拖拽的是当前使用的配置,直接移动到目标位置
775
- this.config.activeIndex = index;
776
- } else if (this.draggingIndex < this.config.activeIndex && index >= this.config.activeIndex) {
945
+ this.config.activeIndex = targetIndex;
946
+ } else if (this.draggingIndex < this.config.activeIndex && targetIndex >= this.config.activeIndex) {
777
947
  // 从前向后拖拽,经过当前使用的配置
778
948
  // activeIndex 位置的元素被向前移了一位,所以需要减 1
779
949
  this.config.activeIndex--;
780
- } else if (this.draggingIndex > this.config.activeIndex && index <= this.config.activeIndex) {
950
+ } else if (this.draggingIndex > this.config.activeIndex && targetIndex <= this.config.activeIndex) {
781
951
  // 从后向前拖拽,经过当前使用的配置
782
952
  // activeIndex 位置的元素被向后移了一位,所以需要加 1
783
953
  this.config.activeIndex++;
784
954
  }
785
955
 
786
- // 重置拖拽状态
787
- this.draggingIndex = null;
956
+ this.draggingIndex = targetIndex;
788
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
+ });
789
976
 
790
977
  // 保存配置
791
978
  try {
979
+ this.normalizeCodexDefaults();
792
980
  const configToSave = {
793
981
  ...this.config,
794
982
  AUTH: this.config.AUTH.filter(auth => !this.isEmptyConfig(auth))
@@ -808,6 +996,7 @@ createApp({
808
996
  this.showTokens = oldShowTokens;
809
997
  this.showEnvSections = oldShowEnvSections;
810
998
  this.showModelSections = oldShowModelSections;
999
+ this.authKeys = oldAuthKeys;
811
1000
  }
812
1001
  }
813
1002
  },