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,168 @@
1
+ import { Component, OnInit } from '@angular/core';
2
+ import { ConfigProviderService } from '../../services/core/config-provider.service';
3
+ import { LoggerService } from '../../services/core/logger.service';
4
+
5
+ @Component({
6
+ selector: 'app-chat-settings',
7
+ templateUrl: './chat-settings.component.html',
8
+ styleUrls: ['./chat-settings.component.scss']
9
+ })
10
+ export class ChatSettingsComponent implements OnInit {
11
+ settings = {
12
+ chatHistoryEnabled: true,
13
+ maxChatHistory: 100,
14
+ autoSaveChat: true,
15
+ theme: 'auto',
16
+ fontSize: 14,
17
+ compactMode: false,
18
+ showTimestamps: true,
19
+ showAvatars: true,
20
+ enterToSend: true,
21
+ soundEnabled: true
22
+ };
23
+
24
+ themes = [
25
+ { value: 'auto', label: '跟随系统' },
26
+ { value: 'light', label: '浅色主题' },
27
+ { value: 'dark', label: '深色主题' }
28
+ ];
29
+
30
+ fontSizes = [12, 14, 16, 18, 20];
31
+
32
+ constructor(
33
+ private config: ConfigProviderService,
34
+ private logger: LoggerService
35
+ ) {}
36
+
37
+ ngOnInit(): void {
38
+ this.loadSettings();
39
+ }
40
+
41
+ /**
42
+ * 加载设置
43
+ */
44
+ private loadSettings(): void {
45
+ this.settings.chatHistoryEnabled = this.config.get('chatHistoryEnabled', true);
46
+ this.settings.maxChatHistory = this.config.get('maxChatHistory', 100);
47
+ this.settings.autoSaveChat = this.config.get('autoSaveChat', true);
48
+ this.settings.theme = this.config.get('theme', 'auto');
49
+ this.settings.fontSize = this.config.get('ui.fontSize', 14);
50
+ this.settings.compactMode = this.config.get('ui.compactMode', false);
51
+ this.settings.showTimestamps = this.config.get('ui.showTimestamps', true);
52
+ this.settings.showAvatars = this.config.get('ui.showAvatars', true);
53
+ this.settings.enterToSend = this.config.get('ui.enterToSend', true);
54
+ this.settings.soundEnabled = this.config.get('ui.soundEnabled', true);
55
+ }
56
+
57
+ /**
58
+ * 保存设置
59
+ */
60
+ saveSetting(key: string, value: any): void {
61
+ try {
62
+ this.config.set(key, value);
63
+ this.logger.debug('Chat setting saved', { key, value });
64
+ } catch (error) {
65
+ this.logger.error('Failed to save chat setting', error);
66
+ }
67
+ }
68
+
69
+ /**
70
+ * 更新主题
71
+ */
72
+ updateTheme(theme: string): void {
73
+ this.settings.theme = theme;
74
+ this.saveSetting('theme', theme);
75
+ this.applyTheme(theme);
76
+ }
77
+
78
+ /**
79
+ * 应用主题
80
+ */
81
+ private applyTheme(theme: string): void {
82
+ const body = document.body;
83
+ body.classList.remove('light-theme', 'dark-theme');
84
+
85
+ if (theme === 'light') {
86
+ body.classList.add('light-theme');
87
+ } else if (theme === 'dark') {
88
+ body.classList.add('dark-theme');
89
+ }
90
+ // 'auto' 主题由系统决定
91
+ }
92
+
93
+ /**
94
+ * 更新字体大小
95
+ */
96
+ updateFontSize(size: number): void {
97
+ this.settings.fontSize = size;
98
+ this.saveSetting('ui.fontSize', size);
99
+ document.documentElement.style.setProperty('--chat-font-size', `${size}px`);
100
+ }
101
+
102
+ /**
103
+ * 切换紧凑模式
104
+ */
105
+ toggleCompactMode(): void {
106
+ this.settings.compactMode = !this.settings.compactMode;
107
+ this.saveSetting('ui.compactMode', this.settings.compactMode);
108
+ document.documentElement.classList.toggle('compact-chat', this.settings.compactMode);
109
+ }
110
+
111
+ /**
112
+ * 清空聊天历史
113
+ */
114
+ clearChatHistory(): void {
115
+ if (confirm('确定要清空所有聊天记录吗?此操作不可恢复。')) {
116
+ localStorage.removeItem('ai-assistant-chat-history');
117
+ this.logger.info('Chat history cleared');
118
+ alert('聊天记录已清空');
119
+ }
120
+ }
121
+
122
+ /**
123
+ * 导出聊天设置
124
+ */
125
+ exportSettings(): void {
126
+ const exportData = {
127
+ chatSettings: this.settings,
128
+ exportTime: new Date().toISOString()
129
+ };
130
+
131
+ const blob = new Blob([JSON.stringify(exportData, null, 2)], { type: 'application/json' });
132
+ const url = window.URL.createObjectURL(blob);
133
+ const a = document.createElement('a');
134
+ a.href = url;
135
+ a.download = `chat-settings-${new Date().toISOString().slice(0, 10)}.json`;
136
+ a.click();
137
+ window.URL.revokeObjectURL(url);
138
+ }
139
+
140
+ /**
141
+ * 重置为默认设置
142
+ */
143
+ resetToDefaults(): void {
144
+ if (confirm('确定要重置所有设置为默认值吗?')) {
145
+ this.settings = {
146
+ chatHistoryEnabled: true,
147
+ maxChatHistory: 100,
148
+ autoSaveChat: true,
149
+ theme: 'auto',
150
+ fontSize: 14,
151
+ compactMode: false,
152
+ showTimestamps: true,
153
+ showAvatars: true,
154
+ enterToSend: true,
155
+ soundEnabled: true
156
+ };
157
+
158
+ // 保存所有设置
159
+ Object.entries(this.settings).forEach(([key, value]) => {
160
+ const configKey = key.includes('.') ? key : `ui.${key}`;
161
+ this.saveSetting(configKey, value);
162
+ });
163
+
164
+ this.logger.info('Chat settings reset to defaults');
165
+ alert('设置已重置为默认值');
166
+ }
167
+ }
168
+ }
@@ -0,0 +1,124 @@
1
+ import { Component, Input, Output, EventEmitter } from '@angular/core';
2
+
3
+ @Component({
4
+ selector: 'app-error-message',
5
+ template: `
6
+ <div class="error-message" [ngClass]="type">
7
+ <div class="error-icon">
8
+ <i [class]="getIconClass()"></i>
9
+ </div>
10
+ <div class="error-content">
11
+ <h4 *ngIf="title">{{ title }}</h4>
12
+ <p>{{ message }}</p>
13
+ <div *ngIf="details" class="error-details">
14
+ <small>{{ details }}</small>
15
+ </div>
16
+ </div>
17
+ <button *ngIf="dismissible" class="error-close" (click)="onDismiss()">
18
+ <i class="icon-close"></i>
19
+ </button>
20
+ </div>
21
+ `,
22
+ styles: [`
23
+ .error-message {
24
+ display: flex;
25
+ align-items: flex-start;
26
+ gap: 0.75rem;
27
+ padding: 1rem;
28
+ border-radius: 0.5rem;
29
+ border: 1px solid;
30
+ margin: 0.5rem 0;
31
+
32
+ &.error {
33
+ background-color: rgba(220, 53, 69, 0.1);
34
+ border-color: var(--ai-danger);
35
+ color: var(--ai-danger);
36
+ }
37
+
38
+ &.warning {
39
+ background-color: rgba(255, 193, 7, 0.1);
40
+ border-color: var(--ai-warning);
41
+ color: #856404;
42
+ }
43
+
44
+ &.info {
45
+ background-color: rgba(23, 162, 184, 0.1);
46
+ border-color: var(--ai-info);
47
+ color: var(--ai-info);
48
+ }
49
+
50
+ &.success {
51
+ background-color: rgba(40, 167, 69, 0.1);
52
+ border-color: var(--ai-success);
53
+ color: var(--ai-success);
54
+ }
55
+ }
56
+
57
+ .error-icon {
58
+ flex-shrink: 0;
59
+ font-size: 1.25rem;
60
+ }
61
+
62
+ .error-content {
63
+ flex: 1;
64
+
65
+ h4 {
66
+ margin: 0 0 0.5rem 0;
67
+ font-size: 1rem;
68
+ font-weight: 600;
69
+ }
70
+
71
+ p {
72
+ margin: 0;
73
+ line-height: 1.5;
74
+ }
75
+
76
+ .error-details {
77
+ margin-top: 0.5rem;
78
+ opacity: 0.8;
79
+ }
80
+ }
81
+
82
+ .error-close {
83
+ flex-shrink: 0;
84
+ width: 24px;
85
+ height: 24px;
86
+ border: none;
87
+ background: transparent;
88
+ border-radius: 0.25rem;
89
+ display: flex;
90
+ align-items: center;
91
+ justify-content: center;
92
+ cursor: pointer;
93
+ opacity: 0.6;
94
+ transition: opacity 0.2s;
95
+
96
+ &:hover {
97
+ opacity: 1;
98
+ }
99
+ }
100
+ `]
101
+ })
102
+ export class ErrorMessageComponent {
103
+ @Input() type: 'error' | 'warning' | 'info' | 'success' = 'error';
104
+ @Input() title: string = '';
105
+ @Input() message: string = '';
106
+ @Input() details: string = '';
107
+ @Input() dismissible: boolean = false;
108
+
109
+ @Output() dismissed = new EventEmitter<void>();
110
+
111
+ getIconClass(): string {
112
+ const icons: { [key: string]: string } = {
113
+ 'error': 'icon-error',
114
+ 'warning': 'icon-warning',
115
+ 'info': 'icon-info',
116
+ 'success': 'icon-success'
117
+ };
118
+ return icons[this.type] || 'icon-error';
119
+ }
120
+
121
+ onDismiss(): void {
122
+ this.dismissed.emit();
123
+ }
124
+ }
@@ -0,0 +1,72 @@
1
+ import { Component, Input } from '@angular/core';
2
+
3
+ @Component({
4
+ selector: 'app-loading-spinner',
5
+ template: `
6
+ <div class="spinner-container" [ngClass]="size">
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
+ `]
68
+ })
69
+ export class LoadingSpinnerComponent {
70
+ @Input() size: 'small' | 'medium' | 'large' = 'medium';
71
+ @Input() message: string = '';
72
+ }
@@ -0,0 +1,77 @@
1
+ import { Component, Input } from '@angular/core';
2
+ import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
3
+ import { RiskLevel } from '../../types/security.types';
4
+
5
+ @Component({
6
+ selector: 'app-consent-dialog',
7
+ template: `
8
+ <div class="consent-dialog">
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
+ `]
62
+ })
63
+ export class ConsentDialogComponent {
64
+ @Input() command: string = '';
65
+ @Input() riskLevel: RiskLevel = RiskLevel.MEDIUM;
66
+ rememberChoice = false;
67
+
68
+ constructor(public activeModal: NgbActiveModal) {}
69
+
70
+ confirm(): void {
71
+ this.activeModal.close({ confirmed: true, remember: this.rememberChoice });
72
+ }
73
+
74
+ cancel(): void {
75
+ this.activeModal.dismiss('cancel');
76
+ }
77
+ }
@@ -0,0 +1,79 @@
1
+ import { Component, Input } from '@angular/core';
2
+ import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
3
+
4
+ @Component({
5
+ selector: 'app-password-prompt',
6
+ template: `
7
+ <div class="password-prompt">
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
+ `]
60
+ })
61
+ export class PasswordPromptComponent {
62
+ @Input() title: string = '密码验证';
63
+ password = '';
64
+ errorMessage = '';
65
+
66
+ constructor(public activeModal: NgbActiveModal) {}
67
+
68
+ submit(): void {
69
+ if (this.password) {
70
+ this.activeModal.close(this.password);
71
+ } else {
72
+ this.errorMessage = '请输入密码';
73
+ }
74
+ }
75
+
76
+ cancel(): void {
77
+ this.activeModal.dismiss('cancel');
78
+ }
79
+ }
@@ -0,0 +1,87 @@
1
+ <div class="risk-confirm-dialog">
2
+ <!-- 头部 -->
3
+ <div class="dialog-header">
4
+ <div class="risk-indicator" [style.color]="getRiskLevelColor()">
5
+ <i [class]="getRiskLevelIcon()"></i>
6
+ <span>{{ getRiskLevelText() }}</span>
7
+ </div>
8
+ <button type="button" class="close-button" (click)="cancel()">
9
+ <i class="icon-x"></i>
10
+ </button>
11
+ </div>
12
+
13
+ <!-- 内容 -->
14
+ <div class="dialog-content">
15
+ <!-- 风险提示 -->
16
+ <div class="risk-warning" [ngClass]="isHighRisk() ? 'high-risk' : 'medium-risk'">
17
+ <div class="warning-icon">
18
+ <i [class]="getRiskLevelIcon()"></i>
19
+ </div>
20
+ <div class="warning-text">
21
+ <h4>安全警告</h4>
22
+ <p>此命令被识别为{{ getRiskLevelText() }},请仔细确认后再执行。</p>
23
+ </div>
24
+ </div>
25
+
26
+ <!-- 命令信息 -->
27
+ <div class="command-info">
28
+ <div class="info-section">
29
+ <label>将要执行的命令:</label>
30
+ <div class="command-display">
31
+ <code>{{ command }}</code>
32
+ <button class="btn-copy" (click)="navigator.clipboard.writeText(command)" title="复制">
33
+ <i class="icon-copy"></i>
34
+ </button>
35
+ </div>
36
+ </div>
37
+
38
+ <div class="info-section">
39
+ <label>命令解释:</label>
40
+ <p class="explanation">{{ explanation }}</p>
41
+ </div>
42
+
43
+ <!-- 安全建议 -->
44
+ <div class="info-section" *ngIf="suggestions.length > 0">
45
+ <label>安全建议:</label>
46
+ <ul class="suggestions-list">
47
+ <li *ngFor="let suggestion of suggestions">
48
+ <i class="icon-lightbulb"></i>
49
+ <span>{{ suggestion }}</span>
50
+ </li>
51
+ </ul>
52
+ </div>
53
+ </div>
54
+
55
+ <!-- 额外警告(高风险) -->
56
+ <div *ngIf="isHighRisk()" class="high-risk-warning">
57
+ <div class="warning-box">
58
+ <i class="icon-alert-triangle"></i>
59
+ <div class="warning-content">
60
+ <strong>⚠️ 重要提示</strong>
61
+ <p *ngIf="riskLevel === 'critical'">
62
+ 此命令可能导致系统损坏或数据丢失,请在执行前备份重要数据!
63
+ </p>
64
+ <p *ngIf="riskLevel === 'high'">
65
+ 此命令将修改系统设置或删除文件,请确保您了解命令的作用。
66
+ </p>
67
+ </div>
68
+ </div>
69
+ </div>
70
+ </div>
71
+
72
+ <!-- 底部按钮 -->
73
+ <div class="dialog-footer">
74
+ <button type="button" class="btn btn-secondary" (click)="cancel()">
75
+ <i class="icon-x"></i>
76
+ 取消
77
+ </button>
78
+ <button
79
+ type="button"
80
+ class="btn btn-danger"
81
+ [ngClass]="{ 'btn-critical': isHighRisk() }"
82
+ (click)="confirm()">
83
+ <i [class]="isHighRisk() ? 'icon-alert-triangle' : 'icon-check'"></i>
84
+ {{ isHighRisk() ? '仍要执行' : '确认执行' }}
85
+ </button>
86
+ </div>
87
+ </div>