@rimori/client 1.0.4 → 1.1.0
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/README.md +13 -0
- package/dist/components/MarkdownEditor.js +6 -4
- package/dist/components/PluginController.d.ts +21 -0
- package/dist/components/PluginController.js +116 -0
- package/dist/components/ai/Assistant.js +1 -1
- package/dist/components/ai/Avatar.d.ts +5 -3
- package/dist/components/ai/Avatar.js +14 -6
- package/dist/components/ai/EmbeddedAssistent/CircleAudioAvatar.d.ts +2 -1
- package/dist/components/ai/EmbeddedAssistent/CircleAudioAvatar.js +35 -14
- package/dist/components/ai/EmbeddedAssistent/TTS/MessageSender.d.ts +1 -0
- package/dist/components/ai/EmbeddedAssistent/TTS/MessageSender.js +3 -0
- package/dist/components/ai/EmbeddedAssistent/TTS/Player.d.ts +2 -0
- package/dist/components/ai/EmbeddedAssistent/TTS/Player.js +5 -0
- package/dist/components/ai/EmbeddedAssistent/VoiceRecoder.d.ts +3 -0
- package/dist/components/ai/EmbeddedAssistent/VoiceRecoder.js +41 -5
- package/dist/components/ai/utils.d.ts +1 -1
- package/dist/components.d.ts +1 -0
- package/dist/components.js +1 -0
- package/dist/controller/AIController.js +2 -1
- package/dist/controller/SettingsController.d.ts +18 -10
- package/dist/controller/SettingsController.js +28 -31
- package/dist/controller/SharedContentController.d.ts +58 -11
- package/dist/controller/SharedContentController.js +161 -26
- package/dist/controller/SidePluginController.d.ts +1 -12
- package/dist/core/components/ContextMenu.d.ts +10 -0
- package/dist/core/components/ContextMenu.js +93 -0
- package/dist/core.d.ts +2 -0
- package/dist/core.js +2 -0
- package/dist/hooks/UseChatHook.d.ts +1 -1
- package/dist/plugin/AccomplishmentHandler.d.ts +38 -0
- package/dist/plugin/AccomplishmentHandler.js +108 -0
- package/dist/plugin/ContextMenu.d.ts +17 -0
- package/dist/plugin/ContextMenu.js +45 -0
- package/dist/plugin/PluginController.js +9 -4
- package/dist/plugin/RimoriClient.d.ts +92 -65
- package/dist/plugin/RimoriClient.js +105 -75
- package/dist/plugin/ThemeSetter.js +4 -4
- package/dist/plugin/fromRimori/EventBus.d.ts +6 -3
- package/dist/plugin/fromRimori/EventBus.js +15 -9
- package/dist/plugin/fromRimori/PluginTypes.d.ts +51 -0
- package/dist/plugin/fromRimori/PluginTypes.js +1 -0
- package/dist/providers/PluginController.d.ts +21 -0
- package/dist/providers/PluginController.js +116 -0
- package/dist/providers/PluginProvider.js +26 -73
- package/dist/types/Actions.d.ts +4 -0
- package/dist/types/Actions.js +1 -0
- package/dist/utils/Language.d.ts +66 -0
- package/dist/utils/Language.js +67 -0
- package/dist/utils/difficultyConverter.d.ts +1 -0
- package/dist/utils/difficultyConverter.js +3 -0
- package/dist/worker/WorkerSetup.js +5 -4
- package/package.json +3 -3
- package/src/components/MarkdownEditor.tsx +78 -76
- package/src/components/ai/Assistant.tsx +1 -1
- package/src/components/ai/Avatar.tsx +65 -48
- package/src/components/ai/EmbeddedAssistent/CircleAudioAvatar.tsx +81 -58
- package/src/components/ai/EmbeddedAssistent/TTS/MessageSender.ts +4 -0
- package/src/components/ai/EmbeddedAssistent/TTS/Player.ts +6 -0
- package/src/components/ai/EmbeddedAssistent/VoiceRecoder.tsx +51 -8
- package/src/components/ai/utils.ts +1 -1
- package/src/components.ts +2 -1
- package/src/controller/AIController.ts +2 -1
- package/src/controller/SettingsController.ts +80 -75
- package/src/controller/SharedContentController.ts +214 -53
- package/src/controller/SidePluginController.ts +1 -13
- package/src/core/components/ContextMenu.tsx +123 -0
- package/src/core.ts +3 -1
- package/src/hooks/UseChatHook.ts +17 -17
- package/src/plugin/AccomplishmentHandler.ts +165 -0
- package/src/plugin/PluginController.ts +107 -100
- package/src/plugin/RimoriClient.ts +267 -250
- package/src/plugin/ThemeSetter.ts +4 -5
- package/src/plugin/fromRimori/EventBus.ts +23 -12
- package/src/plugin/fromRimori/PluginTypes.ts +67 -0
- package/src/providers/PluginProvider.tsx +63 -110
- package/src/types/Actions.ts +6 -0
- package/src/utils/Language.ts +70 -0
- package/src/utils/difficultyConverter.ts +4 -0
- package/src/worker/WorkerSetup.ts +5 -4
- package/dist/components/avatar/Assistant.d.ts +0 -9
- package/dist/components/avatar/Assistant.js +0 -59
- package/dist/components/avatar/Avatar.d.ts +0 -12
- package/dist/components/avatar/Avatar.js +0 -42
- package/dist/components/avatar/EmbeddedAssistent/AudioInputField.d.ts +0 -7
- package/dist/components/avatar/EmbeddedAssistent/AudioInputField.js +0 -38
- package/dist/components/avatar/EmbeddedAssistent/CircleAudioAvatar.d.ts +0 -7
- package/dist/components/avatar/EmbeddedAssistent/CircleAudioAvatar.js +0 -59
- package/dist/components/avatar/EmbeddedAssistent/TTS/MessageSender.d.ts +0 -19
- package/dist/components/avatar/EmbeddedAssistent/TTS/MessageSender.js +0 -84
- package/dist/components/avatar/EmbeddedAssistent/TTS/Player.d.ts +0 -25
- package/dist/components/avatar/EmbeddedAssistent/TTS/Player.js +0 -180
- package/dist/components/avatar/EmbeddedAssistent/VoiceRecoder.d.ts +0 -7
- package/dist/components/avatar/EmbeddedAssistent/VoiceRecoder.js +0 -45
- package/dist/components/avatar/utils.d.ts +0 -6
- package/dist/components/avatar/utils.js +0 -14
|
@@ -1,33 +1,34 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { createContext, useContext, useEffect, useState } from 'react';
|
|
3
3
|
import { PluginController } from '../plugin/PluginController';
|
|
4
4
|
import { EventBusHandler } from '../plugin/fromRimori/EventBus';
|
|
5
|
-
|
|
5
|
+
import ContextMenu from '../core/components/ContextMenu';
|
|
6
6
|
const PluginContext = createContext(null);
|
|
7
7
|
export const PluginProvider = ({ children, pluginId }) => {
|
|
8
8
|
const [plugin, setPlugin] = useState(null);
|
|
9
|
-
|
|
10
|
-
//route change
|
|
9
|
+
initEventBus(pluginId);
|
|
11
10
|
useEffect(() => {
|
|
12
|
-
let lastHash = window.location.hash;
|
|
13
|
-
setInterval(() => {
|
|
14
|
-
if (lastHash !== window.location.hash) {
|
|
15
|
-
lastHash = window.location.hash;
|
|
16
|
-
console.log('url changed:', lastHash);
|
|
17
|
-
plugin === null || plugin === void 0 ? void 0 : plugin.event.emit('session.triggerUrlChange', window.location.hash);
|
|
18
|
-
}
|
|
19
|
-
}, 100);
|
|
20
11
|
PluginController.getInstance(pluginId).then(setPlugin);
|
|
21
|
-
}, []);
|
|
22
|
-
//
|
|
12
|
+
}, [pluginId]);
|
|
13
|
+
//route change
|
|
23
14
|
useEffect(() => {
|
|
24
15
|
if (!plugin)
|
|
25
16
|
return;
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
17
|
+
const url = new URL(window.location.href);
|
|
18
|
+
//sidebar pages should not report url changes
|
|
19
|
+
if (url.searchParams.get("applicationMode") === "sidebar")
|
|
20
|
+
return;
|
|
21
|
+
let lastHash = url.hash;
|
|
22
|
+
const emitUrlChange = (url) => plugin.event.emit('session.triggerUrlChange', { url });
|
|
23
|
+
const interval = setInterval(() => {
|
|
24
|
+
if (lastHash === window.location.hash)
|
|
25
|
+
return;
|
|
26
|
+
lastHash = window.location.hash;
|
|
27
|
+
// console.log('url changed:', lastHash);
|
|
28
|
+
emitUrlChange(lastHash);
|
|
29
|
+
}, 1000);
|
|
30
|
+
emitUrlChange(lastHash);
|
|
31
|
+
return () => clearInterval(interval);
|
|
31
32
|
}, [plugin]);
|
|
32
33
|
//detect page height change
|
|
33
34
|
useEffect(() => {
|
|
@@ -37,63 +38,10 @@ export const PluginProvider = ({ children, pluginId }) => {
|
|
|
37
38
|
handleResize();
|
|
38
39
|
return () => body.removeEventListener('resize', handleResize);
|
|
39
40
|
}, [plugin]);
|
|
40
|
-
//context menu
|
|
41
|
-
useEffect(() => {
|
|
42
|
-
let lastMouseX = 0;
|
|
43
|
-
let lastMouseY = 0;
|
|
44
|
-
let isSelecting = false;
|
|
45
|
-
// Track mouse position
|
|
46
|
-
const handleMouseMove = (e) => {
|
|
47
|
-
lastMouseX = e.clientX;
|
|
48
|
-
lastMouseY = e.clientY;
|
|
49
|
-
};
|
|
50
|
-
const handleContextMenu = (e) => {
|
|
51
|
-
var _a;
|
|
52
|
-
const selection = (_a = window.getSelection()) === null || _a === void 0 ? void 0 : _a.toString().trim();
|
|
53
|
-
if (selection) {
|
|
54
|
-
e.preventDefault();
|
|
55
|
-
// console.log('context menu handled', selection);
|
|
56
|
-
plugin === null || plugin === void 0 ? void 0 : plugin.event.emit('global.contextMenu.trigger', { text: selection, x: e.clientX, y: e.clientY, open: true });
|
|
57
|
-
}
|
|
58
|
-
};
|
|
59
|
-
const handleSelectionChange = () => {
|
|
60
|
-
var _a;
|
|
61
|
-
// if (triggerOnTextSelection) {
|
|
62
|
-
const selection = (_a = window.getSelection()) === null || _a === void 0 ? void 0 : _a.toString().trim();
|
|
63
|
-
const open = !!selection && isSelecting;
|
|
64
|
-
// console.log('Selection change, contextMenuOnSelect:', contextMenuOnSelect);
|
|
65
|
-
plugin === null || plugin === void 0 ? void 0 : plugin.event.emit('global.contextMenu.trigger', { text: selection, x: lastMouseX, y: lastMouseY, open });
|
|
66
|
-
// }
|
|
67
|
-
};
|
|
68
|
-
const handleMouseUpDown = (e) => {
|
|
69
|
-
if (e.type === 'mousedown') {
|
|
70
|
-
isSelecting = false;
|
|
71
|
-
}
|
|
72
|
-
else if (e.type === 'mouseup') {
|
|
73
|
-
isSelecting = true;
|
|
74
|
-
// console.log('mouseup, contextMenuOnSelect:', contextMenuOnSelect);
|
|
75
|
-
if (contextMenuOnSelect) {
|
|
76
|
-
handleSelectionChange();
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
};
|
|
80
|
-
document.addEventListener('contextmenu', handleContextMenu);
|
|
81
|
-
document.addEventListener('selectionchange', handleSelectionChange);
|
|
82
|
-
document.addEventListener("mousemove", handleMouseMove);
|
|
83
|
-
document.addEventListener('mousedown', handleMouseUpDown);
|
|
84
|
-
document.addEventListener('mouseup', handleMouseUpDown);
|
|
85
|
-
return () => {
|
|
86
|
-
document.removeEventListener("mousemove", handleMouseMove);
|
|
87
|
-
document.removeEventListener('contextmenu', handleContextMenu);
|
|
88
|
-
document.removeEventListener('selectionchange', handleSelectionChange);
|
|
89
|
-
document.removeEventListener('mousedown', handleMouseUpDown);
|
|
90
|
-
document.removeEventListener('mouseup', handleMouseUpDown);
|
|
91
|
-
};
|
|
92
|
-
}, [plugin, contextMenuOnSelect]);
|
|
93
41
|
if (!plugin) {
|
|
94
42
|
return "";
|
|
95
43
|
}
|
|
96
|
-
return (
|
|
44
|
+
return (_jsxs(PluginContext.Provider, { value: plugin, children: [_jsx(ContextMenu, { client: plugin }), children] }));
|
|
97
45
|
};
|
|
98
46
|
export const usePlugin = () => {
|
|
99
47
|
const context = useContext(PluginContext);
|
|
@@ -102,3 +50,8 @@ export const usePlugin = () => {
|
|
|
102
50
|
}
|
|
103
51
|
return context;
|
|
104
52
|
};
|
|
53
|
+
function initEventBus(pluginId) {
|
|
54
|
+
const url = new URL(window.location.href);
|
|
55
|
+
const isSidebar = url.searchParams.get("applicationMode") === "sidebar";
|
|
56
|
+
EventBusHandler.getInstance("Plugin EventBus " + pluginId + " " + (isSidebar ? "sidebar" : "main"));
|
|
57
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
export declare const languageKeys: {
|
|
2
|
+
readonly sq: "albanian";
|
|
3
|
+
readonly ar: "arabic";
|
|
4
|
+
readonly hy: "armenian";
|
|
5
|
+
readonly az: "azerbaijani";
|
|
6
|
+
readonly bn: "bengali";
|
|
7
|
+
readonly bs: "bosnian";
|
|
8
|
+
readonly bg: "bulgarian";
|
|
9
|
+
readonly ca: "catalan";
|
|
10
|
+
readonly zh: "chinese";
|
|
11
|
+
readonly hr: "croatian";
|
|
12
|
+
readonly cs: "czech";
|
|
13
|
+
readonly da: "danish";
|
|
14
|
+
readonly nl: "dutch";
|
|
15
|
+
readonly en: "english";
|
|
16
|
+
readonly et: "estonian";
|
|
17
|
+
readonly fi: "finnish";
|
|
18
|
+
readonly fr: "french";
|
|
19
|
+
readonly gl: "galician";
|
|
20
|
+
readonly de: "german";
|
|
21
|
+
readonly el: "greek";
|
|
22
|
+
readonly he: "hebrew";
|
|
23
|
+
readonly hi: "hindi";
|
|
24
|
+
readonly hu: "hungarian";
|
|
25
|
+
readonly is: "icelandic";
|
|
26
|
+
readonly id: "indonesian";
|
|
27
|
+
readonly it: "italian";
|
|
28
|
+
readonly ja: "japanese";
|
|
29
|
+
readonly kn: "kannada";
|
|
30
|
+
readonly kk: "kazakh";
|
|
31
|
+
readonly ko: "korean";
|
|
32
|
+
readonly lv: "latvian";
|
|
33
|
+
readonly lt: "lithuanian";
|
|
34
|
+
readonly mk: "macedonian";
|
|
35
|
+
readonly ms: "malay";
|
|
36
|
+
readonly mr: "marathi";
|
|
37
|
+
readonly mi: "maori";
|
|
38
|
+
readonly ne: "nepali";
|
|
39
|
+
readonly no: "norwegian";
|
|
40
|
+
readonly fa: "persian";
|
|
41
|
+
readonly pl: "polish";
|
|
42
|
+
readonly pt: "portuguese";
|
|
43
|
+
readonly ro: "romanian";
|
|
44
|
+
readonly ru: "russian";
|
|
45
|
+
readonly sr: "serbian";
|
|
46
|
+
readonly sk: "slovak";
|
|
47
|
+
readonly sl: "slovenian";
|
|
48
|
+
readonly es: "spanish";
|
|
49
|
+
readonly sw: "swahili";
|
|
50
|
+
readonly sv: "swedish";
|
|
51
|
+
readonly tl: "filipino";
|
|
52
|
+
readonly ta: "tamil";
|
|
53
|
+
readonly th: "thai";
|
|
54
|
+
readonly tr: "turkish";
|
|
55
|
+
readonly uk: "ukrainian";
|
|
56
|
+
readonly ur: "urdu";
|
|
57
|
+
readonly vi: "vietnamese";
|
|
58
|
+
readonly cy: "welsh";
|
|
59
|
+
};
|
|
60
|
+
export type Language = keyof typeof languageKeys;
|
|
61
|
+
/**
|
|
62
|
+
* Get the language name from the language code
|
|
63
|
+
* @param languageCode The code of the language
|
|
64
|
+
* @returns The language name
|
|
65
|
+
*/
|
|
66
|
+
export declare function getLanguageName(languageCode: Language): string;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
export const languageKeys = {
|
|
2
|
+
sq: "albanian",
|
|
3
|
+
ar: "arabic",
|
|
4
|
+
hy: "armenian",
|
|
5
|
+
az: "azerbaijani",
|
|
6
|
+
bn: "bengali",
|
|
7
|
+
bs: "bosnian",
|
|
8
|
+
bg: "bulgarian",
|
|
9
|
+
ca: "catalan",
|
|
10
|
+
zh: "chinese",
|
|
11
|
+
hr: "croatian",
|
|
12
|
+
cs: "czech",
|
|
13
|
+
da: "danish",
|
|
14
|
+
nl: "dutch",
|
|
15
|
+
en: "english",
|
|
16
|
+
et: "estonian",
|
|
17
|
+
fi: "finnish",
|
|
18
|
+
fr: "french",
|
|
19
|
+
gl: "galician",
|
|
20
|
+
de: "german",
|
|
21
|
+
el: "greek",
|
|
22
|
+
he: "hebrew",
|
|
23
|
+
hi: "hindi",
|
|
24
|
+
hu: "hungarian",
|
|
25
|
+
is: "icelandic",
|
|
26
|
+
id: "indonesian",
|
|
27
|
+
it: "italian",
|
|
28
|
+
ja: "japanese",
|
|
29
|
+
kn: "kannada",
|
|
30
|
+
kk: "kazakh",
|
|
31
|
+
ko: "korean",
|
|
32
|
+
lv: "latvian",
|
|
33
|
+
lt: "lithuanian",
|
|
34
|
+
mk: "macedonian",
|
|
35
|
+
ms: "malay",
|
|
36
|
+
mr: "marathi",
|
|
37
|
+
mi: "maori",
|
|
38
|
+
ne: "nepali",
|
|
39
|
+
no: "norwegian",
|
|
40
|
+
fa: "persian",
|
|
41
|
+
pl: "polish",
|
|
42
|
+
pt: "portuguese",
|
|
43
|
+
ro: "romanian",
|
|
44
|
+
ru: "russian",
|
|
45
|
+
sr: "serbian",
|
|
46
|
+
sk: "slovak",
|
|
47
|
+
sl: "slovenian",
|
|
48
|
+
es: "spanish",
|
|
49
|
+
sw: "swahili",
|
|
50
|
+
sv: "swedish",
|
|
51
|
+
tl: "filipino",
|
|
52
|
+
ta: "tamil",
|
|
53
|
+
th: "thai",
|
|
54
|
+
tr: "turkish",
|
|
55
|
+
uk: "ukrainian",
|
|
56
|
+
ur: "urdu",
|
|
57
|
+
vi: "vietnamese",
|
|
58
|
+
cy: "welsh"
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* Get the language name from the language code
|
|
62
|
+
* @param languageCode The code of the language
|
|
63
|
+
* @returns The language name
|
|
64
|
+
*/
|
|
65
|
+
export function getLanguageName(languageCode) {
|
|
66
|
+
return languageKeys[languageCode];
|
|
67
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
export type LanguageLevel = "Pre-A1" | "A1" | "A2" | "B1" | "B2" | "C1" | "C2" | "Post-C2";
|
|
2
2
|
export declare function getDifficultyLevel(difficulty: LanguageLevel): number;
|
|
3
3
|
export declare function getDifficultyLabel(difficulty: number): LanguageLevel;
|
|
4
|
+
export declare function getNeighborDifficultyLevel(difficulty: LanguageLevel, difficultyAdjustment: number): LanguageLevel;
|
|
@@ -5,3 +5,6 @@ export function getDifficultyLevel(difficulty) {
|
|
|
5
5
|
export function getDifficultyLabel(difficulty) {
|
|
6
6
|
return codes[difficulty];
|
|
7
7
|
}
|
|
8
|
+
export function getNeighborDifficultyLevel(difficulty, difficultyAdjustment) {
|
|
9
|
+
return getDifficultyLabel(getDifficultyLevel(difficulty) + difficultyAdjustment);
|
|
10
|
+
}
|
|
@@ -19,12 +19,13 @@ let debugEnabled = false;
|
|
|
19
19
|
export function setupWorker(init) {
|
|
20
20
|
// Mock of the window object for the worker context to be able to use the PluginController.
|
|
21
21
|
const mockWindow = {
|
|
22
|
+
isWorker: true,
|
|
22
23
|
location: { search: '?secret=123' },
|
|
23
24
|
parent: {
|
|
24
25
|
postMessage: (message) => {
|
|
25
26
|
message.event.sender = "worker." + message.event.sender;
|
|
26
27
|
checkDebugMode(message.event);
|
|
27
|
-
logIfDebug('
|
|
28
|
+
logIfDebug('sending event to Rimori', message.event);
|
|
28
29
|
self.postMessage(message);
|
|
29
30
|
}
|
|
30
31
|
},
|
|
@@ -42,16 +43,16 @@ export function setupWorker(init) {
|
|
|
42
43
|
// Handle init message from Rimori.
|
|
43
44
|
self.onmessage = (response) => __awaiter(this, void 0, void 0, function* () {
|
|
44
45
|
checkDebugMode(response.data);
|
|
45
|
-
logIfDebug('
|
|
46
|
+
logIfDebug('Message received', response.data);
|
|
46
47
|
const event = response.data;
|
|
47
48
|
if (event.topic === 'global.worker.requestInit') {
|
|
48
49
|
if (!controller) {
|
|
49
50
|
mockWindow.APP_CONFIG.SUPABASE_URL = event.data.supabaseUrl;
|
|
50
51
|
mockWindow.APP_CONFIG.SUPABASE_ANON_KEY = event.data.supabaseAnonKey;
|
|
51
52
|
controller = yield PluginController.getInstance(event.data.pluginId);
|
|
52
|
-
logIfDebug('
|
|
53
|
+
logIfDebug('Worker initialized.');
|
|
53
54
|
yield init(controller);
|
|
54
|
-
logIfDebug('
|
|
55
|
+
logIfDebug('Plugin listeners initialized.');
|
|
55
56
|
}
|
|
56
57
|
const initEvent = {
|
|
57
58
|
timestamp: new Date().toISOString(),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rimori/client",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"exports": {
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
},
|
|
20
20
|
"scripts": {
|
|
21
21
|
"build": "tsc && sass src/style.scss:dist/style.css",
|
|
22
|
-
"dev": "tsc -w",
|
|
22
|
+
"dev": "tsc -w --preserveWatchOutput",
|
|
23
23
|
"css-dev": "sass --watch src/style.scss:dist/style.css"
|
|
24
24
|
},
|
|
25
25
|
"peerDependencies": {
|
|
@@ -30,8 +30,8 @@
|
|
|
30
30
|
"@supabase/supabase-js": "^2.48.1",
|
|
31
31
|
"@tiptap/react": "2.10.3",
|
|
32
32
|
"@tiptap/starter-kit": "2.10.3",
|
|
33
|
-
"uuid": "11.1.0",
|
|
34
33
|
"react-icons": "^5.4.0",
|
|
34
|
+
"react-markdown": "^10.1.0",
|
|
35
35
|
"tiptap-markdown": "^0.8.10",
|
|
36
36
|
"typescript": "^5.7.2"
|
|
37
37
|
},
|
|
@@ -11,101 +11,103 @@ import { FaBold, FaCode, FaItalic, FaParagraph, FaStrikethrough } from "react-ic
|
|
|
11
11
|
// This inplementation is rooted in the Tiptap editor basic example https://codesandbox.io/p/devbox/editor-9x9dkd
|
|
12
12
|
|
|
13
13
|
interface EditorButtonProps {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
action: string;
|
|
15
|
+
isActive?: boolean;
|
|
16
|
+
label: string | React.ReactNode;
|
|
17
|
+
disabled?: boolean;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
const EditorButton = ({ action, isActive, label, disabled }: EditorButtonProps) => {
|
|
21
|
-
|
|
21
|
+
const { editor } = useCurrentEditor() as any;
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
if (action.includes("heading")) {
|
|
28
|
-
const level = parseInt(action[action.length - 1]);
|
|
29
|
-
return (
|
|
30
|
-
<button
|
|
31
|
-
onClick={() => editor.chain().focus().toggleHeading({ level: level }).run()}
|
|
32
|
-
className={`pl-2 ${isActive ? "is-active" : ""}`}
|
|
33
|
-
>
|
|
34
|
-
{label}
|
|
35
|
-
</button>
|
|
36
|
-
);
|
|
37
|
-
}
|
|
23
|
+
if (!editor) {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
38
26
|
|
|
27
|
+
if (action.includes("heading")) {
|
|
28
|
+
const level = parseInt(action[action.length - 1]);
|
|
39
29
|
return (
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
</button>
|
|
30
|
+
<button
|
|
31
|
+
onClick={() => editor.chain().focus().toggleHeading({ level: level }).run()}
|
|
32
|
+
className={`pl-2 ${isActive ? "is-active" : ""}`}
|
|
33
|
+
>
|
|
34
|
+
{label}
|
|
35
|
+
</button>
|
|
47
36
|
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
<button
|
|
41
|
+
onClick={() => editor.chain().focus()[action]().run()}
|
|
42
|
+
disabled={disabled ? !editor.can().chain().focus()[action]().run() : false}
|
|
43
|
+
className={`pl-2 ${isActive ? "is-active" : ""}`}
|
|
44
|
+
>
|
|
45
|
+
{label}
|
|
46
|
+
</button>
|
|
47
|
+
);
|
|
48
48
|
};
|
|
49
49
|
|
|
50
50
|
const MenuBar = () => {
|
|
51
|
-
|
|
51
|
+
const { editor } = useCurrentEditor();
|
|
52
52
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
53
|
+
if (!editor) {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
56
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
57
|
+
return (
|
|
58
|
+
<div className="bg-gray-400 dark:bg-gray-800 dark:text-white text-lg flex flex-row flex-wrap items-center p-1">
|
|
59
|
+
<EditorButton action="toggleBold" isActive={editor.isActive("bold")} label={<FaBold />} disabled />
|
|
60
|
+
<EditorButton action="toggleItalic" isActive={editor.isActive("italic")} label={<FaItalic />} disabled />
|
|
61
|
+
<EditorButton action="toggleStrike" isActive={editor.isActive("strike")} label={<FaStrikethrough />} disabled />
|
|
62
|
+
<EditorButton action="toggleCode" isActive={editor.isActive("code")} label={<FaCode />} disabled />
|
|
63
|
+
<EditorButton action="setParagraph" isActive={editor.isActive("paragraph")} label={<FaParagraph />} />
|
|
64
|
+
<EditorButton action='setHeading1' isActive={editor.isActive("heading", { level: 1 })} label={<LuHeading1 size={"24px"} />} />
|
|
65
|
+
<EditorButton action='setHeading2' isActive={editor.isActive("heading", { level: 2 })} label={<LuHeading2 size={"24px"} />} />
|
|
66
|
+
<EditorButton action='setHeading3' isActive={editor.isActive("heading", { level: 3 })} label={<LuHeading3 size={"24px"} />} />
|
|
67
|
+
<EditorButton action="toggleBulletList" isActive={editor.isActive("bulletList")} label={<AiOutlineUnorderedList size={"24px"} />} />
|
|
68
|
+
<EditorButton action="toggleOrderedList" isActive={editor.isActive("orderedList")} label={<GoListOrdered size={"24px"} />} />
|
|
69
|
+
<EditorButton action="toggleCodeBlock" isActive={editor.isActive("codeBlock")} label={<PiCodeBlock size={"24px"} />} />
|
|
70
|
+
<EditorButton action="toggleBlockquote" isActive={editor.isActive("blockquote")} label={<TbBlockquote size={"24px"} />} />
|
|
71
|
+
</div>
|
|
72
|
+
);
|
|
73
73
|
};
|
|
74
74
|
|
|
75
75
|
const extensions = [
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
76
|
+
StarterKit.configure({
|
|
77
|
+
bulletList: {
|
|
78
|
+
HTMLAttributes: {
|
|
79
|
+
class: "list-disc list-inside dark:text-white p-1 mt-1 [&_li]:mb-1 [&_p]:inline m-0",
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
orderedList: {
|
|
83
|
+
HTMLAttributes: {
|
|
84
|
+
className: "list-decimal list-inside dark:text-white p-1 mt-1 [&_li]:mb-1 [&_p]:inline m-0",
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
}),
|
|
88
|
+
Markdown,
|
|
87
89
|
];
|
|
88
90
|
|
|
89
91
|
interface Props {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
92
|
+
content?: string;
|
|
93
|
+
editable: boolean;
|
|
94
|
+
className?: string;
|
|
95
|
+
onUpdate?: (content: string) => void;
|
|
94
96
|
}
|
|
95
97
|
|
|
96
98
|
export const MarkdownEditor = (props: Props) => {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
99
|
+
return (
|
|
100
|
+
<div className={"text-md border border-gray-800 overflow-hidden " + props.className} style={{ borderWidth: props.editable ? 1 : 0 }}>
|
|
101
|
+
<EditorProvider
|
|
102
|
+
key={(props.editable ? "editable" : "readonly") + props.content}
|
|
103
|
+
slotBefore={props.editable ? <MenuBar /> : null}
|
|
104
|
+
extensions={extensions}
|
|
105
|
+
content={props.content}
|
|
106
|
+
editable={props.editable}
|
|
107
|
+
onUpdate={(e) => {
|
|
108
|
+
props.onUpdate && props.onUpdate(e.editor.storage.markdown.getMarkdown());
|
|
109
|
+
}}
|
|
110
|
+
></EditorProvider>
|
|
111
|
+
</div>
|
|
112
|
+
);
|
|
111
113
|
};
|
|
@@ -23,7 +23,7 @@ export function AssistantChat({ avatarImageUrl, voiceId, onComplete, autoStartCo
|
|
|
23
23
|
const lastAssistantMessage = [...messages].filter((m) => m.role === 'assistant').pop()?.content;
|
|
24
24
|
|
|
25
25
|
useEffect(() => {
|
|
26
|
-
sender.setOnLoudnessChange((value: number) => event.emit('self.avatar.triggerLoudness', value));
|
|
26
|
+
sender.setOnLoudnessChange((value: number) => event.emit('self.avatar.triggerLoudness', { loudness: value }));
|
|
27
27
|
|
|
28
28
|
if (!autoStartConversation) {
|
|
29
29
|
return;
|