@rimori/client 1.4.0 → 1.4.4

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 (133) hide show
  1. package/README.md +77 -71
  2. package/dist/cli/scripts/init/dev-registration.d.ts +1 -1
  3. package/dist/cli/scripts/init/dev-registration.js +4 -4
  4. package/dist/cli/scripts/init/main.js +1 -1
  5. package/dist/cli/scripts/init/package-setup.d.ts +1 -1
  6. package/dist/cli/scripts/init/package-setup.js +3 -3
  7. package/dist/cli/scripts/init/router-transformer.js +19 -12
  8. package/dist/cli/scripts/init/vite-config.d.ts +2 -2
  9. package/dist/cli/scripts/init/vite-config.js +2 -2
  10. package/dist/cli/scripts/release/release-config-upload.js +9 -9
  11. package/dist/cli/scripts/release/release-db-update.d.ts +1 -1
  12. package/dist/cli/scripts/release/release-db-update.js +9 -9
  13. package/dist/cli/scripts/release/release-file-upload.js +1 -1
  14. package/dist/cli/scripts/release/release.js +2 -2
  15. package/dist/components/CRUDModal.d.ts +1 -1
  16. package/dist/components/CRUDModal.js +3 -3
  17. package/dist/components/MarkdownEditor.js +16 -16
  18. package/dist/components/Spinner.js +2 -2
  19. package/dist/components/ai/Assistant.js +7 -8
  20. package/dist/components/ai/Avatar.d.ts +2 -2
  21. package/dist/components/ai/Avatar.js +10 -5
  22. package/dist/components/ai/EmbeddedAssistent/AudioInputField.js +5 -6
  23. package/dist/components/ai/EmbeddedAssistent/CircleAudioAvatar.d.ts +1 -1
  24. package/dist/components/ai/EmbeddedAssistent/CircleAudioAvatar.js +1 -2
  25. package/dist/components/ai/EmbeddedAssistent/TTS/MessageSender.d.ts +1 -2
  26. package/dist/components/ai/EmbeddedAssistent/TTS/MessageSender.js +4 -2
  27. package/dist/components/ai/EmbeddedAssistent/VoiceRecoder.js +2 -3
  28. package/dist/components/audio/Playbutton.js +10 -7
  29. package/dist/components/components/ContextMenu.d.ts +1 -1
  30. package/dist/components/components/ContextMenu.js +19 -16
  31. package/dist/components.d.ts +10 -10
  32. package/dist/components.js +10 -10
  33. package/dist/core/controller/AIController.d.ts +2 -2
  34. package/dist/core/controller/AIController.js +12 -12
  35. package/dist/core/controller/ExerciseController.d.ts +2 -2
  36. package/dist/core/controller/ExerciseController.js +2 -2
  37. package/dist/core/controller/ObjectController.js +5 -5
  38. package/dist/core/controller/SettingsController.d.ts +22 -7
  39. package/dist/core/controller/SettingsController.js +73 -8
  40. package/dist/core/controller/SharedContentController.d.ts +3 -3
  41. package/dist/core/controller/SharedContentController.js +38 -20
  42. package/dist/core/controller/VoiceController.js +6 -4
  43. package/dist/core/core.d.ts +15 -15
  44. package/dist/core/core.js +7 -7
  45. package/dist/fromRimori/EventBus.js +23 -23
  46. package/dist/fromRimori/PluginTypes.d.ts +4 -4
  47. package/dist/hooks/UseChatHook.d.ts +3 -3
  48. package/dist/hooks/UseChatHook.js +9 -3
  49. package/dist/index.d.ts +10 -10
  50. package/dist/index.js +9 -9
  51. package/dist/plugin/AccomplishmentHandler.d.ts +5 -5
  52. package/dist/plugin/AccomplishmentHandler.js +31 -27
  53. package/dist/plugin/AudioController.d.ts +1 -1
  54. package/dist/plugin/AudioController.js +6 -6
  55. package/dist/plugin/Logger.js +15 -13
  56. package/dist/plugin/PluginController.d.ts +7 -1
  57. package/dist/plugin/PluginController.js +32 -27
  58. package/dist/plugin/RimoriClient.d.ts +17 -18
  59. package/dist/plugin/RimoriClient.js +31 -31
  60. package/dist/plugin/StandaloneClient.d.ts +1 -1
  61. package/dist/plugin/StandaloneClient.js +35 -16
  62. package/dist/plugin/ThemeSetter.js +4 -4
  63. package/dist/providers/PluginProvider.js +44 -14
  64. package/dist/utils/Language.js +57 -57
  65. package/dist/utils/PluginUtils.js +3 -3
  66. package/dist/utils/difficultyConverter.d.ts +1 -1
  67. package/dist/utils/difficultyConverter.js +1 -1
  68. package/dist/utils/endpoint.js +2 -2
  69. package/dist/worker/WorkerSetup.d.ts +1 -1
  70. package/dist/worker/WorkerSetup.js +6 -6
  71. package/example/docs/devdocs.md +50 -40
  72. package/example/docs/overview.md +1 -1
  73. package/example/docs/userdocs.md +4 -1
  74. package/example/rimori.config.ts +51 -49
  75. package/example/worker/vite.config.ts +3 -3
  76. package/example/worker/worker.ts +2 -2
  77. package/package.json +14 -8
  78. package/prettier.config.js +1 -1
  79. package/src/cli/scripts/init/dev-registration.ts +5 -8
  80. package/src/cli/scripts/init/env-setup.ts +1 -1
  81. package/src/cli/scripts/init/file-operations.ts +1 -1
  82. package/src/cli/scripts/init/html-cleaner.ts +2 -5
  83. package/src/cli/scripts/init/main.ts +16 -13
  84. package/src/cli/scripts/init/package-setup.ts +11 -15
  85. package/src/cli/scripts/init/router-transformer.ts +40 -37
  86. package/src/cli/scripts/init/tailwind-config.ts +17 -26
  87. package/src/cli/scripts/init/vite-config.ts +3 -3
  88. package/src/cli/scripts/release/release-config-upload.ts +11 -11
  89. package/src/cli/scripts/release/release-db-update.ts +12 -12
  90. package/src/cli/scripts/release/release-file-upload.ts +2 -2
  91. package/src/cli/scripts/release/release.ts +4 -4
  92. package/src/cli/types/DatabaseTypes.ts +2 -10
  93. package/src/components/CRUDModal.tsx +64 -48
  94. package/src/components/MarkdownEditor.tsx +58 -27
  95. package/src/components/Spinner.tsx +24 -17
  96. package/src/components/ai/Assistant.tsx +70 -70
  97. package/src/components/ai/Avatar.tsx +17 -14
  98. package/src/components/ai/EmbeddedAssistent/AudioInputField.tsx +63 -54
  99. package/src/components/ai/EmbeddedAssistent/CircleAudioAvatar.tsx +14 -5
  100. package/src/components/ai/EmbeddedAssistent/TTS/MessageSender.ts +75 -74
  101. package/src/components/ai/EmbeddedAssistent/TTS/Player.ts +3 -4
  102. package/src/components/ai/EmbeddedAssistent/VoiceRecoder.tsx +109 -94
  103. package/src/components/ai/utils.ts +4 -4
  104. package/src/components/audio/Playbutton.tsx +101 -93
  105. package/src/components/components/ContextMenu.tsx +47 -35
  106. package/src/components.ts +10 -10
  107. package/src/core/controller/AIController.ts +29 -19
  108. package/src/core/controller/ExerciseController.ts +16 -23
  109. package/src/core/controller/ObjectController.ts +15 -10
  110. package/src/core/controller/SettingsController.ts +89 -16
  111. package/src/core/controller/SharedContentController.ts +80 -44
  112. package/src/core/controller/VoiceController.ts +10 -8
  113. package/src/core/core.ts +15 -16
  114. package/src/fromRimori/EventBus.ts +76 -47
  115. package/src/fromRimori/PluginTypes.ts +26 -17
  116. package/src/fromRimori/readme.md +2 -2
  117. package/src/hooks/UseChatHook.ts +25 -15
  118. package/src/index.ts +10 -10
  119. package/src/plugin/AccomplishmentHandler.ts +53 -35
  120. package/src/plugin/AudioController.ts +18 -12
  121. package/src/plugin/Logger.ts +28 -21
  122. package/src/plugin/PluginController.ts +60 -44
  123. package/src/plugin/RimoriClient.ts +102 -72
  124. package/src/plugin/StandaloneClient.ts +51 -24
  125. package/src/plugin/ThemeSetter.ts +5 -5
  126. package/src/providers/PluginProvider.tsx +90 -36
  127. package/src/style.scss +3 -3
  128. package/src/utils/Language.ts +58 -58
  129. package/src/utils/PluginUtils.ts +16 -20
  130. package/src/utils/difficultyConverter.ts +2 -2
  131. package/src/utils/endpoint.ts +3 -2
  132. package/src/worker/WorkerSetup.ts +8 -9
  133. package/tsconfig.json +2 -4
