@mujian/js-sdk 0.0.6-beta.9 → 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 +323 -15
  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 +1 -1
  18. package/dist/react/chat/useChat/message.d.ts +25 -13
  19. package/dist/react/components/MdRenderer/index.d.ts +3 -3
  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 +686 -112
  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,31 +403,184 @@ function messageFormatting(mes, sanitizerOverrides = {}) {
234
403
  });
235
404
  return mes;
236
405
  }
237
- const MdRendererBase = ({ content })=>{
238
- const mes = messageFormatting(content);
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
+ ]);
239
490
  return /*#__PURE__*/ jsx("div", {
240
491
  className: "mes_text",
241
- dangerouslySetInnerHTML: {
242
- __html: mes
243
- }
492
+ ref: containerRef
244
493
  });
245
494
  };
246
495
  const MdRenderer = /*#__PURE__*/ react.memo(MdRendererBase, (prev, next)=>prev.content === next.content);
247
496
  MdRendererBase.displayName = 'MdRenderer';
248
497
  MdRenderer.displayName = 'MdRenderer';
249
- const chat_complete = async function(message, onData, signal) {
250
- return await this.call("mujian:ai:chat:complete", {
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, {
251
570
  content: message
252
571
  }, {
253
- onData,
572
+ onData: option.parseContent ? wrapOnData(onData) : onData,
254
573
  signal
255
574
  });
256
575
  };
257
576
  const applyRegex = async function(props) {
258
- 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);
259
581
  };
