@mujian/js-sdk 0.0.6-beta.9 → 0.0.6-beta.mjv.67

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