@rimori/react-client 0.4.9 → 0.4.10-next.1

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/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
- padding-top: 5px;
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 h4 {
36
- @apply text-xl;
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 h5 {
39
- @apply text-lg;
25
+ .tiptap pre {
26
+ @apply bg-muted rounded-lg p-4 my-3 overflow-x-auto text-sm font-mono;
40
27
  }
41
- .tiptap h6 {
42
- @apply text-base;
28
+ .tiptap code {
29
+ @apply bg-muted px-1.5 py-0.5 rounded text-sm font-mono;
43
30
  }
44
- .tiptap p {
45
- @apply mb-4;
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-blue-600 hover:text-blue-800;
49
- text-decoration: none;
38
+ @apply text-primary underline underline-offset-2;
50
39
  }
51
- .tiptap a:hover {
52
- @apply underline;
40
+ .tiptap {
41
+ /* Basic editor styles */
53
42
  }
54
- .tiptap ul {
55
- @apply list-disc pl-8;
43
+ .tiptap :first-child {
44
+ margin-top: 0;
56
45
  }
57
- .tiptap ul li > p {
46
+ .tiptap li {
58
47
  @apply mb-1;
59
48
  }
60
- .tiptap ol {
61
- @apply list-decimal pl-7;
49
+ .tiptap ul {
50
+ @apply list-disc list-outside dark:text-white pl-6 m-0 [&_p]:inline;
62
51
  }
63
- .tiptap ol li > p {
64
- @apply mb-1;
52
+ .tiptap ul p {
53
+ @apply m-0;
65
54
  }
66
- .tiptap blockquote {
67
- @apply border-l-4 pl-4 italic text-gray-600 my-4;
68
- border-color: #ccc;
55
+ .tiptap ul li {
56
+ @apply m-0;
69
57
  }
70
- .tiptap code {
71
- font-family: monospace;
58
+ .tiptap ol {
59
+ @apply list-decimal pl-7 m-0;
72
60
  }
73
- .tiptap pre {
74
- @apply bg-gray-800 text-gray-500 p-4 rounded-lg overflow-x-auto;
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 img {
80
- @apply max-w-full h-auto rounded-lg my-4;
64
+ .tiptap ol li {
65
+ @apply m-0;
66
+ }
67
+ .tiptap {
68
+ /* Table-specific styling */
81
69
  }
82
70
  .tiptap table {
83
- @apply table-auto w-full border-collapse mb-4;
84
- }
85
- .tiptap th,
86
- .tiptap td {
87
- @apply border px-4 py-2 text-left;
88
- }
89
- .tiptap th {
90
- @apply bg-gray-500 font-semibold;
91
- }
92
- .tiptap tr:nth-child(even) {
93
- @apply bg-gray-400;
94
- }
95
- @media (max-width: 768px) {
96
- .tiptap h1 {
97
- @apply text-3xl;
98
- }
99
- .tiptap h2 {
100
- @apply text-2xl;
101
- }
102
- .tiptap p {
103
- @apply text-base;
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 */
@@ -1 +1 @@
1
- {"version":3,"sourceRoot":"","sources":["../src/style.scss"],"names":[],"mappings":"AAAA;EACE;;;AAIF;EACE;;;AAGF;EACE;EACA;AACA;;AAEA;EACE;;AAGF;AAAA;AAAA;AAAA;AAAA;AAAA;EAME;EACA;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;EACA;;AAGF;EACE;;AAGF;EACE;;AAEA;EACE;;AAIJ;EACE;;AAEA;EACE;;AAIJ;EACE;EACA;;AAGF;EACE;;AAGF;EACE;EACA;EACA;EACA;;AAGF;EACE;;AAGF;EACE;;AAGF;AAAA;EAEE;;AAGF;EACE;;AAGF;EACE;;AAGF;EACE;IACE;;EAGF;IACE;;EAGF;IACE;;EAGF;IACE","file":"style.css"}
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.9",
3
+ "version": "0.4.10-next.1",
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.16",
27
+ "@rimori/client": "2.5.18-next.4",
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.16",
49
+ "@rimori/client": "2.5.18-next.4",
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>
@@ -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 = () => togglePlayback();
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
+ }