qqsl-agent 0.0.1

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 (70) hide show
  1. package/README.md +24 -0
  2. package/assets/images/card/copy.png +0 -0
  3. package/assets/images/card/refresh.png +0 -0
  4. package/assets/images/card/share.png +0 -0
  5. package/assets/images/card/think-finish.png +0 -0
  6. package/assets/images/input/relate.png +0 -0
  7. package/assets/images/input/send-gray.png +0 -0
  8. package/assets/images/input/send.png +0 -0
  9. package/assets/images/input/toggle-model.png +0 -0
  10. package/assets/images/logo.png +0 -0
  11. package/assets/images/sidebar/collapse.png +0 -0
  12. package/assets/images/sidebar/delete.png +0 -0
  13. package/assets/images/sidebar/expand.png +0 -0
  14. package/assets/images/sidebar/history-chat.png +0 -0
  15. package/assets/images/sidebar/new-chat.png +0 -0
  16. package/assets/images/sidebar/time.png +0 -0
  17. package/assets/styles/index.css +1 -0
  18. package/chat/chat-input/chat-input.component.d.ts +46 -0
  19. package/chat/chat-messages/chat-messages.component.d.ts +33 -0
  20. package/chat/chat-sidebar/chat-sidebar.component.d.ts +20 -0
  21. package/chat/chat-sidebar/history-group/history-group.component.d.ts +18 -0
  22. package/chat/chat.component.d.ts +34 -0
  23. package/chat/chat.module.d.ts +28 -0
  24. package/chat/components/pagination/pagination.component.d.ts +14 -0
  25. package/chat/index.d.ts +5 -0
  26. package/chat/models/conversation.model.d.ts +20 -0
  27. package/chat/models/knowledge-list.model.d.ts +8 -0
  28. package/chat/models/message.model.d.ts +41 -0
  29. package/chat/models/model-list.model.d.ts +9 -0
  30. package/chat/models/send-message.model.d.ts +33 -0
  31. package/chat/pipes/markdown.pipe.d.ts +8 -0
  32. package/chat/public-api.d.ts +2 -0
  33. package/chat/services/http.service.d.ts +13 -0
  34. package/chat/services/http2.service.d.ts +45 -0
  35. package/chat/services/markdown-stream.service.d.ts +10 -0
  36. package/chat/services/sse.service.d.ts +28 -0
  37. package/chat/store/store.service.d.ts +17 -0
  38. package/esm2020/chat/chat-input/chat-input.component.mjs +151 -0
  39. package/esm2020/chat/chat-messages/chat-messages.component.mjs +110 -0
  40. package/esm2020/chat/chat-sidebar/chat-sidebar.component.mjs +118 -0
  41. package/esm2020/chat/chat-sidebar/history-group/history-group.component.mjs +124 -0
  42. package/esm2020/chat/chat.component.mjs +138 -0
  43. package/esm2020/chat/chat.module.mjs +98 -0
  44. package/esm2020/chat/components/pagination/pagination.component.mjs +91 -0
  45. package/esm2020/chat/models/conversation.model.mjs +2 -0
  46. package/esm2020/chat/models/knowledge-list.model.mjs +2 -0
  47. package/esm2020/chat/models/message.model.mjs +35 -0
  48. package/esm2020/chat/models/model-list.model.mjs +2 -0
  49. package/esm2020/chat/models/send-message.model.mjs +2 -0
  50. package/esm2020/chat/pipes/markdown.pipe.mjs +24 -0
  51. package/esm2020/chat/public-api.mjs +3 -0
  52. package/esm2020/chat/qqsl-agent-chat.mjs +5 -0
  53. package/esm2020/chat/services/http.service.mjs +68 -0
  54. package/esm2020/chat/services/http2.service.mjs +101 -0
  55. package/esm2020/chat/services/markdown-stream.service.mjs +137 -0
  56. package/esm2020/chat/services/sse.service.mjs +107 -0
  57. package/esm2020/chat/store/store.service.mjs +38 -0
  58. package/esm2020/public-api.mjs +5 -0
  59. package/esm2020/qqsl-agent.mjs +5 -0
  60. package/fesm2015/qqsl-agent-chat.mjs +1255 -0
  61. package/fesm2015/qqsl-agent-chat.mjs.map +1 -0
  62. package/fesm2015/qqsl-agent.mjs +9 -0
  63. package/fesm2015/qqsl-agent.mjs.map +1 -0
  64. package/fesm2020/qqsl-agent-chat.mjs +1248 -0
  65. package/fesm2020/qqsl-agent-chat.mjs.map +1 -0
  66. package/fesm2020/qqsl-agent.mjs +9 -0
  67. package/fesm2020/qqsl-agent.mjs.map +1 -0
  68. package/index.d.ts +5 -0
  69. package/package.json +45 -0
  70. package/public-api.d.ts +2 -0
