@mujian/js-sdk 0.0.6-beta.8 → 0.0.6-beta.mjv.66

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 (35) hide show
  1. package/dist/events/index.d.ts +30 -2
  2. package/dist/index.d.ts +13 -39
  3. package/dist/index.js +329 -16
  4. package/dist/modules/ai/chat/chat.d.ts +46 -4
  5. package/dist/modules/ai/chat/index.d.ts +1 -1
  6. package/dist/modules/ai/chat/message/index.d.ts +4 -0
  7. package/dist/modules/ai/index.d.ts +3 -2
  8. package/dist/modules/ai/openai/chat.d.ts +25 -0
  9. package/dist/modules/ai/openai/completions.d.ts +19 -0
  10. package/dist/modules/ai/openai/images.d.ts +19 -0
  11. package/dist/modules/ai/openai/index.d.ts +4 -0
  12. package/dist/modules/ai/openai/responses.d.ts +20 -0
  13. package/dist/modules/ai/text/index.d.ts +9 -2
  14. package/dist/modules/utils/clipboard.d.ts +5 -0
  15. package/dist/modules/utils/index.d.ts +4 -0
  16. package/dist/react/chat/useChat/index.d.ts +7 -3
  17. package/dist/react/chat/useChat/inner/chatStreaming.d.ts +2 -1
  18. package/dist/react/chat/useChat/message.d.ts +25 -13
  19. package/dist/react/components/MdRenderer/index.d.ts +5 -4
  20. package/dist/react/components/MdRenderer/utils/height.d.ts +0 -0
  21. package/dist/react/components/MdRenderer/utils/iframe.d.ts +9 -0
  22. package/dist/react/components/MdRenderer/utils/scripts.d.ts +4 -0
  23. package/dist/react/components/MujianSpinner/index.d.ts +7 -0
  24. package/dist/react/components/index.d.ts +1 -0
  25. package/dist/react.css +65 -4
  26. package/dist/react.js +697 -146
  27. package/dist/types/index.d.ts +38 -0
  28. package/dist/umd/index.js +792 -0
  29. package/dist/umd/index.js.LICENSE.txt +7 -0
  30. package/dist/umd/react.css +343 -0
  31. package/dist/umd/react.js +8880 -0
  32. package/dist/umd/react.js.LICENSE.txt +31 -0
  33. package/dist/utils/log.d.ts +4 -0
  34. package/package.json +4 -1
  35. /package/dist/react/components/MdRenderer/{utils.d.ts → utils/md.d.ts} +0 -0
package/dist/react.js CHANGED
@@ -1,11 +1,180 @@
1
- import { useMount, useRequest, useUpdateEffect } from "ahooks";
2
- import react, { createContext, forwardRef, useCallback, useContext, useEffect, useState } from "react";
3
- import { jsx } from "react/jsx-runtime";
1
+ import { useInfiniteScroll, useLatest, useRequest, useUpdateEffect } from "ahooks";
2
+ import react, { createContext, forwardRef, useCallback, useContext, useEffect, useRef, useState } from "react";
3
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
4
4
  import css_tools from "@adobe/css-tools";
5
5
  import dompurify from "dompurify";
6
6
  import showdown from "showdown";
7
+ import { v4 } from "uuid";
7
8
  import postmate from "postmate";
8
9
  import { Virtualizer } from "virtua";