@@ -1,23 +1,30 @@
1
- import { PostgrestQueryBuilder } from "@supabase/postgrest-js";
2
- import { SupabaseClient } from "@supabase/supabase-js";
3
- import { GenericSchema } from "@supabase/supabase-js/dist/module/lib/types";
4
- import { generateText, Message, OnLLMResponse, streamChatGPT } from "../core/controller/AIController";
5
- import { generateObject, ObjectRequest } from "../core/controller/ObjectController";
6
- import { SettingsController, UserInfo } from "../core/controller/SettingsController";
7
- import { SharedContent, SharedContentController, SharedContentFilter, SharedContentObjectRequest } from "../core/controller/SharedContentController";
8
- import { getSTTResponse, getTTSResponse } from "../core/controller/VoiceController";
9
- import { ExerciseController, CreateExerciseParams } from "../core/controller/ExerciseController";
10
- import { EventBus, EventBusMessage, EventHandler, EventPayload } from "../fromRimori/EventBus";
11
- import { ActivePlugin, MainPanelAction, Plugin, Tool } from "../fromRimori/PluginTypes";
12
- import { AccomplishmentHandler, AccomplishmentPayload } from "./AccomplishmentHandler";
13
- import { PluginController, RimoriInfo } from "./PluginController";
14
- import { ClientServerOptions } from "@supabase/postgrest-js/dist/cjs/types/common/common";
15
-
1
+ import { PostgrestQueryBuilder } from '@supabase/postgrest-js';
2
+ import { SupabaseClient } from '@supabase/supabase-js';
3
+ import { GenericSchema } from '@supabase/supabase-js/dist/module/lib/types';
4
+ import { generateText, Message, OnLLMResponse, streamChatGPT } from '../core/controller/AIController';
5
+ import { generateObject, ObjectRequest } from '../core/controller/ObjectController';
6
+ import { SettingsController, UserInfo } from '../core/controller/SettingsController';
7
+ import {
8
+ SharedContent,
9
+ SharedContentController,
10
+ SharedContentFilter,
11
+ SharedContentObjectRequest,
12
+ } from '../core/controller/SharedContentController';
13
+ import { getSTTResponse, getTTSResponse } from '../core/controller/VoiceController';
14
+ import { ExerciseController, CreateExerciseParams } from '../core/controller/ExerciseController';
15
+ import { EventBus, EventBusMessage, EventHandler, EventPayload } from '../fromRimori/EventBus';
16
+ import { ActivePlugin, MainPanelAction, Plugin, Tool } from '../fromRimori/PluginTypes';
17
+ import { AccomplishmentHandler, AccomplishmentPayload } from './AccomplishmentHandler';
18
+ import { PluginController, RimoriInfo } from './PluginController';
16
19
 
