@rimori/client 1.3.1 → 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/.prettierignore +35 -0
- 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 +2 -2
- package/dist/cli/scripts/release/release.js +2 -2
- package/dist/cli/types/DatabaseTypes.d.ts +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 +14 -7
- 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/TTS/Player.js +1 -1
- 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 +20 -18
- package/dist/core/controller/ExerciseController.d.ts +52 -0
- package/dist/core/controller/ExerciseController.js +73 -0
- 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 -14
- 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.d.ts +5 -0
- package/dist/plugin/Logger.js +65 -13
- package/dist/plugin/PluginController.d.ts +7 -1
- package/dist/plugin/PluginController.js +32 -27
- package/dist/plugin/RimoriClient.d.ts +39 -14
- package/dist/plugin/RimoriClient.js +60 -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/eslint.config.js +53 -0
- 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 +17 -4
- package/prettier.config.js +8 -0
- 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 +3 -3
- package/src/cli/scripts/release/release.ts +4 -4
- package/src/cli/types/DatabaseTypes.ts +7 -8
- 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 +20 -16
- 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 +177 -178
- 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 +62 -50
- package/src/core/controller/ExerciseController.ts +98 -0
- 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 -15
- 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 +77 -19
- package/src/plugin/PluginController.ts +60 -44
- package/src/plugin/RimoriClient.ts +133 -69
- 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
- package/dist/components/LoggerExample.d.ts +0 -6
- package/dist/components/LoggerExample.js +0 -79
- package/dist/core/controller/AudioController.d.ts +0 -0
- package/dist/core/controller/AudioController.js +0 -1
- package/dist/hooks/UseLogger.d.ts +0 -30
- package/dist/hooks/UseLogger.js +0 -122
- package/dist/plugin/LoggerExample.d.ts +0 -16
- package/dist/plugin/LoggerExample.js +0 -140
- package/dist/utils/audioFormats.d.ts +0 -26
- package/dist/utils/audioFormats.js +0 -67
|
@@ -4,10 +4,10 @@ export type Plugin<T extends {} = {}> = Omit<RimoriPluginConfig<T>, 'context_men
|
|
|
4
4
|
endpoint: string;
|
|
5
5
|
assetEndpoint: string;
|
|
6
6
|
context_menu_actions: MenuEntry[];
|
|
7
|
-
release_channel:
|
|
8
|
-
}
|
|
7
|
+
release_channel: 'alpha' | 'beta' | 'stable';
|
|
8
|
+
};
|
|
9
9
|
|
|
10
|
-
export type ActivePlugin = Plugin<{ active?: boolean }
|
|
10
|
+
export type ActivePlugin = Plugin<{ active?: boolean }>;
|
|
11
11
|
|
|
12
12
|
// browsable page of a plugin
|
|
13
13
|
export interface PluginPage {
|
|
@@ -17,13 +17,22 @@ export interface PluginPage {
|
|
|
17
17
|
// Whether the page should be shown in the navbar
|
|
18
18
|
show: boolean;
|
|
19
19
|
description: string;
|
|
20
|
-
root:
|
|
20
|
+
root:
|
|
21
|
+
| 'vocabulary'
|
|
22
|
+
| 'grammar'
|
|
23
|
+
| 'reading'
|
|
24
|
+
| 'listening'
|
|
25
|
+
| 'watching'
|
|
26
|
+
| 'writing'
|
|
27
|
+
| 'speaking'
|
|
28
|
+
| 'other'
|
|
29
|
+
| 'community';
|
|
21
30
|
// The actions that can be triggered in the plugin
|
|
22
31
|
// The key is the action key. The other entries are additional properties needed when triggering the action
|
|
23
32
|
action?: {
|
|
24
33
|
key: string;
|
|
25
34
|
parameters: ObjectTool;
|
|
26
|
-
}
|
|
35
|
+
};
|
|
27
36
|
}
|
|
28
37
|
|
|
29
38
|
// a sidebar page of a plugin
|
|
@@ -65,7 +74,7 @@ export interface ContextMenuAction {
|
|
|
65
74
|
// id of the plugin that the action belongs to
|
|
66
75
|
plugin_id: string;
|
|
67
76
|
// key of the action. Used to know which action to trigger when clicking on the context menu
|
|
68
|
-
action_key: string
|
|
77
|
+
action_key: string;
|
|
69
78
|
}
|
|
70
79
|
|
|
71
80
|
/**
|
|
@@ -86,7 +95,7 @@ export interface RimoriPluginConfig<T extends {} = {}> {
|
|
|
86
95
|
logo: string;
|
|
87
96
|
/** Optional website URL for the plugin's homepage or link to plugins owner for contributions */
|
|
88
97
|
website?: string;
|
|
89
|
-
}
|
|
98
|
+
};
|
|
90
99
|
/**
|
|
91
100
|
* Configuration for different types of pages.
|
|
92
101
|
*/
|
|
@@ -101,11 +110,11 @@ export interface RimoriPluginConfig<T extends {} = {}> {
|
|
|
101
110
|
settings?: string;
|
|
102
111
|
/** Optional array of event topics the plugin pages can listen to for cross-plugin communication */
|
|
103
112
|
topics?: string[];
|
|
104
|
-
}
|
|
113
|
+
};
|
|
105
114
|
/**
|
|
106
115
|
* Context menu actions that the plugin registers to appear in right-click menus throughout the application.
|
|
107
116
|
*/
|
|
108
|
-
context_menu_actions: Omit<MenuEntry,
|
|
117
|
+
context_menu_actions: Omit<MenuEntry, 'plugin_id'>[];
|
|
109
118
|
/**
|
|
110
119
|
* Documentation paths for different types of plugin documentation.
|
|
111
120
|
*/
|
|
@@ -116,7 +125,7 @@ export interface RimoriPluginConfig<T extends {} = {}> {
|
|
|
116
125
|
user_path: string;
|
|
117
126
|
/** Path to developer documentation for plugin development */
|
|
118
127
|
developer_path: string;
|
|
119
|
-
}
|
|
128
|
+
};
|
|
120
129
|
/**
|
|
121
130
|
* Configuration for the plugin's web worker if it uses background processing or exposes actions to other plugins.
|
|
122
131
|
*/
|
|
@@ -136,7 +145,7 @@ export interface Tool {
|
|
|
136
145
|
parameters: {
|
|
137
146
|
name: string;
|
|
138
147
|
description: string;
|
|
139
|
-
type:
|
|
148
|
+
type: 'string' | 'number' | 'boolean';
|
|
140
149
|
}[];
|
|
141
150
|
execute?: (args: Record<string, any>) => Promise<unknown> | unknown | void;
|
|
142
151
|
}
|
|
@@ -145,7 +154,7 @@ export interface Tool {
|
|
|
145
154
|
* The tool definition structure is used for LLM function calling and plugin action parameters.
|
|
146
155
|
* It defines the schema for tools that can be used by Language Learning Models (LLMs)
|
|
147
156
|
* and plugin actions.
|
|
148
|
-
*
|
|
157
|
+
*
|
|
149
158
|
* @example
|
|
150
159
|
* ```typescript
|
|
151
160
|
* const flashcardTool: Tool = {
|
|
@@ -155,13 +164,13 @@ export interface Tool {
|
|
|
155
164
|
* description: 'Number of flashcards to practice'
|
|
156
165
|
* },
|
|
157
166
|
* deck: {
|
|
158
|
-
* type: 'string',
|
|
167
|
+
* type: 'string',
|
|
159
168
|
* enum: ['latest', 'random', 'oldest', 'mix', 'best_known'],
|
|
160
169
|
* description: 'Type of deck to practice'
|
|
161
170
|
* }
|
|
162
171
|
* };
|
|
163
172
|
* ```
|
|
164
|
-
*
|
|
173
|
+
*
|
|
165
174
|
*/
|
|
166
175
|
export type ObjectTool = {
|
|
167
176
|
[key: string]: ToolParameter;
|
|
@@ -188,15 +197,15 @@ interface ToolParameter {
|
|
|
188
197
|
* Supports primitive types, nested objects for complex data structures,
|
|
189
198
|
* and arrays of objects for collections. The tuple notation [{}] indicates
|
|
190
199
|
* arrays of objects with a specific structure.
|
|
191
|
-
*
|
|
200
|
+
*
|
|
192
201
|
* @example Primitive: 'string' | 'number' | 'boolean'
|
|
193
202
|
* @example Nested object: { name: { type: 'string' }, age: { type: 'number' } }
|
|
194
203
|
* @example Array of objects: [{ id: { type: 'string' }, value: { type: 'number' } }]
|
|
195
204
|
*/
|
|
196
205
|
type ToolParameterType =
|
|
197
206
|
| PrimitiveType
|
|
198
|
-
| { [key: string]: ToolParameter }
|
|
199
|
-
| [{ [key: string]: ToolParameter }];
|
|
207
|
+
| { [key: string]: ToolParameter } // for nested objects
|
|
208
|
+
| [{ [key: string]: ToolParameter }]; // for arrays of objects (notice the tuple type)
|
|
200
209
|
|
|
201
210
|
/**
|
|
202
211
|
* Primitive data types supported by the LLM tool system.
|
package/src/fromRimori/readme.md
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
Files inside this folder are copied from Rimori and should not be changed.
|
|
2
|
-
If change is needed adapt it in Rimori and copy the file then over.
|
|
1
|
+
Files inside this folder are copied from Rimori and should not be changed.
|
|
2
|
+
If change is needed adapt it in Rimori and copy the file then over.
|
package/src/hooks/UseChatHook.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import React from
|
|
2
|
-
import { Tool } from
|
|
3
|
-
import { useRimori } from
|
|
4
|
-
import { Message, ToolInvocation } from
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Tool } from '../fromRimori/PluginTypes';
|
|
3
|
+
import { useRimori } from '../providers/PluginProvider';
|
|
4
|
+
import { Message, ToolInvocation } from '../core/controller/AIController';
|
|
5
5
|
|
|
6
6
|
export function useChat(tools?: Tool[]) {
|
|
7
7
|
const [messages, setMessages] = React.useState<Message[]>([]);
|
|
@@ -11,18 +11,28 @@ export function useChat(tools?: Tool[]) {
|
|
|
11
11
|
const append = (appendMessages: Message[]) => {
|
|
12
12
|
const allMessages = [...messages, ...appendMessages];
|
|
13
13
|
setMessages(allMessages);
|
|
14
|
-
ai.getSteamedText(
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
ai.getSteamedText(
|
|
15
|
+
allMessages,
|
|
16
|
+
(id, message, finished: boolean, toolInvocations?: ToolInvocation[]) => {
|
|
17
|
+
const lastMessage = messages[messages.length - 1];
|
|
18
|
+
setIsLoading(!finished);
|
|
17
19
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
20
|
+
if (lastMessage?.id === id) {
|
|
21
|
+
lastMessage.content = message;
|
|
22
|
+
setMessages([...messages, lastMessage]);
|
|
23
|
+
} else {
|
|
24
|
+
setMessages([...allMessages, { id, role: 'assistant', content: message, toolCalls: toolInvocations }]);
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
tools,
|
|
28
|
+
);
|
|
25
29
|
};
|
|
26
30
|
|
|
27
|
-
return {
|
|
31
|
+
return {
|
|
32
|
+
messages,
|
|
33
|
+
append,
|
|
34
|
+
isLoading,
|
|
35
|
+
setMessages,
|
|
36
|
+
lastMessage: messages[messages.length - 1] as Message | undefined,
|
|
37
|
+
};
|
|
28
38
|
}
|
package/src/index.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
// Re-export everything
|
|
2
2
|
export * from './components';
|
|
3
|
-
export * from
|
|
4
|
-
export * from
|
|
5
|
-
export * from
|
|
6
|
-
export * from
|
|
7
|
-
export * from
|
|
8
|
-
export * from
|
|
9
|
-
export * from
|
|
10
|
-
export * from
|
|
11
|
-
export { FirstMessages } from
|
|
12
|
-
export { AudioController } from
|
|
3
|
+
export * from './hooks/UseChatHook';
|
|
4
|
+
export * from './plugin/PluginController';
|
|
5
|
+
export * from './providers/PluginProvider';
|
|
6
|
+
export * from './cli/types/DatabaseTypes';
|
|
7
|
+
export * from './utils/difficultyConverter';
|
|
8
|
+
export * from './utils/PluginUtils';
|
|
9
|
+
export * from './utils/Language';
|
|
10
|
+
export * from './fromRimori/PluginTypes';
|
|
11
|
+
export { FirstMessages } from './components/ai/utils';
|
|
12
|
+
export { AudioController } from './plugin/AudioController';
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { EventBus, EventBusMessage } from
|
|
1
|
+
import { EventBus, EventBusMessage } from '../fromRimori/EventBus';
|
|
2
2
|
|
|
3
3
|
export type AccomplishmentMessage = EventBusMessage<MicroAccomplishmentPayload>;
|
|
4
4
|
|
|
5
|
-
export const skillCategories = [
|
|
5
|
+
export const skillCategories = ['reading', 'listening', 'speaking', 'writing', 'learning', 'community'] as const;
|
|
6
6
|
|
|
7
7
|
interface BaseAccomplishmentPayload {
|
|
8
|
-
type:
|
|
8
|
+
type: 'micro' | 'macro';
|
|
9
9
|
skillCategory: (typeof skillCategories)[number];
|
|
10
10
|
/*
|
|
11
11
|
what is the accomplishment? e.g. chapter, flashcard, story, etc.
|
|
@@ -25,11 +25,11 @@ interface BaseAccomplishmentPayload {
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
export interface MicroAccomplishmentPayload extends BaseAccomplishmentPayload {
|
|
28
|
-
type:
|
|
28
|
+
type: 'micro';
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
export interface MacroAccomplishmentPayload extends BaseAccomplishmentPayload {
|
|
32
|
-
type:
|
|
32
|
+
type: 'macro';
|
|
33
33
|
errorRatio: number;
|
|
34
34
|
durationMinutes: number;
|
|
35
35
|
}
|
|
@@ -43,14 +43,17 @@ export class AccomplishmentHandler {
|
|
|
43
43
|
this.pluginId = pluginId;
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
emitAccomplishment(payload: Omit<AccomplishmentPayload,
|
|
47
|
-
const accomplishmentPayload = {
|
|
46
|
+
emitAccomplishment(payload: Omit<AccomplishmentPayload, 'type'>) {
|
|
47
|
+
const accomplishmentPayload = {
|
|
48
|
+
...payload,
|
|
49
|
+
type: 'durationMinutes' in payload ? 'macro' : 'micro',
|
|
50
|
+
} as AccomplishmentPayload;
|
|
48
51
|
|
|
49
52
|
this.validateAccomplishment(accomplishmentPayload);
|
|
50
53
|
|
|
51
54
|
const sanitizedPayload = this.sanitizeAccomplishment(accomplishmentPayload);
|
|
52
55
|
|
|
53
|
-
const topic =
|
|
56
|
+
const topic = 'global.accomplishment.trigger' + (accomplishmentPayload.type === 'macro' ? 'Macro' : 'Micro');
|
|
54
57
|
|
|
55
58
|
EventBus.emit(this.pluginId, topic, sanitizedPayload);
|
|
56
59
|
}
|
|
@@ -62,53 +65,59 @@ export class AccomplishmentHandler {
|
|
|
62
65
|
|
|
63
66
|
//regex validate accomplishmentKeyword
|
|
64
67
|
if (!/^[a-z_-]+$/.test(payload.accomplishmentKeyword)) {
|
|
65
|
-
throw new Error(
|
|
68
|
+
throw new Error(
|
|
69
|
+
`The accomplishment keyword: ${payload.accomplishmentKeyword} is invalid. Only lowercase letters, minuses and underscores are allowed`,
|
|
70
|
+
);
|
|
66
71
|
}
|
|
67
72
|
|
|
68
73
|
//description is required
|
|
69
74
|
if (payload.description.length < 10) {
|
|
70
|
-
throw new Error(
|
|
75
|
+
throw new Error('Description is too short');
|
|
71
76
|
}
|
|
72
77
|
|
|
73
78
|
//check that the type is valid
|
|
74
|
-
if (![
|
|
75
|
-
throw new Error(
|
|
79
|
+
if (!['micro', 'macro'].includes(payload.type)) {
|
|
80
|
+
throw new Error('Invalid accomplishment type ' + payload.type);
|
|
76
81
|
}
|
|
77
82
|
|
|
78
83
|
//durationMinutes is required
|
|
79
|
-
if (payload.type ===
|
|
80
|
-
throw new Error(
|
|
84
|
+
if (payload.type === 'macro' && payload.durationMinutes < 4) {
|
|
85
|
+
throw new Error('The duration must be at least 4 minutes');
|
|
81
86
|
}
|
|
82
87
|
|
|
83
88
|
//errorRatio is required
|
|
84
|
-
if (payload.type ===
|
|
85
|
-
throw new Error(
|
|
89
|
+
if (payload.type === 'macro' && (payload.errorRatio < 0 || payload.errorRatio > 1)) {
|
|
90
|
+
throw new Error('The error ratio must be between 0 and 1');
|
|
86
91
|
}
|
|
87
92
|
|
|
88
93
|
//regex check meta data key
|
|
89
94
|
if (payload.meta) {
|
|
90
|
-
payload.meta.forEach(meta => {
|
|
95
|
+
payload.meta.forEach((meta) => {
|
|
91
96
|
if (!/^[a-z_]+$/.test(meta.key)) {
|
|
92
|
-
throw new Error(
|
|
97
|
+
throw new Error('Invalid meta data key ' + meta.key + ', only lowercase letters and underscores are allowed');
|
|
93
98
|
}
|
|
94
99
|
});
|
|
95
100
|
}
|
|
96
101
|
}
|
|
97
102
|
|
|
98
103
|
private sanitizeAccomplishment(payload: AccomplishmentPayload) {
|
|
99
|
-
payload.description = payload.description.replace(/[^\x20-\x7E]/g,
|
|
104
|
+
payload.description = payload.description.replace(/[^\x20-\x7E]/g, '');
|
|
100
105
|
|
|
101
106
|
payload.meta?.forEach((meta) => {
|
|
102
|
-
meta.description = meta.description.replace(/[^\x20-\x7E]/g,
|
|
107
|
+
meta.description = meta.description.replace(/[^\x20-\x7E]/g, '');
|
|
103
108
|
});
|
|
104
109
|
|
|
105
110
|
return payload;
|
|
106
111
|
}
|
|
107
112
|
|
|
108
113
|
private getDecoupledTopic(topic: string) {
|
|
109
|
-
const [plugin, skillCategory, accomplishmentKeyword] = topic.split(
|
|
114
|
+
const [plugin, skillCategory, accomplishmentKeyword] = topic.split('.');
|
|
110
115
|
|
|
111
|
-
return {
|
|
116
|
+
return {
|
|
117
|
+
plugin: plugin || '*',
|
|
118
|
+
skillCategory: skillCategory || '*',
|
|
119
|
+
accomplishmentKeyword: accomplishmentKeyword || '*',
|
|
120
|
+
};
|
|
112
121
|
}
|
|
113
122
|
|
|
114
123
|
/**
|
|
@@ -116,30 +125,39 @@ export class AccomplishmentHandler {
|
|
|
116
125
|
* @param accomplishmentTopic - The topic of the accomplishment event. The pattern can be any pattern of plugin.skillCategory.accomplishmentKeyword or an * as wildcard for any plugin, skill category or accomplishment keyword
|
|
117
126
|
* @param callback - The callback function to be called when the accomplishment event is triggered
|
|
118
127
|
*/
|
|
119
|
-
subscribe(
|
|
120
|
-
|
|
128
|
+
subscribe(
|
|
129
|
+
accomplishmentTopics = '*' as string | string[],
|
|
130
|
+
callback: (payload: EventBusMessage<AccomplishmentPayload>) => void,
|
|
131
|
+
) {
|
|
132
|
+
if (typeof accomplishmentTopics === 'string') {
|
|
121
133
|
accomplishmentTopics = [accomplishmentTopics];
|
|
122
134
|
}
|
|
123
135
|
|
|
124
136
|
accomplishmentTopics.forEach((accomplishmentTopic) => {
|
|
125
|
-
const topicLength = accomplishmentTopic.split(
|
|
137
|
+
const topicLength = accomplishmentTopic.split('.').length;
|
|
126
138
|
if (topicLength === 1) {
|
|
127
|
-
accomplishmentTopic +=
|
|
139
|
+
accomplishmentTopic += '.*.*';
|
|
128
140
|
} else if (topicLength === 2) {
|
|
129
|
-
accomplishmentTopic +=
|
|
141
|
+
accomplishmentTopic += '.*';
|
|
130
142
|
} else if (topicLength !== 3) {
|
|
131
|
-
throw new Error(
|
|
143
|
+
throw new Error(
|
|
144
|
+
'Invalid accomplishment topic pattern. The pattern must be plugin.skillCategory.accomplishmentKeyword or an * as wildcard for any plugin, skill category or accomplishment keyword',
|
|
145
|
+
);
|
|
132
146
|
}
|
|
133
147
|
|
|
134
|
-
EventBus.on<AccomplishmentPayload>(
|
|
135
|
-
|
|
148
|
+
EventBus.on<AccomplishmentPayload>(
|
|
149
|
+
['global.accomplishment.triggerMicro', 'global.accomplishment.triggerMacro'],
|
|
150
|
+
(event) => {
|
|
151
|
+
const { plugin, skillCategory, accomplishmentKeyword } = this.getDecoupledTopic(accomplishmentTopic);
|
|
136
152
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
153
|
+
if (plugin !== '*' && event.sender !== plugin) return;
|
|
154
|
+
if (skillCategory !== '*' && event.data.skillCategory !== skillCategory) return;
|
|
155
|
+
if (accomplishmentKeyword !== '*' && event.data.accomplishmentKeyword !== accomplishmentKeyword) return;
|
|
140
156
|
|
|
141
|
-
|
|
142
|
-
|
|
157
|
+
callback(event);
|
|
158
|
+
},
|
|
159
|
+
[this.pluginId],
|
|
160
|
+
);
|
|
143
161
|
});
|
|
144
162
|
}
|
|
145
163
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { EventBus } from
|
|
1
|
+
import { EventBus } from '../fromRimori/EventBus';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* AudioController is a class that provides methods to record audio. It is a wrapper around the Capacitor Voice Recorder plugin. For more information, see https://github.com/tchvu3/capacitor-voice-recorder.
|
|
5
|
-
*
|
|
5
|
+
*
|
|
6
6
|
* @example
|
|
7
7
|
* const audioController = new AudioController();
|
|
8
8
|
* await audioController.startRecording();
|
|
@@ -16,43 +16,49 @@ export class AudioController {
|
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
18
|
* Start the recording.
|
|
19
|
-
*
|
|
19
|
+
*
|
|
20
20
|
* @example
|
|
21
21
|
* const audioController = new AudioController();
|
|
22
22
|
* await audioController.startRecording();
|
|
23
23
|
* @returns void
|
|
24
24
|
*/
|
|
25
25
|
public async startRecording(): Promise<void> {
|
|
26
|
-
EventBus.emit(this.pluginId,
|
|
26
|
+
EventBus.emit(this.pluginId, 'global.microphone.triggerStartRecording');
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
/**
|
|
30
30
|
* Stop the recording and return the audio data.
|
|
31
31
|
* @returns The audio data.
|
|
32
|
-
*
|
|
33
|
-
* @example
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
34
|
* const audioRef = new Audio(`data:${mimeType};base64,${base64Sound}`)
|
|
35
35
|
* audioRef.oncanplaythrough = () => audioRef.play()
|
|
36
36
|
* audioRef.load()
|
|
37
37
|
*/
|
|
38
|
-
public async stopRecording(): Promise<{ recording: Blob
|
|
39
|
-
const result = await EventBus.request<{ recording: Blob
|
|
38
|
+
public async stopRecording(): Promise<{ recording: Blob; msDuration: number; mimeType: string }> {
|
|
39
|
+
const result = await EventBus.request<{ recording: Blob; msDuration: number; mimeType: string }>(
|
|
40
|
+
this.pluginId,
|
|
41
|
+
'global.microphone.triggerStopRecording',
|
|
42
|
+
);
|
|
40
43
|
|
|
41
44
|
return result.data;
|
|
42
45
|
}
|
|
43
46
|
|
|
44
47
|
public async pauseRecording(): Promise<boolean> {
|
|
45
|
-
const result = await EventBus.request<boolean>(this.pluginId,
|
|
48
|
+
const result = await EventBus.request<boolean>(this.pluginId, 'global.microphone.triggerPauseRecording');
|
|
46
49
|
return result.data;
|
|
47
50
|
}
|
|
48
51
|
|
|
49
52
|
public async resumeRecording(): Promise<boolean> {
|
|
50
|
-
const result = await EventBus.request<boolean>(this.pluginId,
|
|
53
|
+
const result = await EventBus.request<boolean>(this.pluginId, 'global.microphone.triggerResumeRecording');
|
|
51
54
|
return result.data;
|
|
52
55
|
}
|
|
53
56
|
|
|
54
|
-
public async getCurrentStatus(): Promise<
|
|
55
|
-
const result = await EventBus.request<
|
|
57
|
+
public async getCurrentStatus(): Promise<'RECORDING' | 'PAUSED' | 'NONE'> {
|
|
58
|
+
const result = await EventBus.request<'RECORDING' | 'PAUSED' | 'NONE'>(
|
|
59
|
+
this.pluginId,
|
|
60
|
+
'global.microphone.triggerGetCurrentStatus',
|
|
61
|
+
);
|
|
56
62
|
return result.data;
|
|
57
63
|
}
|
|
58
64
|
}
|
package/src/plugin/Logger.ts
CHANGED
|
@@ -62,7 +62,7 @@ export class Logger {
|
|
|
62
62
|
info: console.info,
|
|
63
63
|
warn: console.warn,
|
|
64
64
|
error: console.error,
|
|
65
|
-
debug: console.debug
|
|
65
|
+
debug: console.debug,
|
|
66
66
|
};
|
|
67
67
|
|
|
68
68
|
// Override console methods globally
|
|
@@ -82,8 +82,8 @@ export class Logger {
|
|
|
82
82
|
const logs = {
|
|
83
83
|
logs: this.logs,
|
|
84
84
|
pluginId: rimori.plugin.pluginId,
|
|
85
|
-
timestamp: new Date().toISOString()
|
|
86
|
-
}
|
|
85
|
+
timestamp: new Date().toISOString(),
|
|
86
|
+
};
|
|
87
87
|
this.logs = [];
|
|
88
88
|
this.logIdCounter = 0;
|
|
89
89
|
return logs;
|
|
@@ -129,7 +129,7 @@ export class Logger {
|
|
|
129
129
|
if (typeof window === 'undefined' || typeof history === 'undefined') return;
|
|
130
130
|
|
|
131
131
|
// Clear logs on browser back/forward
|
|
132
|
-
window.addEventListener('popstate', () => this.logs = []);
|
|
132
|
+
window.addEventListener('popstate', () => (this.logs = []));
|
|
133
133
|
|
|
134
134
|
// Override history methods to clear logs on programmatic navigation
|
|
135
135
|
const originalPushState = history.pushState;
|
|
@@ -158,7 +158,7 @@ export class Logger {
|
|
|
158
158
|
setInterval(checkUrlChange, 100);
|
|
159
159
|
|
|
160
160
|
// Also listen for hash changes (for hash-based routing)
|
|
161
|
-
window.addEventListener('hashchange', () => this.logs = []);
|
|
161
|
+
window.addEventListener('hashchange', () => (this.logs = []));
|
|
162
162
|
}
|
|
163
163
|
|
|
164
164
|
/**
|
|
@@ -167,35 +167,81 @@ export class Logger {
|
|
|
167
167
|
private overrideConsoleMethods(): void {
|
|
168
168
|
// Override console.log
|
|
169
169
|
console.log = (...args: any[]) => {
|
|
170
|
-
this.
|
|
170
|
+
const { location, style } = this.getCallerLocation();
|
|
171
|
+
this.originalConsole.log(location, style, ...args);
|
|
171
172
|
this.handleConsoleCall('info', args);
|
|
172
173
|
};
|
|
173
174
|
|
|
174
175
|
// Override console.info
|
|
175
176
|
console.info = (...args: any[]) => {
|
|
176
|
-
this.
|
|
177
|
+
const { location, style } = this.getCallerLocation();
|
|
178
|
+
this.originalConsole.info(location, style, ...args);
|
|
177
179
|
this.handleConsoleCall('info', args);
|
|
178
180
|
};
|
|
179
181
|
|
|
180
182
|
// Override console.warn
|
|
181
183
|
console.warn = (...args: any[]) => {
|
|
182
|
-
this.
|
|
184
|
+
const { location, style } = this.getCallerLocation();
|
|
185
|
+
this.originalConsole.warn(location, style, ...args);
|
|
183
186
|
this.handleConsoleCall('warn', args);
|
|
184
187
|
};
|
|
185
188
|
|
|
186
189
|
// Override console.error
|
|
187
190
|
console.error = (...args: any[]) => {
|
|
188
|
-
this.
|
|
191
|
+
const { location, style } = this.getCallerLocation();
|
|
192
|
+
this.originalConsole.error(location, style, ...args);
|
|
189
193
|
this.handleConsoleCall('error', args);
|
|
190
194
|
};
|
|
191
195
|
|
|
192
196
|
// Override console.debug
|
|
193
197
|
console.debug = (...args: any[]) => {
|
|
194
|
-
this.
|
|
198
|
+
const { location, style } = this.getCallerLocation();
|
|
199
|
+
this.originalConsole.debug(location, style, ...args);
|
|
195
200
|
this.handleConsoleCall('debug', args);
|
|
196
201
|
};
|
|
197
202
|
}
|
|
198
203
|
|
|
204
|
+
/**
|
|
205
|
+
* Get caller information from stack trace.
|
|
206
|
+
* @returns Object with location string and CSS style, or empty values for production
|
|
207
|
+
*/
|
|
208
|
+
private getCallerLocation(): { location: string; style: string } {
|
|
209
|
+
const emptyResult = { location: '', style: '' };
|
|
210
|
+
const style = 'color: #0063A2; font-weight: bold;';
|
|
211
|
+
|
|
212
|
+
if (this.isProduction) return emptyResult;
|
|
213
|
+
|
|
214
|
+
try {
|
|
215
|
+
const stack = new Error().stack;
|
|
216
|
+
if (!stack) return emptyResult;
|
|
217
|
+
|
|
218
|
+
const stackLines = stack.split('\n');
|
|
219
|
+
// Skip the first 3 lines: Error, getCallerLocation, overrideConsoleMethods wrapper
|
|
220
|
+
const callerLine = stackLines[3];
|
|
221
|
+
|
|
222
|
+
if (!callerLine) return emptyResult;
|
|
223
|
+
|
|
224
|
+
// Extract file name and line number from stack trace
|
|
225
|
+
// Format: "at functionName (file:line:column)" or "at file:line:column"
|
|
226
|
+
const match = callerLine.match(/(?:at\s+.*?\s+\()?([^/\\(]+\.(?:ts|tsx|js|jsx)):(\d+):(\d+)\)?/);
|
|
227
|
+
|
|
228
|
+
if (match) {
|
|
229
|
+
const [, fileName, lineNumber] = match;
|
|
230
|
+
return { style, location: `%c[${fileName}:${lineNumber}]` };
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Fallback: try to extract just the file name
|
|
234
|
+
const simpleMatch = callerLine.match(/([^/\\]+\.(?:ts|tsx|js|jsx))/);
|
|
235
|
+
if (simpleMatch) {
|
|
236
|
+
return { style, location: `%c[${simpleMatch[1]}]` };
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
return emptyResult;
|
|
240
|
+
} catch (error) {
|
|
241
|
+
return emptyResult;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
199
245
|
/**
|
|
200
246
|
* Track mouse position for screenshot context.
|
|
201
247
|
*/
|
|
@@ -205,7 +251,7 @@ export class Logger {
|
|
|
205
251
|
this.mousePosition = {
|
|
206
252
|
x: event.clientX,
|
|
207
253
|
y: event.clientY,
|
|
208
|
-
timestamp: new Date().toISOString()
|
|
254
|
+
timestamp: new Date().toISOString(),
|
|
209
255
|
};
|
|
210
256
|
};
|
|
211
257
|
|
|
@@ -226,9 +272,16 @@ export class Logger {
|
|
|
226
272
|
}
|
|
227
273
|
|
|
228
274
|
// Convert console arguments to message and data
|
|
229
|
-
const message = args
|
|
230
|
-
|
|
231
|
-
|
|
275
|
+
const message = args
|
|
276
|
+
.map((arg) => {
|
|
277
|
+
if (typeof arg !== 'object') return arg;
|
|
278
|
+
try {
|
|
279
|
+
return JSON.stringify(arg);
|
|
280
|
+
} catch (error: any) {
|
|
281
|
+
return 'Error adding object to log: ' + error.message + ' ' + String(arg);
|
|
282
|
+
}
|
|
283
|
+
})
|
|
284
|
+
.join(' ');
|
|
232
285
|
|
|
233
286
|
const data = args.length > 1 ? args.slice(1) : undefined;
|
|
234
287
|
|
|
@@ -248,7 +301,7 @@ export class Logger {
|
|
|
248
301
|
onLine: navigator.onLine,
|
|
249
302
|
screenResolution: `${screen.width}x${screen.height}`,
|
|
250
303
|
windowSize: `${window.innerWidth}x${window.innerHeight}`,
|
|
251
|
-
timestamp: new Date().toISOString()
|
|
304
|
+
timestamp: new Date().toISOString(),
|
|
252
305
|
};
|
|
253
306
|
}
|
|
254
307
|
|
|
@@ -273,7 +326,12 @@ export class Logger {
|
|
|
273
326
|
* @param data - Additional data
|
|
274
327
|
* @returns Log entry
|
|
275
328
|
*/
|
|
276
|
-
private async createLogEntry(
|
|
329
|
+
private async createLogEntry(
|
|
330
|
+
level: LogLevel,
|
|
331
|
+
message: string,
|
|
332
|
+
data?: any,
|
|
333
|
+
forceScreenshot?: boolean,
|
|
334
|
+
): Promise<LogEntry> {
|
|
277
335
|
const context: Partial<LogEntry['context']> = {};
|
|
278
336
|
|
|
279
337
|
// Add URL if available
|
|
@@ -284,7 +342,7 @@ export class Logger {
|
|
|
284
342
|
level,
|
|
285
343
|
message,
|
|
286
344
|
data,
|
|
287
|
-
}
|
|
345
|
+
};
|
|
288
346
|
}
|
|
289
347
|
|
|
290
348
|
context.url = window.location.href;
|
|
@@ -295,7 +353,7 @@ export class Logger {
|
|
|
295
353
|
|
|
296
354
|
// Add screenshot and mouse position if level is error or warn
|
|
297
355
|
if (level === 'error' || level === 'warn' || forceScreenshot) {
|
|
298
|
-
context.screenshot = await this.captureScreenshot() || undefined;
|
|
356
|
+
context.screenshot = (await this.captureScreenshot()) || undefined;
|
|
299
357
|
context.mousePosition = this.mousePosition || undefined;
|
|
300
358
|
}
|
|
301
359
|
|
|
@@ -305,7 +363,7 @@ export class Logger {
|
|
|
305
363
|
level,
|
|
306
364
|
message,
|
|
307
365
|
data,
|
|
308
|
-
context: context as LogEntry['context']
|
|
366
|
+
context: context as LogEntry['context'],
|
|
309
367
|
};
|
|
310
368
|
}
|
|
311
369
|
|