@rimori/react-client 0.4.9-next.2 → 0.4.9-next.4
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/components/ai/Assistant.js +2 -2
- package/dist/components/editor/ImageUploadExtension.d.ts +13 -0
- package/dist/components/editor/ImageUploadExtension.js +93 -0
- package/dist/components/editor/MarkdownEditor.d.ts +47 -0
- package/dist/components/editor/MarkdownEditor.js +207 -0
- package/dist/components/editor/imageUtils.d.ts +10 -0
- package/dist/components/editor/imageUtils.js +52 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +2 -0
- package/dist/style.css +59 -74
- package/dist/style.css.map +1 -1
- package/package.json +17 -5
- package/src/components/ai/Assistant.tsx +2 -2
- package/src/components/editor/ImageUploadExtension.ts +88 -0
- package/src/components/editor/MarkdownEditor.tsx +621 -0
- package/src/components/editor/imageUtils.ts +58 -0
- package/src/index.ts +3 -0
- package/src/style.scss +58 -82
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rimori/react-client",
|
|
3
|
-
"version": "0.4.9-next.
|
|
3
|
+
"version": "0.4.9-next.4",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -13,7 +13,8 @@
|
|
|
13
13
|
".": {
|
|
14
14
|
"types": "./dist/index.d.ts",
|
|
15
15
|
"default": "./dist/index.js"
|
|
16
|
-
}
|
|
16
|
+
},
|
|
17
|
+
"./dist/style.css": "./dist/style.css"
|
|
17
18
|
},
|
|
18
19
|
"scripts": {
|
|
19
20
|
"build": "tsc && sass src/style.scss:dist/style.css",
|
|
@@ -23,18 +24,29 @@
|
|
|
23
24
|
"format": "prettier --write ."
|
|
24
25
|
},
|
|
25
26
|
"peerDependencies": {
|
|
26
|
-
"@rimori/client": "2.5.17-next.
|
|
27
|
+
"@rimori/client": "2.5.17-next.2",
|
|
27
28
|
"react": "^18.1.0",
|
|
28
29
|
"react-dom": "^18.1.0"
|
|
29
30
|
},
|
|
30
31
|
"dependencies": {
|
|
32
|
+
"@tiptap/core": "^2.26.1",
|
|
33
|
+
"@tiptap/extension-image": "^2.26.1",
|
|
34
|
+
"@tiptap/extension-link": "^2.26.1",
|
|
35
|
+
"@tiptap/extension-table": "^2.26.1",
|
|
36
|
+
"@tiptap/extension-table-cell": "^2.26.1",
|
|
37
|
+
"@tiptap/extension-table-header": "^2.26.1",
|
|
38
|
+
"@tiptap/extension-table-row": "^2.26.1",
|
|
39
|
+
"@tiptap/extension-youtube": "^2.26.1",
|
|
40
|
+
"@tiptap/pm": "^2.26.1",
|
|
41
|
+
"@tiptap/react": "^2.26.1",
|
|
42
|
+
"@tiptap/starter-kit": "^2.26.1",
|
|
31
43
|
"html2canvas": "1.4.1",
|
|
32
44
|
"react-icons": "5.4.0",
|
|
33
|
-
"
|
|
45
|
+
"tiptap-markdown": "^0.8.10"
|
|
34
46
|
},
|
|
35
47
|
"devDependencies": {
|
|
36
48
|
"@eslint/js": "^9.37.0",
|
|
37
|
-
"@rimori/client": "2.5.17-next.
|
|
49
|
+
"@rimori/client": "2.5.17-next.2",
|
|
38
50
|
"@types/react": "^18.3.21",
|
|
39
51
|
"eslint-config-prettier": "^10.1.8",
|
|
40
52
|
"eslint-plugin-prettier": "^5.5.4",
|
|
@@ -2,10 +2,10 @@ import React, { useEffect, useMemo } from 'react';
|
|
|
2
2
|
import { CircleAudioAvatar } from './EmbeddedAssistent/CircleAudioAvatar';
|
|
3
3
|
import { AudioInputField } from './EmbeddedAssistent/AudioInputField';
|
|
4
4
|
import { MessageSender } from '@rimori/client';
|
|
5
|
-
import Markdown from 'react-markdown';
|
|
6
5
|
import { useChat } from '../../hooks/UseChatHook';
|
|
7
6
|
import { useRimori } from '../../providers/PluginProvider';
|
|
8
7
|
import { FirstMessages, getFirstMessages } from './utils';
|
|
8
|
+
import { MarkdownEditor } from '../editor/MarkdownEditor';
|
|
9
9
|
|
|
10
10
|
interface Props {
|
|
11
11
|
voiceId: any;
|
|
@@ -84,7 +84,7 @@ export function AssistantChat({ avatarImageUrl, voiceId, onComplete, autoStartCo
|
|
|
84
84
|
<div className="w-full">
|
|
85
85
|
{lastAssistantMessage && (
|
|
86
86
|
<div className="px-5 pt-5 overflow-y-auto remirror-theme" style={{ height: '4k78px' }}>
|
|
87
|
-
<
|
|
87
|
+
<MarkdownEditor editable={false} content={lastAssistantMessage} />
|
|
88
88
|
</div>
|
|
89
89
|
)}
|
|
90
90
|
</div>
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import Image from '@tiptap/extension-image';
|
|
2
|
+
import { Plugin, PluginKey } from '@tiptap/pm/state';
|
|
3
|
+
import type { Editor } from '@tiptap/core';
|
|
4
|
+
import { convertToPng } from './imageUtils';
|
|
5
|
+
|
|
6
|
+
const pluginKey = new PluginKey('imageUpload');
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Tiptap extension that adds image upload via:
|
|
10
|
+
* - Drag-and-drop of image files onto the editor
|
|
11
|
+
* - Paste of image data from the clipboard
|
|
12
|
+
* - A dedicated toolbar button (triggers a hidden <input type="file">)
|
|
13
|
+
*
|
|
14
|
+
* Provide the `uploadImage` callback to handle uploading to your backend.
|
|
15
|
+
* If no callback is provided the extension is still registered but upload is disabled.
|
|
16
|
+
*/
|
|
17
|
+
export const ImageUploadExtension = (uploadImage?: (pngBlob: Blob) => Promise<string | null>) =>
|
|
18
|
+
Image.extend({
|
|
19
|
+
addProseMirrorPlugins() {
|
|
20
|
+
const editor: Editor = this.editor;
|
|
21
|
+
|
|
22
|
+
return [
|
|
23
|
+
new Plugin({
|
|
24
|
+
key: pluginKey,
|
|
25
|
+
props: {
|
|
26
|
+
handleDrop(view, event) {
|
|
27
|
+
if (!uploadImage) return false;
|
|
28
|
+
const files = Array.from(event.dataTransfer?.files ?? []);
|
|
29
|
+
const imageFile = files.find((f) => f.type.startsWith('image/'));
|
|
30
|
+
if (!imageFile) return false;
|
|
31
|
+
|
|
32
|
+
event.preventDefault();
|
|
33
|
+
|
|
34
|
+
// Insert a placeholder position then replace with real URL
|
|
35
|
+
void handleImageFile(imageFile, uploadImage, editor);
|
|
36
|
+
return true;
|
|
37
|
+
},
|
|
38
|
+
|
|
39
|
+
handlePaste(view, event) {
|
|
40
|
+
if (!uploadImage) return false;
|
|
41
|
+
const items = Array.from(event.clipboardData?.items ?? []);
|
|
42
|
+
const imageItem = items.find((i) => i.type.startsWith('image/'));
|
|
43
|
+
if (!imageItem) return false;
|
|
44
|
+
|
|
45
|
+
const file = imageItem.getAsFile();
|
|
46
|
+
if (!file) return false;
|
|
47
|
+
|
|
48
|
+
event.preventDefault();
|
|
49
|
+
void handleImageFile(file, uploadImage, editor);
|
|
50
|
+
return true;
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
}),
|
|
54
|
+
];
|
|
55
|
+
},
|
|
56
|
+
}).configure({
|
|
57
|
+
HTMLAttributes: {
|
|
58
|
+
class: 'max-w-full h-auto rounded-lg my-4',
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
async function handleImageFile(
|
|
63
|
+
file: File | Blob,
|
|
64
|
+
uploadImage: (pngBlob: Blob) => Promise<string | null>,
|
|
65
|
+
editor: Editor,
|
|
66
|
+
): Promise<void> {
|
|
67
|
+
try {
|
|
68
|
+
const pngBlob = await convertToPng(file);
|
|
69
|
+
const url = await uploadImage(pngBlob);
|
|
70
|
+
if (url) {
|
|
71
|
+
editor.chain().focus().setImage({ src: url }).run();
|
|
72
|
+
}
|
|
73
|
+
} catch {
|
|
74
|
+
// Upload failed – silently ignore (caller is responsible for showing errors)
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** Trigger an image file picker and upload the selected file. */
|
|
79
|
+
export function triggerImageUpload(uploadImage: (pngBlob: Blob) => Promise<string | null>, editor: Editor): void {
|
|
80
|
+
const input = document.createElement('input');
|
|
81
|
+
input.type = 'file';
|
|
82
|
+
input.accept = 'image/*';
|
|
83
|
+
input.onchange = async () => {
|
|
84
|
+
const file = input.files?.[0];
|
|
85
|
+
if (file) await handleImageFile(file, uploadImage, editor);
|
|
86
|
+
};
|
|
87
|
+
input.click();
|
|
88
|
+
}
|