@mujian/js-sdk 0.0.6-beta.3 → 0.0.6-beta.31
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.
- package/dist/index.js +6 -1
- package/dist/react/chat/useChat/index.d.ts +2 -2
- package/dist/react/chat/useChat/inner/chatStreaming.d.ts +1 -0
- package/dist/react/components/MdRenderer/index.d.ts +2 -3
- package/dist/react/components/MdRenderer/utils/height.d.ts +5 -0
- package/dist/react/components/MdRenderer/utils/iframe.d.ts +9 -0
- package/dist/react/components/MdRenderer/utils/scripts.d.ts +3 -0
- package/dist/react/components/MujianSpinner/index.d.ts +7 -0
- package/dist/react/components/index.d.ts +1 -0
- package/dist/react.css +47 -61
- package/dist/react.js +327 -41
- package/package.json +2 -1
- /package/dist/react/components/MdRenderer/{utils.d.ts → utils/md.d.ts} +0 -0
package/dist/index.js
CHANGED
|
@@ -113,9 +113,14 @@ class MujianSdk {
|
|
|
113
113
|
pendingRequests = new Map();
|
|
114
114
|
async init() {
|
|
115
115
|
const handshake = new postmate.Model({
|
|
116
|
-
reply: ({ id, complete, data })=>{
|
|
116
|
+
reply: ({ id, complete, data, error })=>{
|
|
117
117
|
const call = this.pendingRequests.get(id);
|
|
118
118
|
if (!call) return;
|
|
119
|
+
if (error) {
|
|
120
|
+
call.reject(error);
|
|
121
|
+
this.pendingRequests.delete(id);
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
119
124
|
call.onData?.(data);
|
|
120
125
|
if (complete) {
|
|
121
126
|
call.onComplete?.();
|
|
@@ -10,7 +10,7 @@ export type { Message };
|
|
|
10
10
|
*/
|
|
11
11
|
export type UseChatProps = {
|
|
12
12
|
body?: object;
|
|
13
|
-
onError?: (e:
|
|
13
|
+
onError?: (e: unknown) => void;
|
|
14
14
|
onFinish?: (message: Message) => void;
|
|
15
15
|
};
|
|
16
16
|
/**
|
|
@@ -24,7 +24,7 @@ export type UseChatProps = {
|
|
|
24
24
|
export type UseChatReturn = {
|
|
25
25
|
messages: Message[];
|
|
26
26
|
status: 'uninitialized' | 'ready' | 'streaming' | 'error';
|
|
27
|
-
error?:
|
|
27
|
+
error?: unknown;
|
|
28
28
|
/** 提交用户输入,让AI回答 */
|
|
29
29
|
append: (query: string) => Promise<void>;
|
|
30
30
|
/** 重新生成最后一条回答 */
|
|
@@ -8,6 +8,7 @@ interface ChatStreamingProps {
|
|
|
8
8
|
setMessages: Dispatch<SetStateAction<Message[]>>;
|
|
9
9
|
mujian: MujianSdk;
|
|
10
10
|
}
|
|
11
|
+
export declare const NOT_SAVED_MSG_ID_PREFIX = "not_saved";
|
|
11
12
|
export declare const useChatStreaming: ({ common, setError, setMessages, mujian, }: ChatStreamingProps) => {
|
|
12
13
|
chatStreaming: ({ query, regenerate, is_continue, signal, }: {
|
|
13
14
|
/** 用户输入 */
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare const unescapeHTML: (str: string) => string;
|
|
2
|
+
export declare const replaceVhInContent: (content: string) => string;
|
|
3
|
+
/**
|
|
4
|
+
* 转义 HTML 属性值中的引号,防止破坏 HTML 属性
|
|
5
|
+
* @param value 需要转义的属性值
|
|
6
|
+
* @returns 转义后的值
|
|
7
|
+
*/
|
|
8
|
+
export declare function escapeHtmlAttribute(value: string): string;
|
|
9
|
+
export declare function createSrcContent(content: string): string;
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export declare const iframeDOMLoaded = "\n(function () {\n function emit_loaded_event() {\n window.parent.postMessage({ type: 'MJ_DOM_CONTENT_LOADED', iframeId: '123' }, '*');\n }\n\n if (window.document.readyState === 'loading') {\n window.document.addEventListener('DOMContentLoaded', emit_loaded_event, { once: true });\n } else {\n emit_loaded_event();\n }\n})();\n";
|
|
2
|
+
export declare const init = "\n(async function () {\n window.$mj_engine = window.parent.$mj_engine;\n})();\n";
|
|
3
|
+
export declare const thirdParty = "\n<script src=\"https://cdn.jsdmirror.com/npm/@fortawesome/fontawesome-free/js/all.min.js\"></script>\n<script src=\"https://cdn.jsdmirror.com/npm/@tailwindcss/browser/dist/index.global.min.js\"></script>\n<script src=\"https://cdn.jsdmirror.com/npm/jquery/dist/jquery.min.js\"></script>\n<script src=\"https://cdn.jsdmirror.com/npm/jquery-ui/dist/jquery-ui.min.js\"></script>\n<link rel=\"stylesheet\" href=\"https://cdn.jsdmirror.com/npm/jquery-ui/themes/base/theme.min.css\" />\n<script src=\"https://cdn.jsdmirror.com/npm/jquery-ui-touch-punch\"></script>\n";
|
package/dist/react.css
CHANGED
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
--SmartThemeBodyColor: #abc6df;
|
|
43
43
|
--SmartThemeEmColor: #fff;
|
|
44
44
|
--SmartThemeUnderlineColor: #bce7cf;
|
|
45
|
-
--SmartThemeQuoteColor: #
|
|
45
|
+
--SmartThemeQuoteColor: #b983ff;
|
|
46
46
|
--SmartThemeBlurTintColor: #171717;
|
|
47
47
|
--SmartThemeChatTintColor: #171717;
|
|
48
48
|
--SmartThemeUserMesBlurTintColor: #0000004d;
|
|
@@ -89,76 +89,72 @@
|
|
|
89
89
|
-moz-osx-font-smoothing: grayscale;
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
-
|
|
93
|
-
-
|
|
94
|
-
-
|
|
95
|
-
-webkit-transform: translateZ(0);
|
|
92
|
+
.spin-overlay {
|
|
93
|
+
background-color: #000;
|
|
94
|
+
animation: 3s ease-in-out infinite breath;
|
|
96
95
|
}
|
|
97
96
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
padding: 0;
|
|
97
|
+
@keyframes breath {
|
|
98
|
+
0% {
|
|
99
|
+
background-color: #0009;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
50% {
|
|
103
|
+
background-color: #00000080;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
100% {
|
|
107
|
+
background-color: #0009;
|
|
108
|
+
}
|
|
111
109
|
}
|
|
112
110
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
111
|
+
.wave-text span {
|
|
112
|
+
font-family: var(--mainFontFamily);
|
|
113
|
+
animation: 2s ease-in-out infinite wave;
|
|
114
|
+
display: inline-block;
|
|
116
115
|
}
|
|
117
116
|
|
|
118
|
-
|
|
119
|
-
|
|
117
|
+
.wave-text span:first-child {
|
|
118
|
+
animation-delay: 0s;
|
|
120
119
|
}
|
|
121
120
|
|
|
122
|
-
|
|
123
|
-
|
|
121
|
+
.wave-text span:nth-child(2) {
|
|
122
|
+
animation-delay: .2s;
|
|
124
123
|
}
|
|
125
124
|
|
|
126
|
-
|
|
127
|
-
|
|
125
|
+
.wave-text span:nth-child(3) {
|
|
126
|
+
animation-delay: .4s;
|
|
128
127
|
}
|
|
129
128
|
|
|
130
|
-
|
|
131
|
-
|
|
129
|
+
.wave-text span:nth-child(4) {
|
|
130
|
+
animation-delay: .6s;
|
|
132
131
|
}
|
|
133
132
|
|
|
134
|
-
.
|
|
135
|
-
|
|
133
|
+
.wave-text span:nth-child(5) {
|
|
134
|
+
animation-delay: .8s;
|
|
136
135
|
}
|
|
137
136
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
box-shadow: inset 0 0 0 1px var(--black50a);
|
|
141
|
-
background-clip: content-box;
|
|
142
|
-
border: 2px solid #0000;
|
|
143
|
-
border-radius: 10px;
|
|
144
|
-
min-height: 40px;
|
|
137
|
+
.wave-text span:nth-child(6) {
|
|
138
|
+
animation-delay: 1s;
|
|
145
139
|
}
|
|
146
140
|
|
|
147
|
-
|
|
148
|
-
|
|
141
|
+
.wave-text span:nth-child(7) {
|
|
142
|
+
animation-delay: 1.2s;
|
|
149
143
|
}
|
|
150
144
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
145
|
+
@keyframes wave {
|
|
146
|
+
0%, 100% {
|
|
147
|
+
transform: translateY(0);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
50% {
|
|
151
|
+
transform: translateY(-8px);
|
|
152
|
+
}
|
|
158
153
|
}
|
|
159
154
|
|
|
160
|
-
|
|
161
|
-
|
|
155
|
+
body {
|
|
156
|
+
font-size: var(--mainFontSize);
|
|
157
|
+
color: var(--SmartThemeBodyColor);
|
|
162
158
|
}
|
|
163
159
|
|
|
164
160
|
.mes_text table, .mes_reasoning table {
|
|
@@ -218,19 +214,11 @@ body.movingUI ::-webkit-scrollbar-thumb:vertical {
|
|
|
218
214
|
}
|
|
219
215
|
|
|
220
216
|
.mes_text, .mes_reasoning {
|
|
221
|
-
font-weight: 500;
|
|
222
217
|
line-height: calc(var(--mainFontSize) + .5rem);
|
|
223
218
|
overflow-wrap: anywhere;
|
|
224
219
|
max-width: 100%;
|
|
225
220
|
}
|
|
226
221
|
|
|
227
|
-
.mes_text {
|
|
228
|
-
padding-top: 5px;
|
|
229
|
-
padding-bottom: 5px;
|
|
230
|
-
padding-left: 0;
|
|
231
|
-
padding-right: var(--mes-right-spacing);
|
|
232
|
-
}
|
|
233
|
-
|
|
234
222
|
.mes_text p:only-child, .mes_text p:only-of-type {
|
|
235
223
|
margin-bottom: 0;
|
|
236
224
|
}
|
|
@@ -239,7 +227,7 @@ body.movingUI ::-webkit-scrollbar-thumb:vertical {
|
|
|
239
227
|
unicode-bidi: isolate;
|
|
240
228
|
margin-block: 1em;
|
|
241
229
|
margin-inline: 0;
|
|
242
|
-
padding-inline-start:
|
|
230
|
+
padding-inline-start: 24px;
|
|
243
231
|
list-style-type: decimal;
|
|
244
232
|
display: block;
|
|
245
233
|
}
|
|
@@ -336,11 +324,9 @@ body.movingUI ::-webkit-scrollbar-thumb:vertical {
|
|
|
336
324
|
code {
|
|
337
325
|
font-family: var(--monoFontFamily);
|
|
338
326
|
white-space: pre-wrap;
|
|
339
|
-
border: 1px solid var(--SmartThemeBorderColor);
|
|
340
|
-
background-color: var(--black70a);
|
|
341
327
|
line-height: var(--mainFontSize);
|
|
342
328
|
color: var(--white70a);
|
|
343
|
-
border
|
|
329
|
+
border: 1px solid #fff;
|
|
344
330
|
padding: 0 3px;
|
|
345
331
|
}
|
|
346
332
|
|
package/dist/react.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
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";
|
|
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";
|
|
9
10
|
addDOMPurifyHooks();
|
|
@@ -95,7 +96,7 @@ function addDOMPurifyHooks() {
|
|
|
95
96
|
* @returns {string} Encoded message text
|
|
96
97
|
* @copyright https://github.com/kwaroran/risuAI
|
|
97
98
|
*/ function encodeStyleTags(text) {
|
|
98
|
-
const styleRegex = /<style>(
|
|
99
|
+
const styleRegex = /<style>([\s\S]+?)<\/style>/gi;
|
|
99
100
|
return text.replaceAll(styleRegex, (_, match)=>`<custom-style>${encodeURIComponent(match)}</custom-style>`);
|
|
100
101
|
}
|
|
101
102
|
/**
|
|
@@ -234,18 +235,203 @@ function messageFormatting(mes, sanitizerOverrides = {}) {
|
|
|
234
235
|
});
|
|
235
236
|
return mes;
|
|
236
237
|
}
|
|
238
|
+
const init = `
|
|
239
|
+
(async function () {
|
|
240
|
+
window.$mj_engine = window.parent.$mj_engine;
|
|
241
|
+
})();
|
|
242
|
+
`;
|
|
243
|
+
const thirdParty = `
|
|
244
|
+
<script src="https://cdn.jsdmirror.com/npm/@fortawesome/fontawesome-free/js/all.min.js"></script>
|
|
245
|
+
<script src="https://cdn.jsdmirror.com/npm/@tailwindcss/browser/dist/index.global.min.js"></script>
|
|
246
|
+
<script src="https://cdn.jsdmirror.com/npm/jquery/dist/jquery.min.js"></script>
|
|
247
|
+
<script src="https://cdn.jsdmirror.com/npm/jquery-ui/dist/jquery-ui.min.js"></script>
|
|
248
|
+
<link rel="stylesheet" href="https://cdn.jsdmirror.com/npm/jquery-ui/themes/base/theme.min.css" />
|
|
249
|
+
<script src="https://cdn.jsdmirror.com/npm/jquery-ui-touch-punch"></script>
|
|
250
|
+
`;
|
|
251
|
+
const unescapeHTML = (str)=>{
|
|
252
|
+
const named = {
|
|
253
|
+
amp: "&",
|
|
254
|
+
lt: "<",
|
|
255
|
+
gt: ">",
|
|
256
|
+
quot: '"',
|
|
257
|
+
apos: "'",
|
|
258
|
+
nbsp: "\u00A0"
|
|
259
|
+
};
|
|
260
|
+
return str.replace(/&(#x?[0-9a-fA-F]+|[a-zA-Z]+);/g, (_m, body)=>{
|
|
261
|
+
if ("#" === body[0]) {
|
|
262
|
+
const isHex = body[1]?.toLowerCase() === "x";
|
|
263
|
+
const numStr = isHex ? body.slice(2) : body.slice(1);
|
|
264
|
+
const codePoint = parseInt(numStr, isHex ? 16 : 10);
|
|
265
|
+
if (Number.isFinite(codePoint)) try {
|
|
266
|
+
return String.fromCodePoint(codePoint);
|
|
267
|
+
} catch {}
|
|
268
|
+
return _m;
|
|
269
|
+
}
|
|
270
|
+
const lower = body.toLowerCase();
|
|
271
|
+
return Object.hasOwn(named, lower) ? named[lower] : _m;
|
|
272
|
+
});
|
|
273
|
+
};
|
|
274
|
+
const replaceVhInContent = (content)=>{
|
|
275
|
+
const has_css_min_vh = /min-height\s*:\s*[^;{}]*\d+(?:\.\d+)?vh/gi.test(content);
|
|
276
|
+
const has_inline_style_vh = /style\s*=\s*(["'])[\s\S]*?min-height\s*:\s*[^;]*?\d+(?:\.\d+)?vh[\s\S]*?\1/gi.test(content);
|
|
277
|
+
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);
|
|
278
|
+
if (!has_css_min_vh && !has_inline_style_vh && !has_js_vh) return content;
|
|
279
|
+
const convertVhToVariable = (value)=>value.replace(/(\d+(?:\.\d+)?)vh\b/gi, (match, value)=>{
|
|
280
|
+
const parsed = parseFloat(value);
|
|
281
|
+
if (!isFinite(parsed)) return match;
|
|
282
|
+
const VARIABLE_EXPRESSION = "var(--MJ-iframe-height)";
|
|
283
|
+
if (100 === parsed) return VARIABLE_EXPRESSION;
|
|
284
|
+
return `calc(${VARIABLE_EXPRESSION} * ${parsed / 100})`;
|
|
285
|
+
});
|
|
286
|
+
content = content.replace(/(min-height\s*:\s*)([^;{}]*?\d+(?:\.\d+)?vh)(?=\s*[;}])/gi, (_m, prefix, value)=>`${prefix}${convertVhToVariable(value)}`);
|
|
287
|
+
content = content.replace(/(style\s*=\s*(["']))([^"'"]*?)(\2)/gi, (match, prefix, _quote, styleContent, suffix)=>{
|
|
288
|
+
if (!/min-height\s*:\s*[^;]*vh/i.test(styleContent)) return match;
|
|
289
|
+
const replaced = styleContent.replace(/(min-height\s*:\s*)([^;]*?\d+(?:\.\d+)?vh)/gi, (_m, p1, p2)=>`${p1}${convertVhToVariable(p2)}`);
|
|
290
|
+
return `${prefix}${replaced}${suffix}`;
|
|
291
|
+
});
|
|
292
|
+
content = content.replace(/(\.style\.minHeight\s*=\s*(["']))([\s\S]*?)(\2)/gi, (match, prefix, _q, val, suffix)=>{
|
|
293
|
+
if (!/\b\d+(?:\.\d+)?vh\b/i.test(val)) return match;
|
|
294
|
+
const converted = convertVhToVariable(val);
|
|
295
|
+
return `${prefix}${converted}${suffix}`;
|
|
296
|
+
});
|
|
297
|
+
content = content.replace(/(setProperty\s*\(\s*(["'])min-height\2\s*,\s*(["']))([\s\S]*?)(\3\s*\))/gi, (match, prefix, _q1, _q2, val, suffix)=>{
|
|
298
|
+
if (!/\b\d+(?:\.\d+)?vh\b/i.test(val)) return match;
|
|
299
|
+
const converted = convertVhToVariable(val);
|
|
300
|
+
return `${prefix}${converted}${suffix}`;
|
|
301
|
+
});
|
|
302
|
+
return content;
|
|
303
|
+
};
|
|
304
|
+
function escapeHtmlAttribute(value) {
|
|
305
|
+
return value.replace(/"/g, """).replace(/'/g, "'");
|
|
306
|
+
}
|
|
307
|
+
function createSrcContent(content) {
|
|
308
|
+
content = replaceVhInContent(content);
|
|
309
|
+
const getUserAvatarPath = ()=>"https://www.mujian.com/avatar.png";
|
|
310
|
+
const getCharAvatarPath = ()=>"https://www.mujian.com/avatar.png";
|
|
311
|
+
return `
|
|
312
|
+
<html>
|
|
313
|
+
<head>
|
|
314
|
+
<style>
|
|
315
|
+
|
|
316
|
+
html,body{margin:0;padding:0;overflow:hidden!important;max-width:100%!important;box-sizing:border-box}
|
|
317
|
+
.user_avatar,.user-avatar{background-image:url('${getUserAvatarPath()}')}
|
|
318
|
+
.char_avatar,.char-avatar{background-image:url('${getCharAvatarPath()}')}
|
|
319
|
+
|
|
320
|
+
</style>
|
|
321
|
+
${thirdParty}
|
|
322
|
+
<script>
|
|
323
|
+
${init}
|
|
324
|
+
</script>
|
|
325
|
+
</head>
|
|
326
|
+
<body>
|
|
327
|
+
${content}
|
|
328
|
+
</body>
|
|
329
|
+
</html>
|
|
330
|
+
`;
|
|
331
|
+
}
|
|
332
|
+
function adjustIframeHeight(iframe) {
|
|
333
|
+
if (!iframe.contentWindow) return;
|
|
334
|
+
const document1 = iframe.contentWindow.document;
|
|
335
|
+
const height = Math.max(document1.body.offsetHeight, document1.documentElement.offsetHeight);
|
|
336
|
+
if (!Number.isFinite(height) || height <= 0) return;
|
|
337
|
+
iframe.style.height = `${height}px`;
|
|
338
|
+
}
|
|
339
|
+
const observed_elements = new Map();
|
|
340
|
+
const observer = new ResizeObserver((entries)=>{
|
|
341
|
+
for (const entry of entries){
|
|
342
|
+
const element = entry.target;
|
|
343
|
+
const iframe = observed_elements.get(element);
|
|
344
|
+
if (iframe) adjustIframeHeight(iframe);
|
|
345
|
+
}
|
|
346
|
+
});
|
|
347
|
+
function useHeightObserver() {
|
|
348
|
+
return {
|
|
349
|
+
observe: (iframe)=>{
|
|
350
|
+
if (!iframe?.contentWindow?.document?.body) return;
|
|
351
|
+
const body = iframe.contentWindow.document.body;
|
|
352
|
+
observed_elements.set(body, iframe);
|
|
353
|
+
observer.observe(body);
|
|
354
|
+
adjustIframeHeight(iframe);
|
|
355
|
+
},
|
|
356
|
+
unobserve: (iframe)=>{
|
|
357
|
+
if (!iframe.contentWindow?.document?.body) return;
|
|
358
|
+
const body = iframe.contentWindow.document.body;
|
|
359
|
+
observed_elements.delete(body);
|
|
360
|
+
observer.unobserve(body);
|
|
361
|
+
}
|
|
362
|
+
};
|
|
363
|
+
}
|
|
237
364
|
const MdRendererBase = ({ content })=>{
|
|
238
|
-
const
|
|
365
|
+
const containerRef = useRef(null);
|
|
366
|
+
const [iframeIdList, setIframeIdList] = useState([]);
|
|
367
|
+
const { observe, unobserve } = useHeightObserver();
|
|
368
|
+
useEffect(()=>{
|
|
369
|
+
let mes = messageFormatting(content);
|
|
370
|
+
mes = mes.replace(/<pre><code(.*)>[\s\S]*?<\/code><\/pre>/g, (match)=>{
|
|
371
|
+
if (!match.includes("<body>") && !match.includes("</body>")) return match;
|
|
372
|
+
const code = match.replace(/<pre><code(.*?)>/g, "").replace(/<\/code><\/pre>/g, "");
|
|
373
|
+
const srcdoc = createSrcContent(unescapeHTML(code));
|
|
374
|
+
const id = v4();
|
|
375
|
+
setIframeIdList((prev)=>[
|
|
376
|
+
...prev,
|
|
377
|
+
id
|
|
378
|
+
]);
|
|
379
|
+
const escapedSrcdoc = escapeHtmlAttribute(srcdoc);
|
|
380
|
+
return `
|
|
381
|
+
<div class="MJ-iframe-container" id="MJ-iframe-container-${id}" style="display:flex;width:100%;height:100%;position:relative;">
|
|
382
|
+
<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;">
|
|
383
|
+
<div class="spin-inner wave-text" style="font-weight:500;font-size:16px;color:white;display:flex;justify-content:center;align-items:center;">
|
|
384
|
+
<span>加</span>
|
|
385
|
+
<span>载</span>
|
|
386
|
+
<span>中</span>
|
|
387
|
+
<span>.</span>
|
|
388
|
+
<span>.</span>
|
|
389
|
+
<span>.</span>
|
|
390
|
+
</div>
|
|
391
|
+
</div>
|
|
392
|
+
<iframe id="MJ-iframe-${id}" sandbox="allow-scripts allow-same-origin" loading="lazy" referrerpolicy="no-referrer" allowTransparency="true" style="color-scheme: none;background-color: transparent;width:100%;height:100%;border:none;" srcdoc="` + escapedSrcdoc + `"></iframe>
|
|
393
|
+
</div>
|
|
394
|
+
`;
|
|
395
|
+
});
|
|
396
|
+
if (containerRef.current) containerRef.current.innerHTML = mes;
|
|
397
|
+
}, [
|
|
398
|
+
content
|
|
399
|
+
]);
|
|
400
|
+
useEffect(()=>{
|
|
401
|
+
iframeIdList.forEach((id)=>{
|
|
402
|
+
const iframe = document.getElementById("MJ-iframe-" + id);
|
|
403
|
+
if (!iframe) return;
|
|
404
|
+
iframe.addEventListener("load", ()=>{
|
|
405
|
+
observe(iframe);
|
|
406
|
+
const spinElement = iframe.parentElement?.querySelector("#MJ-spin-" + id);
|
|
407
|
+
if (spinElement) spinElement.remove();
|
|
408
|
+
});
|
|
409
|
+
});
|
|
410
|
+
return ()=>{
|
|
411
|
+
iframeIdList.forEach((id)=>{
|
|
412
|
+
const iframe = document.getElementById("MJ-iframe-" + id);
|
|
413
|
+
if (!iframe) return;
|
|
414
|
+
unobserve(iframe);
|
|
415
|
+
const spinElement = iframe.parentElement?.querySelector("#MJ-spin-" + iframe.id);
|
|
416
|
+
if (spinElement) spinElement.remove();
|
|
417
|
+
iframe.removeEventListener("load", ()=>{
|
|
418
|
+
console.log("iframe loaded", iframe.id);
|
|
419
|
+
});
|
|
420
|
+
});
|
|
421
|
+
};
|
|
422
|
+
}, [
|
|
423
|
+
iframeIdList,
|
|
424
|
+
observe,
|
|
425
|
+
unobserve
|
|
426
|
+
]);
|
|
239
427
|
return /*#__PURE__*/ jsx("div", {
|
|
240
428
|
className: "mes_text",
|
|
241
|
-
|
|
242
|
-
__html: mes
|
|
243
|
-
}
|
|
429
|
+
ref: containerRef
|
|
244
430
|
});
|
|
245
431
|
};
|
|
246
432
|
const MdRenderer = /*#__PURE__*/ react.memo(MdRendererBase, (prev, next)=>prev.content === next.content);
|
|
247
|
-
MdRendererBase.displayName =
|
|
248
|
-
MdRenderer.displayName =
|
|
433
|
+
MdRendererBase.displayName = "MdRenderer";
|
|
434
|
+
MdRenderer.displayName = "MdRenderer";
|
|
249
435
|
const chat_complete = async function(message, onData, signal) {
|
|
250
436
|
return await this.call("mujian:ai:chat:complete", {
|
|
251
437
|
content: message
|
|
@@ -341,9 +527,14 @@ class MujianSdk {
|
|
|
341
527
|
pendingRequests = new Map();
|
|
342
528
|
async init() {
|
|
343
529
|
const handshake = new postmate.Model({
|
|
344
|
-
reply: ({ id, complete, data })=>{
|
|
530
|
+
reply: ({ id, complete, data, error })=>{
|
|
345
531
|
const call = this.pendingRequests.get(id);
|
|
346
532
|
if (!call) return;
|
|
533
|
+
if (error) {
|
|
534
|
+
call.reject(error);
|
|
535
|
+
this.pendingRequests.delete(id);
|
|
536
|
+
return;
|
|
537
|
+
}
|
|
347
538
|
call.onData?.(data);
|
|
348
539
|
if (complete) {
|
|
349
540
|
call.onComplete?.();
|
|
@@ -445,6 +636,43 @@ class MujianSdk {
|
|
|
445
636
|
}
|
|
446
637
|
};
|
|
447
638
|
}
|
|
639
|
+
const styleSheet = `
|
|
640
|
+
@keyframes spin {
|
|
641
|
+
0% { transform: rotate(0deg); }
|
|
642
|
+
100% { transform: rotate(360deg); }
|
|
643
|
+
}
|
|
644
|
+
`;
|
|
645
|
+
const spinnerStyle = {
|
|
646
|
+
width: 48,
|
|
647
|
+
height: 48,
|
|
648
|
+
animation: 'spin 1s linear infinite'
|
|
649
|
+
};
|
|
650
|
+
const MujianSpinner = ({ className, style })=>/*#__PURE__*/ jsxs(Fragment, {
|
|
651
|
+
children: [
|
|
652
|
+
/*#__PURE__*/ jsx("style", {
|
|
653
|
+
children: styleSheet
|
|
654
|
+
}),
|
|
655
|
+
/*#__PURE__*/ jsx("svg", {
|
|
656
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
657
|
+
width: "24",
|
|
658
|
+
height: "24",
|
|
659
|
+
viewBox: "0 0 24 24",
|
|
660
|
+
fill: "none",
|
|
661
|
+
stroke: "currentColor",
|
|
662
|
+
strokeWidth: "2",
|
|
663
|
+
strokeLinecap: "round",
|
|
664
|
+
strokeLinejoin: "round",
|
|
665
|
+
className: className,
|
|
666
|
+
style: {
|
|
667
|
+
...spinnerStyle,
|
|
668
|
+
...style
|
|
669
|
+
},
|
|
670
|
+
children: /*#__PURE__*/ jsx("path", {
|
|
671
|
+
d: "M21 12a9 9 0 1 1-6.219-8.56"
|
|
672
|
+
})
|
|
673
|
+
})
|
|
674
|
+
]
|
|
675
|
+
});
|
|
448
676
|
const MujianContext = /*#__PURE__*/ createContext(null);
|
|
449
677
|
const MujianProvider = ({ children, loadingComponent })=>{
|
|
450
678
|
const [mujian, setMujian] = useState(null);
|
|
@@ -457,7 +685,20 @@ const MujianProvider = ({ children, loadingComponent })=>{
|
|
|
457
685
|
}, []);
|
|
458
686
|
return /*#__PURE__*/ jsx(MujianContext.Provider, {
|
|
459
687
|
value: mujian,
|
|
460
|
-
children: mujian ? children : loadingComponent ??
|
|
688
|
+
children: mujian ? children : loadingComponent ?? /*#__PURE__*/ jsx("div", {
|
|
689
|
+
style: {
|
|
690
|
+
height: '100%',
|
|
691
|
+
width: '100%',
|
|
692
|
+
display: 'flex',
|
|
693
|
+
justifyContent: 'center',
|
|
694
|
+
alignItems: 'center'
|
|
695
|
+
},
|
|
696
|
+
children: /*#__PURE__*/ jsx(MujianSpinner, {
|
|
697
|
+
style: {
|
|
698
|
+
color: '#EC4342'
|
|
699
|
+
}
|
|
700
|
+
})
|
|
701
|
+
})
|
|
461
702
|
});
|
|
462
703
|
};
|
|
463
704
|
const useMujian = ()=>{
|
|
@@ -574,6 +815,9 @@ async function* callSdk(mujian, content, type = 'generate', signal) {
|
|
|
574
815
|
resolveWait = resolve;
|
|
575
816
|
});
|
|
576
817
|
}
|
|
818
|
+
const NOT_SAVED_MSG_ID_PREFIX = 'not_saved';
|
|
819
|
+
const FALLBACK_MESSAGE = `<!-- 此条HTML消息为系统提示,请勿复读 -->
|
|
820
|
+
哎呀,AI线路好像抽风了,重说一下试试吧~`;
|
|
577
821
|
const useChatStreaming = ({ common, setError, setMessages, mujian })=>{
|
|
578
822
|
const { body } = common;
|
|
579
823
|
const [isStreaming, setIsStreaming] = useState(false);
|
|
@@ -582,7 +826,7 @@ const useChatStreaming = ({ common, setError, setMessages, mujian })=>{
|
|
|
582
826
|
setError(void 0);
|
|
583
827
|
setIsStreaming(true);
|
|
584
828
|
const userMessage = {
|
|
585
|
-
id: Date.now()
|
|
829
|
+
id: `${NOT_SAVED_MSG_ID_PREFIX}_user_${Date.now()}`,
|
|
586
830
|
content: query || '',
|
|
587
831
|
role: 'user',
|
|
588
832
|
swipes: [],
|
|
@@ -591,10 +835,12 @@ const useChatStreaming = ({ common, setError, setMessages, mujian })=>{
|
|
|
591
835
|
sendAt: new Date()
|
|
592
836
|
};
|
|
593
837
|
const aiMessage = {
|
|
594
|
-
id: Date.now()
|
|
838
|
+
id: `${NOT_SAVED_MSG_ID_PREFIX}_assistant_${Date.now()}`,
|
|
595
839
|
content: '',
|
|
596
840
|
role: 'assistant',
|
|
597
|
-
swipes: [
|
|
841
|
+
swipes: [
|
|
842
|
+
''
|
|
843
|
+
],
|
|
598
844
|
activeSwipeId: 0,
|
|
599
845
|
isStreaming: true,
|
|
600
846
|
sendAt: new Date()
|
|
@@ -636,7 +882,7 @@ const useChatStreaming = ({ common, setError, setMessages, mujian })=>{
|
|
|
636
882
|
const data = line.slice(6);
|
|
637
883
|
try {
|
|
638
884
|
const parsedData = JSON.parse(data);
|
|
639
|
-
if (!regenerate && parsedData.
|
|
885
|
+
if (!regenerate && parsedData.reply_id) {
|
|
640
886
|
const { question_id, reply_id } = parsedData;
|
|
641
887
|
setMessages((prev)=>{
|
|
642
888
|
const newMessages = [
|
|
@@ -683,16 +929,24 @@ const useChatStreaming = ({ common, setError, setMessages, mujian })=>{
|
|
|
683
929
|
}
|
|
684
930
|
}
|
|
685
931
|
}
|
|
932
|
+
} catch (error) {
|
|
933
|
+
console.log('Stream error', error);
|
|
934
|
+
setError(error instanceof Error ? error : new SendMessageError(String(error)));
|
|
686
935
|
} finally{
|
|
687
936
|
console.log('stream end finally');
|
|
688
937
|
setMessages((prev)=>{
|
|
689
938
|
const newMessages = [
|
|
690
939
|
...prev
|
|
691
940
|
];
|
|
692
|
-
|
|
941
|
+
const lastMessage = {
|
|
693
942
|
...newMessages[newMessages.length - 1],
|
|
694
943
|
isStreaming: false
|
|
695
944
|
};
|
|
945
|
+
if (!lastMessage.swipes[lastMessage.activeSwipeId]) {
|
|
946
|
+
lastMessage.swipes[lastMessage.activeSwipeId] = FALLBACK_MESSAGE;
|
|
947
|
+
lastMessage.content = FALLBACK_MESSAGE;
|
|
948
|
+
}
|
|
949
|
+
newMessages[newMessages.length - 1] = lastMessage;
|
|
696
950
|
return newMessages;
|
|
697
951
|
});
|
|
698
952
|
}
|
|
@@ -742,39 +996,67 @@ const useChat = ({ body, onError, onFinish })=>{
|
|
|
742
996
|
}, [
|
|
743
997
|
isStreaming
|
|
744
998
|
]);
|
|
745
|
-
const append = async (query)=>{
|
|
999
|
+
const append = useCallback(async (query)=>{
|
|
746
1000
|
if (isStreaming) throw new Error('useChat is streaming already.');
|
|
747
1001
|
const controller = new AbortController();
|
|
748
1002
|
setAbortController(controller);
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
1003
|
+
try {
|
|
1004
|
+
await chatStreaming({
|
|
1005
|
+
query,
|
|
1006
|
+
signal: controller.signal
|
|
1007
|
+
});
|
|
1008
|
+
} catch (e) {
|
|
1009
|
+
setError(e);
|
|
1010
|
+
throw e;
|
|
1011
|
+
} finally{
|
|
1012
|
+
setAbortController(void 0);
|
|
1013
|
+
}
|
|
1014
|
+
}, [
|
|
1015
|
+
isStreaming,
|
|
1016
|
+
chatStreaming
|
|
1017
|
+
]);
|
|
1018
|
+
const regenerate = useCallback(async ()=>{
|
|
756
1019
|
if (isStreaming) throw new Error('useChat is streaming already.');
|
|
757
1020
|
const controller = new AbortController();
|
|
758
1021
|
setAbortController(controller);
|
|
759
1022
|
const lastMessage = messages.findLast((message)=>'user' === message.role);
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
1023
|
+
try {
|
|
1024
|
+
await chatStreaming({
|
|
1025
|
+
query: lastMessage?.content || '',
|
|
1026
|
+
regenerate: true,
|
|
1027
|
+
signal: controller.signal
|
|
1028
|
+
});
|
|
1029
|
+
} catch (e) {
|
|
1030
|
+
setError(e);
|
|
1031
|
+
throw e;
|
|
1032
|
+
} finally{
|
|
1033
|
+
setAbortController(void 0);
|
|
1034
|
+
}
|
|
1035
|
+
}, [
|
|
1036
|
+
isStreaming,
|
|
1037
|
+
messages,
|
|
1038
|
+
chatStreaming
|
|
1039
|
+
]);
|
|
1040
|
+
const continueGenerate = useCallback(async ()=>{
|
|
768
1041
|
if (isStreaming) throw new Error('useChat is streaming already.');
|
|
769
1042
|
const controller = new AbortController();
|
|
770
1043
|
setAbortController(controller);
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
1044
|
+
try {
|
|
1045
|
+
await chatStreaming({
|
|
1046
|
+
query: '',
|
|
1047
|
+
is_continue: true,
|
|
1048
|
+
signal: controller.signal
|
|
1049
|
+
});
|
|
1050
|
+
} catch (e) {
|
|
1051
|
+
setError(e);
|
|
1052
|
+
throw e;
|
|
1053
|
+
} finally{
|
|
1054
|
+
setAbortController(void 0);
|
|
1055
|
+
}
|
|
1056
|
+
}, [
|
|
1057
|
+
isStreaming,
|
|
1058
|
+
chatStreaming
|
|
1059
|
+
]);
|
|
778
1060
|
const stop = ()=>{
|
|
779
1061
|
abortController?.abort();
|
|
780
1062
|
};
|
|
@@ -803,7 +1085,11 @@ const useChat = ({ body, onError, onFinish })=>{
|
|
|
803
1085
|
setMessages((prev)=>prev.map((msg)=>msg.id === messageId ? patchMessage(msg) : msg));
|
|
804
1086
|
};
|
|
805
1087
|
const deleteMessage = async (messageId)=>{
|
|
806
|
-
|
|
1088
|
+
if (!messageId.startsWith(NOT_SAVED_MSG_ID_PREFIX)) try {
|
|
1089
|
+
await mujian.ai.chat.message.deleteOne(messageId);
|
|
1090
|
+
} catch (e) {
|
|
1091
|
+
if (e?.code !== 2011030004) throw e;
|
|
1092
|
+
}
|
|
807
1093
|
setMessages((prev)=>prev.filter((msg)=>msg.id !== messageId));
|
|
808
1094
|
};
|
|
809
1095
|
return {
|
|
@@ -829,4 +1115,4 @@ const useChat = ({ body, onError, onFinish })=>{
|
|
|
829
1115
|
deleteMessage
|
|
830
1116
|
};
|
|
831
1117
|
};
|
|
832
|
-
export { MdRenderer, MujianProvider, Thread, useChat, useMujian };
|
|
1118
|
+
export { MdRenderer, MujianProvider, MujianSpinner, Thread, useChat, useMujian };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mujian/js-sdk",
|
|
3
|
-
"version": "0.0.6-beta.
|
|
3
|
+
"version": "0.0.6-beta.31",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
@@ -76,6 +76,7 @@
|
|
|
76
76
|
},
|
|
77
77
|
"dependencies": {
|
|
78
78
|
"dompurify": "^3.2.6",
|
|
79
|
+
"uuid": "^13.0.0",
|
|
79
80
|
"@adobe/css-tools": "^4.4.4",
|
|
80
81
|
"showdown": "^2.1.0",
|
|
81
82
|
"postmate": "^1.5.2",
|
|
File without changes
|