tabby-ai-assistant 1.0.0

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 (134) hide show
  1. package/README.md +232 -0
  2. package/dist/components/chat/chat-input.component.d.ts +65 -0
  3. package/dist/components/chat/chat-interface.component.d.ts +71 -0
  4. package/dist/components/chat/chat-message.component.d.ts +53 -0
  5. package/dist/components/chat/chat-settings.component.d.ts +62 -0
  6. package/dist/components/common/error-message.component.d.ts +11 -0
  7. package/dist/components/common/loading-spinner.component.d.ts +4 -0
  8. package/dist/components/security/consent-dialog.component.d.ts +11 -0
  9. package/dist/components/security/password-prompt.component.d.ts +10 -0
  10. package/dist/components/security/risk-confirm-dialog.component.d.ts +36 -0
  11. package/dist/components/settings/ai-settings-tab.component.d.ts +72 -0
  12. package/dist/components/settings/general-settings.component.d.ts +60 -0
  13. package/dist/components/settings/provider-config.component.d.ts +182 -0
  14. package/dist/components/settings/security-settings.component.d.ts +23 -0
  15. package/dist/components/terminal/ai-toolbar-button.component.d.ts +10 -0
  16. package/dist/components/terminal/command-preview.component.d.ts +15 -0
  17. package/dist/components/terminal/command-suggestion.component.d.ts +16 -0
  18. package/dist/index.d.ts +8 -0
  19. package/dist/index.js +2 -0
  20. package/dist/index.js.LICENSE.txt +18 -0
  21. package/dist/main.d.ts +8 -0
  22. package/dist/providers/tabby/ai-config.provider.d.ts +18 -0
  23. package/dist/providers/tabby/ai-hotkey.provider.d.ts +21 -0
  24. package/dist/providers/tabby/ai-settings-tab.provider.d.ts +11 -0
  25. package/dist/providers/tabby/ai-toolbar-button.provider.d.ts +17 -0
  26. package/dist/services/chat/chat-history.service.d.ts +67 -0
  27. package/dist/services/chat/chat-session.service.d.ts +58 -0
  28. package/dist/services/chat/command-generator.service.d.ts +49 -0
  29. package/dist/services/core/ai-assistant.service.d.ts +88 -0
  30. package/dist/services/core/ai-provider-manager.service.d.ts +119 -0
  31. package/dist/services/core/config-provider.service.d.ts +137 -0
  32. package/dist/services/core/logger.service.d.ts +21 -0
  33. package/dist/services/providers/anthropic-provider.service.d.ts +39 -0
  34. package/dist/services/providers/base-provider.service.d.ts +137 -0
  35. package/dist/services/providers/glm-provider.service.d.ts +91 -0
  36. package/dist/services/providers/minimax-provider.service.d.ts +93 -0
  37. package/dist/services/providers/openai-compatible.service.d.ts +39 -0
  38. package/dist/services/providers/openai-provider.service.d.ts +38 -0
  39. package/dist/services/security/consent-manager.service.d.ts +65 -0
  40. package/dist/services/security/password-manager.service.d.ts +67 -0
  41. package/dist/services/security/risk-assessment.service.d.ts +65 -0
  42. package/dist/services/security/security-validator.service.d.ts +36 -0
  43. package/dist/services/terminal/command-analyzer.service.d.ts +20 -0
  44. package/dist/services/terminal/context-menu.service.d.ts +24 -0
  45. package/dist/services/terminal/hotkey.service.d.ts +28 -0
  46. package/dist/services/terminal/terminal-context.service.d.ts +100 -0
  47. package/dist/types/ai.types.d.ts +107 -0
  48. package/dist/types/provider.types.d.ts +105 -0
  49. package/dist/types/security.types.d.ts +85 -0
  50. package/dist/types/terminal.types.d.ts +150 -0
  51. package/dist/utils/encryption.utils.d.ts +83 -0
  52. package/dist/utils/formatting.utils.d.ts +106 -0
  53. package/dist/utils/validation.utils.d.ts +83 -0
  54. package/integration-test-output.txt +50 -0
  55. package/integration-tests/api-integration.test.ts +183 -0
  56. package/jest.config.js +47 -0
  57. package/package.json +73 -0
  58. package/setup-jest.ts +37 -0
  59. package/src/components/chat/chat-input.component.html +61 -0
  60. package/src/components/chat/chat-input.component.scss +183 -0
  61. package/src/components/chat/chat-input.component.ts +149 -0
  62. package/src/components/chat/chat-interface.component.html +119 -0
  63. package/src/components/chat/chat-interface.component.scss +354 -0
  64. package/src/components/chat/chat-interface.component.ts +224 -0
  65. package/src/components/chat/chat-message.component.html +65 -0
  66. package/src/components/chat/chat-message.component.scss +178 -0
  67. package/src/components/chat/chat-message.component.ts +93 -0
  68. package/src/components/chat/chat-settings.component.html +132 -0
  69. package/src/components/chat/chat-settings.component.scss +172 -0
  70. package/src/components/chat/chat-settings.component.ts +168 -0
  71. package/src/components/common/error-message.component.ts +124 -0
  72. package/src/components/common/loading-spinner.component.ts +72 -0
  73. package/src/components/security/consent-dialog.component.ts +77 -0
  74. package/src/components/security/password-prompt.component.ts +79 -0
  75. package/src/components/security/risk-confirm-dialog.component.html +87 -0
  76. package/src/components/security/risk-confirm-dialog.component.scss +360 -0
  77. package/src/components/security/risk-confirm-dialog.component.ts +96 -0
  78. package/src/components/settings/ai-settings-tab.component.html +140 -0
  79. package/src/components/settings/ai-settings-tab.component.scss +371 -0
  80. package/src/components/settings/ai-settings-tab.component.ts +193 -0
  81. package/src/components/settings/general-settings.component.html +103 -0
  82. package/src/components/settings/general-settings.component.scss +285 -0
  83. package/src/components/settings/general-settings.component.ts +123 -0
  84. package/src/components/settings/provider-config.component.html +95 -0
  85. package/src/components/settings/provider-config.component.scss +60 -0
  86. package/src/components/settings/provider-config.component.ts +206 -0
  87. package/src/components/settings/security-settings.component.html +51 -0
  88. package/src/components/settings/security-settings.component.scss +66 -0
  89. package/src/components/settings/security-settings.component.ts +71 -0
  90. package/src/components/terminal/ai-toolbar-button.component.ts +49 -0
  91. package/src/components/terminal/command-preview.component.ts +185 -0
  92. package/src/components/terminal/command-suggestion.component.ts +128 -0
  93. package/src/index.ts +163 -0
  94. package/src/main.ts +16 -0
  95. package/src/providers/tabby/ai-config.provider.ts +70 -0
  96. package/src/providers/tabby/ai-hotkey.provider.ts +55 -0
  97. package/src/providers/tabby/ai-settings-tab.provider.ts +18 -0
  98. package/src/providers/tabby/ai-toolbar-button.provider.ts +49 -0
  99. package/src/services/chat/chat-history.service.ts +239 -0
  100. package/src/services/chat/chat-session.service.spec.ts +249 -0
  101. package/src/services/chat/chat-session.service.ts +180 -0
  102. package/src/services/chat/command-generator.service.ts +301 -0
  103. package/src/services/core/ai-assistant.service.ts +334 -0
  104. package/src/services/core/ai-provider-manager.service.ts +314 -0
  105. package/src/services/core/config-provider.service.ts +347 -0
  106. package/src/services/core/logger.service.ts +104 -0
  107. package/src/services/providers/anthropic-provider.service.ts +373 -0
  108. package/src/services/providers/base-provider.service.ts +369 -0
  109. package/src/services/providers/glm-provider.service.ts +467 -0
  110. package/src/services/providers/minimax-provider.service.ts +427 -0
  111. package/src/services/providers/openai-compatible.service.ts +394 -0
  112. package/src/services/providers/openai-provider.service.ts +376 -0
  113. package/src/services/security/consent-manager.service.ts +332 -0
  114. package/src/services/security/password-manager.service.ts +188 -0
  115. package/src/services/security/risk-assessment.service.ts +340 -0
  116. package/src/services/security/security-validator.service.ts +143 -0
  117. package/src/services/terminal/command-analyzer.service.ts +43 -0
  118. package/src/services/terminal/context-menu.service.ts +45 -0
  119. package/src/services/terminal/hotkey.service.ts +53 -0
  120. package/src/services/terminal/terminal-context.service.ts +317 -0
  121. package/src/styles/ai-assistant.scss +449 -0
  122. package/src/types/ai.types.ts +133 -0
  123. package/src/types/provider.types.ts +147 -0
  124. package/src/types/security.types.ts +103 -0
  125. package/src/types/terminal.types.ts +186 -0
  126. package/src/utils/encryption.utils.spec.ts +250 -0
  127. package/src/utils/encryption.utils.ts +271 -0
  128. package/src/utils/formatting.utils.ts +359 -0
  129. package/src/utils/validation.utils.spec.ts +225 -0
  130. package/src/utils/validation.utils.ts +314 -0
  131. package/tsconfig.json +45 -0
  132. package/webpack-docker.config.js +42 -0
  133. package/webpack.config.js +59 -0
  134. package/webpack.config.js.backup +57 -0
