@will1123/lx-ui-utils 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,618 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
3
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
4
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
5
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
6
+ var __spreadValues = (a, b) => {
7
+ for (var prop in b || (b = {}))
8
+ if (__hasOwnProp.call(b, prop))
9
+ __defNormalProp(a, prop, b[prop]);
10
+ if (__getOwnPropSymbols)
11
+ for (var prop of __getOwnPropSymbols(b)) {
12
+ if (__propIsEnum.call(b, prop))
13
+ __defNormalProp(a, prop, b[prop]);
14
+ }
15
+ return a;
16
+ };
17
+ var __async = (__this, __arguments, generator) => {
18
+ return new Promise((resolve, reject) => {
19
+ var fulfilled = (value) => {
20
+ try {
21
+ step(generator.next(value));
22
+ } catch (e) {
23
+ reject(e);
24
+ }
25
+ };
26
+ var rejected = (value) => {
27
+ try {
28
+ step(generator.throw(value));
29
+ } catch (e) {
30
+ reject(e);
31
+ }
32
+ };
33
+ var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
34
+ step((generator = generator.apply(__this, __arguments)).next());
35
+ });
36
+ };
37
+ class SSEConnection {
38
+ constructor() {
39
+ this.controller = null;
40
+ this.eventSource = null;
41
+ this.timeoutTimer = null;
42
+ }
43
+ /**
44
+ * 发起 SSE 请求
45
+ */
46
+ request(config, handlers) {
47
+ return __async(this, null, function* () {
48
+ var _a, _b, _c, _d, _e;
49
+ const { url, method = "GET", headers = {}, body, timeout = 3e4 } = config;
50
+ this.controller = new AbortController();
51
+ try {
52
+ if (timeout > 0) {
53
+ this.timeoutTimer = setTimeout(() => {
54
+ var _a2;
55
+ this.close();
56
+ (_a2 = handlers.onError) == null ? void 0 : _a2.call(handlers, new Error(`Request timeout after ${timeout}ms`));
57
+ }, timeout);
58
+ }
59
+ const response = yield fetch(url, {
60
+ method,
61
+ headers: __spreadValues({
62
+ "Content-Type": "application/json"
63
+ }, headers),
64
+ body: method === "POST" ? JSON.stringify(body) : void 0,
65
+ signal: this.controller.signal
66
+ });
67
+ if (this.timeoutTimer) {
68
+ clearTimeout(this.timeoutTimer);
69
+ this.timeoutTimer = null;
70
+ }
71
+ if (!response.ok) {
72
+ throw new Error(`HTTP error! status: ${response.status}`);
73
+ }
74
+ const contentType = response.headers.get("content-type");
75
+ if (!(contentType == null ? void 0 : contentType.includes("text/event-stream"))) {
76
+ throw new Error("Response is not a Server-Sent Events stream");
77
+ }
78
+ (_a = handlers.onOpen) == null ? void 0 : _a.call(handlers);
79
+ const reader = (_b = response.body) == null ? void 0 : _b.getReader();
80
+ const decoder = new TextDecoder();
81
+ let buffer = "";
82
+ if (!reader) {
83
+ throw new Error("Response body is null");
84
+ }
85
+ while (true) {
86
+ const { done, value } = yield reader.read();
87
+ if (done) {
88
+ (_c = handlers.onClose) == null ? void 0 : _c.call(handlers);
89
+ break;
90
+ }
91
+ buffer += decoder.decode(value, { stream: true });
92
+ const lines = buffer.split("\n\n");
93
+ buffer = lines.pop() || "";
94
+ for (const line of lines) {
95
+ if (line.trim()) {
96
+ const message = this.parseMessage(line);
97
+ (_d = handlers.onMessage) == null ? void 0 : _d.call(handlers, message);
98
+ }
99
+ }
100
+ }
101
+ } catch (error) {
102
+ if (this.timeoutTimer) {
103
+ clearTimeout(this.timeoutTimer);
104
+ this.timeoutTimer = null;
105
+ }
106
+ if (this.controller && !this.controller.signal.aborted) {
107
+ (_e = handlers.onError) == null ? void 0 : _e.call(handlers, error);
108
+ }
109
+ }
110
+ });
111
+ }
112
+ /**
113
+ * 使用 EventSource 连接(仅支持 GET 请求)
114
+ */
115
+ connect(url, handlers) {
116
+ this.eventSource = new EventSource(url);
117
+ this.eventSource.onopen = () => {
118
+ var _a;
119
+ (_a = handlers.onOpen) == null ? void 0 : _a.call(handlers);
120
+ };
121
+ this.eventSource.onmessage = (event) => {
122
+ var _a;
123
+ (_a = handlers.onMessage) == null ? void 0 : _a.call(handlers, {
124
+ data: event.data,
125
+ event: event.type
126
+ });
127
+ };
128
+ this.eventSource.onerror = (error) => {
129
+ var _a;
130
+ (_a = handlers.onError) == null ? void 0 : _a.call(handlers, new Error("EventSource error"));
131
+ this.close();
132
+ };
133
+ }
134
+ /**
135
+ * 解析 SSE 消息
136
+ */
137
+ parseMessage(line) {
138
+ const message = {
139
+ data: ""
140
+ };
141
+ const lines = line.split("\n");
142
+ for (const l of lines) {
143
+ if (l.startsWith("data: ")) {
144
+ message.data = l.slice(6);
145
+ } else if (l.startsWith("event: ")) {
146
+ message.event = l.slice(7);
147
+ } else if (l.startsWith("id: ")) {
148
+ message.id = l.slice(4);
149
+ } else if (l.startsWith("retry: ")) {
150
+ message.retry = parseInt(l.slice(7));
151
+ }
152
+ }
153
+ return message;
154
+ }
155
+ /**
156
+ * 关闭连接
157
+ */
158
+ close() {
159
+ if (this.controller) {
160
+ this.controller.abort();
161
+ this.controller = null;
162
+ }
163
+ if (this.eventSource) {
164
+ this.eventSource.close();
165
+ this.eventSource = null;
166
+ }
167
+ if (this.timeoutTimer) {
168
+ clearTimeout(this.timeoutTimer);
169
+ this.timeoutTimer = null;
170
+ }
171
+ }
172
+ }
173
+ function sse(config, handlers) {
174
+ const connection = new SSEConnection();
175
+ if (config.method === "POST" || config.timeout) {
176
+ connection.request(config, handlers);
177
+ } else {
178
+ connection.connect(config.url, handlers);
179
+ }
180
+ return connection;
181
+ }
182
+ function extractThinking(text, config = {}) {
183
+ const { tagName = "think", includeTags = false, removeRest = false } = config;
184
+ const openTag = `<${tagName}>`;
185
+ const closeTag = `</${tagName}>`;
186
+ const openIndex = text.indexOf(openTag);
187
+ const closeIndex = text.indexOf(closeTag);
188
+ if (openIndex === -1 || closeIndex === -1) {
189
+ return {
190
+ thinking: "",
191
+ rest: text,
192
+ hasThinking: false
193
+ };
194
+ }
195
+ const thinkingStart = openIndex + openTag.length;
196
+ const thinkingEnd = closeIndex;
197
+ const thinkingContent = text.slice(thinkingStart, thinkingEnd);
198
+ const beforeContent = text.slice(0, openIndex);
199
+ const afterContent = text.slice(closeIndex + closeTag.length);
200
+ const restContent = beforeContent + afterContent;
201
+ return {
202
+ thinking: includeTags ? `${openTag}${thinkingContent}${closeTag}` : thinkingContent,
203
+ rest: removeRest ? "" : restContent,
204
+ hasThinking: true
205
+ };
206
+ }
207
+ class ThinkingStreamExtractor {
208
+ constructor(config = {}) {
209
+ this.isComplete = false;
210
+ this.config = {
211
+ tagName: config.tagName || "think",
212
+ includeTags: config.includeTags || false,
213
+ removeRest: config.removeRest || false
214
+ };
215
+ this.state = {
216
+ inThinking: false,
217
+ tagBuffer: "",
218
+ contentBuffer: "",
219
+ beforeContent: "",
220
+ afterContent: ""
221
+ };
222
+ }
223
+ /**
224
+ * 追加文本并提取思考内容
225
+ */
226
+ append(text) {
227
+ if (this.isComplete) {
228
+ return {
229
+ thinking: this.getThinking(),
230
+ rest: this.getRest(),
231
+ isComplete: true
232
+ };
233
+ }
234
+ const { tagName } = this.config;
235
+ const openTag = `<${tagName}>`;
236
+ const closeTag = `</${tagName}>`;
237
+ for (let i = 0; i < text.length; i++) {
238
+ const char = text[i];
239
+ if (!this.state.inThinking) {
240
+ this.state.tagBuffer += char;
241
+ if (this.state.tagBuffer.endsWith(openTag)) {
242
+ const tagStart = this.state.tagBuffer.length - openTag.length;
243
+ this.state.beforeContent += this.state.tagBuffer.slice(0, tagStart);
244
+ this.state.tagBuffer = "";
245
+ this.state.inThinking = true;
246
+ continue;
247
+ }
248
+ if (this.state.tagBuffer.length > openTag.length) {
249
+ this.state.beforeContent += this.state.tagBuffer.slice(0, 1);
250
+ this.state.tagBuffer = this.state.tagBuffer.slice(1);
251
+ }
252
+ } else {
253
+ this.state.tagBuffer += char;
254
+ if (this.state.tagBuffer.endsWith(closeTag)) {
255
+ const tagEnd = this.state.tagBuffer.length - closeTag.length;
256
+ this.state.contentBuffer += this.state.tagBuffer.slice(0, tagEnd);
257
+ this.state.tagBuffer = "";
258
+ this.state.inThinking = false;
259
+ this.isComplete = true;
260
+ break;
261
+ }
262
+ if (this.state.tagBuffer.length > closeTag.length) {
263
+ const overflow = this.state.tagBuffer.slice(0, 1);
264
+ this.state.contentBuffer += overflow;
265
+ this.state.tagBuffer = this.state.tagBuffer.slice(1);
266
+ }
267
+ }
268
+ }
269
+ if (!this.state.inThinking && this.state.tagBuffer) {
270
+ if (this.isComplete) {
271
+ this.state.afterContent += this.state.tagBuffer;
272
+ } else {
273
+ this.state.beforeContent += this.state.tagBuffer;
274
+ }
275
+ this.state.tagBuffer = "";
276
+ }
277
+ return {
278
+ thinking: this.getThinking(),
279
+ rest: this.getRest(),
280
+ isComplete: this.isComplete
281
+ };
282
+ }
283
+ /**
284
+ * 获取当前提取的思考内容
285
+ */
286
+ getThinking() {
287
+ const { tagName, includeTags } = this.config;
288
+ const openTag = `<${tagName}>`;
289
+ const closeTag = `</${tagName}>`;
290
+ const content = this.state.contentBuffer;
291
+ if (includeTags) {
292
+ return this.state.inThinking ? `${openTag}${content}` : `${openTag}${content}${closeTag}`;
293
+ }
294
+ return content;
295
+ }
296
+ /**
297
+ * 获取剩余内容
298
+ */
299
+ getRest() {
300
+ const { removeRest } = this.config;
301
+ if (removeRest) {
302
+ return "";
303
+ }
304
+ return this.state.beforeContent + this.state.afterContent;
305
+ }
306
+ /**
307
+ * 是否已完成提取
308
+ */
309
+ complete() {
310
+ return this.isComplete;
311
+ }
312
+ /**
313
+ * 重置提取器
314
+ */
315
+ reset() {
316
+ this.state = {
317
+ inThinking: false,
318
+ tagBuffer: "",
319
+ contentBuffer: "",
320
+ beforeContent: "",
321
+ afterContent: ""
322
+ };
323
+ this.isComplete = false;
324
+ }
325
+ }
326
+ function createThinkingExtractor(config) {
327
+ return new ThinkingStreamExtractor(config);
328
+ }
329
+ class MarkdownRenderer {
330
+ constructor(config = {}) {
331
+ this.config = {
332
+ highlight: config.highlight || false,
333
+ highlightLanguages: config.highlightLanguages || {},
334
+ taskList: config.taskList !== false,
335
+ table: config.table !== false,
336
+ link: config.link !== false,
337
+ image: config.image !== false
338
+ };
339
+ }
340
+ /**
341
+ * 渲染 Markdown 为 HTML
342
+ */
343
+ render(markdown) {
344
+ let html = markdown;
345
+ html = this.escapeHtml(html);
346
+ html = this.renderCodeBlocks(html);
347
+ html = this.renderHeadings(html);
348
+ html = this.renderEmphasis(html);
349
+ if (this.config.taskList) {
350
+ html = this.renderTaskLists(html);
351
+ }
352
+ html = this.renderUnorderedLists(html);
353
+ html = this.renderOrderedLists(html);
354
+ if (this.config.table) {
355
+ html = this.renderTables(html);
356
+ }
357
+ if (this.config.link) {
358
+ html = this.renderLinks(html);
359
+ }
360
+ if (this.config.image) {
361
+ html = this.renderImages(html);
362
+ }
363
+ html = this.renderParagraphs(html);
364
+ html = this.renderLineBreaks(html);
365
+ return html;
366
+ }
367
+ /**
368
+ * 转义 HTML 特殊字符
369
+ */
370
+ escapeHtml(text) {
371
+ return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
372
+ }
373
+ /**
374
+ * 渲染代码块
375
+ */
376
+ renderCodeBlocks(text) {
377
+ return text.replace(
378
+ /`{3}(\w*)\n([\s\S]*?)`{3}/g,
379
+ (match, lang, code) => {
380
+ const language = lang || "text";
381
+ return `<pre><code class="language-${language}">${code.trim()}</code></pre>`;
382
+ }
383
+ );
384
+ }
385
+ /**
386
+ * 渲染标题
387
+ */
388
+ renderHeadings(text) {
389
+ return text.replace(/^#{6}\s+(.+)$/gm, "<h6>$1</h6>").replace(/^#{5}\s+(.+)$/gm, "<h5>$1</h5>").replace(/^#{4}\s+(.+)$/gm, "<h4>$1</h4>").replace(/^#{3}\s+(.+)$/gm, "<h3>$1</h3>").replace(/^#{2}\s+(.+)$/gm, "<h2>$1</h2>").replace(/^#{1}\s+(.+)$/gm, "<h1>$1</h1>");
390
+ }
391
+ /**
392
+ * 渲染粗体和斜体
393
+ */
394
+ renderEmphasis(text) {
395
+ text = text.replace(/\*\*\*(.+?)\*\*\*/g, "<strong><em>$1</em></strong>");
396
+ text = text.replace(/\*\*(.+?)\*\*/g, "<strong>$1</strong>");
397
+ text = text.replace(/___(.+?)___/g, "<strong><em>$1</em></strong>");
398
+ text = text.replace(/__(.+?)__/g, "<strong>$1</strong>");
399
+ text = text.replace(/\*(.+?)\*/g, "<em>$1</em>");
400
+ text = text.replace(/_(.+?)_/g, "<em>$1</em>");
401
+ return text;
402
+ }
403
+ /**
404
+ * 渲染任务列表
405
+ */
406
+ renderTaskLists(text) {
407
+ return text.replace(/^\s*-\s*\[x\]\s+(.+)$/gm, '<li class="task-checked"><input type="checkbox" checked disabled> $1</li>').replace(/^\s*-\s*\[\s*\]\s+(.+)$/gm, '<li><input type="checkbox" disabled> $1</li>');
408
+ }
409
+ /**
410
+ * 渲染无序列表
411
+ */
412
+ renderUnorderedLists(text) {
413
+ const lines = text.split("\n");
414
+ const result = [];
415
+ const listStack = [];
416
+ for (const line of lines) {
417
+ const match = line.match(/^(\s*)-\s+(.+)$/);
418
+ if (!match) {
419
+ while (listStack.length > 0) {
420
+ result.push(listStack.pop() || "");
421
+ }
422
+ result.push(line);
423
+ continue;
424
+ }
425
+ const [_, indent, content] = match;
426
+ const indentLevel = indent.length;
427
+ while (listStack.length > 0 && listStack.length < indentLevel / 2 + 1) {
428
+ result.push(listStack.pop() || "");
429
+ }
430
+ while (listStack.length < indentLevel / 2 + 1) {
431
+ listStack.push("<ul>");
432
+ result.push("<ul>");
433
+ }
434
+ result.push(`<li>${content}</li>`);
435
+ }
436
+ while (listStack.length > 0) {
437
+ result.push("</ul>");
438
+ listStack.pop();
439
+ }
440
+ return result.join("\n");
441
+ }
442
+ /**
443
+ * 渲染有序列表
444
+ */
445
+ renderOrderedLists(text) {
446
+ const lines = text.split("\n");
447
+ const result = [];
448
+ let inList = false;
449
+ for (const line of lines) {
450
+ const match = line.match(/^\s*\d+\.\s+(.+)$/);
451
+ if (!match) {
452
+ if (inList) {
453
+ result.push("</ol>");
454
+ inList = false;
455
+ }
456
+ result.push(line);
457
+ continue;
458
+ }
459
+ const [_, content] = match;
460
+ if (!inList) {
461
+ result.push("<ol>");
462
+ inList = true;
463
+ }
464
+ result.push(`<li>${content}</li>`);
465
+ }
466
+ if (inList) {
467
+ result.push("</ol>");
468
+ }
469
+ return result.join("\n");
470
+ }
471
+ /**
472
+ * 渲染表格
473
+ */
474
+ renderTables(text) {
475
+ const lines = text.split("\n");
476
+ const result = [];
477
+ let inTable = false;
478
+ let headerRendered = false;
479
+ for (let i = 0; i < lines.length; i++) {
480
+ const line = lines[i];
481
+ if (line.match(/^\|[\s\-:|]+\|$/)) {
482
+ continue;
483
+ }
484
+ const match = line.match(/^\|(.+)\|$/);
485
+ if (!match) {
486
+ if (inTable) {
487
+ result.push("</table>");
488
+ inTable = false;
489
+ headerRendered = false;
490
+ }
491
+ result.push(line);
492
+ continue;
493
+ }
494
+ const cells = match[1].split("|").map((cell) => cell.trim());
495
+ if (!inTable) {
496
+ result.push("<table>");
497
+ inTable = true;
498
+ }
499
+ if (!headerRendered) {
500
+ result.push("<thead><tr>");
501
+ cells.forEach((cell) => {
502
+ result.push(`<th>${cell}</th>`);
503
+ });
504
+ result.push("</tr></thead><tbody>");
505
+ headerRendered = true;
506
+ } else {
507
+ result.push("<tr>");
508
+ cells.forEach((cell) => {
509
+ result.push(`<td>${cell}</td>`);
510
+ });
511
+ result.push("</tr>");
512
+ }
513
+ }
514
+ if (inTable) {
515
+ result.push("</tbody></table>");
516
+ }
517
+ return result.join("\n");
518
+ }
519
+ /**
520
+ * 渲染链接
521
+ */
522
+ renderLinks(text) {
523
+ return text.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2" target="_blank">$1</a>');
524
+ }
525
+ /**
526
+ * 渲染图片
527
+ */
528
+ renderImages(text) {
529
+ return text.replace(/!\[([^\]]*)\]\(([^)]+)\)/g, '<img src="$2" alt="$1" />');
530
+ }
531
+ /**
532
+ * 渲染段落
533
+ */
534
+ renderParagraphs(text) {
535
+ const lines = text.split("\n");
536
+ const result = [];
537
+ let inParagraph = false;
538
+ for (const line of lines) {
539
+ if (!line.trim() || line.match(/^<(h|ul|ol|li|table|pre|div)/)) {
540
+ if (inParagraph) {
541
+ result.push("</p>");
542
+ inParagraph = false;
543
+ }
544
+ result.push(line);
545
+ continue;
546
+ }
547
+ if (!inParagraph) {
548
+ result.push("<p>");
549
+ inParagraph = true;
550
+ }
551
+ result.push(line);
552
+ }
553
+ if (inParagraph) {
554
+ result.push("</p>");
555
+ }
556
+ return result.join("\n");
557
+ }
558
+ /**
559
+ * 渲染换行
560
+ */
561
+ renderLineBreaks(text) {
562
+ return text.replace(/(<p>.*?<\/p>|<li>.*?<\/li>)/g, (match) => {
563
+ return match.replace(/\n/g, "<br>\n");
564
+ });
565
+ }
566
+ }
567
+ function renderMarkdown(markdown, config) {
568
+ const renderer = new MarkdownRenderer(config);
569
+ return renderer.render(markdown);
570
+ }
571
+ class MarkdownStreamRenderer {
572
+ constructor(config = {}) {
573
+ this.buffer = "";
574
+ this.config = config;
575
+ this.renderer = new MarkdownRenderer(config);
576
+ }
577
+ /**
578
+ * 追加文本并渲染
579
+ */
580
+ append(text) {
581
+ this.buffer += text;
582
+ const html = this.renderer.render(this.buffer);
583
+ if (this.config.onUpdate) {
584
+ this.config.onUpdate(html);
585
+ }
586
+ return html;
587
+ }
588
+ /**
589
+ * 完成渲染
590
+ */
591
+ complete() {
592
+ const html = this.renderer.render(this.buffer);
593
+ if (this.config.onComplete) {
594
+ this.config.onComplete(html);
595
+ }
596
+ return html;
597
+ }
598
+ /**
599
+ * 重置渲染器
600
+ */
601
+ reset() {
602
+ this.buffer = "";
603
+ }
604
+ }
605
+ function createMarkdownRenderer(config) {
606
+ return new MarkdownStreamRenderer(config);
607
+ }
608
+ export {
609
+ MarkdownRenderer,
610
+ MarkdownStreamRenderer,
611
+ SSEConnection,
612
+ ThinkingStreamExtractor,
613
+ createMarkdownRenderer,
614
+ createThinkingExtractor,
615
+ extractThinking,
616
+ renderMarkdown,
617
+ sse
618
+ };