tabby-ai-assistant 1.0.8 → 1.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/dist/components/chat/ai-sidebar.component.d.ts +16 -3
  2. package/dist/components/chat/chat-input.component.d.ts +4 -0
  3. package/dist/components/chat/chat-interface.component.d.ts +22 -1
  4. package/dist/components/chat/chat-settings.component.d.ts +21 -11
  5. package/dist/components/settings/ai-settings-tab.component.d.ts +14 -4
  6. package/dist/components/settings/general-settings.component.d.ts +43 -12
  7. package/dist/components/settings/provider-config.component.d.ts +110 -5
  8. package/dist/components/settings/security-settings.component.d.ts +14 -4
  9. package/dist/i18n/index.d.ts +48 -0
  10. package/dist/i18n/translations/en-US.d.ts +5 -0
  11. package/dist/i18n/translations/ja-JP.d.ts +5 -0
  12. package/dist/i18n/translations/zh-CN.d.ts +5 -0
  13. package/dist/i18n/types.d.ts +198 -0
  14. package/dist/index.js +1 -1
  15. package/dist/services/chat/ai-sidebar.service.d.ts +23 -1
  16. package/dist/services/core/theme.service.d.ts +53 -0
  17. package/dist/services/core/toast.service.d.ts +15 -0
  18. package/package.json +1 -1
  19. package/src/components/chat/ai-sidebar.component.scss +468 -0
  20. package/src/components/chat/ai-sidebar.component.ts +47 -344
  21. package/src/components/chat/chat-input.component.scss +2 -2
  22. package/src/components/chat/chat-input.component.ts +16 -5
  23. package/src/components/chat/chat-interface.component.html +11 -11
  24. package/src/components/chat/chat-interface.component.scss +410 -4
  25. package/src/components/chat/chat-interface.component.ts +105 -14
  26. package/src/components/chat/chat-message.component.scss +3 -3
  27. package/src/components/chat/chat-message.component.ts +3 -2
  28. package/src/components/chat/chat-settings.component.html +95 -61
  29. package/src/components/chat/chat-settings.component.scss +224 -50
  30. package/src/components/chat/chat-settings.component.ts +56 -30
  31. package/src/components/security/risk-confirm-dialog.component.scss +7 -7
  32. package/src/components/settings/ai-settings-tab.component.html +27 -27
  33. package/src/components/settings/ai-settings-tab.component.scss +34 -20
  34. package/src/components/settings/ai-settings-tab.component.ts +59 -20
  35. package/src/components/settings/general-settings.component.html +69 -40
  36. package/src/components/settings/general-settings.component.scss +151 -58
  37. package/src/components/settings/general-settings.component.ts +168 -55
  38. package/src/components/settings/provider-config.component.html +183 -60
  39. package/src/components/settings/provider-config.component.scss +332 -153
  40. package/src/components/settings/provider-config.component.ts +268 -19
  41. package/src/components/settings/security-settings.component.html +70 -39
  42. package/src/components/settings/security-settings.component.scss +104 -8
  43. package/src/components/settings/security-settings.component.ts +48 -10
  44. package/src/i18n/index.ts +129 -0
  45. package/src/i18n/translations/en-US.ts +193 -0
  46. package/src/i18n/translations/ja-JP.ts +193 -0
  47. package/src/i18n/translations/zh-CN.ts +193 -0
  48. package/src/i18n/types.ts +224 -0
  49. package/src/index.ts +6 -0
  50. package/src/services/chat/ai-sidebar.service.ts +157 -5
  51. package/src/services/core/theme.service.ts +480 -0
  52. package/src/services/core/toast.service.ts +36 -0
  53. package/src/styles/ai-assistant.scss +8 -88
  54. package/src/styles/themes.scss +161 -0
@@ -3,7 +3,7 @@
3
3
  flex-direction: column;
4
4
  height: 100%;
5
5
  background-color: var(--ai-bg-primary);
6
- color: var(--ai-dark);
6
+ color: var(--ai-text-primary);
7
7
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
8
8
  }
9
9
 
@@ -67,7 +67,7 @@
67
67
 
