@rimori/client 1.4.0 → 1.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +77 -71
- package/dist/cli/scripts/init/dev-registration.d.ts +1 -1
- package/dist/cli/scripts/init/dev-registration.js +4 -4
- package/dist/cli/scripts/init/main.js +1 -1
- package/dist/cli/scripts/init/package-setup.d.ts +1 -1
- package/dist/cli/scripts/init/package-setup.js +3 -3
- package/dist/cli/scripts/init/router-transformer.js +19 -12
- package/dist/cli/scripts/init/vite-config.d.ts +2 -2
- package/dist/cli/scripts/init/vite-config.js +2 -2
- package/dist/cli/scripts/release/release-config-upload.js +9 -9
- package/dist/cli/scripts/release/release-db-update.d.ts +1 -1
- package/dist/cli/scripts/release/release-db-update.js +9 -9
- package/dist/cli/scripts/release/release-file-upload.js +1 -1
- package/dist/cli/scripts/release/release.js +2 -2
- package/dist/components/CRUDModal.d.ts +1 -1
- package/dist/components/CRUDModal.js +3 -3
- package/dist/components/MarkdownEditor.js +16 -16
- package/dist/components/Spinner.js +2 -2
- package/dist/components/ai/Assistant.js +7 -8
- package/dist/components/ai/Avatar.d.ts +2 -2
- package/dist/components/ai/Avatar.js +10 -5
- package/dist/components/ai/EmbeddedAssistent/AudioInputField.js +5 -6
- package/dist/components/ai/EmbeddedAssistent/CircleAudioAvatar.d.ts +1 -1
- package/dist/components/ai/EmbeddedAssistent/CircleAudioAvatar.js +1 -2
- package/dist/components/ai/EmbeddedAssistent/TTS/MessageSender.d.ts +1 -2
- package/dist/components/ai/EmbeddedAssistent/TTS/MessageSender.js +4 -2
- package/dist/components/ai/EmbeddedAssistent/VoiceRecoder.js +2 -3
- package/dist/components/audio/Playbutton.js +10 -7
- package/dist/components/components/ContextMenu.d.ts +1 -1
- package/dist/components/components/ContextMenu.js +19 -16
- package/dist/components.d.ts +10 -10
- package/dist/components.js +10 -10
- package/dist/core/controller/AIController.d.ts +2 -2
- package/dist/core/controller/AIController.js +12 -12
- package/dist/core/controller/ExerciseController.d.ts +2 -2
- package/dist/core/controller/ExerciseController.js +2 -2
- package/dist/core/controller/ObjectController.js +5 -5
- package/dist/core/controller/SettingsController.d.ts +22 -7
- package/dist/core/controller/SettingsController.js +73 -8
- package/dist/core/controller/SharedContentController.d.ts +3 -3
- package/dist/core/controller/SharedContentController.js +38 -20
- package/dist/core/controller/VoiceController.js +6 -4
- package/dist/core/core.d.ts +15 -15
- package/dist/core/core.js +7 -7
- package/dist/fromRimori/EventBus.js +23 -23
- package/dist/fromRimori/PluginTypes.d.ts +4 -4
- package/dist/hooks/UseChatHook.d.ts +3 -3
- package/dist/hooks/UseChatHook.js +9 -3
- package/dist/index.d.ts +10 -10
- package/dist/index.js +9 -9
- package/dist/plugin/AccomplishmentHandler.d.ts +5 -5
- package/dist/plugin/AccomplishmentHandler.js +31 -27
- package/dist/plugin/AudioController.d.ts +1 -1
- package/dist/plugin/AudioController.js +6 -6
- package/dist/plugin/Logger.js +15 -13
- package/dist/plugin/PluginController.d.ts +7 -1
- package/dist/plugin/PluginController.js +32 -27
- package/dist/plugin/RimoriClient.d.ts +17 -18
- package/dist/plugin/RimoriClient.js +31 -31
- package/dist/plugin/StandaloneClient.d.ts +1 -1
- package/dist/plugin/StandaloneClient.js +35 -16
- package/dist/plugin/ThemeSetter.js +4 -4
- package/dist/providers/PluginProvider.js +44 -14
- package/dist/utils/Language.js +57 -57
- package/dist/utils/PluginUtils.js +3 -3
- package/dist/utils/difficultyConverter.d.ts +1 -1
- package/dist/utils/difficultyConverter.js +1 -1
- package/dist/utils/endpoint.js +2 -2
- package/dist/worker/WorkerSetup.d.ts +1 -1
- package/dist/worker/WorkerSetup.js +6 -6
- package/example/docs/devdocs.md +50 -40
- package/example/docs/overview.md +1 -1
- package/example/docs/userdocs.md +4 -1
- package/example/rimori.config.ts +51 -49
- package/example/worker/vite.config.ts +3 -3
- package/example/worker/worker.ts +2 -2
- package/package.json +14 -8
- package/prettier.config.js +1 -1
- package/src/cli/scripts/init/dev-registration.ts +5 -8
- package/src/cli/scripts/init/env-setup.ts +1 -1
- package/src/cli/scripts/init/file-operations.ts +1 -1
- package/src/cli/scripts/init/html-cleaner.ts +2 -5
- package/src/cli/scripts/init/main.ts +16 -13
- package/src/cli/scripts/init/package-setup.ts +11 -15
- package/src/cli/scripts/init/router-transformer.ts +40 -37
- package/src/cli/scripts/init/tailwind-config.ts +17 -26
- package/src/cli/scripts/init/vite-config.ts +3 -3
- package/src/cli/scripts/release/release-config-upload.ts +11 -11
- package/src/cli/scripts/release/release-db-update.ts +12 -12
- package/src/cli/scripts/release/release-file-upload.ts +2 -2
- package/src/cli/scripts/release/release.ts +4 -4
- package/src/cli/types/DatabaseTypes.ts +2 -10
- package/src/components/CRUDModal.tsx +64 -48
- package/src/components/MarkdownEditor.tsx +58 -27
- package/src/components/Spinner.tsx +24 -17
- package/src/components/ai/Assistant.tsx +70 -70
- package/src/components/ai/Avatar.tsx +17 -14
- package/src/components/ai/EmbeddedAssistent/AudioInputField.tsx +63 -54
- package/src/components/ai/EmbeddedAssistent/CircleAudioAvatar.tsx +14 -5
- package/src/components/ai/EmbeddedAssistent/TTS/MessageSender.ts +75 -74
- package/src/components/ai/EmbeddedAssistent/TTS/Player.ts +3 -4
- package/src/components/ai/EmbeddedAssistent/VoiceRecoder.tsx +109 -94
- package/src/components/ai/utils.ts +4 -4
- package/src/components/audio/Playbutton.tsx +101 -93
- package/src/components/components/ContextMenu.tsx +47 -35
- package/src/components.ts +10 -10
- package/src/core/controller/AIController.ts +29 -19
- package/src/core/controller/ExerciseController.ts +16 -23
- package/src/core/controller/ObjectController.ts +15 -10
- package/src/core/controller/SettingsController.ts +89 -16
- package/src/core/controller/SharedContentController.ts +80 -44
- package/src/core/controller/VoiceController.ts +10 -8
- package/src/core/core.ts +15 -16
- package/src/fromRimori/EventBus.ts +76 -47
- package/src/fromRimori/PluginTypes.ts +26 -17
- package/src/fromRimori/readme.md +2 -2
- package/src/hooks/UseChatHook.ts +25 -15
- package/src/index.ts +10 -10
- package/src/plugin/AccomplishmentHandler.ts +53 -35
- package/src/plugin/AudioController.ts +18 -12
- package/src/plugin/Logger.ts +28 -21
- package/src/plugin/PluginController.ts +60 -44
- package/src/plugin/RimoriClient.ts +102 -72
- package/src/plugin/StandaloneClient.ts +51 -24
- package/src/plugin/ThemeSetter.ts +5 -5
- package/src/providers/PluginProvider.tsx +90 -36
- package/src/style.scss +3 -3
- package/src/utils/Language.ts +58 -58
- package/src/utils/PluginUtils.ts +16 -20
- package/src/utils/difficultyConverter.ts +2 -2
- package/src/utils/endpoint.ts +3 -2
- package/src/worker/WorkerSetup.ts +8 -9
- package/tsconfig.json +2 -4
|
@@ -1,23 +1,30 @@
|
|
|
1
|
-
import { PostgrestQueryBuilder } from
|
|
2
|
-
import { SupabaseClient } from
|
|
3
|
-
import { GenericSchema } from
|
|
4
|
-
import { generateText, Message, OnLLMResponse, streamChatGPT } from
|
|
5
|
-
import { generateObject, ObjectRequest } from
|
|
6
|
-
import { SettingsController, UserInfo } from
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
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]>(
|
|
20
|
-
|
|
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>(
|
|
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>(
|
|
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(
|
|
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: (
|
|
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(
|
|
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(
|
|
203
|
-
this.event.on<MainPanelAction>(
|
|
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(
|
|
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<
|
|
234
|
-
private from<
|
|
235
|
-
ViewName
|
|
236
|
-
|
|
237
|
-
|
|
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(
|
|
263
|
+
throw new Error('Table name cannot include uppercase letters. Please use snake_case for table names.');
|
|
245
264
|
}
|
|
246
|
-
if (table.startsWith(
|
|
247
|
-
return table.replace(
|
|
265
|
+
if (table.startsWith('global_')) {
|
|
266
|
+
return table.replace('global_', '');
|
|
248
267
|
}
|
|
249
|
-
return this.db.tablePrefix +
|
|
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(
|
|
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 =
|
|
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
|
-
|
|
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>(
|
|
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(
|
|
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
|
-
|
|
354
|
-
|
|
355
|
-
|
|
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
|
|
2
|
-
import { EventBus } from
|
|
3
|
-
import { DEFAULT_ANON_KEY, DEFAULT_ENDPOINT } from
|
|
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(
|
|
24
|
-
|
|
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(
|
|
49
|
+
console.error('Login failed:', error);
|
|
48
50
|
return false;
|
|
49
51
|
}
|
|
50
|
-
console.log(
|
|
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(
|
|
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(
|
|
64
|
+
EventBus.respond('standalone', 'global.supabase.requestAccess', async () => {
|
|
61
65
|
const session = await supabase.auth.getSession();
|
|
62
|
-
console.log(
|
|
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
|
-
|
|
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(
|
|
95
|
-
|
|
96
|
-
|
|
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(
|
|
2
|
+
document.documentElement.classList.add('dark:text-gray-200');
|
|
3
3
|
|
|
4
4
|
if (isDarkTheme(theme)) {
|
|
5
|
-
document.documentElement.setAttribute(
|
|
6
|
-
document.documentElement.classList.add('dark',
|
|
7
|
-
document.documentElement.style.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 ===
|
|
25
|
-
const isSettings = applicationMode ===
|
|
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(
|
|
45
|
-
const themeParam = client.getQueryParam(
|
|
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
|
|
85
|
-
|
|
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(
|
|
126
|
-
EventBusHandler.getInstance(
|
|
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
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
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
|
|
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
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
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
|
+
}
|