260
582
  const continueComplete = async function(onData, signal) {
261
- return await this.call("mujian:ai:chat:complete", {
583
+ return await this.call(events_EVENT.MUJIAN_AI_CHAT_COMPLETE, {
262
584
  isContinue: true
263
585
  }, {
264
586
  onData,
@@ -266,7 +588,7 @@ const continueComplete = async function(onData, signal) {
266
588
  });
267
589
  };
268
590
  const chat_regenerate = async function(onData, signal) {
269
- return await this.call("mujian:ai:chat:complete", {
591
+ return await this.call(events_EVENT.MUJIAN_AI_CHAT_COMPLETE, {
270
592
  isRegenerate: true
271
593
  }, {
272
594
  onData,
@@ -274,61 +596,117 @@ const chat_regenerate = async function(onData, signal) {
274
596
  });
275
597
  };
276
598
  const getAll = async function() {
277
- return await this.call("mujian:ai:chat:message:getAll");
599
+ return await this.call(events_EVENT.MUJIAN_AI_CHAT_MESSAGE_GET_ALL);
278
600
  };
279
601
  const messageDeleteOne = async function(messageId) {
280
- return await this.call("mujian:ai:chat:message:deleteOne", {
602
+ return await this.call(events_EVENT.MUJIAN_AI_CHAT_MESSAGE_DELETE_ONE, {
281
603
  messageId
282
604
  });
283
605
  };
284
606
  const messageEditOne = async function(messageId, content) {
285
- return await this.call("mujian:ai:chat:message:editOne", {
607
+ return await this.call(events_EVENT.MUJIAN_AI_CHAT_MESSAGE_EDIT_ONE, {
286
608
  messageId,
287
609
  content
288
610
  });
289
611
  };
290
612
  const messageSwipe = async function(messageId, swipeId) {
291
- return await this.call("mujian:ai:chat:message:swipe", {
613
+ return await this.call(events_EVENT.MUJIAN_AI_CHAT_MESSAGE_SWIPE, {
292
614
  messageId,
293
615
  swipeId
294
616
  });
295
617
  };
296
618
  const getPrompt = async function(messageId) {
297
- return await this.call("mujian:ai:chat:message:getPrompt", {
619
+ return await this.call(events_EVENT.MUJIAN_AI_CHAT_MESSAGE_GET_PROMPT, {
298
620
  messageId
299
621
  });
300
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
+ }
301
629
  const getInfo = async function() {
302
- return await this.call("mujian:ai:chat:project:getInfo");
630
+ return await this.call(events_EVENT.MUJIAN_AI_CHAT_PROJECT_GET_INFO);
303
631
  };
304
632
  const getActive = async function() {
305
- return await this.call("mujian:ai:settings:persona:getActive");
633
+ return await this.call(events_EVENT.MUJIAN_AI_SETTINGS_PERSONA_GET_ACTIVE);
306
634
  };
307
635
  const setActive = async function(personaId) {
308
- return await this.call("mujian:ai:settings:persona:setActive", {
636
+ return await this.call(events_EVENT.MUJIAN_AI_SETTINGS_PERSONA_SET_ACTIVE, {
309
637
  personaId
310
638
  });
311
639
  };
312
640
  const model_getActive = async function() {
313
- return await this.call("mujian:ai:settings:model:getActive");
641
+ return await this.call(events_EVENT.MUJIAN_AI_SETTINGS_MODEL_GET_ACTIVE);
314
642
  };
315
643
  const model_setActive = async function(modelId) {
316
- return await this.call("mujian:ai:settings:model:setActive", {
644
+ return await this.call(events_EVENT.MUJIAN_AI_SETTINGS_MODEL_SET_ACTIVE, {
317
645
  modelId
318
646
  });
319
647
  };
320
648
  const model_getAll = async function() {
321
- return await this.call("mujian:ai:settings:model:getAll");
649
+ return await this.call(events_EVENT.MUJIAN_AI_SETTINGS_MODEL_GET_ALL);
322
650
  };
323
- const generate = async function() {
324
- return await this.call("mujian:ai:text:generate", {
325
- content: 'q'
651
+ const generate = async function(content) {
652
+ return await this.call(MujianSdk.EVENT.MUJIAN_AI_TEXT_GENERATE, {
653
+ content
326
654
  });
327
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
+ };
328
702
  const saveGame = async function() {};
329
703
  const loadGame = async function() {};
704
+ async function writeText(text) {
705
+ return await this.call(events_EVENT.MUJIAN_UTILS_CLIPBOARD_WRITE_TEXT, text);
706
+ }
330
707
  class MujianSdk {
331
708
  constructor(){}
709
+ static EVENT = events_EVENT;
332
710
  static getInstance() {
333
711
  if (!window.$mujian) window.$mujian = new MujianSdk();
334
712
  return window.$mujian;
@@ -361,10 +739,25 @@ class MujianSdk {
361
739
  const parent = await handshake;
362
740
  this.ready = true;
363
741
  this.parent = parent;
364
- console.log('mujian sdk client init');
365
- 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
+ }
366
759
  } catch (error) {
367
- console.log('init error', error);
760
+ Log.e('init error', error);
368
761
  }
369
762
  }
370
763
  emit(event, data) {
@@ -391,7 +784,7 @@ class MujianSdk {
391
784
  data
392
785
  });
393
786
  controller?.signal?.addEventListener('abort', ()=>{
394
- this.emit("mujian:ai:chat:stop", {
787
+ this.emit(events_EVENT.MUJIAN_AI_CHAT_STOP, {
395
788
  id: callId
396
789
  });
397
790
  });
@@ -415,6 +808,7 @@ class MujianSdk {
415
808
  },
416
809
  complete: chat_complete.bind(this),
417
810
  applyRegex: applyRegex.bind(this),
811
+ renderMessage: renderMessage.bind(this),
418
812
  continue: continueComplete.bind(this),
419
813
  regenerate: chat_regenerate.bind(this),
420
814
  message: {
@@ -422,17 +816,34 @@ class MujianSdk {
422
816
  deleteOne: messageDeleteOne.bind(this),
423
817
  editOne: messageEditOne.bind(this),
424
818
  swipe: messageSwipe.bind(this),
425
- getPrompt: getPrompt.bind(this)
819
+ getPrompt: getPrompt.bind(this),
820
+ getPage: getPage.bind(this)
426
821
  }
427
822
  },
428
823
  text: {
429
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
+ }
430
841
  }
431
842
  };
432
843
  ui = {};
433
- util = {
434
- cn: ()=>{
435
- console.log('cn');
844
+ utils = {
845
+ clipboard: {
846
+ writeText: writeText.bind(this)
436
847
  }
437
848
  };
438
849
  hybrid = {};
@@ -450,6 +861,43 @@ class MujianSdk {
450
861
  }
451
862
  };
452
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
+ });
453
901
  const MujianContext = /*#__PURE__*/ createContext(null);
454
902
  const MujianProvider = ({ children, loadingComponent })=>{
455
903
  const [mujian, setMujian] = useState(null);
@@ -462,7 +910,20 @@ const MujianProvider = ({ children, loadingComponent })=>{
462
910
  }, []);
463
911
  return /*#__PURE__*/ jsx(MujianContext.Provider, {
464
912
  value: mujian,
465
- 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
+ })
466
927
  });
467
928
  };
468
929
  const useMujian = ()=>{
@@ -480,7 +941,7 @@ const MessageItem = (props)=>{
480
941
  const { data: renderedMessage } = useRequest(async ()=>{
481
942
  const { isStreaming } = message;
482
943
  if (isStreaming) return message;
483
- return await mujian.ai.chat.applyRegex({
944
+ return await mujian.ai.chat.renderMessage({
484
945
  message,
485
946
  depth,
486
947
  index
@@ -530,13 +991,6 @@ class UnexpectedSseEndingError extends Error {
530
991
  this.cause = cause;
531
992
  }
532
993
  }
533
- class InsufficientBalanceError extends Error {
534
- constructor(message, cause){
535
- super(message);
536
- this.name = this.constructor.name;
537
- this.cause = cause;
538
- }
539
- }
540
994
  async function* callSdk(mujian, content, type = 'generate', signal) {
541
995
  const queue = [];
542
996
  let resolveWait = null;
@@ -545,6 +999,7 @@ async function* callSdk(mujian, content, type = 'generate', signal) {
545
999
  try {
546
1000
  const onData = (data)=>{
547
1001
  if (error) return;
1002
+ if ('string' != typeof data) return;
548
1003
  queue.push(data);
549
1004
  if (resolveWait) {
550
1005
  resolveWait();
@@ -580,6 +1035,8 @@ async function* callSdk(mujian, content, type = 'generate', signal) {
580
1035
  });
581
1036
  }
582
1037
  const NOT_SAVED_MSG_ID_PREFIX = 'not_saved';
1038
+ const FALLBACK_MESSAGE = `<!-- 此条HTML消息为系统提示,请勿复读 -->
1039
+ 哎呀,AI线路好像抽风了,重说一下试试吧~`;
583
1040
  const useChatStreaming = ({ common, setError, setMessages, mujian })=>{
584
1041
  const { body } = common;
585
1042
  const [isStreaming, setIsStreaming] = useState(false);
@@ -594,16 +1051,22 @@ const useChatStreaming = ({ common, setError, setMessages, mujian })=>{
594
1051
  swipes: [],
595
1052
  activeSwipeId: 0,
596
1053
  isStreaming: false,
597
- sendAt: new Date()
1054
+ sendAt: new Date(),
1055
+ swipeInfo: []
598
1056
  };
599
1057
  const aiMessage = {
600
1058
  id: `${NOT_SAVED_MSG_ID_PREFIX}_assistant_${Date.now()}`,
601
1059
  content: '',
602
1060
  role: 'assistant',
603
- swipes: [],
1061
+ swipes: [
1062
+ ''
1063
+ ],
604
1064
  activeSwipeId: 0,
605
1065
  isStreaming: true,
606
- sendAt: new Date()
1066
+ sendAt: new Date(),
1067
+ swipeInfo: [
1068
+ {}
1069
+ ]
607
1070
  };
608
1071
  if (regenerate) setMessages((prev)=>{
609
1072
  const newMessages = [
@@ -642,7 +1105,56 @@ const useChatStreaming = ({ common, setError, setMessages, mujian })=>{
642
1105
  const data = line.slice(6);
643
1106
  try {
644
1107
  const parsedData = JSON.parse(data);
645
- 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 {
646
1158
  const { question_id, reply_id } = parsedData;
647
1159
  setMessages((prev)=>{
648
1160
  const newMessages = [
@@ -658,29 +1170,6 @@ const useChatStreaming = ({ common, setError, setMessages, mujian })=>{
658
1170
  });
659
1171
  continue;
660
1172
  }
661
- if (!parsedData.choices?.[0]) continue;
662
- const partialContent = parsedData.choices[0].delta.content;
663
- parsedData.choices[0].delta.reasoning;
664
- content += partialContent;
665
- if (content || '' === content) {
666
- setMessages((prev)=>{
667
- const newMessages = [
668
- ...prev
669
- ];
670
- const lastMessage = newMessages[newMessages.length - 1];
671
- lastMessage.swipes[lastMessage.activeSwipeId] = content;
672
- newMessages[newMessages.length - 1] = {
673
- ...lastMessage,
674
- content: content,
675
- isStreaming: true
676
- };
677
- return newMessages;
678
- });
679
- await new Promise((resolve)=>setTimeout(resolve, 10));
680
- } else {
681
- console.error('data', data);
682
- setError(new InvalidDeltaContentError(data));
683
- }
684
1173
  } catch {
685
1174
  if ('[DONE]' !== data) {
686
1175
  console.error('Received plain SSE message:', data);
@@ -689,22 +1178,30 @@ const useChatStreaming = ({ common, setError, setMessages, mujian })=>{
689
1178
  }
690
1179
  }
691
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);
692
1184
  } finally{
693
- console.log('stream end finally');
1185
+ Log.i('stream end finally');
694
1186
  setMessages((prev)=>{
695
1187
  const newMessages = [
696
1188
  ...prev
697
1189
  ];
698
- newMessages[newMessages.length - 1] = {
1190
+ const lastMessage = {
699
1191
  ...newMessages[newMessages.length - 1],
700
1192
  isStreaming: false
701
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;
702
1199
  return newMessages;
703
1200
  });
704
1201
  }
705
1202
  } catch (error) {
706
1203
  console.error('Error:', error);
707
- 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));
708
1205
  setMessages((prev)=>prev.slice(0, -1));
709
1206
  } finally{
710
1207
  setIsStreaming(false);
@@ -718,9 +1215,8 @@ const useChatStreaming = ({ common, setError, setMessages, mujian })=>{
718
1215
  isStreaming
719
1216
  };
720
1217
  };
721
- const useChat = ({ body, onError, onFinish })=>{
1218
+ const useChat = ({ body, onError, onFinish, pageSize })=>{
722
1219
  const [error, _setError] = useState();
723
- const [messages, setMessages] = useState([]);
724
1220
  const [input, setInput] = useState('');
725
1221
  const [abortController, setAbortController] = useState();
726
1222
  const mujian = useMujian();
@@ -728,6 +1224,51 @@ const useChat = ({ body, onError, onFinish })=>{
728
1224
  _setError(error);
729
1225
  if (error) onError?.(error);
730
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
+ };
731
1272
  const { chatStreaming, isStreaming } = useChatStreaming({
732
1273
  common: {
733
1274
  body
@@ -736,10 +1277,6 @@ const useChat = ({ body, onError, onFinish })=>{
736
1277
  setMessages,
737
1278
  mujian
738
1279
  });
739
- useMount(async ()=>{
740
- const resp = await mujian.ai.chat.message.getAll();
741
- setMessages(resp);
742
- });
743
1280
  useUpdateEffect(()=>{
744
1281
  if (!isStreaming) {
745
1282
  const lastMessage = messages[messages.length - 1];
@@ -748,48 +1285,79 @@ const useChat = ({ body, onError, onFinish })=>{
748
1285
  }, [
749
1286
  isStreaming
750
1287
  ]);
751
- const append = async (query)=>{
1288
+ const append = useCallback(async (query)=>{
752
1289
  if (isStreaming) throw new Error('useChat is streaming already.');
753
1290
  const controller = new AbortController();
754
1291
  setAbortController(controller);
755
- await chatStreaming({
756
- query,
757
- signal: controller.signal
758
- });
759
- setAbortController(void 0);
760
- };
761
- 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 ()=>{
762
1308
  if (isStreaming) throw new Error('useChat is streaming already.');
763
1309
  const controller = new AbortController();
764
1310
  setAbortController(controller);
765
1311
  const lastMessage = messages.findLast((message)=>'user' === message.role);
766
- await chatStreaming({
767
- query: lastMessage?.content || '',
768
- regenerate: true,
769
- signal: controller.signal
770
- });
771
- setAbortController(void 0);
772
- };
773
- 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 ()=>{
774
1330
  if (isStreaming) throw new Error('useChat is streaming already.');
775
1331
  const controller = new AbortController();
776
1332
  setAbortController(controller);
777
- await chatStreaming({
778
- query: '',
779
- is_continue: true,
780
- signal: controller.signal
781
- });
782
- setAbortController(void 0);
783
- };
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
+ ]);
784
1349
  const stop = ()=>{
785
1350
  abortController?.abort();
786
1351
  };
787
1352
  const setSwipe = async (messageId, swipeId)=>{
788
- await mujian.ai.chat.message.swipe(messageId, swipeId);
789
- 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 ? {
790
1355
  ...msg,
791
- activeSwipeId: swipeId
1356
+ activeSwipeId: swipeId,
1357
+ content: msg.swipes[swipeId],
1358
+ mjv: msg.swipeInfo[swipeId].mjv
792
1359
  } : msg));
1360
+ await mujian.ai.chat.message.swipe(messageId, swipeId);
793
1361
  };
794
1362
  const editMessage = async (messageId, content)=>{
795
1363
  await mujian.ai.chat.message.editOne(messageId, content);
@@ -809,7 +1377,11 @@ const useChat = ({ body, onError, onFinish })=>{
809
1377
  setMessages((prev)=>prev.map((msg)=>msg.id === messageId ? patchMessage(msg) : msg));
810
1378
  };
811
1379
  const deleteMessage = async (messageId)=>{
812
- if (!messageId.startsWith(NOT_SAVED_MSG_ID_PREFIX)) 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
+ }
813
1385
  setMessages((prev)=>prev.filter((msg)=>msg.id !== messageId));
814
1386
  };
815
1387
  return {
@@ -832,7 +1404,9 @@ const useChat = ({ body, onError, onFinish })=>{
832
1404
  },
833
1405
  setSwipe,
834
1406
  editMessage,
835
- deleteMessage
1407
+ deleteMessage,
1408
+ loadMoreMessage,
1409
+ messagesStatus: messageLoadError ? 'error' : loadingMore || loading ? 'loading' : data?.noMore ? 'all-loaded' : 'has-more'
836
1410
  };
837
1411
  };
838
- export { MdRenderer, MujianProvider, Thread, useChat, useMujian };
1412
+ export { MdRenderer, MujianProvider, MujianSpinner, Thread, useChat, useMujian };