68
68
  &:hover {
69
69
  background-color: var(--ai-border);
70
- color: var(--ai-dark);
70
+ color: var(--ai-text-primary);
71
71
  }
72
72
 
73
73
  &.danger:hover {
@@ -207,7 +207,7 @@
207
207
  .message-content {
208
208
  .message-bubble {
209
209
  background-color: var(--ai-assistant-message);
210
- color: var(--ai-dark);
210
+ color: var(--ai-text-primary);
211
211
  border-bottom-left-radius: 0.25rem;
212
212
  }
213
213
  }
@@ -227,7 +227,7 @@
227
227
  .message-content {
228
228
  .message-bubble {
229
229
  background-color: var(--ai-system-message);
230
- color: var(--ai-dark);
230
+ color: var(--ai-text-primary);
231
231
  border-radius: 0.5rem;
232
232
  font-style: italic;
233
233
  }
@@ -235,6 +235,54 @@
235
235
  }
236
236
  }
237
237
 
238
+ /* 紧凑模式 */
239
+ .ai-chat-container.compact-mode {
240
+ padding: 0.5rem;
241
+
242
+ .message-item {
243
+ gap: 0.5rem;
244
+ margin-bottom: 0.25rem;
245
+
246
+ .message-avatar {
247
+ .avatar {
248
+ width: 28px;
249
+ height: 28px;
250
+ font-size: 0.875rem;
251
+ }
252
+ }
253
+
254
+ .message-content {
255
+ .message-bubble {
256
+ padding: 0.4rem 0.75rem;
257
+ font-size: calc(var(--chat-font-size, 14px) - 1px);
258
+ }
259
+
260
+ .message-time {
261
+ font-size: 0.625rem;
262
+ padding: 0 0.25rem;
263
+ }
264
+ }
265
+ }
266
+
267
+ .messages-wrapper {
268
+ gap: 0.5rem;
269
+ }
270
+
271
+ .date-separator {
272
+ margin: 0.5rem 0;
273
+
274
+ .date-text {
275
+ font-size: 0.75rem;
276
+ padding: 0 0.5rem;
277
+ }
278
+ }
279
+ }
280
+
281
+ /* 字体大小应用 */
282
+ .message-bubble {
283
+ font-size: var(--chat-font-size, 14px);
284
+ }
285
+
238
286
  /* 头像 */
239
287
  .message-avatar {
240
288
  flex-shrink: 0;
@@ -401,4 +449,362 @@
401
449
  font-size: 0.8rem;
402
450
  }
403
451
  }
