@rimori/client 2.5.28 → 2.5.29-next.1
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 +1 -1
- package/dist/controller/AccomplishmentController.d.ts +3 -2
- package/dist/controller/AccomplishmentController.js +4 -3
- package/dist/controller/TranslationController.js +6 -4
- package/dist/fromRimori/EventBus.d.ts +9 -0
- package/dist/fromRimori/EventBus.js +22 -1
- package/dist/fromRimori/PluginTypes.d.ts +2 -0
- package/dist/plugin/CommunicationHandler.d.ts +6 -1
- package/dist/plugin/CommunicationHandler.js +4 -2
- package/dist/plugin/RimoriClient.d.ts +10 -0
- package/dist/plugin/RimoriClient.js +32 -4
- package/dist/plugin/TTS/ChunkedAudioPlayer.d.ts +2 -0
- package/dist/plugin/TTS/ChunkedAudioPlayer.js +32 -17
- package/dist/plugin/module/AIModule.d.ts +30 -49
- package/dist/plugin/module/AIModule.js +26 -37
- package/dist/plugin/module/EventModule.d.ts +3 -2
- package/dist/plugin/module/EventModule.js +20 -13
- package/dist/plugin/module/PluginModule.d.ts +5 -0
- package/dist/plugin/module/PluginModule.js +12 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -140,7 +140,7 @@ Helpers:
|
|
|
140
140
|
The `client.ai` controller surfaces AI capabilities:
|
|
141
141
|
|
|
142
142
|
- `getText(messages, tools?)` – chat completion (string result).
|
|
143
|
-
- `
|
|
143
|
+
- `getStreamedText(messages, onMessage, tools?)` – streamed responses.
|
|
144
144
|
- `getObject(request)` – structured JSON generation.
|
|
145
145
|
- `getVoice(text, voice?, speed?, language?)` – text-to-speech (returns `Blob`).
|
|
146
146
|
- `getTextFromVoice(file)` – speech-to-text transcription.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { EventBusMessage } from '../fromRimori/EventBus';
|
|
1
|
+
import { EventBusHandler, EventBusMessage } from '../fromRimori/EventBus';
|
|
2
2
|
export type AccomplishmentMessage = EventBusMessage<MicroAccomplishmentPayload>;
|
|
3
3
|
export declare const skillCategories: readonly ["reading", "listening", "speaking", "writing", "learning", "community"];
|
|
4
4
|
interface BaseAccomplishmentPayload {
|
|
@@ -23,7 +23,8 @@ export interface MacroAccomplishmentPayload extends BaseAccomplishmentPayload {
|
|
|
23
23
|
export type AccomplishmentPayload = MicroAccomplishmentPayload | MacroAccomplishmentPayload;
|
|
24
24
|
export declare class AccomplishmentController {
|
|
25
25
|
private pluginId;
|
|
26
|
-
|
|
26
|
+
private eventBus;
|
|
27
|
+
constructor(pluginId: string, eventBus?: EventBusHandler);
|
|
27
28
|
emitAccomplishment(payload: Omit<AccomplishmentPayload, 'type'>): void;
|
|
28
29
|
private validateAccomplishment;
|
|
29
30
|
private sanitizeAccomplishment;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { EventBus } from '../fromRimori/EventBus';
|
|
2
2
|
export const skillCategories = ['reading', 'listening', 'speaking', 'writing', 'learning', 'community'];
|
|
3
3
|
export class AccomplishmentController {
|
|
4
|
-
constructor(pluginId) {
|
|
4
|
+
constructor(pluginId, eventBus) {
|
|
5
5
|
this.pluginId = pluginId;
|
|
6
|
+
this.eventBus = eventBus !== null && eventBus !== void 0 ? eventBus : EventBus;
|
|
6
7
|
}
|
|
7
8
|
emitAccomplishment(payload) {
|
|
8
9
|
const accomplishmentPayload = Object.assign(Object.assign({}, payload), { type: 'durationMinutes' in payload ? 'macro' : 'micro' });
|
|
@@ -11,7 +12,7 @@ export class AccomplishmentController {
|
|
|
11
12
|
}
|
|
12
13
|
const sanitizedPayload = this.sanitizeAccomplishment(accomplishmentPayload);
|
|
13
14
|
const topic = 'global.accomplishment.trigger' + (accomplishmentPayload.type === 'macro' ? 'Macro' : 'Micro');
|
|
14
|
-
|
|
15
|
+
this.eventBus.emit(this.pluginId, topic, sanitizedPayload);
|
|
15
16
|
}
|
|
16
17
|
validateAccomplishment(payload) {
|
|
17
18
|
if (!skillCategories.includes(payload.skillCategory)) {
|
|
@@ -85,7 +86,7 @@ export class AccomplishmentController {
|
|
|
85
86
|
else if (topicLength !== 3) {
|
|
86
87
|
throw new Error('Invalid accomplishment topic pattern. The pattern must be plugin.skillCategory.accomplishmentKeyword or an * as wildcard for any plugin, skill category or accomplishment keyword');
|
|
87
88
|
}
|
|
88
|
-
|
|
89
|
+
this.eventBus.on(['global.accomplishment.triggerMicro', 'global.accomplishment.triggerMacro'], (event) => {
|
|
89
90
|
const { plugin, skillCategory, accomplishmentKeyword } = this.getDecoupledTopic(accomplishmentTopic);
|
|
90
91
|
if (plugin !== '*' && event.sender !== plugin)
|
|
91
92
|
return;
|
|
@@ -48,7 +48,7 @@ export class Translator {
|
|
|
48
48
|
},
|
|
49
49
|
},
|
|
50
50
|
debug: false,
|
|
51
|
-
|
|
51
|
+
showSupportNotice: false,
|
|
52
52
|
parseMissingKeyHandler: (key, defaultValue) => {
|
|
53
53
|
if (!key.trim())
|
|
54
54
|
return '';
|
|
@@ -79,12 +79,13 @@ export class Translator {
|
|
|
79
79
|
});
|
|
80
80
|
}
|
|
81
81
|
getTranslationUrl(language) {
|
|
82
|
+
const baseUrl = this.translationUrl || window.location.origin;
|
|
82
83
|
// For localhost development, use local- prefix for non-English languages
|
|
83
|
-
if (window.location.hostname === 'localhost') {
|
|
84
|
+
if (window.location.hostname === 'localhost' || new URL(baseUrl).hostname === 'localhost') {
|
|
84
85
|
const filename = language !== 'en' ? `local-${language}` : language;
|
|
85
|
-
return `${
|
|
86
|
+
return `${baseUrl}/locales/${filename}.json`;
|
|
86
87
|
}
|
|
87
|
-
return `${
|
|
88
|
+
return `${baseUrl}/locales/${language}.json`;
|
|
88
89
|
}
|
|
89
90
|
usePlugin(plugin) {
|
|
90
91
|
if (!this.i18n) {
|
|
@@ -134,6 +135,7 @@ export class Translator {
|
|
|
134
135
|
if (!this.i18n) {
|
|
135
136
|
throw new Error('Translator is not initialized');
|
|
136
137
|
}
|
|
138
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-explicit-any
|
|
137
139
|
return this.i18n.t(key, options);
|
|
138
140
|
}
|
|
139
141
|
/**
|
|
@@ -29,6 +29,11 @@ export declare class EventBusHandler {
|
|
|
29
29
|
private generatedIds;
|
|
30
30
|
private cleanupInterval;
|
|
31
31
|
private constructor();
|
|
32
|
+
/**
|
|
33
|
+
* Creates a new non-singleton EventBusHandler instance.
|
|
34
|
+
* Used in federation mode where each plugin needs its own isolated EventBus.
|
|
35
|
+
*/
|
|
36
|
+
static create(name?: string): EventBusHandler;
|
|
32
37
|
static getInstance(name?: string): EventBusHandler;
|
|
33
38
|
/**
|
|
34
39
|
* Starts the interval to cleanup the generated ids.
|
|
@@ -109,6 +114,10 @@ export declare class EventBusHandler {
|
|
|
109
114
|
*/
|
|
110
115
|
private validateTopic;
|
|
111
116
|
private logIfDebug;
|
|
117
|
+
/**
|
|
118
|
+
* Destroys this EventBus instance, cleaning up all listeners and intervals.
|
|
119
|
+
*/
|
|
120
|
+
destroy(): void;
|
|
112
121
|
private logAndThrowError;
|
|
113
122
|
}
|
|
114
123
|
export declare const EventBus: EventBusHandler;
|
|
@@ -15,9 +15,18 @@ export class EventBusHandler {
|
|
|
15
15
|
this.evName = '';
|
|
16
16
|
this.generatedIds = new Map(); // Map<id, timestamp>
|
|
17
17
|
this.cleanupInterval = null;
|
|
18
|
-
//private constructor
|
|
19
18
|
this.startIdCleanup();
|
|
20
19
|
}
|
|
20
|
+
/**
|
|
21
|
+
* Creates a new non-singleton EventBusHandler instance.
|
|
22
|
+
* Used in federation mode where each plugin needs its own isolated EventBus.
|
|
23
|
+
*/
|
|
24
|
+
static create(name) {
|
|
25
|
+
const instance = new EventBusHandler();
|
|
26
|
+
if (name)
|
|
27
|
+
instance.evName = name;
|
|
28
|
+
return instance;
|
|
29
|
+
}
|
|
21
30
|
static getInstance(name) {
|
|
22
31
|
if (!EventBusHandler.instance) {
|
|
23
32
|
EventBusHandler.instance = new EventBusHandler();
|
|
@@ -307,6 +316,18 @@ export class EventBusHandler {
|
|
|
307
316
|
console.debug(`[${this.evName}] ` + args[0], ...args.slice(1));
|
|
308
317
|
}
|
|
309
318
|
}
|
|
319
|
+
/**
|
|
320
|
+
* Destroys this EventBus instance, cleaning up all listeners and intervals.
|
|
321
|
+
*/
|
|
322
|
+
destroy() {
|
|
323
|
+
this.listeners.clear();
|
|
324
|
+
this.responseResolvers.clear();
|
|
325
|
+
if (this.cleanupInterval) {
|
|
326
|
+
clearInterval(this.cleanupInterval);
|
|
327
|
+
this.cleanupInterval = null;
|
|
328
|
+
}
|
|
329
|
+
this.generatedIds.clear();
|
|
330
|
+
}
|
|
310
331
|
logAndThrowError(throwError, ...args) {
|
|
311
332
|
const message = `[${this.evName}] ` + args[0];
|
|
312
333
|
console.error(message, ...args.slice(1));
|
|
@@ -86,6 +86,8 @@ export interface RimoriPluginConfig<T extends object = object> {
|
|
|
86
86
|
sidebar: (SidebarPage & T)[];
|
|
87
87
|
/** Optional path to the plugin's settings/configuration page */
|
|
88
88
|
settings?: string;
|
|
89
|
+
/** When true, rimori-main loads this plugin via Module Federation instead of an iframe. */
|
|
90
|
+
federated?: boolean;
|
|
89
91
|
/** Optional array of event topics the plugin pages can listen to for cross-plugin communication */
|
|
90
92
|
topics?: string[];
|
|
91
93
|
};
|
|
@@ -40,6 +40,10 @@ export interface RimoriInfo {
|
|
|
40
40
|
* - 'plugins' for beta and stable release channels
|
|
41
41
|
*/
|
|
42
42
|
dbSchema: 'plugins' | 'plugins_alpha';
|
|
43
|
+
/**
|
|
44
|
+
* Whether text-to-speech is enabled globally (set in rimori-main navbar).
|
|
45
|
+
*/
|
|
46
|
+
ttsEnabled: boolean;
|
|
43
47
|
}
|
|
44
48
|
export declare class RimoriCommunicationHandler {
|
|
45
49
|
private port;
|
|
@@ -63,8 +67,9 @@ export declare class RimoriCommunicationHandler {
|
|
|
63
67
|
/**
|
|
64
68
|
* Handles updates to RimoriInfo from rimori-main.
|
|
65
69
|
* Updates the cached info and Supabase client, then notifies all registered callbacks.
|
|
70
|
+
* Public so that federated mode can call it when the update event arrives via the plugin's isolated EventBus.
|
|
66
71
|
*/
|
|
67
|
-
|
|
72
|
+
handleRimoriInfoUpdate(newInfo: RimoriInfo): void;
|
|
68
73
|
/**
|
|
69
74
|
* Registers a callback to be called when RimoriInfo is updated.
|
|
70
75
|
* @param callback - Function to call with the new RimoriInfo
|
|
@@ -77,7 +77,7 @@ export class RimoriCommunicationHandler {
|
|
|
77
77
|
// Listen for updates from rimori-main (data changes, token refresh, etc.)
|
|
78
78
|
// Topic format: {pluginId}.supabase.triggerUpdate
|
|
79
79
|
EventBus.on(`${this.pluginId}.supabase.triggerUpdate`, (ev) => {
|
|
80
|
-
// console.log('[RimoriCommunicationHandler] Received
|
|
80
|
+
// console.log('[RimoriCommunicationHandler] Received triggerUpdate via MessageChannel for', this.pluginId);
|
|
81
81
|
this.handleRimoriInfoUpdate(ev.data);
|
|
82
82
|
});
|
|
83
83
|
// Mark MessageChannel as ready and process pending requests
|
|
@@ -205,12 +205,14 @@ export class RimoriCommunicationHandler {
|
|
|
205
205
|
/**
|
|
206
206
|
* Handles updates to RimoriInfo from rimori-main.
|
|
207
207
|
* Updates the cached info and Supabase client, then notifies all registered callbacks.
|
|
208
|
+
* Public so that federated mode can call it when the update event arrives via the plugin's isolated EventBus.
|
|
208
209
|
*/
|
|
209
210
|
handleRimoriInfoUpdate(newInfo) {
|
|
210
211
|
if (JSON.stringify(this.rimoriInfo) === JSON.stringify(newInfo)) {
|
|
211
|
-
// console.log('[RimoriCommunicationHandler] RimoriInfo update
|
|
212
|
+
// console.log('[RimoriCommunicationHandler] RimoriInfo update identical to cached info, skipping', this.pluginId);
|
|
212
213
|
return;
|
|
213
214
|
}
|
|
215
|
+
// console.log('[RimoriCommunicationHandler] Applying RimoriInfo update for', this.pluginId, '| ttsEnabled:', newInfo.ttsEnabled);
|
|
214
216
|
// Update cached rimoriInfo
|
|
215
217
|
this.rimoriInfo = newInfo;
|
|
216
218
|
// Update Supabase client with new token
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { SharedContentController } from './module/SharedContentController';
|
|
2
|
+
import { RimoriInfo } from './CommunicationHandler';
|
|
2
3
|
import { PluginModule } from './module/PluginModule';
|
|
3
4
|
import { DbModule } from './module/DbModule';
|
|
4
5
|
import { EventModule } from './module/EventModule';
|
|
5
6
|
import { AIModule } from './module/AIModule';
|
|
6
7
|
import { ExerciseModule } from './module/ExerciseModule';
|
|
7
8
|
import { StorageModule } from './module/StorageModule';
|
|
9
|
+
import { EventBusHandler } from '../fromRimori/EventBus';
|
|
8
10
|
export declare class RimoriClient {
|
|
9
11
|
private static instance;
|
|
10
12
|
sharedContent: SharedContentController;
|
|
@@ -16,7 +18,15 @@ export declare class RimoriClient {
|
|
|
16
18
|
/** Upload and manage images stored in Supabase via the backend. */
|
|
17
19
|
storage: StorageModule;
|
|
18
20
|
private rimoriInfo;
|
|
21
|
+
/** The EventBus instance used by this client. In federation mode this is a per-plugin instance. */
|
|
22
|
+
eventBus: EventBusHandler;
|
|
19
23
|
private constructor();
|
|
24
|
+
/**
|
|
25
|
+
* Creates a RimoriClient with pre-existing RimoriInfo (federation mode).
|
|
26
|
+
* Uses a fresh per-plugin EventBus instance instead of the global singleton.
|
|
27
|
+
* Creates the Supabase PostgrestClient internally from the info.
|
|
28
|
+
*/
|
|
29
|
+
static createWithInfo(info: RimoriInfo): RimoriClient;
|
|
20
30
|
static getInstance(pluginId?: string): Promise<RimoriClient>;
|
|
21
31
|
navigation: {
|
|
22
32
|
toDashboard: () => void;
|
|
@@ -16,9 +16,10 @@ import { EventModule } from './module/EventModule';
|
|
|
16
16
|
import { AIModule } from './module/AIModule';
|
|
17
17
|
import { ExerciseModule } from './module/ExerciseModule';
|
|
18
18
|
import { StorageModule } from './module/StorageModule';
|
|
19
|
-
import {
|
|
19
|
+
import { PostgrestClient } from '@supabase/postgrest-js';
|
|
20
|
+
import { EventBus, EventBusHandler } from '../fromRimori/EventBus';
|
|
20
21
|
export class RimoriClient {
|
|
21
|
-
constructor(controller, supabase, info) {
|
|
22
|
+
constructor(controller, supabase, info, eventBus) {
|
|
22
23
|
this.navigation = {
|
|
23
24
|
toDashboard: () => {
|
|
24
25
|
this.event.emit('global.navigation.triggerToDashboard');
|
|
@@ -30,12 +31,13 @@ export class RimoriClient {
|
|
|
30
31
|
}),
|
|
31
32
|
};
|
|
32
33
|
this.rimoriInfo = info;
|
|
34
|
+
this.eventBus = eventBus !== null && eventBus !== void 0 ? eventBus : EventBus;
|
|
33
35
|
this.sharedContent = new SharedContentController(supabase, this);
|
|
34
36
|
this.ai = new AIModule(info.backendUrl, () => this.rimoriInfo.token, info.pluginId);
|
|
35
37
|
this.ai.setOnRateLimited((exercisesRemaining) => {
|
|
36
|
-
|
|
38
|
+
this.eventBus.emit(info.pluginId, 'global.quota.triggerExceeded', { exercises_remaining: exercisesRemaining });
|
|
37
39
|
});
|
|
38
|
-
this.event = new EventModule(info.pluginId, info.backendUrl, () => this.rimoriInfo.token, this.ai);
|
|
40
|
+
this.event = new EventModule(info.pluginId, info.backendUrl, () => this.rimoriInfo.token, this.ai, this.eventBus);
|
|
39
41
|
this.db = new DbModule(supabase, controller, info);
|
|
40
42
|
this.plugin = new PluginModule(supabase, controller, info, this.ai);
|
|
41
43
|
this.exercise = new ExerciseModule(supabase, controller, info, this.event);
|
|
@@ -48,6 +50,32 @@ export class RimoriClient {
|
|
|
48
50
|
Logger.getInstance(this);
|
|
49
51
|
}
|
|
50
52
|
}
|
|
53
|
+
/**
|
|
54
|
+
* Creates a RimoriClient with pre-existing RimoriInfo (federation mode).
|
|
55
|
+
* Uses a fresh per-plugin EventBus instance instead of the global singleton.
|
|
56
|
+
* Creates the Supabase PostgrestClient internally from the info.
|
|
57
|
+
*/
|
|
58
|
+
static createWithInfo(info) {
|
|
59
|
+
const eventBus = EventBusHandler.create('Plugin EventBus ' + info.pluginId);
|
|
60
|
+
const controller = new RimoriCommunicationHandler(info.pluginId, true);
|
|
61
|
+
const supabase = new PostgrestClient(`${info.url}/rest/v1`, {
|
|
62
|
+
schema: info.dbSchema,
|
|
63
|
+
headers: {
|
|
64
|
+
apikey: info.key,
|
|
65
|
+
Authorization: `Bearer ${info.token}`,
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
const client = new RimoriClient(controller, supabase, info, eventBus);
|
|
69
|
+
// In federated mode, CommunicationHandler skips MessageChannel setup so it never
|
|
70
|
+
// subscribes to triggerUpdate events. PluginEventBridge forwards host-bus events to
|
|
71
|
+
// this plugin's isolated eventBus, so we listen here and hand off to the controller.
|
|
72
|
+
eventBus.on(`${info.pluginId}.supabase.triggerUpdate`, (ev) => {
|
|
73
|
+
var _a;
|
|
74
|
+
console.log('[RimoriClient] Federated triggerUpdate received for', info.pluginId, '| ttsEnabled:', (_a = ev.data) === null || _a === void 0 ? void 0 : _a.ttsEnabled);
|
|
75
|
+
controller.handleRimoriInfoUpdate(ev.data);
|
|
76
|
+
});
|
|
77
|
+
return client;
|
|
78
|
+
}
|
|
51
79
|
static getInstance(pluginId) {
|
|
52
80
|
return __awaiter(this, void 0, void 0, function* () {
|
|
53
81
|
if (!RimoriClient.instance) {
|
|
@@ -13,6 +13,8 @@ export declare class ChunkedAudioPlayer {
|
|
|
13
13
|
private startedPlaying;
|
|
14
14
|
private onEndOfSpeech;
|
|
15
15
|
private readonly backgroundNoiseLevel;
|
|
16
|
+
private currentSource;
|
|
17
|
+
private stopped;
|
|
16
18
|
constructor();
|
|
17
19
|
private init;
|
|
18
20
|
setOnLoudnessChange(callback: (value: number) => void): void;
|
|
@@ -20,6 +20,8 @@ export class ChunkedAudioPlayer {
|
|
|
20
20
|
this.startedPlaying = false;
|
|
21
21
|
this.onEndOfSpeech = () => { };
|
|
22
22
|
this.backgroundNoiseLevel = 30; // Background noise level that should be treated as baseline (0)
|
|
23
|
+
this.currentSource = null;
|
|
24
|
+
this.stopped = false;
|
|
23
25
|
this.init();
|
|
24
26
|
}
|
|
25
27
|
init() {
|
|
@@ -37,12 +39,10 @@ export class ChunkedAudioPlayer {
|
|
|
37
39
|
}
|
|
38
40
|
addChunk(chunk, position) {
|
|
39
41
|
return __awaiter(this, void 0, void 0, function* () {
|
|
42
|
+
if (this.stopped)
|
|
43
|
+
return;
|
|
40
44
|
console.log('Adding chunk', position, chunk);
|
|
41
45
|
this.chunkQueue[position] = chunk;
|
|
42
|
-
// console.log("received chunk", {
|
|
43
|
-
// chunkQueue: this.chunkQueue.length,
|
|
44
|
-
// isPlaying: this.isPlaying,
|
|
45
|
-
// })
|
|
46
46
|
if (position === 0 && !this.startedPlaying) {
|
|
47
47
|
this.startedPlaying = true;
|
|
48
48
|
this.playChunks();
|
|
@@ -50,25 +50,27 @@ export class ChunkedAudioPlayer {
|
|
|
50
50
|
});
|
|
51
51
|
}
|
|
52
52
|
playChunks() {
|
|
53
|
-
|
|
54
|
-
if (this.isPlaying)
|
|
53
|
+
if (this.isPlaying || this.stopped)
|
|
55
54
|
return;
|
|
56
55
|
if (!this.chunkQueue[this.currentIndex]) {
|
|
57
56
|
// wait until the correct chunk arrives
|
|
58
57
|
setTimeout(() => this.playChunks(), 10);
|
|
58
|
+
return;
|
|
59
59
|
}
|
|
60
60
|
this.isPlaying = true;
|
|
61
61
|
this.playChunk(this.chunkQueue[this.currentIndex]).then(() => {
|
|
62
62
|
this.isPlaying = false;
|
|
63
|
+
if (this.stopped)
|
|
64
|
+
return;
|
|
63
65
|
this.currentIndex++;
|
|
64
66
|
if (this.chunkQueue[this.currentIndex]) {
|
|
65
67
|
this.shouldMonitorLoudness = true;
|
|
66
68
|
this.playChunks();
|
|
67
69
|
}
|
|
68
70
|
else {
|
|
69
|
-
// console.log('Playback finished', { currentIndex: this.currentIndex, chunkQueue: this.chunkQueue });
|
|
70
71
|
setTimeout(() => {
|
|
71
|
-
|
|
72
|
+
if (this.stopped)
|
|
73
|
+
return;
|
|
72
74
|
if (this.chunkQueue.length > this.currentIndex) {
|
|
73
75
|
this.playChunks();
|
|
74
76
|
}
|
|
@@ -81,13 +83,23 @@ export class ChunkedAudioPlayer {
|
|
|
81
83
|
});
|
|
82
84
|
}
|
|
83
85
|
stopPlayback() {
|
|
84
|
-
|
|
85
|
-
//
|
|
86
|
+
this.stopped = true;
|
|
87
|
+
// Stop the currently playing audio source node
|
|
88
|
+
if (this.currentSource) {
|
|
89
|
+
try {
|
|
90
|
+
this.currentSource.stop();
|
|
91
|
+
}
|
|
92
|
+
catch (_a) {
|
|
93
|
+
// already stopped
|
|
94
|
+
}
|
|
95
|
+
this.currentSource = null;
|
|
96
|
+
}
|
|
86
97
|
this.isPlaying = false;
|
|
87
98
|
this.chunkQueue = [];
|
|
88
99
|
this.startedPlaying = false;
|
|
89
100
|
this.shouldMonitorLoudness = false;
|
|
90
101
|
cancelAnimationFrame(this.handle);
|
|
102
|
+
this.loudnessCallback(0);
|
|
91
103
|
}
|
|
92
104
|
cleanup() {
|
|
93
105
|
// Stop playback first
|
|
@@ -100,14 +112,17 @@ export class ChunkedAudioPlayer {
|
|
|
100
112
|
}
|
|
101
113
|
}
|
|
102
114
|
playChunk(chunk) {
|
|
103
|
-
|
|
104
|
-
if (!chunk) {
|
|
115
|
+
if (!chunk || this.stopped) {
|
|
105
116
|
return Promise.resolve();
|
|
106
117
|
}
|
|
107
|
-
// console.log('Playing chunk', chunk);
|
|
108
118
|
return new Promise((resolve) => {
|
|
109
119
|
const source = this.audioContext.createBufferSource();
|
|
120
|
+
this.currentSource = source;
|
|
110
121
|
this.audioContext.decodeAudioData(chunk.slice(0)).then((audioBuffer) => {
|
|
122
|
+
if (this.stopped) {
|
|
123
|
+
resolve();
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
111
126
|
source.buffer = audioBuffer;
|
|
112
127
|
// Create a GainNode for volume control
|
|
113
128
|
const gainNode = this.audioContext.createGain();
|
|
@@ -117,10 +132,11 @@ export class ChunkedAudioPlayer {
|
|
|
117
132
|
gainNode.connect(this.analyser);
|
|
118
133
|
this.analyser.connect(this.audioContext.destination);
|
|
119
134
|
source.start(0);
|
|
120
|
-
// console.log('Playing chunk', this.currentIndex);
|
|
121
135
|
gainNode.gain.value = this.volume;
|
|
122
136
|
source.onended = () => {
|
|
123
|
-
|
|
137
|
+
if (this.currentSource === source) {
|
|
138
|
+
this.currentSource = null;
|
|
139
|
+
}
|
|
124
140
|
resolve();
|
|
125
141
|
};
|
|
126
142
|
// Start monitoring loudness only once
|
|
@@ -190,11 +206,10 @@ export class ChunkedAudioPlayer {
|
|
|
190
206
|
this.handle = requestAnimationFrame(() => this.monitorLoudness());
|
|
191
207
|
}
|
|
192
208
|
reset() {
|
|
193
|
-
// console.log('Resetting player');
|
|
194
209
|
this.stopPlayback();
|
|
210
|
+
this.stopped = false;
|
|
195
211
|
this.currentIndex = 0;
|
|
196
212
|
this.shouldMonitorLoudness = true;
|
|
197
|
-
//reset to the beginning when the class gets initialized
|
|
198
213
|
this.isMonitoring = false;
|
|
199
214
|
this.isPlaying = false;
|
|
200
215
|
this.init();
|
|
@@ -35,24 +35,6 @@ export interface Message {
|
|
|
35
35
|
toolCalls?: ToolInvocation[];
|
|
36
36
|
}
|
|
37
37
|
export type OnLLMResponse = (id: string, response: string, finished: boolean, toolInvocations?: ToolInvocation[]) => void;
|
|
38
|
-
export interface ObjectRequest {
|
|
39
|
-
/**
|
|
40
|
-
* The tools that the AI can use.
|
|
41
|
-
*/
|
|
42
|
-
tool: AIObjectTool;
|
|
43
|
-
/**
|
|
44
|
-
* High level instructions for the AI to follow. Behaviour, tone, restrictions, etc.
|
|
45
|
-
* Example: "Act like a recipe writer."
|
|
46
|
-
*/
|
|
47
|
-
/** @deprecated Use server-side prompt definitions (prompt + variables) instead of building requests client-side. */
|
|
48
|
-
behaviour?: string;
|
|
49
|
-
/**
|
|
50
|
-
* The specific instruction for the AI to follow.
|
|
51
|
-
* Example: "Generate a recipe using chicken, rice and vegetables."
|
|
52
|
-
*/
|
|
53
|
-
/** @deprecated Use server-side prompt definitions (prompt + variables) instead of building requests client-side. */
|
|
54
|
-
instructions: string;
|
|
55
|
-
}
|
|
56
38
|
/**
|
|
57
39
|
* Controller for AI-related operations.
|
|
58
40
|
* Provides access to text generation, voice synthesis, and object generation.
|
|
@@ -88,24 +70,41 @@ export declare class AIModule {
|
|
|
88
70
|
setOnRateLimited(cb: (exercisesRemaining: number) => void): void;
|
|
89
71
|
/**
|
|
90
72
|
* Generate text from messages using AI.
|
|
91
|
-
* @param messages The messages to generate text from.
|
|
92
|
-
* @param tools Optional tools to use for generation.
|
|
93
|
-
* @param cache Whether to cache the result (default: false).
|
|
94
|
-
* @param model The model to use for generation.
|
|
73
|
+
* @param params.messages The messages to generate text from.
|
|
74
|
+
* @param params.tools Optional tools to use for generation.
|
|
75
|
+
* @param params.cache Whether to cache the result (default: false).
|
|
76
|
+
* @param params.model The model to use for generation.
|
|
77
|
+
* @param params.prompt Server-side prompt name (e.g. 'writing.analysis').
|
|
78
|
+
* @param params.variables Variables for the server-side prompt template.
|
|
95
79
|
* @returns The generated text.
|
|
96
80
|
*/
|
|
97
|
-
getText(
|
|
81
|
+
getText(params: {
|
|
82
|
+
messages: Message[];
|
|
83
|
+
tools?: Tool[];
|
|
84
|
+
cache?: boolean;
|
|
85
|
+
model?: string;
|
|
86
|
+
prompt?: string;
|
|
87
|
+
variables?: Record<string, any>;
|
|
88
|
+
}): Promise<string>;
|
|
98
89
|
/**
|
|
99
90
|
* Stream text generation from messages using AI.
|
|
100
|
-
* @param messages The messages to generate text from.
|
|
101
|
-
* @param onMessage Callback for each message chunk.
|
|
102
|
-
* @param tools Optional tools to use for generation.
|
|
103
|
-
* @param cache Whether to cache the result (default: false).
|
|
104
|
-
* @param model The model to use for generation.
|
|
91
|
+
* @param params.messages The messages to generate text from.
|
|
92
|
+
* @param params.onMessage Callback for each message chunk.
|
|
93
|
+
* @param params.tools Optional tools to use for generation.
|
|
94
|
+
* @param params.cache Whether to cache the result (default: false).
|
|
95
|
+
* @param params.model The model to use for generation.
|
|
96
|
+
* @param params.prompt Server-side prompt name (e.g. 'writing.analysis').
|
|
97
|
+
* @param params.variables Variables for the server-side prompt template.
|
|
105
98
|
*/
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
99
|
+
getStreamedText(params: {
|
|
100
|
+
messages: Message[];
|
|
101
|
+
onMessage: OnLLMResponse;
|
|
102
|
+
tools?: Tool[];
|
|
103
|
+
cache?: boolean;
|
|
104
|
+
model?: string;
|
|
105
|
+
prompt?: string;
|
|
106
|
+
variables?: Record<string, any>;
|
|
107
|
+
}): Promise<string>;
|
|
109
108
|
/**
|
|
110
109
|
* Generate voice audio from text using AI.
|
|
111
110
|
* @param text The text to convert to voice.
|
|
@@ -123,8 +122,6 @@ export declare class AIModule {
|
|
|
123
122
|
* @returns The transcribed text.
|
|
124
123
|
*/
|
|
125
124
|
getTextFromVoice(file: Blob, language?: Language): Promise<string>;
|
|
126
|
-
/** @deprecated Used by legacy client-side prompt path. Will be removed once all plugins migrate to server-side prompt definitions. */
|
|
127
|
-
private getChatMessage;
|
|
128
125
|
/**
|
|
129
126
|
* Generate a structured object from a request using AI.
|
|
130
127
|
* @param request.cache Whether to cache the result (default: false).
|
|
@@ -135,17 +132,9 @@ export declare class AIModule {
|
|
|
135
132
|
* @returns The generated object.
|
|
136
133
|
*/
|
|
137
134
|
getObject<T = any>(params: {
|
|
138
|
-
/** @deprecated Use prompt + variables instead of client-side system prompts. */
|
|
139
|
-
systemPrompt?: string;
|
|
140
|
-
/** @deprecated Use prompt + variables instead. Schema is loaded server-side from the prompt definition. */
|
|
141
|
-
responseSchema?: AIObjectTool;
|
|
142
|
-
/** @deprecated Use prompt + variables instead of client-side user prompts. */
|
|
143
|
-
userPrompt?: string;
|
|
144
135
|
cache?: boolean;
|
|
145
136
|
tools?: Tool[];
|
|
146
137
|
model?: string;
|
|
147
|
-
/** @deprecated Use uuid variable with resolver 'knowledgeEntry' in prompt definitions instead. */
|
|
148
|
-
knowledgeId?: string;
|
|
149
138
|
prompt?: string;
|
|
150
139
|
variables?: Record<string, any>;
|
|
151
140
|
}): Promise<T>;
|
|
@@ -159,18 +148,10 @@ export declare class AIModule {
|
|
|
159
148
|
* @param request.variables Variables for the server-side prompt template.
|
|
160
149
|
*/
|
|
161
150
|
getStreamedObject<T = any>(params: {
|
|
162
|
-
/** @deprecated Use prompt + variables instead of client-side system prompts. */
|
|
163
|
-
systemPrompt?: string;
|
|
164
|
-
/** @deprecated Use prompt + variables instead. Schema is loaded server-side from the prompt definition. */
|
|
165
|
-
responseSchema?: AIObjectTool;
|
|
166
|
-
/** @deprecated Use prompt + variables instead of client-side user prompts. */
|
|
167
|
-
userPrompt?: string;
|
|
168
151
|
onResult: OnStreamedObjectResult<T>;
|
|
169
152
|
cache?: boolean;
|
|
170
153
|
tools?: Tool[];
|
|
171
154
|
model?: string;
|
|
172
|
-
/** @deprecated Use uuid variable with resolver 'knowledgeEntry' in prompt definitions instead. */
|
|
173
|
-
knowledgeId?: string;
|
|
174
155
|
prompt?: string;
|
|
175
156
|
variables?: Record<string, any>;
|
|
176
157
|
}): Promise<T>;
|
|
@@ -75,42 +75,47 @@ export class AIModule {
|
|
|
75
75
|
}
|
|
76
76
|
/**
|
|
77
77
|
* Generate text from messages using AI.
|
|
78
|
-
* @param messages The messages to generate text from.
|
|
79
|
-
* @param tools Optional tools to use for generation.
|
|
80
|
-
* @param cache Whether to cache the result (default: false).
|
|
81
|
-
* @param model The model to use for generation.
|
|
78
|
+
* @param params.messages The messages to generate text from.
|
|
79
|
+
* @param params.tools Optional tools to use for generation.
|
|
80
|
+
* @param params.cache Whether to cache the result (default: false).
|
|
81
|
+
* @param params.model The model to use for generation.
|
|
82
|
+
* @param params.prompt Server-side prompt name (e.g. 'writing.analysis').
|
|
83
|
+
* @param params.variables Variables for the server-side prompt template.
|
|
82
84
|
* @returns The generated text.
|
|
83
85
|
*/
|
|
84
|
-
getText(
|
|
85
|
-
return __awaiter(this,
|
|
86
|
+
getText(params) {
|
|
87
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
88
|
+
const { messages, tools, cache = false, model, prompt, variables } = params;
|
|
86
89
|
const { result } = yield this.streamObject({
|
|
87
90
|
cache,
|
|
88
91
|
tools,
|
|
89
92
|
model,
|
|
90
93
|
messages,
|
|
94
|
+
prompt,
|
|
95
|
+
variables,
|
|
91
96
|
});
|
|
92
97
|
return result;
|
|
93
98
|
});
|
|
94
99
|
}
|
|
95
100
|
/**
|
|
96
101
|
* Stream text generation from messages using AI.
|
|
97
|
-
* @param messages The messages to generate text from.
|
|
98
|
-
* @param onMessage Callback for each message chunk.
|
|
99
|
-
* @param tools Optional tools to use for generation.
|
|
100
|
-
* @param cache Whether to cache the result (default: false).
|
|
101
|
-
* @param model The model to use for generation.
|
|
102
|
+
* @param params.messages The messages to generate text from.
|
|
103
|
+
* @param params.onMessage Callback for each message chunk.
|
|
104
|
+
* @param params.tools Optional tools to use for generation.
|
|
105
|
+
* @param params.cache Whether to cache the result (default: false).
|
|
106
|
+
* @param params.model The model to use for generation.
|
|
107
|
+
* @param params.prompt Server-side prompt name (e.g. 'writing.analysis').
|
|
108
|
+
* @param params.variables Variables for the server-side prompt template.
|
|
102
109
|
*/
|
|
103
|
-
|
|
104
|
-
return __awaiter(this,
|
|
105
|
-
|
|
106
|
-
knowledgeId, prompt, variables) {
|
|
110
|
+
getStreamedText(params) {
|
|
111
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
112
|
+
const { messages, onMessage, tools, cache = false, model, prompt, variables, } = params;
|
|
107
113
|
const messageId = Math.random().toString(36).substring(3);
|
|
108
114
|
const { result } = yield this.streamObject({
|
|
109
115
|
cache,
|
|
110
116
|
tools,
|
|
111
117
|
model,
|
|
112
118
|
messages,
|
|
113
|
-
knowledgeId,
|
|
114
119
|
prompt,
|
|
115
120
|
variables,
|
|
116
121
|
onResult: ({ result }) => onMessage(messageId, result, false),
|
|
@@ -179,14 +184,6 @@ export class AIModule {
|
|
|
179
184
|
});
|
|
180
185
|
});
|
|
181
186
|
}
|
|
182
|
-
/** @deprecated Used by legacy client-side prompt path. Will be removed once all plugins migrate to server-side prompt definitions. */
|
|
183
|
-
getChatMessage(systemPrompt, userPrompt) {
|
|
184
|
-
const messages = [{ role: 'system', content: systemPrompt }];
|
|
185
|
-
if (userPrompt) {
|
|
186
|
-
messages.push({ role: 'user', content: userPrompt });
|
|
187
|
-
}
|
|
188
|
-
return messages;
|
|
189
|
-
}
|
|
190
187
|
/**
|
|
191
188
|
* Generate a structured object from a request using AI.
|
|
192
189
|
* @param request.cache Whether to cache the result (default: false).
|
|
@@ -198,14 +195,12 @@ export class AIModule {
|
|
|
198
195
|
*/
|
|
199
196
|
getObject(params) {
|
|
200
197
|
return __awaiter(this, void 0, void 0, function* () {
|
|
201
|
-
const {
|
|
198
|
+
const { cache = false, tools = [], model = undefined, prompt, variables } = params;
|
|
202
199
|
return yield this.streamObject({
|
|
203
|
-
|
|
204
|
-
messages: systemPrompt ? this.getChatMessage(systemPrompt, userPrompt) : [],
|
|
200
|
+
messages: [],
|
|
205
201
|
cache,
|
|
206
202
|
tools,
|
|
207
203
|
model,
|
|
208
|
-
knowledgeId,
|
|
209
204
|
prompt,
|
|
210
205
|
variables,
|
|
211
206
|
});
|
|
@@ -222,15 +217,13 @@ export class AIModule {
|
|
|
222
217
|
*/
|
|
223
218
|
getStreamedObject(params) {
|
|
224
219
|
return __awaiter(this, void 0, void 0, function* () {
|
|
225
|
-
const {
|
|
220
|
+
const { onResult, cache = false, tools = [], model = undefined, prompt, variables } = params;
|
|
226
221
|
return yield this.streamObject({
|
|
227
|
-
|
|
228
|
-
messages: systemPrompt ? this.getChatMessage(systemPrompt, userPrompt) : [],
|
|
222
|
+
messages: [],
|
|
229
223
|
onResult,
|
|
230
224
|
cache,
|
|
231
225
|
tools,
|
|
232
226
|
model,
|
|
233
|
-
knowledgeId,
|
|
234
227
|
prompt,
|
|
235
228
|
variables,
|
|
236
229
|
});
|
|
@@ -239,7 +232,7 @@ export class AIModule {
|
|
|
239
232
|
streamObject(params) {
|
|
240
233
|
return __awaiter(this, void 0, void 0, function* () {
|
|
241
234
|
var _a, _b, _c, _d, _e;
|
|
242
|
-
const { messages,
|
|
235
|
+
const { messages, onResult = () => null, cache = false, tools = [], model = undefined, prompt, variables, } = params;
|
|
243
236
|
const chatMessages = messages.map((message, index) => (Object.assign(Object.assign({}, message), { id: `${index + 1}` })));
|
|
244
237
|
const payload = {
|
|
245
238
|
cache,
|
|
@@ -247,15 +240,11 @@ export class AIModule {
|
|
|
247
240
|
stream: true,
|
|
248
241
|
messages: chatMessages,
|
|
249
242
|
model,
|
|
250
|
-
knowledge_id: knowledgeId,
|
|
251
243
|
session_token_id: (_a = this.sessionTokenId) !== null && _a !== void 0 ? _a : undefined,
|
|
252
244
|
};
|
|
253
245
|
if (prompt) {
|
|
254
246
|
payload.prompt = { name: this.resolvePromptName(prompt), variables: variables !== null && variables !== void 0 ? variables : {} };
|
|
255
247
|
}
|
|
256
|
-
if (responseSchema) {
|
|
257
|
-
payload.responseSchema = responseSchema;
|
|
258
|
-
}
|
|
259
248
|
const response = yield fetch(`${this.backendUrl}/ai/llm`, {
|
|
260
249
|
body: JSON.stringify(payload),
|
|
261
250
|
method: 'POST',
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { MainPanelAction, SidebarAction } from '../../fromRimori/PluginTypes';
|
|
2
2
|
import { AccomplishmentPayload } from '../../controller/AccomplishmentController';
|
|
3
|
-
import { EventBusMessage, EventHandler, EventPayload, EventListener } from '../../fromRimori/EventBus';
|
|
3
|
+
import { EventBusHandler, EventBusMessage, EventHandler, EventPayload, EventListener } from '../../fromRimori/EventBus';
|
|
4
4
|
import { AIModule } from './AIModule';
|
|
5
5
|
/**
|
|
6
6
|
* Event module for plugin event bus operations.
|
|
@@ -12,7 +12,8 @@ export declare class EventModule {
|
|
|
12
12
|
private aiModule;
|
|
13
13
|
private backendUrl;
|
|
14
14
|
private getToken;
|
|
15
|
-
|
|
15
|
+
private eventBus;
|
|
16
|
+
constructor(pluginId: string, backendUrl: string, getToken: () => string, aiModule: AIModule, eventBus?: EventBusHandler);
|
|
16
17
|
getGlobalEventTopic(preliminaryTopic: string): string;
|
|
17
18
|
/**
|
|
18
19
|
* Emit an event to Rimori or a plugin.
|
|
@@ -14,12 +14,13 @@ import { EventBus } from '../../fromRimori/EventBus';
|
|
|
14
14
|
* Provides methods for emitting, listening to, and responding to events.
|
|
15
15
|
*/
|
|
16
16
|
export class EventModule {
|
|
17
|
-
constructor(pluginId, backendUrl, getToken, aiModule) {
|
|
17
|
+
constructor(pluginId, backendUrl, getToken, aiModule, eventBus) {
|
|
18
18
|
this.pluginId = pluginId;
|
|
19
19
|
this.backendUrl = backendUrl;
|
|
20
20
|
this.getToken = getToken;
|
|
21
21
|
this.aiModule = aiModule;
|
|
22
|
-
this.
|
|
22
|
+
this.eventBus = eventBus !== null && eventBus !== void 0 ? eventBus : EventBus;
|
|
23
|
+
this.accomplishmentController = new AccomplishmentController(pluginId, this.eventBus);
|
|
23
24
|
}
|
|
24
25
|
getGlobalEventTopic(preliminaryTopic) {
|
|
25
26
|
var _a;
|
|
@@ -54,7 +55,7 @@ export class EventModule {
|
|
|
54
55
|
*/
|
|
55
56
|
emit(topic, data, eventId) {
|
|
56
57
|
const globalTopic = this.getGlobalEventTopic(topic);
|
|
57
|
-
|
|
58
|
+
this.eventBus.emit(this.pluginId, globalTopic, data, eventId);
|
|
58
59
|
}
|
|
59
60
|
/**
|
|
60
61
|
* Request an event.
|
|
@@ -68,7 +69,7 @@ export class EventModule {
|
|
|
68
69
|
const globalTopic = this.getGlobalEventTopic(topic);
|
|
69
70
|
yield this.aiModule.session.ensure();
|
|
70
71
|
const sessionToken = (_a = this.aiModule.session.get()) !== null && _a !== void 0 ? _a : undefined;
|
|
71
|
-
return
|
|
72
|
+
return this.eventBus.request(this.pluginId, globalTopic, data, sessionToken);
|
|
72
73
|
});
|
|
73
74
|
}
|
|
74
75
|
/**
|
|
@@ -79,7 +80,7 @@ export class EventModule {
|
|
|
79
80
|
*/
|
|
80
81
|
on(topic, callback) {
|
|
81
82
|
const topics = Array.isArray(topic) ? topic : [topic];
|
|
82
|
-
return
|
|
83
|
+
return this.eventBus.on(topics.map((t) => this.getGlobalEventTopic(t)), (event) => {
|
|
83
84
|
if (event.ai_session_token && !this.aiModule.session.get()) {
|
|
84
85
|
this.aiModule.session.set(event.ai_session_token);
|
|
85
86
|
}
|
|
@@ -92,7 +93,7 @@ export class EventModule {
|
|
|
92
93
|
* @param callback The callback to call when the event is emitted.
|
|
93
94
|
*/
|
|
94
95
|
once(topic, callback) {
|
|
95
|
-
|
|
96
|
+
this.eventBus.once(this.getGlobalEventTopic(topic), callback);
|
|
96
97
|
}
|
|
97
98
|
/**
|
|
98
99
|
* Respond to an event.
|
|
@@ -124,7 +125,7 @@ export class EventModule {
|
|
|
124
125
|
}
|
|
125
126
|
});
|
|
126
127
|
}
|
|
127
|
-
|
|
128
|
+
this.eventBus.respond(this.pluginId, topics.map((t) => this.getGlobalEventTopic(t)), wrappedData);
|
|
128
129
|
}
|
|
129
130
|
/**
|
|
130
131
|
* Emit an accomplishment.
|
|
@@ -219,15 +220,21 @@ export class EventModule {
|
|
|
219
220
|
*/
|
|
220
221
|
onSidePanelAction(callback, actionsToListen = []) {
|
|
221
222
|
const listeningActions = Array.isArray(actionsToListen) ? actionsToListen : [actionsToListen];
|
|
222
|
-
//
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
// console.log('Listening to actions', listeningActions);
|
|
223
|
+
// Register the listener BEFORE emitting the request, so the synchronous response
|
|
224
|
+
// from the bridge/responder is captured (emit → bridge outbound → host respond → bridge inbound is synchronous).
|
|
225
|
+
console.log('[EventModule] onSidePanelAction: setting up listener for', this.pluginId, 'listening to:', listeningActions);
|
|
226
|
+
const listener = this.on('action.requestSidebar', ({ data }) => {
|
|
227
|
+
console.log('[EventModule] onSidePanelAction: received event', { data, listeningActions });
|
|
228
228
|
if (listeningActions.length === 0 || listeningActions.includes(data.action)) {
|
|
229
|
+
console.log('[EventModule] onSidePanelAction: action matched, calling callback');
|
|
229
230
|
callback(data);
|
|
230
231
|
}
|
|
232
|
+
else {
|
|
233
|
+
console.log('[EventModule] onSidePanelAction: action NOT matched. Got:', data.action, 'expected:', listeningActions);
|
|
234
|
+
}
|
|
231
235
|
});
|
|
236
|
+
console.log('[EventModule] onSidePanelAction: emitting action.requestSidebar for', this.pluginId);
|
|
237
|
+
this.emit('action.requestSidebar');
|
|
238
|
+
return listener;
|
|
232
239
|
}
|
|
233
240
|
}
|
|
@@ -23,6 +23,11 @@ export declare class PluginModule {
|
|
|
23
23
|
releaseChannel: string;
|
|
24
24
|
applicationMode: ApplicationMode;
|
|
25
25
|
theme: Theme;
|
|
26
|
+
/**
|
|
27
|
+
* Whether text-to-speech is globally enabled (set in rimori-main navbar).
|
|
28
|
+
* Updated automatically when the user toggles TTS.
|
|
29
|
+
*/
|
|
30
|
+
ttsEnabled: boolean;
|
|
26
31
|
constructor(supabase: SupabaseClient, communicationHandler: RimoriCommunicationHandler, info: RimoriInfo, ai: AIModule);
|
|
27
32
|
/**
|
|
28
33
|
* Fetches settings based on guild configuration.
|
|
@@ -14,6 +14,12 @@ import { Translator } from '../../controller/TranslationController';
|
|
|
14
14
|
*/
|
|
15
15
|
export class PluginModule {
|
|
16
16
|
constructor(supabase, communicationHandler, info, ai) {
|
|
17
|
+
var _a;
|
|
18
|
+
/**
|
|
19
|
+
* Whether text-to-speech is globally enabled (set in rimori-main navbar).
|
|
20
|
+
* Updated automatically when the user toggles TTS.
|
|
21
|
+
*/
|
|
22
|
+
this.ttsEnabled = true;
|
|
17
23
|
this.rimoriInfo = info;
|
|
18
24
|
this.supabase = supabase;
|
|
19
25
|
this.pluginId = info.pluginId;
|
|
@@ -21,7 +27,12 @@ export class PluginModule {
|
|
|
21
27
|
this.communicationHandler = communicationHandler;
|
|
22
28
|
const currentPlugin = info.installedPlugins.find((plugin) => plugin.id === info.pluginId);
|
|
23
29
|
this.translator = new Translator(info.interfaceLanguage, (currentPlugin === null || currentPlugin === void 0 ? void 0 : currentPlugin.endpoint) || '', ai);
|
|
24
|
-
this.
|
|
30
|
+
this.ttsEnabled = (_a = info.ttsEnabled) !== null && _a !== void 0 ? _a : true;
|
|
31
|
+
this.communicationHandler.onUpdate((updatedInfo) => {
|
|
32
|
+
var _a;
|
|
33
|
+
this.rimoriInfo = updatedInfo;
|
|
34
|
+
this.ttsEnabled = (_a = updatedInfo.ttsEnabled) !== null && _a !== void 0 ? _a : true;
|
|
35
|
+
});
|
|
25
36
|
this.applicationMode = this.communicationHandler.getQueryParam('applicationMode');
|
|
26
37
|
this.theme = this.communicationHandler.getQueryParam('rm_theme') || 'light';
|
|
27
38
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rimori/client",
|
|
3
|
-
"version": "2.5.
|
|
3
|
+
"version": "2.5.29-next.1",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"repository": {
|
|
@@ -36,9 +36,9 @@
|
|
|
36
36
|
"format": "prettier --write ."
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@supabase/postgrest-js": "^2.
|
|
39
|
+
"@supabase/postgrest-js": "^2.100.1",
|
|
40
40
|
"dotenv": "^16.5.0",
|
|
41
|
-
"i18next": "^25.
|
|
41
|
+
"i18next": "^25.10.10"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"@eslint/js": "^9.37.0",
|