tabby-ai-assistant 1.0.9 → 1.0.11

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 (103) hide show
  1. package/dist/index.js +1 -1
  2. package/package.json +3 -3
  3. package/src/components/chat/ai-sidebar.component.scss +43 -0
  4. package/src/components/chat/ai-sidebar.component.ts +43 -1
  5. package/src/components/settings/ai-settings-tab.component.html +13 -5
  6. package/src/components/settings/ai-settings-tab.component.scss +164 -33
  7. package/src/components/settings/ai-settings-tab.component.ts +1 -0
  8. package/src/components/settings/context-settings.component.html +87 -0
  9. package/src/components/settings/context-settings.component.scss +133 -0
  10. package/src/components/settings/context-settings.component.ts +91 -0
  11. package/src/components/settings/provider-config.component.html +46 -12
  12. package/src/components/settings/provider-config.component.scss +59 -0
  13. package/src/components/settings/provider-config.component.ts +112 -15
  14. package/src/i18n/translations/en-US.ts +27 -1
  15. package/src/i18n/translations/ja-JP.ts +27 -1
  16. package/src/i18n/translations/zh-CN.ts +27 -1
  17. package/src/i18n/types.ts +28 -0
  18. package/src/index.ts +6 -0
  19. package/src/services/chat/chat-session.service.ts +91 -2
  20. package/src/services/context/manager.ts +33 -2
  21. package/src/services/core/ai-assistant.service.ts +35 -11
  22. package/src/services/core/config-provider.service.ts +66 -0
  23. package/src/services/core/toast.service.ts +36 -0
  24. package/src/services/providers/anthropic-provider.service.ts +12 -4
  25. package/src/services/providers/glm-provider.service.ts +12 -4
  26. package/src/services/providers/minimax-provider.service.ts +12 -4
  27. package/src/types/ai.types.ts +3 -3
  28. package/src/types/provider.types.ts +1 -0
  29. package/dist/components/chat/ai-sidebar.component.d.ts +0 -160
  30. package/dist/components/chat/chat-input.component.d.ts +0 -69
  31. package/dist/components/chat/chat-interface.component.d.ts +0 -124
  32. package/dist/components/chat/chat-message.component.d.ts +0 -53
  33. package/dist/components/chat/chat-settings.component.d.ts +0 -72
  34. package/dist/components/common/error-message.component.d.ts +0 -11
  35. package/dist/components/common/loading-spinner.component.d.ts +0 -4
  36. package/dist/components/security/consent-dialog.component.d.ts +0 -11
  37. package/dist/components/security/password-prompt.component.d.ts +0 -10
  38. package/dist/components/security/risk-confirm-dialog.component.d.ts +0 -36
  39. package/dist/components/settings/ai-settings-tab.component.d.ts +0 -82
  40. package/dist/components/settings/general-settings.component.d.ts +0 -94
  41. package/dist/components/settings/provider-config.component.d.ts +0 -273
  42. package/dist/components/settings/security-settings.component.d.ts +0 -33
  43. package/dist/components/terminal/ai-toolbar-button.component.d.ts +0 -10
  44. package/dist/components/terminal/command-preview.component.d.ts +0 -53
  45. package/dist/components/terminal/command-suggestion.component.d.ts +0 -16
  46. package/dist/i18n/index.d.ts +0 -48
  47. package/dist/i18n/translations/en-US.d.ts +0 -5
  48. package/dist/i18n/translations/ja-JP.d.ts +0 -5
  49. package/dist/i18n/translations/zh-CN.d.ts +0 -5
  50. package/dist/i18n/types.d.ts +0 -198
  51. package/dist/index-full.d.ts +0 -8
  52. package/dist/index-minimal.d.ts +0 -3
  53. package/dist/index.d.ts +0 -12
  54. package/dist/main.d.ts +0 -8
  55. package/dist/providers/tabby/ai-config.provider.d.ts +0 -70
  56. package/dist/providers/tabby/ai-hotkey.provider.d.ts +0 -15
  57. package/dist/providers/tabby/ai-settings-tab.provider.d.ts +0 -11
  58. package/dist/providers/tabby/ai-toolbar-button.provider.d.ts +0 -16
  59. package/dist/services/chat/ai-sidebar.service.d.ts +0 -111
  60. package/dist/services/chat/chat-history.service.d.ts +0 -145
  61. package/dist/services/chat/chat-session.service.d.ts +0 -113
  62. package/dist/services/chat/command-generator.service.d.ts +0 -49
  63. package/dist/services/context/compaction.d.ts +0 -90
  64. package/dist/services/context/manager.d.ts +0 -69
  65. package/dist/services/context/memory.d.ts +0 -116
  66. package/dist/services/context/token-budget.d.ts +0 -105
  67. package/dist/services/core/ai-assistant.service.d.ts +0 -127
  68. package/dist/services/core/ai-provider-manager.service.d.ts +0 -119
  69. package/dist/services/core/checkpoint.service.d.ts +0 -130
  70. package/dist/services/core/config-provider.service.d.ts +0 -137
  71. package/dist/services/core/logger.service.d.ts +0 -21
  72. package/dist/services/core/theme.service.d.ts +0 -53
  73. package/dist/services/platform/escape-sequence.service.d.ts +0 -132
  74. package/dist/services/platform/platform-detection.service.d.ts +0 -146
  75. package/dist/services/providers/anthropic-provider.service.d.ts +0 -44
  76. package/dist/services/providers/base-provider.service.d.ts +0 -142
  77. package/dist/services/providers/glm-provider.service.d.ts +0 -96
  78. package/dist/services/providers/minimax-provider.service.d.ts +0 -102
  79. package/dist/services/providers/ollama-provider.service.d.ts +0 -76
  80. package/dist/services/providers/openai-compatible.service.d.ts +0 -44
  81. package/dist/services/providers/openai-provider.service.d.ts +0 -43
  82. package/dist/services/providers/vllm-provider.service.d.ts +0 -82
  83. package/dist/services/security/consent-manager.service.d.ts +0 -65
  84. package/dist/services/security/password-manager.service.d.ts +0 -67
  85. package/dist/services/security/risk-assessment.service.d.ts +0 -65
  86. package/dist/services/security/security-validator.service.d.ts +0 -36
  87. package/dist/services/terminal/buffer-analyzer.service.d.ts +0 -128
  88. package/dist/services/terminal/command-analyzer.service.d.ts +0 -20
  89. package/dist/services/terminal/context-menu.service.d.ts +0 -24
  90. package/dist/services/terminal/hotkey.service.d.ts +0 -28
  91. package/dist/services/terminal/terminal-context.service.d.ts +0 -100
  92. package/dist/services/terminal/terminal-manager.service.d.ts +0 -185
  93. package/dist/services/terminal/terminal-tools.service.d.ts +0 -79
  94. package/dist/types/ai.types.d.ts +0 -199
  95. package/dist/types/provider.types.d.ts +0 -105
  96. package/dist/types/security.types.d.ts +0 -85
  97. package/dist/types/terminal.types.d.ts +0 -150
  98. package/dist/utils/encryption.utils.d.ts +0 -83
  99. package/dist/utils/formatting.utils.d.ts +0 -106
  100. package/dist/utils/validation.utils.d.ts +0 -83
  101. package/jest.config.js +0 -47
  102. package/webpack-docker.config.js +0 -42
  103. package/webpack.config.js +0 -81
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tabby-ai-assistant",
3
- "version": "1.0.9",
3
+ "version": "1.0.11",
4
4
  "description": "Tabby终端AI助手插件 - 支持多AI提供商(OpenAI、Anthropic、Minimax、GLM、Ollama、vLLM)",
