@rimori/react-client 0.4.9 → 0.4.10
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/audio/Playbutton.js +6 -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/hooks/I18nHooks.js +3 -1
- 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/audio/Playbutton.tsx +7 -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/hooks/I18nHooks.ts +3 -1
- package/src/index.ts +3 -0
- package/src/style.scss +58 -82
package/dist/style.css
CHANGED
|
@@ -6,105 +6,90 @@ dialog::backdrop {
|
|
|
6
6
|
background: transparent;
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
+
/* Tiptap editor content styles */
|
|
9
10
|
.tiptap {
|
|
10
|
-
|
|
11
|
-
padding-left: 7px;
|
|
12
|
-
/* min-height: 300px; */
|
|
13
|
-
}
|
|
14
|
-
.tiptap:focus-visible {
|
|
15
|
-
outline: none;
|
|
16
|
-
}
|
|
17
|
-
.tiptap h1,
|
|
18
|
-
.tiptap h2,
|
|
19
|
-
.tiptap h3,
|
|
20
|
-
.tiptap h4,
|
|
21
|
-
.tiptap h5,
|
|
22
|
-
.tiptap h6 {
|
|
23
|
-
@apply font-bold;
|
|
24
|
-
margin-bottom: 1rem;
|
|
11
|
+
@apply focus:outline-none;
|
|
25
12
|
}
|
|
26
13
|
.tiptap h1 {
|
|
27
|
-
@apply text-4xl;
|
|
14
|
+
@apply text-4xl font-bold mt-6 mb-3 first:mt-0;
|
|
28
15
|
}
|
|
29
16
|
.tiptap h2 {
|
|
30
|
-
@apply text-3xl;
|
|
17
|
+
@apply text-3xl font-semibold mt-5 mb-2 first:mt-0;
|
|
31
18
|
}
|
|
32
19
|
.tiptap h3 {
|
|
33
|
-
@apply text-2xl;
|
|
20
|
+
@apply text-2xl font-semibold mt-4 mb-2 first:mt-0;
|
|
34
21
|
}
|
|
35
|
-
.tiptap
|
|
36
|
-
@apply text-
|
|
22
|
+
.tiptap blockquote {
|
|
23
|
+
@apply border-l-4 border-primary/30 pl-4 py-1 my-3 text-muted-foreground italic;
|
|
37
24
|
}
|
|
38
|
-
.tiptap
|
|
39
|
-
@apply text-
|
|
25
|
+
.tiptap pre {
|
|
26
|
+
@apply bg-muted rounded-lg p-4 my-3 overflow-x-auto text-sm font-mono;
|
|
40
27
|
}
|
|
41
|
-
.tiptap
|
|
42
|
-
@apply text-
|
|
28
|
+
.tiptap code {
|
|
29
|
+
@apply bg-muted px-1.5 py-0.5 rounded text-sm font-mono;
|
|
43
30
|
}
|
|
44
|
-
.tiptap
|
|
45
|
-
@apply
|
|
31
|
+
.tiptap pre code {
|
|
32
|
+
@apply bg-transparent p-0;
|
|
33
|
+
}
|
|
34
|
+
.tiptap hr {
|
|
35
|
+
@apply border-border my-6;
|
|
46
36
|
}
|
|
47
37
|
.tiptap a {
|
|
48
|
-
@apply text-
|
|
49
|
-
text-decoration: none;
|
|
38
|
+
@apply text-primary underline underline-offset-2;
|
|
50
39
|
}
|
|
51
|
-
.tiptap
|
|
52
|
-
|
|
40
|
+
.tiptap {
|
|
41
|
+
/* Basic editor styles */
|
|
53
42
|
}
|
|
54
|
-
.tiptap
|
|
55
|
-
|
|
43
|
+
.tiptap :first-child {
|
|
44
|
+
margin-top: 0;
|
|
56
45
|
}
|
|
57
|
-
.tiptap
|
|
46
|
+
.tiptap li {
|
|
58
47
|
@apply mb-1;
|
|
59
48
|
}
|
|
60
|
-
.tiptap
|
|
61
|
-
@apply list-
|
|
49
|
+
.tiptap ul {
|
|
50
|
+
@apply list-disc list-inside dark:text-white pl-3 [&_p]:inline m-0;
|
|
62
51
|
}
|
|
63
|
-
.tiptap
|
|
64
|
-
@apply
|
|
52
|
+
.tiptap ul p {
|
|
53
|
+
@apply m-0;
|
|
65
54
|
}
|
|
66
|
-
.tiptap
|
|
67
|
-
@apply
|
|
68
|
-
border-color: #ccc;
|
|
55
|
+
.tiptap ul li {
|
|
56
|
+
@apply m-0;
|
|
69
57
|
}
|
|
70
|
-
.tiptap
|
|
71
|
-
|
|
58
|
+
.tiptap ol {
|
|
59
|
+
@apply list-decimal pl-7 m-0;
|
|
72
60
|
}
|
|
73
|
-
.tiptap
|
|
74
|
-
@apply
|
|
75
|
-
font-family: monospace;
|
|
76
|
-
white-space: pre-wrap;
|
|
77
|
-
word-wrap: break-word;
|
|
61
|
+
.tiptap ol p {
|
|
62
|
+
@apply m-0;
|
|
78
63
|
}
|
|
79
|
-
.tiptap
|
|
80
|
-
@apply
|
|
64
|
+
.tiptap ol li {
|
|
65
|
+
@apply m-0;
|
|
66
|
+
}
|
|
67
|
+
.tiptap {
|
|
68
|
+
/* Table-specific styling */
|
|
81
69
|
}
|
|
82
70
|
.tiptap table {
|
|
83
|
-
@apply
|
|
84
|
-
}
|
|
85
|
-
.tiptap
|
|
86
|
-
.tiptap
|
|
87
|
-
@apply border
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
@
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
.
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
.tiptap img {
|
|
106
|
-
@apply max-w-full;
|
|
107
|
-
}
|
|
71
|
+
@apply border-collapse m-0 overflow-hidden w-full mb-1;
|
|
72
|
+
}
|
|
73
|
+
.tiptap table td,
|
|
74
|
+
.tiptap table th {
|
|
75
|
+
@apply dark:border-gray-700 border;
|
|
76
|
+
@apply mb-0 align-baseline pl-1.5;
|
|
77
|
+
}
|
|
78
|
+
.tiptap table td p,
|
|
79
|
+
.tiptap table th p {
|
|
80
|
+
@apply mb-0;
|
|
81
|
+
}
|
|
82
|
+
.tiptap table th {
|
|
83
|
+
/* @apply bg-gray-500; */
|
|
84
|
+
@apply font-bold text-left;
|
|
85
|
+
}
|
|
86
|
+
.tiptap .tableWrapper {
|
|
87
|
+
margin: 1.5rem 0;
|
|
88
|
+
@apply overflow-x-auto;
|
|
89
|
+
}
|
|
90
|
+
.tiptap.resize-cursor {
|
|
91
|
+
@apply cursor-ew-resize;
|
|
92
|
+
cursor: col-resize;
|
|
108
93
|
}
|
|
109
94
|
|
|
110
95
|
/*# sourceMappingURL=style.css.map */
|
package/dist/style.css.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sourceRoot":"","sources":["../src/style.scss"],"names":[],"mappings":"AAAA;EACE;;;AAIF;EACE;;;AAGF;EACE
|
|
1
|
+
{"version":3,"sourceRoot":"","sources":["../src/style.scss"],"names":[],"mappings":"AAAA;EACE;;;AAIF;EACE;;;AAGF;AACA;EACE;;AAEA;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AApCJ;AAuCE;;AACA;EACE;;AAGF;EACE;;AAGF;EACE;;AAEA;EACE;;AAGF;EACE;;AAIJ;EACE;;AAEA;EACE;;AAGF;EACE;;AApEN;AAwEE;;AACA;EACE;;AAEA;AAAA;EAEE;EACA;;AAEA;AAAA;EACE;;AAIJ;AACE;EACA;;AAIJ;EACE;EACA;;AAGF;EACE;EACA","file":"style.css"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rimori/react-client",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.10",
|
|
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.
|
|
27
|
+
"@rimori/client": "^2.5.18",
|
|
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.
|
|
49
|
+
"@rimori/client": "^2.5.18",
|
|
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>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useState, useEffect, useRef } from 'react';
|
|
1
|
+
import React, { useState, useEffect, useLayoutEffect, useRef } from 'react';
|
|
2
2
|
import { FaPlayCircle, FaStopCircle } from 'react-icons/fa';
|
|
3
3
|
import { useRimori } from '../../providers/PluginProvider';
|
|
4
4
|
import { EventBus } from '@rimori/client';
|
|
@@ -129,9 +129,14 @@ export const AudioPlayer: React.FC<AudioPlayerProps> = ({
|
|
|
129
129
|
}
|
|
130
130
|
};
|
|
131
131
|
|
|
132
|
+
const togglePlaybackRef = useRef(togglePlayback);
|
|
133
|
+
useLayoutEffect(() => {
|
|
134
|
+
togglePlaybackRef.current = togglePlayback;
|
|
135
|
+
});
|
|
136
|
+
|
|
132
137
|
useEffect(() => {
|
|
133
138
|
if (!playListenerEvent) return;
|
|
134
|
-
const handler = () =>
|
|
139
|
+
const handler = () => togglePlaybackRef.current();
|
|
135
140
|
const listener = EventBus.on(playListenerEvent, handler);
|
|
136
141
|
eventBusListenerRef.current = listener;
|
|
137
142
|
|
|
@@ -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
|
+
}
|