10
+ const adjustIframeHeight = (iframeId)=>`
11
+ (function () {
12
+ let scheduled = false;
13
+ function measureAndPost() {
14
+ scheduled = false;
15
+ try {
16
+ const doc = window.document;
17
+ const body = doc.body;
18
+ const html = doc.documentElement;
19
+ if (!body || !html) {
20
+ return;
21
+ }
22
+ let height = 0;
23
+ // srcdoc 模式: 只用 body.scrollHeight
24
+ height = body.scrollHeight;
25
+ if (!Number.isFinite(height) || height <= 0) {
26
+ return;
27
+ }
28
+ window.parent.postMessage({ type: 'MJ_ADJUST_IFRAME_HEIGHT', iframe_id: \`${iframeId}\`, height: height }, '*');
29
+ } catch {
30
+ console.error('Error measuring iframe height');
31
+ }
32
+ }
33
+
34
+ function postIframeHeight() {
35
+ if (scheduled) {
36
+ return;
37
+ }
38
+ scheduled = true;
39
+ if (typeof window.requestAnimationFrame === 'function') {
40
+ window.requestAnimationFrame(measureAndPost);
41
+ } else {
42
+ setTimeout(measureAndPost, 500);
43
+ }
44
+ }
45
+
46
+ function observeHeightChange() {
47
+ const body = document.body;
48
+ if (!body) {
49
+ return;
50
+ }
51
+ const observer = new ResizeObserver(entries => {
52
+ postIframeHeight();
53
+ });
54
+ observer.observe(body);
55
+ }
56
+
57
+ function init() {
58
+ postIframeHeight();
59
+ observeHeightChange();
60
+ }
61
+
62
+ if (window.document.readyState === 'loading') {
63
+ window.document.addEventListener('DOMContentLoaded', init, { once: true });
64
+ } else {
65
+ init();
66
+ }
67
+ })();
68
+ `;
69
+ const iframeAdjustViewport = (parentHeight)=>`
70
+ $('html').css('--MJ-viewport-height', \`${parentHeight}px\`);
71
+ window.addEventListener('message', function (event) {
72
+ if (event.data?.type === 'MJ_UPDATE_VIEWPORT_HEIGHT') {
73
+ $('html').css('--MJ-viewport-height', \`${parentHeight}px\`);
74
+ }
75
+ });
76
+ `;
77
+ const init = (_iframeId, unsafe)=>unsafe ? "" : `
78
+ (async function () {
79
+ window.$mujian = window.parent.$mj_engine;
80
+ })();
81
+ `;
82
+ const thirdParty = `
83
+ <script src="https://cdn.jsdmirror.com/npm/@fortawesome/fontawesome-free/js/all.min.js"></script>
84
+ <script src="https://cdn.jsdmirror.com/npm/@tailwindcss/browser/dist/index.global.min.js"></script>
85
+ <script src="https://cdn.jsdmirror.com/npm/jquery/dist/jquery.min.js"></script>
86
+ <script src="https://cdn.jsdmirror.com/npm/jquery-ui/dist/jquery-ui.min.js"></script>
87
+ <link rel="stylesheet" href="https://cdn.jsdmirror.com/npm/jquery-ui/themes/base/theme.min.css" />
88
+ <script src="https://cdn.jsdmirror.com/npm/jquery-ui-touch-punch"></script>
89
+ `;
90
+ const unescapeHTML = (str)=>{
91
+ const named = {
92
+ amp: "&",
93
+ lt: "<",
94
+ gt: ">",
95
+ quot: '"',
96
+ apos: "'",
97
+ nbsp: "\u00A0"
98
+ };
99
+ return str.replace(/&(#x?[0-9a-fA-F]+|[a-zA-Z]+);/g, (_m, body)=>{
100
+ if ("#" === body[0]) {
101
+ const isHex = body[1]?.toLowerCase() === "x";
102
+ const numStr = isHex ? body.slice(2) : body.slice(1);
103
+ const codePoint = parseInt(numStr, isHex ? 16 : 10);
104
+ if (Number.isFinite(codePoint)) try {
105
+ return String.fromCodePoint(codePoint);
106
+ } catch {}
107
+ return _m;
108
+ }
109
+ const lower = body.toLowerCase();
110
+ return Object.hasOwn(named, lower) ? named[lower] : _m;
111
+ });
112
+ };
113
+ const replaceVhInContent = (content)=>{
114
+ const has_css_min_vh = /min-height\s*:\s*[^;{}]*\d+(?:\.\d+)?vh/gi.test(content);
115
+ const has_inline_style_vh = /style\s*=\s*(["'])[\s\S]*?min-height\s*:\s*[^;]*?\d+(?:\.\d+)?vh[\s\S]*?\1/gi.test(content);
116
+ const has_js_vh = /(\.style\.minHeight\s*=\s*(["']))([\s\S]*?vh)(\2)/gi.test(content) || /(setProperty\s*\(\s*(["'])min-height\2\s*,\s*(["']))([\s\S]*?vh)(\3\s*\))/gi.test(content);
117
+ if (!has_css_min_vh && !has_inline_style_vh && !has_js_vh) return content;
118
+ const convertVhToVariable = (value)=>value.replace(/(\d+(?:\.\d+)?)vh\b/gi, (match, value)=>{
119
+ const parsed = parseFloat(value);
120
+ if (!isFinite(parsed)) return match;
121
+ const VARIABLE_EXPRESSION = "var(--MJ-viewport-height)";
122
+ if (100 === parsed) return VARIABLE_EXPRESSION;
123
+ return `calc(${VARIABLE_EXPRESSION} * ${parsed / 100})`;
124
+ });
125
+ content = content.replace(/(min-height\s*:\s*)([^;{}]*?\d+(?:\.\d+)?vh)(?=\s*[;}])/gi, (_m, prefix, value)=>`${prefix}${convertVhToVariable(value)}`);
126
+ content = content.replace(/(style\s*=\s*(["']))([^"'"]*?)(\2)/gi, (match, prefix, _quote, styleContent, suffix)=>{
127
+ if (!/min-height\s*:\s*[^;]*vh/i.test(styleContent)) return match;
128
+ const replaced = styleContent.replace(/(min-height\s*:\s*)([^;]*?\d+(?:\.\d+)?vh)/gi, (_m, p1, p2)=>`${p1}${convertVhToVariable(p2)}`);
129
+ return `${prefix}${replaced}${suffix}`;
130
+ });
131
+ content = content.replace(/(\.style\.minHeight\s*=\s*(["']))([\s\S]*?)(\2)/gi, (match, prefix, _q, val, suffix)=>{
132
+ if (!/\b\d+(?:\.\d+)?vh\b/i.test(val)) return match;
133
+ const converted = convertVhToVariable(val);
134
+ return `${prefix}${converted}${suffix}`;
135
+ });
136
+ content = content.replace(/(setProperty\s*\(\s*(["'])min-height\2\s*,\s*(["']))([\s\S]*?)(\3\s*\))/gi, (match, prefix, _q1, _q2, val, suffix)=>{
137
+ if (!/\b\d+(?:\.\d+)?vh\b/i.test(val)) return match;
138
+ const converted = convertVhToVariable(val);
139
+ return `${prefix}${converted}${suffix}`;
140
+ });
141
+ return content;
142
+ };
143
+ function escapeHtmlAttribute(value) {
144
+ return value.replace(/"/g, "&quot;").replace(/'/g, "&#39;");
145
+ }
146
+ function createSrcContent(content, iframeId, unsafe) {
147
+ content = replaceVhInContent(content);
148
+ return `
149
+ <html>
150
+ <head>
151
+ <meta charset="utf-8">
152
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
153
+ <style>
154
+ *, *::before, *::after { box-sizing: border-box; }
155
+ html,body{margin:0!important;padding:0;overflow:hidden!important;max-width:100%!important;}
156
+ </style>
157
+ ${thirdParty}
158
+ <script>
159
+ ${init(iframeId, unsafe)}
160
+ ${adjustIframeHeight(iframeId)}
161
+ ${iframeAdjustViewport(window.innerHeight)}
162
+ </script>
163
+ </head>
164
+ <body>
165
+ ${content}
166
+ </body>
167
+ </html>
168
+ `;
169
+ }
170
+ const Log = {
171
+ i (...msg) {
172
+ console.log('[MujianSDK] ', ...msg);
173
+ },
174
+ e (...msg) {
175
+ console.error('[MujianSDK] ', ...msg);
176
+ }
177
+ };
9
178
  addDOMPurifyHooks();
10
179
  function canUseNegativeLookbehind() {
11
180
  try {
@@ -95,7 +264,7 @@ function addDOMPurifyHooks() {
95
264
  * @returns {string} Encoded message text
96
265
  * @copyright https://github.com/kwaroran/risuAI
97
266
  */ function encodeStyleTags(text) {
98
- const styleRegex = /<style>(.+?)<\/style>/gi;
267
+ const styleRegex = /<style>([\s\S]+?)<\/style>/gi;
99
268
  return text.replaceAll(styleRegex, (_, match)=>`<custom-style>${encodeURIComponent(match)}</custom-style>`);
100
269
  }
101
270
  /**
@@ -159,7 +328,7 @@ function addDOMPurifyHooks() {
159
328
  const markdownUnderscoreExt = ()=>{
160
329
  try {
161
330
  if (!canUseNegativeLookbehind()) {
162
- console.log('Showdown-underscore extension: Negative lookbehind not supported. Skipping.');
331
+ Log.i('Showdown-underscore extension: Negative lookbehind not supported. Skipping.');
163
332
  return [];
164
333
  }
165
334
  return [
@@ -234,60 +403,184 @@ function messageFormatting(mes, sanitizerOverrides = {}) {
234
403
  });
235
404
  return mes;
236
405
  }
237
- const unescapeHTML = (str)=>{
238
- const named = {
239
- amp: "&",
240
- lt: "<",
241
- gt: ">",
242
- quot: '"',
243
- apos: "'",
244
- nbsp: "\u00A0"
245
- };
246
- return str.replace(/&(#x?[0-9a-fA-F]+|[a-zA-Z]+);/g, (_m, body)=>{
247
- if ("#" === body[0]) {
248
- const isHex = body[1]?.toLowerCase() === "x";
249
- const numStr = isHex ? body.slice(2) : body.slice(1);
250
- const codePoint = parseInt(numStr, isHex ? 16 : 10);
251
- if (Number.isFinite(codePoint)) try {
252
- return String.fromCodePoint(codePoint);
253
- } catch {}
254
- return _m;
255
- }
256
- const lower = body.toLowerCase();
257
- return Object.hasOwn(named, lower) ? named[lower] : _m;
258
- });
259
- };
260
- const MdRendererBase = ({ content })=>{
261
- let mes = messageFormatting(content);
262
- mes = mes.replace(/<code(.*)>[\s\S]*?<\/code>/g, (match)=>{
263
- const code = match.replace("<code>", "").replace("</code>", "");
264
- const unescapedCode = unescapeHTML(code);
265
- const srcdoc = unescapedCode.replace(/'/g, "&#39;");
266
- return "<iframe sandbox='allow-scripts' loading='lazy' referrerpolicy='no-referrer' style='width: 100%; height: 600px; border: none;' srcdoc='" + srcdoc + "'></iframe>";
267
- });
406
+ const MdRendererBase = ({ content, unsafe = false })=>{
407
+ const containerRef = useRef(null);
408
+ const [iframeIdList, setIframeIdList] = useState([]);
409
+ useEffect(()=>{
410
+ let mes = messageFormatting(content);
411
+ mes = mes.replace(/<pre><code(.*)>[\s\S]*?<\/code><\/pre>/g, (match)=>{
412
+ if (!match.includes('&lt;body&gt;') && !match.includes('&lt;/body&gt;')) return match;
413
+ const code = match.replace(/<pre><code(.*?)>/g, '').replace(/<\/code><\/pre>/g, '');
414
+ const id = v4();
415
+ const containerId = `MJ-iframe-container-${id}`;
416
+ const iframeId = `MJ-iframe-${id}`;
417
+ const srcdoc = createSrcContent(unescapeHTML(code), iframeId, unsafe);
418
+ setIframeIdList((prev)=>[
419
+ ...prev,
420
+ id
421
+ ]);
422
+ const escapedSrcdoc = escapeHtmlAttribute(srcdoc);
423
+ return `
424
+ <div class="MJ-iframe-container" id="${containerId}" style="display:flex;width:100%;height:100%;position:relative;">
425
+ <div class="spin-overlay" id="MJ-spin-${id}" style="width:100%;height:100%;position:absolute;top:0;left:0;z-index:1000;background-color:rgba(0,0,0,0.5);display:flex;justify-content:center;align-items:center;">
426
+ <div class="spin-inner wave-text" style="font-weight:500;font-size:16px;color:white;display:flex;justify-content:center;align-items:center;">
427
+ <span>加</span>
428
+ <span>载</span>
429
+ <span>中</span>
430
+ <span>.</span>
431
+ <span>.</span>
432
+ <span>.</span>
433
+ </div>
434
+ </div>
435
+ <iframe id="${iframeId}" class="w-full" sandbox="${unsafe ? "allow-scripts" : "allow-scripts allow-same-origin"}" loading="lazy"
436
+ referrerpolicy="no-referrer" allowTransparency="true"
437
+ style="color-scheme: none;background-color: transparent;width:100%;;border:none;" srcdoc="` + escapedSrcdoc + `"></iframe>
438
+ </div>`;
439
+ });
440
+ if (containerRef.current) containerRef.current.innerHTML = mes;
441
+ }, [
442
+ content
443
+ ]);
444
+ useEffect(()=>{
445
+ window.addEventListener('message', function(event) {
446
+ if (event.data?.type === 'MJ_ADJUST_IFRAME_HEIGHT' && event.data?.iframe_id) {
447
+ const targetIframe = containerRef.current?.querySelector('#' + event.data.iframe_id);
448
+ if (!targetIframe) return;
449
+ targetIframe.style.height = `${event.data.height}px`;
450
+ }
451
+ });
452
+ window.addEventListener('resize', ()=>{
453
+ iframeIdList.forEach((id)=>{
454
+ const iframe = containerRef.current?.querySelector('#MJ-iframe-' + id);
455
+ if (!iframe) return;
456
+ iframe.contentWindow?.postMessage({
457
+ type: 'MJ_UPDATE_VIEWPORT_HEIGHT'
458
+ }, '*');
459
+ });
460
+ });
461
+ }, []);
462
+ useEffect(()=>{
463
+ iframeIdList.forEach((id)=>{
464
+ const iframe = containerRef.current?.querySelector('#MJ-iframe-' + id);
465
+ if (!iframe) return;
466
+ const removeSpin = ()=>{
467
+ const spinElement = iframe.parentElement?.querySelector('#MJ-spin-' + id);
468
+ if (spinElement) spinElement.remove();
469
+ };
470
+ const timeout = setTimeout(removeSpin, 5000);
471
+ iframe.addEventListener('load', ()=>{
472
+ clearTimeout(timeout);
473
+ removeSpin();
474
+ });
475
+ });
476
+ return ()=>{
477
+ iframeIdList.forEach((id)=>{
478
+ const iframe = containerRef.current?.querySelector('#MJ-iframe-' + id);
479
+ if (!iframe) return;
480
+ const spinElement = iframe.parentElement?.querySelector('#MJ-spin-' + iframe.id);
481
+ if (spinElement) spinElement.remove();
482
+ iframe.removeEventListener('load', ()=>{
483
+ Log.i('iframe loaded', iframe.id);
484
+ });
485
+ });
486
+ };
487
+ }, [
488
+ iframeIdList
489
+ ]);
268
490
  return /*#__PURE__*/ jsx("div", {
269
491
  className: "mes_text",
270
- dangerouslySetInnerHTML: {
271
- __html: mes
272
- }
492
+ ref: containerRef
273
493
  });
274
494
  };
275
495
  const MdRenderer = /*#__PURE__*/ react.memo(MdRendererBase, (prev, next)=>prev.content === next.content);
276
- MdRendererBase.displayName = "MdRenderer";
277
- MdRenderer.displayName = "MdRenderer";
278
- const chat_complete = async function(message, onData, signal) {
279
- return await this.call("mujian:ai:chat:complete", {
496
+ MdRendererBase.displayName = 'MdRenderer';
497
+ MdRenderer.displayName = 'MdRenderer';
498
+ var events_EVENT = /*#__PURE__*/ function(EVENT) {
499
+ EVENT["MUJIAN_INIT"] = "mujian:init";
500
+ EVENT["MUJIAN_AI_CHAT_STOP"] = "mujian:ai:chat:stop";
501
+ EVENT["MUJIAN_AI_CHAT_COMPLETE"] = "mujian:ai:chat:complete";
502
+ EVENT["MUJIAN_AI_CHAT_APPLY_REGEX"] = "mujian:ai:chat:applyRegex";
503
+ EVENT["MUJIAN_AI_CHAT_RENDER_MESSAGE"] = "mujian:ai:chat:renderMessage";
504
+ EVENT["MUJIAN_AI_TEXT_GENERATE"] = "mujian:ai:text:generate";
505
+ EVENT["MUJIAN_AI_OPENAI_COMPLETIONS_CREATE"] = "mujian:ai:openai:completions:create";
506
+ EVENT["MUJIAN_AI_OPENAI_CHAT_COMPLETIONS_CREATE"] = "mujian:ai:openai:chat:completions:create";
507
+ EVENT["MUJIAN_AI_OPENAI_RESPONSES_CREATE"] = "mujian:ai:openai:responses:create";
508
+ EVENT["MUJIAN_AI_CHAT_MESSAGE_GET_ALL"] = "mujian:ai:chat:message:getAll";
509
+ EVENT["MUJIAN_AI_CHAT_MESSAGE_GET_PAGE"] = "mujian:ai:chat:message:getPage";
510
+ EVENT["MUJIAN_AI_CHAT_PROJECT_GET_INFO"] = "mujian:ai:chat:project:getInfo";
511
+ EVENT["MUJIAN_AI_SETTINGS_PERSONA_GET_ACTIVE"] = "mujian:ai:settings:persona:getActive";
512
+ EVENT["MUJIAN_AI_SETTINGS_PERSONA_SET_ACTIVE"] = "mujian:ai:settings:persona:setActive";
513
+ EVENT["MUJIAN_AI_SETTINGS_MODEL_GET_ALL"] = "mujian:ai:settings:model:getAll";
514
+ EVENT["MUJIAN_AI_SETTINGS_MODEL_SET_ACTIVE"] = "mujian:ai:settings:model:setActive";
515
+ EVENT["MUJIAN_AI_SETTINGS_MODEL_GET_ACTIVE"] = "mujian:ai:settings:model:getActive";
516
+ EVENT["MUJIAN_AI_CHAT_MESSAGE_DELETE_ONE"] = "mujian:ai:chat:message:deleteOne";
517
+ EVENT["MUJIAN_AI_CHAT_MESSAGE_EDIT_ONE"] = "mujian:ai:chat:message:editOne";
518
+ EVENT["MUJIAN_AI_CHAT_MESSAGE_SWIPE"] = "mujian:ai:chat:message:swipe";
519
+ EVENT["MUJIAN_AI_CHAT_MESSAGE_GET_PROMPT"] = "mujian:ai:chat:message:getPrompt";
520
+ EVENT["MUJIAN_AI_OPENAI_IMAGES_GENERATE"] = "mujian:ai:openai:images:generate";
521
+ EVENT["MUJIAN_UTILS_CLIPBOARD_WRITE_TEXT"] = "mujian:utils:clipboard:writeText";
522
+ return EVENT;
523
+ }({});
524
+ function wrapOnData(onData) {
525
+ let fullContent = '';
526
+ let buffer = '';
527
+ let questionId;
528
+ let replyId;
529
+ return function(data) {
530
+ buffer += data;
531
+ const lines = buffer.split('\n');
532
+ buffer = lines.pop() || '';
533
+ for (const line of lines)if (line.startsWith('data: ')) try {
534
+ const parsedData = JSON.parse(line.slice(6));
535
+ if (parsedData.question_id) questionId = parsedData.question_id;
536
+ if (parsedData.reply_id) replyId = parsedData.reply_id;
537
+ if (parsedData.isFinished) return void onData({
538
+ isFinished: true,
539
+ deltaContent: '',
540
+ fullContent,
541
+ questionId,
542
+ replyId
543
+ });
544
+ const deltaContent = parsedData?.choices?.[0]?.delta?.content;
545
+ if (deltaContent?.length > 0) {
546
+ fullContent += deltaContent;
547
+ onData({
548
+ isFinished: false,
549
+ deltaContent: deltaContent,
550
+ fullContent,
551
+ questionId,
552
+ replyId
553
+ });
554
+ }
555
+ } catch (e) {
556
+ onData({
557
+ isFinished: true,
558
+ error: e,
559
+ deltaContent: '',
560
+ fullContent,
561
+ questionId,
562
+ replyId
563
+ });
564
+ return;
565
+ }
566
+ };
567
+ }
568
+ const chat_complete = async function(message, onData, signal, option = {}) {
569
+ await this.call(events_EVENT.MUJIAN_AI_CHAT_COMPLETE, {
280
570
  content: message
281
571
  }, {
282
- onData,
572
+ onData: option.parseContent ? wrapOnData(onData) : onData,
283
573
  signal
284
574
  });
285
575
  };
286
576
  const applyRegex = async function(props) {
287
- return await this.call("mujian:ai:chat:applyRegex", props);
577
+ return await this.call(events_EVENT.MUJIAN_AI_CHAT_APPLY_REGEX, props);
578
+ };
579
+ const renderMessage = async function(props) {
580
+ return await this.call(events_EVENT.MUJIAN_AI_CHAT_RENDER_MESSAGE, props);
288
581
  };
289
582
  const continueComplete = async function(onData, signal) {
290
- return await this.call("mujian:ai:chat:complete", {
583
+ return await this.call(events_EVENT.MUJIAN_AI_CHAT_COMPLETE, {
291
584
  isContinue: true
292
585
  }, {
293
586
  onData,
@@ -295,7 +588,7 @@ const continueComplete = async function(onData, signal) {
295
588
  });
296
589
  };
297
590
  const chat_regenerate = async function(onData, signal) {
298
- return await this.call("mujian:ai:chat:complete", {
591
+ return await this.call(events_EVENT.MUJIAN_AI_CHAT_COMPLETE, {
299
592
  isRegenerate: true
300
593
  }, {
301
594
  onData,
@@ -303,61 +596,117 @@ const chat_regenerate = async function(onData, signal) {
303
596
  });
304
597
  };
305
598
  const getAll = async function() {
306
- return await this.call("mujian:ai:chat:message:getAll");
599
+ return await this.call(events_EVENT.MUJIAN_AI_CHAT_MESSAGE_GET_ALL);
307
600
  };
308
601
  const messageDeleteOne = async function(messageId) {
309
- return await this.call("mujian:ai:chat:message:deleteOne", {
602
+ return await this.call(events_EVENT.MUJIAN_AI_CHAT_MESSAGE_DELETE_ONE, {
310
603
  messageId
311
604
  });
312
605
  };
313
606
  const messageEditOne = async function(messageId, content) {
314
- return await this.call("mujian:ai:chat:message:editOne", {
607
+ return await this.call(events_EVENT.MUJIAN_AI_CHAT_MESSAGE_EDIT_ONE, {
315
608
  messageId,
316
609
  content
317
610
  });
318
611
  };
319
612
  const messageSwipe = async function(messageId, swipeId) {
320
- return await this.call("mujian:ai:chat:message:swipe", {
613
+ return await this.call(events_EVENT.MUJIAN_AI_CHAT_MESSAGE_SWIPE, {
321
614
  messageId,
322
615
  swipeId
323
616
  });
324
617
  };
325
618
  const getPrompt = async function(messageId) {
326
- return await this.call("mujian:ai:chat:message:getPrompt", {
619
+ return await this.call(events_EVENT.MUJIAN_AI_CHAT_MESSAGE_GET_PROMPT, {
327
620
  messageId
328
621
  });
329
622
  };
623
+ async function getPage(fromCursor, pageSize) {
624
+ return await this.call(events_EVENT.MUJIAN_AI_CHAT_MESSAGE_GET_PAGE, {
625
+ fromCursor,
626
+ pageSize
627
+ });
628
+ }
330
629
  const getInfo = async function() {
331
- return await this.call("mujian:ai:chat:project:getInfo");
630
+ return await this.call(events_EVENT.MUJIAN_AI_CHAT_PROJECT_GET_INFO);
332
631
  };
333
632
  const getActive = async function() {
334
- return await this.call("mujian:ai:settings:persona:getActive");
633
+ return await this.call(events_EVENT.MUJIAN_AI_SETTINGS_PERSONA_GET_ACTIVE);
335
634
  };
336
635
  const setActive = async function(personaId) {
337
- return await this.call("mujian:ai:settings:persona:setActive", {
636
+ return await this.call(events_EVENT.MUJIAN_AI_SETTINGS_PERSONA_SET_ACTIVE, {
338
637
  personaId
339
638
  });
340
639
  };
341
640
  const model_getActive = async function() {
342
- return await this.call("mujian:ai:settings:model:getActive");
641
+ return await this.call(events_EVENT.MUJIAN_AI_SETTINGS_MODEL_GET_ACTIVE);
343
642
  };
344
643
  const model_setActive = async function(modelId) {
345
- return await this.call("mujian:ai:settings:model:setActive", {
644
+ return await this.call(events_EVENT.MUJIAN_AI_SETTINGS_MODEL_SET_ACTIVE, {
346
645
  modelId
347
646
  });
348
647
  };
349
648
  const model_getAll = async function() {
350
- return await this.call("mujian:ai:settings:model:getAll");
649
+ return await this.call(events_EVENT.MUJIAN_AI_SETTINGS_MODEL_GET_ALL);
351
650
  };
352
- const generate = async function() {
353
- return await this.call("mujian:ai:text:generate", {
354
- content: 'q'
651
+ const generate = async function(content) {
652
+ return await this.call(MujianSdk.EVENT.MUJIAN_AI_TEXT_GENERATE, {
653
+ content
355
654
  });
356
655
  };
656
+ const chat = {
657
+ completions: {
658
+ create: async function(params, options, onData, signal) {
659
+ return await this.call(MujianSdk.EVENT.MUJIAN_AI_OPENAI_CHAT_COMPLETIONS_CREATE, {
660
+ params,
661
+ options
662
+ }, {
663
+ onData,
664
+ signal
665
+ });
666
+ }
667
+ }
668
+ };
669
+ const completions = {
670
+ create: async function(params, options, onData, signal) {
671
+ return await this.call(MujianSdk.EVENT.MUJIAN_AI_OPENAI_COMPLETIONS_CREATE, {
672
+ params,
673
+ options
674
+ }, {
675
+ onData,
676
+ signal
677
+ });
678
+ }
679
+ };
680
+ const responses = {
681
+ create: async function(params, options, onData, signal) {
682
+ return await this.call(MujianSdk.EVENT.MUJIAN_AI_OPENAI_RESPONSES_CREATE, {
683
+ params,
684
+ options
685
+ }, {
686
+ onData,
687
+ signal
688
+ });
689
+ }
690
+ };
691
+ const images_images = {
692
+ generate: async function(params, options, onData, signal) {
693
+ return await this.call(MujianSdk.EVENT.MUJIAN_AI_OPENAI_IMAGES_GENERATE, {
694
+ params,
695
+ options
696
+ }, {
697
+ onData,
698
+ signal
699
+ });
700
+ }
701
+ };
357
702
  const saveGame = async function() {};
358
703
  const loadGame = async function() {};
704
+ async function writeText(text) {
705
+ return await this.call(events_EVENT.MUJIAN_UTILS_CLIPBOARD_WRITE_TEXT, text);
706
+ }
359
707
  class MujianSdk {
360
708
  constructor(){}
709
+ static EVENT = events_EVENT;
361
710
  static getInstance() {
362
711
  if (!window.$mujian) window.$mujian = new MujianSdk();
363
712
  return window.$mujian;
@@ -370,9 +719,14 @@ class MujianSdk {
370
719
  pendingRequests = new Map();
371
720
  async init() {
372
721
  const handshake = new postmate.Model({
373
- reply: ({ id, complete, data })=>{
722
+ reply: ({ id, complete, data, error })=>{
374
723
  const call = this.pendingRequests.get(id);
375
724
  if (!call) return;
725
+ if (error) {
726
+ call.reject(error);
727
+ this.pendingRequests.delete(id);
728
+ return;
729
+ }
376
730
  call.onData?.(data);
377
731
  if (complete) {
378
732
  call.onComplete?.();
@@ -385,10 +739,25 @@ class MujianSdk {
385
739
  const parent = await handshake;
386
740
  this.ready = true;
387
741
  this.parent = parent;
388
- console.log('mujian sdk client init');
389
- await this.call("mujian:init");
742
+ Log.i('mujian sdk client init');
743
+ await this.call(events_EVENT.MUJIAN_INIT);
744
+ const projectInfo = await this.ai.chat.project.getInfo();
745
+ if (projectInfo.config?.customCss) {
746
+ const style = document.createElement('style');
747
+ style.setAttribute('type', 'text/css');
748
+ style.setAttribute('id', 'mujian-custom-css');
749
+ style.textContent = projectInfo.config.customCss;
750
+ document.head.appendChild(style);
751
+ }
752
+ if (projectInfo.config?.customJs) {
753
+ const script = document.createElement("script");
754
+ script.setAttribute('type', "text/javascript");
755
+ script.setAttribute('id', 'mujian-custom-js');
756
+ script.textContent = projectInfo.config.customJs;
757
+ document.head.appendChild(script);
758
+ }
390
759
  } catch (error) {
391
- console.log('init error', error);
760
+ Log.e('init error', error);
392
761
  }
393
762
  }
394
763
  emit(event, data) {
@@ -415,7 +784,7 @@ class MujianSdk {
415
784
  data
416
785
  });
417
786
  controller?.signal?.addEventListener('abort', ()=>{
418
- this.emit("mujian:ai:chat:stop", {
787
+ this.emit(events_EVENT.MUJIAN_AI_CHAT_STOP, {
419
788
  id: callId
420
789
  });
421
790
  });
@@ -439,6 +808,7 @@ class MujianSdk {
439
808
  },
440
809
  complete: chat_complete.bind(this),
441
810
  applyRegex: applyRegex.bind(this),
811
+ renderMessage: renderMessage.bind(this),
442
812
  continue: continueComplete.bind(this),
443
813
  regenerate: chat_regenerate.bind(this),
444
814
  message: {
@@ -446,17 +816,34 @@ class MujianSdk {
446
816
  deleteOne: messageDeleteOne.bind(this),
447
817
  editOne: messageEditOne.bind(this),
448
818
  swipe: messageSwipe.bind(this),
449
- getPrompt: getPrompt.bind(this)
819
+ getPrompt: getPrompt.bind(this),
820
+ getPage: getPage.bind(this)
450
821
  }
451
822
  },
452
823
  text: {
453
824
  complete: generate.bind(this)
825
+ },
826
+ openai: {
827
+ completions: {
828
+ create: completions.create.bind(this)
829
+ },
830
+ chat: {
831
+ completions: {
832
+ create: chat.completions.create.bind(this)
833
+ }
834
+ },
835
+ responses: {
836
+ create: responses.create.bind(this)
837
+ },
838
+ images: {
839
+ generate: images_images.generate.bind(this)
840
+ }
454
841
  }
455
842
  };
456
843
  ui = {};
457
- util = {
458
- cn: ()=>{
459
- console.log('cn');
844
+ utils = {
845
+ clipboard: {
846
+ writeText: writeText.bind(this)
460
847
  }
461
848
  };
462
849
  hybrid = {};
@@ -474,6 +861,43 @@ class MujianSdk {
474
861
  }
475
862
  };
476
863
  }
864
+ const styleSheet = `
865
+ @keyframes spin {
866
+ 0% { transform: rotate(0deg); }
867
+ 100% { transform: rotate(360deg); }
868
+ }
869
+ `;
870
+ const spinnerStyle = {
871
+ width: 48,
872
+ height: 48,
873
+ animation: 'spin 1s linear infinite'
874
+ };
875
+ const MujianSpinner = ({ className, style })=>/*#__PURE__*/ jsxs(Fragment, {
876
+ children: [
877
+ /*#__PURE__*/ jsx("style", {
878
+ children: styleSheet
879
+ }),
880
+ /*#__PURE__*/ jsx("svg", {
881
+ xmlns: "http://www.w3.org/2000/svg",
882
+ width: "24",
883
+ height: "24",
884
+ viewBox: "0 0 24 24",
885
+ fill: "none",
886
+ stroke: "currentColor",
887
+ strokeWidth: "2",
888
+ strokeLinecap: "round",
889
+ strokeLinejoin: "round",
890
+ className: className,
891
+ style: {
892
+ ...spinnerStyle,
893
+ ...style
894
+ },
895
+ children: /*#__PURE__*/ jsx("path", {
896
+ d: "M21 12a9 9 0 1 1-6.219-8.56"
897
+ })
898
+ })
899
+ ]
900
+ });
477
901
  const MujianContext = /*#__PURE__*/ createContext(null);
478
902
  const MujianProvider = ({ children, loadingComponent })=>{
479
903
  const [mujian, setMujian] = useState(null);
@@ -486,7 +910,20 @@ const MujianProvider = ({ children, loadingComponent })=>{
486
910
  }, []);
487
911
  return /*#__PURE__*/ jsx(MujianContext.Provider, {
488
912
  value: mujian,
489
- children: mujian ? children : loadingComponent ?? 'Mujian is loading'
913
+ children: mujian ? children : loadingComponent ?? /*#__PURE__*/ jsx("div", {
914
+ style: {
915
+ height: '100%',
916
+ width: '100%',
917
+ display: 'flex',
918
+ justifyContent: 'center',
919
+ alignItems: 'center'
920
+ },
921
+ children: /*#__PURE__*/ jsx(MujianSpinner, {
922
+ style: {
923
+ color: '#EC4342'
924
+ }
925
+ })
926
+ })
490
927
  });
491
928
  };
492
929
  const useMujian = ()=>{
@@ -504,7 +941,7 @@ const MessageItem = (props)=>{
504
941
  const { data: renderedMessage } = useRequest(async ()=>{
505
942
  const { isStreaming } = message;
506
943
  if (isStreaming) return message;
507
- return await mujian.ai.chat.applyRegex({
944
+ return await mujian.ai.chat.renderMessage({
508
945
  message,
509
946
  depth,
510
947
  index
@@ -554,13 +991,6 @@ class UnexpectedSseEndingError extends Error {
554
991
  this.cause = cause;
555
992
  }
556
993
  }
557
- class InsufficientBalanceError extends Error {
558
- constructor(message, cause){
559
- super(message);
560
- this.name = this.constructor.name;
561
- this.cause = cause;
562
- }
563
- }
564
994
  async function* callSdk(mujian, content, type = 'generate', signal) {
565
995
  const queue = [];
566
996
  let resolveWait = null;
@@ -569,6 +999,7 @@ async function* callSdk(mujian, content, type = 'generate', signal) {
569
999
  try {
570
1000
  const onData = (data)=>{
571
1001
  if (error) return;
1002
+ if ('string' != typeof data) return;
572
1003
  queue.push(data);
573
1004
  if (resolveWait) {
574
1005
  resolveWait();
@@ -603,6 +1034,9 @@ async function* callSdk(mujian, content, type = 'generate', signal) {
603
1034
  resolveWait = resolve;
604
1035
  });
605
1036
  }
1037
+ const NOT_SAVED_MSG_ID_PREFIX = 'not_saved';
1038
+ const FALLBACK_MESSAGE = `<!-- 此条HTML消息为系统提示,请勿复读 -->
1039
+ 哎呀,AI线路好像抽风了,重说一下试试吧~`;
606
1040
  const useChatStreaming = ({ common, setError, setMessages, mujian })=>{
607
1041
  const { body } = common;
608
1042
  const [isStreaming, setIsStreaming] = useState(false);
@@ -611,22 +1045,28 @@ const useChatStreaming = ({ common, setError, setMessages, mujian })=>{
611
1045
  setError(void 0);
612
1046
  setIsStreaming(true);
613
1047
  const userMessage = {
614
- id: Date.now().toString(),
1048
+ id: `${NOT_SAVED_MSG_ID_PREFIX}_user_${Date.now()}`,
615
1049
  content: query || '',
616
1050
  role: 'user',
617
1051
  swipes: [],
618
1052
  activeSwipeId: 0,
619
1053
  isStreaming: false,
620
- sendAt: new Date()
1054
+ sendAt: new Date(),
1055
+ swipeInfo: []
621
1056
  };
622
1057
  const aiMessage = {
623
- id: Date.now().toString() + '1',
1058
+ id: `${NOT_SAVED_MSG_ID_PREFIX}_assistant_${Date.now()}`,
624
1059
  content: '',
625
1060
  role: 'assistant',
626
- swipes: [],
1061
+ swipes: [
1062
+ ''
1063
+ ],
627
1064
  activeSwipeId: 0,
628
1065
  isStreaming: true,
629
- sendAt: new Date()
1066
+ sendAt: new Date(),
1067
+ swipeInfo: [
1068
+ {}
1069
+ ]
630
1070
  };
631
1071
  if (regenerate) setMessages((prev)=>{
632
1072
  const newMessages = [
@@ -665,7 +1105,56 @@ const useChatStreaming = ({ common, setError, setMessages, mujian })=>{
665
1105
  const data = line.slice(6);
666
1106
  try {
667
1107
  const parsedData = JSON.parse(data);
668
- if (!regenerate && parsedData.question_id) {
1108
+ if (regenerate || 'meta' !== parsedData.type) {
1109
+ if ('stream' === parsedData.type) {
1110
+ const partialContent = parsedData.choices[0].delta.content;
1111
+ parsedData.choices[0].delta.reasoning;
1112
+ content += partialContent;
1113
+ if (content || '' === content) {
1114
+ setMessages((prev)=>{
1115
+ const newMessages = [
1116
+ ...prev
1117
+ ];
1118
+ const lastMessage = newMessages[newMessages.length - 1];
1119
+ lastMessage.swipes[lastMessage.activeSwipeId] = content;
1120
+ newMessages[newMessages.length - 1] = {
1121
+ ...lastMessage,
1122
+ content: content,
1123
+ isStreaming: true
1124
+ };
1125
+ return newMessages;
1126
+ });
1127
+ await new Promise((resolve)=>setTimeout(resolve, 10));
1128
+ } else {
1129
+ console.error('data', data);
1130
+ setError(new InvalidDeltaContentError(data));
1131
+ }
1132
+ } else if ('mjv' === parsedData.type) {
1133
+ const { value, delta, schema } = parsedData;
1134
+ setMessages((prev)=>{
1135
+ const newMessages = [
1136
+ ...prev
1137
+ ];
1138
+ const lastMessage = newMessages[newMessages.length - 1];
1139
+ const mjv = {
1140
+ value,
1141
+ delta,
1142
+ schema
1143
+ };
1144
+ const newSwipeInfo = lastMessage.swipeInfo;
1145
+ if (newSwipeInfo[lastMessage.activeSwipeId]) newSwipeInfo[lastMessage.activeSwipeId].mjv = mjv;
1146
+ else newSwipeInfo[lastMessage.activeSwipeId] = {
1147
+ mjv
1148
+ };
1149
+ newMessages[newMessages.length - 1] = {
1150
+ ...lastMessage,
1151
+ swipeInfo: newSwipeInfo,
1152
+ mjv
1153
+ };
1154
+ return newMessages;
1155
+ });
1156
+ }
1157
+ } else {
669
1158
  const { question_id, reply_id } = parsedData;
670
1159
  setMessages((prev)=>{
671
1160
  const newMessages = [
@@ -681,29 +1170,6 @@ const useChatStreaming = ({ common, setError, setMessages, mujian })=>{
681
1170
  });
682
1171
  continue;
683
1172
  }
684
- if (!parsedData.choices?.[0]) continue;
685
- const partialContent = parsedData.choices[0].delta.content;
686
- parsedData.choices[0].delta.reasoning;
687
- content += partialContent;
688
- if (content || '' === content) {
689
- setMessages((prev)=>{
690
- const newMessages = [
691
- ...prev
692
- ];
693
- const lastMessage = newMessages[newMessages.length - 1];
694
- lastMessage.swipes[lastMessage.activeSwipeId] = content;
695
- newMessages[newMessages.length - 1] = {
696
- ...lastMessage,
697
- content: content,
698
- isStreaming: true
699
- };
700
- return newMessages;
701
- });
702
- await new Promise((resolve)=>setTimeout(resolve, 10));
703
- } else {
704
- console.error('data', data);
705
- setError(new InvalidDeltaContentError(data));
706
- }
707
1173
  } catch {
708
1174
  if ('[DONE]' !== data) {
709
1175
  console.error('Received plain SSE message:', data);
@@ -712,22 +1178,30 @@ const useChatStreaming = ({ common, setError, setMessages, mujian })=>{
712
1178
  }
713
1179
  }
714
1180
  }
1181
+ } catch (error) {
1182
+ Log.e('Stream error', error);
1183
+ 'object' == typeof error && error && 'message' in error && 'string' == typeof error.message ? setError(new SendMessageError(error.message)) : setError(error);
715
1184
  } finally{
716
- console.log('stream end finally');
1185
+ Log.i('stream end finally');
717
1186
  setMessages((prev)=>{
718
1187
  const newMessages = [
719
1188
  ...prev
720
1189
  ];
721
- newMessages[newMessages.length - 1] = {
1190
+ const lastMessage = {
722
1191
  ...newMessages[newMessages.length - 1],
723
1192
  isStreaming: false
724
1193
  };
1194
+ if (!lastMessage.swipes[lastMessage.activeSwipeId]) {
1195
+ lastMessage.swipes[lastMessage.activeSwipeId] = FALLBACK_MESSAGE;
1196
+ lastMessage.content = FALLBACK_MESSAGE;
1197
+ }
1198
+ newMessages[newMessages.length - 1] = lastMessage;
725
1199
  return newMessages;
726
1200
  });
727
1201
  }
728
1202
  } catch (error) {
729
1203
  console.error('Error:', error);
730
- error instanceof Error && JSON.parse(error.message || '{}')?.code === 111 ? setError(new InsufficientBalanceError()) : setError(new SendMessageError('Send message failed', error));
1204
+ setError(new SendMessageError('Send message failed', error));
731
1205
  setMessages((prev)=>prev.slice(0, -1));
732
1206
  } finally{
733
1207
  setIsStreaming(false);
@@ -741,9 +1215,8 @@ const useChatStreaming = ({ common, setError, setMessages, mujian })=>{
741
1215
  isStreaming
742
1216
  };
743
1217
  };
744
- const useChat = ({ body, onError, onFinish })=>{
1218
+ const useChat = ({ body, onError, onFinish, pageSize })=>{
745
1219
  const [error, _setError] = useState();
746
- const [messages, setMessages] = useState([]);
747
1220
  const [input, setInput] = useState('');
748
1221
  const [abortController, setAbortController] = useState();
749
1222
  const mujian = useMujian();
@@ -751,6 +1224,51 @@ const useChat = ({ body, onError, onFinish })=>{
751
1224
  _setError(error);
752
1225
  if (error) onError?.(error);
753
1226
  };
1227
+ const isLoadingMore = useRef(false);
1228
+ const { data, loadMoreAsync, loadingMore, loading, mutate, error: messageLoadError } = useInfiniteScroll(async (current)=>{
1229
+ isLoadingMore.current = true;
1230
+ if (current?.noMore) return {
1231
+ noMore: true,
1232
+ list: []
1233
+ };
1234
+ try {
1235
+ const resp = await mujian.ai.chat.message.getPage(current?.cursor, pageSize);
1236
+ return {
1237
+ cursor: resp.nextCursor,
1238
+ list: resp.messages.map((m)=>({
1239
+ ...m,
1240
+ mjv: m.swipeInfo[m.activeSwipeId]?.mjv
1241
+ })),
1242
+ noMore: !resp.nextCursor
1243
+ };
1244
+ } finally{
1245
+ isLoadingMore.current = false;
1246
+ }
1247
+ }, {
1248
+ direction: 'top'
1249
+ });
1250
+ const loadMoreMessage = async ()=>{
1251
+ if (isLoadingMore.current) return;
1252
+ try {
1253
+ isLoadingMore.current = true;
1254
+ await loadMoreAsync();
1255
+ } finally{
1256
+ isLoadingMore.current = false;
1257
+ }
1258
+ };
1259
+ const messages = data?.list ?? [];
1260
+ const latestData = useLatest(data);
1261
+ const latestListRef = useRef(messages);
1262
+ latestListRef.current = messages;
1263
+ const setMessages = (messages)=>{
1264
+ let newList;
1265
+ newList = 'function' == typeof messages ? messages(latestListRef.current) : messages;
1266
+ latestListRef.current = newList;
1267
+ mutate({
1268
+ ...latestData.current,
1269
+ list: newList
1270
+ });
1271
+ };
754
1272
  const { chatStreaming, isStreaming } = useChatStreaming({
755
1273
  common: {
756
1274
  body
@@ -759,10 +1277,6 @@ const useChat = ({ body, onError, onFinish })=>{
759
1277
  setMessages,
760
1278
  mujian
761
1279
  });
762
- useMount(async ()=>{
763
- const resp = await mujian.ai.chat.message.getAll();
764
- setMessages(resp);
765
- });
766
1280
  useUpdateEffect(()=>{
767
1281
  if (!isStreaming) {
768
1282
  const lastMessage = messages[messages.length - 1];
@@ -771,48 +1285,79 @@ const useChat = ({ body, onError, onFinish })=>{
771
1285
  }, [
772
1286
  isStreaming
773
1287
  ]);
774
- const append = async (query)=>{
1288
+ const append = useCallback(async (query)=>{
775
1289
  if (isStreaming) throw new Error('useChat is streaming already.');
776
1290
  const controller = new AbortController();
777
1291
  setAbortController(controller);
778
- await chatStreaming({
779
- query,
780
- signal: controller.signal
781
- });
782
- setAbortController(void 0);
783
- };
784
- const regenerate = async ()=>{
1292
+ try {
1293
+ await chatStreaming({
1294
+ query,
1295
+ signal: controller.signal
1296
+ });
1297
+ } catch (e) {
1298
+ setError(e);
1299
+ throw e;
1300
+ } finally{
1301
+ setAbortController(void 0);
1302
+ }
1303
+ }, [
1304
+ isStreaming,
1305
+ chatStreaming
1306
+ ]);
1307
+ const regenerate = useCallback(async ()=>{
785
1308
  if (isStreaming) throw new Error('useChat is streaming already.');
786
1309
  const controller = new AbortController();
787
1310
  setAbortController(controller);
788
1311
  const lastMessage = messages.findLast((message)=>'user' === message.role);
789
- await chatStreaming({
790
- query: lastMessage?.content || '',
791
- regenerate: true,
792
- signal: controller.signal
793
- });
794
- setAbortController(void 0);
795
- };
796
- const continueGenerate = async ()=>{
1312
+ try {
1313
+ await chatStreaming({
1314
+ query: lastMessage?.content || '',
1315
+ regenerate: true,
1316
+ signal: controller.signal
1317
+ });
1318
+ } catch (e) {
1319
+ setError(e);
1320
+ throw e;
1321
+ } finally{
1322
+ setAbortController(void 0);
1323
+ }
1324
+ }, [
1325
+ isStreaming,
1326
+ messages,
1327
+ chatStreaming
1328
+ ]);
1329
+ const continueGenerate = useCallback(async ()=>{
797
1330
  if (isStreaming) throw new Error('useChat is streaming already.');
798
1331
  const controller = new AbortController();
799
1332
  setAbortController(controller);
800
- await chatStreaming({
801
- query: '',
802
- is_continue: true,
803
- signal: controller.signal
804
- });
805
- setAbortController(void 0);
806
- };
1333
+ try {
1334
+ await chatStreaming({
1335
+ query: '',
1336
+ is_continue: true,
1337
+ signal: controller.signal
1338
+ });
1339
+ } catch (e) {
1340
+ setError(e);
1341
+ throw e;
1342
+ } finally{
1343
+ setAbortController(void 0);
1344
+ }
1345
+ }, [
1346
+ isStreaming,
1347
+ chatStreaming
1348
+ ]);
807
1349
  const stop = ()=>{
808
1350
  abortController?.abort();
809
1351
  };
810
1352
  const setSwipe = async (messageId, swipeId)=>{
811
- await mujian.ai.chat.message.swipe(messageId, swipeId);
812
- setMessages((prev)=>prev.map((msg)=>msg.id === messageId ? {
1353
+ if (swipeId < 0) return;
1354
+ setMessages((prev)=>prev.map((msg)=>msg.id === messageId && swipeId < msg.swipes.length ? {
813
1355
  ...msg,
814
- activeSwipeId: swipeId
1356
+ activeSwipeId: swipeId,
1357
+ content: msg.swipes[swipeId],
1358
+ mjv: msg.swipeInfo[swipeId].mjv
815
1359
  } : msg));
1360
+ await mujian.ai.chat.message.swipe(messageId, swipeId);
816
1361
  };
817
1362
  const editMessage = async (messageId, content)=>{
818
1363
  await mujian.ai.chat.message.editOne(messageId, content);
@@ -832,7 +1377,11 @@ const useChat = ({ body, onError, onFinish })=>{
832
1377
  setMessages((prev)=>prev.map((msg)=>msg.id === messageId ? patchMessage(msg) : msg));
833
1378
  };
834
1379
  const deleteMessage = async (messageId)=>{
835
- await mujian.ai.chat.message.deleteOne(messageId);
1380
+ if (!messageId.startsWith(NOT_SAVED_MSG_ID_PREFIX)) try {
1381
+ await mujian.ai.chat.message.deleteOne(messageId);
1382
+ } catch (e) {
1383
+ if (e?.code !== 2011030004) throw e;
1384
+ }
836
1385
  setMessages((prev)=>prev.filter((msg)=>msg.id !== messageId));
837
1386
  };
838
1387
  return {
@@ -855,7 +1404,9 @@ const useChat = ({ body, onError, onFinish })=>{
855
1404
  },
856
1405
  setSwipe,
857
1406
  editMessage,
858
- deleteMessage
1407
+ deleteMessage,
1408
+ loadMoreMessage,
1409
+ messagesStatus: messageLoadError ? 'error' : loadingMore || loading ? 'loading' : data?.noMore ? 'all-loaded' : 'has-more'
859
1410
  };
860
1411
  };
861
- export { MdRenderer, MujianProvider, Thread, useChat, useMujian };
1412
+ export { MdRenderer, MujianProvider, MujianSpinner, Thread, useChat, useMujian };