17
20
  interface Db {
18
21
  from: {
19
- <TableName extends string & keyof GenericSchema['Tables'], Table extends GenericSchema['Tables'][TableName]>(relation: TableName): PostgrestQueryBuilder<ClientServerOptions, GenericSchema, Table, TableName>;
20
- <ViewName extends string & keyof GenericSchema['Views'], View extends GenericSchema['Views'][ViewName]>(relation: ViewName): PostgrestQueryBuilder<ClientServerOptions, GenericSchema, View, ViewName>;
22
+ <TableName extends string & keyof GenericSchema['Tables'], Table extends GenericSchema['Tables'][TableName]>(
23
+ relation: TableName,
24
+ ): PostgrestQueryBuilder<GenericSchema, Table, TableName>;
25
+ <ViewName extends string & keyof GenericSchema['Views'], View extends GenericSchema['Views'][ViewName]>(
26
+ relation: ViewName,
27
+ ): PostgrestQueryBuilder<GenericSchema, View, ViewName>;
21
28
  };
22
29
  // storage: SupabaseClient["storage"];
23
30
 
@@ -42,7 +49,7 @@ interface PluginInterface {
42
49
  * Get the settings for the plugin. T can be any type of settings, UserSettings or SystemSettings.
43
50
  * @param defaultSettings The default settings to use if no settings are found.
44
51
  * @param genericSettings The type of settings to get.
45
- * @returns The settings for the plugin.
52
+ * @returns The settings for the plugin.
46
53
  */
47
54
  getSettings: <T extends object>(defaultSettings: T) => Promise<T>;
48
55
  /**
@@ -55,15 +62,15 @@ interface PluginInterface {
55
62
  /**
56
63
  * All installed plugins.
57
64
  */
58
- installedPlugins: Plugin[],
65
+ installedPlugins: Plugin[];
59
66
  /**
60
67
  * The plugin that is loaded in the main panel.
61
68
  */
62
- mainPanelPlugin?: ActivePlugin,
69
+ mainPanelPlugin?: ActivePlugin;
63
70
  /**
64
71
  * The plugin that is loaded in the side panel.
65
72
  */
66
- sidePanelPlugin?: ActivePlugin,
73
+ sidePanelPlugin?: ActivePlugin;
67
74
  };
68
75
  getUserInfo: () => UserInfo;
69
76
  }
@@ -84,7 +91,7 @@ export class RimoriClient {
84
91
  this.rimoriInfo = info;
85
92
  this.superbase = supabase;
86
93
  this.pluginController = pluginController;
87
- this.settingsController = new SettingsController(supabase, info.pluginId);
94
+ this.settingsController = new SettingsController(supabase, info.pluginId, info.guild);
88
95
  this.sharedContentController = new SharedContentController(this.superbase, this);
89
96
  this.exerciseController = new ExerciseController(supabase, pluginController);
90
97
  this.accomplishmentHandler = new AccomplishmentHandler(info.pluginId);
@@ -98,7 +105,7 @@ export class RimoriClient {
98
105
  // functions: this.superbase.functions,
99
106
  tablePrefix: info.tablePrefix,
100
107
  getTableName: this.getTableName,
101
- }
108
+ };
102
109
  this.plugin = {
103
110
  pluginId: info.pluginId,
104
111
  setSettings: async (settings: any) => {
@@ -115,14 +122,14 @@ export class RimoriClient {
115
122
  installedPlugins: this.rimoriInfo.installedPlugins,
116
123
  mainPanelPlugin: this.rimoriInfo.mainPanelPlugin,
117
124
  sidePanelPlugin: this.rimoriInfo.sidePanelPlugin,
118
- }
119
- }
120
- }
125
+ };
126
+ },
127
+ };
121
128
  }