452
+ }
453
+
454
+ // ============================================
455
+ // 像素风格主题 (Pixel Retro)
456
+ // ============================================
457
+ .ai-chat-interface.ai-theme-pixel,
458
+ .ai-chat-interface[data-ai-theme="pixel"] {
459
+ background-color: #0f380f !important;
460
+ color: #9bbc0f !important;
461
+
462
+ .ai-chat-header {
463
+ background: #1a2a1a !important;
464
+ border-color: #9bbc0f !important;
465
+
466
+ .ai-title {
467
+ color: #9bbc0f !important;
468
+ }
469
+
470
+ .provider-badge {
471
+ background: #306230 !important;
472
+ border: 2px solid #9bbc0f !important;
473
+ }
474
+
475
+ .btn-icon {
476
+ color: #9bbc0f !important;
477
+ border: 2px solid #9bbc0f !important;
478
+
479
+ &:hover {
480
+ background: #9bbc0f !important;
481
+ color: #0f380f !important;
482
+ }
483
+ }
484
+ }
485
+
486
+ .ai-chat-container {
487
+ .message-item {
488
+ &.user-message {
489
+ .message-bubble {
490
+ background: #0f380f !important;
491
+ border: 3px solid #9bbc0f !important;
492
+ border-radius: 0 !important;
493
+ color: #9bbc0f !important;
494
+ }
495
+ }
496
+
497
+ &.assistant-message {
498
+ .message-bubble {
499
+ background: #306230 !important;
500
+ border: 3px solid #9bbc0f !important;
501
+ border-radius: 0 !important;
502
+ color: #9bbc0f !important;
503
+ }
504
+ }
505
+
506
+ &.system-message {
507
+ .message-bubble {
508
+ background: #1a1a1a !important;
509
+ border: 2px solid #ffeb3b !important;
510
+ border-radius: 0 !important;
511
+ color: #ffeb3b !important;
512
+ }
513
+ }
514
+ }
515
+
516
+ .date-separator::before {
517
+ background-color: #9bbc0f !important;
518
+ }
519
+
520
+ .date-text {
521
+ background-color: #0f380f !important;
522
+ color: #9bbc0f !important;
523
+ }
524
+ }
525
+
526
+ .scroll-btn {
527
+ background: #306230 !important;
528
+ border: 2px solid #9bbc0f !important;
529
+ color: #9bbc0f !important;
530
+ }
531
+
532
+ .ai-chat-input {
533
+ background: #1a2a1a !important;
534
+ border-color: #9bbc0f !important;
535
+ }
536
+
537
+ .chat-tips {
538
+ background: rgba(155, 188, 15, 0.1) !important;
539
+ border-color: #9bbc0f !important;
540
+
541
+ .tip-item {
542
+ color: #9bbc0f !important;
543
+ }
544
+ }
545
+
546
+ // 输入框
547
+ .chat-textarea {
548
+ background: #0f380f !important;
549
+ border: 3px solid #9bbc0f !important;
550
+ border-radius: 0 !important;
551
+ color: #9bbc0f !important;
552
+ font-family: 'Courier New', monospace !important;
553
+
554
+ &:focus {
555
+ outline: none;
556
+ border-color: #8bac0f !important;
557
+ box-shadow: 4px 4px 0 rgba(48, 98, 48, 0.8) !important;
558
+ }
559
+
560
+ &::placeholder {
561
+ color: rgba(155, 188, 15, 0.5) !important;
562
+ }
563
+ }
564
+
565
+ // 发送按钮
566
+ .send-btn {
567
+ background: #306230 !important;
568
+ border: 3px solid #9bbc0f !important;
569
+ border-radius: 0 !important;
570
+ color: #9bbc0f !important;
571
+
572
+ &:hover:not(:disabled) {
573
+ background: #9bbc0f !important;
574
+ color: #0f380f !important;
575
+ }
576
+
577
+ &:disabled {
578
+ opacity: 0.5;
579
+ }
580
+ }
581
+
582
+ h2, h3, h4 {
583
+ font-family: 'Courier New', monospace !important;
584
+ letter-spacing: 1px !important;
585
+ }
586
+ }
587
+
588
+ // ============================================
589
+ // 科技风格主题 (Cyber Tech)
590
+ // ============================================
591
+ .ai-chat-interface.ai-theme-tech,
592
+ .ai-chat-interface[data-ai-theme="tech"] {
593
+ background-color: #0a0a0f !important;
594
+ color: #00fff9 !important;
595
+
596
+ .ai-chat-header {
597
+ background: linear-gradient(90deg, #12121a 0%, #1a1a2e 100%) !important;
598
+ border-color: rgba(0, 255, 249, 0.3) !important;
599
+
600
+ .ai-title {
601
+ color: #00fff9 !important;
602
+ text-shadow: 0 0 10px rgba(0, 255, 249, 0.5) !important;
603
+ }
604
+
605
+ .provider-badge {
606
+ background: rgba(0, 255, 249, 0.1) !important;
607
+ border: 1px solid #00fff9 !important;
608
+ color: #00fff9 !important;
609
+ box-shadow: 0 0 10px rgba(0, 255, 249, 0.3) !important;
610
+ }
611
+
612
+ .btn-icon {
613
+ color: #00fff9 !important;
614
+ border: 1px solid rgba(0, 255, 249, 0.3) !important;
615
+ box-shadow: 0 0 5px rgba(0, 255, 249, 0.2) !important;
616
+
617
+ &:hover {
618
+ background: rgba(0, 255, 249, 0.2) !important;
619
+ box-shadow: 0 0 15px rgba(0, 255, 249, 0.4) !important;
620
+ }
621
+
622
+ &.danger:hover {
623
+ background: rgba(255, 51, 102, 0.3) !important;
624
+ border-color: #ff3366 !important;
625
+ box-shadow: 0 0 15px rgba(255, 51, 102, 0.4) !important;
626
+ }
627
+ }
628
+ }
629
+
630
+ .ai-chat-container {
631
+ background: repeating-linear-gradient(
632
+ 0deg,
633
+ transparent,
634
+ transparent 2px,
635
+ rgba(0, 255, 249, 0.02) 2px,
636
+ rgba(0, 255, 249, 0.02) 4px
637
+ ) !important;
638
+
639
+ .message-item {
640
+ &.user-message {
641
+ .message-bubble {
642
+ background: linear-gradient(
643
+ 135deg,
644
+ rgba(0, 255, 249, 0.15) 0%,
645
+ rgba(0, 255, 249, 0.05) 100%
646
+ ) !important;
647
+ border: 1px solid rgba(0, 255, 249, 0.4) !important;
648
+ border-radius: 4px !important;
649
+ color: #00fff9 !important;
650
+ box-shadow:
651
+ 0 0 10px rgba(0, 255, 249, 0.2),
652
+ inset 0 0 20px rgba(0, 255, 249, 0.05) !important;
653
+ }
654
+
655
+ .avatar.user {
656
+ background: linear-gradient(135deg, #00fff9 0%, #00e6e0 100%) !important;
657
+ box-shadow: 0 0 10px rgba(0, 255, 249, 0.5) !important;
658
+ }
659
+ }
660
+
661
+ &.assistant-message {
662
+ .message-bubble {
663
+ background: linear-gradient(
664
+ 135deg,
665
+ rgba(255, 0, 255, 0.1) 0%,
666
+ rgba(255, 0, 255, 0.05) 100%
667
+ ) !important;
668
+ border: 1px solid rgba(255, 0, 255, 0.3) !important;
669
+ border-radius: 4px !important;
670
+ color: #f8f9fa !important;
671
+ box-shadow:
672
+ 0 0 10px rgba(255, 0, 255, 0.15),
673
+ inset 0 0 20px rgba(255, 0, 255, 0.05) !important;
674
+ }
675
+
676
+ .avatar.assistant {
677
+ background: linear-gradient(135deg, #ff00ff 0%, #e600e6 100%) !important;
678
+ box-shadow: 0 0 10px rgba(255, 0, 255, 0.5) !important;
679
+ }
680
+ }
681
+
682
+ &.system-message {
683
+ .message-bubble {
684
+ background: rgba(0, 255, 136, 0.1) !important;
685
+ border: 1px solid rgba(0, 255, 136, 0.4) !important;
686
+ border-radius: 4px !important;
687
+ color: #00ff88 !important;
688
+ box-shadow: 0 0 10px rgba(0, 255, 136, 0.2) !important;
689
+ }
690
+
691
+ .avatar.system {
692
+ background: linear-gradient(135deg, #00ff88 0%, #00cc6a 100%) !important;
693
+ box-shadow: 0 0 10px rgba(0, 255, 136, 0.5) !important;
694
+ }
695
+ }
696
+ }
697
+
698
+ .date-separator::before {
699
+ background: linear-gradient(
700
+ 90deg,
701
+ transparent,
702
+ rgba(0, 255, 249, 0.3),
703
+ transparent
704
+ ) !important;
705
+ }
706
+
707
+ .date-text {
708
+ background-color: #0a0a0f !important;
709
+ color: rgba(0, 255, 249, 0.7) !important;
710
+ border: 1px solid rgba(0, 255, 249, 0.2) !important;
711
+ border-radius: 4px !important;
712
+ }
713
+ }
714
+
715
+ .scroll-btn {
716
+ background: linear-gradient(135deg, #12121a 0%, #1a1a2e 100%) !important;
717
+ border: 1px solid #00fff9 !important;
718
+ color: #00fff9 !important;
719
+ box-shadow: 0 0 15px rgba(0, 255, 249, 0.3) !important;
720
+
721
+ &:hover {
722
+ box-shadow: 0 0 25px rgba(0, 255, 249, 0.5) !important;
723
+ }
724
+ }
725
+
726
+ .ai-chat-input {
727
+ background: linear-gradient(180deg, #12121a 0%, #0a0a0f 100%) !important;
728
+ border-color: rgba(0, 255, 249, 0.2) !important;
729
+ box-shadow: 0 -5px 20px rgba(0, 255, 249, 0.1) !important;
730
+ }
731
+
732
+ .chat-tips {
733
+ background: rgba(0, 255, 249, 0.03) !important;
734
+ border-color: rgba(0, 255, 249, 0.2) !important;
735
+
736
+ .tip-item {
737
+ color: rgba(0, 255, 249, 0.8) !important;
738
+ text-shadow: 0 0 5px rgba(0, 255, 249, 0.3) !important;
739
+ }
740
+ }
741
+
742
+ // 输入框
743
+ .chat-textarea {
744
+ background: rgba(0, 255, 249, 0.05) !important;
745
+ border: 1px solid rgba(0, 255, 249, 0.3) !important;
746
+ border-radius: 4px !important;
747
+ color: #00fff9 !important;
748
+ font-family: 'Segoe UI', 'Share Tech Mono', monospace !important;
749
+ box-shadow:
750
+ 0 0 10px rgba(0, 255, 249, 0.1),
751
+ inset 0 0 20px rgba(0, 255, 249, 0.05) !important;
752
+
753
+ &:focus {
754
+ outline: none;
755
+ border-color: #00fff9 !important;
756
+ box-shadow:
757
+ 0 0 20px rgba(0, 255, 249, 0.3),
758
+ inset 0 0 30px rgba(0, 255, 249, 0.1) !important;
759
+ }
760
+
761
+ &::placeholder {
762
+ color: rgba(0, 255, 249, 0.4) !important;
763
+ }
764
+ }
765
+
766
+ // 发送按钮
767
+ .send-btn {
768
+ background: linear-gradient(135deg, rgba(0, 255, 249, 0.2) 0%, rgba(0, 255, 249, 0.1) 100%) !important;
769
+ border: 1px solid #00fff9 !important;
770
+ border-radius: 4px !important;
771
+ color: #00fff9 !important;
772
+ box-shadow:
773
+ 0 0 10px rgba(0, 255, 249, 0.2),
774
+ inset 0 0 10px rgba(0, 255, 249, 0.1) !important;
775
+
776
+ &:hover:not(:disabled) {
777
+ background: linear-gradient(135deg, rgba(0, 255, 249, 0.4) 0%, rgba(0, 255, 249, 0.2) 100%) !important;
778
+ box-shadow:
779
+ 0 0 20px rgba(0, 255, 249, 0.4),
780
+ inset 0 0 20px rgba(0, 255, 249, 0.2) !important;
781
+ }
782
+
783
+ &:disabled {
784
+ opacity: 0.5;
785
+ box-shadow: none !important;
786
+ }
787
+ }
788
+
789
+ // 加载指示器
790
+ .loading-indicator {
791
+ background: rgba(0, 255, 249, 0.05) !important;
792
+ border: 1px solid rgba(0, 255, 249, 0.2) !important;
793
+
794
+ .typing-indicator span {
795
+ background: #00fff9 !important;
796
+ box-shadow: 0 0 10px #00fff9 !important;
797
+ }
798
+
799
+ .loading-text {
800
+ color: rgba(0, 255, 249, 0.7) !important;
801
+ }
802
+ }
803
+
804
+ h2, h3, h4 {
805
+ font-family: 'Segoe UI', 'Share Tech Mono', monospace !important;
806
+ text-shadow:
807
+ 0 0 10px rgba(0, 255, 249, 0.5),
808
+ 0 0 20px rgba(0, 255, 249, 0.3) !important;
809
+ }
404
810
  }
@@ -1,4 +1,4 @@
1
- import { Component, OnInit, OnDestroy, ViewChild, ElementRef, AfterViewChecked } from '@angular/core';
1
+ import { Component, OnInit, OnDestroy, ViewChild, ElementRef, AfterViewChecked, ViewEncapsulation } from '@angular/core';
2
2
  import { Subject } from 'rxjs';
3
3
  import { takeUntil } from 'rxjs/operators';
4
4
  import { ChatMessage, MessageRole, StreamEvent } from '../../types/ai.types';
@@ -7,11 +7,13 @@ import { ConfigProviderService } from '../../services/core/config-provider.servi
7
7
  import { LoggerService } from '../../services/core/logger.service';
8
8
  import { ChatHistoryService } from '../../services/chat/chat-history.service';
9
9
  import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
10
+ import { TranslateService } from '../../i18n';
10
11
 
11
12
  @Component({
12
13
  selector: 'app-chat-interface',
13
14
  templateUrl: './chat-interface.component.html',
14
- styleUrls: ['./chat-interface.component.scss']
15
+ styleUrls: ['./chat-interface.component.scss'],
16
+ encapsulation: ViewEncapsulation.None
15
17
  })
16
18
  export class ChatInterfaceComponent implements OnInit, OnDestroy, AfterViewChecked {
17
19
  @ViewChild('chatContainer') chatContainerRef!: ElementRef;
@@ -22,21 +24,50 @@ export class ChatInterfaceComponent implements OnInit, OnDestroy, AfterViewCheck
22
24
  currentSessionId: string = '';
23
25
  showScrollTop = false;
24
26
  showScrollBottom = false;
27
+
28
+ // UI 设置(从配置加载)
29
+ showTimestamps: boolean = true;
30
+ showAvatars: boolean = true;
31
+ soundEnabled: boolean = false;
32
+ compactMode: boolean = false;
33
+ fontSize: number = 14;
34
+
35
+ // 翻译对象
36
+ t: any;
37
+
25
38
  private destroy$ = new Subject<void>();
26
39
  private shouldScrollToBottom = false;
40
+ private notificationSound: HTMLAudioElement | null = null;
27
41
 
28
42
  constructor(
29
43
  private aiService: AiAssistantService,
30
44
  private config: ConfigProviderService,
31
45
  private logger: LoggerService,
32
46
  private modal: NgbModal,
33
- private chatHistory: ChatHistoryService
34
- ) { }
47
+ private chatHistory: ChatHistoryService,
48
+ private translate: TranslateService
49
+ ) {
50
+ this.t = this.translate.t;
51
+ }
35
52
 
36
53
  ngOnInit(): void {
54
+ // 监听语言变化
55
+ this.translate.translation$.pipe(
56
+ takeUntil(this.destroy$)
57
+ ).subscribe(translation => {
58
+ this.t = translation;
59
+ // 如果有欢迎消息,重新发送以更新语言
60
+ if (this.messages.length > 0 && this.messages[0].role === MessageRole.ASSISTANT) {
61
+ this.sendWelcomeMessage();
62
+ }
63
+ });
64
+
37
65
  // 生成或加载会话 ID
38
66
  this.currentSessionId = this.generateSessionId();
39
67
 
68
+ // 加载 UI 设置
69
+ this.loadUISettings();
70
+
40
71
  // 加载当前提供商信息
41
72
  this.loadCurrentProvider();
42
73
 
@@ -52,6 +83,38 @@ export class ChatInterfaceComponent implements OnInit, OnDestroy, AfterViewCheck
52
83
  setTimeout(() => this.checkScrollState(), 100);
53
84
  }
54
85
 
86
+ /**
87
+ * 加载 UI 设置
88
+ */
89
+ private loadUISettings(): void {
90
+ this.showTimestamps = this.config.get<boolean>('ui.showTimestamps', true) ?? true;
91
+ this.showAvatars = this.config.get<boolean>('ui.showAvatars', true) ?? true;
92
+ this.soundEnabled = this.config.get<boolean>('ui.soundEnabled', false) ?? false;
93
+ this.compactMode = this.config.get<boolean>('ui.compactMode', false) ?? false;
94
+ this.fontSize = this.config.get<number>('ui.fontSize', 14) ?? 14;
95
+
96
+ // 应用设置
97
+ this.applyStoredSettings();
98
+ }
99
+
100
+ /**
101
+ * 应用存储的 UI 设置
102
+ */
103
+ private applyStoredSettings(): void {
104
+ // 应用字体大小
105
+ document.documentElement.style.setProperty('--chat-font-size', `${this.fontSize}px`);
106
+
107
+ // 应用紧凑模式
108
+ const container = document.querySelector('.ai-chat-container');
109
+ if (container) {
110
+ if (this.compactMode) {
111
+ container.classList.add('compact-mode');
112
+ } else {
113
+ container.classList.remove('compact-mode');
114
+ }
115
+ }
116
+ }
117
+
55
118
  ngOnDestroy(): void {
56
119
  // 保存当前会话
57
120
  this.saveChatHistory();
@@ -121,7 +184,7 @@ export class ChatInterfaceComponent implements OnInit, OnDestroy, AfterViewCheck
121
184
  const welcomeMessage: ChatMessage = {
122
185
  id: this.generateId(),
123
186
  role: MessageRole.ASSISTANT,
124
- content: `您好!我是AI助手。\n\n我可以帮助您:\n• 将自然语言转换为终端命令\n• 解释复杂的命令\n• 分析命令执行结果\n• 提供错误修复建议\n\n当前使用:${this.currentProvider}\n\n请输入您的问题或描述您想执行的命令。`,
187
+ content: `${this.t.chatInterface.welcomeMessage}\n\n${this.t.chatInterface.tipCommand}\n\n${this.t.chatInterface.tipShortcut}`,
125
188
  timestamp: new Date()
126
189
  };
127
190
  this.messages.push(welcomeMessage);
@@ -179,22 +242,23 @@ export class ChatInterfaceComponent implements OnInit, OnDestroy, AfterViewCheck
179
242
  }
180
243
  // 工具调用开始 - 显示提示
181
244
  else if (event.type === 'tool_use_start') {
182
- aiMessage.content += '\n\n🔧 正在执行工具...';
245
+ aiMessage.content += `\n\n🔧 ${this.t.chatInterface.executingTool}`;
183
246
  this.shouldScrollToBottom = true;
184
247
  }
185
248
  // 工具调用完成 - 更新状态
186
249
  else if (event.type === 'tool_use_end') {
187
- aiMessage.content = aiMessage.content.replace('🔧 正在执行工具...', '✅ 工具执行完成');
250
+ aiMessage.content = aiMessage.content.replace(`🔧 ${this.t.chatInterface.executingTool}`, `✅ ${this.t.chatInterface.toolComplete}`);
188
251
  this.shouldScrollToBottom = true;
189
252
  }
190
253
  // 消息结束
191
254
  else if (event.type === 'message_end') {
192
255
  this.logger.info('Stream completed');
256
+ this.playNotificationSound();
193
257
  }
194
258
  },
195
259
  error: (error) => {
196
260
  this.logger.error('Stream error', error);
197
- aiMessage.content += `\n\n 错误: ${error instanceof Error ? error.message : 'Unknown error'}`;
261
+ aiMessage.content += `\n\n${this.t.chatInterface.errorPrefix}: ${error instanceof Error ? error.message : 'Unknown error'}`;
198
262
  this.isLoading = false;
199
263
  this.shouldScrollToBottom = true;
200
264
  this.saveChatHistory();
@@ -213,7 +277,7 @@ export class ChatInterfaceComponent implements OnInit, OnDestroy, AfterViewCheck
213
277
  const errorMessage: ChatMessage = {
214
278
  id: this.generateId(),
215
279
  role: MessageRole.ASSISTANT,
216
- content: `抱歉,我遇到了一些问题:${error instanceof Error ? error.message : 'Unknown error'}\n\n请稍后重试。`,
280
+ content: `${this.t.chatInterface.errorPrefix}: ${error instanceof Error ? error.message : 'Unknown error'}\n\n${this.t.chatInterface.tipShortcut}`,
217
281
  timestamp: new Date()
218
282
  };
219
283
  this.messages.push(errorMessage);
@@ -226,7 +290,7 @@ export class ChatInterfaceComponent implements OnInit, OnDestroy, AfterViewCheck
226
290
  * 清空聊天记录
227
291
  */
228
292
  clearChat(): void {
229
- if (confirm('确定要清空聊天记录吗?')) {
293
+ if (confirm(this.t.chatInterface.clearChatConfirm)) {
230
294
  // 删除当前会话
231
295
  if (this.currentSessionId) {
232
296
  this.chatHistory.deleteSession(this.currentSessionId);
@@ -286,7 +350,7 @@ export class ChatInterfaceComponent implements OnInit, OnDestroy, AfterViewCheck
286
350
  }));
287
351
 
288
352
  if (configuredProviders.length === 0) {
289
- alert('没有可用的AI提供商,请先在设置中配置。');
353
+ alert(this.t.providers.testError);
290
354
  return;
291
355
  }
292
356
 
@@ -296,7 +360,7 @@ export class ChatInterfaceComponent implements OnInit, OnDestroy, AfterViewCheck
296
360
  ).join('\n');
297
361
 
298
362
  const choice = prompt(
299
- `当前使用: ${this.currentProvider}\n\n可用的AI提供商:\n${providerList}\n\n请输入序号选择提供商:`,
363
+ `${this.t.chatInterface.providerBadge}: ${this.currentProvider}\n\n${this.t.chatInterface.switchProvider}:\n${providerList}\n\n${this.t.chatInterface.inputPlaceholder}`,
300
364
  '1'
301
365
  );
302
366
 
@@ -312,12 +376,12 @@ export class ChatInterfaceComponent implements OnInit, OnDestroy, AfterViewCheck
312
376
  const systemMessage: ChatMessage = {
313
377
  id: this.generateId(),
314
378
  role: MessageRole.SYSTEM,
315
- content: `已切换到 ${this.currentProvider}`,
379
+ content: `${this.t.chatInterface.providerBadge}: ${this.currentProvider}`,
316
380
  timestamp: new Date()
317
381
  };
318
382
  this.messages.push(systemMessage);
319
383
  } else {
320
- alert('无效的选择');
384
+ alert(this.t.chatInterface.errorPrefix);
321
385
  }
322
386
  }
323
387
  }
@@ -422,6 +486,33 @@ export class ChatInterfaceComponent implements OnInit, OnDestroy, AfterViewCheck
422
486
  });
423
487
  }