@@ -0,0 +1,371 @@
1
+ .ai-settings-tab {
2
+ display: flex;
3
+ flex-direction: column;
4
+ height: 100%;
5
+ background-color: var(--ai-bg-primary);
6
+ }
7
+
8
+ /* 头部 */
9
+ .settings-header {
10
+ padding: 1.5rem;
11
+ background-color: var(--ai-bg-secondary);
12
+ border-bottom: 1px solid var(--ai-border);
13
+ display: flex;
14
+ align-items: center;
15
+ justify-content: space-between;
16
+
17
+ .header-content {
18
+ flex: 1;
19
+
20
+ h2 {
21
+ margin: 0 0 0.75rem 0;
22
+ font-size: 1.5rem;
23
+ font-weight: 600;
24
+ display: flex;
25
+ align-items: center;
26
+ gap: 0.5rem;
27
+ color: var(--ai-dark);
28
+
29
+ .icon-robot {
30
+ font-size: 1.75rem;
31
+ }
32
+ }
33
+
34
+ .header-status {
35
+ display: flex;
36
+ gap: 1.5rem;
37
+ flex-wrap: wrap;
38
+
39
+ .status-indicator,
40
+ .provider-info {
41
+ display: flex;
42
+ align-items: center;
43
+ gap: 0.5rem;
44
+ font-size: 0.875rem;
45
+
46
+ .icon-power {
47
+ font-size: 1rem;
48
+ }
49
+ }
50
+
51
+ .status-indicator {
52
+ &.enabled {
53
+ color: var(--ai-success);
54
+
55
+ .icon-power {
56
+ animation: pulse 2s infinite;
57
+ }
58
+ }
59
+
60
+ &:not(.enabled) {
61
+ color: var(--ai-secondary);
62
+ }
63
+ }
64
+
65
+ .provider-info {
66
+ color: var(--ai-secondary);
67
+ }
68
+ }
69
+ }
70
+
71
+ .header-actions {
72
+ display: flex;
73
+ gap: 0.5rem;
74
+
75
+ .btn-icon {
76
+ width: 40px;
77
+ height: 40px;
78
+ border: none;
79
+ background: transparent;
80
+ border-radius: 0.5rem;
81
+ display: flex;
82
+ align-items: center;
83
+ justify-content: center;
84
+ cursor: pointer;
85
+ transition: all 0.2s;
86
+ color: var(--ai-secondary);
87
+
88
+ &:hover {
89
+ background-color: var(--ai-border);
90
+ color: var(--ai-dark);
91
+ }
92
+
93
+ .icon-refresh {
94
+ animation: spin 2s linear infinite;
95
+ }
96
+ }
97
+ }
98
+ }
99
+
100
+ /* 标签页导航 */
101
+ .tabs-nav {
102
+ display: flex;
103
+ background-color: var(--ai-bg-secondary);
104
+ border-bottom: 1px solid var(--ai-border);
105
+ overflow-x: auto;
106
+
107
+ .tab-button {
108
+ padding: 0.75rem 1.5rem;
109
+ border: none;
110
+ 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-secondary);
118
+ white-space: nowrap;
119
+ font-size: 0.875rem;
120
+ font-weight: 500;
121
+
122
+ &:hover {
123
+ color: var(--ai-dark);
124
+ background-color: rgba(0, 0, 0, 0.05);
125
+ }
126
+
127
+ &.active {
128
+ color: var(--ai-primary);
129
+ border-bottom-color: var(--ai-primary);
130
+ background-color: rgba(0, 123, 255, 0.05);
131
+ }
132
+
133
+ .icon-settings,
134
+ .icon-provider,
135
+ .icon-shield,
136
+ .icon-chat,
137
+ .icon-advanced {
138
+ font-size: 1rem;
139
+ }
140
+ }
141
+ }
142
+
143
+ /* 内容区域 */
144
+ .settings-content {
145
+ flex: 1;
146
+ overflow-y: auto;
147
+
148
+ .tab-content {
149
+ padding: 1.5rem;
150
+ }
151
+ }
152
+
153
+ /* 高级设置 */
154
+ .advanced-settings {
155
+ max-width: 800px;
156
+
157
+ h3 {
158
+ margin: 0 0 1.5rem 0;
159
+ font-size: 1.25rem;
160
+ font-weight: 600;
161
+ color: var(--ai-dark);
162
+ }
163
+
164
+ .settings-section {
165
+ margin-bottom: 2rem;
166
+ padding: 1.5rem;
167
+ background-color: var(--ai-bg-secondary);
168
+ border-radius: 0.5rem;
169
+ border: 1px solid var(--ai-border);
170
+
171
+ h4 {
172
+ margin: 0 0 1rem 0;
173
+ font-size: 1.125rem;
174
+ font-weight: 600;
175
+ color: var(--ai-dark);
176
+ }
177
+
178
+ .btn {
179
+ padding: 0.5rem 1rem;
180
+ margin-right: 0.75rem;
181
+ margin-bottom: 0.5rem;
182
+ border: none;
183
+ border-radius: 0.375rem;
184
+ font-weight: 500;
185
+ cursor: pointer;
186
+ transition: all 0.2s;
187
+ display: inline-flex;
188
+ align-items: center;
189
+ gap: 0.375rem;
190
+ font-size: 0.875rem;
191
+
192
+ &.btn-primary {
193
+ background-color: var(--ai-primary);
194
+ color: white;
195
+
196
+ &:hover {
197
+ background-color: darken(#007bff, 7.5%);
198
+ }
199
+ }
200
+
201
+ &.btn-warning {
202
+ background-color: var(--ai-warning);
203
+ color: var(--ai-dark);
204
+
205
+ &:hover {
206
+ background-color: darken(#ffc107, 7.5%);
207
+ }
208
+ }
209
+ }
210
+
211
+ .log-level-selector {
212
+ label {
213
+ display: block;
214
+ font-weight: 500;
215
+ margin-bottom: 0.5rem;
216
+ color: var(--ai-dark);
217
+ }
218
+
219
+ .form-control {
220
+ width: 200px;
221
+ padding: 0.5rem 0.75rem;
222
+ border: 1px solid var(--ai-border);
223
+ border-radius: 0.375rem;
224
+ background-color: var(--ai-bg-primary);
225
+ color: var(--ai-dark);
226
+
227
+ &:focus {
228
+ outline: none;
229
+ border-color: var(--ai-primary);
230
+ }
231
+ }
232
+ }
233
+
234
+ .system-info {
235
+ .info-item {
236
+ display: flex;
237
+ justify-content: space-between;
238
+ padding: 0.5rem 0;
239
+ border-bottom: 1px solid var(--ai-border);
240
+
241
+ &:last-child {
242
+ border-bottom: none;
243
+ }
244
+
245
+ label {
246
+ font-weight: 500;
247
+ color: var(--ai-dark);
248
+ }
249
+
250
+ span {
251
+ color: var(--ai-secondary);
252
+ }
253
+ }
254
+ }
255
+ }
256
+ }
257
+
258
+ /* 禁用提示 */
259
+ .disabled-message {
260
+ flex: 1;
261
+ display: flex;
262
+ align-items: center;
263
+ justify-content: center;
264
+ padding: 3rem;
265
+
266
+ .message-content {
267
+ text-align: center;
268
+ max-width: 400px;
269
+
270
+ .icon-pause-circle {
271
+ font-size: 4rem;
272
+ color: var(--ai-secondary);
273
+ margin-bottom: 1rem;
274
+ }
275
+
276
+ h3 {
277
+ margin: 0 0 0.75rem 0;
278
+ font-size: 1.5rem;
279
+ font-weight: 600;
280
+ color: var(--ai-dark);
281
+ }
282
+
283
+ p {
284
+ margin: 0 0 2rem 0;
285
+ color: var(--ai-secondary);
286
+ font-size: 1rem;
287
+ }
288
+
289
+ .btn {
290
+ padding: 0.75rem 2rem;
291
+ border: none;
292
+ border-radius: 0.5rem;
293
+ background-color: var(--ai-primary);
294
+ color: white;
295
+ font-weight: 500;
296
+ cursor: pointer;
297
+ transition: all 0.2s;
298
+ display: inline-flex;
299
+ align-items: center;
300
+ gap: 0.5rem;
301
+ font-size: 1rem;
302
+
303
+ &:hover {
304
+ background-color: darken(#007bff, 7.5%);
305
+ transform: translateY(-1px);
306
+ }
307
+ }
308
+ }
309
+ }
310
+
311
+ /* 动画 */
312
+ @keyframes pulse {
313
+ 0%, 100% {
314
+ opacity: 1;
315
+ }
316
+ 50% {
317
+ opacity: 0.5;
318
+ }
319
+ }
320
+
321
+ @keyframes spin {
322
+ to {
323
+ transform: rotate(360deg);
324
+ }
325
+ }
326
+
327
+ /* 响应式设计 */
328
+ @media (max-width: 768px) {
329
+ .settings-header {
330
+ flex-direction: column;
331
+ align-items: flex-start;
332
+ gap: 1rem;
333
+
334
+ .header-content {
335
+ width: 100%;
336
+
337
+ .header-status {
338
+ flex-direction: column;
339
+ gap: 0.5rem;
340
+ }
341
+ }
342
+
343
+ .header-actions {
344
+ width: 100%;
345
+ justify-content: flex-end;
346
+ }
347
+ }
348
+
349
+ .tabs-nav {
350
+ .tab-button {
351
+ padding: 0.5rem 1rem;
352
+ font-size: 0.8rem;
353
+ }
354
+ }
355
+
356
+ .settings-content .tab-content {
357
+ padding: 1rem;
358
+ }
359
+
360
+ .advanced-settings {
361
+ .settings-section {
362
+ padding: 1rem;
363
+
364
+ .btn {
365
+ width: 100%;
366
+ margin-right: 0;
367
+ justify-content: center;
368
+ }
369
+ }
370
+ }
371
+ }
@@ -0,0 +1,193 @@
1
+ import { Component, OnInit } from '@angular/core';
2
+ import { AiAssistantService } from '../../services/core/ai-assistant.service';
3
+ import { ConfigProviderService } from '../../services/core/config-provider.service';
4
+ import { LoggerService } from '../../services/core/logger.service';
5
+
6
+ @Component({
7
+ selector: 'app-ai-settings-tab',
8
+ templateUrl: './ai-settings-tab.component.html',
9
+ styleUrls: ['./ai-settings-tab.component.scss']
10
+ })
11
+ export class AiSettingsTabComponent implements OnInit {
12
+ activeTab = 'general';
13
+ isEnabled = true;
14
+ currentProvider = '';
15
+ providerStatus: any = {};
16
+
17
+ tabs = [
18
+ { id: 'general', label: '基本设置', icon: 'icon-settings' },
19
+ { id: 'providers', label: 'AI提供商', icon: 'icon-provider' },
20
+ { id: 'security', label: '安全设置', icon: 'icon-shield' },
21
+ { id: 'chat', label: '聊天设置', icon: 'icon-chat' },
22
+ { id: 'advanced', label: '高级设置', icon: 'icon-advanced' }
23
+ ];
24
+
25
+ constructor(
26
+ private aiService: AiAssistantService,
27
+ private config: ConfigProviderService,
28
+ private logger: LoggerService
29
+ ) {}
30
+
31
+ ngOnInit(): void {
32
+ this.loadSettings();
33
+ this.loadProviderStatus();
34
+ }
35
+
36
+ /**
37
+ * 加载设置
38
+ */
39
+ private loadSettings(): void {
40
+ this.isEnabled = this.config.isEnabled();
41
+ const defaultProvider = this.config.getDefaultProvider();
42
+ this.currentProvider = defaultProvider;
43
+ }
44
+
45
+ /**
46
+ * 加载提供商状态
47
+ */
48
+ private loadProviderStatus(): void {
49
+ this.providerStatus = this.aiService.getProviderStatus();
50
+ }
51
+
52
+ /**
53
+ * 切换标签页
54
+ */
55
+ switchTab(tabId: string): void {
56
+ this.activeTab = tabId;
57
+ }
58
+
59
+ /**
60
+ * 切换启用状态
61
+ */
62
+ toggleEnabled(): void {
63
+ this.isEnabled = !this.isEnabled;
64
+ this.config.setEnabled(this.isEnabled);
65
+
66
+ if (this.isEnabled) {
67
+ this.logger.info('AI Assistant enabled');
68
+ } else {
69
+ this.logger.info('AI Assistant disabled');
70
+ }
71
+ }
72
+
73
+ /**
74
+ * 切换提供商
75
+ */
76
+ switchProvider(providerName: string): void {
77
+ if (this.aiService.switchProvider(providerName)) {
78
+ this.currentProvider = providerName;
79
+ this.config.setDefaultProvider(providerName);
80
+ this.loadProviderStatus();
81
+ this.logger.info('Provider switched', { provider: providerName });
82
+ }
83
+ }
84
+
85
+ /**
86
+ * 刷新提供商状态
87
+ */
88
+ async refreshProviderStatus(): Promise<void> {
89
+ this.loadProviderStatus();
90
+ this.logger.debug('Provider status refreshed');
91
+ }
92
+
93
+ /**
94
+ * 获取提供商图标类名
95
+ */
96
+ getProviderIcon(providerName: string): string {
97
+ const icons: { [key: string]: string } = {
98
+ 'openai': 'icon-openai',
99
+ 'anthropic': 'icon-anthropic',
100
+ 'minimax': 'icon-minimax',
101
+ 'glm': 'icon-glm',
102
+ 'openai-compatible': 'icon-compatible'
103
+ };
104
+ return icons[providerName] || 'icon-provider';
105
+ }
106
+
107
+ /**
108
+ * 获取提供商状态颜色
109
+ */
110
+ getProviderStatusColor(healthy: boolean): string {
111
+ return healthy ? 'var(--ai-success)' : 'var(--ai-danger)';
112
+ }
113
+
114
+ /**
115
+ * 获取提供商状态文本
116
+ */
117
+ getProviderStatusText(healthy: boolean): string {
118
+ return healthy ? '正常' : '异常';
119
+ }
120
+
121
+ /**
122
+ * 验证配置
123
+ */
124
+ async validateConfig(): Promise<void> {
125
+ try {
126
+ const results = await this.aiService.validateConfig();
127
+ const invalidProviders = results.filter(r => !r.valid);
128
+
129
+ if (invalidProviders.length > 0) {
130
+ alert(`发现 ${invalidProviders.length} 个配置问题,请检查AI提供商设置。`);
131
+ } else {
132
+ alert('所有配置验证通过!');
133
+ }
134
+ } catch (error) {
135
+ this.logger.error('Failed to validate config', error);
136
+ alert('配置验证失败,请检查日志。');
137
+ }
138
+ }
139
+
140
+ /**
141
+ * 导出配置
142
+ */
143
+ exportConfig(): void {
144
+ const config = this.config.exportConfig();
145
+ const blob = new Blob([config], { type: 'application/json' });
146
+ const url = window.URL.createObjectURL(blob);
147
+ const a = document.createElement('a');
148
+ a.href = url;
149
+ a.download = `ai-assistant-config-${new Date().toISOString().slice(0, 10)}.json`;
150
+ a.click();
151
+ window.URL.revokeObjectURL(url);
152
+ }
153
+
154
+ /**
155
+ * 导入配置
156
+ */
157
+ importConfig(): void {
158
+ const input = document.createElement('input');
159
+ input.type = 'file';
160
+ input.accept = '.json';
161
+ input.onchange = (event) => {
162
+ const file = (event.target as HTMLInputElement).files?.[0];
163
+ if (file) {
164
+ const reader = new FileReader();
165
+ reader.onload = (e) => {
166
+ try {
167
+ const config = e.target?.result as string;
168
+ this.config.importConfig(config);
169
+ alert('配置导入成功,请重启插件生效。');
170
+ this.loadSettings();
171
+ this.loadProviderStatus();
172
+ } catch (error) {
173
+ alert('配置导入失败,请检查文件格式。');
174
+ }
175
+ };
176
+ reader.readAsText(file);
177
+ }
178
+ };
179
+ input.click();
180
+ }
181
+
182
+ /**
183
+ * 重置为默认配置
184
+ */
185
+ resetToDefaults(): void {
186
+ if (confirm('确定要重置所有设置为默认值吗?此操作不可恢复。')) {
187
+ this.config.reset();
188
+ this.loadSettings();
189
+ this.loadProviderStatus();
190
+ alert('配置已重置为默认值。');
191
+ }
192
+ }
193
+ }
@@ -0,0 +1,103 @@
1
+ <div class="general-settings">
2
+ <h3>基本设置</h3>
3
+
4
+ <!-- 启用状态 -->
5
+ <div class="settings-section">
6
+ <h4>功能状态</h4>
7
+ <div class="form-check">
8
+ <input
9
+ type="checkbox"
10
+ id="enabled"
11
+ [(ngModel)]="isEnabled"
12
+ (change)="updateEnabled(isEnabled)">
13
+ <label for="enabled">
14
+ <strong>启用AI助手</strong>
15
+ <p class="form-description">启用或禁用整个AI助手功能</p>
16
+ </label>
17
+ </div>
18
+ </div>
19
+
20
+ <!-- 默认提供商 -->
21
+ <div class="settings-section">
22
+ <h4>默认AI提供商</h4>
23
+ <div class="provider-selector">
24
+ <label>选择默认提供商</label>
25
+ <div class="providers-grid">
26
+ <div
27
+ *ngFor="let provider of availableProviders"
28
+ class="provider-card"
29
+ [class.selected]="selectedProvider === provider.name"
30
+ (click)="updateDefaultProvider(provider.name)">
31
+ <div class="provider-header">
32
+ <i class="icon-provider"></i>
33
+ <h5>{{ provider.displayName }}</h5>
34
+ </div>
35
+ <div class="provider-body">
36
+ <p class="provider-desc">{{ provider.description || 'AI模型提供商' }}</p>
37
+ <div class="provider-meta">
38
+ <span class="status-badge" [style.color]="getProviderStatus(provider.name).color">
39
+ {{ getProviderStatus(provider.name).text }}
40
+ </span>
41
+ </div>
42
+ </div>
43
+ </div>
44
+ </div>
45
+ </div>
46
+ </div>
47
+
48
+ <!-- 界面设置 -->
49
+ <div class="settings-section">
50
+ <h4>界面设置</h4>
51
+
52
+ <div class="form-group">
53
+ <label>语言</label>
54
+ <select class="form-control" [(ngModel)]="language" (change)="updateLanguage(language)">
55
+ <option *ngFor="let lang of languages" [value]="lang.value">
56
+ {{ lang.label }}
57
+ </option>
58
+ </select>
59
+ </div>
60
+
61
+ <div class="form-group">
62
+ <label>主题</label>
63
+ <div class="theme-selector">
64
+ <button
65
+ *ngFor="let theme of themes"
66
+ class="theme-btn"
67
+ [class.active]="this.theme === theme.value"
68
+ (click)="updateTheme(theme.value)">
69
+ <div class="theme-preview" [attr.data-theme]="theme.value"></div>
70
+ <span>{{ theme.label }}</span>
71
+ </button>
72
+ </div>
73
+ </div>
74
+ </div>
75
+
76
+ <!-- 快捷键提示 -->
77
+ <div class="settings-section">
78
+ <h4>快捷键</h4>
79
+ <div class="hotkey-list">
80
+ <div class="hotkey-item">
81
+ <div class="hotkey-info">
82
+ <strong>打开AI助手</strong>
83
+ <p class="form-description">打开聊天界面</p>
84
+ </div>
85
+ <kbd>Ctrl</kbd> + <kbd>Shift</kbd> + <kbd>A</kbd>
86
+ </div>
87
+ <div class="hotkey-item">
88
+ <div class="hotkey-info">
89
+ <strong>生成命令</strong>
90
+ <p class="form-description">从选择生成命令</p>
91
+ </div>
92
+ <kbd>Ctrl</kbd> + <kbd>Shift</kbd> + <kbd>G</kbd>
93
+ </div>
94
+ <div class="hotkey-item">
95
+ <div class="hotkey-info">
96
+ <strong>解释命令</strong>
97
+ <p class="form-description">解释当前选择</p>
98
+ </div>
99
+ <kbd>Ctrl</kbd> + <kbd>Shift</kbd> + <kbd>E</kbd>
100
+ </div>
101
+ </div>
102
+ </div>
103
+ </div>