122
129
 
123
130
  public event = {
124
131
  /**
125
- * Emit an event to Rimori or a plugin.
132
+ * Emit an event to Rimori or a plugin.
126
133
  * The topic schema is:
127
134
  * {pluginId}.{eventId}
128
135
  * Check out the event bus documentation for more information.
@@ -153,7 +160,10 @@ export class RimoriClient {
153
160
  */
154
161
  on: <T = EventPayload>(topic: string | string[], callback: EventHandler<T>) => {
155
162
  const topics = Array.isArray(topic) ? topic : [topic];
156
- return EventBus.on<T>(topics.map(t => this.pluginController.getGlobalEventTopic(t)), callback);
163
+ return EventBus.on<T>(
164
+ topics.map((t) => this.pluginController.getGlobalEventTopic(t)),
165
+ callback,
166
+ );
157
167
  },
158
168
  /**
159
169
  * Subscribe to an event once.
@@ -168,9 +178,16 @@ export class RimoriClient {
168
178
  * @param topic The topic to respond to.
169
179
  * @param data The data to respond with.
170
180
  */
171
- respond: <T = EventPayload>(topic: string | string[], data: EventPayload | ((data: EventBusMessage<T>) => EventPayload | Promise<EventPayload>)) => {
181
+ respond: <T = EventPayload>(
182
+ topic: string | string[],
183
+ data: EventPayload | ((data: EventBusMessage<T>) => EventPayload | Promise<EventPayload>),
184
+ ) => {
172
185
  const topics = Array.isArray(topic) ? topic : [topic];
173
- EventBus.respond(this.plugin.pluginId, topics.map(t => this.pluginController.getGlobalEventTopic(t)), data);
186
+ EventBus.respond(
187
+ this.plugin.pluginId,
188
+ topics.map((t) => this.pluginController.getGlobalEventTopic(t)),
189
+ data,
190
+ );
174
191
  },
175
192
  /**
176
193
  * Emit an accomplishment.
@@ -184,7 +201,10 @@ export class RimoriClient {
184
201
  * @param accomplishmentTopic The topic to subscribe to.
185
202
  * @param callback The callback to call when the accomplishment is emitted.
186
203
  */
187
- onAccomplishment: (accomplishmentTopic: string, callback: (payload: EventBusMessage<AccomplishmentPayload>) => void) => {
204
+ onAccomplishment: (
205
+ accomplishmentTopic: string,
206
+ callback: (payload: EventBusMessage<AccomplishmentPayload>) => void,
207
+ ) => {
188
208
  this.accomplishmentHandler.subscribe(accomplishmentTopic, callback);
189
209
  },
190
210
  /**
@@ -194,21 +214,21 @@ export class RimoriClient {
194
214
  * @param text Optional text to be used for the action like for example text that the translator would look up.
195
215
  */
196
216
  emitSidebarAction: (pluginId: string, actionKey: string, text?: string) => {
197
- this.event.emit("global.sidebar.triggerAction", { plugin_id: pluginId, action_key: actionKey, text });
217
+ this.event.emit('global.sidebar.triggerAction', { plugin_id: pluginId, action_key: actionKey, text });
198
218
  },
199
219
 
200
220
  onMainPanelAction: (callback: (data: MainPanelAction) => void) => {
201
221
  // this needs to be a emit and on because the main panel action is triggered by the user and not by the plugin
202
- this.event.emit("action.requestMain")
203
- this.event.on<MainPanelAction>("action.requestMain", ({ data }) => callback(data));
204
- }
205
- }
222
+ this.event.emit('action.requestMain');
223
+ this.event.on<MainPanelAction>('action.requestMain', ({ data }) => callback(data));
224
+ },
225
+ };
206
226
 
207
227
  public navigation = {
208
228
  toDashboard: () => {
209
- this.event.emit("global.navigation.triggerToDashboard");
210
- }
211
- }
229
+ this.event.emit('global.navigation.triggerToDashboard');
230
+ },
231
+ };
212
232
 
