tabby-ai-assistant 1.0.5 → 1.0.6
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 +147 -0
- package/dist/components/chat/chat-interface.component.d.ts +38 -6
- package/dist/components/settings/general-settings.component.d.ts +6 -3
- package/dist/components/settings/provider-config.component.d.ts +25 -12
- package/dist/components/terminal/command-preview.component.d.ts +38 -0
- package/dist/index-full.d.ts +8 -0
- package/dist/index-minimal.d.ts +3 -0
- package/dist/index.d.ts +7 -3
- package/dist/index.js +1 -2
- package/dist/providers/tabby/ai-config.provider.d.ts +57 -5
- package/dist/providers/tabby/ai-hotkey.provider.d.ts +8 -14
- package/dist/providers/tabby/ai-toolbar-button.provider.d.ts +8 -9
- package/dist/services/chat/ai-sidebar.service.d.ts +89 -0
- package/dist/services/chat/chat-history.service.d.ts +78 -0
- package/dist/services/chat/chat-session.service.d.ts +57 -2
- package/dist/services/context/compaction.d.ts +90 -0
- package/dist/services/context/manager.d.ts +69 -0
- package/dist/services/context/memory.d.ts +116 -0
- package/dist/services/context/token-budget.d.ts +105 -0
- package/dist/services/core/ai-assistant.service.d.ts +40 -1
- package/dist/services/core/checkpoint.service.d.ts +130 -0
- package/dist/services/platform/escape-sequence.service.d.ts +132 -0
- package/dist/services/platform/platform-detection.service.d.ts +146 -0
- package/dist/services/providers/anthropic-provider.service.d.ts +5 -0
- package/dist/services/providers/base-provider.service.d.ts +6 -1
- package/dist/services/providers/glm-provider.service.d.ts +5 -0
- package/dist/services/providers/minimax-provider.service.d.ts +10 -1
- package/dist/services/providers/ollama-provider.service.d.ts +76 -0
- package/dist/services/providers/openai-compatible.service.d.ts +5 -0
- package/dist/services/providers/openai-provider.service.d.ts +5 -0
- package/dist/services/providers/vllm-provider.service.d.ts +82 -0
- package/dist/services/terminal/buffer-analyzer.service.d.ts +128 -0
- package/dist/services/terminal/terminal-manager.service.d.ts +185 -0
- package/dist/services/terminal/terminal-tools.service.d.ts +79 -0
- package/dist/types/ai.types.d.ts +92 -0
- package/dist/types/provider.types.d.ts +1 -1
- package/package.json +7 -10
- package/src/components/chat/ai-sidebar.component.ts +945 -0
- package/src/components/chat/chat-input.component.html +9 -24
- package/src/components/chat/chat-input.component.scss +3 -2
- package/src/components/chat/chat-interface.component.html +77 -69
- package/src/components/chat/chat-interface.component.scss +54 -4
- package/src/components/chat/chat-interface.component.ts +250 -34
- package/src/components/chat/chat-settings.component.scss +4 -4
- package/src/components/chat/chat-settings.component.ts +22 -11
- package/src/components/common/error-message.component.html +15 -0
- package/src/components/common/error-message.component.scss +77 -0
- package/src/components/common/error-message.component.ts +2 -96
- package/src/components/common/loading-spinner.component.html +4 -0
- package/src/components/common/loading-spinner.component.scss +57 -0
- package/src/components/common/loading-spinner.component.ts +2 -63
- package/src/components/security/consent-dialog.component.html +22 -0
- package/src/components/security/consent-dialog.component.scss +34 -0
- package/src/components/security/consent-dialog.component.ts +2 -55
- package/src/components/security/password-prompt.component.html +19 -0
- package/src/components/security/password-prompt.component.scss +30 -0
- package/src/components/security/password-prompt.component.ts +2 -54
- package/src/components/security/risk-confirm-dialog.component.html +8 -12
- package/src/components/security/risk-confirm-dialog.component.scss +8 -5
- package/src/components/security/risk-confirm-dialog.component.ts +6 -6
- package/src/components/settings/ai-settings-tab.component.html +16 -20
- package/src/components/settings/ai-settings-tab.component.scss +8 -5
- package/src/components/settings/ai-settings-tab.component.ts +12 -12
- package/src/components/settings/general-settings.component.html +8 -17
- package/src/components/settings/general-settings.component.scss +6 -3
- package/src/components/settings/general-settings.component.ts +62 -22
- package/src/components/settings/provider-config.component.html +19 -39
- package/src/components/settings/provider-config.component.scss +182 -39
- package/src/components/settings/provider-config.component.ts +119 -7
- package/src/components/settings/security-settings.component.scss +1 -1
- package/src/components/terminal/ai-toolbar-button.component.html +8 -0
- package/src/components/terminal/ai-toolbar-button.component.scss +20 -0
- package/src/components/terminal/ai-toolbar-button.component.ts +2 -30
- package/src/components/terminal/command-preview.component.html +61 -0
- package/src/components/terminal/command-preview.component.scss +72 -0
- package/src/components/terminal/command-preview.component.ts +127 -140
- package/src/components/terminal/command-suggestion.component.html +23 -0
- package/src/components/terminal/command-suggestion.component.scss +55 -0
- package/src/components/terminal/command-suggestion.component.ts +2 -77
- package/src/index-minimal.ts +32 -0
- package/src/index.ts +94 -11
- package/src/index.ts.backup +165 -0
- package/src/providers/tabby/ai-config.provider.ts +60 -51
- package/src/providers/tabby/ai-hotkey.provider.ts +23 -39
- package/src/providers/tabby/ai-settings-tab.provider.ts +2 -2
- package/src/providers/tabby/ai-toolbar-button.provider.ts +29 -24
- package/src/services/chat/ai-sidebar.service.ts +258 -0
- package/src/services/chat/chat-history.service.ts +308 -0
- package/src/services/chat/chat-history.service.ts.backup +239 -0
- package/src/services/chat/chat-session.service.ts +276 -3
- package/src/services/context/compaction.ts +483 -0
- package/src/services/context/manager.ts +442 -0
- package/src/services/context/memory.ts +519 -0
- package/src/services/context/token-budget.ts +422 -0
- package/src/services/core/ai-assistant.service.ts +280 -5
- package/src/services/core/ai-provider-manager.service.ts +2 -2
- package/src/services/core/checkpoint.service.ts +619 -0
- package/src/services/platform/escape-sequence.service.ts +499 -0
- package/src/services/platform/platform-detection.service.ts +494 -0
- package/src/services/providers/anthropic-provider.service.ts +28 -1
- package/src/services/providers/base-provider.service.ts +7 -1
- package/src/services/providers/glm-provider.service.ts +28 -1
- package/src/services/providers/minimax-provider.service.ts +209 -11
- package/src/services/providers/ollama-provider.service.ts +445 -0
- package/src/services/providers/openai-compatible.service.ts +9 -0
- package/src/services/providers/openai-provider.service.ts +9 -0
- package/src/services/providers/vllm-provider.service.ts +463 -0
- package/src/services/security/risk-assessment.service.ts +6 -2
- package/src/services/terminal/buffer-analyzer.service.ts +594 -0
- package/src/services/terminal/terminal-manager.service.ts +748 -0
- package/src/services/terminal/terminal-tools.service.ts +441 -0
- package/src/styles/ai-assistant.scss +78 -6
- package/src/types/ai.types.ts +144 -0
- package/src/types/provider.types.ts +1 -1
- package/tsconfig.json +9 -9
- package/webpack.config.js +28 -6
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
.spinner-container {
|
|
2
|
+
display: flex;
|
|
3
|
+
flex-direction: column;
|
|
4
|
+
align-items: center;
|
|
5
|
+
justify-content: center;
|
|
6
|
+
padding: 2rem;
|
|
7
|
+
|
|
8
|
+
&.small {
|
|
9
|
+
padding: 1rem;
|
|
10
|
+
|
|
11
|
+
.spinner {
|
|
12
|
+
width: 24px;
|
|
13
|
+
height: 24px;
|
|
14
|
+
border-width: 2px;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.spinner-message {
|
|
18
|
+
font-size: 0.75rem;
|
|
19
|
+
margin-top: 0.5rem;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
&.medium {
|
|
24
|
+
.spinner {
|
|
25
|
+
width: 36px;
|
|
26
|
+
height: 36px;
|
|
27
|
+
border-width: 3px;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
&.large {
|
|
32
|
+
.spinner {
|
|
33
|
+
width: 48px;
|
|
34
|
+
height: 48px;
|
|
35
|
+
border-width: 4px;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.spinner {
|
|
41
|
+
border: 3px solid var(--ai-border);
|
|
42
|
+
border-top-color: var(--ai-primary);
|
|
43
|
+
border-radius: 50%;
|
|
44
|
+
animation: spin 0.8s linear infinite;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.spinner-message {
|
|
48
|
+
margin-top: 1rem;
|
|
49
|
+
color: var(--ai-secondary);
|
|
50
|
+
font-size: 0.875rem;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
@keyframes spin {
|
|
54
|
+
to {
|
|
55
|
+
transform: rotate(360deg);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -2,69 +2,8 @@ import { Component, Input } from '@angular/core';
|
|
|
2
2
|
|
|
3
3
|
@Component({
|
|
4
4
|
selector: 'app-loading-spinner',
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
<div class="spinner"></div>
|
|
8
|
-
<p *ngIf="message" class="spinner-message">{{ message }}</p>
|
|
9
|
-
</div>
|
|
10
|
-
`,
|
|
11
|
-
styles: [`
|
|
12
|
-
.spinner-container {
|
|
13
|
-
display: flex;
|
|
14
|
-
flex-direction: column;
|
|
15
|
-
align-items: center;
|
|
16
|
-
justify-content: center;
|
|
17
|
-
padding: 2rem;
|
|
18
|
-
|
|
19
|
-
&.small {
|
|
20
|
-
padding: 1rem;
|
|
21
|
-
|
|
22
|
-
.spinner {
|
|
23
|
-
width: 24px;
|
|
24
|
-
height: 24px;
|
|
25
|
-
border-width: 2px;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
.spinner-message {
|
|
29
|
-
font-size: 0.75rem;
|
|
30
|
-
margin-top: 0.5rem;
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
&.medium {
|
|
35
|
-
.spinner {
|
|
36
|
-
width: 36px;
|
|
37
|
-
height: 36px;
|
|
38
|
-
border-width: 3px;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
&.large {
|
|
43
|
-
.spinner {
|
|
44
|
-
width: 48px;
|
|
45
|
-
height: 48px;
|
|
46
|
-
border-width: 4px;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
.spinner {
|
|
52
|
-
border: 3px solid var(--ai-border);
|
|
53
|
-
border-top-color: var(--ai-primary);
|
|
54
|
-
border-radius: 50%;
|
|
55
|
-
animation: spin 0.8s linear infinite;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
.spinner-message {
|
|
59
|
-
margin-top: 1rem;
|
|
60
|
-
color: var(--ai-secondary);
|
|
61
|
-
font-size: 0.875rem;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
@keyframes spin {
|
|
65
|
-
to { transform: rotate(360deg); }
|
|
66
|
-
}
|
|
67
|
-
`]
|
|
5
|
+
templateUrl: './loading-spinner.component.html',
|
|
6
|
+
styleUrls: ['./loading-spinner.component.scss']
|
|
68
7
|
})
|
|
69
8
|
export class LoadingSpinnerComponent {
|
|
70
9
|
@Input() size: 'small' | 'medium' | 'large' = 'medium';
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<div class="consent-dialog">
|
|
2
|
+
<div class="dialog-header">
|
|
3
|
+
<i class="fa fa-question-circle"></i>
|
|
4
|
+
<h3>确认执行</h3>
|
|
5
|
+
</div>
|
|
6
|
+
<div class="dialog-content">
|
|
7
|
+
<p>确定要执行此命令吗?</p>
|
|
8
|
+
<div class="command-preview">
|
|
9
|
+
<code>{{ command }}</code>
|
|
10
|
+
</div>
|
|
11
|
+
<div class="checkbox-group">
|
|
12
|
+
<label>
|
|
13
|
+
<input type="checkbox" [(ngModel)]="rememberChoice">
|
|
14
|
+
记住我的选择(30天内不再提示)
|
|
15
|
+
</label>
|
|
16
|
+
</div>
|
|
17
|
+
</div>
|
|
18
|
+
<div class="dialog-footer">
|
|
19
|
+
<button class="btn btn-secondary" (click)="cancel()">取消</button>
|
|
20
|
+
<button class="btn btn-primary" (click)="confirm()">确认执行</button>
|
|
21
|
+
</div>
|
|
22
|
+
</div>
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
.consent-dialog {
|
|
2
|
+
background: var(--ai-bg-primary);
|
|
3
|
+
border-radius: 0.5rem;
|
|
4
|
+
max-width: 500px;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
.dialog-header {
|
|
8
|
+
padding: 1.5rem;
|
|
9
|
+
text-align: center;
|
|
10
|
+
border-bottom: 1px solid var(--ai-border);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.dialog-content {
|
|
14
|
+
padding: 1.5rem;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.command-preview {
|
|
18
|
+
background: var(--ai-bg-secondary);
|
|
19
|
+
padding: 0.75rem;
|
|
20
|
+
border-radius: 0.375rem;
|
|
21
|
+
margin: 1rem 0;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.checkbox-group {
|
|
25
|
+
margin-top: 1rem;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.dialog-footer {
|
|
29
|
+
padding: 1rem 1.5rem;
|
|
30
|
+
display: flex;
|
|
31
|
+
justify-content: flex-end;
|
|
32
|
+
gap: 0.5rem;
|
|
33
|
+
border-top: 1px solid var(--ai-border);
|
|
34
|
+
}
|
|
@@ -4,61 +4,8 @@ import { RiskLevel } from '../../types/security.types';
|
|
|
4
4
|
|
|
5
5
|
@Component({
|
|
6
6
|
selector: 'app-consent-dialog',
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
<div class="dialog-header">
|
|
10
|
-
<i class="icon-help-circle"></i>
|
|
11
|
-
<h3>确认执行</h3>
|
|
12
|
-
</div>
|
|
13
|
-
<div class="dialog-content">
|
|
14
|
-
<p>确定要执行此命令吗?</p>
|
|
15
|
-
<div class="command-preview">
|
|
16
|
-
<code>{{ command }}</code>
|
|
17
|
-
</div>
|
|
18
|
-
<div class="checkbox-group">
|
|
19
|
-
<label>
|
|
20
|
-
<input type="checkbox" [(ngModel)]="rememberChoice">
|
|
21
|
-
记住我的选择(30天内不再提示)
|
|
22
|
-
</label>
|
|
23
|
-
</div>
|
|
24
|
-
</div>
|
|
25
|
-
<div class="dialog-footer">
|
|
26
|
-
<button class="btn btn-secondary" (click)="cancel()">取消</button>
|
|
27
|
-
<button class="btn btn-primary" (click)="confirm()">确认执行</button>
|
|
28
|
-
</div>
|
|
29
|
-
</div>
|
|
30
|
-
`,
|
|
31
|
-
styles: [`
|
|
32
|
-
.consent-dialog {
|
|
33
|
-
background: var(--ai-bg-primary);
|
|
34
|
-
border-radius: 0.5rem;
|
|
35
|
-
max-width: 500px;
|
|
36
|
-
}
|
|
37
|
-
.dialog-header {
|
|
38
|
-
padding: 1.5rem;
|
|
39
|
-
text-align: center;
|
|
40
|
-
border-bottom: 1px solid var(--ai-border);
|
|
41
|
-
}
|
|
42
|
-
.dialog-content {
|
|
43
|
-
padding: 1.5rem;
|
|
44
|
-
}
|
|
45
|
-
.command-preview {
|
|
46
|
-
background: var(--ai-bg-secondary);
|
|
47
|
-
padding: 0.75rem;
|
|
48
|
-
border-radius: 0.375rem;
|
|
49
|
-
margin: 1rem 0;
|
|
50
|
-
}
|
|
51
|
-
.checkbox-group {
|
|
52
|
-
margin-top: 1rem;
|
|
53
|
-
}
|
|
54
|
-
.dialog-footer {
|
|
55
|
-
padding: 1rem 1.5rem;
|
|
56
|
-
display: flex;
|
|
57
|
-
justify-content: flex-end;
|
|
58
|
-
gap: 0.5rem;
|
|
59
|
-
border-top: 1px solid var(--ai-border);
|
|
60
|
-
}
|
|
61
|
-
`]
|
|
7
|
+
templateUrl: './consent-dialog.component.html',
|
|
8
|
+
styleUrls: ['./consent-dialog.component.scss']
|
|
62
9
|
})
|
|
63
10
|
export class ConsentDialogComponent {
|
|
64
11
|
@Input() command: string = '';
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<div class="password-prompt">
|
|
2
|
+
<div class="prompt-header">
|
|
3
|
+
<i class="fa fa-lock"></i>
|
|
4
|
+
<h3>密码验证</h3>
|
|
5
|
+
</div>
|
|
6
|
+
<div class="prompt-content">
|
|
7
|
+
<p>请输入密码以执行此高风险操作:</p>
|
|
8
|
+
<input type="password" class="form-control" [(ngModel)]="password" (keydown.enter)="submit()" placeholder="输入密码"
|
|
9
|
+
#passwordInput>
|
|
10
|
+
<div *ngIf="errorMessage" class="error-message">
|
|
11
|
+
<i class="fa fa-exclamation-circle"></i>
|
|
12
|
+
{{ errorMessage }}
|
|
13
|
+
</div>
|
|
14
|
+
</div>
|
|
15
|
+
<div class="prompt-footer">
|
|
16
|
+
<button class="btn btn-secondary" (click)="cancel()">取消</button>
|
|
17
|
+
<button class="btn btn-primary" (click)="submit()" [disabled]="!password">确认</button>
|
|
18
|
+
</div>
|
|
19
|
+
</div>
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
.password-prompt {
|
|
2
|
+
background: var(--ai-bg-primary);
|
|
3
|
+
border-radius: 0.5rem;
|
|
4
|
+
max-width: 400px;
|
|
5
|
+
margin: 0 auto;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
.prompt-header {
|
|
9
|
+
padding: 1.5rem;
|
|
10
|
+
text-align: center;
|
|
11
|
+
border-bottom: 1px solid var(--ai-border);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.prompt-content {
|
|
15
|
+
padding: 1.5rem;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.error-message {
|
|
19
|
+
margin-top: 0.75rem;
|
|
20
|
+
color: var(--ai-danger);
|
|
21
|
+
font-size: 0.875rem;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.prompt-footer {
|
|
25
|
+
padding: 1rem 1.5rem;
|
|
26
|
+
display: flex;
|
|
27
|
+
justify-content: flex-end;
|
|
28
|
+
gap: 0.5rem;
|
|
29
|
+
border-top: 1px solid var(--ai-border);
|
|
30
|
+
}
|
|
@@ -3,60 +3,8 @@ import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
|
|
|
3
3
|
|
|
4
4
|
@Component({
|
|
5
5
|
selector: 'app-password-prompt',
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
<div class="prompt-header">
|
|
9
|
-
<i class="icon-lock"></i>
|
|
10
|
-
<h3>密码验证</h3>
|
|
11
|
-
</div>
|
|
12
|
-
<div class="prompt-content">
|
|
13
|
-
<p>请输入密码以执行此高风险操作:</p>
|
|
14
|
-
<input
|
|
15
|
-
type="password"
|
|
16
|
-
class="form-control"
|
|
17
|
-
[(ngModel)]="password"
|
|
18
|
-
(keydown.enter)="submit()"
|
|
19
|
-
placeholder="输入密码"
|
|
20
|
-
#passwordInput>
|
|
21
|
-
<div *ngIf="errorMessage" class="error-message">
|
|
22
|
-
<i class="icon-alert-circle"></i>
|
|
23
|
-
{{ errorMessage }}
|
|
24
|
-
</div>
|
|
25
|
-
</div>
|
|
26
|
-
<div class="prompt-footer">
|
|
27
|
-
<button class="btn btn-secondary" (click)="cancel()">取消</button>
|
|
28
|
-
<button class="btn btn-primary" (click)="submit()" [disabled]="!password">确认</button>
|
|
29
|
-
</div>
|
|
30
|
-
</div>
|
|
31
|
-
`,
|
|
32
|
-
styles: [`
|
|
33
|
-
.password-prompt {
|
|
34
|
-
background: var(--ai-bg-primary);
|
|
35
|
-
border-radius: 0.5rem;
|
|
36
|
-
max-width: 400px;
|
|
37
|
-
margin: 0 auto;
|
|
38
|
-
}
|
|
39
|
-
.prompt-header {
|
|
40
|
-
padding: 1.5rem;
|
|
41
|
-
text-align: center;
|
|
42
|
-
border-bottom: 1px solid var(--ai-border);
|
|
43
|
-
}
|
|
44
|
-
.prompt-content {
|
|
45
|
-
padding: 1.5rem;
|
|
46
|
-
}
|
|
47
|
-
.error-message {
|
|
48
|
-
margin-top: 0.75rem;
|
|
49
|
-
color: var(--ai-danger);
|
|
50
|
-
font-size: 0.875rem;
|
|
51
|
-
}
|
|
52
|
-
.prompt-footer {
|
|
53
|
-
padding: 1rem 1.5rem;
|
|
54
|
-
display: flex;
|
|
55
|
-
justify-content: flex-end;
|
|
56
|
-
gap: 0.5rem;
|
|
57
|
-
border-top: 1px solid var(--ai-border);
|
|
58
|
-
}
|
|
59
|
-
`]
|
|
6
|
+
templateUrl: './password-prompt.component.html',
|
|
7
|
+
styleUrls: ['./password-prompt.component.scss']
|
|
60
8
|
})
|
|
61
9
|
export class PasswordPromptComponent {
|
|
62
10
|
@Input() title: string = '密码验证';
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
<span>{{ getRiskLevelText() }}</span>
|
|
7
7
|
</div>
|
|
8
8
|
<button type="button" class="close-button" (click)="cancel()">
|
|
9
|
-
<i class="
|
|
9
|
+
<i class="fa fa-times"></i>
|
|
10
10
|
</button>
|
|
11
11
|
</div>
|
|
12
12
|
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
<div class="command-display">
|
|
31
31
|
<code>{{ command }}</code>
|
|
32
32
|
<button class="btn-copy" (click)="navigator.clipboard.writeText(command)" title="复制">
|
|
33
|
-
<i class="
|
|
33
|
+
<i class="fa fa-copy"></i>
|
|
34
34
|
</button>
|
|
35
35
|
</div>
|
|
36
36
|
</div>
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
<label>安全建议:</label>
|
|
46
46
|
<ul class="suggestions-list">
|
|
47
47
|
<li *ngFor="let suggestion of suggestions">
|
|
48
|
-
<i class="
|
|
48
|
+
<i class="fa fa-lightbulb-o"></i>
|
|
49
49
|
<span>{{ suggestion }}</span>
|
|
50
50
|
</li>
|
|
51
51
|
</ul>
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
<!-- 额外警告(高风险) -->
|
|
56
56
|
<div *ngIf="isHighRisk()" class="high-risk-warning">
|
|
57
57
|
<div class="warning-box">
|
|
58
|
-
<i class="
|
|
58
|
+
<i class="fa fa-exclamation-triangle"></i>
|
|
59
59
|
<div class="warning-content">
|
|
60
60
|
<strong>⚠️ 重要提示</strong>
|
|
61
61
|
<p *ngIf="riskLevel === 'critical'">
|
|
@@ -72,16 +72,12 @@
|
|
|
72
72
|
<!-- 底部按钮 -->
|
|
73
73
|
<div class="dialog-footer">
|
|
74
74
|
<button type="button" class="btn btn-secondary" (click)="cancel()">
|
|
75
|
-
<i class="
|
|
75
|
+
<i class="fa fa-times"></i>
|
|
76
76
|
取消
|
|
77
77
|
</button>
|
|
78
|
-
<button
|
|
79
|
-
|
|
80
|
-
class="btn btn-danger"
|
|
81
|
-
[ngClass]="{ 'btn-critical': isHighRisk() }"
|
|
82
|
-
(click)="confirm()">
|
|
83
|
-
<i [class]="isHighRisk() ? 'icon-alert-triangle' : 'icon-check'"></i>
|
|
78
|
+
<button type="button" class="btn btn-danger" [ngClass]="{ 'btn-critical': isHighRisk() }" (click)="confirm()">
|
|
79
|
+
<i [class]="isHighRisk() ? 'fa fa-exclamation-triangle' : 'fa fa-check'"></i>
|
|
84
80
|
{{ isHighRisk() ? '仍要执行' : '确认执行' }}
|
|
85
81
|
</button>
|
|
86
82
|
</div>
|
|
87
|
-
</div>
|
|
83
|
+
</div>
|
|
@@ -272,7 +272,7 @@
|
|
|
272
272
|
color: white;
|
|
273
273
|
|
|
274
274
|
&:hover {
|
|
275
|
-
background-color:
|
|
275
|
+
background-color: #5a6268;
|
|
276
276
|
}
|
|
277
277
|
}
|
|
278
278
|
|
|
@@ -281,7 +281,7 @@
|
|
|
281
281
|
color: white;
|
|
282
282
|
|
|
283
283
|
&:hover {
|
|
284
|
-
background-color:
|
|
284
|
+
background-color: #c82333;
|
|
285
285
|
}
|
|
286
286
|
}
|
|
287
287
|
|
|
@@ -292,7 +292,7 @@
|
|
|
292
292
|
|
|
293
293
|
&:hover {
|
|
294
294
|
animation: none;
|
|
295
|
-
background-color:
|
|
295
|
+
background-color: #bd2130;
|
|
296
296
|
}
|
|
297
297
|
}
|
|
298
298
|
}
|
|
@@ -300,9 +300,12 @@
|
|
|
300
300
|
|
|
301
301
|
/* 动画 */
|
|
302
302
|
@keyframes pulse {
|
|
303
|
-
|
|
303
|
+
|
|
304
|
+
0%,
|
|
305
|
+
100% {
|
|
304
306
|
box-shadow: 0 0 0 0 rgba(220, 53, 69, 0.7);
|
|
305
307
|
}
|
|
308
|
+
|
|
306
309
|
50% {
|
|
307
310
|
box-shadow: 0 0 0 10px rgba(220, 53, 69, 0);
|
|
308
311
|
}
|
|
@@ -357,4 +360,4 @@
|
|
|
357
360
|
justify-content: center;
|
|
358
361
|
}
|
|
359
362
|
}
|
|
360
|
-
}
|
|
363
|
+
}
|
|
@@ -15,7 +15,7 @@ export class RiskConfirmDialogComponent {
|
|
|
15
15
|
|
|
16
16
|
@Output() confirmed = new EventEmitter<boolean>();
|
|
17
17
|
|
|
18
|
-
constructor(public activeModal: NgbActiveModal) {}
|
|
18
|
+
constructor(public activeModal: NgbActiveModal) { }
|
|
19
19
|
|
|
20
20
|
/**
|
|
21
21
|
* 确认执行
|
|
@@ -75,15 +75,15 @@ export class RiskConfirmDialogComponent {
|
|
|
75
75
|
getRiskLevelIcon(): string {
|
|
76
76
|
switch (this.riskLevel) {
|
|
77
77
|
case RiskLevel.LOW:
|
|
78
|
-
return '
|
|
78
|
+
return 'fa fa-check-circle';
|
|
79
79
|
case RiskLevel.MEDIUM:
|
|
80
|
-
return '
|
|
80
|
+
return 'fa fa-exclamation-triangle';
|
|
81
81
|
case RiskLevel.HIGH:
|
|
82
|
-
return '
|
|
82
|
+
return 'fa fa-exclamation-circle';
|
|
83
83
|
case RiskLevel.CRITICAL:
|
|
84
|
-
return '
|
|
84
|
+
return 'fa fa-ban';
|
|
85
85
|
default:
|
|
86
|
-
return '
|
|
86
|
+
return 'fa fa-question-circle';
|
|
87
87
|
}
|
|
88
88
|
}
|
|
89
89
|
|
|
@@ -3,42 +3,39 @@
|
|
|
3
3
|
<div class="settings-header">
|
|
4
4
|
<div class="header-content">
|
|
5
5
|
<h2>
|
|
6
|
-
<i class="
|
|
6
|
+
<i class="fa fa-robot"></i>
|
|
7
7
|
AI助手设置
|
|
8
8
|
</h2>
|
|
9
9
|
<div class="header-status">
|
|
10
10
|
<div class="status-indicator" [class.enabled]="isEnabled">
|
|
11
|
-
<i class="
|
|
11
|
+
<i class="fa fa-power-off"></i>
|
|
12
12
|
<span>{{ isEnabled ? '已启用' : '已禁用' }}</span>
|
|
13
13
|
</div>
|
|
14
14
|
<div class="provider-info" *ngIf="isEnabled">
|
|
15
|
-
<i class="
|
|
15
|
+
<i class="fa fa-cloud"></i>
|
|
16
16
|
<span>当前提供商: {{ currentProvider }}</span>
|
|
17
17
|
</div>
|
|
18
18
|
</div>
|
|
19
19
|
</div>
|
|
20
20
|
<div class="header-actions">
|
|
21
21
|
<button class="btn-icon" (click)="toggleEnabled()" [title]="isEnabled ? '禁用AI助手' : '启用AI助手'">
|
|
22
|
-
<i [class]="isEnabled ? '
|
|
22
|
+
<i [class]="isEnabled ? 'fa fa-pause' : 'fa fa-play'"></i>
|
|
23
23
|
</button>
|
|
24
24
|
<button class="btn-icon" (click)="refreshProviderStatus()" title="刷新状态">
|
|
25
|
-
<i class="
|
|
25
|
+
<i class="fa fa-refresh"></i>
|
|
26
26
|
</button>
|
|
27
27
|
<button class="btn-icon" (click)="exportConfig()" title="导出配置">
|
|
28
|
-
<i class="
|
|
28
|
+
<i class="fa fa-download"></i>
|
|
29
29
|
</button>
|
|
30
30
|
<button class="btn-icon" (click)="importConfig()" title="导入配置">
|
|
31
|
-
<i class="
|
|
31
|
+
<i class="fa fa-upload"></i>
|
|
32
32
|
</button>
|
|
33
33
|
</div>
|
|
34
34
|
</div>
|
|
35
35
|
|
|
36
36
|
<!-- 标签页导航 -->
|
|
37
37
|
<div class="tabs-nav" *ngIf="isEnabled">
|
|
38
|
-
<button
|
|
39
|
-
*ngFor="let tab of tabs"
|
|
40
|
-
class="tab-button"
|
|
41
|
-
[class.active]="activeTab === tab.id"
|
|
38
|
+
<button *ngFor="let tab of tabs" class="tab-button" [class.active]="activeTab === tab.id"
|
|
42
39
|
(click)="switchTab(tab.id)">
|
|
43
40
|
<i [class]="tab.icon"></i>
|
|
44
41
|
<span>{{ tab.label }}</span>
|
|
@@ -54,9 +51,7 @@
|
|
|
54
51
|
|
|
55
52
|
<!-- AI提供商设置 -->
|
|
56
53
|
<div *ngIf="activeTab === 'providers'" class="tab-content">
|
|
57
|
-
<app-provider-config
|
|
58
|
-
[providerStatus]="providerStatus"
|
|
59
|
-
(refreshStatus)="refreshProviderStatus()"
|
|
54
|
+
<app-provider-config [providerStatus]="providerStatus" (refreshStatus)="refreshProviderStatus()"
|
|
60
55
|
(switchProvider)="switchProvider($event)">
|
|
61
56
|
</app-provider-config>
|
|
62
57
|
</div>
|
|
@@ -80,11 +75,11 @@
|
|
|
80
75
|
<div class="settings-section">
|
|
81
76
|
<h4>配置管理</h4>
|
|
82
77
|
<button class="btn btn-primary" (click)="validateConfig()">
|
|
83
|
-
<i class="
|
|
78
|
+
<i class="fa fa-check"></i>
|
|
84
79
|
验证配置
|
|
85
80
|
</button>
|
|
86
81
|
<button class="btn btn-warning" (click)="resetToDefaults()">
|
|
87
|
-
<i class="
|
|
82
|
+
<i class="fa fa-refresh"></i>
|
|
88
83
|
重置为默认
|
|
89
84
|
</button>
|
|
90
85
|
</div>
|
|
@@ -94,7 +89,8 @@
|
|
|
94
89
|
<h4>日志设置</h4>
|
|
95
90
|
<div class="log-level-selector">
|
|
96
91
|
<label>日志级别</label>
|
|
97
|
-
<select class="form-control" [(ngModel)]="config.logLevel"
|
|
92
|
+
<select class="form-control" [(ngModel)]="config.logLevel"
|
|
93
|
+
(change)="config.setLogLevel(config.logLevel)">
|
|
98
94
|
<option value="debug">Debug (详细)</option>
|
|
99
95
|
<option value="info">Info (信息)</option>
|
|
100
96
|
<option value="warn">Warn (警告)</option>
|
|
@@ -128,13 +124,13 @@
|
|
|
128
124
|
<!-- 禁用提示 -->
|
|
129
125
|
<div class="disabled-message" *ngIf="!isEnabled">
|
|
130
126
|
<div class="message-content">
|
|
131
|
-
<i class="
|
|
127
|
+
<i class="fa fa-pause-circle fa-3x"></i>
|
|
132
128
|
<h3>AI助手已禁用</h3>
|
|
133
129
|
<p>点击上方按钮启用AI助手功能</p>
|
|
134
130
|
<button class="btn btn-primary" (click)="toggleEnabled()">
|
|
135
|
-
<i class="
|
|
131
|
+
<i class="fa fa-play"></i>
|
|
136
132
|
启用AI助手
|
|
137
133
|
</button>
|
|
138
134
|
</div>
|
|
139
135
|
</div>
|
|
140
|
-
</div>
|
|
136
|
+
</div>
|
|
@@ -194,7 +194,7 @@
|
|
|
194
194
|
color: white;
|
|
195
195
|
|
|
196
196
|
&:hover {
|
|
197
|
-
background-color:
|
|
197
|
+
background-color: #0069d9;
|
|
198
198
|
}
|
|
199
199
|
}
|
|
200
200
|
|
|
@@ -203,7 +203,7 @@
|
|
|
203
203
|
color: var(--ai-dark);
|
|
204
204
|
|
|
205
205
|
&:hover {
|
|
206
|
-
background-color:
|
|
206
|
+
background-color: #e0a800;
|
|
207
207
|
}
|
|
208
208
|
}
|
|
209
209
|
}
|
|
@@ -301,7 +301,7 @@
|
|
|
301
301
|
font-size: 1rem;
|
|
302
302
|
|
|
303
303
|
&:hover {
|
|
304
|
-
background-color:
|
|
304
|
+
background-color: #0069d9;
|
|
305
305
|
transform: translateY(-1px);
|
|
306
306
|
}
|
|
307
307
|
}
|
|
@@ -310,9 +310,12 @@
|
|
|
310
310
|
|
|
311
311
|
/* 动画 */
|
|
312
312
|
@keyframes pulse {
|
|
313
|
-
|
|
313
|
+
|
|
314
|
+
0%,
|
|
315
|
+
100% {
|
|
314
316
|
opacity: 1;
|
|
315
317
|
}
|
|
318
|
+
|
|
316
319
|
50% {
|
|
317
320
|
opacity: 0.5;
|
|
318
321
|
}
|
|
@@ -368,4 +371,4 @@
|
|
|
368
371
|
}
|
|
369
372
|
}
|
|
370
373
|
}
|
|
371
|
-
}
|
|
374
|
+
}
|