@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.
Files changed (148) hide show
  1. package/.prettierignore +35 -0
  2. package/README.md +77 -71
  3. package/dist/cli/scripts/init/dev-registration.d.ts +1 -1
  4. package/dist/cli/scripts/init/dev-registration.js +4 -4
  5. package/dist/cli/scripts/init/main.js +1 -1
  6. package/dist/cli/scripts/init/package-setup.d.ts +1 -1
  7. package/dist/cli/scripts/init/package-setup.js +3 -3
  8. package/dist/cli/scripts/init/router-transformer.js +19 -12
  9. package/dist/cli/scripts/init/vite-config.d.ts +2 -2
  10. package/dist/cli/scripts/init/vite-config.js +2 -2
  11. package/dist/cli/scripts/release/release-config-upload.js +9 -9
  12. package/dist/cli/scripts/release/release-db-update.d.ts +1 -1
  13. package/dist/cli/scripts/release/release-db-update.js +9 -9
  14. package/dist/cli/scripts/release/release-file-upload.js +2 -2
  15. package/dist/cli/scripts/release/release.js +2 -2
  16. package/dist/cli/types/DatabaseTypes.d.ts +2 -2
  17. package/dist/components/CRUDModal.d.ts +1 -1
  18. package/dist/components/CRUDModal.js +3 -3
  19. package/dist/components/MarkdownEditor.js +16 -16
  20. package/dist/components/Spinner.js +2 -2
  21. package/dist/components/ai/Assistant.js +7 -8
  22. package/dist/components/ai/Avatar.d.ts +2 -2
  23. package/dist/components/ai/Avatar.js +14 -7
  24. package/dist/components/ai/EmbeddedAssistent/AudioInputField.js +5 -6
  25. package/dist/components/ai/EmbeddedAssistent/CircleAudioAvatar.d.ts +1 -1
  26. package/dist/components/ai/EmbeddedAssistent/CircleAudioAvatar.js +1 -2
  27. package/dist/components/ai/EmbeddedAssistent/TTS/MessageSender.d.ts +1 -2
  28. package/dist/components/ai/EmbeddedAssistent/TTS/MessageSender.js +4 -2
  29. package/dist/components/ai/EmbeddedAssistent/TTS/Player.js +1 -1
  30. package/dist/components/ai/EmbeddedAssistent/VoiceRecoder.js +2 -3
  31. package/dist/components/audio/Playbutton.js +10 -7
  32. package/dist/components/components/ContextMenu.d.ts +1 -1
  33. package/dist/components/components/ContextMenu.js +19 -16
  34. package/dist/components.d.ts +10 -10
  35. package/dist/components.js +10 -10
  36. package/dist/core/controller/AIController.d.ts +2 -2
  37. package/dist/core/controller/AIController.js +20 -18
  38. package/dist/core/controller/ExerciseController.d.ts +52 -0
  39. package/dist/core/controller/ExerciseController.js +73 -0
  40. package/dist/core/controller/ObjectController.js +5 -5
  41. package/dist/core/controller/SettingsController.d.ts +22 -7
  42. package/dist/core/controller/SettingsController.js +73 -8
  43. package/dist/core/controller/SharedContentController.d.ts +3 -3
  44. package/dist/core/controller/SharedContentController.js +38 -20
  45. package/dist/core/controller/VoiceController.js +6 -4
  46. package/dist/core/core.d.ts +15 -14
  47. package/dist/core/core.js +7 -7
  48. package/dist/fromRimori/EventBus.js +23 -23
  49. package/dist/fromRimori/PluginTypes.d.ts +4 -4
  50. package/dist/hooks/UseChatHook.d.ts +3 -3
  51. package/dist/hooks/UseChatHook.js +9 -3
  52. package/dist/index.d.ts +10 -10
  53. package/dist/index.js +9 -9
  54. package/dist/plugin/AccomplishmentHandler.d.ts +5 -5
  55. package/dist/plugin/AccomplishmentHandler.js +31 -27
  56. package/dist/plugin/AudioController.d.ts +1 -1
  57. package/dist/plugin/AudioController.js +6 -6
  58. package/dist/plugin/Logger.d.ts +5 -0
  59. package/dist/plugin/Logger.js +65 -13
  60. package/dist/plugin/PluginController.d.ts +7 -1
  61. package/dist/plugin/PluginController.js +32 -27
  62. package/dist/plugin/RimoriClient.d.ts +39 -14
  63. package/dist/plugin/RimoriClient.js +60 -31
  64. package/dist/plugin/StandaloneClient.d.ts +1 -1
  65. package/dist/plugin/StandaloneClient.js +35 -16
  66. package/dist/plugin/ThemeSetter.js +4 -4
  67. package/dist/providers/PluginProvider.js +44 -14
  68. package/dist/utils/Language.js +57 -57
  69. package/dist/utils/PluginUtils.js +3 -3
  70. package/dist/utils/difficultyConverter.d.ts +1 -1
  71. package/dist/utils/difficultyConverter.js +1 -1
  72. package/dist/utils/endpoint.js +2 -2
  73. package/dist/worker/WorkerSetup.d.ts +1 -1
  74. package/dist/worker/WorkerSetup.js +6 -6
  75. package/eslint.config.js +53 -0
  76. package/example/docs/devdocs.md +50 -40
  77. package/example/docs/overview.md +1 -1
  78. package/example/docs/userdocs.md +4 -1
  79. package/example/rimori.config.ts +51 -49
  80. package/example/worker/vite.config.ts +3 -3
  81. package/example/worker/worker.ts +2 -2
  82. package/package.json +17 -4
  83. package/prettier.config.js +8 -0
  84. package/src/cli/scripts/init/dev-registration.ts +5 -8
  85. package/src/cli/scripts/init/env-setup.ts +1 -1
  86. package/src/cli/scripts/init/file-operations.ts +1 -1
  87. package/src/cli/scripts/init/html-cleaner.ts +2 -5
  88. package/src/cli/scripts/init/main.ts +16 -13
  89. package/src/cli/scripts/init/package-setup.ts +11 -15
  90. package/src/cli/scripts/init/router-transformer.ts +40 -37
  91. package/src/cli/scripts/init/tailwind-config.ts +17 -26
  92. package/src/cli/scripts/init/vite-config.ts +3 -3
  93. package/src/cli/scripts/release/release-config-upload.ts +11 -11
  94. package/src/cli/scripts/release/release-db-update.ts +12 -12
  95. package/src/cli/scripts/release/release-file-upload.ts +3 -3
  96. package/src/cli/scripts/release/release.ts +4 -4
  97. package/src/cli/types/DatabaseTypes.ts +7 -8
  98. package/src/components/CRUDModal.tsx +64 -48
  99. package/src/components/MarkdownEditor.tsx +58 -27
  100. package/src/components/Spinner.tsx +24 -17
  101. package/src/components/ai/Assistant.tsx +70 -70
  102. package/src/components/ai/Avatar.tsx +20 -16
  103. package/src/components/ai/EmbeddedAssistent/AudioInputField.tsx +63 -54
  104. package/src/components/ai/EmbeddedAssistent/CircleAudioAvatar.tsx +14 -5
  105. package/src/components/ai/EmbeddedAssistent/TTS/MessageSender.ts +75 -74
  106. package/src/components/ai/EmbeddedAssistent/TTS/Player.ts +177 -178
  107. package/src/components/ai/EmbeddedAssistent/VoiceRecoder.tsx +109 -94
  108. package/src/components/ai/utils.ts +4 -4
  109. package/src/components/audio/Playbutton.tsx +101 -93
  110. package/src/components/components/ContextMenu.tsx +47 -35
  111. package/src/components.ts +10 -10
  112. package/src/core/controller/AIController.ts +62 -50
  113. package/src/core/controller/ExerciseController.ts +98 -0
  114. package/src/core/controller/ObjectController.ts +15 -10
  115. package/src/core/controller/SettingsController.ts +89 -16
  116. package/src/core/controller/SharedContentController.ts +80 -44
  117. package/src/core/controller/VoiceController.ts +10 -8
  118. package/src/core/core.ts +15 -15
  119. package/src/fromRimori/EventBus.ts +76 -47
  120. package/src/fromRimori/PluginTypes.ts +26 -17
  121. package/src/fromRimori/readme.md +2 -2
  122. package/src/hooks/UseChatHook.ts +25 -15
  123. package/src/index.ts +10 -10
  124. package/src/plugin/AccomplishmentHandler.ts +53 -35
  125. package/src/plugin/AudioController.ts +18 -12
  126. package/src/plugin/Logger.ts +77 -19
  127. package/src/plugin/PluginController.ts +60 -44
  128. package/src/plugin/RimoriClient.ts +133 -69
  129. package/src/plugin/StandaloneClient.ts +51 -24
  130. package/src/plugin/ThemeSetter.ts +5 -5
  131. package/src/providers/PluginProvider.tsx +90 -36
  132. package/src/style.scss +3 -3
  133. package/src/utils/Language.ts +58 -58
  134. package/src/utils/PluginUtils.ts +16 -20
  135. package/src/utils/difficultyConverter.ts +2 -2
  136. package/src/utils/endpoint.ts +3 -2
  137. package/src/worker/WorkerSetup.ts +8 -9
  138. package/tsconfig.json +2 -4
  139. package/dist/components/LoggerExample.d.ts +0 -6
  140. package/dist/components/LoggerExample.js +0 -79
  141. package/dist/core/controller/AudioController.d.ts +0 -0
  142. package/dist/core/controller/AudioController.js +0 -1
  143. package/dist/hooks/UseLogger.d.ts +0 -30
  144. package/dist/hooks/UseLogger.js +0 -122
  145. package/dist/plugin/LoggerExample.d.ts +0 -16
  146. package/dist/plugin/LoggerExample.js +0 -140
  147. package/dist/utils/audioFormats.d.ts +0 -26
  148. 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: "alpha" | "beta" | "stable";
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: "vocabulary" | "grammar" | "reading" | "listening" | "watching" | "writing" | "speaking" | "other" | "community";
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, "plugin_id">[];
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: "string" | "number" | "boolean";
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 } // for nested objects
199
- | [{ [key: string]: ToolParameter }]; // for arrays of objects (notice the tuple type)
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.
@@ -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.
@@ -1,7 +1,7 @@
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";
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(allMessages, (id, message, finished: boolean, toolInvocations?: ToolInvocation[]) => {
15
- const lastMessage = messages[messages.length - 1];
16
- setIsLoading(!finished);
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
- if (lastMessage?.id === id) {
19
- lastMessage.content = message;
20
- setMessages([...messages, lastMessage]);
21
- } else {
22
- setMessages([...allMessages, { id, role: 'assistant', content: message, toolCalls: toolInvocations }]);
23
- }
24
- }, tools);
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 { messages, append, isLoading, setMessages, lastMessage: messages[messages.length - 1] as Message | undefined };
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 "./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";
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 "../fromRimori/EventBus";
1
+ import { EventBus, EventBusMessage } from '../fromRimori/EventBus';
2
2
 