213
233
  /**
214
234
  * Get a query parameter value that was passed via MessageChannel
@@ -229,36 +249,37 @@ export class RimoriClient {
229
249
 
230
250
  private from<
231
251
  TableName extends string & keyof GenericSchema['Tables'],
232
- Table extends GenericSchema['Tables'][TableName]
233
- >(relation: TableName): PostgrestQueryBuilder<ClientServerOptions, GenericSchema, Table, TableName>
234
- private from<
235
- ViewName extends string & keyof GenericSchema['Views'],
236
- View extends GenericSchema['Views'][ViewName]
237
- >(relation: ViewName): PostgrestQueryBuilder<ClientServerOptions, GenericSchema, View, ViewName>
238
- private from(relation: string): PostgrestQueryBuilder<ClientServerOptions, GenericSchema, any, any> {
252
+ Table extends GenericSchema['Tables'][TableName],
253
+ >(relation: TableName): PostgrestQueryBuilder<GenericSchema, Table, TableName>;
254
+ private from<ViewName extends string & keyof GenericSchema['Views'], View extends GenericSchema['Views'][ViewName]>(
255
+ relation: ViewName,
256
+ ): PostgrestQueryBuilder<GenericSchema, View, ViewName>;
257
+ private from(relation: string): PostgrestQueryBuilder<GenericSchema, any, any> {
239
258
  return this.superbase.from(this.getTableName(relation));
240
259
  }
241
260
 
242
261
  private getTableName(table: string) {
243
262
  if (/[A-Z]/.test(table)) {
244
- throw new Error("Table name cannot include uppercase letters. Please use snake_case for table names.");
263
+ throw new Error('Table name cannot include uppercase letters. Please use snake_case for table names.');
245
264
  }
246
- if (table.startsWith("global_")) {
247
- return table.replace("global_", "");
265
+ if (table.startsWith('global_')) {
266
+ return table.replace('global_', '');
248
267
  }
249
- return this.db.tablePrefix + "_" + table;
268
+ return this.db.tablePrefix + '_' + table;
250
269
  }
251
270
 
252
271
  public ai = {
253
272
  getText: async (messages: Message[], tools?: Tool[]): Promise<string> => {
254
273
  const token = await this.pluginController.getToken();
255
- return generateText(this.pluginController.getBackendUrl(), messages, tools || [], token).then(({ messages }) => messages[0].content[0].text);
274
+ return generateText(this.pluginController.getBackendUrl(), messages, tools || [], token).then(
275
+ ({ messages }) => messages[0].content[0].text,
276
+ );
256
277
  },
257
278
  getSteamedText: async (messages: Message[], onMessage: OnLLMResponse, tools?: Tool[]) => {
258
279
  const token = await this.pluginController.getToken();
259
280
  streamChatGPT(this.pluginController.getBackendUrl(), messages, tools || [], onMessage, token);
260
281
  },
261
- getVoice: async (text: string, voice = "alloy", speed = 1, language?: string): Promise<Blob> => {
282
+ getVoice: async (text: string, voice = 'alloy', speed = 1, language?: string): Promise<Blob> => {
262
283
  const token = await this.pluginController.getToken();
263
284
  return getTTSResponse(this.pluginController.getBackendUrl(), { input: text, voice, speed, language }, token);
264
285
  },
@@ -271,7 +292,7 @@ export class RimoriClient {
271
292
  return generateObject(this.pluginController.getBackendUrl(), request, token);
272
293
  },
273
294
  // getSteamedObject: this.generateObjectStream,
274
- }
295
+ };
275
296
 
276
297
  public runtime = {
277
298
  fetchBackend: async (url: string, options: RequestInit) => {
@@ -280,11 +301,11 @@ export class RimoriClient {
280
301
  ...options,
281
302
  headers: {
282
303
  ...options.headers,
283
- 'Authorization': `Bearer ${token}`
284
- }
304
+ Authorization: `Bearer ${token}`,
305
+ },
285
306
  });
286
- }
287
- }
307
+ },
308
+ };
288
309
 
289
310
  public community = {
290
311
  /**
@@ -309,7 +330,11 @@ export class RimoriClient {
309
330
  * @param limit The optional limit for the number of results.
310
331
  * @returns The list of shared content items.
311
332
  */
312
- getList: async <T = any>(contentType: string, filter?: SharedContentFilter, limit?: number): Promise<SharedContent<T>[]> => {
333
+ getList: async <T = any>(
334
+ contentType: string,
335
+ filter?: SharedContentFilter,
336
+ limit?: number,
337
+ ): Promise<SharedContent<T>[]> => {
313
338
  return await this.sharedContentController.getSharedContentList(contentType, filter, limit);
314
339
  },
315
340
  /**
@@ -330,7 +355,12 @@ export class RimoriClient {
330
355
  filter?: SharedContentFilter,
331
356
  options?: { privateTopic?: boolean; skipDbSave?: boolean; alwaysGenerateNew?: boolean; excludeIds?: string[] },
332
357
  ): Promise<SharedContent<T>> => {
333
- return await this.sharedContentController.getNewSharedContent(contentType, generatorInstructions, filter, options);
358
+ return await this.sharedContentController.getNewSharedContent(
359
+ contentType,
360
+ generatorInstructions,
361
+ filter,
362
+ options,
363
+ );
334
364
  },
335
365
  /**
336
366
  * Create a new shared content item.
@@ -350,10 +380,10 @@ export class RimoriClient {
350
380
  return await this.sharedContentController.updateSharedContent(id, content);
351
381
  },
352
382
  /**
353
- * Complete a shared content item.
354
- * @param contentType The type of shared content to complete. E.g. assignments, exercises, etc.
355
- * @param assignmentId The id of the shared content item to complete.
356
- */
383
+ * Complete a shared content item.
384
+ * @param contentType The type of shared content to complete. E.g. assignments, exercises, etc.
385
+ * @param assignmentId The id of the shared content item to complete.
386
+ */
357
387
  complete: async (contentType: string, assignmentId: string) => {
358
388
  return await this.sharedContentController.completeSharedContent(contentType, assignmentId);
359
389
  },
@@ -363,11 +393,11 @@ export class RimoriClient {
363
393
  * Useful for marking content as completed, ongoing, hidden, liked, disliked, or bookmarked.
364
394
  */
365
395
  updateState: async (params: {
366
- contentType: string
367
- id: string
368
- state?: 'completed' | 'ongoing' | 'hidden'
369
- reaction?: 'liked' | 'disliked' | null
370
- bookmarked?: boolean
396
+ contentType: string;
397
+ id: string;
398
+ state?: 'completed' | 'ongoing' | 'hidden';
399
+ reaction?: 'liked' | 'disliked' | null;
400
+ bookmarked?: boolean;
371
401
  }): Promise<void> => {
372
402
  return await this.sharedContentController.updateSharedContentState(params);
373
403
  },
@@ -378,9 +408,9 @@ export class RimoriClient {
378
408
  */
379
409
  remove: async (id: string): Promise<SharedContent<any>> => {
380
410
  return await this.sharedContentController.removeSharedContent(id);
381
- }
382
- }
383
- }
411
+ },
412
+ },
413
+ };
384
414
 