@@ -0,0 +1,1255 @@
1
+ import * as i0 from '@angular/core';
2
+ import { Injectable, inject, EventEmitter, Component, ViewChild, Input, Output, Pipe, NgZone, NgModule } from '@angular/core';
3
+ import * as i1$1 from '@angular/common';
4
+ import { CommonModule } from '@angular/common';
5
+ import { Marked } from 'marked';
6
+ import { markedHighlight } from 'marked-highlight';
7
+ import hljs from 'highlight.js';
8
+ import DOMPurify from 'dompurify';
9
+ import * as i1 from '@angular/common/http';
10
+ import { HttpHeaders, HttpResponse, HttpClient, HttpParams } from '@angular/common/http';
11
+ import { BehaviorSubject, filter, map } from 'rxjs';
12
+ import { __awaiter } from 'tslib';
13
+ import { DomSanitizer } from '@angular/platform-browser';
14
+ import * as i2 from 'ng-zorro-antd/core/transition-patch';
15
+ import * as i3 from 'ng-zorro-antd/popover';
16
+ import { NzPopoverModule } from 'ng-zorro-antd/popover';
17
+ import * as i4 from 'ng-zorro-antd/radio';
18
+ import { NzRadioModule } from 'ng-zorro-antd/radio';
19
+ import * as i5 from 'ng-zorro-antd/checkbox';
20
+ import { NzCheckboxModule } from 'ng-zorro-antd/checkbox';
21
+ import * as i6 from 'ng-zorro-antd/icon';
22
+ import { NzIconModule } from 'ng-zorro-antd/icon';
23
+ import * as i7 from '@angular/forms';
24
+ import { FormsModule } from '@angular/forms';
25
+ import { NzMessageService } from 'ng-zorro-antd/message';
26
+ import { trigger, state, style, transition, animate } from '@angular/animations';
27
+ import { NzModalService } from 'ng-zorro-antd/modal';
28
+ import { NzAvatarModule } from 'ng-zorro-antd/avatar';
29
+ import { NzSpinModule } from 'ng-zorro-antd/spin';
30
+ import { NzTagModule } from 'ng-zorro-antd/tag';
31
+ import { NzInputModule } from 'ng-zorro-antd/input';
32
+ import { NzButtonModule } from 'ng-zorro-antd/button';
33
+ import { NzListModule } from 'ng-zorro-antd/list';
34
+ import { NzSliderModule } from 'ng-zorro-antd/slider';
35
+ import { NzLayoutModule } from 'ng-zorro-antd/layout';
36
+ import { NzPaginationModule } from 'ng-zorro-antd/pagination';
37
+
38
+ // markdown-stream.service.ts
39
+ class MarkdownStreamService {
40
+ constructor() {
41
+ this.marked = new Marked(
42
+ // 高亮代码
43
+ markedHighlight({
44
+ emptyLangClass: 'hljs',
45
+ langPrefix: 'hljs language-',
46
+ highlight(code, lang, info) {
47
+ const language = hljs.getLanguage(lang) ? lang : 'plaintext';
48
+ return hljs.highlight(code, { language }).value;
49
+ },
50
+ }));
51
+ // 支持 $$...$$ 和 $...$ 数学公式
52
+ // this.marked.use({
53
+ // // 包含从 markdown 创建标记的函数的对象
54
+ // tokenizer: {
55
+ // inlineMath(src: string) {
56
+ // const cap = /^\$([^\$]+)\$/.exec(src);
57
+ // if (cap) {
58
+ // return { type: 'inlineMath', raw: cap[0], text: cap[1] };
59
+ // }
60
+ // },
61
+ // codespan(src: string) {
62
+ // const math = this.inlineMath(src);
63
+ // if (math) return math;
64
+ // return false;
65
+ // }
66
+ // }
67
+ // });
68
+ // marked 17+ 必须使用 extensions 方式注册
69
+ this.marked.use({
70
+ extensions: [
71
+ {
72
+ name: 'inlineMath',
73
+ level: 'inline',
74
+ start(src) {
75
+ return src.indexOf('$'); // 告诉 marked 从哪里开始尝试匹配
76
+ },
77
+ tokenizer(src) {
78
+ // 匹配 $...$,但不能包含换行,且不允许 $$ 开头(留给块级公式)
79
+ const match = src.match(/^\$(?!\$)([^\n$]+?)\$/);
80
+ if (match) {
81
+ return {
82
+ type: 'inlineMath',
83
+ raw: match[0],
84
+ text: match[1].trim(), // 公式内容
85
+ };
86
+ }
87
+ return undefined;
88
+ },
89
+ renderer(token) {
90
+ // 这里直接返回原始文本,后面你可以用 KaTeX / MathJax 渲染
91
+ return `$${token.text}$`;
92
+ // 或者如果你已经全局接管了渲染,可以 return false
93
+ },
94
+ },
95
+ // 可选:同时支持 $$...$$ 块级公式
96
+ {
97
+ name: 'displayMath',
98
+ level: 'block',
99
+ start(src) {
100
+ return src.indexOf('$$');
101
+ },
102
+ tokenizer(src) {
103
+ const match = src.match(/^\$\$([\s\S]+?)\$\$/);
104
+ if (match) {
105
+ return {
106
+ type: 'displayMath',
107
+ raw: match[0],
108
+ text: match[1].trim(),
109
+ };
110
+ }
111
+ return undefined;
112
+ },
113
+ renderer(token) {
114
+ return `$$${token.text}$$`;
115
+ },
116
+ },
117
+ ],
118
+ });
119
+ }
120
+ // 关键:增量渲染(流式专用!)
121
+ renderIncremental(markdown) {
122
+ // 防止渲染一半的代码块崩溃
123
+ const safeMarkdown = this.completeCodeBlocks(markdown);
124
+ try {
125
+ let html = this.marked.parse(safeMarkdown);
126
+ html = DOMPurify.sanitize(html); // 安全
127
+ return html;
128
+ }
129
+ catch (e) {
130
+ return this.escapeHtml(safeMarkdown);
131
+ }
132
+ }
133
+ // 智能补全未闭合的代码块(流式必备!)
134
+ completeCodeBlocks(text) {
135
+ const lines = text.split('\n');
136
+ let inCode = false;
137
+ let fence = '';
138
+ for (let i = 0; i < lines.length; i++) {
139
+ const line = lines[i];
140
+ if (line.trim().match(/^```/)) {
141
+ if (!inCode) {
142
+ fence = line.trim();
143
+ inCode = true;
144
+ }
145
+ else if (line.trim() === '```' || line.trim().startsWith('```')) {
146
+ inCode = false;
147
+ fence = '';
148
+ }
149
+ }
150
+ }
151
+ if (inCode) {
152
+ text += '\n```';
153
+ }
154
+ return text;
155
+ }
156
+ escapeHtml(text) {
157
+ const div = document.createElement('div');
158
+ div.textContent = text;
159
+ return div.innerHTML;
160
+ }
161
+ }
162
+ MarkdownStreamService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: MarkdownStreamService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
163
+ MarkdownStreamService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: MarkdownStreamService, providedIn: 'root' });
164
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: MarkdownStreamService, decorators: [{
165
+ type: Injectable,
166
+ args: [{ providedIn: 'root' }]
167
+ }], ctorParameters: function () { return []; } });
168
+
169
+ class StoreService {
170
+ constructor() {
171
+ this.token = '';
172
+ this.baseUrl = '';
173
+ this.currentConversationSource = new BehaviorSubject(null);
174
+ this.currentConversation$ = this.currentConversationSource.asObservable();
175
+ }
176
+ setToken(token) {
177
+ this.token = token;
178
+ }
179
+ getToken() {
180
+ return (this.token ||
181
+ 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0aWQiOiI2YjBjZjcwNC1hNmE5LTQxZDQtOWRkYy1kZmZmODlkYTZiNWMiLCJ1c2VyX3V1aWQiOjMxNTAsInVzZXJfbmFtZSI6IjEzMDg5MTI3MzcwIiwicmVnaW9uQ29kZSI6IjYzMDEwMDAwMDAwMCIsImF1dGhvcml0aWVzIjpbIldBVEVSX1NVUFBMWV9VU0VSIiwiTkVUV09SS19QUk9UT0NPTF9VU0VSIiwiUE9SVEFMX01BTkFHRV9BRE1JTiIsIkRJR0lUQUxfSVJSSUdBVElPTl9BRE1JTiIsIkVRVUlQTUVOVF9NT0RFTF9BRE1JTiIsIklOU1BFQ1RJT05fVVNFUiIsIkJJTV9CQU5LIiwiREFUQV9DQURBU1RSRV9BRE1JTiIsIklSUklHQVRJT05fR0FURV9VU0VSIiwiV0FURVJfUVVBTElUWV9VU0VSIiwiSVJSSUdBVElPTl9BRE1JTl9VU0VSIiwiQlVER0VUX01BVEVSX0FETUlOIiwiV0FURVJfTkVUV09SS19BRE1JTiIsIklOU1BFQ1RJT05fQURNSU5fVVNFUiIsIkRJR0lUQUxfSVJSSUdBVElPTl9KRyIsIlJPTEVfVVNFUiIsIkRBVEFfQ0VOVEVSX0lDT05fQURNSU4iLCJXQVJOSU5HX1VTRVIiLCJSRVNPVVJDRV9BRE1JTiIsIldBVEVSX1FVQUxJVFlfQURNSU4iLCJCVURHRVRfTUFURVJfVVNFUiIsIkJVSUxEX0NPTlNUUlVDVElPTl9BRE1JTiIsIkNBTENVTEFUSU9OX0RBVEFfQURNSU4iLCJURVhUVVJFX0FETUlOIiwiREFUQV9DRU5URVJfRklMRV9BRE1JTiIsIlNUQVRJQ19NT0RFTF9BRE1JTiIsIkJJT19QTEFOVF9BRE1JTiIsIkJJTV9ERVNJR04iLCJST0xFX1NVUEVSX0FETUlOIiwiTkVUV09SS19NT0RFTF9VU0VSIiwiV0FURVJfUVVPVEFfQURNSU4iLCJEQVRBX0NFTlRFUl9SSVZFUl9MQUtFIiwiREFNX1VTRVIiLCJTVVBQTFlfRFJBSU5BR0VfQURNSU4iLCJSRVNPVVJDRV9VU0VSIiwiQVJHVU1FTlRBVElPTl9VU0VSIiwiTE9HR0lOR19VU0VSIiwiREFUQV9URVJSQUlOX0FETUlOIiwiQlVER0VUX1FVT1RBX1VTRVIiLCJEQVRBX0NFTlRFUl9SRUdJT05fQURNSU4iLCJURVhUVVJFX1VTRVIiLCJEQVRBX0NFTlRFUl9VU0VSIiwiRVFVSVBNRU5UX01PREVMX1VTRVIiLCJEQVRBX0NFTlRFUl9QQU5PUkFNQSIsIkFSR1VNRU5UQVRJT05fQURNSU4iLCJEQVRBX0NFTlRFUl9XQVRFUl9SRVNPVVJDRSIsIkJJTV9QUk9DRURVUkUiLCJNQU5BR0VNRU5UX0FETUlOX1VTRVIiLCJEUklOR1dBVEVSX1VTRVIiLCJXQVRFUl9TVVBQTFlfQURNSU4iLCJMQU5EX1VTRV9BRE1JTiIsIkRBVEFfQ0VOVEVSX0ZJTEVfVVNFUiIsIkRBVEFfQ0VOVEVSX1dBVEVSX1BST0pFQ1QiLCJIWl9TTEFWRSIsIkRvY3VtZW50X1VTRVIiLCJCVURHRVRfUVVPVEFfQURNSU4iLCJJUlJJR0FUSU9OX1VTRVIiLCJORVRXT1JLX01PREVMX0FETUlOIiwiU1VQUExZX0RSQUlOQUdFX1VTRVIiLCJMT0dHSU5HX0FETUlOIiwiRFJJTkdXQVRFUl9BRE1JTl9VU0VSIiwiTkVUV09SS19VU0VSIiwiTU9ERUxfUEFSQU1fQURNSU4iLCJXQVRFUl9RVU9UQV9VU0VSIiwiU1RBVElDX01PREVMX0RFVkVMT1BFUiIsIkRFUEFSVE1FTlRfQURNSU4iLCJEQVRBX0NFTlRFUl9BTk5FWCIsIk5FVFdPUktfQURNSU4iLCJCSU1fRVFVSVBNRU5UIiwiRElHSVRBTF9JUlJJR0FUSU9OX1VTRVIiLCJNQU5BR0VNRU5UX1VTRVIiLCJCVUlMRF9DT05TVFJVQ1RJT05fVVNFUiIsIkRBVEFfQ0VOVEVSX0FETUlOIiwiUklWRVJfTEFLRV9FVkFMVVRJT05fVVNFUiIsIkJJTV9DT1NUIiwiRFJJTkdXQVRFUl9HQVRFX1VTRVIiLCJEQVRBX0NFTlRFUl9XQVRFUl9XQURJTkciLCJORVRXT1JLX1BST1RPQ09MX0FETUlOIiwiQklNX1VTRVIiLCJEQVRBX0NBREFTVFJFX1VTRVIiLCJTVEFUSUNfTU9ERUxfVVNFUiIsIlJPTEVfQURNSU4iLCJXQVRFUl9TVVBQTFlfR0FURV9VU0VSIiwiUklWRVJfTEFLRV9FVkFMVVRJT05fQURNSU4iLCJCSU1fQURNSU4iLCJCSU9fUExBTlRfVVNFUiJdLCJleHAiOjE3NjQwNjA0NzA1MTQsInJvbGVzIjpbIkhaX1NMQVZFIiwiUk9MRV9BRE1JTiIsIkJJT19QTEFOVF9BRE1JTiIsIlJPTEVfVVNFUiIsIkRBVEFfQ0VOVEVSX1VTRVIiLCJEQVRBX0NFTlRFUl9BRE1JTiIsIkRvY3VtZW50X1VTRVIiLCJEQVRBX0NFTlRFUl9BTk5FWCIsIkJJTV9FUVVJUE1FTlQiLCJMT0dHSU5HX0FETUlOIiwiU1VQUExZX0RSQUlOQUdFX1VTRVIiLCJSSVZFUl9MQUtFX0VWQUxVVElPTl9BRE1JTiIsIkRBVEFfQ0VOVEVSX0lDT05fQURNSU4iLCJXQVRFUl9RVUFMSVRZX1VTRVIiLCJXQVRFUl9RVU9UQV9VU0VSIiwiSVJSSUdBVElPTl9BRE1JTl9VU0VSIiwiQlVER0VUX1FVT1RBX0FETUlOIiwiREFUQV9DRU5URVJfRklMRV9VU0VSIiwiQklNX0RFU0lHTiIsIk1BTkFHRU1FTlRfVVNFUiIsIk5FVFdPUktfUFJPVE9DT0xfQURNSU4iLCJTVEFUSUNfTU9ERUxfREVWRUxPUEVSIiwiV0FSTklOR19VU0VSIiwiQklNX1VTRVIiLCJEQVRBX0NFTlRFUl9SSVZFUl9MQUtFIiwiRFJJTkdXQVRFUl9BRE1JTl9VU0VSIiwiQVJHVU1FTlRBVElPTl9VU0VSIiwiREFUQV9DRU5URVJfV0FURVJfUFJPSkVDVCIsIkRBVEFfQ0FEQVNUUkVfQURNSU4iLCJERVBBUlRNRU5UX0FETUlOIiwiTkVUV09SS19VU0VSIiwiU1RBVElDX01PREVMX0FETUlOIiwiQklNX0JBTksiLCJXQVRFUl9TVVBQTFlfQURNSU4iLCJCSU1fQURNSU4iLCJSRVNPVVJDRV9VU0VSIiwiREFUQV9URVJSQUlOX0FETUlOIiwiREFUQV9DRU5URVJfUkVHSU9OX0FETUlOIiwiV0FURVJfU1VQUExZX1VTRVIiLCJESUdJVEFMX0lSUklHQVRJT05fVVNFUiIsIldBVEVSX1FVQUxJVFlfQURNSU4iLCJORVRXT1JLX0FETUlOIiwiTE9HR0lOR19VU0VSIiwiSU5TUEVDVElPTl9BRE1JTl9VU0VSIiwiSVJSSUdBVElPTl9HQVRFX1VTRVIiLCJORVRXT1JLX1BST1RPQ09MX1VTRVIiLCJMQU5EX1VTRV9BRE1JTiIsIkJVSUxEX0NPTlNUUlVDVElPTl9BRE1JTiIsIkRBVEFfQ0VOVEVSX1BBTk9SQU1BIiwiREFUQV9DRU5URVJfV0FURVJfV0FESU5HIiwiQ0FMQ1VMQVRJT05fREFUQV9BRE1JTiIsIkRBVEFfQ0FEQVNUUkVfVVNFUiIsIkJJT19QTEFOVF9VU0VSIiwiTU9ERUxfUEFSQU1fQURNSU4iLCJTVVBQTFlfRFJBSU5BR0VfQURNSU4iLCJFUVVJUE1FTlRfTU9ERUxfQURNSU4iLCJURVhUVVJFX0FETUlOIiwiQlVER0VUX01BVEVSX1VTRVIiLCJXQVRFUl9RVU9UQV9BRE1JTiIsIkRJR0lUQUxfSVJSSUdBVElPTl9KRyIsIldBVEVSX05FVFdPUktfQURNSU4iLCJCSU1fUFJPQ0VEVVJFIiwiRFJJTkdXQVRFUl9VU0VSIiwiV0FURVJfU1VQUExZX0dBVEVfVVNFUiIsIkJVSUxEX0NPTlNUUlVDVElPTl9VU0VSIiwiRElHSVRBTF9JUlJJR0FUSU9OX0FETUlOIiwiSVJSSUdBVElPTl9VU0VSIiwiUk9MRV9TVVBFUl9BRE1JTiIsIkVRVUlQTUVOVF9NT0RFTF9VU0VSIiwiQklNX0NPU1QiLCJEUklOR1dBVEVSX0dBVEVfVVNFUiIsIklOU1BFQ1RJT05fVVNFUiIsIkFSR1VNRU5UQVRJT05fQURNSU4iLCJQT1JUQUxfTUFOQUdFX0FETUlOIiwiVEVYVFVSRV9VU0VSIiwiTUFOQUdFTUVOVF9BRE1JTl9VU0VSIiwiTkVUV09SS19NT0RFTF9BRE1JTiIsIkJVREdFVF9RVU9UQV9VU0VSIiwiQlVER0VUX01BVEVSX0FETUlOIiwiU1RBVElDX01PREVMX1VTRVIiLCJSSVZFUl9MQUtFX0VWQUxVVElPTl9VU0VSIiwiREFNX1VTRVIiLCJSRVNPVVJDRV9BRE1JTiIsIkRBVEFfQ0VOVEVSX1dBVEVSX1JFU09VUkNFIiwiREFUQV9DRU5URVJfRklMRV9BRE1JTiIsIk5FVFdPUktfTU9ERUxfVVNFUiJdLCJyZWFsX25hbWUiOiLnrqHnkIblkZgiLCJwbGF0Zm9ybUNvZGUiOiI2MzAxMDAwMDAwMDAiLCJwbGF0Zm9ybU5hbWUiOiLopb_lroHluIIiLCJ1c2VyTWFyayI6ZmFsc2UsInBob25lIjoiMTMwODkxMjczNzAiLCJlbnYiOiJyZWxlYXNlIiwidG9wUmVnaW9uQ29kZSI6IjYzMDEwMDAwMDAwMCJ9.Bzlz0LC5rbwItBgw6B-dhSKhXnAATXiaL-rR3EHD4wmJ-BpWWrDakYAjzUCtWL4ExkTqSxXIClCztc412o5pT3b9EN7R6WH5namjpxzwozCVXQyBYvjoCrS5UX1_iUkvVMr6WAqiUUzIanVSrmaRpszxyD8S_fLBge971-Cli44J1FQDBY8QN8TiH3v3c5aDWDBJVYPHqe3_zEAeL3dlERgl8pTLAydx54a2QqCVH2xfX_zSzFXJeK2iamIyI8H4cyP0pQrYl2rjSuR7LsjJSJz_QeSQKZlctG8odrBMV5OoHlWC2isNGqszqyp1POX2HyWb-U4qn8KuKKryjACrow');
182
+ }
183
+ setBaseUrl(url) {
184
+ this.baseUrl = url;
185
+ }
186
+ getBaseUrl() {
187
+ return this.baseUrl;
188
+ }
189
+ setCurrentConversation(conversation) {
190
+ this.conversationICode = conversation === null || conversation === void 0 ? void 0 : conversation.iCode;
191
+ this.currentConversationSource.next(conversation);
192
+ }
193
+ getConversationICode() {
194
+ return this.conversationICode;
195
+ }
196
+ }
197
+ StoreService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: StoreService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
198
+ StoreService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: StoreService, providedIn: 'root' });
199
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: StoreService, decorators: [{
200
+ type: Injectable,
201
+ args: [{ providedIn: 'root' }]
202
+ }] });
203
+
204
+ let HttpService$1 = class HttpService {
205
+ constructor(http, storeService) {
206
+ this.http = http;
207
+ this.storeService = storeService;
208
+ }
209
+ get(url, paramObj) {
210
+ if (paramObj) {
211
+ return this.http
212
+ .get(url, {
213
+ headers: new HttpHeaders({
214
+ 'Content-Type': 'application/json',
215
+ Authorization: this.storeService.getToken(),
216
+ }),
217
+ observe: 'response',
218
+ params: paramObj,
219
+ responseType: 'json',
220
+ withCredentials: false,
221
+ })
222
+ .pipe(filter((r) => r instanceof HttpResponse), map((r) => {
223
+ return r.body;
224
+ }));
225
+ }
226
+ return this.http
227
+ .get(url, {
228
+ headers: new HttpHeaders({
229
+ 'Content-Type': 'application/json',
230
+ Authorization: this.storeService.getToken(),
231
+ }),
232
+ observe: 'response',
233
+ responseType: 'json',
234
+ withCredentials: false,
235
+ })
236
+ .pipe(filter((r) => r instanceof HttpResponse), map((r) => {
237
+ return r.body;
238
+ }));
239
+ }
240
+ post(url, body, options) {
241
+ // 默认配置
242
+ const defaultOptions = {
243
+ headers: new HttpHeaders({
244
+ 'Content-Type': 'application/json',
245
+ Authorization: this.storeService.getToken(),
246
+ }),
247
+ observe: 'response',
248
+ responseType: 'json',
249
+ withCredentials: false,
250
+ };
251
+ // 如果提供了自定义选项,则合并配置
252
+ const httpOptions = options ? Object.assign(Object.assign({}, defaultOptions), options) : defaultOptions;
253
+ // 发送 POST 请求
254
+ return this.http.post(url, body, httpOptions);
255
+ }
256
+ };
257
+ HttpService$1.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: HttpService$1, deps: [{ token: i1.HttpClient }, { token: StoreService }], target: i0.ɵɵFactoryTarget.Injectable });
258
+ HttpService$1.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: HttpService$1, providedIn: 'root' });
259
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: HttpService$1, decorators: [{
260
+ type: Injectable,
261
+ args: [{
262
+ providedIn: 'root',
263
+ }]
264
+ }], ctorParameters: function () { return [{ type: i1.HttpClient }, { type: StoreService }]; } });
265
+
266
+ class SseService {
267
+ constructor() {
268
+ this.storeService = inject(StoreService);
269
+ this.conversationICodeSource = new BehaviorSubject(null);
270
+ this.conversationICode$ = this.conversationICodeSource.asObservable();
271
+ // 思考内容
272
+ this.thinkingSource = new BehaviorSubject('');
273
+ this.thinking$ = this.thinkingSource.asObservable();
274
+ // 实时流式文本
275
+ this.messageSource = new BehaviorSubject('');
276
+ this.currentMessage$ = this.messageSource.asObservable();
277
+ this.deltaSource = new BehaviorSubject('');
278
+ this.delta$ = this.deltaSource.asObservable();
279
+ this.loadingSource = new BehaviorSubject(false);
280
+ this.loading$ = this.loadingSource.asObservable();
281
+ // 完整消息历史
282
+ this.fullThinking = '';
283
+ this.fullMessage = '';
284
+ // 支持取消(关键!)
285
+ this.controller = new AbortController();
286
+ }
287
+ streamChat(body) {
288
+ return __awaiter(this, void 0, void 0, function* () {
289
+ this.loadingSource.next(true);
290
+ this.messageSource.next(''); // 清空
291
+ this.fullMessage = '';
292
+ try {
293
+ const response = yield fetch(`${this.storeService.getBaseUrl()}/api/ChatModel/generate`, {
294
+ method: 'POST',
295
+ headers: {
296
+ 'Content-Type': 'application/json',
297
+ Authorization: this.storeService.getToken(),
298
+ Accept: 'text/event-stream',
299
+ },
300
+ body: JSON.stringify(body),
301
+ });
302
+ if (!response.ok || !response.body) {
303
+ throw new Error('网络错误');
304
+ }
305
+ const reader = response.body.getReader();
306
+ const decoder = new TextDecoder('utf-8');
307
+ while (true) {
308
+ const { done, value } = yield reader.read();
309
+ if (done)
310
+ break;
311
+ const chunk = decoder.decode(value, { stream: true });
312
+ const lines = chunk.split('\n\n');
313
+ for (const line of lines) {
314
+ if (line.startsWith('data:')) {
315
+ const jsonStr = line.replaceAll('data:', '');
316
+ try {
317
+ const data = JSON.parse(jsonStr);
318
+ const delta = data.content || '';
319
+ if (delta) {
320
+ // 对话以及问答编码
321
+ if (delta.conversation) {
322
+ this.conversationICodeSource.next(delta);
323
+ continue;
324
+ }
325
+ // 比如显示“AI 正在思考...”
326
+ if (data.think) {
327
+ this.fullThinking += delta;
328
+ this.thinkingSource.next(this.fullThinking);
329
+ continue;
330
+ }
331
+ this.fullMessage += delta;
332
+ this.messageSource.next(this.fullMessage); // 实时推送完整内容
333
+ this.deltaSource.next(delta); // 只推本次新增的文字
334
+ }
335
+ if (data.down) {
336
+ // down: true 表示结束
337
+ this.loadingSource.next(false);
338
+ }
339
+ }
340
+ catch (e) {
341
+ console.log('sse error', e);
342
+ }
343
+ }
344
+ }
345
+ }
346
+ }
347
+ catch (err) {
348
+ if (err.name !== 'AbortError') {
349
+ this.messageSource.next('请求失败:' + err.message);
350
+ }
351
+ }
352
+ finally {
353
+ this.loadingSource.next(false);
354
+ }
355
+ });
356
+ }
357
+ cancel() {
358
+ this.controller.abort();
359
+ this.controller = new AbortController();
360
+ }
361
+ }
362
+ SseService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: SseService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
363
+ SseService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: SseService, providedIn: 'root' });
364
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: SseService, decorators: [{
365
+ type: Injectable,
366
+ args: [{
367
+ providedIn: 'root',
368
+ }]
369
+ }] });
370
+
371
+ // chat-input.component.ts
372
+ class ChatInputComponent {
373
+ constructor() {
374
+ this.messages = [];
375
+ this.sendMessage = new EventEmitter();
376
+ this.messageChange = new EventEmitter();
377
+ this.enter = new EventEmitter();
378
+ this.paste = new EventEmitter();
379
+ this.httpService = inject(HttpService$1);
380
+ this.storeService = inject(StoreService);
381
+ this.sseService = inject(SseService);
382
+ this.isEmpty = true;
383
+ this.currentModel = null;
384
+ this.currentKnowledge = [];
385
+ this.modelList = [];
386
+ this.knowledgeList = [];
387
+ this.modelPopoverVisible = false;
388
+ this.knowledgePopoverVisible = false;
389
+ this.deepThink = true;
390
+ }
391
+ ngOnInit() {
392
+ this.getModelList();
393
+ this.getKnowledgeList();
394
+ }
395
+ // 可供交互的模型
396
+ getModelList() {
397
+ this.httpService.get(`${this.storeService.getBaseUrl()}/api/ChatModel/models`).subscribe((res) => {
398
+ this.modelList = res;
399
+ if (res.length) {
400
+ this.currentModel = res[0];
401
+ }
402
+ });
403
+ }
404
+ // 模型可选的知识库
405
+ getKnowledgeList() {
406
+ this.httpService
407
+ .get(`${this.storeService.getBaseUrl()}/api/ChatModel/knowledges`)
408
+ .subscribe((res) => {
409
+ this.knowledgeList = res;
410
+ });
411
+ }
412
+ modelVisibleChange(value) {
413
+ this.modelPopoverVisible = value;
414
+ }
415
+ knowledgeVisibleChange(value) {
416
+ this.knowledgePopoverVisible = value;
417
+ }
418
+ knowledgeSelectChange(value) {
419
+ this.currentKnowledge = value;
420
+ }
421
+ toggleDeepThink() {
422
+ this.deepThink = !this.deepThink;
423
+ }
424
+ updateEmptyState() {
425
+ var _a;
426
+ const el = this.editor.nativeElement;
427
+ const text = ((_a = el.textContent) === null || _a === void 0 ? void 0 : _a.trim()) || '';
428
+ const hasOnlyBr = el.innerHTML === '<br>' || el.innerHTML === '';
429
+ this.isEmpty = text === '' && hasOnlyBr;
430
+ }
431
+ handleInput() {
432
+ this.updateEmptyState();
433
+ const html = this.editor.nativeElement.innerHTML;
434
+ const text = this.editor.nativeElement.textContent || '';
435
+ // this.messageChange.emit(text);
436
+ }
437
+ handleEnter(event) {
438
+ if (!event.shiftKey) {
439
+ event.preventDefault();
440
+ // this.enter.emit((event as any).target!.innerText!.replace(/\s+/g, " ").trim());
441
+ this.send();
442
+ }
443
+ }
444
+ handlePaste(event) {
445
+ // this.paste.emit(event);
446
+ setTimeout(() => this.updateEmptyState(), 0);
447
+ }
448
+ clearInput() {
449
+ this.editor.nativeElement.innerHTML = '';
450
+ this.handleInput();
451
+ }
452
+ regenerateSend(msg) {
453
+ var _a, _b;
454
+ // 正在回答中
455
+ if (this.sseService.loadingSource.value) {
456
+ return;
457
+ }
458
+ const conversation = this.storeService.getConversationICode();
459
+ const message = {
460
+ conversation,
461
+ questionICode: msg.iCode,
462
+ model: (_b = (_a = this.currentModel) === null || _a === void 0 ? void 0 : _a.iCode) !== null && _b !== void 0 ? _b : '',
463
+ think: this.deepThink,
464
+ question: msg.question,
465
+ knowledge: !!this.currentKnowledge.length,
466
+ knowledgeList: this.currentKnowledge,
467
+ };
468
+ this.sendMessage.emit({ message, isRegenerate: true });
469
+ }
470
+ send() {
471
+ var _a, _b;
472
+ // 正在回答中
473
+ if (this.sseService.loadingSource.value) {
474
+ return;
475
+ }
476
+ const text = this.editor.nativeElement.textContent || '';
477
+ const conversation = this.storeService.getConversationICode();
478
+ const message = {
479
+ conversation,
480
+ model: (_b = (_a = this.currentModel) === null || _a === void 0 ? void 0 : _a.iCode) !== null && _b !== void 0 ? _b : '',
481
+ think: this.deepThink,
482
+ question: text,
483
+ knowledge: !!this.currentKnowledge.length,
484
+ knowledgeList: this.currentKnowledge,
485
+ };
486
+ this.clearInput();
487
+ this.sendMessage.emit({ message, isRegenerate: false });
488
+ }
489
+ }
490
+ ChatInputComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: ChatInputComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
491
+ ChatInputComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.10", type: ChatInputComponent, selector: "ngx-chat-input", inputs: { messages: "messages" }, outputs: { sendMessage: "sendMessage", messageChange: "messageChange", enter: "enter", paste: "paste" }, viewQueries: [{ propertyName: "editor", first: true, predicate: ["editorRef"], descendants: true }, { propertyName: "chatInputWrapper", first: true, predicate: ["chatInputWrapper"], descendants: true }], ngImport: i0, template: "<!-- \u65B0\u804A\u5929\u4F4D\u4E8E\u5C4F\u5E55\u4E2D\u95F4\uFF0C\u53D1\u9001\u6D88\u606F\u4E4B\u540E\u4F4D\u4E8E\u5E95\u90E8 -->\n<div class=\"chat-input-wrapper\" #chatInputWrapper [class.center]=\"!messages.length\">\n <div class=\"new-chat-title\" *ngIf=\"!messages.length\">\n <img src=\"/assets/images/logo.png\" />\n <span>\u4F60\u597D\uFF0C\u6709\u4EC0\u4E48\u53EF\u4EE5\u5E2E\u5230\u4F60\uFF1F\u5F00\u59CB\u5BF9\u8BDD\u5427</span>\n </div>\n <div class=\"chat-input\">\n <div\n #editorRef\n class=\"input-creative-editor\"\n contenteditable=\"true\"\n spellcheck=\"false\"\n data-placeholder=\"\u8BF7\u8F93\u5165\u5E76\u53D1\u9001\u6D88\u606F\"\n [attr.data-empty]=\"isEmpty\"\n (input)=\"handleInput()\"\n (keydown.enter)=\"handleEnter($event)\"\n (paste)=\"handlePaste($event)\"\n ></div>\n <div class=\"input-bottom\">\n <div class=\"input-tools\">\n <div class=\"select-tool\" [ngClass]=\"{ 'select-tool-active': deepThink }\" (click)=\"toggleDeepThink()\">\n <svg\n t=\"1764131026949\"\n style=\"width: 14px; height: 14px\"\n class=\"icon\"\n viewBox=\"0 0 1024 1024\"\n version=\"1.1\"\n xmlns=\"http://www.w3.org/2000/svg\"\n p-id=\"5012\"\n width=\"64\"\n height=\"64\"\n >\n <path\n d=\"M903.36 512a795.52 795.52 0 0 1 65.536 136.704c48 132.16 39.616 230.528-25.024 295.168-64.64 64.64-163.008 72.96-295.168 24.96A795.52 795.52 0 0 1 512 903.424a795.52 795.52 0 0 1-136.704 65.536c-132.096 48-230.528 39.616-295.168-25.024-64.64-64.64-72.96-163.008-24.96-295.104 16.64-46.016 38.528-91.52 65.536-136.768A795.52 795.52 0 0 1 55.04 375.232C7.04 243.2 15.36 144.768 80 80.128 144.768 15.488 243.2 7.168 375.296 55.04c46.016 16.64 91.584 38.528 136.768 65.536A795.52 795.52 0 0 1 648.768 55.104c132.096-48 230.464-39.616 295.104 25.024 64.64 64.64 72.96 163.008 25.024 295.104A795.52 795.52 0 0 1 903.36 512z m-53.12-79.424c15.168-28.736 27.968-57.536 38.464-86.4 35.584-98.112 33.92-166.656-5.12-205.696-39.04-39.04-107.648-40.768-205.696-5.12a685.44 685.44 0 0 0-86.4 38.4 1240.96 1240.96 0 0 1 138.432 120.32 1240.832 1240.832 0 0 1 120.32 138.496zM432.576 173.824a685.44 685.44 0 0 0-86.4-38.528c-98.112-35.584-166.656-33.92-205.696 5.12-39.04 39.04-40.768 107.648-5.12 205.696 10.432 28.928 23.296 57.728 38.4 86.4a1240.896 1240.896 0 0 1 120.32-138.432 1240.832 1240.832 0 0 1 138.496-120.32zM173.824 591.488a685.44 685.44 0 0 0-38.464 86.4c-35.648 98.048-33.92 166.592 5.12 205.632 39.04 39.04 107.584 40.768 205.696 5.12a685.44 685.44 0 0 0 86.4-38.4 1240.768 1240.768 0 0 1-138.496-120.32 1240.96 1240.96 0 0 1-120.256-138.432z m495.744 78.08A1112.064 1112.064 0 0 0 802.048 512a1112.064 1112.064 0 0 0-132.48-157.568A1112.128 1112.128 0 0 0 512 221.952a1112.128 1112.128 0 0 0-157.504 132.48A1112.064 1112.064 0 0 0 222.016 512a1112.192 1112.192 0 0 0 132.416 157.568A1112.064 1112.064 0 0 0 512 802.048a1112.128 1112.128 0 0 0 157.568-132.48z m-78.08 180.608c28.672 15.168 57.472 28.032 86.4 38.464 98.048 35.648 166.592 33.92 205.632-5.12 39.04-39.04 40.768-107.584 5.12-205.696a685.504 685.504 0 0 0-38.4-86.4 1240.832 1240.832 0 0 1-120.32 138.496 1240.96 1240.96 0 0 1-138.432 120.32zM585.088 512a73.152 73.152 0 1 1-146.24 0 73.152 73.152 0 0 1 146.304 0z\"\n fill=\"currentColor\"\n p-id=\"5013\"\n ></path>\n </svg>\n \u6DF1\u5EA6\u601D\u8003\n </div>\n <div\n class=\"select-tool\"\n nz-popover\n [nzPopoverContent]=\"modelListContent\"\n nzPopoverTrigger=\"click\"\n nzPopoverOverlayClassName=\"params-select-popover\"\n [nzPopoverVisible]=\"modelPopoverVisible\"\n (nzPopoverVisibleChange)=\"modelVisibleChange($event)\"\n >\n <img src=\"/assets/images/input/toggle-model.png\" style=\"width: 12px; height: 12px\" />\n {{ currentModel ? currentModel?.name : '\u9009\u62E9\u6A21\u578B' }}\n </div>\n <div\n class=\"select-tool\"\n nz-popover\n [nzPopoverContent]=\"knowledgeListContent\"\n nzPopoverTrigger=\"click\"\n nzPopoverOverlayClassName=\"params-select-popover\"\n [nzPopoverVisible]=\"knowledgePopoverVisible\"\n (nzPopoverVisibleChange)=\"knowledgeVisibleChange($event)\"\n >\n <img src=\"/assets/images/input/relate.png\" style=\"width: 14px; height: 8px\" />\n \u5173\u8054\u77E5\u8BC6\u5E93\n </div>\n </div>\n <img class=\"send\" src=\"/assets/images/input/send.png\" (click)=\"send()\" [hidden]=\"sseService.loading$ | async\" />\n <div class=\"send-loading\" *ngIf=\"sseService.loading$ | async\">\n <div class=\"send-loading-inner\">\n <svg viewBox=\"0 0 36 36\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" data-icon=\"spin\">\n <defs>\n <linearGradient x1=\"0%\" y1=\"100%\" x2=\"100%\" y2=\"100%\" id=\"linearGradient-1\">\n <stop stop-color=\"currentColor\" stop-opacity=\"0\" offset=\"0%\"></stop>\n <stop stop-color=\"currentColor\" stop-opacity=\"0.50\" offset=\"39.9430698%\"></stop>\n <stop stop-color=\"currentColor\" offset=\"100%\"></stop>\n </linearGradient>\n </defs>\n <g stroke=\"none\" stroke-width=\"1\" fill=\"none\" fill-rule=\"evenodd\">\n <rect fill-opacity=\"0.01\" fill=\"none\" x=\"0\" y=\"0\" width=\"36\" height=\"36\"></rect>\n <path\n d=\"M34,18 C34,9.163444 26.836556,2 18,2 C11.6597233,2 6.18078805,5.68784135 3.59122325,11.0354951\"\n stroke=\"url(#linearGradient-1)\"\n stroke-width=\"4\"\n stroke-linecap=\"round\"\n ></path>\n </g>\n </svg>\n </div>\n </div>\n <!-- <img class=\"send\" src=\"/assets/images/input/send-gray.png\" /> -->\n </div>\n </div>\n <div class=\"chat-input-backdrop\"></div>\n</div>\n\n<ng-template #modelListContent>\n <div style=\"width: 200px\">\n <div class=\"select-title\">\n \u5207\u6362\u6A21\u578B\n <i nz-icon nzType=\"close\" style=\"color: #999\" (click)=\"modelVisibleChange(false)\"></i>\n </div>\n <div class=\"select-content\">\n <nz-radio-group [(ngModel)]=\"currentModel\">\n <label\n nz-radio\n [nzValue]=\"item\"\n [style.margin-top.px]=\"i === 0 ? 0 : 10\"\n *ngFor=\"let item of modelList; let i = index\"\n >\n {{ item.name }}\n </label>\n </nz-radio-group>\n </div>\n </div>\n</ng-template>\n\n<ng-template #knowledgeListContent>\n <div style=\"width: 150px\">\n <div class=\"select-title\">\n \u5173\u8054\u77E5\u8BC6\u5E93\n <i nz-icon nzType=\"close\" style=\"color: #999\" (click)=\"knowledgeVisibleChange(false)\"></i>\n </div>\n <div class=\"select-content\">\n <nz-checkbox-wrapper style=\"width: 100%\" (nzOnChange)=\"knowledgeSelectChange($event)\">\n <div *ngFor=\"let item of knowledgeList\">\n <label nz-checkbox [nzValue]=\"item.iCode\">{{ item.name }}</label>\n </div>\n </nz-checkbox-wrapper>\n </div>\n </div>\n</ng-template>\n", styles: ["@charset \"UTF-8\";.chat-input-wrapper{position:absolute;bottom:0;left:0;right:0;max-width:800px;min-width:350px;margin:0 auto;padding-bottom:40px}.chat-input-wrapper.center{bottom:50%;transform:translateY(50%)}.new-chat-title{padding-bottom:40px;text-align:center;font-weight:500;font-size:24px;color:#000}.new-chat-title img{width:40px;height:32px;margin-right:12px;margin-top:-6px}.chat-input{position:relative;display:flex;flex-direction:column;justify-content:space-between;padding:20px 18px;margin:0 auto;background:#FFFFFF;box-shadow:0 2px 8px #0000001a;border-radius:20px;border:1px solid #D9DADB;z-index:30}.chat-input .input-creative-editor{min-height:60px;max-height:300px;overflow-y:auto;outline:none;box-shadow:none;border:none}.chat-input .input-creative-editor[data-empty=true]:before{content:attr(data-placeholder);color:#9a9b9b}.chat-input .input-bottom{padding-top:16px;display:flex;align-items:center;justify-content:space-between;-webkit-user-select:none;user-select:none}.chat-input .input-bottom .input-tools{display:flex;align-items:center;gap:10px}.chat-input .input-bottom .select-tool{display:flex;align-items:center;justify-content:center;height:36px;padding:0 12px;gap:12px;background:#FFFFFF;border-radius:12px;border:1px solid #DBDCE0;cursor:pointer}.chat-input .input-bottom .select-tool-active{border-color:#b7c8fe;color:#3964fe;background:#edf3fe}.chat-input .input-bottom .send{width:32px;height:32px}::ng-deep .params-select-popover .ant-popover-inner{border-radius:10px}::ng-deep .params-select-popover .select-title{display:flex;align-items:center;justify-content:center;position:relative}::ng-deep .params-select-popover .select-title i{position:absolute;right:0;top:3px;cursor:pointer}::ng-deep .params-select-popover .select-content{max-height:200px;padding:16px 0;overflow-y:auto}.chat-input-backdrop{position:absolute;bottom:0;left:0;width:100%;height:100px;z-index:10;background-image:linear-gradient(to bottom,hsl(0,0%,99%),hsla(0,0%,99%,.8))}.send-loading{min-width:34px;height:34px;border-radius:50%;margin-top:auto;display:flex;flex-shrink:0;align-items:center;flex-direction:column;justify-content:center;cursor:not-allowed;white-space:nowrap;color:#fff;background:#3964fe;transition:background .2s;opacity:.4}.send-loading-inner{width:16px;height:16px}.send-loading svg{will-change:transform;animation:.6s linear infinite send-loading}@keyframes send-loading{0%{transform:rotate(0)}to{transform:rotate(360deg)}}\n"], dependencies: [{ kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2.ɵNzTransitionPatchDirective, selector: "[nz-button], nz-button-group, [nz-icon], [nz-menu-item], [nz-submenu], nz-select-top-control, nz-select-placeholder, nz-input-group", inputs: ["hidden"] }, { kind: "directive", type: i3.NzPopoverDirective, selector: "[nz-popover]", inputs: ["nzPopoverArrowPointAtCenter", "nzPopoverTitle", "nzPopoverContent", "nz-popover", "nzPopoverTrigger", "nzPopoverPlacement", "nzPopoverOrigin", "nzPopoverVisible", "nzPopoverMouseEnterDelay", "nzPopoverMouseLeaveDelay", "nzPopoverOverlayClassName", "nzPopoverOverlayStyle", "nzPopoverBackdrop"], outputs: ["nzPopoverVisibleChange"], exportAs: ["nzPopover"] }, { kind: "component", type: i4.NzRadioComponent, selector: "[nz-radio],[nz-radio-button]", inputs: ["nzValue", "nzDisabled", "nzAutoFocus"], exportAs: ["nzRadio"] }, { kind: "component", type: i4.NzRadioGroupComponent, selector: "nz-radio-group", inputs: ["nzDisabled", "nzButtonStyle", "nzSize", "nzName"], exportAs: ["nzRadioGroup"] }, { kind: "component", type: i5.NzCheckboxComponent, selector: "[nz-checkbox]", inputs: ["nzValue", "nzAutoFocus", "nzDisabled", "nzIndeterminate", "nzChecked", "nzId"], outputs: ["nzCheckedChange"], exportAs: ["nzCheckbox"] }, { kind: "component", type: i5.NzCheckboxWrapperComponent, selector: "nz-checkbox-wrapper", outputs: ["nzOnChange"], exportAs: ["nzCheckboxWrapper"] }, { kind: "directive", type: i6.NzIconDirective, selector: "[nz-icon]", inputs: ["nzSpin", "nzRotate", "nzType", "nzTheme", "nzTwotoneColor", "nzIconfont"], exportAs: ["nzIcon"] }, { kind: "directive", type: i7.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i7.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }] });
492
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: ChatInputComponent, decorators: [{
493
+ type: Component,
494
+ args: [{ selector: 'ngx-chat-input', template: "<!-- \u65B0\u804A\u5929\u4F4D\u4E8E\u5C4F\u5E55\u4E2D\u95F4\uFF0C\u53D1\u9001\u6D88\u606F\u4E4B\u540E\u4F4D\u4E8E\u5E95\u90E8 -->\n<div class=\"chat-input-wrapper\" #chatInputWrapper [class.center]=\"!messages.length\">\n <div class=\"new-chat-title\" *ngIf=\"!messages.length\">\n <img src=\"/assets/images/logo.png\" />\n <span>\u4F60\u597D\uFF0C\u6709\u4EC0\u4E48\u53EF\u4EE5\u5E2E\u5230\u4F60\uFF1F\u5F00\u59CB\u5BF9\u8BDD\u5427</span>\n </div>\n <div class=\"chat-input\">\n <div\n #editorRef\n class=\"input-creative-editor\"\n contenteditable=\"true\"\n spellcheck=\"false\"\n data-placeholder=\"\u8BF7\u8F93\u5165\u5E76\u53D1\u9001\u6D88\u606F\"\n [attr.data-empty]=\"isEmpty\"\n (input)=\"handleInput()\"\n (keydown.enter)=\"handleEnter($event)\"\n (paste)=\"handlePaste($event)\"\n ></div>\n <div class=\"input-bottom\">\n <div class=\"input-tools\">\n <div class=\"select-tool\" [ngClass]=\"{ 'select-tool-active': deepThink }\" (click)=\"toggleDeepThink()\">\n <svg\n t=\"1764131026949\"\n style=\"width: 14px; height: 14px\"\n class=\"icon\"\n viewBox=\"0 0 1024 1024\"\n version=\"1.1\"\n xmlns=\"http://www.w3.org/2000/svg\"\n p-id=\"5012\"\n width=\"64\"\n height=\"64\"\n >\n <path\n d=\"M903.36 512a795.52 795.52 0 0 1 65.536 136.704c48 132.16 39.616 230.528-25.024 295.168-64.64 64.64-163.008 72.96-295.168 24.96A795.52 795.52 0 0 1 512 903.424a795.52 795.52 0 0 1-136.704 65.536c-132.096 48-230.528 39.616-295.168-25.024-64.64-64.64-72.96-163.008-24.96-295.104 16.64-46.016 38.528-91.52 65.536-136.768A795.52 795.52 0 0 1 55.04 375.232C7.04 243.2 15.36 144.768 80 80.128 144.768 15.488 243.2 7.168 375.296 55.04c46.016 16.64 91.584 38.528 136.768 65.536A795.52 795.52 0 0 1 648.768 55.104c132.096-48 230.464-39.616 295.104 25.024 64.64 64.64 72.96 163.008 25.024 295.104A795.52 795.52 0 0 1 903.36 512z m-53.12-79.424c15.168-28.736 27.968-57.536 38.464-86.4 35.584-98.112 33.92-166.656-5.12-205.696-39.04-39.04-107.648-40.768-205.696-5.12a685.44 685.44 0 0 0-86.4 38.4 1240.96 1240.96 0 0 1 138.432 120.32 1240.832 1240.832 0 0 1 120.32 138.496zM432.576 173.824a685.44 685.44 0 0 0-86.4-38.528c-98.112-35.584-166.656-33.92-205.696 5.12-39.04 39.04-40.768 107.648-5.12 205.696 10.432 28.928 23.296 57.728 38.4 86.4a1240.896 1240.896 0 0 1 120.32-138.432 1240.832 1240.832 0 0 1 138.496-120.32zM173.824 591.488a685.44 685.44 0 0 0-38.464 86.4c-35.648 98.048-33.92 166.592 5.12 205.632 39.04 39.04 107.584 40.768 205.696 5.12a685.44 685.44 0 0 0 86.4-38.4 1240.768 1240.768 0 0 1-138.496-120.32 1240.96 1240.96 0 0 1-120.256-138.432z m495.744 78.08A1112.064 1112.064 0 0 0 802.048 512a1112.064 1112.064 0 0 0-132.48-157.568A1112.128 1112.128 0 0 0 512 221.952a1112.128 1112.128 0 0 0-157.504 132.48A1112.064 1112.064 0 0 0 222.016 512a1112.192 1112.192 0 0 0 132.416 157.568A1112.064 1112.064 0 0 0 512 802.048a1112.128 1112.128 0 0 0 157.568-132.48z m-78.08 180.608c28.672 15.168 57.472 28.032 86.4 38.464 98.048 35.648 166.592 33.92 205.632-5.12 39.04-39.04 40.768-107.584 5.12-205.696a685.504 685.504 0 0 0-38.4-86.4 1240.832 1240.832 0 0 1-120.32 138.496 1240.96 1240.96 0 0 1-138.432 120.32zM585.088 512a73.152 73.152 0 1 1-146.24 0 73.152 73.152 0 0 1 146.304 0z\"\n fill=\"currentColor\"\n p-id=\"5013\"\n ></path>\n </svg>\n \u6DF1\u5EA6\u601D\u8003\n </div>\n <div\n class=\"select-tool\"\n nz-popover\n [nzPopoverContent]=\"modelListContent\"\n nzPopoverTrigger=\"click\"\n nzPopoverOverlayClassName=\"params-select-popover\"\n [nzPopoverVisible]=\"modelPopoverVisible\"\n (nzPopoverVisibleChange)=\"modelVisibleChange($event)\"\n >\n <img src=\"/assets/images/input/toggle-model.png\" style=\"width: 12px; height: 12px\" />\n {{ currentModel ? currentModel?.name : '\u9009\u62E9\u6A21\u578B' }}\n </div>\n <div\n class=\"select-tool\"\n nz-popover\n [nzPopoverContent]=\"knowledgeListContent\"\n nzPopoverTrigger=\"click\"\n nzPopoverOverlayClassName=\"params-select-popover\"\n [nzPopoverVisible]=\"knowledgePopoverVisible\"\n (nzPopoverVisibleChange)=\"knowledgeVisibleChange($event)\"\n >\n <img src=\"/assets/images/input/relate.png\" style=\"width: 14px; height: 8px\" />\n \u5173\u8054\u77E5\u8BC6\u5E93\n </div>\n </div>\n <img class=\"send\" src=\"/assets/images/input/send.png\" (click)=\"send()\" [hidden]=\"sseService.loading$ | async\" />\n <div class=\"send-loading\" *ngIf=\"sseService.loading$ | async\">\n <div class=\"send-loading-inner\">\n <svg viewBox=\"0 0 36 36\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" data-icon=\"spin\">\n <defs>\n <linearGradient x1=\"0%\" y1=\"100%\" x2=\"100%\" y2=\"100%\" id=\"linearGradient-1\">\n <stop stop-color=\"currentColor\" stop-opacity=\"0\" offset=\"0%\"></stop>\n <stop stop-color=\"currentColor\" stop-opacity=\"0.50\" offset=\"39.9430698%\"></stop>\n <stop stop-color=\"currentColor\" offset=\"100%\"></stop>\n </linearGradient>\n </defs>\n <g stroke=\"none\" stroke-width=\"1\" fill=\"none\" fill-rule=\"evenodd\">\n <rect fill-opacity=\"0.01\" fill=\"none\" x=\"0\" y=\"0\" width=\"36\" height=\"36\"></rect>\n <path\n d=\"M34,18 C34,9.163444 26.836556,2 18,2 C11.6597233,2 6.18078805,5.68784135 3.59122325,11.0354951\"\n stroke=\"url(#linearGradient-1)\"\n stroke-width=\"4\"\n stroke-linecap=\"round\"\n ></path>\n </g>\n </svg>\n </div>\n </div>\n <!-- <img class=\"send\" src=\"/assets/images/input/send-gray.png\" /> -->\n </div>\n </div>\n <div class=\"chat-input-backdrop\"></div>\n</div>\n\n<ng-template #modelListContent>\n <div style=\"width: 200px\">\n <div class=\"select-title\">\n \u5207\u6362\u6A21\u578B\n <i nz-icon nzType=\"close\" style=\"color: #999\" (click)=\"modelVisibleChange(false)\"></i>\n </div>\n <div class=\"select-content\">\n <nz-radio-group [(ngModel)]=\"currentModel\">\n <label\n nz-radio\n [nzValue]=\"item\"\n [style.margin-top.px]=\"i === 0 ? 0 : 10\"\n *ngFor=\"let item of modelList; let i = index\"\n >\n {{ item.name }}\n </label>\n </nz-radio-group>\n </div>\n </div>\n</ng-template>\n\n<ng-template #knowledgeListContent>\n <div style=\"width: 150px\">\n <div class=\"select-title\">\n \u5173\u8054\u77E5\u8BC6\u5E93\n <i nz-icon nzType=\"close\" style=\"color: #999\" (click)=\"knowledgeVisibleChange(false)\"></i>\n </div>\n <div class=\"select-content\">\n <nz-checkbox-wrapper style=\"width: 100%\" (nzOnChange)=\"knowledgeSelectChange($event)\">\n <div *ngFor=\"let item of knowledgeList\">\n <label nz-checkbox [nzValue]=\"item.iCode\">{{ item.name }}</label>\n </div>\n </nz-checkbox-wrapper>\n </div>\n </div>\n</ng-template>\n", styles: ["@charset \"UTF-8\";.chat-input-wrapper{position:absolute;bottom:0;left:0;right:0;max-width:800px;min-width:350px;margin:0 auto;padding-bottom:40px}.chat-input-wrapper.center{bottom:50%;transform:translateY(50%)}.new-chat-title{padding-bottom:40px;text-align:center;font-weight:500;font-size:24px;color:#000}.new-chat-title img{width:40px;height:32px;margin-right:12px;margin-top:-6px}.chat-input{position:relative;display:flex;flex-direction:column;justify-content:space-between;padding:20px 18px;margin:0 auto;background:#FFFFFF;box-shadow:0 2px 8px #0000001a;border-radius:20px;border:1px solid #D9DADB;z-index:30}.chat-input .input-creative-editor{min-height:60px;max-height:300px;overflow-y:auto;outline:none;box-shadow:none;border:none}.chat-input .input-creative-editor[data-empty=true]:before{content:attr(data-placeholder);color:#9a9b9b}.chat-input .input-bottom{padding-top:16px;display:flex;align-items:center;justify-content:space-between;-webkit-user-select:none;user-select:none}.chat-input .input-bottom .input-tools{display:flex;align-items:center;gap:10px}.chat-input .input-bottom .select-tool{display:flex;align-items:center;justify-content:center;height:36px;padding:0 12px;gap:12px;background:#FFFFFF;border-radius:12px;border:1px solid #DBDCE0;cursor:pointer}.chat-input .input-bottom .select-tool-active{border-color:#b7c8fe;color:#3964fe;background:#edf3fe}.chat-input .input-bottom .send{width:32px;height:32px}::ng-deep .params-select-popover .ant-popover-inner{border-radius:10px}::ng-deep .params-select-popover .select-title{display:flex;align-items:center;justify-content:center;position:relative}::ng-deep .params-select-popover .select-title i{position:absolute;right:0;top:3px;cursor:pointer}::ng-deep .params-select-popover .select-content{max-height:200px;padding:16px 0;overflow-y:auto}.chat-input-backdrop{position:absolute;bottom:0;left:0;width:100%;height:100px;z-index:10;background-image:linear-gradient(to bottom,hsl(0,0%,99%),hsla(0,0%,99%,.8))}.send-loading{min-width:34px;height:34px;border-radius:50%;margin-top:auto;display:flex;flex-shrink:0;align-items:center;flex-direction:column;justify-content:center;cursor:not-allowed;white-space:nowrap;color:#fff;background:#3964fe;transition:background .2s;opacity:.4}.send-loading-inner{width:16px;height:16px}.send-loading svg{will-change:transform;animation:.6s linear infinite send-loading}@keyframes send-loading{0%{transform:rotate(0)}to{transform:rotate(360deg)}}\n"] }]
495
+ }], propDecorators: { editor: [{
496
+ type: ViewChild,
497
+ args: ['editorRef']
498
+ }], chatInputWrapper: [{
499
+ type: ViewChild,
500
+ args: ['chatInputWrapper']
501
+ }], messages: [{
502
+ type: Input
503
+ }], sendMessage: [{
504
+ type: Output
505
+ }], messageChange: [{
506
+ type: Output
507
+ }], enter: [{
508
+ type: Output
509
+ }], paste: [{
510
+ type: Output
511
+ }] } });
512
+
513
+ class SimplePaginationComponent {
514
+ constructor() {
515
+ /** 当前页码 */
516
+ this.current = 1;
517
+ /** 总页数 */
518
+ this.total = 1;
519
+ /** 当页码改变时触发 */
520
+ this.pageChange = new EventEmitter();
521
+ }
522
+ onPrev() {
523
+ if (this.current > 1) {
524
+ this.pageChange.emit(this.current - 1);
525
+ }
526
+ }
527
+ onNext() {
528
+ if (this.current < this.total) {
529
+ this.pageChange.emit(this.current + 1);
530
+ }
531
+ }
532
+ }
533
+ SimplePaginationComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: SimplePaginationComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
534
+ SimplePaginationComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.10", type: SimplePaginationComponent, selector: "ngx-simple-pagination", inputs: { current: "current", total: "total" }, outputs: { pageChange: "pageChange" }, ngImport: i0, template: `
535
+ <div class="pagination-container">
536
+ <button
537
+ class="nav-btn"
538
+ [disabled]="current === 1"
539
+ (click)="onPrev()">
540
+ <svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2"
541
+ stroke-linecap="round" stroke-linejoin="round">
542
+ <polyline points="15 18 9 12 15 6"></polyline>
543
+ </svg>
544
+ </button>
545
+
546
+ <span class="page-info">
547
+ <span class="current">{{ current }}</span>
548
+ <span class="separator">/</span>
549
+ <span class="total">{{ total }}</span>
550
+ </span>
551
+
552
+ <button
553
+ class="nav-btn"
554
+ [disabled]="current === total"
555
+ (click)="onNext()">
556
+ <svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2"
557
+ stroke-linecap="round" stroke-linejoin="round">
558
+ <polyline points="9 18 15 12 9 6"></polyline>
559
+ </svg>
560
+ </button>
561
+ </div>
562
+ `, isInline: true, styles: [":host{display:inline-block}.pagination-container{display:flex;align-items:center;gap:8px;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif;-webkit-user-select:none;user-select:none;color:#606266}.nav-btn{background:none;border:none;cursor:pointer;padding:4px;display:flex;align-items:center;justify-content:center;color:#909399;transition:color .2s,background-color .2s;border-radius:4px}.nav-btn:hover:not(:disabled){color:#303133;background-color:#f0f2f5}.nav-btn:disabled{cursor:not-allowed;color:#dcdfe6;opacity:.6}.page-info{font-size:14px;font-weight:500;letter-spacing:1px;color:#606266;display:flex;align-items:center;gap:4px}.separator{color:#909399}\n"] });
563
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: SimplePaginationComponent, decorators: [{
564
+ type: Component,
565
+ args: [{ selector: 'ngx-simple-pagination', template: `
566
+ <div class="pagination-container">
567
+ <button
568
+ class="nav-btn"
569
+ [disabled]="current === 1"
570
+ (click)="onPrev()">
571
+ <svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2"
572
+ stroke-linecap="round" stroke-linejoin="round">
573
+ <polyline points="15 18 9 12 15 6"></polyline>
574
+ </svg>
575
+ </button>
576
+
577
+ <span class="page-info">
578
+ <span class="current">{{ current }}</span>
579
+ <span class="separator">/</span>
580
+ <span class="total">{{ total }}</span>
581
+ </span>
582
+
583
+ <button
584
+ class="nav-btn"
585
+ [disabled]="current === total"
586
+ (click)="onNext()">
587
+ <svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2"
588
+ stroke-linecap="round" stroke-linejoin="round">
589
+ <polyline points="9 18 15 12 9 6"></polyline>
590
+ </svg>
591
+ </button>
592
+ </div>
593
+ `, styles: [":host{display:inline-block}.pagination-container{display:flex;align-items:center;gap:8px;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif;-webkit-user-select:none;user-select:none;color:#606266}.nav-btn{background:none;border:none;cursor:pointer;padding:4px;display:flex;align-items:center;justify-content:center;color:#909399;transition:color .2s,background-color .2s;border-radius:4px}.nav-btn:hover:not(:disabled){color:#303133;background-color:#f0f2f5}.nav-btn:disabled{cursor:not-allowed;color:#dcdfe6;opacity:.6}.page-info{font-size:14px;font-weight:500;letter-spacing:1px;color:#606266;display:flex;align-items:center;gap:4px}.separator{color:#909399}\n"] }]
594
+ }], propDecorators: { current: [{
595
+ type: Input
596
+ }], total: [{
597
+ type: Input
598
+ }], pageChange: [{
599
+ type: Output
600
+ }] } });
601
+
602
+ class MarkdownPipe {
603
+ constructor() {
604
+ this.md = inject(MarkdownStreamService);
605
+ }
606
+ transform(value) {
607
+ if (value !== null && value !== undefined) {
608
+ return this.md.renderIncremental(value);
609
+ }
610
+ return value;
611
+ }
612
+ }
613
+ MarkdownPipe.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: MarkdownPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
614
+ MarkdownPipe.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "15.2.10", ngImport: i0, type: MarkdownPipe, isStandalone: true, name: "markdown" });
615
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: MarkdownPipe, decorators: [{
616
+ type: Pipe,
617
+ args: [{
618
+ name: 'markdown',
619
+ standalone: true,
620
+ }]
621
+ }] });
622
+
623
+ // chat-messages.component.ts
624
+ class ChatMessagesComponent {
625
+ constructor() {
626
+ this.messages = [];
627
+ /**
628
+ * 重新生成回答
629
+ */
630
+ this.regenerateAnswer = new EventEmitter();
631
+ this.md = inject(MarkdownStreamService);
632
+ this.sseService = inject(SseService);
633
+ this.sanitizer = inject(DomSanitizer);
634
+ this.msg = inject(NzMessageService);
635
+ this.isLoading = false;
636
+ this.inputHeight = 180;
637
+ this.renderedHtml = '';
638
+ this.accumulatedMarkdown = '';
639
+ }
640
+ ngAfterViewInit() {
641
+ this.sseService.loading$.subscribe(status => {
642
+ this.isLoading = status;
643
+ });
644
+ this.sseService.delta$.subscribe(delta => {
645
+ if (!delta)
646
+ return;
647
+ this.accumulatedMarkdown += delta;
648
+ this.renderedHtml = this.md.renderIncremental(this.accumulatedMarkdown);
649
+ });
650
+ }
651
+ ngAfterViewChecked() {
652
+ // 正在回答中
653
+ if (this.isLoading) {
654
+ this.scrollToBottom();
655
+ }
656
+ }
657
+ ngOnChanges(changes) {
658
+ if (changes['messages']) {
659
+ setTimeout(() => {
660
+ this.scrollToBottom();
661
+ }, 50);
662
+ }
663
+ }
664
+ // 滚动到底部
665
+ scrollToBottom() {
666
+ if (!this.messagesContainer) {
667
+ return;
668
+ }
669
+ this.messagesContainer.nativeElement.scrollTop = this.messagesContainer.nativeElement.scrollHeight;
670
+ }
671
+ copyContent(text) {
672
+ if (navigator.clipboard && window.isSecureContext) {
673
+ // 使用现代 Clipboard API
674
+ navigator.clipboard.writeText(text).then(() => {
675
+ this.msg.success('复制成功');
676
+ });
677
+ }
678
+ }
679
+ onPageChange(num, msg) {
680
+ msg.answerIndex = num - 1;
681
+ }
682
+ /**
683
+ * 重新生成多个回答
684
+ */
685
+ regenerate(msg) {
686
+ // const question = this.messages[index - 1].message;
687
+ // 重新生成使用底部最新的配置,重新调用sendMessage
688
+ this.regenerateAnswer.emit(msg);
689
+ }
690
+ // 把完整的 markdown 块渲染,剩下的留给打字区
691
+ renderCompleteBlocks(markdown) {
692
+ // 找到最后一个换行符之前的都渲染(足够丝滑)
693
+ const lines = markdown.split('\n');
694
+ let breakPoint = lines.length;
695
+ // 从后往前找,保留未完成的代码块/表格/列表
696
+ for (let i = lines.length - 1; i >= 0; i--) {
697
+ const line = lines[i];
698
+ if (line.trim() === '' || line.startsWith('```') || line.startsWith('|') || line.startsWith('- ')) {
699
+ breakPoint = i;
700
+ break;
701
+ }
702
+ }
703
+ const completePart = lines.slice(0, breakPoint).join('\n');
704
+ const remainingPart = lines.slice(breakPoint).join('\n');
705
+ const html = this.md.renderIncremental(completePart);
706
+ // const html = marked(completePart, { breaks: true, gfm: true });
707
+ return { rendered: html, remaining: remainingPart };
708
+ }
709
+ }
710
+ ChatMessagesComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: ChatMessagesComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
711
+ ChatMessagesComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.10", type: ChatMessagesComponent, selector: "ngx-chat-messages", inputs: { messages: "messages" }, outputs: { regenerateAnswer: "regenerateAnswer" }, viewQueries: [{ propertyName: "messagesContainer", first: true, predicate: ["messagesContainer"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<!-- chat-messages.component.html -->\n<script src=\"chat-messages.component.ts\"></script>\n<div class=\"messages-container\" #messagesContainer>\n <div class=\"message-list\" [style.--input-height.px]=\"inputHeight\">\n <div class=\"question message-wrapper\" *ngFor=\"let msg of messages; let i = index\">\n <!-- Q -->\n <div class=\"bubble-wrapper user-msg\">\n <div class=\"message-bubble\">\n <div class=\"message-content message-human\">\n {{ msg.question }}\n </div>\n </div>\n </div>\n <!-- A -->\n <div class=\"bubble-wrapper ai-msg\" *ngIf=\"msg.answers[msg.answerIndex]\">\n <div\n class=\"bubble-loading\"\n *ngIf=\"!msg.answers[msg.answerIndex].think && !msg.answers[msg.answerIndex].content\"\n >\n <div class=\"bubble-loading-dot\" *ngFor=\"let item of [1, 2, 3]\"></div>\n </div>\n <div class=\"message-bubble\">\n <!-- \u601D\u8003 -->\n <div class=\"message-think\" *ngIf=\"msg.answers[msg.answerIndex].think\">\n <div\n class=\"message-think-header\"\n (click)=\"msg.answers[msg.answerIndex].thinkExpand = !msg.answers[msg.answerIndex].thinkExpand\"\n >\n <div class=\"message-think-header-title\">\n <img src=\"/assets/images/card/think-finish.png\" />\n {{ msg.answers[msg.answerIndex].content ? '\u5DF2\u5B8C\u6210\u601D\u8003' : '\u6B63\u5728\u601D\u8003\u4E2D...' }}\n </div>\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <polyline points=\"6 9 12 15 18 9\"></polyline>\n </svg>\n </div>\n <div\n class=\"message-content message-assistant message-markdown\"\n [innerHTML]=\"msg.answers[msg.answerIndex].think | markdown\"\n [hidden]=\"!msg.answers[msg.answerIndex].thinkExpand\"\n ></div>\n </div>\n <!-- \u56DE\u7B54 -->\n <div\n class=\"message-content message-assistant message-markdown\"\n [innerHTML]=\"msg.answers[msg.answerIndex].content | markdown\"\n ></div>\n <!-- \u56DE\u7B54\u5DE5\u5177\u680F -->\n <div class=\"assistant-tools\" *ngIf=\"msg.answers[msg.answerIndex].content\">\n <ngx-simple-pagination\n [current]=\"msg.answerIndex + 1\"\n [total]=\"msg.answers.length\"\n (pageChange)=\"onPageChange($event, msg)\"\n *ngIf=\"msg.answers.length > 1\"\n ></ngx-simple-pagination>\n <img\n src=\"assets/images/card/copy.png\"\n alt=\"\u590D\u5236\"\n (click)=\"copyContent(msg.answers[msg.answerIndex].content)\"\n />\n <img\n src=\"assets/images/card/refresh.png\"\n alt=\"\u91CD\u65B0\u751F\u6210\"\n (click)=\"regenerate(msg)\"\n *ngIf=\"i === messages.length - 1\"\n />\n </div>\n </div>\n </div>\n </div>\n\n <!-- <div style=\"padding-bottom: 160px\"></div> -->\n </div>\n</div>\n", styles: [":host{width:100%;height:100%}.messages-container{width:100%;height:100%;overflow-y:auto}.message-list{height:auto;padding-top:16px;padding-bottom:var(--input-height, 210px);display:flex;flex-direction:column;align-items:center;justify-content:center;font-size:16px}.message-wrapper{display:flex;flex-direction:column;justify-content:center;align-items:center;gap:16px;width:100%;margin-bottom:16px;max-width:800px;min-width:350px}.bubble-wrapper{width:100%;display:flex;flex-direction:column;justify-content:center}.bubble-wrapper.user-msg{align-items:end}.bubble-wrapper.ai-msg{align-items:start}.bubble-loading{display:flex;align-items:center;justify-content:flex-start;height:24px;margin-top:4px;margin-bottom:16px}.bubble-loading-dot{width:6px;height:6px;border-radius:50%;margin-right:6px;background-color:#bbb;animation:.9s linear infinite loading-dot}.bubble-loading-dot:first-child{animation-delay:0s}.bubble-loading-dot:nth-child(2){animation-delay:.1s}.bubble-loading-dot:nth-child(3){animation-delay:.2s}@keyframes loading-dot{0%,to{opacity:.3}33%,66%{opacity:1}}.message-bubble{width:100%;display:flex;align-items:flex-end;flex-direction:column;gap:8px}.message-bubble .message-content.message-human{padding:8px 16px;margin-left:auto;border-radius:12px;background:#E4EDFD;color:#004ad3}.message-bubble .message-content.message-assistant{padding:0;width:100%;background:#fff}.message-bubble .message-markdown{background:#fff}.message-bubble .message-think{width:100%;padding:12px;background:#FFFFFF;border-radius:10px;border:1px solid #D9DADB;font-size:14px}.message-bubble .message-think-header{display:flex;align-items:center;justify-content:space-between;cursor:pointer;color:#000;font-size:16px}.message-bubble .message-think-header-title{display:flex;align-items:center;gap:10px}.message-bubble .message-think-header-title img{width:14px;height:14px}.message-bubble .message-think .message-content{padding-top:12px;color:#61666b}.message-bubble .message-time{font-size:12px;color:#999}.assistant-tools{width:100%;display:flex;align-items:center;gap:24px}.assistant-tools img{width:14px;height:14px;cursor:pointer}\n"], dependencies: [{ kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: SimplePaginationComponent, selector: "ngx-simple-pagination", inputs: ["current", "total"], outputs: ["pageChange"] }, { kind: "pipe", type: MarkdownPipe, name: "markdown" }] });
712
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: ChatMessagesComponent, decorators: [{
713
+ type: Component,
714
+ args: [{ selector: 'ngx-chat-messages', template: "<!-- chat-messages.component.html -->\n<script src=\"chat-messages.component.ts\"></script>\n<div class=\"messages-container\" #messagesContainer>\n <div class=\"message-list\" [style.--input-height.px]=\"inputHeight\">\n <div class=\"question message-wrapper\" *ngFor=\"let msg of messages; let i = index\">\n <!-- Q -->\n <div class=\"bubble-wrapper user-msg\">\n <div class=\"message-bubble\">\n <div class=\"message-content message-human\">\n {{ msg.question }}\n </div>\n </div>\n </div>\n <!-- A -->\n <div class=\"bubble-wrapper ai-msg\" *ngIf=\"msg.answers[msg.answerIndex]\">\n <div\n class=\"bubble-loading\"\n *ngIf=\"!msg.answers[msg.answerIndex].think && !msg.answers[msg.answerIndex].content\"\n >\n <div class=\"bubble-loading-dot\" *ngFor=\"let item of [1, 2, 3]\"></div>\n </div>\n <div class=\"message-bubble\">\n <!-- \u601D\u8003 -->\n <div class=\"message-think\" *ngIf=\"msg.answers[msg.answerIndex].think\">\n <div\n class=\"message-think-header\"\n (click)=\"msg.answers[msg.answerIndex].thinkExpand = !msg.answers[msg.answerIndex].thinkExpand\"\n >\n <div class=\"message-think-header-title\">\n <img src=\"/assets/images/card/think-finish.png\" />\n {{ msg.answers[msg.answerIndex].content ? '\u5DF2\u5B8C\u6210\u601D\u8003' : '\u6B63\u5728\u601D\u8003\u4E2D...' }}\n </div>\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <polyline points=\"6 9 12 15 18 9\"></polyline>\n </svg>\n </div>\n <div\n class=\"message-content message-assistant message-markdown\"\n [innerHTML]=\"msg.answers[msg.answerIndex].think | markdown\"\n [hidden]=\"!msg.answers[msg.answerIndex].thinkExpand\"\n ></div>\n </div>\n <!-- \u56DE\u7B54 -->\n <div\n class=\"message-content message-assistant message-markdown\"\n [innerHTML]=\"msg.answers[msg.answerIndex].content | markdown\"\n ></div>\n <!-- \u56DE\u7B54\u5DE5\u5177\u680F -->\n <div class=\"assistant-tools\" *ngIf=\"msg.answers[msg.answerIndex].content\">\n <ngx-simple-pagination\n [current]=\"msg.answerIndex + 1\"\n [total]=\"msg.answers.length\"\n (pageChange)=\"onPageChange($event, msg)\"\n *ngIf=\"msg.answers.length > 1\"\n ></ngx-simple-pagination>\n <img\n src=\"assets/images/card/copy.png\"\n alt=\"\u590D\u5236\"\n (click)=\"copyContent(msg.answers[msg.answerIndex].content)\"\n />\n <img\n src=\"assets/images/card/refresh.png\"\n alt=\"\u91CD\u65B0\u751F\u6210\"\n (click)=\"regenerate(msg)\"\n *ngIf=\"i === messages.length - 1\"\n />\n </div>\n </div>\n </div>\n </div>\n\n <!-- <div style=\"padding-bottom: 160px\"></div> -->\n </div>\n</div>\n", styles: [":host{width:100%;height:100%}.messages-container{width:100%;height:100%;overflow-y:auto}.message-list{height:auto;padding-top:16px;padding-bottom:var(--input-height, 210px);display:flex;flex-direction:column;align-items:center;justify-content:center;font-size:16px}.message-wrapper{display:flex;flex-direction:column;justify-content:center;align-items:center;gap:16px;width:100%;margin-bottom:16px;max-width:800px;min-width:350px}.bubble-wrapper{width:100%;display:flex;flex-direction:column;justify-content:center}.bubble-wrapper.user-msg{align-items:end}.bubble-wrapper.ai-msg{align-items:start}.bubble-loading{display:flex;align-items:center;justify-content:flex-start;height:24px;margin-top:4px;margin-bottom:16px}.bubble-loading-dot{width:6px;height:6px;border-radius:50%;margin-right:6px;background-color:#bbb;animation:.9s linear infinite loading-dot}.bubble-loading-dot:first-child{animation-delay:0s}.bubble-loading-dot:nth-child(2){animation-delay:.1s}.bubble-loading-dot:nth-child(3){animation-delay:.2s}@keyframes loading-dot{0%,to{opacity:.3}33%,66%{opacity:1}}.message-bubble{width:100%;display:flex;align-items:flex-end;flex-direction:column;gap:8px}.message-bubble .message-content.message-human{padding:8px 16px;margin-left:auto;border-radius:12px;background:#E4EDFD;color:#004ad3}.message-bubble .message-content.message-assistant{padding:0;width:100%;background:#fff}.message-bubble .message-markdown{background:#fff}.message-bubble .message-think{width:100%;padding:12px;background:#FFFFFF;border-radius:10px;border:1px solid #D9DADB;font-size:14px}.message-bubble .message-think-header{display:flex;align-items:center;justify-content:space-between;cursor:pointer;color:#000;font-size:16px}.message-bubble .message-think-header-title{display:flex;align-items:center;gap:10px}.message-bubble .message-think-header-title img{width:14px;height:14px}.message-bubble .message-think .message-content{padding-top:12px;color:#61666b}.message-bubble .message-time{font-size:12px;color:#999}.assistant-tools{width:100%;display:flex;align-items:center;gap:24px}.assistant-tools img{width:14px;height:14px;cursor:pointer}\n"] }]
715
+ }], propDecorators: { messages: [{
716
+ type: Input
717
+ }], messagesContainer: [{
718
+ type: ViewChild,
719
+ args: ['messagesContainer']
720
+ }], regenerateAnswer: [{
721
+ type: Output
722
+ }] } });
723
+
724
+ class HttpService {
725
+ constructor() {
726
+ this.http = inject(HttpClient);
727
+ this.storeService = inject(StoreService);
728
+ this.BASE_URL = '/api'; // 或者完整域名
729
+ }
730
+ /**
731
+ * 统一获取 Headers
732
+ */
733
+ getHeaders() {
734
+ return new HttpHeaders({
735
+ 'Content-Type': 'application/json',
736
+ Authorization: `${this.storeService.getToken()}`,
737
+ });
738
+ }
739
+ /**
740
+ * GET 请求
741
+ */
742
+ get(url, params) {
743
+ return this.http.get(this.formatUrl(url), {
744
+ headers: this.getHeaders(),
745
+ params: this.resolveParams(params),
746
+ });
747
+ }
748
+ /**
749
+ * POST 请求
750
+ */
751
+ post(url, body) {
752
+ return this.http.post(this.formatUrl(url), body, {
753
+ headers: this.getHeaders(),
754
+ });
755
+ }
756
+ /**
757
+ * PUT 请求
758
+ */
759
+ put(url, body) {
760
+ return this.http.put(this.formatUrl(url), body, {
761
+ headers: this.getHeaders(),
762
+ });
763
+ }
764
+ /**
765
+ * DELETE 请求
766
+ * 兼容支持:既支持 URL 参数,也支持 Body
767
+ */
768
+ delete(url, params, body) {
769
+ return this.http.request('DELETE', this.formatUrl(url), {
770
+ headers: this.getHeaders(),
771
+ params: this.resolveParams(params),
772
+ body: body, // HttpClient 的 delete() 方法默认不支持 body,需用 request() 通用方法
773
+ });
774
+ }
775
+ // ================= 私有辅助方法 =================
776
+ /**
777
+ * 统一处理 URL
778
+ * 比如自动拼接 BaseUrl,或者处理斜杠
779
+ */
780
+ formatUrl(url) {
781
+ if (url.startsWith('http'))
782
+ return url;
783
+ // 确保拼接顺滑
784
+ const baseUrl = this.storeService.getBaseUrl();
785
+ const cleanBase = baseUrl.replace(/\/$/, '');
786
+ const cleanUrl = url.startsWith('/') ? url : `/${url}`;
787
+ return `${cleanBase}${cleanUrl}`;
788
+ }
789
+ /**
790
+ * 统一清洗参数
791
+ * 过滤掉 null 和 undefined,防止传给后端 "null" 字符串
792
+ */
793
+ resolveParams(params) {
794
+ let httpParams = new HttpParams();
795
+ if (params) {
796
+ Object.keys(params).forEach(key => {
797
+ const value = params[key];
798
+ if (value !== null && value !== undefined) {
799
+ if (Array.isArray(value)) {
800
+ // 支持数组参数 ids: [1, 2] => ids=1&ids=2
801
+ value.forEach(val => (httpParams = httpParams.append(key, val)));
802
+ }
803
+ else {
804
+ httpParams = httpParams.set(key, value);
805
+ }
806
+ }
807
+ });
808
+ }
809
+ return httpParams;
810
+ }
811
+ }
812
+ HttpService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: HttpService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
813
+ HttpService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: HttpService, providedIn: 'root' });
814
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: HttpService, decorators: [{
815
+ type: Injectable,
816
+ args: [{
817
+ providedIn: 'root',
818
+ }]
819
+ }] });
820
+
821
+ class HistoryGroupComponent {
822
+ constructor() {
823
+ this.select = new EventEmitter();
824
+ this.refresh = new EventEmitter();
825
+ this.currentSelected = null;
826
+ this.storeService = inject(StoreService);
827
+ this.modalService = inject(NzModalService);
828
+ this.msg = inject(NzMessageService);
829
+ this.http = inject(HttpService);
830
+ }
831
+ toggleExpand() {
832
+ this.group.expanded = !this.group.expanded;
833
+ }
834
+ selectConversation(item) {
835
+ if (item) {
836
+ item.isActive = true;
837
+ }
838
+ this.currentSelected = item;
839
+ this.select.emit(this.currentSelected);
840
+ this.storeService.setCurrentConversation(this.currentSelected);
841
+ }
842
+ deleteConversation(e, item) {
843
+ e.stopPropagation();
844
+ this.modalService.confirm({
845
+ nzTitle: '删除对话',
846
+ nzContent: `删除后无法恢复,是否确认删除“${item.name}”对话?`,
847
+ nzOkText: '确定',
848
+ nzCancelText: '取消',
849
+ nzOnOk: () => {
850
+ this.http.delete(`/api/ChatModel/conversation`, { iCode: item.iCode }).subscribe(res => {
851
+ this.msg.success('删除成功');
852
+ this.refresh.emit(item.iCode);
853
+ });
854
+ },
855
+ });
856
+ }
857
+ }
858
+ HistoryGroupComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: HistoryGroupComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
859
+ HistoryGroupComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.10", type: HistoryGroupComponent, selector: "history-group", inputs: { group: "group" }, outputs: { select: "select", refresh: "refresh" }, ngImport: i0, template: `
860
+ <div class="group-container">
861
+ <div class="group-header" (click)="toggleExpand()">
862
+ <div class="left">
863
+ <img src="assets/images/sidebar/time.png" />
864
+ <span class="date-text">{{ group.dateLabel }}</span>
865
+ </div>
866
+ <div class="arrow-icon" [class.rotated]="group.expanded">
867
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
868
+ <polyline points="6 9 12 15 18 9"></polyline>
869
+ </svg>
870
+ </div>
871
+ </div>
872
+
873
+ <div class="group-content" [@expandCollapse]="group.expanded ? 'expanded' : 'collapsed'">
874
+ <div
875
+ *ngFor="let item of group.items"
876
+ class="history-item"
877
+ [class.active]="item.isActive"
878
+ (click)="selectConversation(item)"
879
+ >
880
+ <span class="item-title">{{ item.name }}</span>
881
+
882
+ <img class="delete-img" src="assets/images/sidebar/delete.png" (click)="deleteConversation($event, item)" />
883
+ </div>
884
+ </div>
885
+ </div>
886
+ `, isInline: true, styles: ["@charset \"UTF-8\";.group-header{display:flex;justify-content:space-between;align-items:center;padding:12px 0;cursor:pointer;color:#999;font-size:14px;-webkit-user-select:none;user-select:none}.group-header:hover{color:#666}.left{display:flex;align-items:center;gap:8px}.left img{width:14px;height:14px}.arrow-icon{transition:transform .3s ease}.arrow-icon.rotated{transform:rotate(0)}.arrow-icon:not(.rotated){transform:rotate(-90deg)}.group-content{overflow:hidden}.history-item{padding:10px 12px;margin-bottom:4px;border-radius:8px;font-size:14px;color:#444;cursor:pointer;display:flex;justify-content:space-between;align-items:center;transition:background-color .2s}.history-item .delete-img{width:14px;height:14px;display:none}.history-item:hover{background-color:#ebeced}.history-item:hover:hover .delete-img{display:block}.history-item.active{background-color:#e4edfd;color:#004ad3}.item-title{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;flex:1}\n"], dependencies: [{ kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }], animations: [
887
+ trigger('expandCollapse', [
888
+ state('collapsed', style({ height: '0px', opacity: 0 })),
889
+ state('expanded', style({ height: '*', opacity: 1 })),
890
+ transition('expanded <=> collapsed', animate('300ms cubic-bezier(0.4, 0.0, 0.2, 1)')),
891
+ ]),
892
+ ] });
893
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: HistoryGroupComponent, decorators: [{
894
+ type: Component,
895
+ args: [{ selector: 'history-group', template: `
896
+ <div class="group-container">
897
+ <div class="group-header" (click)="toggleExpand()">
898
+ <div class="left">
899
+ <img src="assets/images/sidebar/time.png" />
900
+ <span class="date-text">{{ group.dateLabel }}</span>
901
+ </div>
902
+ <div class="arrow-icon" [class.rotated]="group.expanded">
903
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
904
+ <polyline points="6 9 12 15 18 9"></polyline>
905
+ </svg>
906
+ </div>
907
+ </div>
908
+
909
+ <div class="group-content" [@expandCollapse]="group.expanded ? 'expanded' : 'collapsed'">
910
+ <div
911
+ *ngFor="let item of group.items"
912
+ class="history-item"
913
+ [class.active]="item.isActive"
914
+ (click)="selectConversation(item)"
915
+ >
916
+ <span class="item-title">{{ item.name }}</span>
917
+
918
+ <img class="delete-img" src="assets/images/sidebar/delete.png" (click)="deleteConversation($event, item)" />
919
+ </div>
920
+ </div>
921
+ </div>
922
+ `, animations: [
923
+ trigger('expandCollapse', [
924
+ state('collapsed', style({ height: '0px', opacity: 0 })),
925
+ state('expanded', style({ height: '*', opacity: 1 })),
926
+ transition('expanded <=> collapsed', animate('300ms cubic-bezier(0.4, 0.0, 0.2, 1)')),
927
+ ]),
928
+ ], styles: ["@charset \"UTF-8\";.group-header{display:flex;justify-content:space-between;align-items:center;padding:12px 0;cursor:pointer;color:#999;font-size:14px;-webkit-user-select:none;user-select:none}.group-header:hover{color:#666}.left{display:flex;align-items:center;gap:8px}.left img{width:14px;height:14px}.arrow-icon{transition:transform .3s ease}.arrow-icon.rotated{transform:rotate(0)}.arrow-icon:not(.rotated){transform:rotate(-90deg)}.group-content{overflow:hidden}.history-item{padding:10px 12px;margin-bottom:4px;border-radius:8px;font-size:14px;color:#444;cursor:pointer;display:flex;justify-content:space-between;align-items:center;transition:background-color .2s}.history-item .delete-img{width:14px;height:14px;display:none}.history-item:hover{background-color:#ebeced}.history-item:hover:hover .delete-img{display:block}.history-item.active{background-color:#e4edfd;color:#004ad3}.item-title{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;flex:1}\n"] }]
929
+ }], propDecorators: { group: [{
930
+ type: Input
931
+ }], select: [{
932
+ type: Output
933
+ }], refresh: [{
934
+ type: Output
935
+ }] } });
936
+
937
+ // chat-sidebar.component.ts
938
+ class ChatSidebarComponent {
939
+ constructor() {
940
+ this.http = inject(HttpService$1);
941
+ this.storeService = inject(StoreService);
942
+ this.isCollapsed = false;
943
+ this.historyGroups = [];
944
+ }
945
+ ngOnInit() {
946
+ this.getHistory();
947
+ }
948
+ // 获取历史对话
949
+ getHistory(needSelectFirst = false) {
950
+ this.http.get(`${this.storeService.getBaseUrl()}/api/ChatModel/conversation`).subscribe((res) => {
951
+ var _a;
952
+ this.historyGroups = this.groupByFriendlyTime(res);
953
+ // 选中第一个对话
954
+ if (needSelectFirst) {
955
+ if ((_a = this.historyGroups[0]) === null || _a === void 0 ? void 0 : _a.items[0]) {
956
+ this.historyGroupRef.selectConversation(this.historyGroups[0].items[0]);
957
+ }
958
+ else {
959
+ this.historyGroupRef.selectConversation(null);
960
+ }
961
+ }
962
+ });
963
+ }
964
+ // 删除后刷新,若删除当前选中会话
965
+ refresh(conversationICode) {
966
+ const needSelectFirst = this.storeService.getConversationICode() === conversationICode;
967
+ this.getHistory(needSelectFirst);
968
+ }
969
+ selectConversation(conversation) {
970
+ // 取消勾选其他会话
971
+ this.historyGroups.forEach(group => {
972
+ group.items.forEach(item => {
973
+ if (!conversation || conversation.iCode !== item.iCode) {
974
+ item.isActive = false;
975
+ }
976
+ });
977
+ });
978
+ }
979
+ toggleSidebar() {
980
+ this.isCollapsed = !this.isCollapsed;
981
+ }
982
+ openNewChat() {
983
+ this.historyGroupRef.selectConversation(null);
984
+ }
985
+ groupByFriendlyTime(dataList) {
986
+ if (!dataList || dataList.length === 0) {
987
+ return [];
988
+ }
989
+ const list = [];
990
+ const now = new Date();
991
+ const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
992
+ const yesterday = new Date(today);
993
+ yesterday.setDate(yesterday.getDate() - 1);
994
+ dataList.forEach((item, index) => {
995
+ const itemTime = new Date(item.time);
996
+ const itemDate = new Date(itemTime.getFullYear(), itemTime.getMonth(), itemTime.getDate());
997
+ let dateLabel = '';
998
+ item.isActive = false;
999
+ if (itemDate.getTime() === today.getTime()) {
1000
+ dateLabel = '今天';
1001
+ }
1002
+ else if (itemDate.getTime() === yesterday.getTime()) {
1003
+ dateLabel = '昨天';
1004
+ }
1005
+ else {
1006
+ // 其他日期显示具体的年月日
1007
+ dateLabel = `${itemTime.getFullYear()}年${(itemTime.getMonth() + 1).toString().padStart(2, '0')}月${itemTime.getDate().toString().padStart(2, '0')}日`;
1008
+ }
1009
+ const find = list.find(item => item.dateLabel === dateLabel);
1010
+ if (find) {
1011
+ find.items.push(item);
1012
+ }
1013
+ else {
1014
+ const group = { dateLabel: '', expanded: index === 0, items: [] };
1015
+ group.dateLabel = dateLabel;
1016
+ group.items.push(item);
1017
+ list.push(group);
1018
+ }
1019
+ });
1020
+ return list;
1021
+ }
1022
+ }
1023
+ ChatSidebarComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: ChatSidebarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1024
+ ChatSidebarComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.10", type: ChatSidebarComponent, selector: "ngx-chat-sidebar", viewQueries: [{ propertyName: "historyGroupRef", first: true, predicate: ["historyGroupRef"], descendants: true }], ngImport: i0, template: "<div class=\"sidebar\" [class.collapsed]=\"isCollapsed\">\n <div class=\"sidebar-inner\">\n <div class=\"sidebar-header\">\n <div class=\"logo-area\">\n <img src=\"assets/images/logo.png\" alt=\"Logo\" class=\"logo-img\" />\n <span class=\"app-name\">\u9752\u6E05\u6C34\u5229</span>\n </div>\n <button class=\"toggle-btn\" (click)=\"toggleSidebar()\">\n <img src=\"assets/images/sidebar/collapse.png\" />\n </button>\n </div>\n\n <div class=\"sidebar-content\">\n <button class=\"new-chat-btn\" (click)=\"openNewChat()\">\n <img src=\"assets/images/sidebar/new-chat.png\" />\n \u5F00\u542F\u65B0\u5BF9\u8BDD\n </button>\n\n <div class=\"divider\"></div>\n\n <div class=\"section-title\">\n <img src=\"assets/images/sidebar/history-chat.png\" />\n <span>\u5386\u53F2\u5BF9\u8BDD</span>\n </div>\n\n <div class=\"history-list-scroll\" *ngIf=\"historyGroups.length\">\n <history-group\n #historyGroupRef\n *ngFor=\"let group of historyGroups\"\n [group]=\"group\"\n (select)=\"selectConversation($event)\"\n (refresh)=\"refresh($event)\"\n >\n </history-group>\n </div>\n\n <div class=\"history-empty\" *ngIf=\"!historyGroups.length\">\u6682\u65E0\u5386\u53F2\u5BF9\u8BDD</div>\n </div>\n </div>\n</div>\n\n<div class=\"logo-actions\" [@logoActionsAnimation]=\"isCollapsed ? 'leave' : 'enter'\" *ngIf=\"isCollapsed\">\n <img src=\"assets/images/logo.png\" alt=\"Logo\" class=\"logo-img\" />\n <div class=\"left-top-bar\">\n <img src=\"assets/images/sidebar/expand.png\" (click)=\"toggleSidebar()\" />\n <img src=\"assets/images/sidebar/new-chat.png\" (click)=\"openNewChat()\" />\n </div>\n</div>\n", styles: ["@charset \"UTF-8\";:host{display:block;height:100vh}.sidebar{width:280px;height:100%;background-color:#f3f4f6;border-right:1px solid #e0e0e0;display:flex;flex-direction:column;transition:width .4s cubic-bezier(.25,.8,.25,1),border .4s;overflow:hidden;box-sizing:border-box}.sidebar.collapsed{width:0;padding:0;border-right:0 solid transparent}.sidebar-inner{width:280px;height:100%;display:flex;flex-direction:column;padding:16px;box-sizing:border-box}.sidebar-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:20px;height:32px}.logo-area{display:flex;align-items:center;gap:8px;white-space:nowrap}.logo-img{width:28px;height:28px;object-fit:contain}.app-name{font-weight:600;font-size:16px;color:#000}.toggle-btn{background:none;border:none;cursor:pointer;color:#666;padding:4px;border-radius:4px}.toggle-btn img{width:16px;height:15px}.toggle-btn:hover{background-color:#0000000d}.sidebar-content{flex:1;display:flex;flex-direction:column;overflow:hidden}.new-chat-btn{width:100%;background-color:#fff;border:1px solid #e0e0e0;border-radius:20px;padding:10px;color:#333;font-weight:500;cursor:pointer;display:flex;align-items:center;justify-content:center;gap:8px;box-shadow:0 2px 5px #00000008;transition:all .2s;white-space:nowrap}.new-chat-btn img{width:16px;height:16px}.new-chat-btn:hover{box-shadow:0 4px 8px #00000014;border-color:#d0d0d0}.divider{height:1px;background-color:#e0e0e0;margin:20px 0}.section-title{display:flex;align-items:center;gap:8px;font-weight:600;color:#333;margin-bottom:10px;white-space:nowrap}.section-title img{width:16px;height:16px}.history-list-scroll{flex:1;overflow-y:auto;overflow-x:hidden;padding-right:4px}.history-empty{flex:1;display:flex;align-items:center;justify-content:center}.logo-actions{position:absolute;left:12px;top:12px;display:flex;align-items:center;gap:8px;z-index:99}.logo-actions .logo-img{width:40px;height:32px}.logo-actions .left-top-bar{width:75px;height:40px;display:flex;align-items:center;justify-content:center;gap:19px;background:#FFFFFF;box-shadow:0 2px 4px #0000001a;border-radius:20px;border:1px solid #EEEEEE}.logo-actions .left-top-bar img{width:16px;height:16px;cursor:pointer}\n"], dependencies: [{ kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: HistoryGroupComponent, selector: "history-group", inputs: ["group"], outputs: ["select", "refresh"] }], animations: [
1025
+ trigger('logoActionsAnimation', [
1026
+ transition(':enter', [
1027
+ style({ opacity: 0, transform: 'translateX(20px)' }),
1028
+ animate('600ms ease-out', style({ opacity: 1, transform: 'translateX(0)' })),
1029
+ ]),
1030
+ transition(':leave', [animate('0ms ease-in', style({ opacity: 0, transform: 'translateX(20px)' }))]),
1031
+ ]),
1032
+ ] });
1033
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: ChatSidebarComponent, decorators: [{
1034
+ type: Component,
1035
+ args: [{ selector: 'ngx-chat-sidebar', animations: [
1036
+ trigger('logoActionsAnimation', [
1037
+ transition(':enter', [
1038
+ style({ opacity: 0, transform: 'translateX(20px)' }),
1039
+ animate('600ms ease-out', style({ opacity: 1, transform: 'translateX(0)' })),
1040
+ ]),
1041
+ transition(':leave', [animate('0ms ease-in', style({ opacity: 0, transform: 'translateX(20px)' }))]),
1042
+ ]),
1043
+ ], template: "<div class=\"sidebar\" [class.collapsed]=\"isCollapsed\">\n <div class=\"sidebar-inner\">\n <div class=\"sidebar-header\">\n <div class=\"logo-area\">\n <img src=\"assets/images/logo.png\" alt=\"Logo\" class=\"logo-img\" />\n <span class=\"app-name\">\u9752\u6E05\u6C34\u5229</span>\n </div>\n <button class=\"toggle-btn\" (click)=\"toggleSidebar()\">\n <img src=\"assets/images/sidebar/collapse.png\" />\n </button>\n </div>\n\n <div class=\"sidebar-content\">\n <button class=\"new-chat-btn\" (click)=\"openNewChat()\">\n <img src=\"assets/images/sidebar/new-chat.png\" />\n \u5F00\u542F\u65B0\u5BF9\u8BDD\n </button>\n\n <div class=\"divider\"></div>\n\n <div class=\"section-title\">\n <img src=\"assets/images/sidebar/history-chat.png\" />\n <span>\u5386\u53F2\u5BF9\u8BDD</span>\n </div>\n\n <div class=\"history-list-scroll\" *ngIf=\"historyGroups.length\">\n <history-group\n #historyGroupRef\n *ngFor=\"let group of historyGroups\"\n [group]=\"group\"\n (select)=\"selectConversation($event)\"\n (refresh)=\"refresh($event)\"\n >\n </history-group>\n </div>\n\n <div class=\"history-empty\" *ngIf=\"!historyGroups.length\">\u6682\u65E0\u5386\u53F2\u5BF9\u8BDD</div>\n </div>\n </div>\n</div>\n\n<div class=\"logo-actions\" [@logoActionsAnimation]=\"isCollapsed ? 'leave' : 'enter'\" *ngIf=\"isCollapsed\">\n <img src=\"assets/images/logo.png\" alt=\"Logo\" class=\"logo-img\" />\n <div class=\"left-top-bar\">\n <img src=\"assets/images/sidebar/expand.png\" (click)=\"toggleSidebar()\" />\n <img src=\"assets/images/sidebar/new-chat.png\" (click)=\"openNewChat()\" />\n </div>\n</div>\n", styles: ["@charset \"UTF-8\";:host{display:block;height:100vh}.sidebar{width:280px;height:100%;background-color:#f3f4f6;border-right:1px solid #e0e0e0;display:flex;flex-direction:column;transition:width .4s cubic-bezier(.25,.8,.25,1),border .4s;overflow:hidden;box-sizing:border-box}.sidebar.collapsed{width:0;padding:0;border-right:0 solid transparent}.sidebar-inner{width:280px;height:100%;display:flex;flex-direction:column;padding:16px;box-sizing:border-box}.sidebar-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:20px;height:32px}.logo-area{display:flex;align-items:center;gap:8px;white-space:nowrap}.logo-img{width:28px;height:28px;object-fit:contain}.app-name{font-weight:600;font-size:16px;color:#000}.toggle-btn{background:none;border:none;cursor:pointer;color:#666;padding:4px;border-radius:4px}.toggle-btn img{width:16px;height:15px}.toggle-btn:hover{background-color:#0000000d}.sidebar-content{flex:1;display:flex;flex-direction:column;overflow:hidden}.new-chat-btn{width:100%;background-color:#fff;border:1px solid #e0e0e0;border-radius:20px;padding:10px;color:#333;font-weight:500;cursor:pointer;display:flex;align-items:center;justify-content:center;gap:8px;box-shadow:0 2px 5px #00000008;transition:all .2s;white-space:nowrap}.new-chat-btn img{width:16px;height:16px}.new-chat-btn:hover{box-shadow:0 4px 8px #00000014;border-color:#d0d0d0}.divider{height:1px;background-color:#e0e0e0;margin:20px 0}.section-title{display:flex;align-items:center;gap:8px;font-weight:600;color:#333;margin-bottom:10px;white-space:nowrap}.section-title img{width:16px;height:16px}.history-list-scroll{flex:1;overflow-y:auto;overflow-x:hidden;padding-right:4px}.history-empty{flex:1;display:flex;align-items:center;justify-content:center}.logo-actions{position:absolute;left:12px;top:12px;display:flex;align-items:center;gap:8px;z-index:99}.logo-actions .logo-img{width:40px;height:32px}.logo-actions .left-top-bar{width:75px;height:40px;display:flex;align-items:center;justify-content:center;gap:19px;background:#FFFFFF;box-shadow:0 2px 4px #0000001a;border-radius:20px;border:1px solid #EEEEEE}.logo-actions .left-top-bar img{width:16px;height:16px;cursor:pointer}\n"] }]
1044
+ }], propDecorators: { historyGroupRef: [{
1045
+ type: ViewChild,
1046
+ args: ['historyGroupRef']
1047
+ }] } });
1048
+
1049
+ class NgxAgentChatComponent {
1050
+ constructor() {
1051
+ /**
1052
+ * 接口baseUrl配置
1053
+ */
1054
+ this.baseUrl = 'http://39.102.56.254:8082';
1055
+ this.token = '';
1056
+ this.md = inject(MarkdownStreamService);
1057
+ this.httpService = inject(HttpService$1);
1058
+ this.sseService = inject(SseService);
1059
+ this.storeService = inject(StoreService);
1060
+ this.sanitizer = inject(DomSanitizer);
1061
+ this.ngZone = inject(NgZone);
1062
+ this.messages = []; // 消息数组
1063
+ }
1064
+ ngOnInit() {
1065
+ this.storeService.setToken(this.token);
1066
+ this.storeService.setBaseUrl(this.baseUrl);
1067
+ this.sseService.conversationICode$.subscribe(iCodeObj => {
1068
+ if (iCodeObj) {
1069
+ const currentMsg = this.messages[this.messages.length - 1];
1070
+ currentMsg.iCode = iCodeObj.question;
1071
+ currentMsg.answers[currentMsg.answerIndex].iCode = iCodeObj.answer;
1072
+ }
1073
+ });
1074
+ this.sseService.thinking$.subscribe(text => {
1075
+ if (this.messages.length > 0) {
1076
+ const currentMsg = this.messages[this.messages.length - 1];
1077
+ currentMsg.answers[currentMsg.answerIndex].think = text;
1078
+ }
1079
+ });
1080
+ this.sseService.currentMessage$.subscribe(text => {
1081
+ if (this.messages.length > 0) {
1082
+ const currentMsg = this.messages[this.messages.length - 1];
1083
+ currentMsg.answers[currentMsg.answerIndex].content = text;
1084
+ }
1085
+ });
1086
+ this.storeService.currentConversation$.subscribe(conversation => {
1087
+ if (!conversation) {
1088
+ this.messages = [];
1089
+ return;
1090
+ }
1091
+ this.httpService
1092
+ .get(`${this.storeService.getBaseUrl()}/api/ChatModel/conversation/questions`, { iCode: conversation === null || conversation === void 0 ? void 0 : conversation.iCode })
1093
+ .subscribe((res) => {
1094
+ this.messages = res.map(item => {
1095
+ item.answerIndex = 0;
1096
+ item.answers.forEach(answer => {
1097
+ answer.thinkExpand = true;
1098
+ });
1099
+ return item;
1100
+ });
1101
+ });
1102
+ });
1103
+ }
1104
+ ngAfterViewInit() {
1105
+ this.resizeObserver = new ResizeObserver(entries => {
1106
+ for (let entry of entries) {
1107
+ const newHeight = entry.contentRect.height;
1108
+ this.ngZone.run(() => {
1109
+ this.chatMessagesRef.inputHeight = newHeight + 60;
1110
+ });
1111
+ }
1112
+ });
1113
+ // 开始监听输入框元素
1114
+ this.resizeObserver.observe(this.chatInputRef.chatInputWrapper.nativeElement);
1115
+ }
1116
+ // 重新生成回答(仅最新的回答允许重新生成)
1117
+ regenerateAnswer(msg) {
1118
+ this.chatInputRef.regenerateSend(msg);
1119
+ }
1120
+ // 发起提问请求
1121
+ onSendMessage(event) {
1122
+ if (!event.isRegenerate) {
1123
+ // 推入一个新问答,iCode在流接收到补上
1124
+ const qa = {
1125
+ iCode: '',
1126
+ question: event.message.question,
1127
+ answers: [
1128
+ {
1129
+ iCode: '',
1130
+ think: '',
1131
+ content: '',
1132
+ thinkExpand: true,
1133
+ },
1134
+ ],
1135
+ answerIndex: 0,
1136
+ };
1137
+ this.messages.push(qa);
1138
+ }
1139
+ else {
1140
+ const latestMessage = this.messages[this.messages.length - 1];
1141
+ latestMessage.answers.push({
1142
+ iCode: '',
1143
+ think: '',
1144
+ content: '',
1145
+ thinkExpand: true,
1146
+ });
1147
+ latestMessage.answerIndex = latestMessage.answers.length - 1;
1148
+ }
1149
+ this.sseService.streamChat(event.message).then(res => {
1150
+ // 开启新对话,刷新左侧历史对话
1151
+ if (!event.message.conversation) {
1152
+ this.chatSidebarRef.getHistory(true);
1153
+ }
1154
+ });
1155
+ }
1156
+ }
1157
+ NgxAgentChatComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: NgxAgentChatComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1158
+ NgxAgentChatComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.10", type: NgxAgentChatComponent, selector: "ngx-agent-chat", inputs: { baseUrl: "baseUrl", token: "token" }, viewQueries: [{ propertyName: "chatSidebarRef", first: true, predicate: ["chatSidebarRef"], descendants: true }, { propertyName: "chatMessagesRef", first: true, predicate: ["chatMessagesRef"], descendants: true }, { propertyName: "chatInputRef", first: true, predicate: ["chatInputRef"], descendants: true }], ngImport: i0, template: "<div class=\"qq-agent\">\n <!-- \u5DE6\u4FA7\u8FB9\u680F -->\n <ngx-chat-sidebar #chatSidebarRef></ngx-chat-sidebar>\n\n <!-- \u53F3\u4FA7\u4E3B\u5185\u5BB9 -->\n <div class=\"agent-main\">\n <ngx-chat-messages #chatMessagesRef [messages]=\"messages\" (regenerateAnswer)=\"regenerateAnswer($event)\"></ngx-chat-messages>\n <ngx-chat-input #chatInputRef [messages]=\"messages\" (sendMessage)=\"onSendMessage($event)\"></ngx-chat-input>\n </div>\n</div>", styles: [":host{width:100%;height:100%}:host ::ng-deep ::-webkit-scrollbar{width:6px}:host ::ng-deep ::-webkit-scrollbar-track{background:transparent}:host ::ng-deep ::-webkit-scrollbar-thumb{background:#d1d1d1;border-radius:8px}:host ::ng-deep ::-webkit-scrollbar-thumb:hover{background:#b0b0b0}.qq-agent{width:100%;height:100%;display:flex;overflow:hidden}.qq-agent .agent-left{width:280px;height:100%;background:#F3F4F6}.qq-agent .agent-main{position:relative;width:100%;height:100%;display:flex;flex-direction:column;justify-content:space-between;align-items:center}\n"], dependencies: [{ kind: "component", type: ChatInputComponent, selector: "ngx-chat-input", inputs: ["messages"], outputs: ["sendMessage", "messageChange", "enter", "paste"] }, { kind: "component", type: ChatMessagesComponent, selector: "ngx-chat-messages", inputs: ["messages"], outputs: ["regenerateAnswer"] }, { kind: "component", type: ChatSidebarComponent, selector: "ngx-chat-sidebar" }] });
1159
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: NgxAgentChatComponent, decorators: [{
1160
+ type: Component,
1161
+ args: [{ selector: 'ngx-agent-chat', template: "<div class=\"qq-agent\">\n <!-- \u5DE6\u4FA7\u8FB9\u680F -->\n <ngx-chat-sidebar #chatSidebarRef></ngx-chat-sidebar>\n\n <!-- \u53F3\u4FA7\u4E3B\u5185\u5BB9 -->\n <div class=\"agent-main\">\n <ngx-chat-messages #chatMessagesRef [messages]=\"messages\" (regenerateAnswer)=\"regenerateAnswer($event)\"></ngx-chat-messages>\n <ngx-chat-input #chatInputRef [messages]=\"messages\" (sendMessage)=\"onSendMessage($event)\"></ngx-chat-input>\n </div>\n</div>", styles: [":host{width:100%;height:100%}:host ::ng-deep ::-webkit-scrollbar{width:6px}:host ::ng-deep ::-webkit-scrollbar-track{background:transparent}:host ::ng-deep ::-webkit-scrollbar-thumb{background:#d1d1d1;border-radius:8px}:host ::ng-deep ::-webkit-scrollbar-thumb:hover{background:#b0b0b0}.qq-agent{width:100%;height:100%;display:flex;overflow:hidden}.qq-agent .agent-left{width:280px;height:100%;background:#F3F4F6}.qq-agent .agent-main{position:relative;width:100%;height:100%;display:flex;flex-direction:column;justify-content:space-between;align-items:center}\n"] }]
1162
+ }], propDecorators: { baseUrl: [{
1163
+ type: Input
1164
+ }], token: [{
1165
+ type: Input
1166
+ }], chatSidebarRef: [{
1167
+ type: ViewChild,
1168
+ args: ['chatSidebarRef']
1169
+ }], chatMessagesRef: [{
1170
+ type: ViewChild,
1171
+ args: ['chatMessagesRef']
1172
+ }], chatInputRef: [{
1173
+ type: ViewChild,
1174
+ args: ['chatInputRef']
1175
+ }] } });
1176
+
1177
+ class AgentChatModule {
1178
+ }
1179
+ AgentChatModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: AgentChatModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
1180
+ AgentChatModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "15.2.10", ngImport: i0, type: AgentChatModule, declarations: [NgxAgentChatComponent,
1181
+ ChatInputComponent,
1182
+ ChatMessagesComponent,
1183
+ ChatSidebarComponent,
1184
+ SimplePaginationComponent,
1185
+ HistoryGroupComponent], imports: [CommonModule,
1186
+ NzAvatarModule,
1187
+ NzSpinModule,
1188
+ NzTagModule,
1189
+ NzInputModule,
1190
+ NzButtonModule,
1191
+ NzListModule,
1192
+ NzSliderModule,
1193
+ NzLayoutModule,
1194
+ NzPopoverModule,
1195
+ NzRadioModule,
1196
+ NzCheckboxModule,
1197
+ NzIconModule,
1198
+ NzPaginationModule,
1199
+ FormsModule,
1200
+ MarkdownPipe], exports: [NgxAgentChatComponent] });
1201
+ AgentChatModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: AgentChatModule, providers: [NzMessageService], imports: [CommonModule,
1202
+ NzAvatarModule,
1203
+ NzSpinModule,
1204
+ NzTagModule,
1205
+ NzInputModule,
1206
+ NzButtonModule,
1207
+ NzListModule,
1208
+ NzSliderModule,
1209
+ NzLayoutModule,
1210
+ NzPopoverModule,
1211
+ NzRadioModule,
1212
+ NzCheckboxModule,
1213
+ NzIconModule,
1214
+ NzPaginationModule,
1215
+ FormsModule] });
1216
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: AgentChatModule, decorators: [{
1217
+ type: NgModule,
1218
+ args: [{
1219
+ declarations: [
1220
+ NgxAgentChatComponent,
1221
+ ChatInputComponent,
1222
+ ChatMessagesComponent,
1223
+ ChatSidebarComponent,
1224
+ SimplePaginationComponent,
1225
+ HistoryGroupComponent,
1226
+ ],
1227
+ imports: [
1228
+ CommonModule,
1229
+ NzAvatarModule,
1230
+ NzSpinModule,
1231
+ NzTagModule,
1232
+ NzInputModule,
1233
+ NzButtonModule,
1234
+ NzListModule,
1235
+ NzSliderModule,
1236
+ NzLayoutModule,
1237
+ NzPopoverModule,
1238
+ NzRadioModule,
1239
+ NzCheckboxModule,
1240
+ NzIconModule,
1241
+ NzPaginationModule,
1242
+ FormsModule,
1243
+ MarkdownPipe,
1244
+ ],
1245
+ providers: [NzMessageService],
1246
+ exports: [NgxAgentChatComponent],
1247
+ }]
1248
+ }] });
1249
+
1250
+ /**
1251
+ * Generated bundle index. Do not edit.
1252
+ */
1253
+
1254
+ export { AgentChatModule, NgxAgentChatComponent };
1255
+ //# sourceMappingURL=qqsl-agent-chat.mjs.map