@rimori/client 1.0.4 → 1.1.0
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 +13 -0
- package/dist/components/MarkdownEditor.js +6 -4
- package/dist/components/PluginController.d.ts +21 -0
- package/dist/components/PluginController.js +116 -0
- package/dist/components/ai/Assistant.js +1 -1
- package/dist/components/ai/Avatar.d.ts +5 -3
- package/dist/components/ai/Avatar.js +14 -6
- package/dist/components/ai/EmbeddedAssistent/CircleAudioAvatar.d.ts +2 -1
- package/dist/components/ai/EmbeddedAssistent/CircleAudioAvatar.js +35 -14
- package/dist/components/ai/EmbeddedAssistent/TTS/MessageSender.d.ts +1 -0
- package/dist/components/ai/EmbeddedAssistent/TTS/MessageSender.js +3 -0
- package/dist/components/ai/EmbeddedAssistent/TTS/Player.d.ts +2 -0
- package/dist/components/ai/EmbeddedAssistent/TTS/Player.js +5 -0
- package/dist/components/ai/EmbeddedAssistent/VoiceRecoder.d.ts +3 -0
- package/dist/components/ai/EmbeddedAssistent/VoiceRecoder.js +41 -5
- package/dist/components/ai/utils.d.ts +1 -1
- package/dist/components.d.ts +1 -0
- package/dist/components.js +1 -0
- package/dist/controller/AIController.js +2 -1
- package/dist/controller/SettingsController.d.ts +18 -10
- package/dist/controller/SettingsController.js +28 -31
- package/dist/controller/SharedContentController.d.ts +58 -11
- package/dist/controller/SharedContentController.js +161 -26
- package/dist/controller/SidePluginController.d.ts +1 -12
- package/dist/core/components/ContextMenu.d.ts +10 -0
- package/dist/core/components/ContextMenu.js +93 -0
- package/dist/core.d.ts +2 -0
- package/dist/core.js +2 -0
- package/dist/hooks/UseChatHook.d.ts +1 -1
- package/dist/plugin/AccomplishmentHandler.d.ts +38 -0
- package/dist/plugin/AccomplishmentHandler.js +108 -0
- package/dist/plugin/ContextMenu.d.ts +17 -0
- package/dist/plugin/ContextMenu.js +45 -0
- package/dist/plugin/PluginController.js +9 -4
- package/dist/plugin/RimoriClient.d.ts +92 -65
- package/dist/plugin/RimoriClient.js +105 -75
- package/dist/plugin/ThemeSetter.js +4 -4
- package/dist/plugin/fromRimori/EventBus.d.ts +6 -3
- package/dist/plugin/fromRimori/EventBus.js +15 -9
- package/dist/plugin/fromRimori/PluginTypes.d.ts +51 -0
- package/dist/plugin/fromRimori/PluginTypes.js +1 -0
- package/dist/providers/PluginController.d.ts +21 -0
- package/dist/providers/PluginController.js +116 -0
- package/dist/providers/PluginProvider.js +26 -73
- package/dist/types/Actions.d.ts +4 -0
- package/dist/types/Actions.js +1 -0
- package/dist/utils/Language.d.ts +66 -0
- package/dist/utils/Language.js +67 -0
- package/dist/utils/difficultyConverter.d.ts +1 -0
- package/dist/utils/difficultyConverter.js +3 -0
- package/dist/worker/WorkerSetup.js +5 -4
- package/package.json +3 -3
- package/src/components/MarkdownEditor.tsx +78 -76
- package/src/components/ai/Assistant.tsx +1 -1
- package/src/components/ai/Avatar.tsx +65 -48
- package/src/components/ai/EmbeddedAssistent/CircleAudioAvatar.tsx +81 -58
- package/src/components/ai/EmbeddedAssistent/TTS/MessageSender.ts +4 -0
- package/src/components/ai/EmbeddedAssistent/TTS/Player.ts +6 -0
- package/src/components/ai/EmbeddedAssistent/VoiceRecoder.tsx +51 -8
- package/src/components/ai/utils.ts +1 -1
- package/src/components.ts +2 -1
- package/src/controller/AIController.ts +2 -1
- package/src/controller/SettingsController.ts +80 -75
- package/src/controller/SharedContentController.ts +214 -53
- package/src/controller/SidePluginController.ts +1 -13
- package/src/core/components/ContextMenu.tsx +123 -0
- package/src/core.ts +3 -1
- package/src/hooks/UseChatHook.ts +17 -17
- package/src/plugin/AccomplishmentHandler.ts +165 -0
- package/src/plugin/PluginController.ts +107 -100
- package/src/plugin/RimoriClient.ts +267 -250
- package/src/plugin/ThemeSetter.ts +4 -5
- package/src/plugin/fromRimori/EventBus.ts +23 -12
- package/src/plugin/fromRimori/PluginTypes.ts +67 -0
- package/src/providers/PluginProvider.tsx +63 -110
- package/src/types/Actions.ts +6 -0
- package/src/utils/Language.ts +70 -0
- package/src/utils/difficultyConverter.ts +4 -0
- package/src/worker/WorkerSetup.ts +5 -4
- package/dist/components/avatar/Assistant.d.ts +0 -9
- package/dist/components/avatar/Assistant.js +0 -59
- package/dist/components/avatar/Avatar.d.ts +0 -12
- package/dist/components/avatar/Avatar.js +0 -42
- package/dist/components/avatar/EmbeddedAssistent/AudioInputField.d.ts +0 -7
- package/dist/components/avatar/EmbeddedAssistent/AudioInputField.js +0 -38
- package/dist/components/avatar/EmbeddedAssistent/CircleAudioAvatar.d.ts +0 -7
- package/dist/components/avatar/EmbeddedAssistent/CircleAudioAvatar.js +0 -59
- package/dist/components/avatar/EmbeddedAssistent/TTS/MessageSender.d.ts +0 -19
- package/dist/components/avatar/EmbeddedAssistent/TTS/MessageSender.js +0 -84
- package/dist/components/avatar/EmbeddedAssistent/TTS/Player.d.ts +0 -25
- package/dist/components/avatar/EmbeddedAssistent/TTS/Player.js +0 -180
- package/dist/components/avatar/EmbeddedAssistent/VoiceRecoder.d.ts +0 -7
- package/dist/components/avatar/EmbeddedAssistent/VoiceRecoder.js +0 -45
- package/dist/components/avatar/utils.d.ts +0 -6
- package/dist/components/avatar/utils.js +0 -14
|
@@ -7,12 +7,13 @@ 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 { generateText, streamChatGPT } from "../controller/AIController";
|
|
11
|
+
import { generateObject as generateObjectFunction } from "../controller/ObjectController";
|
|
10
12
|
import { SettingsController } from "../controller/SettingsController";
|
|
11
|
-
import { getSTTResponse, getTTSResponse } from "../controller/VoiceController";
|
|
12
13
|
import { SharedContentController } from "../controller/SharedContentController";
|
|
13
|
-
import { streamChatGPT, generateText } from "../controller/AIController";
|
|
14
|
-
import { generateObject as generateObjectFunction } from "../controller/ObjectController";
|
|
15
14
|
import { getPlugins } from "../controller/SidePluginController";
|
|
15
|
+
import { getSTTResponse, getTTSResponse } from "../controller/VoiceController";
|
|
16
|
+
import { AccomplishmentHandler } from "./AccomplishmentHandler";
|
|
16
17
|
import { EventBus } from "./fromRimori/EventBus";
|
|
17
18
|
export class RimoriClient {
|
|
18
19
|
constructor(options) {
|
|
@@ -45,9 +46,11 @@ export class RimoriClient {
|
|
|
45
46
|
* Subscribe to an event.
|
|
46
47
|
* @param topic The topic to subscribe to.
|
|
47
48
|
* @param callback The callback to call when the event is emitted.
|
|
49
|
+
* @returns The unsubscribe ids.
|
|
48
50
|
*/
|
|
49
51
|
on: (topic, callback) => {
|
|
50
|
-
|
|
52
|
+
const topics = Array.isArray(topic) ? topic : [topic];
|
|
53
|
+
return topics.map(topic => EventBus.on(this.pluginController.getGlobalEventTopic(topic), callback));
|
|
51
54
|
},
|
|
52
55
|
/**
|
|
53
56
|
* Subscribe to an event once.
|
|
@@ -64,45 +67,134 @@ export class RimoriClient {
|
|
|
64
67
|
*/
|
|
65
68
|
respond: (topic, data) => {
|
|
66
69
|
EventBus.respond(this.plugin.pluginId, this.pluginController.getGlobalEventTopic(topic), data);
|
|
70
|
+
},
|
|
71
|
+
/**
|
|
72
|
+
* Emit an accomplishment.
|
|
73
|
+
* @param payload The payload to emit.
|
|
74
|
+
*/
|
|
75
|
+
emitAccomplishment: (payload) => {
|
|
76
|
+
this.accomplishmentHandler.emitAccomplishment(payload);
|
|
77
|
+
},
|
|
78
|
+
/**
|
|
79
|
+
* Subscribe to an accomplishment.
|
|
80
|
+
* @param accomplishmentTopic The topic to subscribe to.
|
|
81
|
+
* @param callback The callback to call when the accomplishment is emitted.
|
|
82
|
+
*/
|
|
83
|
+
onAccomplishment: (accomplishmentTopic, callback) => {
|
|
84
|
+
this.accomplishmentHandler.subscribe(accomplishmentTopic, callback);
|
|
85
|
+
},
|
|
86
|
+
/**
|
|
87
|
+
* Trigger an action that opens the sidebar and triggers an action in the designated plugin.
|
|
88
|
+
* @param pluginId The id of the plugin to trigger the action for.
|
|
89
|
+
* @param actionKey The key of the action to trigger.
|
|
90
|
+
* @param text Optional text to be used for the action like for example text that the translator would look up.
|
|
91
|
+
*/
|
|
92
|
+
emitSidebarAction: (pluginId, actionKey, text) => {
|
|
93
|
+
this.event.emit("global.sidebar.triggerAction", { pluginId, actionKey, text });
|
|
67
94
|
}
|
|
68
95
|
};
|
|
69
96
|
this.llm = {
|
|
70
97
|
getText: (messages, tools) => __awaiter(this, void 0, void 0, function* () {
|
|
71
98
|
const token = yield this.pluginController.getToken();
|
|
72
|
-
return generateText(this.supabaseUrl, messages, tools || [], token).then(
|
|
99
|
+
return generateText(this.supabaseUrl, messages, tools || [], token).then(({ messages }) => messages[0].content[0].text);
|
|
73
100
|
}),
|
|
74
101
|
getSteamedText: (messages, onMessage, tools) => __awaiter(this, void 0, void 0, function* () {
|
|
75
102
|
const token = yield this.pluginController.getToken();
|
|
76
103
|
streamChatGPT(this.supabaseUrl, messages, tools || [], onMessage, token);
|
|
77
104
|
}),
|
|
78
105
|
getVoice: (text_1, ...args_1) => __awaiter(this, [text_1, ...args_1], void 0, function* (text, voice = "alloy", speed = 1, language) {
|
|
79
|
-
|
|
106
|
+
const token = yield this.pluginController.getToken();
|
|
107
|
+
return getTTSResponse(this.supabaseUrl, { input: text, voice, speed, language }, token);
|
|
80
108
|
}),
|
|
81
109
|
getTextFromVoice: (file) => {
|
|
82
110
|
return getSTTResponse(this.superbase, file);
|
|
83
111
|
},
|
|
84
112
|
getObject: (request) => __awaiter(this, void 0, void 0, function* () {
|
|
85
113
|
const token = yield this.pluginController.getToken();
|
|
86
|
-
return generateObjectFunction(this.
|
|
114
|
+
return generateObjectFunction(this.supabaseUrl, request, token);
|
|
87
115
|
}),
|
|
88
116
|
// getSteamedObject: this.generateObjectStream,
|
|
89
117
|
};
|
|
118
|
+
this.community = {
|
|
119
|
+
/**
|
|
120
|
+
* Shared content is a way to share completable content with other users using this plugin.
|
|
121
|
+
* Typical examples are assignments, exercises, stories, etc.
|
|
122
|
+
* Users generate new shared content items and others can complete the content too.
|
|
123
|
+
*/
|
|
124
|
+
sharedContent: {
|
|
125
|
+
/**
|
|
126
|
+
* Get one dedicated shared content item by id. It does not matter if it is completed or not.
|
|
127
|
+
* @param contentType The type of shared content to get. E.g. assignments, exercises, etc.
|
|
128
|
+
* @param id The id of the shared content item.
|
|
129
|
+
* @returns The shared content item.
|
|
130
|
+
*/
|
|
131
|
+
get: (contentType, id) => __awaiter(this, void 0, void 0, function* () {
|
|
132
|
+
return yield this.sharedContentController.getSharedContent(contentType, id);
|
|
133
|
+
}),
|
|
134
|
+
/**
|
|
135
|
+
* Get a list of shared content items.
|
|
136
|
+
* @param contentType The type of shared content to get. E.g. assignments, exercises, etc.
|
|
137
|
+
* @param filter The optional additional filter for checking new shared content based on a column and value. This is useful if the aditional information stored on the shared content is used to further narrow down the kind of shared content wanted to be received. E.g. only adjective grammar exercises.
|
|
138
|
+
* @param limit The optional limit for the number of results.
|
|
139
|
+
* @returns The list of shared content items.
|
|
140
|
+
*/
|
|
141
|
+
getList: (contentType, filter, limit) => __awaiter(this, void 0, void 0, function* () {
|
|
142
|
+
return yield this.sharedContentController.getSharedContentList(contentType, filter, limit);
|
|
143
|
+
}),
|
|
144
|
+
/**
|
|
145
|
+
* Get new shared content.
|
|
146
|
+
* @param contentType The type of shared content to fetch. E.g. assignments, exercises, etc.
|
|
147
|
+
* @param generatorInstructions The instructions for the creation of new shared content. The object will automatically be extended with a tool property with a topic and keywords property to let a new unique topic be generated.
|
|
148
|
+
* @param filter The optional additional filter for checking new shared content based on a column and value. This is useful if the aditional information stored on the shared content is used to further narrow down the kind of shared content wanted to be received. E.g. only adjective grammar exercises.
|
|
149
|
+
* @param privateTopic An optional flag to indicate if the topic should be private and only be visible to the user. This is useful if the topic is not meant to be shared with other users. Like for personal topics or if the content is based on the personal study goal.
|
|
150
|
+
* @returns The new shared content.
|
|
151
|
+
*/
|
|
152
|
+
getNew: (contentType, generatorInstructions, filter, privateTopic) => __awaiter(this, void 0, void 0, function* () {
|
|
153
|
+
return yield this.sharedContentController.getNewSharedContent(contentType, generatorInstructions, filter, privateTopic);
|
|
154
|
+
}),
|
|
155
|
+
/**
|
|
156
|
+
* Create a new shared content item.
|
|
157
|
+
* @param content The content to create.
|
|
158
|
+
* @returns The new shared content item.
|
|
159
|
+
*/
|
|
160
|
+
create: (content) => __awaiter(this, void 0, void 0, function* () {
|
|
161
|
+
return yield this.sharedContentController.createSharedContent(content);
|
|
162
|
+
}),
|
|
163
|
+
/**
|
|
164
|
+
* Update a shared content item.
|
|
165
|
+
* @param id The id of the shared content item to update.
|
|
166
|
+
* @param content The content to update.
|
|
167
|
+
* @returns The updated shared content item.
|
|
168
|
+
*/
|
|
169
|
+
update: (id, content) => __awaiter(this, void 0, void 0, function* () {
|
|
170
|
+
return yield this.sharedContentController.updateSharedContent(id, content);
|
|
171
|
+
}),
|
|
172
|
+
/**
|
|
173
|
+
* Complete a shared content item.
|
|
174
|
+
* @param contentType The type of shared content to complete. E.g. assignments, exercises, etc.
|
|
175
|
+
* @param assignmentId The id of the shared content item to complete.
|
|
176
|
+
*/
|
|
177
|
+
complete: (contentType, assignmentId) => __awaiter(this, void 0, void 0, function* () {
|
|
178
|
+
return yield this.sharedContentController.completeSharedContent(contentType, assignmentId);
|
|
179
|
+
})
|
|
180
|
+
}
|
|
181
|
+
};
|
|
90
182
|
this.superbase = options.supabase;
|
|
91
183
|
this.pluginController = options.pluginController;
|
|
92
184
|
this.settingsController = new SettingsController(options.supabase, options.pluginId);
|
|
93
|
-
this.sharedContentController = new SharedContentController(this);
|
|
185
|
+
this.sharedContentController = new SharedContentController(this.superbase, this);
|
|
94
186
|
this.supabaseUrl = this.pluginController.getSupabaseUrl();
|
|
95
|
-
this.
|
|
187
|
+
this.accomplishmentHandler = new AccomplishmentHandler(options.pluginId);
|
|
96
188
|
this.from = this.from.bind(this);
|
|
97
189
|
this.db = {
|
|
98
|
-
rpc: this.rpc,
|
|
99
190
|
from: this.from,
|
|
100
191
|
storage: this.superbase.storage,
|
|
101
|
-
functions: this.superbase.functions,
|
|
192
|
+
// functions: this.superbase.functions,
|
|
193
|
+
tablePrefix: options.tablePrefix,
|
|
194
|
+
getTableName: this.getTableName.bind(this),
|
|
102
195
|
};
|
|
103
196
|
this.plugin = {
|
|
104
197
|
pluginId: options.pluginId,
|
|
105
|
-
tablePrefix: options.tablePrefix,
|
|
106
198
|
setSettings: (settings) => __awaiter(this, void 0, void 0, function* () {
|
|
107
199
|
yield this.settingsController.setSettings(settings);
|
|
108
200
|
}),
|
|
@@ -129,69 +221,7 @@ export class RimoriClient {
|
|
|
129
221
|
from(relation) {
|
|
130
222
|
return this.superbase.from(this.getTableName(relation));
|
|
131
223
|
}
|
|
132
|
-
/**
|
|
133
|
-
* Perform a function call.
|
|
134
|
-
*
|
|
135
|
-
* @param functionName - The function name to call
|
|
136
|
-
* @param args - The arguments to pass to the function call
|
|
137
|
-
* @param options - Named parameters
|
|
138
|
-
* @param options.head - When set to `true`, `data` will not be returned.
|
|
139
|
-
* Useful if you only need the count.
|
|
140
|
-
* @param options.get - When set to `true`, the function will be called with
|
|
141
|
-
* read-only access mode.
|
|
142
|
-
* @param options.count - Count algorithm to use to count rows returned by the
|
|
143
|
-
* function. Only applicable for [set-returning
|
|
144
|
-
* functions](https://www.postgresql.org/docs/current/functions-srf.html).
|
|
145
|
-
*
|
|
146
|
-
* `"exact"`: Exact but slow count algorithm. Performs a `COUNT(*)` under the
|
|
147
|
-
* hood.
|
|
148
|
-
*
|
|
149
|
-
* `"planned"`: Approximated but fast count algorithm. Uses the Postgres
|
|
150
|
-
* statistics under the hood.
|
|
151
|
-
*
|
|
152
|
-
* `"estimated"`: Uses exact count for low numbers and planned count for high
|
|
153
|
-
* numbers.
|
|
154
|
-
*/
|
|
155
|
-
rpc(functionName, args = {}, options = {}) {
|
|
156
|
-
return this.superbase.rpc(this.getTableName(functionName), args, options);
|
|
157
|
-
}
|
|
158
224
|
getTableName(type) {
|
|
159
|
-
return this.
|
|
160
|
-
}
|
|
161
|
-
/**
|
|
162
|
-
* Fetch new shared content.
|
|
163
|
-
* @param type The type of shared content to fetch. E.g. assignments, exercises, etc.
|
|
164
|
-
* @param generatorInstructions The instructions for the generator.
|
|
165
|
-
* @param filter The filter for the shared content.
|
|
166
|
-
* @returns The new shared content.
|
|
167
|
-
*/
|
|
168
|
-
fetchNewSharedContent(type, generatorInstructions, filter) {
|
|
169
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
170
|
-
return this.sharedContentController.fetchNewSharedContent(type, generatorInstructions, filter);
|
|
171
|
-
});
|
|
172
|
-
}
|
|
173
|
-
/**
|
|
174
|
-
* Get a shared content item by id.
|
|
175
|
-
* @param type The type of shared content to get. E.g. assignments, exercises, etc.
|
|
176
|
-
* @param id The id of the shared content item.
|
|
177
|
-
* @returns The shared content item.
|
|
178
|
-
*/
|
|
179
|
-
getSharedContent(type, id) {
|
|
180
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
181
|
-
return this.sharedContentController.getSharedContent(type, id);
|
|
182
|
-
});
|
|
183
|
-
}
|
|
184
|
-
/**
|
|
185
|
-
* Complete a shared content item.
|
|
186
|
-
* @param type The type of shared content to complete. E.g. assignments, exercises, etc.
|
|
187
|
-
* @param assignmentId The id of the shared content item to complete.
|
|
188
|
-
*/
|
|
189
|
-
completeSharedContent(type, assignmentId) {
|
|
190
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
191
|
-
return this.sharedContentController.completeSharedContent(type, assignmentId);
|
|
192
|
-
});
|
|
193
|
-
}
|
|
194
|
-
triggerSidebarAction(pluginId, actionKey, text) {
|
|
195
|
-
this.event.emit("global.sidebar.triggerAction", { pluginId, actionKey, text });
|
|
225
|
+
return this.db.tablePrefix + "_" + type;
|
|
196
226
|
}
|
|
197
227
|
}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
export function setTheme() {
|
|
2
2
|
const urlParams = new URLSearchParams(window.location.search);
|
|
3
3
|
let theme = urlParams.get('theme');
|
|
4
|
-
const isSidebar = urlParams.get('applicationMode') === "sidebar";
|
|
5
4
|
if (!theme || theme === 'system') {
|
|
6
5
|
theme = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
|
7
6
|
}
|
|
8
|
-
document.documentElement.classList.add("dark:text-gray-200"
|
|
7
|
+
document.documentElement.classList.add("dark:text-gray-200");
|
|
9
8
|
if (theme === 'dark') {
|
|
10
|
-
document.documentElement.
|
|
11
|
-
document.documentElement.
|
|
9
|
+
document.documentElement.setAttribute("data-theme", "dark");
|
|
10
|
+
document.documentElement.classList.add('dark', "dark:bg-gray-950");
|
|
11
|
+
document.documentElement.style.background = "hsl(var(--background))";
|
|
12
12
|
}
|
|
13
13
|
}
|
|
@@ -15,6 +15,9 @@ export interface EventBusMessage<T = EventPayload> {
|
|
|
15
15
|
debug: boolean;
|
|
16
16
|
}
|
|
17
17
|
export type EventHandler<T = EventPayload> = (event: EventBusMessage<T>) => void | Promise<void>;
|
|
18
|
+
export interface EventListener {
|
|
19
|
+
off: () => void;
|
|
20
|
+
}
|
|
18
21
|
export declare class EventBusHandler {
|
|
19
22
|
private listeners;
|
|
20
23
|
private responseResolvers;
|
|
@@ -51,7 +54,7 @@ export declare class EventBusHandler {
|
|
|
51
54
|
* @param ignoreSender - The senders to ignore.
|
|
52
55
|
* @returns The ids of the listeners.
|
|
53
56
|
*/
|
|
54
|
-
on<T = EventPayload>(topics: string | string[], handler: EventHandler<T>, ignoreSender?: string[]):
|
|
57
|
+
on<T = EventPayload>(topics: string | string[], handler: EventHandler<T>, ignoreSender?: string[]): EventListener;
|
|
55
58
|
/**
|
|
56
59
|
* Subscribes to an event, processes the data and emits a response on the event bus.
|
|
57
60
|
* @param sender - The sender of the event.
|
|
@@ -59,7 +62,7 @@ export declare class EventBusHandler {
|
|
|
59
62
|
* @param handler - The handler to be called when the event is received. The handler returns the data to be emitted. Can be a static object or a function.
|
|
60
63
|
* @returns The ids of the listeners.
|
|
61
64
|
*/
|
|
62
|
-
respond(sender: string, topic: string, handler: EventPayload | ((data: EventBusMessage) => EventPayload | Promise<EventPayload>)):
|
|
65
|
+
respond(sender: string, topic: string, handler: EventPayload | ((data: EventBusMessage) => EventPayload | Promise<EventPayload>)): EventListener;
|
|
63
66
|
/**
|
|
64
67
|
* Subscribes to an event on the event bus. The handler will be called once and then removed.
|
|
65
68
|
* @param topic - The topic of the event.
|
|
@@ -70,7 +73,7 @@ export declare class EventBusHandler {
|
|
|
70
73
|
* Unsubscribes from an event on the event bus.
|
|
71
74
|
* @param listenerIds - The ids of the listeners to unsubscribe from.
|
|
72
75
|
*/
|
|
73
|
-
off
|
|
76
|
+
private off;
|
|
74
77
|
private toArray;
|
|
75
78
|
/**
|
|
76
79
|
* Requests data from the event bus.
|
|
@@ -11,7 +11,7 @@ export class EventBusHandler {
|
|
|
11
11
|
constructor() {
|
|
12
12
|
this.listeners = new Map();
|
|
13
13
|
this.responseResolvers = new Map();
|
|
14
|
-
this.debugEnabled =
|
|
14
|
+
this.debugEnabled = true;
|
|
15
15
|
this.evName = "";
|
|
16
16
|
//private constructor
|
|
17
17
|
}
|
|
@@ -93,7 +93,8 @@ export class EventBusHandler {
|
|
|
93
93
|
* @returns The ids of the listeners.
|
|
94
94
|
*/
|
|
95
95
|
on(topics, handler, ignoreSender = []) {
|
|
96
|
-
|
|
96
|
+
const ids = this.toArray(topics).map(topic => {
|
|
97
|
+
this.logIfDebug(`Subscribing to ` + topic, { ignoreSender });
|
|
97
98
|
if (!this.validateTopic(topic)) {
|
|
98
99
|
this.logAndThrowError(true, `Invalid topic: ` + topic);
|
|
99
100
|
}
|
|
@@ -107,6 +108,9 @@ export class EventBusHandler {
|
|
|
107
108
|
this.logIfDebug(`Subscribed to ` + topic, { listenerId: id, ignoreSender });
|
|
108
109
|
return btoa(JSON.stringify({ topic, id }));
|
|
109
110
|
});
|
|
111
|
+
return {
|
|
112
|
+
off: () => this.off(ids)
|
|
113
|
+
};
|
|
110
114
|
}
|
|
111
115
|
/**
|
|
112
116
|
* Subscribes to an event, processes the data and emits a response on the event bus.
|
|
@@ -116,12 +120,14 @@ export class EventBusHandler {
|
|
|
116
120
|
* @returns The ids of the listeners.
|
|
117
121
|
*/
|
|
118
122
|
respond(sender, topic, handler) {
|
|
119
|
-
const
|
|
123
|
+
const listener = this.on(topic, (data) => __awaiter(this, void 0, void 0, function* () {
|
|
120
124
|
const response = typeof handler === "function" ? yield handler(data) : handler;
|
|
121
125
|
this.emit(sender, topic, response, data.eventId);
|
|
122
126
|
}), [sender]);
|
|
123
|
-
this.logIfDebug(`Added respond listener ` + sender + " to topic " + topic, {
|
|
124
|
-
return
|
|
127
|
+
this.logIfDebug(`Added respond listener ` + sender + " to topic " + topic, { listener, sender });
|
|
128
|
+
return {
|
|
129
|
+
off: () => listener.off()
|
|
130
|
+
};
|
|
125
131
|
}
|
|
126
132
|
/**
|
|
127
133
|
* Subscribes to an event on the event bus. The handler will be called once and then removed.
|
|
@@ -133,13 +139,13 @@ export class EventBusHandler {
|
|
|
133
139
|
this.logAndThrowError(false, `Invalid topic: ` + topic);
|
|
134
140
|
return;
|
|
135
141
|
}
|
|
136
|
-
let
|
|
142
|
+
let listener;
|
|
137
143
|
const wrapper = (event) => {
|
|
138
144
|
handler(event);
|
|
139
|
-
|
|
145
|
+
listener === null || listener === void 0 ? void 0 : listener.off();
|
|
140
146
|
};
|
|
141
|
-
|
|
142
|
-
this.logIfDebug(`Added once listener ` + topic, {
|
|
147
|
+
listener = this.on(topic, wrapper);
|
|
148
|
+
this.logIfDebug(`Added once listener ` + topic, { listener, topic });
|
|
143
149
|
}
|
|
144
150
|
/**
|
|
145
151
|
* Unsubscribes from an event on the event bus.
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
export interface Plugin {
|
|
2
|
+
id: string;
|
|
3
|
+
title: string;
|
|
4
|
+
description: string;
|
|
5
|
+
git_repository: string;
|
|
6
|
+
website: string;
|
|
7
|
+
icon_url: string;
|
|
8
|
+
version: string;
|
|
9
|
+
author: string;
|
|
10
|
+
endpoint: string;
|
|
11
|
+
context_menu_actions: MenuEntry[];
|
|
12
|
+
plugin_pages: PluginPage[];
|
|
13
|
+
sidebar_pages: SidebarPage[];
|
|
14
|
+
settings_page: string;
|
|
15
|
+
worker?: {
|
|
16
|
+
url: string;
|
|
17
|
+
topics?: string[];
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
export interface PluginPage {
|
|
21
|
+
name: string;
|
|
22
|
+
url: string;
|
|
23
|
+
show: boolean;
|
|
24
|
+
description: string;
|
|
25
|
+
root: string;
|
|
26
|
+
action?: (Record<string, string> & {
|
|
27
|
+
key: string;
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
export interface SidebarPage {
|
|
31
|
+
name: string;
|
|
32
|
+
url: string;
|
|
33
|
+
iconUrl: string;
|
|
34
|
+
description: string;
|
|
35
|
+
actionKey: string;
|
|
36
|
+
}
|
|
37
|
+
export interface MenuEntry {
|
|
38
|
+
text: string;
|
|
39
|
+
pluginId: string;
|
|
40
|
+
actionKey: string;
|
|
41
|
+
icon?: React.ReactNode;
|
|
42
|
+
}
|
|
43
|
+
export type MainPanelAction = {
|
|
44
|
+
pluginId: string;
|
|
45
|
+
actionKey: string;
|
|
46
|
+
} & Record<string, string>;
|
|
47
|
+
export interface ContextMenuAction {
|
|
48
|
+
text: string;
|
|
49
|
+
pluginId: string;
|
|
50
|
+
actionKey: string;
|
|
51
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { SupabaseClient } from '@supabase/supabase-js';
|
|
2
|
+
import { RimoriClient } from "../plugin/RimoriClient";
|
|
3
|
+
export declare class PluginController {
|
|
4
|
+
private static client;
|
|
5
|
+
private static instance;
|
|
6
|
+
private communicationSecret;
|
|
7
|
+
private supabase;
|
|
8
|
+
private supabaseInfo;
|
|
9
|
+
private pluginId;
|
|
10
|
+
private constructor();
|
|
11
|
+
static getInstance(sender: string): Promise<RimoriClient>;
|
|
12
|
+
private getSecret;
|
|
13
|
+
getClient(): Promise<{
|
|
14
|
+
supabase: SupabaseClient;
|
|
15
|
+
tablePrefix: string;
|
|
16
|
+
pluginId: string;
|
|
17
|
+
}>;
|
|
18
|
+
getToken(): Promise<string>;
|
|
19
|
+
getSupabaseUrl(): string;
|
|
20
|
+
getGlobalEventTopic(preliminaryTopic: string): string;
|
|
21
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import { createClient } from '@supabase/supabase-js';
|
|
11
|
+
import { EventBus } from '../plugin/fromRimori/EventBus';
|
|
12
|
+
import { RimoriClient } from "../plugin/RimoriClient";
|
|
13
|
+
import { setTheme } from '../plugin/ThemeSetter';
|
|
14
|
+
export class PluginController {
|
|
15
|
+
constructor(pluginId) {
|
|
16
|
+
this.communicationSecret = null;
|
|
17
|
+
this.supabase = null;
|
|
18
|
+
this.supabaseInfo = null;
|
|
19
|
+
this.pluginId = pluginId;
|
|
20
|
+
this.getClient = this.getClient.bind(this);
|
|
21
|
+
if (typeof WorkerGlobalScope === 'undefined') {
|
|
22
|
+
setTheme();
|
|
23
|
+
}
|
|
24
|
+
window.addEventListener("message", (event) => {
|
|
25
|
+
// console.log("client: message received", event);
|
|
26
|
+
const { topic, sender, data, eventId } = event.data.event;
|
|
27
|
+
// skip forwarding messages from own plugin
|
|
28
|
+
if (sender === pluginId)
|
|
29
|
+
return;
|
|
30
|
+
EventBus.emit(sender, topic, data, eventId);
|
|
31
|
+
});
|
|
32
|
+
EventBus.on("*", (event) => {
|
|
33
|
+
// skip messages which are not from the own plugin
|
|
34
|
+
if (event.sender !== this.pluginId)
|
|
35
|
+
return;
|
|
36
|
+
if (event.topic.startsWith("self."))
|
|
37
|
+
return;
|
|
38
|
+
window.parent.postMessage({ event, secret: this.getSecret() }, "*");
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
static getInstance(sender) {
|
|
42
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
43
|
+
if (!PluginController.instance) {
|
|
44
|
+
PluginController.instance = new PluginController(sender);
|
|
45
|
+
PluginController.client = yield RimoriClient.getInstance(PluginController.instance);
|
|
46
|
+
}
|
|
47
|
+
return PluginController.client;
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
getSecret() {
|
|
51
|
+
if (!this.communicationSecret) {
|
|
52
|
+
const secret = new URLSearchParams(window.location.search).get("secret");
|
|
53
|
+
if (!secret) {
|
|
54
|
+
throw new Error("Communication secret not found in URL as query parameter");
|
|
55
|
+
}
|
|
56
|
+
this.communicationSecret = secret;
|
|
57
|
+
}
|
|
58
|
+
return this.communicationSecret;
|
|
59
|
+
}
|
|
60
|
+
getClient() {
|
|
61
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
62
|
+
if (this.supabase &&
|
|
63
|
+
this.supabaseInfo &&
|
|
64
|
+
this.supabaseInfo.expiration > new Date()) {
|
|
65
|
+
return { supabase: this.supabase, tablePrefix: this.supabaseInfo.tablePrefix, pluginId: this.supabaseInfo.pluginId };
|
|
66
|
+
}
|
|
67
|
+
const { data } = yield EventBus.request(this.pluginId, "global.supabase.requestAccess");
|
|
68
|
+
this.supabaseInfo = data;
|
|
69
|
+
this.supabase = createClient(this.supabaseInfo.url, this.supabaseInfo.key, {
|
|
70
|
+
accessToken: () => Promise.resolve(this.getToken())
|
|
71
|
+
});
|
|
72
|
+
return { supabase: this.supabase, tablePrefix: this.supabaseInfo.tablePrefix, pluginId: this.supabaseInfo.pluginId };
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
getToken() {
|
|
76
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
77
|
+
if (this.supabaseInfo && this.supabaseInfo.expiration && this.supabaseInfo.expiration > new Date()) {
|
|
78
|
+
return this.supabaseInfo.token;
|
|
79
|
+
}
|
|
80
|
+
const { data } = yield EventBus.request(this.pluginId, "global.supabase.requestAccess");
|
|
81
|
+
if (!this.supabaseInfo) {
|
|
82
|
+
throw new Error("Supabase info not found");
|
|
83
|
+
}
|
|
84
|
+
this.supabaseInfo.token = data.token;
|
|
85
|
+
this.supabaseInfo.expiration = data.expiration;
|
|
86
|
+
return this.supabaseInfo.token;
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
getSupabaseUrl() {
|
|
90
|
+
if (!this.supabaseInfo) {
|
|
91
|
+
throw new Error("Supabase info not found");
|
|
92
|
+
}
|
|
93
|
+
return this.supabaseInfo.url;
|
|
94
|
+
}
|
|
95
|
+
getGlobalEventTopic(preliminaryTopic) {
|
|
96
|
+
var _a, _b;
|
|
97
|
+
if (preliminaryTopic.startsWith("global.")) {
|
|
98
|
+
return preliminaryTopic;
|
|
99
|
+
}
|
|
100
|
+
if (preliminaryTopic.startsWith("self.")) {
|
|
101
|
+
return preliminaryTopic;
|
|
102
|
+
}
|
|
103
|
+
const topicParts = preliminaryTopic.split(".");
|
|
104
|
+
if (topicParts.length === 3) {
|
|
105
|
+
if (!topicParts[0].startsWith("pl") && topicParts[0] !== "global") {
|
|
106
|
+
throw new Error("The event topic must start with the plugin id or 'global'.");
|
|
107
|
+
}
|
|
108
|
+
return preliminaryTopic;
|
|
109
|
+
}
|
|
110
|
+
else if (topicParts.length > 3) {
|
|
111
|
+
throw new Error(`The event topic must consist of 3 parts. <pluginId>.<topic area>.<action>. Received: ${preliminaryTopic}`);
|
|
112
|
+
}
|
|
113
|
+
const topicRoot = (_b = (_a = this.supabaseInfo) === null || _a === void 0 ? void 0 : _a.pluginId) !== null && _b !== void 0 ? _b : "global";
|
|
114
|
+
return `${topicRoot}.${preliminaryTopic}`;
|
|
115
|
+
}
|
|
116
|
+
}
|