385
415
  public exercise = {
386
416
  /**
@@ -409,5 +439,5 @@ export class RimoriClient {
409
439
  delete: async (id: string) => {
410
440
  return this.exerciseController.deleteExercise(id);
411
441
  },
412
- }
442
+ };
413
443
  }
@@ -1,11 +1,11 @@
1
- import { createClient, SupabaseClient } from "@supabase/supabase-js";
2
- import { EventBus } from "../fromRimori/EventBus";
3
- import { DEFAULT_ANON_KEY, DEFAULT_ENDPOINT } from "../utils/endpoint";
1
+ import { createClient, SupabaseClient } from '@supabase/supabase-js';
2
+ import { EventBus } from '../fromRimori/EventBus';
3
+ import { DEFAULT_ANON_KEY, DEFAULT_ENDPOINT } from '../utils/endpoint';
4
4
 
5
5
  export interface StandaloneConfig {
6
- url: string,
7
- key: string,
8
- backendUrl?: string
6
+ url: string;
7
+ key: string;
8
+ backendUrl?: string;
9
9
  }
10
10
 
11
11
  export class StandaloneClient {
@@ -20,9 +20,11 @@ export class StandaloneClient {
20
20
 
21
21
  public static async getInstance(): Promise<StandaloneClient> {
22
22
  if (!StandaloneClient.instance) {
23
- const config = await fetch("https://app.rimori.se/config.json").then(res => res.json()).catch(err => {
24
- console.warn("Error fetching config.json, using default values", err);
25
- });
23
+ const config = await fetch('https://app.rimori.se/config.json')
24
+ .then((res) => res.json())
25
+ .catch((err) => {
26
+ console.warn('Error fetching config.json, using default values', err);
27
+ });
26
28
  StandaloneClient.instance = new StandaloneClient({
27
29
  url: config?.SUPABASE_URL || DEFAULT_ENDPOINT,
28
30
  key: config?.SUPABASE_ANON_KEY || DEFAULT_ANON_KEY,
@@ -44,33 +46,54 @@ export class StandaloneClient {
44
46
  public async login(email: string, password: string) {
45
47
  const { error } = await this.supabase.auth.signInWithPassword({ email, password });
46
48
  if (error) {
47
- console.error("Login failed:", error);
49
+ console.error('Login failed:', error);
48
50
  return false;
49
51
  }
50
- console.log("Successfully logged in");
52
+ console.log('Successfully logged in');
51
53
  return true;
52
54
  }
53
55
 
54
56
  public static async initListeners(pluginId: string) {
55
- console.warn("The plugin seams to not be running inside the Rimori platform. Switching to development standalone mode.");
57
+ console.warn(
58
+ 'The plugin seams to not be running inside the Rimori platform. Switching to development standalone mode.',
59
+ );
56
60
  // console.log("event that needs to be handled", event);
57
61
  const { supabase, config } = await StandaloneClient.getInstance();
58
62
 
59
63
  // EventBus.on("*", async (event) => {
60
- EventBus.respond("standalone", "global.supabase.requestAccess", async () => {
64
+ EventBus.respond('standalone', 'global.supabase.requestAccess', async () => {
61
65
  const session = await supabase.auth.getSession();
62
- console.log("session", session);
63
-
66
+ console.log('session', session);
67
+
64
68
  // Call the NestJS backend endpoint instead of the Supabase edge function
69
+ // get current guild id if any
70
+ let guildId: string | null = null;
71
+ try {
72
+ const {
73
+ data: { user },
74
+ } = await supabase.auth.getUser();
75
+ if (user) {
76
+ const { data: profile } = await supabase
77
+ .from('profiles')
78
+ .select('current_guild_id')
79
+ .eq('user_id', user.id)
80
+ .maybeSingle();
81
+ guildId = (profile as { current_guild_id?: string | null } | null)?.current_guild_id || null;
82
+ }
83
+ } catch (_) {
84
+ guildId = null;
85
+ }
86
+
65
87
  const response = await fetch(`${config.backendUrl}/plugin/token`, {
66
88
  method: 'POST',
67
89
  headers: {
68
90
  'Content-Type': 'application/json',
69
- 'Authorization': `Bearer ${session.data.session?.access_token}`
91
+ Authorization: `Bearer ${session.data.session?.access_token}`,
70
92
  },
71
93
  body: JSON.stringify({
72
- pluginId: pluginId
73
- })
94
+ pluginId: pluginId,
95
+ guildId: guildId,
96
+ }),
74
97
  });
75
98
 
76
99
  if (!response.ok) {
@@ -79,7 +102,7 @@ export class StandaloneClient {
79
102
  }
80
103
 
81
104
  const data = await response.json();
82
-
105
+
83
106
  return {
84
107
  token: data.token,
85
108
  pluginId: pluginId,
@@ -88,11 +111,15 @@ export class StandaloneClient {
88
111
  backendUrl: config.backendUrl,
89
112
  tablePrefix: pluginId,
90
113
  expiration: new Date(Date.now() + 1000 * 60 * 60 * 1.5), // 1.5 hours
91
- }
114
+ };
92
115
  });
93
116
 
94
- EventBus.on("*", async (event) => {
95
- console.log("[standalone] would send event to parent", event);
96
- }, ["standalone"]);
117
+ EventBus.on(
118
+ '*',
119
+ async (event) => {
120
+ console.log('[standalone] would send event to parent', event);
121
+ },
122
+ ['standalone'],
123
+ );
97
124
  }
98
- }
125
+ }
@@ -1,10 +1,10 @@
1
1
  export function setTheme(theme?: string | null) {
2
- document.documentElement.classList.add("dark:text-gray-200");
2
+ document.documentElement.classList.add('dark:text-gray-200');
3
3
 
4
4
  if (isDarkTheme(theme)) {
5
- document.documentElement.setAttribute("data-theme", "dark");
6
- document.documentElement.classList.add('dark', "dark:bg-gray-950");
7
- document.documentElement.style.background = "hsl(var(--background))";
5
+ document.documentElement.setAttribute('data-theme', 'dark');
6
+ document.documentElement.classList.add('dark', 'dark:bg-gray-950');
7
+ document.documentElement.style.background = 'hsl(var(--background))';
8
8
  }
9
9
  }
10
10
 
@@ -20,4 +20,4 @@ export function isDarkTheme(theme?: string | null): boolean {
20
20
  }
21
21
 
22
22
  return theme === 'dark';
23
- }
23
+ }
@@ -10,7 +10,7 @@ interface PluginProviderProps {
10
10
  pluginId: string;
11
11
  settings?: {
12
12
  disableContextMenu?: boolean;
13
- }
13
+ };
14
14
  }
15
15
 
16
16
  const PluginContext = createContext<RimoriClient | null>(null);
@@ -20,29 +20,29 @@ export const PluginProvider: React.FC<PluginProviderProps> = ({ children, plugin
20
20
  const [standaloneClient, setStandaloneClient] = useState<StandaloneClient | boolean>(false);
21
21
  const [applicationMode, setApplicationMode] = useState<string | null>(null);
22
22
  const [theme, setTheme] = useState<string | null>(null);
23
-
24
- const isSidebar = applicationMode === "sidebar";
25
- const isSettings = applicationMode === "settings";
23
+
24
+ const isSidebar = applicationMode === 'sidebar';
25
+ const isSettings = applicationMode === 'settings';
26
26
 
27
27
  useEffect(() => {
28
28
  initEventBus(pluginId);
29
-
29
+
30
30
  // Check if we're in an iframe context - if not, we're standalone
31
31
  const standaloneDetected = window === window.parent;
32
-
32
+
33
33
  if (standaloneDetected && !standaloneClient) {
34
- StandaloneClient.getInstance().then(client => {
34
+ StandaloneClient.getInstance().then((client) => {
35
35
  client.needsLogin().then((needLogin) => setStandaloneClient(needLogin ? client : true));
36
36
  });
37
37
  }
38
38
 
39
39
  if ((!standaloneDetected && !plugin) || (standaloneDetected && standaloneClient === true)) {
40
- PluginController.getInstance(pluginId, standaloneDetected).then(client => {
40
+ PluginController.getInstance(pluginId, standaloneDetected).then((client) => {
41
41
  setPlugin(client);
42
42
  // Get applicationMode and theme from MessageChannel query params
43
43
  if (!standaloneDetected) {
44
- const mode = client.getQueryParam("applicationMode");
45
- const themeParam = client.getQueryParam("rm_theme");
44
+ const mode = client.getQueryParam('applicationMode');
45
+ const themeParam = client.getQueryParam('rm_theme');
46
46
  setApplicationMode(mode);
47
47
  setTheme(themeParam);
48
48
  }
@@ -81,13 +81,17 @@ export const PluginProvider: React.FC<PluginProviderProps> = ({ children, plugin
81
81
  }, [plugin]);
82
82
 
83
83
  if (standaloneClient instanceof StandaloneClient) {
84
- return <StandaloneAuth onLogin={async (email, password) => {
85
- if (await standaloneClient.login(email, password)) setStandaloneClient(true);
86
- }} />
84
+ return (
85
+ <StandaloneAuth
86
+ onLogin={async (email, password) => {
87
+ if (await standaloneClient.login(email, password)) setStandaloneClient(true);
88
+ }}
89
+ />
90
+ );
87
91
  }
88
92
 
89
93
  if (!plugin) {
90
- return ""
94
+ return '';
91
95
  }
92
96
 
93
97
  return (
@@ -114,7 +118,7 @@ function getUrlParam(name: string) {
114
118
  const hashValue = hashParams.get(name);
115
119
  if (hashValue) return hashValue;
116
120
  }
117
-
121
+
118
122
  // Fallback to regular URL search params
119
123
  const url = new URL(window.location.href);
120
124
  return url.searchParams.get(name);
@@ -122,34 +126,84 @@ function getUrlParam(name: string) {
122
126
 
123
127
  function initEventBus(pluginId: string) {
124
128
  // For now, use URL fallback for EventBus naming - this will be updated once MessageChannel is ready
125
- const isSidebar = getUrlParam("applicationMode") === "sidebar";
126
- EventBusHandler.getInstance("Plugin EventBus " + pluginId + " " + (isSidebar ? "sidebar" : "main"));
129
+ const isSidebar = getUrlParam('applicationMode') === 'sidebar';
130
+ EventBusHandler.getInstance('Plugin EventBus ' + pluginId + ' ' + (isSidebar ? 'sidebar' : 'main'));
127
131
  }
128
132
 
129
133
  function StandaloneAuth({ onLogin }: { onLogin: (user: string, password: string) => void }) {
130
- const [user, setUser] = useState("");
131
- const [password, setPassword] = useState("");
134
+ const [user, setUser] = useState('');
135
+ const [password, setPassword] = useState('');
132
136
  return (
133
- <div style={{
134
- position: 'fixed',
135
- inset: 0,
136
- display: 'flex',
137
- alignItems: 'center',
138
- justifyContent: 'center',
139
- backgroundColor: 'rgba(0, 0, 0, 0.5)',
140
- }}>
141
- <div style={{ backgroundColor: '#343534', padding: '1rem', borderRadius: '0.5rem', width: '500px', flexDirection: 'column', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
137
+ <div
138
+ style={{
139
+ position: 'fixed',
140
+ inset: 0,
141
+ display: 'flex',
142
+ alignItems: 'center',
143
+ justifyContent: 'center',
144
+ backgroundColor: 'rgba(0, 0, 0, 0.5)',
145
+ }}
146
+ >
147
+ <div
148
+ style={{
149
+ backgroundColor: '#343534',
150
+ padding: '1rem',
151
+ borderRadius: '0.5rem',
152
+ width: '500px',
153
+ flexDirection: 'column',
154
+ display: 'flex',
155
+ alignItems: 'center',
156
+ justifyContent: 'center',
157
+ }}
158
+ >
142
159
  <p style={{ fontSize: '2rem', fontWeight: 'bold', marginBottom: '1rem', textAlign: 'center' }}>Rimori Login</p>
143
160
  <p style={{ marginBottom: '1rem', textAlign: 'center' }}>
144
- Please login with your Rimori developer account for this plugin to be able to access the Rimori platform the same it will operate in the Rimori platform.
161
+ Please login with your Rimori developer account for this plugin to be able to access the Rimori platform the
162
+ same it will operate in the Rimori platform.
145
163
  </p>
146
164
  {/* email and password input */}
