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.
- package/dist/components/chat/ai-sidebar.component.d.ts +16 -3
- package/dist/components/chat/chat-input.component.d.ts +4 -0
- package/dist/components/chat/chat-interface.component.d.ts +22 -1
- package/dist/components/chat/chat-settings.component.d.ts +21 -11
- package/dist/components/settings/ai-settings-tab.component.d.ts +14 -4
- package/dist/components/settings/general-settings.component.d.ts +43 -12
- package/dist/components/settings/provider-config.component.d.ts +110 -5
- package/dist/components/settings/security-settings.component.d.ts +14 -4
- package/dist/i18n/index.d.ts +48 -0
- package/dist/i18n/translations/en-US.d.ts +5 -0
- package/dist/i18n/translations/ja-JP.d.ts +5 -0
- package/dist/i18n/translations/zh-CN.d.ts +5 -0
- package/dist/i18n/types.d.ts +198 -0
- package/dist/index.js +1 -1
- package/dist/services/chat/ai-sidebar.service.d.ts +23 -1
- package/dist/services/core/theme.service.d.ts +53 -0
- package/dist/services/core/toast.service.d.ts +15 -0
- package/package.json +1 -1
- package/src/components/chat/ai-sidebar.component.scss +468 -0
- package/src/components/chat/ai-sidebar.component.ts +47 -344
- package/src/components/chat/chat-input.component.scss +2 -2
- package/src/components/chat/chat-input.component.ts +16 -5
- package/src/components/chat/chat-interface.component.html +11 -11
- package/src/components/chat/chat-interface.component.scss +410 -4
- package/src/components/chat/chat-interface.component.ts +105 -14
- package/src/components/chat/chat-message.component.scss +3 -3
- package/src/components/chat/chat-message.component.ts +3 -2
- package/src/components/chat/chat-settings.component.html +95 -61
- package/src/components/chat/chat-settings.component.scss +224 -50
- package/src/components/chat/chat-settings.component.ts +56 -30
- package/src/components/security/risk-confirm-dialog.component.scss +7 -7
- package/src/components/settings/ai-settings-tab.component.html +27 -27
- package/src/components/settings/ai-settings-tab.component.scss +34 -20
- package/src/components/settings/ai-settings-tab.component.ts +59 -20
- package/src/components/settings/general-settings.component.html +69 -40
- package/src/components/settings/general-settings.component.scss +151 -58
- package/src/components/settings/general-settings.component.ts +168 -55
- package/src/components/settings/provider-config.component.html +183 -60
- package/src/components/settings/provider-config.component.scss +332 -153
- package/src/components/settings/provider-config.component.ts +268 -19
- package/src/components/settings/security-settings.component.html +70 -39
- package/src/components/settings/security-settings.component.scss +104 -8
- package/src/components/settings/security-settings.component.ts +48 -10
- package/src/i18n/index.ts +129 -0
- package/src/i18n/translations/en-US.ts +193 -0
- package/src/i18n/translations/ja-JP.ts +193 -0
- package/src/i18n/translations/zh-CN.ts +193 -0
- package/src/i18n/types.ts +224 -0
- package/src/index.ts +6 -0
- package/src/services/chat/ai-sidebar.service.ts +157 -5
- package/src/services/core/theme.service.ts +480 -0
- package/src/services/core/toast.service.ts +36 -0
- package/src/styles/ai-assistant.scss +8 -88
- 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-
|
|
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-
|
|
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-
|
|
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-
|
|
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:
|
|
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 +=
|
|
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
|
|
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:
|
|
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(
|
|
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
|
-
|
|
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:
|
|
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-
|
|
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-
|
|
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-
|
|
154
|
+
color: var(--ai-text-primary);
|
|
155
155
|
}
|
|
156
156
|
}
|
|
157
157
|
}
|