@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
@@ -1,14 +1,14 @@
1
- import { EventBus } from "../fromRimori/EventBus";
2
- export const skillCategories = ["reading", "listening", "speaking", "writing", "learning", "community"];
1
+ import { EventBus } from '../fromRimori/EventBus';
2
+ export const skillCategories = ['reading', 'listening', 'speaking', 'writing', 'learning', 'community'];
3
3
  export class AccomplishmentHandler {
4
4
  constructor(pluginId) {
5
5
  this.pluginId = pluginId;
6
6
  }
7
7
  emitAccomplishment(payload) {
8
- const accomplishmentPayload = Object.assign(Object.assign({}, payload), { type: "durationMinutes" in payload ? "macro" : "micro" });
8
+ const accomplishmentPayload = Object.assign(Object.assign({}, payload), { type: 'durationMinutes' in payload ? 'macro' : 'micro' });
9
9
  this.validateAccomplishment(accomplishmentPayload);
10
10
  const sanitizedPayload = this.sanitizeAccomplishment(accomplishmentPayload);
11
- const topic = "global.accomplishment.trigger" + (accomplishmentPayload.type === "macro" ? "Macro" : "Micro");
11
+ const topic = 'global.accomplishment.trigger' + (accomplishmentPayload.type === 'macro' ? 'Macro' : 'Micro');
12
12
  EventBus.emit(this.pluginId, topic, sanitizedPayload);
13
13
  }
14
14
  validateAccomplishment(payload) {
@@ -21,68 +21,72 @@ export class AccomplishmentHandler {
21
21
  }
22
22
  //description is required
23
23
  if (payload.description.length < 10) {
24
- throw new Error("Description is too short");
24
+ throw new Error('Description is too short');
25
25
  }
26
26
  //check that the type is valid
27
- if (!["micro", "macro"].includes(payload.type)) {
28
- throw new Error("Invalid accomplishment type " + payload.type);
27
+ if (!['micro', 'macro'].includes(payload.type)) {
28
+ throw new Error('Invalid accomplishment type ' + payload.type);
29
29
  }
30
30
  //durationMinutes is required
31
- if (payload.type === "macro" && payload.durationMinutes < 4) {
32
- throw new Error("The duration must be at least 4 minutes");
31
+ if (payload.type === 'macro' && payload.durationMinutes < 4) {
32
+ throw new Error('The duration must be at least 4 minutes');
33
33
  }
34
34
  //errorRatio is required
35
- if (payload.type === "macro" && (payload.errorRatio < 0 || payload.errorRatio > 1)) {
36
- throw new Error("The error ratio must be between 0 and 1");
35
+ if (payload.type === 'macro' && (payload.errorRatio < 0 || payload.errorRatio > 1)) {
36
+ throw new Error('The error ratio must be between 0 and 1');
37
37
  }
38
38
  //regex check meta data key
39
39
  if (payload.meta) {
40
- payload.meta.forEach(meta => {
40
+ payload.meta.forEach((meta) => {
41
41
  if (!/^[a-z_]+$/.test(meta.key)) {
42
- throw new Error("Invalid meta data key " + meta.key + ", only lowercase letters and underscores are allowed");
42
+ throw new Error('Invalid meta data key ' + meta.key + ', only lowercase letters and underscores are allowed');
43
43
  }
44
44
  });
45
45
  }
46
46
  }
47
47
  sanitizeAccomplishment(payload) {
48
48
  var _a;
49
- payload.description = payload.description.replace(/[^\x20-\x7E]/g, "");
49
+ payload.description = payload.description.replace(/[^\x20-\x7E]/g, '');
50
50
  (_a = payload.meta) === null || _a === void 0 ? void 0 : _a.forEach((meta) => {
51
- meta.description = meta.description.replace(/[^\x20-\x7E]/g, "");
51
+ meta.description = meta.description.replace(/[^\x20-\x7E]/g, '');
52
52
  });
53
53
  return payload;
54
54
  }
55
55
  getDecoupledTopic(topic) {
56
- const [plugin, skillCategory, accomplishmentKeyword] = topic.split(".");
57
- return { plugin: plugin || "*", skillCategory: skillCategory || "*", accomplishmentKeyword: accomplishmentKeyword || "*" };
56
+ const [plugin, skillCategory, accomplishmentKeyword] = topic.split('.');
57
+ return {
58
+ plugin: plugin || '*',
59
+ skillCategory: skillCategory || '*',
60
+ accomplishmentKeyword: accomplishmentKeyword || '*',
61
+ };
58
62
  }
59
63
  /**
60
64
  * Subscribe to accomplishment events
61
65
  * @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
62
66
  * @param callback - The callback function to be called when the accomplishment event is triggered
63
67
  */
64
- subscribe(accomplishmentTopics = "*", callback) {
65
- if (typeof accomplishmentTopics === "string") {
68
+ subscribe(accomplishmentTopics = '*', callback) {
69
+ if (typeof accomplishmentTopics === 'string') {
66
70
  accomplishmentTopics = [accomplishmentTopics];
67
71
  }
68
72
  accomplishmentTopics.forEach((accomplishmentTopic) => {
69
- const topicLength = accomplishmentTopic.split(".").length;
73
+ const topicLength = accomplishmentTopic.split('.').length;
70
74
  if (topicLength === 1) {
71
- accomplishmentTopic += ".*.*";
75
+ accomplishmentTopic += '.*.*';
72
76
  }
73
77
  else if (topicLength === 2) {
74
- accomplishmentTopic += ".*";
78
+ accomplishmentTopic += '.*';
75
79
  }
76
80
  else if (topicLength !== 3) {
77
- 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");
81
+ 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');
78
82
  }
79
- EventBus.on(["global.accomplishment.triggerMicro", "global.accomplishment.triggerMacro"], (event) => {
83
+ EventBus.on(['global.accomplishment.triggerMicro', 'global.accomplishment.triggerMacro'], (event) => {
80
84
  const { plugin, skillCategory, accomplishmentKeyword } = this.getDecoupledTopic(accomplishmentTopic);
81
- if (plugin !== "*" && event.sender !== plugin)
85
+ if (plugin !== '*' && event.sender !== plugin)
82
86
  return;
83
- if (skillCategory !== "*" && event.data.skillCategory !== skillCategory)
87
+ if (skillCategory !== '*' && event.data.skillCategory !== skillCategory)
84
88
  return;
85
- if (accomplishmentKeyword !== "*" && event.data.accomplishmentKeyword !== accomplishmentKeyword)
89
+ if (accomplishmentKeyword !== '*' && event.data.accomplishmentKeyword !== accomplishmentKeyword)
86
90
  return;
87
91
  callback(event);
88
92
  }, [this.pluginId]);
@@ -33,5 +33,5 @@ export declare class AudioController {
33
33
  }>;
34
34
  pauseRecording(): Promise<boolean>;
35
35
  resumeRecording(): Promise<boolean>;
36
- getCurrentStatus(): Promise<"RECORDING" | "PAUSED" | "NONE">;
36
+ getCurrentStatus(): Promise<'RECORDING' | 'PAUSED' | 'NONE'>;
37
37
  }
@@ -7,7 +7,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
7
7
  step((generator = generator.apply(thisArg, _arguments || [])).next());
8
8
  });
9
9
  };
10
- import { EventBus } from "../fromRimori/EventBus";
10
+ import { EventBus } from '../fromRimori/EventBus';
11
11
  /**
12
12
  * 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.
13
13
  *
@@ -29,7 +29,7 @@ export class AudioController {
29
29
  */
30
30
  startRecording() {
31
31
  return __awaiter(this, void 0, void 0, function* () {
32
- EventBus.emit(this.pluginId, "global.microphone.triggerStartRecording");
32
+ EventBus.emit(this.pluginId, 'global.microphone.triggerStartRecording');
33
33
  });
34
34
  }
35
35
  /**
@@ -43,25 +43,25 @@ export class AudioController {
43
43
  */
44
44
  stopRecording() {
45
45
  return __awaiter(this, void 0, void 0, function* () {
46
- const result = yield EventBus.request(this.pluginId, "global.microphone.triggerStopRecording");
46
+ const result = yield EventBus.request(this.pluginId, 'global.microphone.triggerStopRecording');
47
47
  return result.data;
48
48
  });
49
49
  }
50
50
  pauseRecording() {
51
51
  return __awaiter(this, void 0, void 0, function* () {
52
- const result = yield EventBus.request(this.pluginId, "global.microphone.triggerPauseRecording");
52
+ const result = yield EventBus.request(this.pluginId, 'global.microphone.triggerPauseRecording');
53
53
  return result.data;
54
54
  });
55
55
  }
56
56
  resumeRecording() {
57
57
  return __awaiter(this, void 0, void 0, function* () {
58
- const result = yield EventBus.request(this.pluginId, "global.microphone.triggerResumeRecording");
58
+ const result = yield EventBus.request(this.pluginId, 'global.microphone.triggerResumeRecording');
59
59
  return result.data;
60
60
  });
61
61
  }
62
62
  getCurrentStatus() {
63
63
  return __awaiter(this, void 0, void 0, function* () {
64
- const result = yield EventBus.request(this.pluginId, "global.microphone.triggerGetCurrentStatus");
64
+ const result = yield EventBus.request(this.pluginId, 'global.microphone.triggerGetCurrentStatus');
65
65
  return result.data;
66
66
  });
67
67
  }
@@ -32,6 +32,11 @@ export declare class Logger {
32
32
  * Override console methods globally to capture all console calls.
33
33
  */
34
34
  private overrideConsoleMethods;
35
+ /**
36
+ * Get caller information from stack trace.
37
+ * @returns Object with location string and CSS style, or empty values for production
38
+ */
39
+ private getCallerLocation;
35
40
  /**
36
41
  * Track mouse position for screenshot context.
37
42
  */
@@ -25,7 +25,7 @@ export class Logger {
25
25
  info: console.info,
26
26
  warn: console.warn,
27
27
  error: console.error,
28
- debug: console.debug
28
+ debug: console.debug,
29
29
  };
30
30
  // Override console methods globally
31
31
  this.overrideConsoleMethods();
@@ -40,7 +40,7 @@ export class Logger {
40
40
  const logs = {
41
41
  logs: this.logs,
42
42
  pluginId: rimori.plugin.pluginId,
43
- timestamp: new Date().toISOString()
43
+ timestamp: new Date().toISOString(),
44
44
  };
45
45
  this.logs = [];
46
46
  this.logIdCounter = 0;
@@ -84,7 +84,7 @@ export class Logger {
84
84
  if (typeof window === 'undefined' || typeof history === 'undefined')
85
85
  return;
86
86
  // Clear logs on browser back/forward
87
- window.addEventListener('popstate', () => this.logs = []);
87
+ window.addEventListener('popstate', () => (this.logs = []));
88
88
  // Override history methods to clear logs on programmatic navigation
89
89
  const originalPushState = history.pushState;
90
90
  const originalReplaceState = history.replaceState;
@@ -107,7 +107,7 @@ export class Logger {
107
107
  // Check for URL changes periodically
108
108
  setInterval(checkUrlChange, 100);
109
109
  // Also listen for hash changes (for hash-based routing)
110
- window.addEventListener('hashchange', () => this.logs = []);
110
+ window.addEventListener('hashchange', () => (this.logs = []));
111
111
  }
112
112
  /**
113
113
  * Override console methods globally to capture all console calls.
@@ -115,30 +115,71 @@ export class Logger {
115
115
  overrideConsoleMethods() {
116
116
  // Override console.log
117
117
  console.log = (...args) => {
118
- this.originalConsole.log(...args);
118
+ const { location, style } = this.getCallerLocation();
119
+ this.originalConsole.log(location, style, ...args);
119
120
  this.handleConsoleCall('info', args);
120
121
  };
121
122
  // Override console.info
122
123
  console.info = (...args) => {
123
- this.originalConsole.info(...args);
124
+ const { location, style } = this.getCallerLocation();
125
+ this.originalConsole.info(location, style, ...args);
124
126
  this.handleConsoleCall('info', args);
125
127
  };
126
128
  // Override console.warn
127
129
  console.warn = (...args) => {
128
- this.originalConsole.warn(...args);
130
+ const { location, style } = this.getCallerLocation();
131
+ this.originalConsole.warn(location, style, ...args);
129
132
  this.handleConsoleCall('warn', args);
130
133
  };
131
134
  // Override console.error
132
135
  console.error = (...args) => {
133
- this.originalConsole.error(...args);
136
+ const { location, style } = this.getCallerLocation();
137
+ this.originalConsole.error(location, style, ...args);
134
138
  this.handleConsoleCall('error', args);
135
139
  };
136
140
  // Override console.debug
137
141
  console.debug = (...args) => {
138
- this.originalConsole.debug(...args);
142
+ const { location, style } = this.getCallerLocation();
143
+ this.originalConsole.debug(location, style, ...args);
139
144
  this.handleConsoleCall('debug', args);
140
145
  };
141
146
  }
147
+ /**
148
+ * Get caller information from stack trace.
149
+ * @returns Object with location string and CSS style, or empty values for production
150
+ */
151
+ getCallerLocation() {
152
+ const emptyResult = { location: '', style: '' };
153
+ const style = 'color: #0063A2; font-weight: bold;';
154
+ if (this.isProduction)
155
+ return emptyResult;
156
+ try {
157
+ const stack = new Error().stack;
158
+ if (!stack)
159
+ return emptyResult;
160
+ const stackLines = stack.split('\n');
161
+ // Skip the first 3 lines: Error, getCallerLocation, overrideConsoleMethods wrapper
162
+ const callerLine = stackLines[3];
163
+ if (!callerLine)
164
+ return emptyResult;
165
+ // Extract file name and line number from stack trace
166
+ // Format: "at functionName (file:line:column)" or "at file:line:column"
167
+ const match = callerLine.match(/(?:at\s+.*?\s+\()?([^/\\(]+\.(?:ts|tsx|js|jsx)):(\d+):(\d+)\)?/);
168
+ if (match) {
169
+ const [, fileName, lineNumber] = match;
170
+ return { style, location: `%c[${fileName}:${lineNumber}]` };
171
+ }
172
+ // Fallback: try to extract just the file name
173
+ const simpleMatch = callerLine.match(/([^/\\]+\.(?:ts|tsx|js|jsx))/);
174
+ if (simpleMatch) {
175
+ return { style, location: `%c[${simpleMatch[1]}]` };
176
+ }
177
+ return emptyResult;
178
+ }
179
+ catch (error) {
180
+ return emptyResult;
181
+ }
182
+ }
142
183
  /**
143
184
  * Track mouse position for screenshot context.
144
185
  */
@@ -148,7 +189,7 @@ export class Logger {
148
189
  this.mousePosition = {
149
190
  x: event.clientX,
150
191
  y: event.clientY,
151
- timestamp: new Date().toISOString()
192
+ timestamp: new Date().toISOString(),
152
193
  };
153
194
  };
154
195
  window.addEventListener('mousemove', updateMousePosition);
@@ -167,7 +208,18 @@ export class Logger {
167
208
  return;
168
209
  }
169
210
  // Convert console arguments to message and data
170
- const message = args.map(arg => typeof arg === 'object' ? JSON.stringify(arg) : String(arg)).join(' ');
211
+ const message = args
212
+ .map((arg) => {
213
+ if (typeof arg !== 'object')
214
+ return arg;
215
+ try {
216
+ return JSON.stringify(arg);
217
+ }
218
+ catch (error) {
219
+ return 'Error adding object to log: ' + error.message + ' ' + String(arg);
220
+ }
221
+ })
222
+ .join(' ');
171
223
  const data = args.length > 1 ? args.slice(1) : undefined;
172
224
  const entry = yield this.createLogEntry(level, message, data);
173
225
  this.addLogEntry(entry);
@@ -185,7 +237,7 @@ export class Logger {
185
237
  onLine: navigator.onLine,
186
238
  screenResolution: `${screen.width}x${screen.height}`,
187
239
  windowSize: `${window.innerWidth}x${window.innerHeight}`,
188
- timestamp: new Date().toISOString()
240
+ timestamp: new Date().toISOString(),
189
241
  };
190
242
  }
191
243
  /**
@@ -238,7 +290,7 @@ export class Logger {
238
290
  level,
239
291
  message,
240
292
  data,
241
- context: context
293
+ context: context,
242
294
  };
243
295
  });
244
296
  }
@@ -1,7 +1,12 @@
1
1
  import { SupabaseClient } from '@supabase/supabase-js';
2
2
  import { UserInfo } from '../core/controller/SettingsController';
3
3
  import { ActivePlugin, Plugin } from '../fromRimori/PluginTypes';
4
- import { RimoriClient } from "./RimoriClient";
4
+ import { RimoriClient } from './RimoriClient';
5
+ export interface Guild {
6
+ id: string;
7
+ longTermGoalOverride: string;
8
+ allowUserPluginSettings: boolean;
9
+ }
5
10
  export interface RimoriInfo {
6
11
  url: string;
7
12
  key: string;
@@ -10,6 +15,7 @@ export interface RimoriInfo {
10
15
  expiration: Date;
11
16
  tablePrefix: string;
12
17
  pluginId: string;
18
+ guild: Guild;
13
19
  installedPlugins: Plugin[];
14
20
  profile: UserInfo;
15
21
  mainPanelPlugin?: ActivePlugin;
@@ -9,7 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  };
10
10
  import { createClient } from '@supabase/supabase-js';
11
11
  import { EventBus } from '../fromRimori/EventBus';
12
- import { RimoriClient } from "./RimoriClient";
12
+ import { RimoriClient } from './RimoriClient';
13
13
  import { StandaloneClient } from './StandaloneClient';
14
14
  import { setTheme } from './ThemeSetter';
15
15
  import { Logger } from './Logger';
@@ -36,11 +36,15 @@ export class PluginController {
36
36
  }
37
37
  initMessageChannel(worker = false) {
38
38
  const listener = (event) => {
39
- console.log("[PluginController] window message", { origin: event.origin, data: event.data });
39
+ console.log('[PluginController] window message', { origin: event.origin, data: event.data });
40
40
  const { type, pluginId, queryParams, rimoriInfo } = event.data || {};
41
41
  const [transferredPort] = event.ports || [];
42
- if (type !== "rimori:init" || !transferredPort || pluginId !== this.pluginId) {
43
- console.log("[PluginController] message ignored (not init or wrong plugin)", { type, pluginId, hasPort: !!transferredPort });
42
+ if (type !== 'rimori:init' || !transferredPort || pluginId !== this.pluginId) {
43
+ console.log('[PluginController] message ignored (not init or wrong plugin)', {
44
+ type,
45
+ pluginId,
46
+ hasPort: !!transferredPort,
47
+ });
44
48
  return;
45
49
  }
46
50
  this.queryParams = queryParams || {};
@@ -49,7 +53,7 @@ export class PluginController {
49
53
  if (rimoriInfo) {
50
54
  this.rimoriInfo = rimoriInfo;
51
55
  this.supabase = createClient(rimoriInfo.url, rimoriInfo.key, {
52
- accessToken: () => Promise.resolve(rimoriInfo.token)
56
+ accessToken: () => Promise.resolve(rimoriInfo.token),
53
57
  });
54
58
  }
55
59
  // Handle messages from parent
@@ -75,38 +79,38 @@ export class PluginController {
75
79
  setTheme(theme);
76
80
  }
77
81
  // Forward plugin events to parent (only after MessageChannel is ready)
78
- EventBus.on("*", (ev) => {
82
+ EventBus.on('*', (ev) => {
79
83
  var _a;
80
- if (ev.sender === this.pluginId && !ev.topic.startsWith("self.")) {
84
+ if (ev.sender === this.pluginId && !ev.topic.startsWith('self.')) {
81
85
  (_a = this.port) === null || _a === void 0 ? void 0 : _a.postMessage({ event: ev });
82
86
  }
83
87
  });
84
88
  // Mark MessageChannel as ready and process pending requests
85
89
  this.isMessageChannelReady = true;
86
90
  // Process any pending requests
87
- this.pendingRequests.forEach(request => request());
91
+ this.pendingRequests.forEach((request) => request());
88
92
  this.pendingRequests = [];
89
93
  };
90
94
  if (worker) {
91
95
  self.onmessage = listener;
92
96
  }
93
97
  else {
94
- window.addEventListener("message", listener);
98
+ window.addEventListener('message', listener);
95
99
  }
96
100
  this.sendHello(worker);
97
101
  }
98
102
  sendHello(isWorker = false) {
99
103
  try {
100
- const payload = { type: "rimori:hello", pluginId: this.pluginId };
104
+ const payload = { type: 'rimori:hello', pluginId: this.pluginId };
101
105
  if (isWorker) {
102
106
  self.postMessage(payload);
103
107
  }
104
108
  else {
105
- window.parent.postMessage(payload, "*");
109
+ window.parent.postMessage(payload, '*');
106
110
  }
107
111
  }
108
112
  catch (e) {
109
- console.error("[PluginController] Error sending hello:", e);
113
+ console.error('[PluginController] Error sending hello:', e);
110
114
  }
111
115
  }
112
116
  static getInstance(pluginId_1) {
@@ -118,7 +122,7 @@ export class PluginController {
118
122
  PluginController.instance = new PluginController(pluginId, standalone);
119
123
  PluginController.client = yield RimoriClient.getInstance(PluginController.instance);
120
124
  //only init logger in workers and on main plugin pages
121
- if (PluginController.instance.getQueryParam("applicationMode") !== "sidebar") {
125
+ if (PluginController.instance.getQueryParam('applicationMode') !== 'sidebar') {
122
126
  Logger.getInstance(PluginController.client);
123
127
  }
124
128
  }
@@ -159,8 +163,8 @@ export class PluginController {
159
163
  sender: this.pluginId,
160
164
  topic: 'global.supabase.requestAccess',
161
165
  data: {},
162
- debug: false
163
- }
166
+ debug: false,
167
+ },
164
168
  };
165
169
  return new Promise((resolve) => {
166
170
  // Listen for the response
@@ -170,7 +174,7 @@ export class PluginController {
170
174
  if (((_a = event.data) === null || _a === void 0 ? void 0 : _a.topic) === 'global.supabase.requestAccess' && ((_b = event.data) === null || _b === void 0 ? void 0 : _b.eventId) === eventId) {
171
175
  this.rimoriInfo = event.data.data;
172
176
  this.supabase = createClient(this.rimoriInfo.url, this.rimoriInfo.key, {
173
- accessToken: () => Promise.resolve(this.getToken())
177
+ accessToken: () => Promise.resolve(this.getToken()),
174
178
  });
175
179
  self.onmessage = originalOnMessage; // Restore original handler
176
180
  resolve({ supabase: this.supabase, info: this.rimoriInfo });
@@ -185,10 +189,11 @@ export class PluginController {
185
189
  }
186
190
  else {
187
191
  // In main thread context, use EventBus
188
- const { data } = yield EventBus.request(this.pluginId, "global.supabase.requestAccess");
192
+ const { data } = yield EventBus.request(this.pluginId, 'global.supabase.requestAccess');
193
+ console.log({ data });
189
194
  this.rimoriInfo = data;
190
195
  this.supabase = createClient(this.rimoriInfo.url, this.rimoriInfo.key, {
191
- accessToken: () => Promise.resolve(this.getToken())
196
+ accessToken: () => Promise.resolve(this.getToken()),
192
197
  });
193
198
  }
194
199
  }
@@ -202,12 +207,12 @@ export class PluginController {
202
207
  }
203
208
  // If we don't have rimoriInfo, request it
204
209
  if (!this.rimoriInfo) {
205
- const { data } = yield EventBus.request(this.pluginId, "global.supabase.requestAccess");
210
+ const { data } = yield EventBus.request(this.pluginId, 'global.supabase.requestAccess');
206
211
  this.rimoriInfo = data;
207
212
  return this.rimoriInfo.token;
208
213
  }
209
214
  // If token is expired, request fresh access
210
- const { data } = yield EventBus.request(this.pluginId, "global.supabase.requestAccess");
215
+ const { data } = yield EventBus.request(this.pluginId, 'global.supabase.requestAccess');
211
216
  this.rimoriInfo.token = data.token;
212
217
  this.rimoriInfo.expiration = data.expiration;
213
218
  return this.rimoriInfo.token;
@@ -220,27 +225,27 @@ export class PluginController {
220
225
  */
221
226
  getSupabaseUrl() {
222
227
  if (!this.rimoriInfo) {
223
- throw new Error("Supabase info not found");
228
+ throw new Error('Supabase info not found');
224
229
  }
225
230
  return this.rimoriInfo.url;
226
231
  }
227
232
  getBackendUrl() {
228
233
  if (!this.rimoriInfo) {
229
- throw new Error("Rimori info not found");
234
+ throw new Error('Rimori info not found');
230
235
  }
231
236
  return this.rimoriInfo.backendUrl;
232
237
  }
233
238
  getGlobalEventTopic(preliminaryTopic) {
234
239
  var _a, _b;
235
- if (preliminaryTopic.startsWith("global.")) {
240
+ if (preliminaryTopic.startsWith('global.')) {
236
241
  return preliminaryTopic;
237
242
  }
238
- if (preliminaryTopic.startsWith("self.")) {
243
+ if (preliminaryTopic.startsWith('self.')) {
239
244
  return preliminaryTopic;
240
245
  }
241
- const topicParts = preliminaryTopic.split(".");
246
+ const topicParts = preliminaryTopic.split('.');
242
247
  if (topicParts.length === 3) {
243
- if (!topicParts[0].startsWith("pl") && topicParts[0] !== "global") {
248
+ if (!topicParts[0].startsWith('pl') && topicParts[0] !== 'global') {
244
249
  throw new Error("The event topic must start with the plugin id or 'global'.");
245
250
  }
246
251
  return preliminaryTopic;
@@ -248,7 +253,7 @@ export class PluginController {
248
253
  else if (topicParts.length > 3) {
249
254
  throw new Error(`The event topic must consist of 3 parts. <pluginId>.<topic area>.<action>. Received: ${preliminaryTopic}`);
250
255
  }
251
- const topicRoot = (_b = (_a = this.rimoriInfo) === null || _a === void 0 ? void 0 : _a.pluginId) !== null && _b !== void 0 ? _b : "global";
256
+ const topicRoot = (_b = (_a = this.rimoriInfo) === null || _a === void 0 ? void 0 : _a.pluginId) !== null && _b !== void 0 ? _b : 'global';
252
257
  return `${topicRoot}.${preliminaryTopic}`;
253
258
  }
254
259
  }
@@ -1,13 +1,14 @@
1
- import { PostgrestQueryBuilder } from "@supabase/postgrest-js";
2
- import { GenericSchema } from "@supabase/supabase-js/dist/module/lib/types";
3
- import { Message, OnLLMResponse } from "../core/controller/AIController";
4
- import { ObjectRequest } from "../core/controller/ObjectController";
5
- import { UserInfo } from "../core/controller/SettingsController";
6
- import { SharedContent, SharedContentFilter, SharedContentObjectRequest } from "../core/controller/SharedContentController";
7
- import { EventBusMessage, EventHandler, EventPayload } from "../fromRimori/EventBus";
8
- import { ActivePlugin, MainPanelAction, Plugin, Tool } from "../fromRimori/PluginTypes";
9
- import { AccomplishmentPayload } from "./AccomplishmentHandler";
10
- import { PluginController } from "./PluginController";
1
+ import { PostgrestQueryBuilder } from '@supabase/postgrest-js';
2
+ import { GenericSchema } from '@supabase/supabase-js/dist/module/lib/types';
3
+ import { Message, OnLLMResponse } from '../core/controller/AIController';
4
+ import { ObjectRequest } from '../core/controller/ObjectController';
5
+ import { UserInfo } from '../core/controller/SettingsController';
6
+ import { SharedContent, SharedContentFilter, SharedContentObjectRequest } from '../core/controller/SharedContentController';
7
+ import { CreateExerciseParams } from '../core/controller/ExerciseController';
8
+ import { EventBusMessage, EventHandler, EventPayload } from '../fromRimori/EventBus';
9
+ import { ActivePlugin, MainPanelAction, Plugin, Tool } from '../fromRimori/PluginTypes';
10
+ import { AccomplishmentPayload } from './AccomplishmentHandler';
11
+ import { PluginController } from './PluginController';
11
12
  interface Db {
12
13
  from: {
13
14
  <TableName extends string & keyof GenericSchema['Tables'], Table extends GenericSchema['Tables'][TableName]>(relation: TableName): PostgrestQueryBuilder<GenericSchema, Table, TableName>;
@@ -63,6 +64,7 @@ export declare class RimoriClient {
63
64
  private pluginController;
64
65
  private settingsController;
65
66
  private sharedContentController;
67
+ private exerciseController;
66
68
  private accomplishmentHandler;
67
69
  private rimoriInfo;
68
70
  plugin: PluginInterface;
@@ -202,10 +204,10 @@ export declare class RimoriClient {
202
204
  */
203
205
  update: <T = any>(id: string, content: Partial<SharedContent<T>>) => Promise<SharedContent<T>>;
204
206
  /**
205
- * Complete a shared content item.
206
- * @param contentType The type of shared content to complete. E.g. assignments, exercises, etc.
207
- * @param assignmentId The id of the shared content item to complete.
208
- */
207
+ * Complete a shared content item.
208
+ * @param contentType The type of shared content to complete. E.g. assignments, exercises, etc.
209
+ * @param assignmentId The id of the shared content item to complete.
210
+ */
209
211
  complete: (contentType: string, assignmentId: string) => Promise<void>;
210
212
  /**
211
213
  /**
@@ -227,5 +229,28 @@ export declare class RimoriClient {
227
229
  remove: (id: string) => Promise<SharedContent<any>>;
228
230
  };
229
231
  };
232
+ exercise: {
233
+ /**
234
+ * Fetches weekly exercises from the weekly_exercises view.
235
+ * Shows exercises for the current week that haven't expired.
236
+ * @returns Array of exercise objects.
237
+ */
238
+ view: () => Promise<import("../core/controller/ExerciseController").Exercise[]>;
239
+ /**
240
+ * Creates a new exercise via the backend API.
241
+ * @param params Exercise creation parameters.
242
+ * @returns Created exercise object.
243
+ */
244
+ add: (params: CreateExerciseParams) => Promise<import("../core/controller/ExerciseController").Exercise>;
245
+ /**
246
+ * Deletes an exercise via the backend API.
247
+ * @param id The exercise ID to delete.
248
+ * @returns Success status.
249
+ */
250
+ delete: (id: string) => Promise<{
251
+ success: boolean;
252
+ message: string;
253
+ }>;
254
+ };
230
255
  }
231
256
  export {};