147
- <input style={{ marginBottom: '1rem', width: '100%', padding: '0.5rem', borderRadius: '0.5rem', border: 'none', backgroundColor: '#444444' }} type="email" placeholder="Email" onChange={(e) => setUser(e.target.value)} />
148
- <input style={{ marginBottom: '1rem', width: '100%', padding: '0.5rem', borderRadius: '0.5rem', border: 'none', backgroundColor: '#444444' }} type="password" placeholder="Password" onChange={(e) => setPassword(e.target.value)} />
149
- <button style={{ marginBottom: '1rem', width: '100%', padding: '0.5rem', borderRadius: '0.5rem', border: 'none', backgroundColor: '#928358' }} onClick={() => {
150
- onLogin(user, password);
151
- }}>Login</button>
165
+ <input
166
+ style={{
167
+ marginBottom: '1rem',
168
+ width: '100%',
169
+ padding: '0.5rem',
170
+ borderRadius: '0.5rem',
171
+ border: 'none',
172
+ backgroundColor: '#444444',
173
+ }}
174
+ type="email"
175
+ placeholder="Email"
176
+ onChange={(e) => setUser(e.target.value)}
177
+ />
178
+ <input
179
+ style={{
180
+ marginBottom: '1rem',
181
+ width: '100%',
182
+ padding: '0.5rem',
183
+ borderRadius: '0.5rem',
184
+ border: 'none',
185
+ backgroundColor: '#444444',
186
+ }}
187
+ type="password"
188
+ placeholder="Password"
189
+ onChange={(e) => setPassword(e.target.value)}
190
+ />
191
+ <button
192
+ style={{
193
+ marginBottom: '1rem',
194
+ width: '100%',
195
+ padding: '0.5rem',
196
+ borderRadius: '0.5rem',
197
+ border: 'none',
198
+ backgroundColor: '#928358',
199
+ }}
200
+ onClick={() => {
201
+ onLogin(user, password);
202
+ }}
203
+ >
204
+ Login
205
+ </button>
152
206
  </div>
153
207
  </div>
154
- )
155
- }
208
+ );
209
+ }