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,1248 @@
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 { DomSanitizer } from '@angular/platform-browser';
13
+ import * as i2 from 'ng-zorro-antd/core/transition-patch';
14
+ import * as i3 from 'ng-zorro-antd/popover';
15
+ import { NzPopoverModule } from 'ng-zorro-antd/popover';
16
+ import * as i4 from 'ng-zorro-antd/radio';
17
+ import { NzRadioModule } from 'ng-zorro-antd/radio';
18
+ import * as i5 from 'ng-zorro-antd/checkbox';
19
+ import { NzCheckboxModule } from 'ng-zorro-antd/checkbox';
20
+ import * as i6 from 'ng-zorro-antd/icon';
21
+ import { NzIconModule } from 'ng-zorro-antd/icon';
22
+ import * as i7 from '@angular/forms';
23
+ import { FormsModule } from '@angular/forms';
24
+ import { NzMessageService } from 'ng-zorro-antd/message';
25
+ import { trigger, state, style, transition, animate } from '@angular/animations';
26
+ import { NzModalService } from 'ng-zorro-antd/modal';
27
+ import { NzAvatarModule } from 'ng-zorro-antd/avatar';
28
+ import { NzSpinModule } from 'ng-zorro-antd/spin';
29
+ import { NzTagModule } from 'ng-zorro-antd/tag';
30
+ import { NzInputModule } from 'ng-zorro-antd/input';
31
+ import { NzButtonModule } from 'ng-zorro-antd/button';
32
+ import { NzListModule } from 'ng-zorro-antd/list';
33
+ import { NzSliderModule } from 'ng-zorro-antd/slider';
34
+ import { NzLayoutModule } from 'ng-zorro-antd/layout';
35
+ import { NzPaginationModule } from 'ng-zorro-antd/pagination';
36
+
37
+ // markdown-stream.service.ts
38
+ class MarkdownStreamService {
39
+ constructor() {
40
+ this.marked = new Marked(
41
+ // 高亮代码
42
+ markedHighlight({
43
+ emptyLangClass: 'hljs',
44
+ langPrefix: 'hljs language-',
45
+ highlight(code, lang, info) {
46
+ const language = hljs.getLanguage(lang) ? lang : 'plaintext';
47
+ return hljs.highlight(code, { language }).value;
48
+ },
49
+ }));
50
+ // 支持 $$...$$ 和 $...$ 数学公式
51
+ // this.marked.use({
52
+ // // 包含从 markdown 创建标记的函数的对象
53
+ // tokenizer: {
54
+ // inlineMath(src: string) {
55
+ // const cap = /^\$([^\$]+)\$/.exec(src);
56
+ // if (cap) {
57
+ // return { type: 'inlineMath', raw: cap[0], text: cap[1] };
58
+ // }
59
+ // },
60
+ // codespan(src: string) {
61
+ // const math = this.inlineMath(src);
62
+ // if (math) return math;
63
+ // return false;
64
+ // }
65
+ // }
66
+ // });
67
+ // marked 17+ 必须使用 extensions 方式注册
68
+ this.marked.use({
69
+ extensions: [
70
+ {
71
+ name: 'inlineMath',
72
+ level: 'inline',
73
+ start(src) {
74
+ return src.indexOf('$'); // 告诉 marked 从哪里开始尝试匹配
75
+ },
76
+ tokenizer(src) {
77
+ // 匹配 $...$,但不能包含换行,且不允许 $$ 开头(留给块级公式)
78
+ const match = src.match(/^\$(?!\$)([^\n$]+?)\$/);
79
+ if (match) {
80
+ return {
81
+ type: 'inlineMath',
82
+ raw: match[0],
83
+ text: match[1].trim(), // 公式内容
84
+ };
85
+ }
86
+ return undefined;
87
+ },
88
+ renderer(token) {
89
+ // 这里直接返回原始文本,后面你可以用 KaTeX / MathJax 渲染
90
+ return `$${token.text}$`;
91
+ // 或者如果你已经全局接管了渲染,可以 return false
92
+ },
93
+ },
94
+ // 可选:同时支持 $$...$$ 块级公式
95
+ {
96
+ name: 'displayMath',
97
+ level: 'block',
98
+ start(src) {
99
+ return src.indexOf('$$');
100
+ },
101
+ tokenizer(src) {
102
+ const match = src.match(/^\$\$([\s\S]+?)\$\$/);
103
+ if (match) {
104
+ return {
105
+ type: 'displayMath',
106
+ raw: match[0],
107
+ text: match[1].trim(),
108
+ };
109
+ }
110
+ return undefined;
111
+ },
112
+ renderer(token) {
113
+ return `$$${token.text}$$`;
114
+ },
115
+ },
116
+ ],
117
+ });
118
+ }
119
+ // 关键:增量渲染(流式专用!)
120
+ renderIncremental(markdown) {
121
+ // 防止渲染一半的代码块崩溃
122
+ const safeMarkdown = this.completeCodeBlocks(markdown);
123
+ try {
124
+ let html = this.marked.parse(safeMarkdown);
125
+ html = DOMPurify.sanitize(html); // 安全
126
+ return html;
127
+ }
128
+ catch (e) {
129
+ return this.escapeHtml(safeMarkdown);
130
+ }
131
+ }
132
+ // 智能补全未闭合的代码块(流式必备!)
133
+ completeCodeBlocks(text) {
134
+ const lines = text.split('\n');
135
+ let inCode = false;
136
+ let fence = '';
137
+ for (let i = 0; i < lines.length; i++) {
138
+ const line = lines[i];
139
+ if (line.trim().match(/^```/)) {
140
+ if (!inCode) {
141
+ fence = line.trim();
142
+ inCode = true;
143
+ }
144
+ else if (line.trim() === '```' || line.trim().startsWith('```')) {
145
+ inCode = false;
146
+ fence = '';
147
+ }
148
+ }
149
+ }
150
+ if (inCode) {
151
+ text += '\n```';
152
+ }
153
+ return text;
154
+ }
155
+ escapeHtml(text) {
156
+ const div = document.createElement('div');
157
+ div.textContent = text;
158
+ return div.innerHTML;
159
+ }
160
+ }
161
+ MarkdownStreamService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: MarkdownStreamService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
162
+ MarkdownStreamService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: MarkdownStreamService, providedIn: 'root' });
163
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: MarkdownStreamService, decorators: [{
164
+ type: Injectable,
165
+ args: [{ providedIn: 'root' }]
166
+ }], ctorParameters: function () { return []; } });
167
+
168
+ class StoreService {
169
+ constructor() {
170
+ this.token = '';
171
+ this.baseUrl = '';
172
+ this.currentConversationSource = new BehaviorSubject(null);
173
+ this.currentConversation$ = this.currentConversationSource.asObservable();
174
+ }
175
+ setToken(token) {
176
+ this.token = token;
177
+ }
178
+ getToken() {
179
+ return (this.token ||
180
+ 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0aWQiOiI2YjBjZjcwNC1hNmE5LTQxZDQtOWRkYy1kZmZmODlkYTZiNWMiLCJ1c2VyX3V1aWQiOjMxNTAsInVzZXJfbmFtZSI6IjEzMDg5MTI3MzcwIiwicmVnaW9uQ29kZSI6IjYzMDEwMDAwMDAwMCIsImF1dGhvcml0aWVzIjpbIldBVEVSX1NVUFBMWV9VU0VSIiwiTkVUV09SS19QUk9UT0NPTF9VU0VSIiwiUE9SVEFMX01BTkFHRV9BRE1JTiIsIkRJR0lUQUxfSVJSSUdBVElPTl9BRE1JTiIsIkVRVUlQTUVOVF9NT0RFTF9BRE1JTiIsIklOU1BFQ1RJT05fVVNFUiIsIkJJTV9CQU5LIiwiREFUQV9DQURBU1RSRV9BRE1JTiIsIklSUklHQVRJT05fR0FURV9VU0VSIiwiV0FURVJfUVVBTElUWV9VU0VSIiwiSVJSSUdBVElPTl9BRE1JTl9VU0VSIiwiQlVER0VUX01BVEVSX0FETUlOIiwiV0FURVJfTkVUV09SS19BRE1JTiIsIklOU1BFQ1RJT05fQURNSU5fVVNFUiIsIkRJR0lUQUxfSVJSSUdBVElPTl9KRyIsIlJPTEVfVVNFUiIsIkRBVEFfQ0VOVEVSX0lDT05fQURNSU4iLCJXQVJOSU5HX1VTRVIiLCJSRVNPVVJDRV9BRE1JTiIsIldBVEVSX1FVQUxJVFlfQURNSU4iLCJCVURHRVRfTUFURVJfVVNFUiIsIkJVSUxEX0NPTlNUUlVDVElPTl9BRE1JTiIsIkNBTENVTEFUSU9OX0RBVEFfQURNSU4iLCJURVhUVVJFX0FETUlOIiwiREFUQV9DRU5URVJfRklMRV9BRE1JTiIsIlNUQVRJQ19NT0RFTF9BRE1JTiIsIkJJT19QTEFOVF9BRE1JTiIsIkJJTV9ERVNJR04iLCJST0xFX1NVUEVSX0FETUlOIiwiTkVUV09SS19NT0RFTF9VU0VSIiwiV0FURVJfUVVPVEFfQURNSU4iLCJEQVRBX0NFTlRFUl9SSVZFUl9MQUtFIiwiREFNX1VTRVIiLCJTVVBQTFlfRFJBSU5BR0VfQURNSU4iLCJSRVNPVVJDRV9VU0VSIiwiQVJHVU1FTlRBVElPTl9VU0VSIiwiTE9HR0lOR19VU0VSIiwiREFUQV9URVJSQUlOX0FETUlOIiwiQlVER0VUX1FVT1RBX1VTRVIiLCJEQVRBX0NFTlRFUl9SRUdJT05fQURNSU4iLCJURVhUVVJFX1VTRVIiLCJEQVRBX0NFTlRFUl9VU0VSIiwiRVFVSVBNRU5UX01PREVMX1VTRVIiLCJEQVRBX0NFTlRFUl9QQU5PUkFNQSIsIkFSR1VNRU5UQVRJT05fQURNSU4iLCJEQVRBX0NFTlRFUl9XQVRFUl9SRVNPVVJDRSIsIkJJTV9QUk9DRURVUkUiLCJNQU5BR0VNRU5UX0FETUlOX1VTRVIiLCJEUklOR1dBVEVSX1VTRVIiLCJXQVRFUl9TVVBQTFlfQURNSU4iLCJMQU5EX1VTRV9BRE1JTiIsIkRBVEFfQ0VOVEVSX0ZJTEVfVVNFUiIsIkRBVEFfQ0VOVEVSX1dBVEVSX1BST0pFQ1QiLCJIWl9TTEFWRSIsIkRvY3VtZW50X1VTRVIiLCJCVURHRVRfUVVPVEFfQURNSU4iLCJJUlJJR0FUSU9OX1VTRVIiLCJORVRXT1JLX01PREVMX0FETUlOIiwiU1VQUExZX0RSQUlOQUdFX1VTRVIiLCJMT0dHSU5HX0FETUlOIiwiRFJJTkdXQVRFUl9BRE1JTl9VU0VSIiwiTkVUV09SS19VU0VSIiwiTU9ERUxfUEFSQU1fQURNSU4iLCJXQVRFUl9RVU9UQV9VU0VSIiwiU1RBVElDX01PREVMX0RFVkVMT1BFUiIsIkRFUEFSVE1FTlRfQURNSU4iLCJEQVRBX0NFTlRFUl9BTk5FWCIsIk5FVFdPUktfQURNSU4iLCJCSU1fRVFVSVBNRU5UIiwiRElHSVRBTF9JUlJJR0FUSU9OX1VTRVIiLCJNQU5BR0VNRU5UX1VTRVIiLCJCVUlMRF9DT05TVFJVQ1RJT05fVVNFUiIsIkRBVEFfQ0VOVEVSX0FETUlOIiwiUklWRVJfTEFLRV9FVkFMVVRJT05fVVNFUiIsIkJJTV9DT1NUIiwiRFJJTkdXQVRFUl9HQVRFX1VTRVIiLCJEQVRBX0NFTlRFUl9XQVRFUl9XQURJTkciLCJORVRXT1JLX1BST1RPQ09MX0FETUlOIiwiQklNX1VTRVIiLCJEQVRBX0NBREFTVFJFX1VTRVIiLCJTVEFUSUNfTU9ERUxfVVNFUiIsIlJPTEVfQURNSU4iLCJXQVRFUl9TVVBQTFlfR0FURV9VU0VSIiwiUklWRVJfTEFLRV9FVkFMVVRJT05fQURNSU4iLCJCSU1fQURNSU4iLCJCSU9fUExBTlRfVVNFUiJdLCJleHAiOjE3NjQwNjA0NzA1MTQsInJvbGVzIjpbIkhaX1NMQVZFIiwiUk9MRV9BRE1JTiIsIkJJT19QTEFOVF9BRE1JTiIsIlJPTEVfVVNFUiIsIkRBVEFfQ0VOVEVSX1VTRVIiLCJEQVRBX0NFTlRFUl9BRE1JTiIsIkRvY3VtZW50X1VTRVIiLCJEQVRBX0NFTlRFUl9BTk5FWCIsIkJJTV9FUVVJUE1FTlQiLCJMT0dHSU5HX0FETUlOIiwiU1VQUExZX0RSQUlOQUdFX1VTRVIiLCJSSVZFUl9MQUtFX0VWQUxVVElPTl9BRE1JTiIsIkRBVEFfQ0VOVEVSX0lDT05fQURNSU4iLCJXQVRFUl9RVUFMSVRZX1VTRVIiLCJXQVRFUl9RVU9UQV9VU0VSIiwiSVJSSUdBVElPTl9BRE1JTl9VU0VSIiwiQlVER0VUX1FVT1RBX0FETUlOIiwiREFUQV9DRU5URVJfRklMRV9VU0VSIiwiQklNX0RFU0lHTiIsIk1BTkFHRU1FTlRfVVNFUiIsIk5FVFdPUktfUFJPVE9DT0xfQURNSU4iLCJTVEFUSUNfTU9ERUxfREVWRUxPUEVSIiwiV0FSTklOR19VU0VSIiwiQklNX1VTRVIiLCJEQVRBX0NFTlRFUl9SSVZFUl9MQUtFIiwiRFJJTkdXQVRFUl9BRE1JTl9VU0VSIiwiQVJHVU1FTlRBVElPTl9VU0VSIiwiREFUQV9DRU5URVJfV0FURVJfUFJPSkVDVCIsIkRBVEFfQ0FEQVNUUkVfQURNSU4iLCJERVBBUlRNRU5UX0FETUlOIiwiTkVUV09SS19VU0VSIiwiU1RBVElDX01PREVMX0FETUlOIiwiQklNX0JBTksiLCJXQVRFUl9TVVBQTFlfQURNSU4iLCJCSU1fQURNSU4iLCJSRVNPVVJDRV9VU0VSIiwiREFUQV9URVJSQUlOX0FETUlOIiwiREFUQV9DRU5URVJfUkVHSU9OX0FETUlOIiwiV0FURVJfU1VQUExZX1VTRVIiLCJESUdJVEFMX0lSUklHQVRJT05fVVNFUiIsIldBVEVSX1FVQUxJVFlfQURNSU4iLCJORVRXT1JLX0FETUlOIiwiTE9HR0lOR19VU0VSIiwiSU5TUEVDVElPTl9BRE1JTl9VU0VSIiwiSVJSSUdBVElPTl9HQVRFX1VTRVIiLCJORVRXT1JLX1BST1RPQ09MX1VTRVIiLCJMQU5EX1VTRV9BRE1JTiIsIkJVSUxEX0NPTlNUUlVDVElPTl9BRE1JTiIsIkRBVEFfQ0VOVEVSX1BBTk9SQU1BIiwiREFUQV9DRU5URVJfV0FURVJfV0FESU5HIiwiQ0FMQ1VMQVRJT05fREFUQV9BRE1JTiIsIkRBVEFfQ0FEQVNUUkVfVVNFUiIsIkJJT19QTEFOVF9VU0VSIiwiTU9ERUxfUEFSQU1fQURNSU4iLCJTVVBQTFlfRFJBSU5BR0VfQURNSU4iLCJFUVVJUE1FTlRfTU9ERUxfQURNSU4iLCJURVhUVVJFX0FETUlOIiwiQlVER0VUX01BVEVSX1VTRVIiLCJXQVRFUl9RVU9UQV9BRE1JTiIsIkRJR0lUQUxfSVJSSUdBVElPTl9KRyIsIldBVEVSX05FVFdPUktfQURNSU4iLCJCSU1fUFJPQ0VEVVJFIiwiRFJJTkdXQVRFUl9VU0VSIiwiV0FURVJfU1VQUExZX0dBVEVfVVNFUiIsIkJVSUxEX0NPTlNUUlVDVElPTl9VU0VSIiwiRElHSVRBTF9JUlJJR0FUSU9OX0FETUlOIiwiSVJSSUdBVElPTl9VU0VSIiwiUk9MRV9TVVBFUl9BRE1JTiIsIkVRVUlQTUVOVF9NT0RFTF9VU0VSIiwiQklNX0NPU1QiLCJEUklOR1dBVEVSX0dBVEVfVVNFUiIsIklOU1BFQ1RJT05fVVNFUiIsIkFSR1VNRU5UQVRJT05fQURNSU4iLCJQT1JUQUxfTUFOQUdFX0FETUlOIiwiVEVYVFVSRV9VU0VSIiwiTUFOQUdFTUVOVF9BRE1JTl9VU0VSIiwiTkVUV09SS19NT0RFTF9BRE1JTiIsIkJVREdFVF9RVU9UQV9VU0VSIiwiQlVER0VUX01BVEVSX0FETUlOIiwiU1RBVElDX01PREVMX1VTRVIiLCJSSVZFUl9MQUtFX0VWQUxVVElPTl9VU0VSIiwiREFNX1VTRVIiLCJSRVNPVVJDRV9BRE1JTiIsIkRBVEFfQ0VOVEVSX1dBVEVSX1JFU09VUkNFIiwiREFUQV9DRU5URVJfRklMRV9BRE1JTiIsIk5FVFdPUktfTU9ERUxfVVNFUiJdLCJyZWFsX25hbWUiOiLnrqHnkIblkZgiLCJwbGF0Zm9ybUNvZGUiOiI2MzAxMDAwMDAwMDAiLCJwbGF0Zm9ybU5hbWUiOiLopb_lroHluIIiLCJ1c2VyTWFyayI6ZmFsc2UsInBob25lIjoiMTMwODkxMjczNzAiLCJlbnYiOiJyZWxlYXNlIiwidG9wUmVnaW9uQ29kZSI6IjYzMDEwMDAwMDAwMCJ9.Bzlz0LC5rbwItBgw6B-dhSKhXnAATXiaL-rR3EHD4wmJ-BpWWrDakYAjzUCtWL4ExkTqSxXIClCztc412o5pT3b9EN7R6WH5namjpxzwozCVXQyBYvjoCrS5UX1_iUkvVMr6WAqiUUzIanVSrmaRpszxyD8S_fLBge971-Cli44J1FQDBY8QN8TiH3v3c5aDWDBJVYPHqe3_zEAeL3dlERgl8pTLAydx54a2QqCVH2xfX_zSzFXJeK2iamIyI8H4cyP0pQrYl2rjSuR7LsjJSJz_QeSQKZlctG8odrBMV5OoHlWC2isNGqszqyp1POX2HyWb-U4qn8KuKKryjACrow');
181
+ }
182
+ setBaseUrl(url) {
183
+ this.baseUrl = url;
184
+ }
185
+ getBaseUrl() {
186
+ return this.baseUrl;
187
+ }
188
+ setCurrentConversation(conversation) {
189
+ this.conversationICode = conversation?.iCode;
190
+ this.currentConversationSource.next(conversation);
191
+ }
192
+ getConversationICode() {
193
+ return this.conversationICode;
194
+ }
195
+ }
196
+ StoreService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: StoreService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
197
+ StoreService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: StoreService, providedIn: 'root' });
198
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: StoreService, decorators: [{
199
+ type: Injectable,
200
+ args: [{ providedIn: 'root' }]
201
+ }] });
202
+
203
+ let HttpService$1 = class HttpService {
204
+ constructor(http, storeService) {
205
+ this.http = http;
206
+ this.storeService = storeService;
207
+ }
208
+ get(url, paramObj) {
209
+ if (paramObj) {
210
+ return this.http
211
+ .get(url, {
212
+ headers: new HttpHeaders({
213
+ 'Content-Type': 'application/json',
214
+ Authorization: this.storeService.getToken(),
215
+ }),
216
+ observe: 'response',
217
+ params: paramObj,
218
+ responseType: 'json',
219
+ withCredentials: false,
220
+ })
221
+ .pipe(filter((r) => r instanceof HttpResponse), map((r) => {
222
+ return r.body;
223
+ }));
224
+ }
225
+ return this.http
226
+ .get(url, {
227
+ headers: new HttpHeaders({
228
+ 'Content-Type': 'application/json',
229
+ Authorization: this.storeService.getToken(),
230
+ }),
231
+ observe: 'response',
232
+ responseType: 'json',
233
+ withCredentials: false,
234
+ })
235
+ .pipe(filter((r) => r instanceof HttpResponse), map((r) => {
236
+ return r.body;
237
+ }));
238
+ }
239
+ post(url, body, options) {
240
+ // 默认配置
241
+ const defaultOptions = {
242
+ headers: new HttpHeaders({
243
+ 'Content-Type': 'application/json',
244
+ Authorization: this.storeService.getToken(),
245
+ }),
246
+ observe: 'response',
247
+ responseType: 'json',
248
+ withCredentials: false,
249
+ };
250
+ // 如果提供了自定义选项,则合并配置
251
+ const httpOptions = options ? { ...defaultOptions, ...options } : defaultOptions;
252
+ // 发送 POST 请求
253
+ return this.http.post(url, body, httpOptions);
254
+ }
255
+ };
256
+ 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 });
257
+ HttpService$1.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: HttpService$1, providedIn: 'root' });
258
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: HttpService$1, decorators: [{
259
+ type: Injectable,
260
+ args: [{
261
+ providedIn: 'root',
262
+ }]
263
+ }], ctorParameters: function () { return [{ type: i1.HttpClient }, { type: StoreService }]; } });
264
+
265
+ class SseService {
266
+ constructor() {
267
+ this.storeService = inject(StoreService);
268
+ this.conversationICodeSource = new BehaviorSubject(null);
269
+ this.conversationICode$ = this.conversationICodeSource.asObservable();
270
+ // 思考内容
271
+ this.thinkingSource = new BehaviorSubject('');
272
+ this.thinking$ = this.thinkingSource.asObservable();
273
+ // 实时流式文本
274
+ this.messageSource = new BehaviorSubject('');
275
+ this.currentMessage$ = this.messageSource.asObservable();
276
+ this.deltaSource = new BehaviorSubject('');
277
+ this.delta$ = this.deltaSource.asObservable();
278
+ this.loadingSource = new BehaviorSubject(false);
279
+ this.loading$ = this.loadingSource.asObservable();
280
+ // 完整消息历史
281
+ this.fullThinking = '';
282
+ this.fullMessage = '';
283
+ // 支持取消(关键!)
284
+ this.controller = new AbortController();
285
+ }
286
+ async streamChat(body) {
287
+ this.loadingSource.next(true);
288
+ this.messageSource.next(''); // 清空
289
+ this.fullMessage = '';
290
+ try {
291
+ const response = await fetch(`${this.storeService.getBaseUrl()}/api/ChatModel/generate`, {
292
+ method: 'POST',
293
+ headers: {
294
+ 'Content-Type': 'application/json',
295
+ Authorization: this.storeService.getToken(),
296
+ Accept: 'text/event-stream',
297
+ },
298
+ body: JSON.stringify(body),
299
+ });
300
+ if (!response.ok || !response.body) {
301
+ throw new Error('网络错误');
302
+ }
303
+ const reader = response.body.getReader();
304
+ const decoder = new TextDecoder('utf-8');
305
+ while (true) {
306
+ const { done, value } = await reader.read();
307
+ if (done)
308
+ break;
309
+ const chunk = decoder.decode(value, { stream: true });
310
+ const lines = chunk.split('\n\n');
311
+ for (const line of lines) {
312
+ if (line.startsWith('data:')) {
313
+ const jsonStr = line.replaceAll('data:', '');
314
+ try {
315
+ const data = JSON.parse(jsonStr);
316
+ const delta = data.content || '';
317
+ if (delta) {
318
+ // 对话以及问答编码
319
+ if (delta.conversation) {
320
+ this.conversationICodeSource.next(delta);
321
+ continue;
322
+ }
323
+ // 比如显示“AI 正在思考...”
324
+ if (data.think) {
325
+ this.fullThinking += delta;
326
+ this.thinkingSource.next(this.fullThinking);
327
+ continue;
328
+ }
329
+ this.fullMessage += delta;
330
+ this.messageSource.next(this.fullMessage); // 实时推送完整内容
331
+ this.deltaSource.next(delta); // 只推本次新增的文字
332
+ }
333
+ if (data.down) {
334
+ // down: true 表示结束
335
+ this.loadingSource.next(false);
336
+ }
337
+ }
338
+ catch (e) {
339
+ console.log('sse error', e);
340
+ }
341
+ }
342
+ }
343
+ }
344
+ }
345
+ catch (err) {
346
+ if (err.name !== 'AbortError') {
347
+ this.messageSource.next('请求失败:' + err.message);
348
+ }
349
+ }
350
+ finally {
351
+ this.loadingSource.next(false);
352
+ }
353
+ }
354
+ cancel() {
355
+ this.controller.abort();
356
+ this.controller = new AbortController();
357
+ }
358
+ }
359
+ SseService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: SseService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
360
+ SseService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: SseService, providedIn: 'root' });
361
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: SseService, decorators: [{
362
+ type: Injectable,
363
+ args: [{
364
+ providedIn: 'root',
365
+ }]
366
+ }] });
367
+
368
+ // chat-input.component.ts
369
+ class ChatInputComponent {
370
+ constructor() {
371
+ this.messages = [];
372
+ this.sendMessage = new EventEmitter();
373
+ this.messageChange = new EventEmitter();
374
+ this.enter = new EventEmitter();
375
+ this.paste = new EventEmitter();
376
+ this.httpService = inject(HttpService$1);
377
+ this.storeService = inject(StoreService);
378
+ this.sseService = inject(SseService);
379
+ this.isEmpty = true;
380
+ this.currentModel = null;
381
+ this.currentKnowledge = [];
382
+ this.modelList = [];
383
+ this.knowledgeList = [];
384
+ this.modelPopoverVisible = false;
385
+ this.knowledgePopoverVisible = false;
386
+ this.deepThink = true;
387
+ }
388
+ ngOnInit() {
389
+ this.getModelList();
390
+ this.getKnowledgeList();
391
+ }
392
+ // 可供交互的模型
393
+ getModelList() {
394
+ this.httpService.get(`${this.storeService.getBaseUrl()}/api/ChatModel/models`).subscribe((res) => {
395
+ this.modelList = res;
396
+ if (res.length) {
397
+ this.currentModel = res[0];
398
+ }
399
+ });
400
+ }
401
+ // 模型可选的知识库
402
+ getKnowledgeList() {
403
+ this.httpService
404
+ .get(`${this.storeService.getBaseUrl()}/api/ChatModel/knowledges`)
405
+ .subscribe((res) => {
406
+ this.knowledgeList = res;
407
+ });
408
+ }
409
+ modelVisibleChange(value) {
410
+ this.modelPopoverVisible = value;
411
+ }
412
+ knowledgeVisibleChange(value) {
413
+ this.knowledgePopoverVisible = value;
414
+ }
415
+ knowledgeSelectChange(value) {
416
+ this.currentKnowledge = value;
417
+ }
418
+ toggleDeepThink() {
419
+ this.deepThink = !this.deepThink;
420
+ }
421
+ updateEmptyState() {
422
+ const el = this.editor.nativeElement;
423
+ const text = el.textContent?.trim() || '';
424
+ const hasOnlyBr = el.innerHTML === '<br>' || el.innerHTML === '';
425
+ this.isEmpty = text === '' && hasOnlyBr;
426
+ }
427
+ handleInput() {
428
+ this.updateEmptyState();
429
+ const html = this.editor.nativeElement.innerHTML;
430
+ const text = this.editor.nativeElement.textContent || '';
431
+ // this.messageChange.emit(text);
432
+ }
433
+ handleEnter(event) {
434
+ if (!event.shiftKey) {
435
+ event.preventDefault();
436
+ // this.enter.emit((event as any).target!.innerText!.replace(/\s+/g, " ").trim());
437
+ this.send();
438
+ }
439
+ }
440
+ handlePaste(event) {
441
+ // this.paste.emit(event);
442
+ setTimeout(() => this.updateEmptyState(), 0);
443
+ }
444
+ clearInput() {
445
+ this.editor.nativeElement.innerHTML = '';
446
+ this.handleInput();
447
+ }
448
+ regenerateSend(msg) {
449
+ // 正在回答中
450
+ if (this.sseService.loadingSource.value) {
451
+ return;
452
+ }
453
+ const conversation = this.storeService.getConversationICode();
454
+ const message = {
455
+ conversation,
456
+ questionICode: msg.iCode,
457
+ model: this.currentModel?.iCode ?? '',
458
+ think: this.deepThink,
459
+ question: msg.question,
460
+ knowledge: !!this.currentKnowledge.length,
461
+ knowledgeList: this.currentKnowledge,
462
+ };
463
+ this.sendMessage.emit({ message, isRegenerate: true });
464
+ }
465
+ send() {
466
+ // 正在回答中
467
+ if (this.sseService.loadingSource.value) {
468
+ return;
469
+ }
470
+ const text = this.editor.nativeElement.textContent || '';
471
+ const conversation = this.storeService.getConversationICode();
472
+ const message = {
473
+ conversation,
474
+ model: this.currentModel?.iCode ?? '',
475
+ think: this.deepThink,
476
+ question: text,
477
+ knowledge: !!this.currentKnowledge.length,
478
+ knowledgeList: this.currentKnowledge,
479
+ };
480
+ this.clearInput();
481
+ this.sendMessage.emit({ message, isRegenerate: false });
482
+ }
483
+ }
484
+ ChatInputComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: ChatInputComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
485
+ 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" }] });
486
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: ChatInputComponent, decorators: [{
487
+ type: Component,
488
+ 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"] }]
489
+ }], propDecorators: { editor: [{
490
+ type: ViewChild,
491
+ args: ['editorRef']
492
+ }], chatInputWrapper: [{
493
+ type: ViewChild,
494
+ args: ['chatInputWrapper']
495
+ }], messages: [{
496
+ type: Input
497
+ }], sendMessage: [{
498
+ type: Output
499
+ }], messageChange: [{
500
+ type: Output
501
+ }], enter: [{
502
+ type: Output
503
+ }], paste: [{
504
+ type: Output
505
+ }] } });
506
+
507
+ class SimplePaginationComponent {
508
+ constructor() {
509
+ /** 当前页码 */
510
+ this.current = 1;
511
+ /** 总页数 */
512
+ this.total = 1;
513
+ /** 当页码改变时触发 */
514
+ this.pageChange = new EventEmitter();
515
+ }
516
+ onPrev() {
517
+ if (this.current > 1) {
518
+ this.pageChange.emit(this.current - 1);
519
+ }
520
+ }
521
+ onNext() {
522
+ if (this.current < this.total) {
523
+ this.pageChange.emit(this.current + 1);
524
+ }
525
+ }
526
+ }
527
+ SimplePaginationComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: SimplePaginationComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
528
+ 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: `
529
+ <div class="pagination-container">
530
+ <button
531
+ class="nav-btn"
532
+ [disabled]="current === 1"
533
+ (click)="onPrev()">
534
+ <svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2"
535
+ stroke-linecap="round" stroke-linejoin="round">
536
+ <polyline points="15 18 9 12 15 6"></polyline>
537
+ </svg>
538
+ </button>
539
+
540
+ <span class="page-info">
541
+ <span class="current">{{ current }}</span>
542
+ <span class="separator">/</span>
543
+ <span class="total">{{ total }}</span>
544
+ </span>
545
+
546
+ <button
547
+ class="nav-btn"
548
+ [disabled]="current === total"
549
+ (click)="onNext()">
550
+ <svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2"
551
+ stroke-linecap="round" stroke-linejoin="round">
552
+ <polyline points="9 18 15 12 9 6"></polyline>
553
+ </svg>
554
+ </button>
555
+ </div>
556
+ `, 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"] });
557
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: SimplePaginationComponent, decorators: [{
558
+ type: Component,
559
+ args: [{ selector: 'ngx-simple-pagination', template: `
560
+ <div class="pagination-container">
561
+ <button
562
+ class="nav-btn"
563
+ [disabled]="current === 1"
564
+ (click)="onPrev()">
565
+ <svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2"
566
+ stroke-linecap="round" stroke-linejoin="round">
567
+ <polyline points="15 18 9 12 15 6"></polyline>
568
+ </svg>
569
+ </button>
570
+
571
+ <span class="page-info">
572
+ <span class="current">{{ current }}</span>
573
+ <span class="separator">/</span>
574
+ <span class="total">{{ total }}</span>
575
+ </span>
576
+
577
+ <button
578
+ class="nav-btn"
579
+ [disabled]="current === total"
580
+ (click)="onNext()">
581
+ <svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2"
582
+ stroke-linecap="round" stroke-linejoin="round">
583
+ <polyline points="9 18 15 12 9 6"></polyline>
584
+ </svg>
585
+ </button>
586
+ </div>
587
+ `, 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"] }]
588
+ }], propDecorators: { current: [{
589
+ type: Input
590
+ }], total: [{
591
+ type: Input
592
+ }], pageChange: [{
593
+ type: Output
594
+ }] } });
595
+
596
+ class MarkdownPipe {
597
+ constructor() {
598
+ this.md = inject(MarkdownStreamService);
599
+ }
600
+ transform(value) {
601
+ if (value !== null && value !== undefined) {
602
+ return this.md.renderIncremental(value);
603
+ }
604
+ return value;
605
+ }
606
+ }
607
+ MarkdownPipe.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: MarkdownPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
608
+ MarkdownPipe.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "15.2.10", ngImport: i0, type: MarkdownPipe, isStandalone: true, name: "markdown" });
609
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: MarkdownPipe, decorators: [{
610
+ type: Pipe,
611
+ args: [{
612
+ name: 'markdown',
613
+ standalone: true,
614
+ }]
615
+ }] });
616
+
617
+ // chat-messages.component.ts
618
+ class ChatMessagesComponent {
619
+ constructor() {
620
+ this.messages = [];
621
+ /**
622
+ * 重新生成回答
623
+ */
624
+ this.regenerateAnswer = new EventEmitter();
625
+ this.md = inject(MarkdownStreamService);
626
+ this.sseService = inject(SseService);
627
+ this.sanitizer = inject(DomSanitizer);
628
+ this.msg = inject(NzMessageService);
629
+ this.isLoading = false;
630
+ this.inputHeight = 180;
631
+ this.renderedHtml = '';
632
+ this.accumulatedMarkdown = '';
633
+ }
634
+ ngAfterViewInit() {
635
+ this.sseService.loading$.subscribe(status => {
636
+ this.isLoading = status;
637
+ });
638
+ this.sseService.delta$.subscribe(delta => {
639
+ if (!delta)
640
+ return;
641
+ this.accumulatedMarkdown += delta;
642
+ this.renderedHtml = this.md.renderIncremental(this.accumulatedMarkdown);
643
+ });
644
+ }
645
+ ngAfterViewChecked() {
646
+ // 正在回答中
647
+ if (this.isLoading) {
648
+ this.scrollToBottom();
649
+ }
650
+ }
651
+ ngOnChanges(changes) {
652
+ if (changes['messages']) {
653
+ setTimeout(() => {
654
+ this.scrollToBottom();
655
+ }, 50);
656
+ }
657
+ }
658
+ // 滚动到底部
659
+ scrollToBottom() {
660
+ if (!this.messagesContainer) {
661
+ return;
662
+ }
663
+ this.messagesContainer.nativeElement.scrollTop = this.messagesContainer.nativeElement.scrollHeight;
664
+ }
665
+ copyContent(text) {
666
+ if (navigator.clipboard && window.isSecureContext) {
667
+ // 使用现代 Clipboard API
668
+ navigator.clipboard.writeText(text).then(() => {
669
+ this.msg.success('复制成功');
670
+ });
671
+ }
672
+ }
673
+ onPageChange(num, msg) {
674
+ msg.answerIndex = num - 1;
675
+ }
676
+ /**
677
+ * 重新生成多个回答
678
+ */
679
+ regenerate(msg) {
680
+ // const question = this.messages[index - 1].message;
681
+ // 重新生成使用底部最新的配置,重新调用sendMessage
682
+ this.regenerateAnswer.emit(msg);
683
+ }
684
+ // 把完整的 markdown 块渲染,剩下的留给打字区
685
+ renderCompleteBlocks(markdown) {
686
+ // 找到最后一个换行符之前的都渲染(足够丝滑)
687
+ const lines = markdown.split('\n');
688
+ let breakPoint = lines.length;
689
+ // 从后往前找,保留未完成的代码块/表格/列表
690
+ for (let i = lines.length - 1; i >= 0; i--) {
691
+ const line = lines[i];
692
+ if (line.trim() === '' || line.startsWith('```') || line.startsWith('|') || line.startsWith('- ')) {
693
+ breakPoint = i;
694
+ break;
695
+ }
696
+ }
697
+ const completePart = lines.slice(0, breakPoint).join('\n');
698
+ const remainingPart = lines.slice(breakPoint).join('\n');
699
+ const html = this.md.renderIncremental(completePart);
700
+ // const html = marked(completePart, { breaks: true, gfm: true });
701
+ return { rendered: html, remaining: remainingPart };
702
+ }
703
+ }
704
+ ChatMessagesComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: ChatMessagesComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
705
+ 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" }] });
706
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: ChatMessagesComponent, decorators: [{
707
+ type: Component,
708
+ 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"] }]
709
+ }], propDecorators: { messages: [{
710
+ type: Input
711
+ }], messagesContainer: [{
712
+ type: ViewChild,
713
+ args: ['messagesContainer']
714
+ }], regenerateAnswer: [{
715
+ type: Output
716
+ }] } });
717
+
718
+ class HttpService {
719
+ constructor() {
720
+ this.http = inject(HttpClient);
721
+ this.storeService = inject(StoreService);
722
+ this.BASE_URL = '/api'; // 或者完整域名
723
+ }
724
+ /**
725
+ * 统一获取 Headers
726
+ */
727
+ getHeaders() {
728
+ return new HttpHeaders({
729
+ 'Content-Type': 'application/json',
730
+ Authorization: `${this.storeService.getToken()}`,
731
+ });
732
+ }
733
+ /**
734
+ * GET 请求
735
+ */
736
+ get(url, params) {
737
+ return this.http.get(this.formatUrl(url), {
738
+ headers: this.getHeaders(),
739
+ params: this.resolveParams(params),
740
+ });
741
+ }
742
+ /**
743
+ * POST 请求
744
+ */
745
+ post(url, body) {
746
+ return this.http.post(this.formatUrl(url), body, {
747
+ headers: this.getHeaders(),
748
+ });
749
+ }
750
+ /**
751
+ * PUT 请求
752
+ */
753
+ put(url, body) {
754
+ return this.http.put(this.formatUrl(url), body, {
755
+ headers: this.getHeaders(),
756
+ });
757
+ }
758
+ /**
759
+ * DELETE 请求
760
+ * 兼容支持:既支持 URL 参数,也支持 Body
761
+ */
762
+ delete(url, params, body) {
763
+ return this.http.request('DELETE', this.formatUrl(url), {
764
+ headers: this.getHeaders(),
765
+ params: this.resolveParams(params),
766
+ body: body, // HttpClient 的 delete() 方法默认不支持 body,需用 request() 通用方法
767
+ });
768
+ }
769
+ // ================= 私有辅助方法 =================
770
+ /**
771
+ * 统一处理 URL
772
+ * 比如自动拼接 BaseUrl,或者处理斜杠
773
+ */
774
+ formatUrl(url) {
775
+ if (url.startsWith('http'))
776
+ return url;
777
+ // 确保拼接顺滑
778
+ const baseUrl = this.storeService.getBaseUrl();
779
+ const cleanBase = baseUrl.replace(/\/$/, '');
780
+ const cleanUrl = url.startsWith('/') ? url : `/${url}`;
781
+ return `${cleanBase}${cleanUrl}`;
782
+ }
783
+ /**
784
+ * 统一清洗参数
785
+ * 过滤掉 null 和 undefined,防止传给后端 "null" 字符串
786
+ */
787
+ resolveParams(params) {
788
+ let httpParams = new HttpParams();
789
+ if (params) {
790
+ Object.keys(params).forEach(key => {
791
+ const value = params[key];
792
+ if (value !== null && value !== undefined) {
793
+ if (Array.isArray(value)) {
794
+ // 支持数组参数 ids: [1, 2] => ids=1&ids=2
795
+ value.forEach(val => (httpParams = httpParams.append(key, val)));
796
+ }
797
+ else {
798
+ httpParams = httpParams.set(key, value);
799
+ }
800
+ }
801
+ });
802
+ }
803
+ return httpParams;
804
+ }
805
+ }
806
+ HttpService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: HttpService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
807
+ HttpService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: HttpService, providedIn: 'root' });
808
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: HttpService, decorators: [{
809
+ type: Injectable,
810
+ args: [{
811
+ providedIn: 'root',
812
+ }]
813
+ }] });
814
+
815
+ class HistoryGroupComponent {
816
+ constructor() {
817
+ this.select = new EventEmitter();
818
+ this.refresh = new EventEmitter();
819
+ this.currentSelected = null;
820
+ this.storeService = inject(StoreService);
821
+ this.modalService = inject(NzModalService);
822
+ this.msg = inject(NzMessageService);
823
+ this.http = inject(HttpService);
824
+ }
825
+ toggleExpand() {
826
+ this.group.expanded = !this.group.expanded;
827
+ }
828
+ selectConversation(item) {
829
+ if (item) {
830
+ item.isActive = true;
831
+ }
832
+ this.currentSelected = item;
833
+ this.select.emit(this.currentSelected);
834
+ this.storeService.setCurrentConversation(this.currentSelected);
835
+ }
836
+ deleteConversation(e, item) {
837
+ e.stopPropagation();
838
+ this.modalService.confirm({
839
+ nzTitle: '删除对话',
840
+ nzContent: `删除后无法恢复,是否确认删除“${item.name}”对话?`,
841
+ nzOkText: '确定',
842
+ nzCancelText: '取消',
843
+ nzOnOk: () => {
844
+ this.http.delete(`/api/ChatModel/conversation`, { iCode: item.iCode }).subscribe(res => {
845
+ this.msg.success('删除成功');
846
+ this.refresh.emit(item.iCode);
847
+ });
848
+ },
849
+ });
850
+ }
851
+ }
852
+ HistoryGroupComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: HistoryGroupComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
853
+ 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: `
854
+ <div class="group-container">
855
+ <div class="group-header" (click)="toggleExpand()">
856
+ <div class="left">
857
+ <img src="assets/images/sidebar/time.png" />
858
+ <span class="date-text">{{ group.dateLabel }}</span>
859
+ </div>
860
+ <div class="arrow-icon" [class.rotated]="group.expanded">
861
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
862
+ <polyline points="6 9 12 15 18 9"></polyline>
863
+ </svg>
864
+ </div>
865
+ </div>
866
+
867
+ <div class="group-content" [@expandCollapse]="group.expanded ? 'expanded' : 'collapsed'">
868
+ <div
869
+ *ngFor="let item of group.items"
870
+ class="history-item"
871
+ [class.active]="item.isActive"
872
+ (click)="selectConversation(item)"
873
+ >
874
+ <span class="item-title">{{ item.name }}</span>
875
+
876
+ <img class="delete-img" src="assets/images/sidebar/delete.png" (click)="deleteConversation($event, item)" />
877
+ </div>
878
+ </div>
879
+ </div>
880
+ `, 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: [
881
+ trigger('expandCollapse', [
882
+ state('collapsed', style({ height: '0px', opacity: 0 })),
883
+ state('expanded', style({ height: '*', opacity: 1 })),
884
+ transition('expanded <=> collapsed', animate('300ms cubic-bezier(0.4, 0.0, 0.2, 1)')),
885
+ ]),
886
+ ] });
887
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: HistoryGroupComponent, decorators: [{
888
+ type: Component,
889
+ args: [{ selector: 'history-group', template: `
890
+ <div class="group-container">
891
+ <div class="group-header" (click)="toggleExpand()">
892
+ <div class="left">
893
+ <img src="assets/images/sidebar/time.png" />
894
+ <span class="date-text">{{ group.dateLabel }}</span>
895
+ </div>
896
+ <div class="arrow-icon" [class.rotated]="group.expanded">
897
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
898
+ <polyline points="6 9 12 15 18 9"></polyline>
899
+ </svg>
900
+ </div>
901
+ </div>
902
+
903
+ <div class="group-content" [@expandCollapse]="group.expanded ? 'expanded' : 'collapsed'">
904
+ <div
905
+ *ngFor="let item of group.items"
906
+ class="history-item"
907
+ [class.active]="item.isActive"
908
+ (click)="selectConversation(item)"
909
+ >
910
+ <span class="item-title">{{ item.name }}</span>
911
+
912
+ <img class="delete-img" src="assets/images/sidebar/delete.png" (click)="deleteConversation($event, item)" />
913
+ </div>
914
+ </div>
915
+ </div>
916
+ `, animations: [
917
+ trigger('expandCollapse', [
918
+ state('collapsed', style({ height: '0px', opacity: 0 })),
919
+ state('expanded', style({ height: '*', opacity: 1 })),
920
+ transition('expanded <=> collapsed', animate('300ms cubic-bezier(0.4, 0.0, 0.2, 1)')),
921
+ ]),
922
+ ], 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"] }]
923
+ }], propDecorators: { group: [{
924
+ type: Input
925
+ }], select: [{
926
+ type: Output
927
+ }], refresh: [{
928
+ type: Output
929
+ }] } });
930
+
931
+ // chat-sidebar.component.ts
932
+ class ChatSidebarComponent {
933
+ constructor() {
934
+ this.http = inject(HttpService$1);
935
+ this.storeService = inject(StoreService);
936
+ this.isCollapsed = false;
937
+ this.historyGroups = [];
938
+ }
939
+ ngOnInit() {
940
+ this.getHistory();
941
+ }
942
+ // 获取历史对话
943
+ getHistory(needSelectFirst = false) {
944
+ this.http.get(`${this.storeService.getBaseUrl()}/api/ChatModel/conversation`).subscribe((res) => {
945
+ this.historyGroups = this.groupByFriendlyTime(res);
946
+ // 选中第一个对话
947
+ if (needSelectFirst) {
948
+ if (this.historyGroups[0]?.items[0]) {
949
+ this.historyGroupRef.selectConversation(this.historyGroups[0].items[0]);
950
+ }
951
+ else {
952
+ this.historyGroupRef.selectConversation(null);
953
+ }
954
+ }
955
+ });
956
+ }
957
+ // 删除后刷新,若删除当前选中会话
958
+ refresh(conversationICode) {
959
+ const needSelectFirst = this.storeService.getConversationICode() === conversationICode;
960
+ this.getHistory(needSelectFirst);
961
+ }
962
+ selectConversation(conversation) {
963
+ // 取消勾选其他会话
964
+ this.historyGroups.forEach(group => {
965
+ group.items.forEach(item => {
966
+ if (!conversation || conversation.iCode !== item.iCode) {
967
+ item.isActive = false;
968
+ }
969
+ });
970
+ });
971
+ }
972
+ toggleSidebar() {
973
+ this.isCollapsed = !this.isCollapsed;
974
+ }
975
+ openNewChat() {
976
+ this.historyGroupRef.selectConversation(null);
977
+ }
978
+ groupByFriendlyTime(dataList) {
979
+ if (!dataList || dataList.length === 0) {
980
+ return [];
981
+ }
982
+ const list = [];
983
+ const now = new Date();
984
+ const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
985
+ const yesterday = new Date(today);
986
+ yesterday.setDate(yesterday.getDate() - 1);
987
+ dataList.forEach((item, index) => {
988
+ const itemTime = new Date(item.time);
989
+ const itemDate = new Date(itemTime.getFullYear(), itemTime.getMonth(), itemTime.getDate());
990
+ let dateLabel = '';
991
+ item.isActive = false;
992
+ if (itemDate.getTime() === today.getTime()) {
993
+ dateLabel = '今天';
994
+ }
995
+ else if (itemDate.getTime() === yesterday.getTime()) {
996
+ dateLabel = '昨天';
997
+ }
998
+ else {
999
+ // 其他日期显示具体的年月日
1000
+ dateLabel = `${itemTime.getFullYear()}年${(itemTime.getMonth() + 1).toString().padStart(2, '0')}月${itemTime.getDate().toString().padStart(2, '0')}日`;
1001
+ }
1002
+ const find = list.find(item => item.dateLabel === dateLabel);
1003
+ if (find) {
1004
+ find.items.push(item);
1005
+ }
1006
+ else {
1007
+ const group = { dateLabel: '', expanded: index === 0, items: [] };
1008
+ group.dateLabel = dateLabel;
1009
+ group.items.push(item);
1010
+ list.push(group);
1011
+ }
1012
+ });
1013
+ return list;
1014
+ }
1015
+ }
1016
+ ChatSidebarComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: ChatSidebarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1017
+ 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: [
1018
+ trigger('logoActionsAnimation', [
1019
+ transition(':enter', [
1020
+ style({ opacity: 0, transform: 'translateX(20px)' }),
1021
+ animate('600ms ease-out', style({ opacity: 1, transform: 'translateX(0)' })),
1022
+ ]),
1023
+ transition(':leave', [animate('0ms ease-in', style({ opacity: 0, transform: 'translateX(20px)' }))]),
1024
+ ]),
1025
+ ] });
1026
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: ChatSidebarComponent, decorators: [{
1027
+ type: Component,
1028
+ args: [{ selector: 'ngx-chat-sidebar', animations: [
1029
+ trigger('logoActionsAnimation', [
1030
+ transition(':enter', [
1031
+ style({ opacity: 0, transform: 'translateX(20px)' }),
1032
+ animate('600ms ease-out', style({ opacity: 1, transform: 'translateX(0)' })),
1033
+ ]),
1034
+ transition(':leave', [animate('0ms ease-in', style({ opacity: 0, transform: 'translateX(20px)' }))]),
1035
+ ]),
1036
+ ], 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"] }]
1037
+ }], propDecorators: { historyGroupRef: [{
1038
+ type: ViewChild,
1039
+ args: ['historyGroupRef']
1040
+ }] } });
1041
+
1042
+ class NgxAgentChatComponent {
1043
+ constructor() {
1044
+ /**
1045
+ * 接口baseUrl配置
1046
+ */
1047
+ this.baseUrl = 'http://39.102.56.254:8082';
1048
+ this.token = '';
1049
+ this.md = inject(MarkdownStreamService);
1050
+ this.httpService = inject(HttpService$1);
1051
+ this.sseService = inject(SseService);
1052
+ this.storeService = inject(StoreService);
1053
+ this.sanitizer = inject(DomSanitizer);
1054
+ this.ngZone = inject(NgZone);
1055
+ this.messages = []; // 消息数组
1056
+ }
1057
+ ngOnInit() {
1058
+ this.storeService.setToken(this.token);
1059
+ this.storeService.setBaseUrl(this.baseUrl);
1060
+ this.sseService.conversationICode$.subscribe(iCodeObj => {
1061
+ if (iCodeObj) {
1062
+ const currentMsg = this.messages[this.messages.length - 1];
1063
+ currentMsg.iCode = iCodeObj.question;
1064
+ currentMsg.answers[currentMsg.answerIndex].iCode = iCodeObj.answer;
1065
+ }
1066
+ });
1067
+ this.sseService.thinking$.subscribe(text => {
1068
+ if (this.messages.length > 0) {
1069
+ const currentMsg = this.messages[this.messages.length - 1];
1070
+ currentMsg.answers[currentMsg.answerIndex].think = text;
1071
+ }
1072
+ });
1073
+ this.sseService.currentMessage$.subscribe(text => {
1074
+ if (this.messages.length > 0) {
1075
+ const currentMsg = this.messages[this.messages.length - 1];
1076
+ currentMsg.answers[currentMsg.answerIndex].content = text;
1077
+ }
1078
+ });
1079
+ this.storeService.currentConversation$.subscribe(conversation => {
1080
+ if (!conversation) {
1081
+ this.messages = [];
1082
+ return;
1083
+ }
1084
+ this.httpService
1085
+ .get(`${this.storeService.getBaseUrl()}/api/ChatModel/conversation/questions`, { iCode: conversation?.iCode })
1086
+ .subscribe((res) => {
1087
+ this.messages = res.map(item => {
1088
+ item.answerIndex = 0;
1089
+ item.answers.forEach(answer => {
1090
+ answer.thinkExpand = true;
1091
+ });
1092
+ return item;
1093
+ });
1094
+ });
1095
+ });
1096
+ }
1097
+ ngAfterViewInit() {
1098
+ this.resizeObserver = new ResizeObserver(entries => {
1099
+ for (let entry of entries) {
1100
+ const newHeight = entry.contentRect.height;
1101
+ this.ngZone.run(() => {
1102
+ this.chatMessagesRef.inputHeight = newHeight + 60;
1103
+ });
1104
+ }
1105
+ });
1106
+ // 开始监听输入框元素
1107
+ this.resizeObserver.observe(this.chatInputRef.chatInputWrapper.nativeElement);
1108
+ }
1109
+ // 重新生成回答(仅最新的回答允许重新生成)
1110
+ regenerateAnswer(msg) {
1111
+ this.chatInputRef.regenerateSend(msg);
1112
+ }
1113
+ // 发起提问请求
1114
+ onSendMessage(event) {
1115
+ if (!event.isRegenerate) {
1116
+ // 推入一个新问答,iCode在流接收到补上
1117
+ const qa = {
1118
+ iCode: '',
1119
+ question: event.message.question,
1120
+ answers: [
1121
+ {
1122
+ iCode: '',
1123
+ think: '',
1124
+ content: '',
1125
+ thinkExpand: true,
1126
+ },
1127
+ ],
1128
+ answerIndex: 0,
1129
+ };
1130
+ this.messages.push(qa);
1131
+ }
1132
+ else {
1133
+ const latestMessage = this.messages[this.messages.length - 1];
1134
+ latestMessage.answers.push({
1135
+ iCode: '',
1136
+ think: '',
1137
+ content: '',
1138
+ thinkExpand: true,
1139
+ });
1140
+ latestMessage.answerIndex = latestMessage.answers.length - 1;
1141
+ }
1142
+ this.sseService.streamChat(event.message).then(res => {
1143
+ // 开启新对话,刷新左侧历史对话
1144
+ if (!event.message.conversation) {
1145
+ this.chatSidebarRef.getHistory(true);
1146
+ }
1147
+ });
1148
+ }
1149
+ }
1150
+ NgxAgentChatComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: NgxAgentChatComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1151
+ 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" }] });
1152
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: NgxAgentChatComponent, decorators: [{
1153
+ type: Component,
1154
+ 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"] }]
1155
+ }], propDecorators: { baseUrl: [{
1156
+ type: Input
1157
+ }], token: [{
1158
+ type: Input
1159
+ }], chatSidebarRef: [{
1160
+ type: ViewChild,
1161
+ args: ['chatSidebarRef']
1162
+ }], chatMessagesRef: [{
1163
+ type: ViewChild,
1164
+ args: ['chatMessagesRef']
1165
+ }], chatInputRef: [{
1166
+ type: ViewChild,
1167
+ args: ['chatInputRef']
1168
+ }] } });
1169
+
1170
+ class AgentChatModule {
1171
+ }
1172
+ AgentChatModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: AgentChatModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
1173
+ AgentChatModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "15.2.10", ngImport: i0, type: AgentChatModule, declarations: [NgxAgentChatComponent,
1174
+ ChatInputComponent,
1175
+ ChatMessagesComponent,
1176
+ ChatSidebarComponent,
1177
+ SimplePaginationComponent,
1178
+ HistoryGroupComponent], imports: [CommonModule,
1179
+ NzAvatarModule,
1180
+ NzSpinModule,
1181
+ NzTagModule,
1182
+ NzInputModule,
1183
+ NzButtonModule,
1184
+ NzListModule,
1185
+ NzSliderModule,
1186
+ NzLayoutModule,
1187
+ NzPopoverModule,
1188
+ NzRadioModule,
1189
+ NzCheckboxModule,
1190
+ NzIconModule,
1191
+ NzPaginationModule,
1192
+ FormsModule,
1193
+ MarkdownPipe], exports: [NgxAgentChatComponent] });
1194
+ AgentChatModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: AgentChatModule, providers: [NzMessageService], imports: [CommonModule,
1195
+ NzAvatarModule,
1196
+ NzSpinModule,
1197
+ NzTagModule,
1198
+ NzInputModule,
1199
+ NzButtonModule,
1200
+ NzListModule,
1201
+ NzSliderModule,
1202
+ NzLayoutModule,
1203
+ NzPopoverModule,
1204
+ NzRadioModule,
1205
+ NzCheckboxModule,
1206
+ NzIconModule,
1207
+ NzPaginationModule,
1208
+ FormsModule] });
1209
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: AgentChatModule, decorators: [{
1210
+ type: NgModule,
1211
+ args: [{
1212
+ declarations: [
1213
+ NgxAgentChatComponent,
1214
+ ChatInputComponent,
1215
+ ChatMessagesComponent,
1216
+ ChatSidebarComponent,
1217
+ SimplePaginationComponent,
1218
+ HistoryGroupComponent,
1219
+ ],
1220
+ imports: [
1221
+ CommonModule,
1222
+ NzAvatarModule,
1223
+ NzSpinModule,
1224
+ NzTagModule,
1225
+ NzInputModule,
1226
+ NzButtonModule,
1227
+ NzListModule,
1228
+ NzSliderModule,
1229
+ NzLayoutModule,
1230
+ NzPopoverModule,
1231
+ NzRadioModule,
1232
+ NzCheckboxModule,
1233
+ NzIconModule,
1234
+ NzPaginationModule,
1235
+ FormsModule,
1236
+ MarkdownPipe,
1237
+ ],
1238
+ providers: [NzMessageService],
1239
+ exports: [NgxAgentChatComponent],
1240
+ }]
1241
+ }] });
1242
+
1243
+ /**
1244
+ * Generated bundle index. Do not edit.
1245
+ */
1246
+
1247
+ export { AgentChatModule, NgxAgentChatComponent };
1248
+ //# sourceMappingURL=qqsl-agent-chat.mjs.map