5
5
  "main": "dist/index.js",
6
6
  "typings": "dist/index.d.ts",
@@ -25,8 +25,8 @@
25
25
  ],
26
26
  "author": "Mr.zhang",
27
27
  "license": "MIT",
28
- "repository": "",
29
- "homepage": "",
28
+ "repository": "https://github.com/zhangyang-crazy-one/tabby-ai-assistant",
29
+ "homepage": "https://github.com/zhangyang-crazy-one/tabby-ai-assistant",
30
30
  "dependencies": {
31
31
  "@angular/animations": "~15.2.10",
32
32
  "@angular/common": "~15.2.10",
@@ -74,6 +74,49 @@
74
74
  }
75
75
  }
76
76
 
77
+ // Token 使用进度条
78
+ .token-usage-bar {
79
+ position: relative;
80
+ height: 20px;
81
+ background: rgba(255, 255, 255, 0.08);
82
+ border-radius: 4px;
83
+ margin: 8px 16px;
84
+ overflow: hidden;
85
+
86
+ .usage-progress {
87
+ position: absolute;
88
+ top: 0;
89
+ left: 0;
90
+ height: 100%;
91
+ border-radius: 4px;
92
+ transition: width 0.3s ease, background-color 0.3s ease;
93
+
94
+ &.usage-low {
95
+ background: linear-gradient(90deg, #4ade80, #22c55e);
96
+ }
97
+
98
+ &.usage-medium {
99
+ background: linear-gradient(90deg, #fbbf24, #f59e0b);
100
+ }
101
+
102
+ &.usage-high {
103
+ background: linear-gradient(90deg, #f87171, #ef4444);
104
+ }
105
+ }
106
+
107
+ .usage-text {
108
+ position: absolute;
109
+ top: 50%;
110
+ left: 50%;
111
+ transform: translate(-50%, -50%);
112
+ font-size: 11px;
113
+ font-weight: 500;
114
+ color: var(--ai-text-primary, #f8f9fa);
115
+ text-shadow: 0 1px 2px rgba(0, 0, 0, 0.5);
116
+ white-space: nowrap;
117
+ }
118
+ }
119
+
77
120
  // Messages container
78
121
  .ai-sidebar-messages {
79
122
  flex: 1 !important;
@@ -8,6 +8,7 @@ import { LoggerService } from '../../services/core/logger.service';
8
8
  import { ChatHistoryService } from '../../services/chat/chat-history.service';
9
9
  import { AiSidebarService } from '../../services/chat/ai-sidebar.service';
10
10
  import { ThemeService, ThemeType } from '../../services/core/theme.service';
11
+ import { ContextManager } from '../../services/context/manager';
11
12
 
12
13
  /**
13
14
  * AI Sidebar 组件 - 替代 ChatInterfaceComponent
@@ -54,6 +55,19 @@ import { ThemeService, ThemeType } from '../../services/core/theme.service';
54
55
  </div>
55
56
  </div>
56
57
 
58
+ <!-- Token 使用情况 -->
59
+ <div class="token-usage-bar" *ngIf="currentTokens > 0">
60
+ <div class="usage-progress"
61
+ [style.width.%]="tokenUsagePercent"
62
+ [ngClass]="{
63
+ 'usage-low': tokenUsagePercent < 50,
64
+ 'usage-medium': tokenUsagePercent >= 50 && tokenUsagePercent < 80,
65
+ 'usage-high': tokenUsagePercent >= 80
66
+ }">
67
+ </div>
68
+ <span class="usage-text">{{ currentTokens | number }} / {{ maxTokens | number }} Token</span>
69
+ </div>
70
+
57
71
  <!-- Messages -->
58
72
  <div class="ai-sidebar-messages" #chatContainer (scroll)="onScroll($event)">
59
73
  <div *ngFor="let message of messages; let i = index" class="message-item" [ngClass]="message.role">
@@ -167,6 +181,11 @@ export class AiSidebarComponent implements OnInit, OnDestroy, AfterViewChecked,
167
181
  isComposing = false;
168
182
  charLimit = 4000;
169
183
 
184
+ // Token 使用状态
185
+ currentTokens: number = 0;
186
+ maxTokens: number = 200000;
187
+ tokenUsagePercent: number = 0;
188
+
170
189
  private destroy$ = new Subject<void>();
171
190
  private shouldScrollToBottom = false;
172
191
 
@@ -175,7 +194,8 @@ export class AiSidebarComponent implements OnInit, OnDestroy, AfterViewChecked,
175
194
  private config: ConfigProviderService,
176
195
  private logger: LoggerService,
177
196
  private chatHistory: ChatHistoryService,
178
- private themeService: ThemeService
197
+ private themeService: ThemeService,
198
+ private contextManager: ContextManager
179
199
  ) { }
180
200
 
181
201
  ngOnInit(): void {
@@ -357,11 +377,33 @@ export class AiSidebarComponent implements OnInit, OnDestroy, AfterViewChecked,
357
377
  this.messages.push(errorMessage);
358
378
  } finally {
359
379
  this.isLoading = false;
380
+ // 更新 Token 使用情况
381
+ this.updateTokenUsage();
360
382
  // 滚动到底部
361
383
  setTimeout(() => this.scrollToBottom(), 0);
362
384
  }
363
385
  }
364
386
 
387
+ /**
388
+ * 更新 Token 使用情况
389
+ */
390
+ private updateTokenUsage(): void {
391
+ // 获取最大上下文限制
392
+ this.maxTokens = this.config.getActiveProviderContextWindow() || 200000;
393
+
394
+ // 计算当前消息的 Token 使用量(简单估算:每4个字符≈1 Token)
395
+ this.currentTokens = this.messages.reduce((sum, msg) => {
396
+ const content = typeof msg.content === 'string' ? msg.content : '';
397
+ return sum + Math.ceil(content.length / 4);
398
+ }, 0);
399
+
400
+ // 计算使用百分比
401
+ this.tokenUsagePercent = Math.min(
402
+ Math.round((this.currentTokens / this.maxTokens) * 100),
403
+ 100
404
+ );
405
+ }
406
+
365
407
  /**
366
408
  * 清空聊天记录
367
409
  */
@@ -34,12 +34,15 @@
34
34
  </div>
35
35
 
36
36
  <!-- 标签页导航 -->
37
- <div class="tabs-nav" *ngIf="isEnabled">
38
- <button *ngFor="let tab of tabs" class="tab-button" [class.active]="activeTab === tab.id"
39
- (click)="switchTab(tab.id)">
37
+ <div class="settings-tabs" *ngIf="isEnabled">
38
+ <div *ngFor="let tab of tabs"
39
+ class="tab-item"
40
+ [class.active]="activeTab === tab.id"
41
+ [attr.data-tab]="tab.id"
42
+ (click)="switchTab(tab.id)">
40
43
  <i [class]="tab.icon"></i>
41
- <span>{{ t[tab.labelKey.split('.')[0]][tab.labelKey.split('.')[1]] }}</span>
42
- </button>
44
+ <span>{{ t.settings?.[tab.labelKey.split('.')[1]] || tab.labelKey }}</span>
45
+ </div>
43
46
  </div>
44
47
 
45
48
  <!-- 内容区域 -->
@@ -56,6 +59,11 @@
56
59
  </app-provider-config>
57
60
  </div>
58
61
 
62
+ <!-- 上下文管理设置 -->
63
+ <div *ngIf="activeTab === 'context'" class="tab-content">
64
+ <app-context-settings></app-context-settings>
65
+ </div>
66
+
59
67
  <!-- 安全设置 -->
60
68
  <div *ngIf="activeTab === 'security'" class="tab-content">
61
69
  <app-security-settings></app-security-settings>
@@ -97,49 +97,168 @@
97
97
  }
98
98
  }
99
99
 
100
- /* 标签页导航 */
101
- .tabs-nav {
100
+ // ==================== Tab 导航栏 ====================
101
+ .settings-tabs {
102
102
  display: flex;
103
- background-color: var(--ai-bg-secondary);
104
- border-bottom: 1px solid var(--ai-border);
103
+ align-items: center;
104
+ gap: 6px;
105
+ padding: 10px 16px;
106
+ background: linear-gradient(180deg,
107
+ rgba(0, 0, 0, 0.15) 0%,
108
+ rgba(0, 0, 0, 0.05) 100%
109
+ );
110
+ border-bottom: 1px solid rgba(255, 255, 255, 0.08);
111
+ margin-bottom: 20px;
105
112
  overflow-x: auto;
113
+ scrollbar-width: thin;
106
114
 
107
- .tab-button {
108
- padding: 0.75rem 1.5rem;
109
- border: none;
115
+ // 自定义滚动条
116
+ &::-webkit-scrollbar {
117
+ height: 4px;
118
+ }
119
+ &::-webkit-scrollbar-track {
110
120
  background: transparent;
111
- border-bottom: 2px solid transparent;
112
- display: flex;
113
- align-items: center;
114
- gap: 0.5rem;
115
- cursor: pointer;
116
- transition: all 0.2s;
117
- color: var(--ai-text-secondary);
118
- white-space: nowrap;
119
- font-size: 0.875rem;
120
- font-weight: 500;
121
+ }
122
+ &::-webkit-scrollbar-thumb {
123
+ background: rgba(255, 255, 255, 0.15);
124
+ border-radius: 4px;
121
125
 
122
126
  &:hover {
123
- color: var(--ai-text-primary);
124
- background-color: rgba(0, 0, 0, 0.05);
127
+ background: rgba(255, 255, 255, 0.25);
128
+ }
129
+ }
130
+ }
131
+
132
+ .tab-item {
133
+ display: flex;
134
+ align-items: center;
135
+ justify-content: center;
136
+ gap: 8px;
137
+ padding: 10px 20px;
138
+ border-radius: 20px;
139
+ cursor: pointer;
140
+ white-space: nowrap;
141
+ font-size: 13px;
142
+ font-weight: 500;
143
+ color: var(--ai-text-secondary, #8a8f98);
144
+ background: transparent;
145
+ border: 1px solid transparent;
146
+ transition: all 0.28s cubic-bezier(0.4, 0, 0.2, 1);
147
+ position: relative;
148
+ user-select: none;
149
+
150
+ // 图标
151
+ i {
152
+ font-size: 14px;
153
+ transition: transform 0.2s ease, color 0.2s ease, filter 0.2s ease;
154
+ }
155
+
156
+ // 悬停效果
157
+ &:hover:not(.active) {
158
+ background: rgba(255, 255, 255, 0.06);
159
+ color: var(--ai-text-primary, #e0e0e0);
160
+ border-color: rgba(255, 255, 255, 0.08);
161
+
162
+ i {
163
+ transform: scale(1.12);
125
164
  }
165
+ }
166
+
167
+ // 点击效果
168
+ &:active:not(.active) {
169
+ transform: scale(0.97);
170
+ }
126
171
 
127
- &.active {
128
- color: var(--ai-primary);
129
- border-bottom-color: var(--ai-primary);
130
- background-color: rgba(0, 123, 255, 0.05);
172
+ // 选中状态
173
+ &.active {
174
+ background: linear-gradient(135deg,
175
+ #4a9eff 0%,
176
+ #2d7dd2 50%,
177
+ #1a6bc0 100%
178
+ );
179
+ color: #ffffff;
180
+ border: 1px solid rgba(255, 255, 255, 0.2);
181
+ box-shadow:
182
+ 0 4px 16px rgba(45, 125, 210, 0.4),
183
+ 0 2px 4px rgba(0, 0, 0, 0.2),
184
+ inset 0 1px 0 rgba(255, 255, 255, 0.25);
185
+
186
+ i {
187
+ color: white;
188
+ filter: drop-shadow(0 0 6px rgba(255, 255, 255, 0.5));
131
189
  }
132
190
 
133
- .icon-settings,
134
- .icon-provider,
135
- .icon-shield,
136
- .icon-chat,
137
- .icon-advanced {
138
- font-size: 1rem;
191
+ // 选中光晕效果
192
+ &::before {
193
+ content: '';
194
+ position: absolute;
195
+ top: -2px;
196
+ left: -2px;
197
+ right: -2px;
198
+ bottom: -2px;
199
+ border-radius: 22px;
200
+ background: linear-gradient(135deg,
201
+ rgba(74, 158, 255, 0.3) 0%,
202
+ rgba(45, 125, 210, 0.1) 100%
203
+ );
204
+ z-index: -1;
205
+ opacity: 0;
206
+ animation: glowPulse 2s ease-in-out infinite;
139
207
  }
140
208
  }
141
209
  }
142
210
 
211
+ // 各 Tab 图标独特颜色(未选中状态)
212
+ .tab-item:not(.active) {
213
+ // 基本设置 - 银灰色
214
+ &[data-tab="general"] i,
215
+ i.fa-cog {
216
+ color: #9ca3af;
217
+ }
218
+
219
+ // AI 提供商 - 天蓝色
220
+ &[data-tab="providers"] i,
221
+ i.fa-cloud {
222
+ color: #60a5fa;
223
+ }
224
+
225
+ // 上下文设置 - 青蓝色
226
+ &[data-tab="context"] i,
227
+ i.fa-database {
228
+ color: #38bdf8;
229
+ }
230
+
231
+ // 安全设置 - 翠绿色
232
+ &[data-tab="security"] i,
233
+ i.fa-shield {
234
+ color: #4ade80;
235
+ }
236
+
237
+ // 聊天设置 - 紫罗兰色
238
+ &[data-tab="chat"] i,
239
+ i.fa-comments {
240
+ color: #c084fc;
241
+ }
242
+
243
+ // 高级设置 - 橙色
244
+ &[data-tab="advanced"] i,
245
+ i.fa-sliders {
246
+ color: #fb923c;
247
+ }
248
+ }
249
+
250
+ // 光晕脉冲动画
251
+ @keyframes glowPulse {
252
+ 0%, 100% {
253
+ opacity: 0.5;
254
+ transform: scale(1);
255
+ }
256
+ 50% {
257
+ opacity: 0.8;
258
+ transform: scale(1.02);
259
+ }
260
+ }
261
+
143
262
  /* 内容区域 */
144
263
  .settings-content {
145
264
  flex: 1;
@@ -363,10 +482,22 @@
363
482
  }
364
483
  }
365
484
 
366
- .tabs-nav {
367
- .tab-button {
368
- padding: 0.5rem 1rem;
369
- font-size: 0.8rem;
485
+ .settings-tabs {
486
+ padding: 8px 12px;
487
+ gap: 4px;
488
+
489
+ .tab-item {
490
+ padding: 8px 14px;
491
+ font-size: 12px;
492
+
493
+ // 小屏幕隐藏文字,只显示图标
494
+ span {
495
+ display: none;
496
+ }
497
+
498
+ i {
499
+ font-size: 16px;
500
+ }
370
501
  }
371
502
  }
372
503
 
@@ -66,6 +66,7 @@ export class AiSettingsTabComponent implements OnInit, OnDestroy {
66
66
  this.tabs = [
67
67
  { id: 'general', labelKey: 'settings.generalTab', icon: 'fa fa-cog' },
68
68
  { id: 'providers', labelKey: 'settings.providersTab', icon: 'fa fa-cloud' },
69
+ { id: 'context', labelKey: 'settings.contextTab', icon: 'fa fa-database' },
69
70
  { id: 'security', labelKey: 'settings.securityTab', icon: 'fa fa-shield' },
70
71
  { id: 'chat', labelKey: 'settings.chatTab', icon: 'fa fa-comments' },
71
72
  { id: 'advanced', labelKey: 'settings.advancedTab', icon: 'fa fa-sliders' }
@@ -0,0 +1,87 @@
1
+ <div class="context-settings">
2
+ <h3>{{ t.contextSettings?.title || '上下文管理' }}</h3>
3
+
4
+ <!-- 自动压缩开关 -->
5
+ <div class="setting-section">
6
+ <div class="section-header">
7
+ <h4>{{ t.contextSettings?.autoCompact || '自动压缩' }}</h4>
8
+ </div>
9
+ <div class="setting-item">
10
+ <label>{{ t.contextSettings?.enableAutoCompact || '启用自动压缩' }}</label>
11
+ <div class="toggle-switch">
12
+ <input type="checkbox" id="auto-compact"
13
+ [checked]="autoCompactEnabled"
14
+ (change)="toggleAutoCompact()">
15
+ <label for="auto-compact">
16
+ {{ autoCompactEnabled ? (t.common?.enabled || '启用') : (t.common?.disabled || '禁用') }}
17
+ </label>
18
+ </div>
19
+ <p class="setting-desc">{{ t.contextSettings?.autoCompactDesc || '当上下文超过阈值时自动压缩历史消息' }}</p>
20
+ </div>
21
+ </div>
22
+
23
+ <!-- Token 配置 -->
24
+ <div class="setting-section">
25
+ <div class="section-header">
26
+ <h4>{{ t.contextSettings?.tokenConfig || 'Token 配置' }}</h4>
27
+ </div>
28
+
29
+ <!-- 当前供应商上下文信息 -->
30
+ <div class="provider-context-info">
31
+ <i class="fa fa-info-circle"></i>
32
+ <span>{{ t.contextSettings?.currentProviderLimit || '当前供应商上下文限制' }}: {{ activeProviderContextWindow | number }} Token</span>
33
+ </div>
34
+
35
+ <div class="setting-item">
36
+ <label>{{ t.contextSettings?.maxContextTokens || '最大上下文 Token 数' }}</label>
37
+ <input type="number" class="form-control" [(ngModel)]="config.maxContextTokens"
38
+ min="10000" [max]="activeProviderContextWindow" step="10000">
39
+ <p class="setting-desc">{{ t.contextSettings?.maxContextTokensDesc || '不能超过当前供应商的上下文限制' }}</p>
40
+ </div>
41
+
42
+ <div class="setting-item">
43
+ <label>{{ t.contextSettings?.reservedOutputTokens || '输出预留 Token 数' }}</label>
44
+ <input type="number" class="form-control" [(ngModel)]="config.reservedOutputTokens"
45
+ min="1000" max="32000" step="1000">
46
+ <p class="setting-desc">{{ t.contextSettings?.reservedOutputTokensDesc || '为 AI 回复预留的 Token 数量' }}</p>
47
+ </div>
48
+ </div>
49
+
50
+ <!-- 压缩阈值配置 -->
51
+ <div class="setting-section">
52
+ <div class="section-header">
53
+ <h4>{{ t.contextSettings?.thresholdConfig || '压缩阈值' }}</h4>
54
+ </div>
55
+
56
+ <div class="setting-item">
57
+ <label>{{ t.contextSettings?.pruneThreshold || '裁剪阈值' }}: {{ (config.pruneThreshold * 100).toFixed(0) }}%</label>
58
+ <input type="range" class="form-range" [(ngModel)]="config.pruneThreshold"
59
+ min="0.5" max="0.9" step="0.05">
60
+ <p class="setting-desc">{{ t.contextSettings?.pruneThresholdDesc || '使用率超过此阈值时裁剪冗余内容(默认:70%)' }}</p>
61
+ </div>
62
+
63
+ <div class="setting-item">
64
+ <label>{{ t.contextSettings?.compactThreshold || '压缩阈值' }}: {{ (config.compactThreshold * 100).toFixed(0) }}%</label>
65
+ <input type="range" class="form-range" [(ngModel)]="config.compactThreshold"
66
+ min="0.7" max="0.95" step="0.05">
67
+ <p class="setting-desc">{{ t.contextSettings?.compactThresholdDesc || '使用率超过此阈值时生成摘要压缩(默认:85%)' }}</p>
68
+ </div>
69
+
70
+ <div class="setting-item">
71
+ <label>{{ t.contextSettings?.messagesToKeep || '保留消息数' }}</label>
72
+ <input type="number" class="form-control" [(ngModel)]="config.messagesToKeep"
73
+ min="1" max="20" step="1">
74
+ <p class="setting-desc">{{ t.contextSettings?.messagesToKeepDesc || '压缩时始终保留的最近消息数量(默认:3)' }}</p>
75
+ </div>
76
+ </div>
77
+
78
+ <!-- 操作按钮 -->
79
+ <div class="form-actions">
80
+ <button class="btn btn-primary" (click)="saveConfig()">
81
+ <i class="fa fa-save"></i> {{ t.providers?.saveConfig || '保存配置' }}
82
+ </button>
83
+ <button class="btn btn-secondary" (click)="resetToDefaults()">
84
+ <i class="fa fa-undo"></i> {{ t.common?.reset || '重置默认' }}
85
+ </button>
86
+ </div>
87
+ </div>
@@ -0,0 +1,133 @@
1
+ .context-settings {
2
+ max-width: 800px;
3
+
4
+ h3 {
5
+ margin: 0 0 1.5rem 0;
6
+ font-size: 1.25rem;
7
+ font-weight: 600;
8
+ color: var(--ai-text-primary);
9
+ }
10
+ }
11
+
12
+ .provider-context-info {
13
+ display: flex;
14
+ align-items: center;
15
+ gap: 8px;
16
+ padding: 10px 12px;
17
+ background: var(--ai-bg-tertiary, #f0f4f8);
18
+ border: 1px solid var(--ai-primary, #3b82f6);
19
+ border-radius: 8px;
20
+ margin-bottom: 16px;
21
+ font-size: 0.9em;
22
+ color: var(--ai-text-secondary, #64748b);
23
+
24
+ i {
25
+ color: var(--ai-primary, #3b82f6);
26
+ font-size: 1.1em;
27
+ }
28
+ }
29
+
30
+ .setting-section {
31
+ margin-bottom: 24px;
32
+ padding: 16px;
33
+ background: var(--ai-bg-secondary);
34
+ border: 1px solid var(--ai-border);
35
+ border-radius: 12px;
36
+
37
+ .section-header h4 {
38
+ margin: 0 0 16px 0;
39
+ font-size: 16px;
40
+ font-weight: 600;
41
+ color: var(--ai-text-primary);
42
+ }
43
+ }
44
+
45
+ .setting-item {
46
+ margin-bottom: 16px;
47
+
48
+ label {
49
+ display: block;
50
+ font-weight: 500;
51
+ margin-bottom: 6px;
52
+ color: var(--ai-text-primary);
53
+ }
54
+
55
+ .setting-desc {
56
+ font-size: 0.85em;
57
+ color: var(--ai-text-secondary);
58
+ margin-top: 4px;
59
+ margin-bottom: 0;
60
+ }
61
+
62
+ .form-control, .form-range {
63
+ width: 100%;
64
+ max-width: 300px;
65
+ padding: 8px 12px;
66
+ border: 1px solid var(--ai-border);
67
+ border-radius: 8px;
68
+ background: var(--ai-bg-primary);
69
+ color: var(--ai-text-primary);
70
+ }
71
+
72
+ .form-range {
73
+ padding: 0;
74
+ height: 6px;
75
+ cursor: pointer;
76
+ }
77
+ }
78
+
79
+ .toggle-switch {
80
+ display: flex;
81
+ align-items: center;
82
+ gap: 8px;
83
+
84
+ input[type="checkbox"] {
85
+ width: 18px;
86
+ height: 18px;
87
+ accent-color: var(--ai-primary);
88
+ cursor: pointer;
89
+ }
90
+
91
+ label {
92
+ margin: 0;
93
+ font-weight: normal;
94
+ cursor: pointer;
95
+ }
96
+ }
97
+
98
+ .form-actions {
99
+ display: flex;
100
+ gap: 12px;
101
+ margin-top: 24px;
102
+
103
+ .btn {
104
+ padding: 10px 20px;
105
+ border: none;
106
+ border-radius: 8px;
107
+ cursor: pointer;
108
+ display: inline-flex;
109
+ align-items: center;
110
+ gap: 8px;
111
+ font-size: 14px;
112
+ font-weight: 500;
113
+ transition: all 0.2s;
114
+
115
+ &.btn-primary {
116
+ background-color: var(--ai-primary);
117
+ color: white;
118
+
119
+ &:hover {
120
+ background-color: var(--ai-primary-hover);
121
+ }
122
+ }
123
+
124
+ &.btn-secondary {
125
+ background-color: var(--ai-secondary);
126
+ color: white;
127
+
128
+ &:hover {
129
+ filter: brightness(0.9);
130
+ }
131
+ }
132
+ }
133
+ }