424
488
 
489
+ /**
490
+ * 播放提示音
491
+ */
492
+ private playNotificationSound(): void {
493
+ if (!this.soundEnabled) return;
494
+
495
+ try {
496
+ // 使用系统提示音
497
+ const audioContext = new (window.AudioContext || (window as any).webkitAudioContext)();
498
+ const oscillator = audioContext.createOscillator();
499
+ const gainNode = audioContext.createGain();
500
+
501
+ oscillator.connect(gainNode);
502
+ gainNode.connect(audioContext.destination);
503
+
504
+ oscillator.frequency.value = 800;
505
+ oscillator.type = 'sine';
506
+ gainNode.gain.setValueAtTime(0.1, audioContext.currentTime);
507
+ gainNode.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 0.2);
508
+
509
+ oscillator.start(audioContext.currentTime);
510
+ oscillator.stop(audioContext.currentTime + 0.2);
511
+ } catch (error) {
512
+ // 忽略音频播放错误
513
+ }
514
+ }
515
+
425
516
  /**
426
517
  * 检查是否为今天的消息
427
518
  */
@@ -39,7 +39,7 @@
39
39
  .message-content {
40
40
  .message-bubble {
41
41
  background-color: var(--ai-assistant-message);
42
- color: var(--ai-dark);
42
+ color: var(--ai-text-primary);
43
43
  border-bottom-left-radius: 0.25rem;
44
44
  }
45
45
  }
@@ -58,7 +58,7 @@
58
58
  .message-content {
59
59
  .message-bubble {
60
60
  background-color: var(--ai-system-message);
61
- color: var(--ai-dark);
61
+ color: var(--ai-text-primary);
62
62
  border-radius: 0.5rem;
63
63
  font-style: italic;
64
64
  }
@@ -151,7 +151,7 @@
151
151
 
152
152
  &:hover {
153
153
  background: rgba(0, 0, 0, 0.1);
154
- color: var(--ai-dark);
154
+ color: var(--ai-text-primary);
155
155
  }
156
156
  }
157
157
  }