3
3
  export type AccomplishmentMessage = EventBusMessage<MicroAccomplishmentPayload>;
4
4
 
5
- export const skillCategories = ["reading", "listening", "speaking", "writing", "learning", "community"] as const;
5
+ export const skillCategories = ['reading', 'listening', 'speaking', 'writing', 'learning', 'community'] as const;
6
6
 
7
7
  interface BaseAccomplishmentPayload {
8
- type: "micro" | "macro";
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: "micro";
28
+ type: 'micro';
29
29
  }
30
30
 
31
31
  export interface MacroAccomplishmentPayload extends BaseAccomplishmentPayload {
32
- type: "macro";
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, "type">) {
47
- const accomplishmentPayload = { ...payload, type: "durationMinutes" in payload ? "macro" : "micro" } as 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 = "global.accomplishment.trigger" + (accomplishmentPayload.type === "macro" ? "Macro" : "Micro");
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(`The accomplishment keyword: ${payload.accomplishmentKeyword} is invalid. Only lowercase letters, minuses and underscores are allowed`);
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("Description is too short");
75
+ throw new Error('Description is too short');
71
76
  }
72
77
 
73
78
  //check that the type is valid
74
- if (!["micro", "macro"].includes(payload.type)) {
75
- throw new Error("Invalid accomplishment type " + payload.type);
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 === "macro" && payload.durationMinutes < 4) {
80
- throw new Error("The duration must be at least 4 minutes");
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 === "macro" && (payload.errorRatio < 0 || payload.errorRatio > 1)) {
85
- throw new Error("The error ratio must be between 0 and 1");
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("Invalid meta data key " + meta.key + ", only lowercase letters and underscores are allowed");
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 { plugin: plugin || "*", skillCategory: skillCategory || "*", accomplishmentKeyword: accomplishmentKeyword || "*" };
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(accomplishmentTopics = "*" as string | string[], callback: (payload: EventBusMessage<AccomplishmentPayload>) => void) {
120
- if (typeof accomplishmentTopics === "string") {
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(".").length
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("Invalid accomplishment topic pattern. The pattern must be plugin.skillCategory.accomplishmentKeyword or an * as wildcard for any plugin, skill category or accomplishment keyword");
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>(["global.accomplishment.triggerMicro", "global.accomplishment.triggerMacro"], (event) => {
135
- const { plugin, skillCategory, accomplishmentKeyword } = this.getDecoupledTopic(accomplishmentTopic);
148
+ EventBus.on<AccomplishmentPayload>(
149
+ ['global.accomplishment.triggerMicro', 'global.accomplishment.triggerMacro'],
150
+ (event) => {
151
+ const { plugin, skillCategory, accomplishmentKeyword } = this.getDecoupledTopic(accomplishmentTopic);
136
152
 
137
- if (plugin !== "*" && event.sender !== plugin) return;
138
- if (skillCategory !== "*" && event.data.skillCategory !== skillCategory) return;
139
- if (accomplishmentKeyword !== "*" && event.data.accomplishmentKeyword !== accomplishmentKeyword) return;
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
- callback(event);
142
- }, [this.pluginId]);
157
+ callback(event);
158
+ },
159
+ [this.pluginId],
160
+ );
143
161
  });
144
162
  }
145
163
  }
@@ -1,8 +1,8 @@
1
- import { EventBus } from "../fromRimori/EventBus";
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, "global.microphone.triggerStartRecording");
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, msDuration: number, mimeType: string }> {
39
- const result = await EventBus.request<{ recording: Blob, msDuration: number, mimeType: string }>(this.pluginId, "global.microphone.triggerStopRecording");
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, "global.microphone.triggerPauseRecording");
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, "global.microphone.triggerResumeRecording");
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<"RECORDING" | "PAUSED" | "NONE"> {
55
- const result = await EventBus.request<"RECORDING" | "PAUSED" | "NONE">(this.pluginId, "global.microphone.triggerGetCurrentStatus");
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
  }
@@ -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.originalConsole.log(...args);
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.originalConsole.info(...args);
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.originalConsole.warn(...args);
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.originalConsole.error(...args);
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.originalConsole.debug(...args);
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.map(arg =>
230
- typeof arg === 'object' ? JSON.stringify(arg) : String(arg)
231
- ).join(' ');
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(level: LogLevel, message: string, data?: any, forceScreenshot?: boolean): Promise<LogEntry> {
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