@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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rimori/react-client",
3
- "version": "0.4.9-next.2",
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.1",
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
- "react-markdown": "^10.1.0"
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.1",
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
- <Markdown>{lastAssistantMessage}</Markdown>
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
+ }