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,183 @@
1
+ .chat-input-container {
2
+ padding: 1rem;
3
+ background-color: var(--ai-bg-secondary);
4
+ }
5
+
6
+ /* 输入区域 */
7
+ .input-wrapper {
8
+ position: relative;
9
+ margin-bottom: 0.75rem;
10
+
11
+ .chat-textarea {
12
+ width: 100%;
13
+ min-height: 44px;
14
+ max-height: 120px;
15
+ padding: 0.75rem 1rem;
16
+ padding-right: 4rem; // 为字符计数留出空间
17
+ border: 2px solid var(--ai-border);
18
+ border-radius: 0.75rem;
19
+ background-color: var(--ai-bg-primary);
20
+ color: var(--ai-dark);
21
+ font-family: inherit;
22
+ font-size: 14px;
23
+ line-height: 1.5;
24
+ resize: none;
25
+ transition: all 0.2s;
26
+ overflow-y: hidden; // 初始隐藏滚动条
27
+
28
+ &:focus {
29
+ outline: none;
30
+ border-color: var(--ai-primary);
31
+ box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.15);
32
+ }
33
+
34
+ &:disabled {
35
+ background-color: var(--ai-bg-secondary);
36
+ cursor: not-allowed;
37
+ opacity: 0.6;
38
+ }
39
+
40
+ &::placeholder {
41
+ color: var(--ai-secondary);
42
+ opacity: 0.8;
43
+ }
44
+ }
45
+
46
+ .char-counter {
47
+ position: absolute;
48
+ bottom: 0.5rem;
49
+ right: 0.75rem;
50
+ font-size: 0.75rem;
51
+ color: var(--ai-secondary);
52
+ background-color: var(--ai-bg-primary);
53
+ padding: 0.125rem 0.375rem;
54
+ border-radius: 0.25rem;
55
+
56
+ &.warning {
57
+ color: var(--ai-warning);
58
+ }
59
+
60
+ &.error {
61
+ color: var(--ai-danger);
62
+ font-weight: 600;
63
+ }
64
+ }
65
+ }
66
+
67
+ /* 操作按钮 */
68
+ .input-actions {
69
+ display: flex;
70
+ justify-content: flex-end;
71
+ gap: 0.5rem;
72
+
73
+ .btn-icon {
74
+ width: 40px;
75
+ height: 40px;
76
+ border: none;
77
+ background: transparent;
78
+ border-radius: 0.5rem;
79
+ display: flex;
80
+ align-items: center;
81
+ justify-content: center;
82
+ cursor: pointer;
83
+ transition: all 0.2s;
84
+ color: var(--ai-secondary);
85
+
86
+ &:hover {
87
+ background-color: var(--ai-border);
88
+ color: var(--ai-dark);
89
+ }
90
+
91
+ .icon-clear {
92
+ font-size: 1.125rem;
93
+ }
94
+ }
95
+
96
+ .btn-send {
97
+ min-width: 100px;
98
+ height: 40px;
99
+ padding: 0 1rem;
100
+ border: none;
101
+ border-radius: 0.5rem;
102
+ background-color: var(--ai-primary);
103
+ color: white;
104
+ font-weight: 500;
105
+ cursor: pointer;
106
+ transition: all 0.2s;
107
+ display: flex;
108
+ align-items: center;
109
+ justify-content: center;
110
+ gap: 0.5rem;
111
+
112
+ &:hover:not(:disabled) {
113
+ background-color: darken(#007bff, 7.5%);
114
+ transform: translateY(-1px);
115
+ }
116
+
117
+ &:disabled {
118
+ opacity: 0.5;
119
+ cursor: not-allowed;
120
+ transform: none;
121
+ }
122
+
123
+ .icon-send {
124
+ font-size: 1rem;
125
+ }
126
+
127
+ .btn-text {
128
+ font-size: 0.875rem;
129
+ }
130
+ }
131
+ }
132
+
133
+ /* 帮助提示 */
134
+ .input-hints {
135
+ margin-top: 0.5rem;
136
+ display: flex;
137
+ gap: 1rem;
138
+ flex-wrap: wrap;
139
+
140
+ .hint-item {
141
+ font-size: 0.75rem;
142
+ color: var(--ai-secondary);
143
+ display: flex;
144
+ align-items: center;
145
+ gap: 0.375rem;
146
+
147
+ kbd {
148
+ padding: 0.125rem 0.375rem;
149
+ background-color: var(--ai-bg-primary);
150
+ border: 1px solid var(--ai-border);
151
+ border-radius: 0.25rem;
152
+ font-size: 0.7rem;
153
+ font-family: monospace;
154
+ }
155
+
156
+ .icon-warning {
157
+ color: var(--ai-warning);
158
+ font-size: 0.875rem;
159
+ }
160
+ }
161
+ }
162
+
163
+ /* 响应式设计 */
164
+ @media (max-width: 768px) {
165
+ .chat-input-container {
166
+ padding: 0.75rem;
167
+ }
168
+
169
+ .input-actions {
170
+ .btn-send {
171
+ min-width: 80px;
172
+ .btn-text {
173
+ display: none;
174
+ }
175
+ }
176
+ }
177
+
178
+ .input-hints {
179
+ .hint-item {
180
+ font-size: 0.7rem;
181
+ }
182
+ }
183
+ }
@@ -0,0 +1,149 @@
1
+ import { Component, Output, EventEmitter, Input, ViewChild, ElementRef, OnInit, OnDestroy } from '@angular/core';
2
+ import { Subject } from 'rxjs';
3
+ import { debounceTime, takeUntil } from 'rxjs/operators';
4
+
5
+ @Component({
6
+ selector: 'app-chat-input',
7
+ templateUrl: './chat-input.component.html',
8
+ styleUrls: ['./chat-input.component.scss']
9
+ })
10
+ export class ChatInputComponent implements OnInit, OnDestroy {
11
+ @Input() disabled = false;
12
+ @Input() placeholder = '输入您的问题或描述要执行的命令...';
13
+ @Output() send = new EventEmitter<string>();
14
+
15
+ @ViewChild('textInput', { static: false }) textInput!: ElementRef<HTMLTextAreaElement>;
16
+
17
+ inputValue = '';
18
+ private inputSubject = new Subject<string>();
19
+ private destroy$ = new Subject<void>();
20
+ isComposing = false; // 用于处理中文输入法
21
+
22
+ ngOnInit(): void {
23
+ // 监听输入变化,实现防抖
24
+ this.inputSubject.pipe(
25
+ debounceTime(300),
26
+ takeUntil(this.destroy$)
27
+ ).subscribe(value => {
28
+ // 这里可以触发自动完成或其他功能
29
+ this.onInputChange(value);
30
+ });
31
+ }
32
+
33
+ ngOnDestroy(): void {
34
+ this.destroy$.next();
35
+ this.destroy$.complete();
36
+ }
37
+
38
+ /**
39
+ * 处理输入变化
40
+ */
41
+ onInputChange(value: string): void {
42
+ // TODO: 实现智能建议功能
43
+ // 可以基于输入内容提供命令建议
44
+ }
45
+
46
+ /**
47
+ * 处理键盘事件
48
+ */
49
+ onKeydown(event: KeyboardEvent): void {
50
+ // Enter 发送(不包含Shift)
51
+ if (event.key === 'Enter' && !event.shiftKey && !this.isComposing) {
52
+ event.preventDefault();
53
+ this.submit();
54
+ }
55
+ }
56
+
57
+ /**
58
+ * 处理输入事件
59
+ */
60
+ onInput(event: Event): void {
61
+ const target = event.target as HTMLTextAreaElement;
62
+ this.inputValue = target.value;
63
+ this.inputSubject.next(this.inputValue);
64
+ this.autoResize();
65
+ }
66
+
67
+ /**
68
+ * 处理composition开始(输入法)
69
+ */
70
+ onCompositionStart(): void {
71
+ this.isComposing = true;
72
+ }
73
+
74
+ /**
75
+ * 处理composition结束(输入法)
76
+ */
77
+ onCompositionEnd(): void {
78
+ this.isComposing = false;
79
+ this.autoResize();
80
+ }
81
+
82
+ /**
83
+ * 提交消息
84
+ */
85
+ submit(): void {
86
+ const message = this.inputValue.trim();
87
+ if (message && !this.disabled) {
88
+ this.send.emit(message);
89
+ this.inputValue = '';
90
+ setTimeout(() => this.autoResize(), 0);
91
+ this.textInput?.nativeElement.focus();
92
+ }
93
+ }
94
+
95
+ /**
96
+ * 清空输入
97
+ */
98
+ clear(): void {
99
+ this.inputValue = '';
100
+ this.autoResize();
101
+ this.textInput?.nativeElement.focus();
102
+ }
103
+
104
+ /**
105
+ * 自动调整高度
106
+ */
107
+ private autoResize(): void {
108
+ if (this.textInput?.nativeElement) {
109
+ const textarea = this.textInput.nativeElement;
110
+ textarea.style.height = 'auto';
111
+ textarea.style.height = Math.min(textarea.scrollHeight, 120) + 'px';
112
+ }
113
+ }
114
+
115
+ /**
116
+ * 获取字符计数
117
+ */
118
+ getCharCount(): number {
119
+ return this.inputValue.length;
120
+ }
121
+
122
+ /**
123
+ * 获取字符限制
124
+ */
125
+ getCharLimit(): number {
126
+ return 4000; // 4K字符限制
127
+ }
128
+
129
+ /**
130
+ * 检查是否接近限制
131
+ */
132
+ isNearLimit(): boolean {
133
+ return this.getCharCount() > this.getCharLimit() * 0.8;
134
+ }
135
+
136
+ /**
137
+ * 检查是否超过限制
138
+ */
139
+ isOverLimit(): boolean {
140
+ return this.getCharCount() > this.getCharLimit();
141
+ }
142
+
143
+ /**
144
+ * 聚焦输入框
145
+ */
146
+ focus(): void {
147
+ this.textInput?.nativeElement.focus();
148
+ }
149
+ }
@@ -0,0 +1,119 @@
1
+ <div class="ai-chat-interface">
2
+ <!-- 头部 -->
3
+ <div class="ai-chat-header">
4
+ <div class="header-left">
5
+ <h2 class="ai-title">
6
+ <i class="icon-chat"></i>
7
+ AI助手
8
+ </h2>
9
+ <span class="provider-badge">
10
+ <i class="icon-provider"></i>
11
+ {{ currentProvider }}
12
+ </span>
13
+ </div>
14
+ <div class="header-actions">
15
+ <button class="btn-icon" (click)="switchProvider()" title="切换AI提供商">
16
+ <i class="icon-switch"></i>
17
+ </button>
18
+ <button class="btn-icon" (click)="exportChat()" title="导出聊天记录">
19
+ <i class="icon-export"></i>
20
+ </button>
21
+ <button class="btn-icon danger" (click)="clearChat()" title="清空聊天记录">
22
+ <i class="icon-clear"></i>
23
+ </button>
24
+ </div>
25
+ </div>
26
+
27
+ <!-- 消息列表 -->
28
+ <div class="ai-chat-container">
29
+ <div class="messages-wrapper">
30
+ <!-- 消息分组 -->
31
+ <ng-container *ngFor="let message of messages; let i = index">
32
+ <!-- 检查是否需要显示日期分隔线 -->
33
+ <div *ngIf="i === 0 || !isSameDay(message.timestamp, messages[i-1].timestamp)"
34
+ class="date-separator">
35
+ <span class="date-text">
36
+ {{ isToday(message.timestamp) ? '今天' : message.timestamp.toLocaleDateString('zh-CN') }}
37
+ </span>
38
+ </div>
39
+
40
+ <!-- 消息项 -->
41
+ <div class="message-item"
42
+ [ngClass]="{
43
+ 'user-message': message.role === 'user',
44
+ 'assistant-message': message.role === 'assistant',
45
+ 'system-message': message.role === 'system'
46
+ }">
47
+ <!-- 头像 -->
48
+ <div class="message-avatar">
49
+ <div *ngIf="message.role === 'user'" class="avatar user">
50
+ <i class="icon-user"></i>
51
+ </div>
52
+ <div *ngIf="message.role === 'assistant'" class="avatar assistant">
53
+ <i class="icon-bot"></i>
54
+ </div>
55
+ <div *ngIf="message.role === 'system'" class="avatar system">
56
+ <i class="icon-info"></i>
57
+ </div>
58
+ </div>
59
+
60
+ <!-- 消息内容 -->
61
+ <div class="message-content">
62
+ <div class="message-bubble">
63
+ <!-- 用户消息 -->
64
+ <div *ngIf="message.role === 'user'" class="message-text">
65
+ {{ message.content }}
66
+ </div>
67
+
68
+ <!-- AI消息 -->
69
+ <div *ngIf="message.role === 'assistant'" class="message-text ai-response">
70
+ <pre>{{ message.content }}</pre>
71
+ </div>
72
+
73
+ <!-- 系统消息 -->
74
+ <div *ngIf="message.role === 'system'" class="message-text system">
75
+ <i class="icon-warning"></i>
76
+ {{ message.content }}
77
+ </div>
78
+ </div>
79
+
80
+ <!-- 消息时间 -->
81
+ <div class="message-time">
82
+ {{ formatTimestamp(message.timestamp) }}
83
+ </div>
84
+ </div>
85
+ </div>
86
+ </ng-container>
87
+
88
+ <!-- 加载指示器 -->
89
+ <div *ngIf="isLoading" class="loading-indicator">
90
+ <div class="typing-indicator">
91
+ <span></span>
92
+ <span></span>
93
+ <span></span>
94
+ </div>
95
+ <span class="loading-text">AI正在思考...</span>
96
+ </div>
97
+ </div>
98
+ </div>
99
+
100
+ <!-- 输入区域 -->
101
+ <div class="ai-chat-input">
102
+ <app-chat-input
103
+ (send)="onSendMessage($event)"
104
+ [disabled]="isLoading">
105
+ </app-chat-input>
106
+ </div>
107
+
108
+ <!-- 提示信息 -->
109
+ <div class="chat-tips" *ngIf="messages.length === 1">
110
+ <div class="tip-item">
111
+ <i class="icon-lightbulb"></i>
112
+ <span>提示:您可以描述想执行的命令,例如"查看当前目录的所有文件"</span>
113
+ </div>
114
+ <div class="tip-item">
115
+ <i class="icon-keyboard"></i>
116
+ <span>快捷键:Ctrl+Shift+G 生成命令,Ctrl+Shift+E 解释命令</span>
117
+ </div>
118
+ </div>
119
+ </div>