@rimori/client 1.4.0 → 1.4.3
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 +77 -71
- package/dist/cli/scripts/init/dev-registration.d.ts +1 -1
- package/dist/cli/scripts/init/dev-registration.js +4 -4
- package/dist/cli/scripts/init/main.js +1 -1
- package/dist/cli/scripts/init/package-setup.d.ts +1 -1
- package/dist/cli/scripts/init/package-setup.js +3 -3
- package/dist/cli/scripts/init/router-transformer.js +19 -12
- package/dist/cli/scripts/init/vite-config.d.ts +2 -2
- package/dist/cli/scripts/init/vite-config.js +2 -2
- package/dist/cli/scripts/release/release-config-upload.js +9 -9
- package/dist/cli/scripts/release/release-db-update.d.ts +1 -1
- package/dist/cli/scripts/release/release-db-update.js +9 -9
- package/dist/cli/scripts/release/release-file-upload.js +1 -1
- package/dist/cli/scripts/release/release.js +2 -2
- package/dist/components/CRUDModal.d.ts +1 -1
- package/dist/components/CRUDModal.js +3 -3
- package/dist/components/MarkdownEditor.js +16 -16
- package/dist/components/Spinner.js +2 -2
- package/dist/components/ai/Assistant.js +7 -8
- package/dist/components/ai/Avatar.d.ts +2 -2
- package/dist/components/ai/Avatar.js +10 -5
- package/dist/components/ai/EmbeddedAssistent/AudioInputField.js +5 -6
- package/dist/components/ai/EmbeddedAssistent/CircleAudioAvatar.d.ts +1 -1
- package/dist/components/ai/EmbeddedAssistent/CircleAudioAvatar.js +1 -2
- package/dist/components/ai/EmbeddedAssistent/TTS/MessageSender.d.ts +1 -2
- package/dist/components/ai/EmbeddedAssistent/TTS/MessageSender.js +4 -2
- package/dist/components/ai/EmbeddedAssistent/VoiceRecoder.js +2 -3
- package/dist/components/audio/Playbutton.js +10 -7
- package/dist/components/components/ContextMenu.d.ts +1 -1
- package/dist/components/components/ContextMenu.js +19 -16
- package/dist/components.d.ts +10 -10
- package/dist/components.js +10 -10
- package/dist/core/controller/AIController.d.ts +2 -2
- package/dist/core/controller/AIController.js +12 -12
- package/dist/core/controller/ExerciseController.d.ts +2 -2
- package/dist/core/controller/ExerciseController.js +2 -2
- package/dist/core/controller/ObjectController.js +5 -5
- package/dist/core/controller/SettingsController.d.ts +22 -7
- package/dist/core/controller/SettingsController.js +73 -8
- package/dist/core/controller/SharedContentController.d.ts +3 -3
- package/dist/core/controller/SharedContentController.js +38 -20
- package/dist/core/controller/VoiceController.js +6 -4
- package/dist/core/core.d.ts +15 -15
- package/dist/core/core.js +7 -7
- package/dist/fromRimori/EventBus.js +23 -23
- package/dist/fromRimori/PluginTypes.d.ts +4 -4
- package/dist/hooks/UseChatHook.d.ts +3 -3
- package/dist/hooks/UseChatHook.js +9 -3
- package/dist/index.d.ts +10 -10
- package/dist/index.js +9 -9
- package/dist/plugin/AccomplishmentHandler.d.ts +5 -5
- package/dist/plugin/AccomplishmentHandler.js +31 -27
- package/dist/plugin/AudioController.d.ts +1 -1
- package/dist/plugin/AudioController.js +6 -6
- package/dist/plugin/Logger.js +15 -13
- package/dist/plugin/PluginController.d.ts +7 -1
- package/dist/plugin/PluginController.js +32 -27
- package/dist/plugin/RimoriClient.d.ts +17 -18
- package/dist/plugin/RimoriClient.js +31 -31
- package/dist/plugin/StandaloneClient.d.ts +1 -1
- package/dist/plugin/StandaloneClient.js +35 -16
- package/dist/plugin/ThemeSetter.js +4 -4
- package/dist/providers/PluginProvider.js +44 -14
- package/dist/utils/Language.js +57 -57
- package/dist/utils/PluginUtils.js +3 -3
- package/dist/utils/difficultyConverter.d.ts +1 -1
- package/dist/utils/difficultyConverter.js +1 -1
- package/dist/utils/endpoint.js +2 -2
- package/dist/worker/WorkerSetup.d.ts +1 -1
- package/dist/worker/WorkerSetup.js +6 -6
- package/example/docs/devdocs.md +50 -40
- package/example/docs/overview.md +1 -1
- package/example/docs/userdocs.md +4 -1
- package/example/rimori.config.ts +51 -49
- package/example/worker/vite.config.ts +3 -3
- package/example/worker/worker.ts +2 -2
- package/package.json +14 -8
- package/prettier.config.js +1 -1
- package/src/cli/scripts/init/dev-registration.ts +5 -8
- package/src/cli/scripts/init/env-setup.ts +1 -1
- package/src/cli/scripts/init/file-operations.ts +1 -1
- package/src/cli/scripts/init/html-cleaner.ts +2 -5
- package/src/cli/scripts/init/main.ts +16 -13
- package/src/cli/scripts/init/package-setup.ts +11 -15
- package/src/cli/scripts/init/router-transformer.ts +40 -37
- package/src/cli/scripts/init/tailwind-config.ts +17 -26
- package/src/cli/scripts/init/vite-config.ts +3 -3
- package/src/cli/scripts/release/release-config-upload.ts +11 -11
- package/src/cli/scripts/release/release-db-update.ts +12 -12
- package/src/cli/scripts/release/release-file-upload.ts +2 -2
- package/src/cli/scripts/release/release.ts +4 -4
- package/src/cli/types/DatabaseTypes.ts +2 -10
- package/src/components/CRUDModal.tsx +64 -48
- package/src/components/MarkdownEditor.tsx +58 -27
- package/src/components/Spinner.tsx +24 -17
- package/src/components/ai/Assistant.tsx +70 -70
- package/src/components/ai/Avatar.tsx +17 -14
- package/src/components/ai/EmbeddedAssistent/AudioInputField.tsx +63 -54
- package/src/components/ai/EmbeddedAssistent/CircleAudioAvatar.tsx +14 -5
- package/src/components/ai/EmbeddedAssistent/TTS/MessageSender.ts +75 -74
- package/src/components/ai/EmbeddedAssistent/TTS/Player.ts +3 -4
- package/src/components/ai/EmbeddedAssistent/VoiceRecoder.tsx +109 -94
- package/src/components/ai/utils.ts +4 -4
- package/src/components/audio/Playbutton.tsx +101 -93
- package/src/components/components/ContextMenu.tsx +47 -35
- package/src/components.ts +10 -10
- package/src/core/controller/AIController.ts +29 -19
- package/src/core/controller/ExerciseController.ts +16 -23
- package/src/core/controller/ObjectController.ts +15 -10
- package/src/core/controller/SettingsController.ts +89 -16
- package/src/core/controller/SharedContentController.ts +80 -44
- package/src/core/controller/VoiceController.ts +10 -8
- package/src/core/core.ts +15 -16
- package/src/fromRimori/EventBus.ts +76 -47
- package/src/fromRimori/PluginTypes.ts +26 -17
- package/src/fromRimori/readme.md +2 -2
- package/src/hooks/UseChatHook.ts +25 -15
- package/src/index.ts +10 -10
- package/src/plugin/AccomplishmentHandler.ts +53 -35
- package/src/plugin/AudioController.ts +18 -12
- package/src/plugin/Logger.ts +28 -21
- package/src/plugin/PluginController.ts +60 -44
- package/src/plugin/RimoriClient.ts +102 -72
- package/src/plugin/StandaloneClient.ts +51 -24
- package/src/plugin/ThemeSetter.ts +5 -5
- package/src/providers/PluginProvider.tsx +90 -36
- package/src/style.scss +3 -3
- package/src/utils/Language.ts +58 -58
- package/src/utils/PluginUtils.ts +16 -20
- package/src/utils/difficultyConverter.ts +2 -2
- package/src/utils/endpoint.ts +3 -2
- package/src/worker/WorkerSetup.ts +8 -9
- package/tsconfig.json +2 -4
|
@@ -58,14 +58,14 @@ function detectRouteComponents(content) {
|
|
|
58
58
|
componentNames.add(componentName);
|
|
59
59
|
}
|
|
60
60
|
// For each component, find its corresponding import statement
|
|
61
|
-
componentNames.forEach(componentName => {
|
|
61
|
+
componentNames.forEach((componentName) => {
|
|
62
62
|
const importInfo = findImportForComponent(content, componentName);
|
|
63
63
|
if (importInfo) {
|
|
64
64
|
routeComponents.push({
|
|
65
65
|
componentName,
|
|
66
66
|
importStatement: importInfo.importStatement,
|
|
67
67
|
importPath: importInfo.importPath,
|
|
68
|
-
isDefaultImport: importInfo.isDefaultImport
|
|
68
|
+
isDefaultImport: importInfo.isDefaultImport,
|
|
69
69
|
});
|
|
70
70
|
}
|
|
71
71
|
});
|
|
@@ -86,19 +86,19 @@ function findImportForComponent(content, componentName) {
|
|
|
86
86
|
return {
|
|
87
87
|
importStatement: defaultMatch[0],
|
|
88
88
|
importPath: defaultMatch[1],
|
|
89
|
-
isDefaultImport: true
|
|
89
|
+
isDefaultImport: true,
|
|
90
90
|
};
|
|
91
91
|
}
|
|
92
92
|
// Check for named import: import { ComponentName } from "path"
|
|
93
93
|
const namedImportRegex = /import\s*\{\s*([^}]*)\s*\}\s*from\s*["']([^"']+)["'];?/g;
|
|
94
94
|
let namedMatch;
|
|
95
95
|
while ((namedMatch = namedImportRegex.exec(content)) !== null) {
|
|
96
|
-
const imports = namedMatch[1].split(',').map(imp => imp.trim());
|
|
96
|
+
const imports = namedMatch[1].split(',').map((imp) => imp.trim());
|
|
97
97
|
if (imports.includes(componentName)) {
|
|
98
98
|
return {
|
|
99
99
|
importStatement: namedMatch[0],
|
|
100
100
|
importPath: namedMatch[2],
|
|
101
|
-
isDefaultImport: false
|
|
101
|
+
isDefaultImport: false,
|
|
102
102
|
};
|
|
103
103
|
}
|
|
104
104
|
}
|
|
@@ -142,16 +142,17 @@ function addLazyImport(content) {
|
|
|
142
142
|
if (reactImportMatch) {
|
|
143
143
|
// React import exists, add lazy to it
|
|
144
144
|
const existingImports = reactImportMatch[1] || '';
|
|
145
|
-
const importList = existingImports
|
|
145
|
+
const importList = existingImports
|
|
146
|
+
.split(',')
|
|
147
|
+
.map((imp) => imp.trim())
|
|
148
|
+
.filter(Boolean);
|
|
146
149
|
if (!importList.includes('lazy')) {
|
|
147
150
|
importList.push('lazy');
|
|
148
151
|
}
|
|
149
152
|
if (!importList.includes('Suspense')) {
|
|
150
153
|
importList.push('Suspense');
|
|
151
154
|
}
|
|
152
|
-
const newImport = importList.length > 0
|
|
153
|
-
? `import React, { ${importList.join(', ')} } from "react";`
|
|
154
|
-
: `import React from "react";`;
|
|
155
|
+
const newImport = importList.length > 0 ? `import React, { ${importList.join(', ')} } from "react";` : `import React from "react";`;
|
|
155
156
|
return content.replace(reactImportMatch[0], newImport);
|
|
156
157
|
}
|
|
157
158
|
else {
|
|
@@ -221,7 +222,10 @@ function transformImports(content) {
|
|
|
221
222
|
else {
|
|
222
223
|
// Update existing @rimori/client import to include PluginProvider
|
|
223
224
|
content = content.replace(/import\s*{\s*([^}]*)\s*}\s*from\s*["']@rimori\/client["'];?/, (match, imports) => {
|
|
224
|
-
const importList = imports
|
|
225
|
+
const importList = imports
|
|
226
|
+
.split(',')
|
|
227
|
+
.map((imp) => imp.trim())
|
|
228
|
+
.filter(Boolean);
|
|
225
229
|
if (!importList.includes('PluginProvider')) {
|
|
226
230
|
importList.push('PluginProvider');
|
|
227
231
|
}
|
|
@@ -230,8 +234,11 @@ function transformImports(content) {
|
|
|
230
234
|
}
|
|
231
235
|
// Transform react-router-dom import: replace BrowserRouter with HashRouter
|
|
232
236
|
content = content.replace(/import\s*{\s*([^}]*)\s*}\s*from\s*["']react-router-dom["'];?/, (match, imports) => {
|
|
233
|
-
const importList = imports
|
|
234
|
-
|
|
237
|
+
const importList = imports
|
|
238
|
+
.split(',')
|
|
239
|
+
.map((imp) => imp.trim())
|
|
240
|
+
.filter(Boolean);
|
|
241
|
+
const updatedImports = importList.map((imp) => (imp === 'BrowserRouter' ? 'HashRouter' : imp));
|
|
235
242
|
return `import { ${updatedImports.join(', ')} } from "react-router-dom";`;
|
|
236
243
|
});
|
|
237
244
|
return content;
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* @param param.configPath - Path to the vite.config.ts file (defaults to './vite.config.ts')
|
|
6
6
|
* @throws {Error} if vite.config.ts file is not found or cannot be modified.
|
|
7
7
|
*/
|
|
8
|
-
export declare function updateViteConfigBase({ basePath, configPath }?: {
|
|
8
|
+
export declare function updateViteConfigBase({ basePath, configPath, }?: {
|
|
9
9
|
basePath?: string;
|
|
10
10
|
configPath?: string;
|
|
11
11
|
}): void;
|
|
@@ -15,6 +15,6 @@ export declare function updateViteConfigBase({ basePath, configPath }?: {
|
|
|
15
15
|
* @param param.configPath - Path to the vite.config.ts file (defaults to './vite.config.ts')
|
|
16
16
|
* @returns The current base value or null if not found.
|
|
17
17
|
*/
|
|
18
|
-
export declare function getCurrentViteBase({ configPath }?: {
|
|
18
|
+
export declare function getCurrentViteBase({ configPath, }?: {
|
|
19
19
|
configPath?: string;
|
|
20
20
|
}): string | null;
|
|
@@ -7,7 +7,7 @@ import * as path from 'path';
|
|
|
7
7
|
* @param param.configPath - Path to the vite.config.ts file (defaults to './vite.config.ts')
|
|
8
8
|
* @throws {Error} if vite.config.ts file is not found or cannot be modified.
|
|
9
9
|
*/
|
|
10
|
-
export function updateViteConfigBase({ basePath = './', configPath = './vite.config.ts' } = {}) {
|
|
10
|
+
export function updateViteConfigBase({ basePath = './', configPath = './vite.config.ts', } = {}) {
|
|
11
11
|
const viteConfigPath = path.resolve(configPath);
|
|
12
12
|
if (!fs.existsSync(viteConfigPath)) {
|
|
13
13
|
throw new Error(`vite.config.ts not found at ${viteConfigPath}`);
|
|
@@ -43,7 +43,7 @@ export function updateViteConfigBase({ basePath = './', configPath = './vite.con
|
|
|
43
43
|
* @param param.configPath - Path to the vite.config.ts file (defaults to './vite.config.ts')
|
|
44
44
|
* @returns The current base value or null if not found.
|
|
45
45
|
*/
|
|
46
|
-
export function getCurrentViteBase({ configPath = './vite.config.ts' } = {}) {
|
|
46
|
+
export function getCurrentViteBase({ configPath = './vite.config.ts', } = {}) {
|
|
47
47
|
const viteConfigPath = path.resolve(configPath);
|
|
48
48
|
if (!fs.existsSync(viteConfigPath)) {
|
|
49
49
|
return null;
|
|
@@ -31,7 +31,7 @@ export function sendConfiguration(config) {
|
|
|
31
31
|
// Transpile TypeScript to JavaScript
|
|
32
32
|
const result = ts.transpile(configContent, {
|
|
33
33
|
target: ts.ScriptTarget.ES2020,
|
|
34
|
-
module: ts.ModuleKind.ES2020
|
|
34
|
+
module: ts.ModuleKind.ES2020,
|
|
35
35
|
});
|
|
36
36
|
// Create a temporary file to import the transpiled code
|
|
37
37
|
const tempFile = path.join(process.cwd(), 'temp_config.js');
|
|
@@ -67,7 +67,7 @@ export function sendConfiguration(config) {
|
|
|
67
67
|
method: 'POST',
|
|
68
68
|
headers: {
|
|
69
69
|
'Content-Type': 'application/json',
|
|
70
|
-
|
|
70
|
+
Authorization: `Bearer ${config.token}`,
|
|
71
71
|
},
|
|
72
72
|
body: JSON.stringify(requestBody),
|
|
73
73
|
});
|
|
@@ -87,8 +87,8 @@ export function sendConfiguration(config) {
|
|
|
87
87
|
}
|
|
88
88
|
}
|
|
89
89
|
catch (e) {
|
|
90
|
-
console.log(
|
|
91
|
-
throw new Error(
|
|
90
|
+
console.log('error', e);
|
|
91
|
+
throw new Error('Error sending configuration');
|
|
92
92
|
}
|
|
93
93
|
}
|
|
94
94
|
catch (error) {
|
|
@@ -103,14 +103,14 @@ export function releasePlugin(config, release_id) {
|
|
|
103
103
|
method: 'POST',
|
|
104
104
|
headers: {
|
|
105
105
|
'Content-Type': 'application/json',
|
|
106
|
-
|
|
106
|
+
Authorization: `Bearer ${config.token}`,
|
|
107
107
|
},
|
|
108
|
-
body: JSON.stringify({ plugin_id: config.plugin_id })
|
|
108
|
+
body: JSON.stringify({ plugin_id: config.plugin_id }),
|
|
109
109
|
});
|
|
110
110
|
if (!response.ok) {
|
|
111
|
-
console.log(
|
|
112
|
-
throw new Error(
|
|
111
|
+
console.log('Response:', yield response.text());
|
|
112
|
+
throw new Error('Failed to release plugin');
|
|
113
113
|
}
|
|
114
|
-
console.log(
|
|
114
|
+
console.log('✅ Plugin released successfully');
|
|
115
115
|
});
|
|
116
116
|
}
|
|
@@ -32,7 +32,7 @@ export default function dbUpdate(config, release_id) {
|
|
|
32
32
|
// Transpile TypeScript to JavaScript
|
|
33
33
|
const result = ts.transpile(dbConfigContent, {
|
|
34
34
|
target: ts.ScriptTarget.ES2020,
|
|
35
|
-
module: ts.ModuleKind.ES2020
|
|
35
|
+
module: ts.ModuleKind.ES2020,
|
|
36
36
|
});
|
|
37
37
|
// Create a temporary file to import the transpiled code
|
|
38
38
|
const tempFile = path.join(process.cwd(), 'temp_db_config.js');
|
|
@@ -66,30 +66,30 @@ export default function dbUpdate(config, release_id) {
|
|
|
66
66
|
method: 'POST',
|
|
67
67
|
headers: {
|
|
68
68
|
'Content-Type': 'application/json',
|
|
69
|
-
|
|
69
|
+
Authorization: `Bearer ${config.token}`,
|
|
70
70
|
},
|
|
71
71
|
body: JSON.stringify(requestBody),
|
|
72
72
|
}).catch((e) => {
|
|
73
|
-
console.log(
|
|
74
|
-
throw new Error(
|
|
73
|
+
console.log('error', e);
|
|
74
|
+
throw new Error('Error sending database configuration');
|
|
75
75
|
});
|
|
76
76
|
try {
|
|
77
77
|
const responseText = yield response.text().catch((e) => {
|
|
78
|
-
console.log(
|
|
79
|
-
throw new Error(
|
|
78
|
+
console.log('error', e);
|
|
79
|
+
throw new Error('Error sending database configuration');
|
|
80
80
|
});
|
|
81
81
|
const responseData = JSON.parse(responseText);
|
|
82
82
|
if (response.ok) {
|
|
83
83
|
console.log('✅ Database configuration deployed successfully!');
|
|
84
84
|
}
|
|
85
85
|
else {
|
|
86
|
-
console.log(
|
|
86
|
+
console.log('responseData', responseData);
|
|
87
87
|
throw new Error(responseData.message);
|
|
88
88
|
}
|
|
89
89
|
}
|
|
90
90
|
catch (e) {
|
|
91
|
-
console.log(
|
|
92
|
-
throw new Error(
|
|
91
|
+
console.log('error', e);
|
|
92
|
+
throw new Error('Error sending database configuration');
|
|
93
93
|
}
|
|
94
94
|
}
|
|
95
95
|
catch (error) {
|
|
@@ -65,7 +65,7 @@ export function uploadDirectory(config, release_id) {
|
|
|
65
65
|
// Upload to the release endpoint
|
|
66
66
|
const response = yield fetch(`${config.domain}/release/${release_id}/files`, {
|
|
67
67
|
method: 'POST',
|
|
68
|
-
headers: {
|
|
68
|
+
headers: { Authorization: `Bearer ${config.token}` },
|
|
69
69
|
body: formData,
|
|
70
70
|
});
|
|
71
71
|
if (response.ok) {
|
|
@@ -43,7 +43,7 @@ const config = {
|
|
|
43
43
|
release_channel: releaseChannel,
|
|
44
44
|
plugin_id: pluginId,
|
|
45
45
|
token: RIMORI_TOKEN,
|
|
46
|
-
domain: process.env.RIMORI_BACKEND_URL ||
|
|
46
|
+
domain: process.env.RIMORI_BACKEND_URL || 'https://api.rimori.se',
|
|
47
47
|
rimori_client_version: packageJson.dependencies['@rimori/client'].replace('^', ''),
|
|
48
48
|
};
|
|
49
49
|
/**
|
|
@@ -62,7 +62,7 @@ function releaseProcess() {
|
|
|
62
62
|
yield releasePlugin(config, release_id);
|
|
63
63
|
}
|
|
64
64
|
catch (error) {
|
|
65
|
-
console.log(
|
|
65
|
+
console.log('❌ Error:', error.message);
|
|
66
66
|
process.exit(1);
|
|
67
67
|
}
|
|
68
68
|
});
|
|
@@ -13,5 +13,5 @@ interface ActionButton {
|
|
|
13
13
|
onClick: () => void;
|
|
14
14
|
closeModal?: boolean;
|
|
15
15
|
}
|
|
16
|
-
export declare function CRUDModal({ actionbuttons, children, title, buttonText, className, closeAble, show, onClose }: Props): import("react/jsx-runtime").JSX.Element;
|
|
16
|
+
export declare function CRUDModal({ actionbuttons, children, title, buttonText, className, closeAble, show, onClose, }: Props): import("react/jsx-runtime").JSX.Element;
|
|
17
17
|
export {};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import { useEffect, useRef } from
|
|
3
|
-
export function CRUDModal({ actionbuttons, children, title, buttonText, className, closeAble = true, show = false, onClose }) {
|
|
2
|
+
import { useEffect, useRef } from 'react';
|
|
3
|
+
export function CRUDModal({ actionbuttons, children, title, buttonText, className, closeAble = true, show = false, onClose, }) {
|
|
4
4
|
const dialogRef = useRef(null);
|
|
5
5
|
useEffect(() => {
|
|
6
6
|
var _a, _b;
|
|
@@ -16,7 +16,7 @@ export function CRUDModal({ actionbuttons, children, title, buttonText, classNam
|
|
|
16
16
|
(_a = dialogRef.current) === null || _a === void 0 ? void 0 : _a.close();
|
|
17
17
|
onClose === null || onClose === void 0 ? void 0 : onClose();
|
|
18
18
|
};
|
|
19
|
-
return (_jsxs(_Fragment, { children: [!!buttonText && _jsx("button", { className: className, onClick: () => { var _a; return (_a = dialogRef.current) === null || _a === void 0 ? void 0 : _a.showModal(); }, children: buttonText }), _jsxs("dialog", { ref: dialogRef, className: "bg-gray-400 rounded-lg font-normal", onClose: handleClose, children: [_jsxs("div", { className: "bg-gray-500 text-xl flex flex-row justify-between p-3 items-start font-bold", children: [_jsx("h2", { children: title }), closeAble && _jsx("button", { onClick: handleClose, children: "\u00D7" })] }), _jsx("div", { className: "modal-body p-2", children: children }), _jsx("div", { className: "modal-footer px-2 py-2 flex flex-row gap-2 border-t-2", children: actionbuttons.map(({ onClick, text, closeModal = true }, index) => (_jsx("button", { className: "bg-blue-500 hover:bg-blue-600 dark:border-gray-900 rounded-md py-2 px-4 dark:text-white font-bold", onClick: () => {
|
|
19
|
+
return (_jsxs(_Fragment, { children: [!!buttonText && (_jsx("button", { className: className, onClick: () => { var _a; return (_a = dialogRef.current) === null || _a === void 0 ? void 0 : _a.showModal(); }, children: buttonText })), _jsxs("dialog", { ref: dialogRef, className: "bg-gray-400 rounded-lg font-normal", onClose: handleClose, children: [_jsxs("div", { className: "bg-gray-500 text-xl flex flex-row justify-between p-3 items-start font-bold", children: [_jsx("h2", { children: title }), closeAble && _jsx("button", { onClick: handleClose, children: "\u00D7" })] }), _jsx("div", { className: "modal-body p-2", children: children }), _jsx("div", { className: "modal-footer px-2 py-2 flex flex-row gap-2 border-t-2", children: actionbuttons.map(({ onClick, text, closeModal = true }, index) => (_jsx("button", { className: "bg-blue-500 hover:bg-blue-600 dark:border-gray-900 rounded-md py-2 px-4 dark:text-white font-bold", onClick: () => {
|
|
20
20
|
if (closeModal)
|
|
21
21
|
handleClose();
|
|
22
22
|
onClick();
|
|
@@ -1,48 +1,48 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Markdown } from 'tiptap-markdown';
|
|
3
|
-
import StarterKit from
|
|
4
|
-
import { PiCodeBlock } from
|
|
5
|
-
import { TbBlockquote } from
|
|
6
|
-
import { GoListOrdered } from
|
|
7
|
-
import { AiOutlineUnorderedList } from
|
|
8
|
-
import { EditorProvider, useCurrentEditor } from
|
|
9
|
-
import { LuHeading1, LuHeading2, LuHeading3 } from
|
|
10
|
-
import { FaBold, FaCode, FaItalic, FaParagraph, FaStrikethrough } from
|
|
3
|
+
import StarterKit from '@tiptap/starter-kit';
|
|
4
|
+
import { PiCodeBlock } from 'react-icons/pi';
|
|
5
|
+
import { TbBlockquote } from 'react-icons/tb';
|
|
6
|
+
import { GoListOrdered } from 'react-icons/go';
|
|
7
|
+
import { AiOutlineUnorderedList } from 'react-icons/ai';
|
|
8
|
+
import { EditorProvider, useCurrentEditor } from '@tiptap/react';
|
|
9
|
+
import { LuHeading1, LuHeading2, LuHeading3 } from 'react-icons/lu';
|
|
10
|
+
import { FaBold, FaCode, FaItalic, FaParagraph, FaStrikethrough } from 'react-icons/fa';
|
|
11
11
|
const EditorButton = ({ action, isActive, label, disabled }) => {
|
|
12
12
|
const { editor } = useCurrentEditor();
|
|
13
13
|
if (!editor) {
|
|
14
14
|
return null;
|
|
15
15
|
}
|
|
16
|
-
if (action.includes(
|
|
16
|
+
if (action.includes('heading')) {
|
|
17
17
|
const level = parseInt(action[action.length - 1]);
|
|
18
|
-
return (_jsx("button", { onClick: () => editor.chain().focus().toggleHeading({ level: level }).run(), className: `pl-2 ${isActive ?
|
|
18
|
+
return (_jsx("button", { onClick: () => editor.chain().focus().toggleHeading({ level: level }).run(), className: `pl-2 ${isActive ? 'is-active' : ''}`, children: label }));
|
|
19
19
|
}
|
|
20
|
-
return (_jsx("button", { onClick: () => editor.chain().focus()[action]().run(), disabled: disabled ? !editor.can().chain().focus()[action]().run() : false, className: `pl-2 ${isActive ?
|
|
20
|
+
return (_jsx("button", { onClick: () => editor.chain().focus()[action]().run(), disabled: disabled ? !editor.can().chain().focus()[action]().run() : false, className: `pl-2 ${isActive ? 'is-active' : ''}`, children: label }));
|
|
21
21
|
};
|
|
22
22
|
const MenuBar = () => {
|
|
23
23
|
const { editor } = useCurrentEditor();
|
|
24
24
|
if (!editor) {
|
|
25
25
|
return null;
|
|
26
26
|
}
|
|
27
|
-
return (_jsxs("div", { className: "bg-gray-400 dark:bg-gray-800 dark:text-white text-lg flex flex-row flex-wrap items-center p-1", children: [_jsx(EditorButton, { action: "toggleBold", isActive: editor.isActive(
|
|
27
|
+
return (_jsxs("div", { className: "bg-gray-400 dark:bg-gray-800 dark:text-white text-lg flex flex-row flex-wrap items-center p-1", children: [_jsx(EditorButton, { action: "toggleBold", isActive: editor.isActive('bold'), label: _jsx(FaBold, {}), disabled: true }), _jsx(EditorButton, { action: "toggleItalic", isActive: editor.isActive('italic'), label: _jsx(FaItalic, {}), disabled: true }), _jsx(EditorButton, { action: "toggleStrike", isActive: editor.isActive('strike'), label: _jsx(FaStrikethrough, {}), disabled: true }), _jsx(EditorButton, { action: "toggleCode", isActive: editor.isActive('code'), label: _jsx(FaCode, {}), disabled: true }), _jsx(EditorButton, { action: "setParagraph", isActive: editor.isActive('paragraph'), label: _jsx(FaParagraph, {}) }), _jsx(EditorButton, { action: "setHeading1", isActive: editor.isActive('heading', { level: 1 }), label: _jsx(LuHeading1, { size: '24px' }) }), _jsx(EditorButton, { action: "setHeading2", isActive: editor.isActive('heading', { level: 2 }), label: _jsx(LuHeading2, { size: '24px' }) }), _jsx(EditorButton, { action: "setHeading3", isActive: editor.isActive('heading', { level: 3 }), label: _jsx(LuHeading3, { size: '24px' }) }), _jsx(EditorButton, { action: "toggleBulletList", isActive: editor.isActive('bulletList'), label: _jsx(AiOutlineUnorderedList, { size: '24px' }) }), _jsx(EditorButton, { action: "toggleOrderedList", isActive: editor.isActive('orderedList'), label: _jsx(GoListOrdered, { size: '24px' }) }), _jsx(EditorButton, { action: "toggleCodeBlock", isActive: editor.isActive('codeBlock'), label: _jsx(PiCodeBlock, { size: '24px' }) }), _jsx(EditorButton, { action: "toggleBlockquote", isActive: editor.isActive('blockquote'), label: _jsx(TbBlockquote, { size: '24px' }) })] }));
|
|
28
28
|
};
|
|
29
29
|
const extensions = [
|
|
30
30
|
StarterKit.configure({
|
|
31
31
|
bulletList: {
|
|
32
32
|
HTMLAttributes: {
|
|
33
|
-
class:
|
|
33
|
+
class: 'list-disc list-inside dark:text-white p-1 mt-1 [&_li]:mb-1 [&_p]:inline m-0',
|
|
34
34
|
},
|
|
35
35
|
},
|
|
36
36
|
orderedList: {
|
|
37
37
|
HTMLAttributes: {
|
|
38
|
-
className:
|
|
38
|
+
className: 'list-decimal list-inside dark:text-white p-1 mt-1 [&_li]:mb-1 [&_p]:inline m-0',
|
|
39
39
|
},
|
|
40
40
|
},
|
|
41
41
|
}),
|
|
42
42
|
Markdown,
|
|
43
43
|
];
|
|
44
44
|
export const MarkdownEditor = (props) => {
|
|
45
|
-
return (_jsx("div", { className:
|
|
45
|
+
return (_jsx("div", { className: 'text-md border border-gray-800 overflow-hidden ' + props.className, style: { borderWidth: props.editable ? 1 : 0 }, children: _jsx(EditorProvider, { slotBefore: props.editable ? _jsx(MenuBar, {}) : null, extensions: extensions, content: props.content, editable: props.editable, onUpdate: (e) => {
|
|
46
46
|
props.onUpdate && props.onUpdate(e.editor.storage.markdown.getMarkdown());
|
|
47
|
-
} }, (props.editable ?
|
|
47
|
+
} }, (props.editable ? 'editable' : 'readonly') + props.content) }));
|
|
48
48
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
export const Spinner = ({ text, className, size =
|
|
3
|
-
return (_jsxs("div", { className:
|
|
2
|
+
export const Spinner = ({ text, className, size = '30px' }) => {
|
|
3
|
+
return (_jsxs("div", { className: 'flex items-center space-x-2 ' + className, children: [_jsxs("svg", { style: { width: size, height: size }, className: "animate-spin -ml-1 mr-3 h-5 w-5 text-white", xmlns: "http://www.w3.org/2000/svg", fill: "none", viewBox: "0 0 24 24", children: [_jsx("circle", { className: "opacity-25", cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "4" }), _jsx("path", { className: "opacity-75", fill: "currentColor", d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" })] }), text && _jsx("span", { className: "", children: text })] }));
|
|
4
4
|
};
|
|
@@ -36,24 +36,23 @@ export function AssistantChat({ avatarImageUrl, voiceId, onComplete, autoStartCo
|
|
|
36
36
|
}, [messages, isLoading]);
|
|
37
37
|
const lastMessage = messages[messages.length - 1];
|
|
38
38
|
useEffect(() => {
|
|
39
|
-
console.log(
|
|
39
|
+
console.log('lastMessage', lastMessage);
|
|
40
40
|
const toolInvocations = lastMessage === null || lastMessage === void 0 ? void 0 : lastMessage.toolCalls;
|
|
41
41
|
if (toolInvocations && toolInvocations.length > 0) {
|
|
42
|
-
console.log(
|
|
42
|
+
console.log('toolInvocations', toolInvocations);
|
|
43
43
|
onComplete(toolInvocations[0].args);
|
|
44
44
|
}
|
|
45
45
|
}, [lastMessage]);
|
|
46
46
|
if ((lastMessage === null || lastMessage === void 0 ? void 0 : lastMessage.toolCalls) && lastMessage.toolCalls.length > 0) {
|
|
47
|
-
console.log(
|
|
47
|
+
console.log('lastMessage test2', lastMessage);
|
|
48
48
|
const args = lastMessage.toolCalls[0].args;
|
|
49
|
-
const success = args.explanationUnderstood ===
|
|
50
|
-
return _jsxs("div", { className: "px-5 pt-5 overflow-y-auto text-center", style: { height:
|
|
49
|
+
const success = args.explanationUnderstood === 'TRUE' || args.studentKnowsTopic === 'TRUE';
|
|
50
|
+
return (_jsxs("div", { className: "px-5 pt-5 overflow-y-auto text-center", style: { height: '478px' }, children: [_jsx("h1", { className: "text-center mt-5 mb-5", children: success ? 'Great job!' : 'You failed' }), _jsx("p", { children: args.improvementHints })] }));
|
|
51
51
|
}
|
|
52
|
-
return (_jsxs("div", { children: [oralCommunication && _jsx(CircleAudioAvatar, { imageUrl: avatarImageUrl, className:
|
|
52
|
+
return (_jsxs("div", { children: [oralCommunication && _jsx(CircleAudioAvatar, { imageUrl: avatarImageUrl, className: "mx-auto my-10" }), _jsx("div", { className: "w-full", children: lastAssistantMessage && (_jsx("div", { className: "px-5 pt-5 overflow-y-auto remirror-theme", style: { height: '4k78px' }, children: _jsx(Markdown, { children: lastAssistantMessage }) })) }), _jsx(AudioInputField, { blockSubmission: isLoading, onSubmit: (message) => {
|
|
53
53
|
append([{ role: 'user', content: message, id: messages.length.toString() }]);
|
|
54
|
-
}, onAudioControl: voice => {
|
|
54
|
+
}, onAudioControl: (voice) => {
|
|
55
55
|
setOralCommunication(voice);
|
|
56
56
|
sender.setVolume(voice ? 1 : 0);
|
|
57
57
|
} })] }));
|
|
58
58
|
}
|
|
59
|
-
;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Tool } from '../../fromRimori/PluginTypes';
|
|
2
2
|
import { FirstMessages } from './utils';
|
|
3
3
|
interface Props {
|
|
4
|
-
voiceId:
|
|
4
|
+
voiceId: string;
|
|
5
5
|
agentTools: Tool[];
|
|
6
6
|
avatarImageUrl: string;
|
|
7
7
|
circleSize?: string;
|
|
@@ -10,5 +10,5 @@ interface Props {
|
|
|
10
10
|
autoStartConversation?: FirstMessages;
|
|
11
11
|
className?: string;
|
|
12
12
|
}
|
|
13
|
-
export declare function Avatar({ avatarImageUrl, voiceId, agentTools, autoStartConversation, children, isDarkTheme, circleSize, className }: Props): import("react/jsx-runtime").JSX.Element;
|
|
13
|
+
export declare function Avatar({ avatarImageUrl, voiceId, agentTools, autoStartConversation, children, isDarkTheme, circleSize, className, }: Props): import("react/jsx-runtime").JSX.Element;
|
|
14
14
|
export {};
|
|
@@ -6,14 +6,14 @@ import { CircleAudioAvatar } from './EmbeddedAssistent/CircleAudioAvatar';
|
|
|
6
6
|
import { useChat } from '../../hooks/UseChatHook';
|
|
7
7
|
import { useRimori } from '../../components';
|
|
8
8
|
import { getFirstMessages } from './utils';
|
|
9
|
-
export function Avatar({ avatarImageUrl, voiceId, agentTools, autoStartConversation, children, isDarkTheme = false, circleSize =
|
|
9
|
+
export function Avatar({ avatarImageUrl, voiceId, agentTools, autoStartConversation, children, isDarkTheme = false, circleSize = '300px', className, }) {
|
|
10
10
|
const { ai, event } = useRimori();
|
|
11
11
|
const [agentReplying, setAgentReplying] = useState(false);
|
|
12
12
|
const [isProcessingMessage, setIsProcessingMessage] = useState(false);
|
|
13
13
|
const sender = useMemo(() => new MessageSender(ai.getVoice, voiceId), [voiceId]);
|
|
14
14
|
const { messages, append, isLoading, lastMessage, setMessages } = useChat(agentTools);
|
|
15
15
|
useEffect(() => {
|
|
16
|
-
console.log(
|
|
16
|
+
console.log('messages', messages);
|
|
17
17
|
}, [messages]);
|
|
18
18
|
useEffect(() => {
|
|
19
19
|
if (!isLoading)
|
|
@@ -46,9 +46,14 @@ export function Avatar({ avatarImageUrl, voiceId, agentTools, autoStartConversat
|
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
48
|
}, [lastMessage, isLoading]);
|
|
49
|
-
return (_jsxs("div", { className: `md:pb-8 ${className || ''}`, children: [_jsx(CircleAudioAvatar, { width: circleSize, className:
|
|
49
|
+
return (_jsxs("div", { className: `md:pb-8 ${className || ''}`, children: [_jsx(CircleAudioAvatar, { width: circleSize, className: "mx-auto", imageUrl: avatarImageUrl, isDarkTheme: isDarkTheme }), children, _jsx(VoiceRecorder, { iconSize: "30", className: "w-16 h-16 shadow-lg rounded-full bg-gray-400 dark:bg-gray-800", disabled: agentReplying, loading: isProcessingMessage, enablePushToTalk: true, onVoiceRecorded: (message) => {
|
|
50
50
|
setAgentReplying(true);
|
|
51
|
-
append([
|
|
51
|
+
append([
|
|
52
|
+
{
|
|
53
|
+
role: 'user',
|
|
54
|
+
content: 'Message(' + Math.floor((messages.length + 1) / 2) + '): ' + message,
|
|
55
|
+
id: messages.length.toString(),
|
|
56
|
+
},
|
|
57
|
+
]);
|
|
52
58
|
}, onRecordingStatusChange: (running) => !running && setIsProcessingMessage(true) })] }));
|
|
53
59
|
}
|
|
54
|
-
;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useState } from 'react';
|
|
3
3
|
import { VoiceRecorder } from './VoiceRecoder';
|
|
4
|
-
import { BiSolidRightArrow } from
|
|
5
|
-
import { HiMiniSpeakerXMark, HiMiniSpeakerWave } from
|
|
4
|
+
import { BiSolidRightArrow } from 'react-icons/bi';
|
|
5
|
+
import { HiMiniSpeakerXMark, HiMiniSpeakerWave } from 'react-icons/hi2';
|
|
6
6
|
export function AudioInputField({ onSubmit, onAudioControl, blockSubmission = false }) {
|
|
7
7
|
const [text, setText] = useState('');
|
|
8
8
|
const [audioEnabled, setAudioEnabled] = useState(true);
|
|
@@ -27,12 +27,11 @@ export function AudioInputField({ onSubmit, onAudioControl, blockSubmission = fa
|
|
|
27
27
|
handleSubmit();
|
|
28
28
|
}
|
|
29
29
|
};
|
|
30
|
-
return (_jsxs("div", { className: "flex items-center bg-gray-600 pt-2 pb-2 p-2", children: [onAudioControl && _jsx("button", { onClick: () => {
|
|
30
|
+
return (_jsxs("div", { className: "flex items-center bg-gray-600 pt-2 pb-2 p-2", children: [onAudioControl && (_jsx("button", { onClick: () => {
|
|
31
31
|
onAudioControl(!audioEnabled);
|
|
32
32
|
setAudioEnabled(!audioEnabled);
|
|
33
|
-
}, className: "cursor-default", children: audioEnabled ? _jsx(HiMiniSpeakerWave, { className:
|
|
33
|
+
}, className: "cursor-default", children: audioEnabled ? (_jsx(HiMiniSpeakerWave, { className: "w-9 h-9 cursor-pointer" })) : (_jsx(HiMiniSpeakerXMark, { className: "w-9 h-9 cursor-pointer" })) })), _jsx(VoiceRecorder, { onRecordingStatusChange: () => { }, onVoiceRecorded: (m) => {
|
|
34
34
|
console.log('onVoiceRecorded', m);
|
|
35
35
|
handleSubmit(m);
|
|
36
|
-
} }), _jsx("textarea", { value: text, onChange: (e) => setText(e.target.value), onKeyDown: handleKeyDown, className: "flex-1 border-none rounded-lg p-2 text-gray-800 focus::outline-none", placeholder:
|
|
36
|
+
} }), _jsx("textarea", { value: text, onChange: (e) => setText(e.target.value), onKeyDown: handleKeyDown, className: "flex-1 border-none rounded-lg p-2 text-gray-800 focus::outline-none", placeholder: "Type a message...", disabled: blockSubmission }), _jsx("button", { onClick: () => handleSubmit(), className: "cursor-default", disabled: blockSubmission, children: _jsx(BiSolidRightArrow, { className: "w-9 h-10 cursor-pointer" }) })] }));
|
|
37
37
|
}
|
|
38
|
-
;
|
|
@@ -4,5 +4,5 @@ interface CircleAudioAvatarProps {
|
|
|
4
4
|
className?: string;
|
|
5
5
|
isDarkTheme?: boolean;
|
|
6
6
|
}
|
|
7
|
-
export declare function CircleAudioAvatar({ imageUrl, className, isDarkTheme, width }: CircleAudioAvatarProps): import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
export declare function CircleAudioAvatar({ imageUrl, className, isDarkTheme, width, }: CircleAudioAvatarProps): import("react/jsx-runtime").JSX.Element;
|
|
8
8
|
export {};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect, useRef } from 'react';
|
|
3
3
|
import { EventBus } from '../../../fromRimori/EventBus';
|
|
4
|
-
export function CircleAudioAvatar({ imageUrl, className, isDarkTheme = false, width =
|
|
4
|
+
export function CircleAudioAvatar({ imageUrl, className, isDarkTheme = false, width = '150px', }) {
|
|
5
5
|
const canvasRef = useRef(null);
|
|
6
6
|
const currentLoudnessRef = useRef(0);
|
|
7
7
|
const targetLoudnessRef = useRef(0);
|
|
@@ -77,4 +77,3 @@ export function CircleAudioAvatar({ imageUrl, className, isDarkTheme = false, wi
|
|
|
77
77
|
};
|
|
78
78
|
return _jsx("canvas", { ref: canvasRef, className: className, width: 500, height: 500, style: { width } });
|
|
79
79
|
}
|
|
80
|
-
;
|
|
@@ -4,9 +4,8 @@ export declare class MessageSender {
|
|
|
4
4
|
private fetchedSentences;
|
|
5
5
|
private lastLoading;
|
|
6
6
|
private voice;
|
|
7
|
-
private model;
|
|
8
7
|
private voiceBackend;
|
|
9
|
-
constructor(voiceBackend: VoiceBackend, voice
|
|
8
|
+
constructor(voiceBackend: VoiceBackend, voice: string);
|
|
10
9
|
private getCompletedSentences;
|
|
11
10
|
handleNewText(currentText: string | undefined, isLoading: boolean): Promise<void>;
|
|
12
11
|
private generateSpeech;
|
|
@@ -9,13 +9,15 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
};
|
|
10
10
|
import { ChunkedAudioPlayer } from './Player';
|
|
11
11
|
export class MessageSender {
|
|
12
|
-
constructor(voiceBackend, voice
|
|
12
|
+
constructor(voiceBackend, voice) {
|
|
13
13
|
this.player = new ChunkedAudioPlayer();
|
|
14
14
|
this.fetchedSentences = new Set();
|
|
15
15
|
this.lastLoading = false;
|
|
16
|
+
if ((voice === null || voice === void 0 ? void 0 : voice.split('_').length) !== 2) {
|
|
17
|
+
throw new Error("Invalid voice id format '" + voice + "'. Voice id needs to look like <provider>_<voice_id>");
|
|
18
|
+
}
|
|
16
19
|
this.voiceBackend = voiceBackend;
|
|
17
20
|
this.voice = voice;
|
|
18
|
-
this.model = model;
|
|
19
21
|
}
|
|
20
22
|
getCompletedSentences(currentText, isLoading) {
|
|
21
23
|
// Split the text based on the following characters: .,?!
|
|
@@ -12,7 +12,7 @@ import { useRimori } from '../../../components';
|
|
|
12
12
|
import { FaMicrophone, FaSpinner } from 'react-icons/fa6';
|
|
13
13
|
import { AudioController } from '../../../plugin/AudioController';
|
|
14
14
|
import { useState, useRef, forwardRef, useImperativeHandle, useEffect } from 'react';
|
|
15
|
-
export const VoiceRecorder = forwardRef(({ onVoiceRecorded, iconSize, className, disabled, loading, onRecordingStatusChange, enablePushToTalk = false }, ref) => {
|
|
15
|
+
export const VoiceRecorder = forwardRef(({ onVoiceRecorded, iconSize, className, disabled, loading, onRecordingStatusChange, enablePushToTalk = false, }, ref) => {
|
|
16
16
|
const [isRecording, setIsRecording] = useState(false);
|
|
17
17
|
const [internalIsProcessing, setInternalIsProcessing] = useState(false);
|
|
18
18
|
const audioControllerRef = useRef(null);
|
|
@@ -91,6 +91,5 @@ export const VoiceRecorder = forwardRef(({ onVoiceRecorded, iconSize, className,
|
|
|
91
91
|
window.removeEventListener('keyup', handleKeyUp);
|
|
92
92
|
};
|
|
93
93
|
}, [enablePushToTalk]);
|
|
94
|
-
return (_jsx("button", { className:
|
|
95
|
-
_jsx(FaMicrophone, { size: iconSize, className: (isRecording ? "text-red-600" : "") }) }));
|
|
94
|
+
return (_jsx("button", { className: 'flex flex-row justify-center items-center rounded-full mx-auto disabled:opacity-50 ' + className, onClick: isRecording ? stopRecording : startRecording, disabled: disabled || loading || internalIsProcessing, children: loading || internalIsProcessing ? (_jsx(FaSpinner, { className: "animate-spin" })) : (_jsx(FaMicrophone, { size: iconSize, className: isRecording ? 'text-red-600' : '' })) }));
|
|
96
95
|
});
|
|
@@ -9,8 +9,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
};
|
|
10
10
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
11
11
|
import { useState, useEffect } from 'react';
|
|
12
|
-
import { FaPlayCircle, FaStopCircle } from
|
|
13
|
-
import { useRimori } from
|
|
12
|
+
import { FaPlayCircle, FaStopCircle } from 'react-icons/fa';
|
|
13
|
+
import { useRimori } from '../../providers/PluginProvider';
|
|
14
14
|
import { Spinner } from '../Spinner';
|
|
15
15
|
import { EventBus } from '../../fromRimori/EventBus';
|
|
16
16
|
export const AudioPlayOptions = [0.8, 0.9, 1.0, 1.1, 1.2, 1.5];
|
|
@@ -35,7 +35,7 @@ export const AudioPlayer = ({ text, voice, language, hide, playListenerEvent, in
|
|
|
35
35
|
// Function to generate audio from text using API
|
|
36
36
|
const generateAudio = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
37
37
|
setIsLoading(true);
|
|
38
|
-
const blob = yield ai.getVoice(text, voice || (language ?
|
|
38
|
+
const blob = yield ai.getVoice(text, voice || (language ? 'aws_default' : 'openai_alloy'), 1, language);
|
|
39
39
|
setAudioUrl(URL.createObjectURL(blob));
|
|
40
40
|
setIsLoading(false);
|
|
41
41
|
});
|
|
@@ -45,13 +45,16 @@ export const AudioPlayer = ({ text, voice, language, hide, playListenerEvent, in
|
|
|
45
45
|
return;
|
|
46
46
|
const audio = new Audio(audioUrl);
|
|
47
47
|
audio.playbackRate = speed;
|
|
48
|
-
audio
|
|
48
|
+
audio
|
|
49
|
+
.play()
|
|
50
|
+
.then(() => {
|
|
49
51
|
audio.onended = () => {
|
|
50
52
|
setIsPlaying(false);
|
|
51
53
|
isFetchingAudio = false;
|
|
52
54
|
};
|
|
53
|
-
})
|
|
54
|
-
|
|
55
|
+
})
|
|
56
|
+
.catch((e) => {
|
|
57
|
+
console.warn('Error playing audio:', e);
|
|
55
58
|
setIsPlaying(false);
|
|
56
59
|
});
|
|
57
60
|
return () => {
|
|
@@ -73,5 +76,5 @@ export const AudioPlayer = ({ text, voice, language, hide, playListenerEvent, in
|
|
|
73
76
|
// console.log("playOnMount", playOnMount);
|
|
74
77
|
togglePlayback();
|
|
75
78
|
}, [playOnMount]);
|
|
76
|
-
return (_jsx("div", { className: "group relative", children: _jsxs("div", { className:
|
|
79
|
+
return (_jsx("div", { className: "group relative", children: _jsxs("div", { className: "flex flex-row items-end", children: [!hide && (_jsx("button", { className: "text-gray-500", onClick: togglePlayback, disabled: isLoading, children: isLoading ? _jsx(Spinner, {}) : isPlaying ? _jsx(FaStopCircle, { size: '25px' }) : _jsx(FaPlayCircle, { size: '25px' }) })), enableSpeedAdjustment && (_jsxs("div", { className: "ml-1 opacity-0 group-hover:opacity-100 transition-opacity duration-300 flex flex-row text-sm text-gray-500", children: [_jsx("span", { className: "pr-1", children: "Speed: " }), _jsx("select", { value: speed, className: "appearance-none cursor-pointer pr-0 p-0 rounded shadow leading-tight focus:outline-none focus:bg-gray-800 focus:ring bg-transparent border-0", onChange: (e) => setSpeed(parseFloat(e.target.value)), disabled: isLoading, children: AudioPlayOptions.map((s) => (_jsx("option", { value: s, children: s }, s))) })] }))] }) }));
|
|
77
80
|
};
|