@rimori/client 2.1.7 → 2.2.0-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.
@@ -0,0 +1,105 @@
1
+ name: Pre-Release Rimori Client
2
+
3
+ on:
4
+ push:
5
+ branches: [dev]
6
+ paths:
7
+ - '**'
8
+ - '!.github/workflows/**'
9
+
10
+ jobs:
11
+ pre-release:
12
+ runs-on: ubuntu-latest
13
+ permissions:
14
+ contents: write
15
+ id-token: write
16
+
17
+ steps:
18
+ - name: Checkout repository
19
+ uses: actions/checkout@v4
20
+ with:
21
+ token: ${{ secrets.GITHUB_TOKEN }}
22
+ fetch-depth: 0
23
+
24
+ - name: Setup Node.js
25
+ uses: actions/setup-node@v4
26
+ with:
27
+ node-version: '20'
28
+ registry-url: 'https://registry.npmjs.org'
29
+ cache: 'yarn'
30
+ cache-dependency-path: yarn.lock
31
+
32
+ - name: Update npm
33
+ run: npm install -g npm@latest
34
+
35
+ - name: Install dependencies
36
+ run: yarn install --frozen-lockfile
37
+
38
+ - name: Build rimori-client (TypeScript verification)
39
+ run: yarn build
40
+
41
+ - name: Calculate next pre-release version
42
+ id: version
43
+ run: |
44
+ # Read current version from package.json (may be base or pre-release)
45
+ CURRENT_VERSION=$(node -p "require('./package.json').version")
46
+
47
+ # Extract base version (strip any pre-release suffix)
48
+ # Examples: "2.2.0" -> "2.2.0", "2.2.0-next.5" -> "2.2.0"
49
+ if [[ "$CURRENT_VERSION" =~ ^([0-9]+\.[0-9]+\.[0-9]+) ]]; then
50
+ BASE_VERSION="${BASH_REMATCH[1]}"
51
+ else
52
+ BASE_VERSION="$CURRENT_VERSION"
53
+ fi
54
+
55
+ # Try to get latest next version from npm
56
+ LATEST_NEXT=$(npm view @rimori/client@next version 2>/dev/null || echo "none")
57
+
58
+ if [ "$LATEST_NEXT" != "none" ]; then
59
+ # Extract base version and pre-release number from latest next version
60
+ # Example: "2.2.0-next.5" -> extract "2.2.0" and "5"
61
+ if [[ "$LATEST_NEXT" =~ ^([0-9]+\.[0-9]+\.[0-9]+)-next\.([0-9]+)$ ]]; then
62
+ LATEST_BASE="${BASH_REMATCH[1]}"
63
+ PRERELEASE_NUM="${BASH_REMATCH[2]}"
64
+
65
+ # If base version changed, reset to 1, otherwise increment
66
+ if [ "$LATEST_BASE" != "$BASE_VERSION" ]; then
67
+ NEW_NUM=1
68
+ else
69
+ NEW_NUM=$((PRERELEASE_NUM + 1))
70
+ fi
71
+ else
72
+ # Fallback: if format doesn't match, start at 1
73
+ NEW_NUM=1
74
+ fi
75
+ else
76
+ # First pre-release
77
+ NEW_NUM=1
78
+ fi
79
+
80
+ NEW_VERSION="${BASE_VERSION}-next.${NEW_NUM}"
81
+ echo "new_version=$NEW_VERSION" >> $GITHUB_OUTPUT
82
+ echo "Base version: $BASE_VERSION"
83
+ echo "Calculated next version: $NEW_VERSION"
84
+
85
+ - name: Update package.json version
86
+ run: |
87
+ # Use node to update version directly (yarn version creates git tags)
88
+ node -e "const fs = require('fs'); const pkg = JSON.parse(fs.readFileSync('package.json')); pkg.version = '${{ steps.version.outputs.new_version }}'; fs.writeFileSync('package.json', JSON.stringify(pkg, null, 2) + '\n');"
89
+
90
+ - name: Publish to npm
91
+ run: npm publish --tag next --access public
92
+ # Uses OIDC token automatically (no NODE_AUTH_TOKEN needed)
93
+ # Requires npm 11.5.1+ and id-token: write permission (already set)
94
+
95
+ - name: Commit version bump
96
+ run: |
97
+ git config --local user.email "action@github.com"
98
+ git config --local user.name "GitHub Action"
99
+ git add package.json
100
+ git commit -m "chore: bump @rimori/client to ${{ steps.version.outputs.new_version }} [skip ci]"
101
+ git push
102
+
103
+ - name: Output published version
104
+ run: |
105
+ echo "✅ Published @rimori/client@${{ steps.version.outputs.new_version }} to npm with @next tag"
File without changes
File without changes
@@ -42,8 +42,10 @@ export interface DbColumnDefinition {
42
42
  restrict?: {
43
43
  /** Restrictions for the user */
44
44
  user: Partial<Omit<DbPermissionDefinition, 'delete'>>;
45
- /** Restrictions for the moderator */
46
- moderator?: Partial<Omit<DbPermissionDefinition, 'delete'>>;
45
+ /** Restrictions for the guild moderator */
46
+ guild_moderator?: Partial<Omit<DbPermissionDefinition, 'delete'>>;
47
+ /** Restrictions for the language moderator */
48
+ lang_moderator?: Partial<Omit<DbPermissionDefinition, 'delete'>>;
47
49
  };
48
50
  }
49
51
  /**
@@ -69,8 +71,12 @@ export interface DbTableDefinition {
69
71
  description: string;
70
72
  /** Permissions for the table */
71
73
  permissions: {
74
+ /** Permissions for the user */
72
75
  user: DbPermissionDefinition;
73
- moderator?: DbPermissionDefinition;
76
+ /** Permissions for the guild moderator */
77
+ guild_moderator?: DbPermissionDefinition;
78
+ /** Permissions for the language moderator */
79
+ lang_moderator?: DbPermissionDefinition;
74
80
  };
75
81
  /** Column definitions for the table */
76
82
  columns: {
@@ -81,11 +87,13 @@ export interface DbTableDefinition {
81
87
  * Permission definition for a database table.
82
88
  * NONE means the action is not allowed.
83
89
  * OWN means only do the action on your own records.
90
+ * GUILD means do the action on all records in the guild.
91
+ * LANG means do the action on all records in the language.
84
92
  * ALL means do the action on all records.
85
93
  *
86
94
  * Defines the permissions for a database table.
87
95
  */
88
- export type DbPermission = 'NONE' | 'OWN' | 'ALL';
96
+ export type DbPermission = 'NONE' | 'OWN' | 'GUILD' | 'LANG' | 'ALL';
89
97
  /**
90
98
  * Permission definition for a database table.
91
99
  * Defines the permissions for a database table.
@@ -6,7 +6,9 @@ export class AccomplishmentController {
6
6
  }
7
7
  emitAccomplishment(payload) {
8
8
  const accomplishmentPayload = Object.assign(Object.assign({}, payload), { type: 'durationMinutes' in payload ? 'macro' : 'micro' });
9
- this.validateAccomplishment(accomplishmentPayload);
9
+ if (!this.validateAccomplishment(accomplishmentPayload)) {
10
+ return;
11
+ }
10
12
  const sanitizedPayload = this.sanitizeAccomplishment(accomplishmentPayload);
11
13
  const topic = 'global.accomplishment.trigger' + (accomplishmentPayload.type === 'macro' ? 'Macro' : 'Micro');
12
14
  EventBus.emit(this.pluginId, topic, sanitizedPayload);
@@ -29,7 +31,8 @@ export class AccomplishmentController {
29
31
  }
30
32
  //durationMinutes is required
31
33
  if (payload.type === 'macro' && payload.durationMinutes < 4) {
32
- throw new Error('The duration must be at least 4 minutes');
34
+ console.warn('The duration must be at least 4 minutes');
35
+ return false;
33
36
  }
34
37
  //errorRatio is required
35
38
  if (payload.type === 'macro' && (payload.errorRatio < 0 || payload.errorRatio > 1)) {
@@ -43,6 +46,7 @@ export class AccomplishmentController {
43
46
  }
44
47
  });
45
48
  }
49
+ return true;
46
50
  }
47
51
  sanitizeAccomplishment(payload) {
48
52
  var _a;
@@ -16,6 +16,7 @@ export interface Language {
16
16
  capitalized: string;
17
17
  uppercase: string;
18
18
  }
19
+ export type UserRole = 'user' | 'plugin_moderator' | 'lang_moderator' | 'admin';
19
20
  export interface UserInfo {
20
21
  skill_level_reading: LanguageLevel;
21
22
  skill_level_writing: LanguageLevel;
@@ -29,8 +30,7 @@ export interface UserInfo {
29
30
  story_genre: string;
30
31
  study_duration: number;
31
32
  /**
32
- * The 2 letter language code of the language the user speaks natively.
33
- * With the function getLanguageName, the language name can be retrieved.
33
+ * The language the user speaks natively.
34
34
  */
35
35
  mother_tongue: Language;
36
36
  /**
@@ -49,6 +49,10 @@ export interface UserInfo {
49
49
  * Optional: nearest big city (>100,000) near user's location
50
50
  */
51
51
  target_city?: string;
52
+ /**
53
+ * The user's role: 'user', 'plugin_moderator', 'lang_moderator', or 'admin'
54
+ */
55
+ user_role: UserRole;
52
56
  }
53
57
  export declare class SettingsController {
54
58
  private pluginId;
@@ -28,31 +28,34 @@ export class SharedContentController {
28
28
  //this filter is there if the content should be filtered additionally by a column and value
29
29
  filter, options) {
30
30
  return __awaiter(this, void 0, void 0, function* () {
31
- let query = this.supabase
32
- .from('shared_content')
33
- .select('*, scc:shared_content_completed(id, state)')
34
- .eq('content_type', contentType)
35
- .not('scc.state', 'in', '("completed","ongoing","hidden")')
36
- .is('deleted_at', null);
37
- if ((options === null || options === void 0 ? void 0 : options.excludeIds) && options.excludeIds.length > 0) {
38
- const excludeIds = options.excludeIds.filter((id) => !id.startsWith('internal-temp-id-'));
39
- // Supabase expects raw PostgREST syntax like '("id1","id2")'.
40
- const excludeList = `(${excludeIds.map((id) => `"${id}"`).join(',')})`;
41
- query = query.not('id', 'in', excludeList);
42
- }
43
- if (filter) {
44
- query.contains('data', filter);
45
- }
46
- const { data: newAssignments, error } = yield query.limit(30);
47
- if (error) {
48
- console.error('error fetching new assignments:', error);
49
- throw new Error('error fetching new assignments');
50
- }
51
- // console.log('newAssignments:', newAssignments);
52
- if (!(options === null || options === void 0 ? void 0 : options.alwaysGenerateNew) && newAssignments.length > 0) {
53
- const index = Math.floor(Math.random() * newAssignments.length);
54
- return newAssignments[index];
55
- }
31
+ // The db cache of the shared content is temporary disabled until the new shared content implementation is completed
32
+ // if (false) {
33
+ // let query = this.supabase
34
+ // .from('shared_content')
35
+ // .select('*, scc:shared_content_completed(id, state)')
36
+ // .eq('content_type', contentType)
37
+ // .not('scc.state', 'in', '("completed","ongoing","hidden")')
38
+ // .is('deleted_at', null);
39
+ // if (options?.excludeIds?.length ?? 0 > 0) {
40
+ // const excludeIds = options.excludeIds.filter((id) => !id.startsWith('internal-temp-id-'));
41
+ // // Supabase expects raw PostgREST syntax like '("id1","id2")'.
42
+ // const excludeList = `(${excludeIds.map((id) => `"${id}"`).join(',')})`;
43
+ // query = query.not('id', 'in', excludeList);
44
+ // }
45
+ // if (filter) {
46
+ // query.contains('data', filter);
47
+ // }
48
+ // const { data: newAssignments, error } = await query.limit(30);
49
+ // if (error) {
50
+ // console.error('error fetching new assignments:', error);
51
+ // throw new Error('error fetching new assignments');
52
+ // }
53
+ // // console.log('newAssignments:', newAssignments);
54
+ // if (!options?.alwaysGenerateNew && newAssignments.length > 0) {
55
+ // const index = Math.floor(Math.random() * newAssignments.length);
56
+ // return newAssignments[index];
57
+ // }
58
+ // }
56
59
  const instructions = yield this.generateNewAssignment(contentType, generatorInstructions, filter);
57
60
  console.log('instructions:', instructions);
58
61
  //create the shared content object
@@ -4,7 +4,8 @@ import { ThirdPartyModule, TOptions } from 'i18next';
4
4
  */
5
5
  export declare class Translator {
6
6
  private currentLanguage;
7
- private isInitialized;
7
+ private initializationState;
8
+ private initializationPromise;
8
9
  private i18n;
9
10
  constructor(initialLanguage: string);
10
11
  /**
@@ -13,8 +13,9 @@ import { createInstance } from 'i18next';
13
13
  */
14
14
  export class Translator {
15
15
  constructor(initialLanguage) {
16
- this.isInitialized = false;
17
16
  this.currentLanguage = initialLanguage;
17
+ this.initializationState = 'not-inited';
18
+ this.initializationPromise = null;
18
19
  }
19
20
  /**
20
21
  * Initialize translator with user's language
@@ -22,21 +23,41 @@ export class Translator {
22
23
  */
23
24
  initialize() {
24
25
  return __awaiter(this, void 0, void 0, function* () {
25
- if (this.isInitialized)
26
+ // If already finished, return immediately
27
+ if (this.initializationState === 'finished') {
26
28
  return;
27
- const translations = yield this.fetchTranslations(this.currentLanguage);
28
- const instance = createInstance({
29
- lng: this.currentLanguage,
30
- resources: {
31
- [this.currentLanguage]: {
32
- translation: translations,
33
- },
34
- },
35
- debug: window.location.hostname === 'localhost',
36
- });
37
- yield instance.init();
38
- this.i18n = instance;
39
- this.isInitialized = true;
29
+ }
30
+ // If currently initializing, wait for the existing initialization to complete
31
+ if (this.initializationState === 'initing' && this.initializationPromise) {
32
+ return this.initializationPromise;
33
+ }
34
+ // Start initialization
35
+ this.initializationState = 'initing';
36
+ // Create a promise that will be resolved when initialization completes
37
+ this.initializationPromise = (() => __awaiter(this, void 0, void 0, function* () {
38
+ try {
39
+ const translations = yield this.fetchTranslations(this.currentLanguage);
40
+ const instance = createInstance({
41
+ lng: this.currentLanguage,
42
+ resources: {
43
+ [this.currentLanguage]: {
44
+ translation: translations,
45
+ },
46
+ },
47
+ debug: window.location.hostname === 'localhost',
48
+ });
49
+ yield instance.init();
50
+ this.i18n = instance;
51
+ this.initializationState = 'finished';
52
+ }
53
+ catch (error) {
54
+ // Reset state on error so it can be retried
55
+ this.initializationState = 'not-inited';
56
+ this.initializationPromise = null;
57
+ throw error;
58
+ }
59
+ }))();
60
+ return this.initializationPromise;
40
61
  });
41
62
  }
42
63
  getTranslationUrl(language) {
@@ -101,6 +122,6 @@ export class Translator {
101
122
  * Check if translator is initialized
102
123
  */
103
124
  isReady() {
104
- return this.isInitialized;
125
+ return this.initializationState === 'finished';
105
126
  }
106
127
  }
@@ -67,7 +67,7 @@ export class EventBusHandler {
67
67
  }
68
68
  const event = this.createEvent(sender, topic, data, eventId);
69
69
  const handlers = this.getMatchingHandlers(event.topic);
70
- handlers.forEach(handler => {
70
+ handlers.forEach((handler) => {
71
71
  if (handler.ignoreSender && handler.ignoreSender.includes(sender)) {
72
72
  // console.log("ignore event as its in the ignoreSender list", { event, ignoreList: handler.ignoreSender });
73
73
  return;
@@ -79,7 +79,9 @@ export class EventBusHandler {
79
79
  this.logAndThrowError(false, `No handlers found for topic: ` + topic);
80
80
  }
81
81
  // If it's a response to a request
82
- if (eventId && this.responseResolvers.has(eventId) && !skipResponseTrigger) {
82
+ if (eventId &&
83
+ this.responseResolvers.has(eventId) &&
84
+ !skipResponseTrigger) {
83
85
  // console.log("[Rimori] Resolving response to request: " + eventId, event.data);
84
86
  this.responseResolvers.get(eventId)(event);
85
87
  this.responseResolvers.delete(eventId);
@@ -93,7 +95,7 @@ export class EventBusHandler {
93
95
  * @returns An EventListener object containing an off() method to unsubscribe the listeners.
94
96
  */
95
97
  on(topics, handler, ignoreSender = []) {
96
- const ids = this.toArray(topics).map(topic => {
98
+ const ids = this.toArray(topics).map((topic) => {
97
99
  this.logIfDebug(`Subscribing to ` + topic, { ignoreSender });
98
100
  if (!this.validateTopic(topic)) {
99
101
  this.logAndThrowError(true, `Invalid topic: ` + topic);
@@ -102,14 +104,33 @@ export class EventBusHandler {
102
104
  this.listeners.set(topic, new Set());
103
105
  }
104
106
  const id = Math.floor(Math.random() * 10000000000);
105
- // Type assertion to handle the generic type mismatch
106
- const eventHandler = handler;
107
- this.listeners.get(topic).add({ id, handler: eventHandler, ignoreSender });
108
- this.logIfDebug(`Subscribed to ` + topic, { listenerId: id, ignoreSender });
107
+ // To prevent infinite loops and processing the same eventId multiple times
108
+ const blackListedEventIds = [];
109
+ const eventHandler = (data) => {
110
+ if (blackListedEventIds.some((item) => item.eventId === data.eventId && item.sender === data.sender)) {
111
+ console.log("BLACKLISTED EVENT ID", data.eventId, data);
112
+ return;
113
+ }
114
+ blackListedEventIds.push({
115
+ eventId: data.eventId,
116
+ sender: data.sender,
117
+ });
118
+ if (blackListedEventIds.length > 100) {
119
+ blackListedEventIds.shift();
120
+ }
121
+ return handler(data);
122
+ };
123
+ this.listeners
124
+ .get(topic)
125
+ .add({ id, handler: eventHandler, ignoreSender });
126
+ this.logIfDebug(`Subscribed to ` + topic, {
127
+ listenerId: id,
128
+ ignoreSender,
129
+ });
109
130
  return btoa(JSON.stringify({ topic, id }));
110
131
  });
111
132
  return {
112
- off: () => this.off(ids)
133
+ off: () => this.off(ids),
113
134
  };
114
135
  }
115
136
  /**
@@ -121,7 +142,7 @@ export class EventBusHandler {
121
142
  */
122
143
  respond(sender, topic, handler) {
123
144
  const topics = Array.isArray(topic) ? topic : [topic];
124
- const listeners = topics.map(topic => {
145
+ const listeners = topics.map((topic) => {
125
146
  const blackListedEventIds = [];
126
147
  //To allow event communication inside the same plugin the sender needs to be ignored but the events still need to be checked for the same event just reaching the subscriber to prevent infinite loops
127
148
  const finalIgnoreSender = !topic.startsWith("self.") ? [sender] : [];
@@ -131,7 +152,7 @@ export class EventBusHandler {
131
152
  return;
132
153
  }
133
154
  blackListedEventIds.push(data.eventId);
134
- if (blackListedEventIds.length > 20) {
155
+ if (blackListedEventIds.length > 100) {
135
156
  blackListedEventIds.shift();
136
157
  }
137
158
  const response = typeof handler === "function" ? yield handler(data) : handler;
@@ -139,11 +160,11 @@ export class EventBusHandler {
139
160
  }), finalIgnoreSender);
140
161
  this.logIfDebug(`Added respond listener ` + sender + " to topic " + topic, { listener, sender });
141
162
  return {
142
- off: () => listener.off()
163
+ off: () => listener.off(),
143
164
  };
144
165
  });
145
166
  return {
146
- off: () => listeners.forEach(listener => listener.off())
167
+ off: () => listeners.forEach((listener) => listener.off()),
147
168
  };
148
169
  }
149
170
  /**
@@ -169,13 +190,16 @@ export class EventBusHandler {
169
190
  * @param listenerIds - The ids of the listeners to unsubscribe from.
170
191
  */
171
192
  off(listenerIds) {
172
- this.toArray(listenerIds).forEach(fullId => {
193
+ this.toArray(listenerIds).forEach((fullId) => {
173
194
  const { topic, id } = JSON.parse(atob(fullId));
174
195
  const listeners = this.listeners.get(topic) || new Set();
175
- listeners.forEach(listener => {
196
+ listeners.forEach((listener) => {
176
197
  if (listener.id === Number(id)) {
177
198
  listeners.delete(listener);
178
- this.logIfDebug(`Removed listener ` + fullId, { topic, listenerId: id });
199
+ this.logIfDebug(`Removed listener ` + fullId, {
200
+ topic,
201
+ listenerId: id,
202
+ });
179
203
  }
180
204
  });
181
205
  });
@@ -197,7 +221,7 @@ export class EventBusHandler {
197
221
  }
198
222
  const event = this.createEvent(sender, topic, data || {});
199
223
  this.logIfDebug(`Requesting data from ` + topic, { event });
200
- return new Promise(resolve => {
224
+ return new Promise((resolve) => {
201
225
  this.responseResolvers.set(event.eventId, (value) => resolve(value));
202
226
  this.emitInternal(sender, topic, data || {}, event.eventId, true);
203
227
  });
@@ -240,10 +264,13 @@ export class EventBusHandler {
240
264
  }
241
265
  // Validate action part
242
266
  const validActions = ["request", "create", "update", "delete", "trigger"];
243
- if (validActions.some(a => action.startsWith(a))) {
267
+ if (validActions.some((a) => action.startsWith(a))) {
244
268
  return true;
245
269
  }
246
- this.logAndThrowError(false, `Invalid event topic name. The action: ` + action + ". Must be or start with one of: " + validActions.join(", "));
270
+ this.logAndThrowError(false, `Invalid event topic name. The action: ` +
271
+ action +
272
+ ". Must be or start with one of: " +
273
+ validActions.join(", "));
247
274
  return false;
248
275
  }
249
276
  logIfDebug(...args) {
@@ -31,7 +31,7 @@ export interface MenuEntry {
31
31
  plugin_id: string;
32
32
  action_key: string;
33
33
  text: string;
34
- icon?: React.ReactNode;
34
+ iconUrl?: string;
35
35
  }
36
36
  export type MainPanelAction = {
37
37
  plugin_id: string;
package/dist/index.d.ts CHANGED
@@ -11,7 +11,8 @@ export { Translator } from './controller/TranslationController';
11
11
  export type { TOptions } from 'i18next';
12
12
  export type { SharedContent, SharedContentObjectRequest } from './controller/SharedContentController';
13
13
  export type { Exercise } from './controller/ExerciseController';
14
- export type { UserInfo, Language } from './controller/SettingsController';
14
+ export type { UserInfo, Language, UserRole } from './controller/SettingsController';
15
15
  export type { Message, ToolInvocation } from './controller/AIController';
16
16
  export type { TriggerAction } from './controller/ExerciseController';
17
17
  export type { MacroAccomplishmentPayload, MicroAccomplishmentPayload } from './controller/AccomplishmentController';
18
+ export type { EventBusMessage } from './fromRimori/EventBus';
@@ -2,9 +2,16 @@ import { SupabaseClient } from '@supabase/supabase-js';
2
2
  import { UserInfo } from '../controller/SettingsController';
3
3
  import { ActivePlugin, Plugin } from '../fromRimori/PluginTypes';
4
4
  export interface Guild {
5
- id: string;
6
- longTermGoalOverride: string;
7
5
  allowUserPluginSettings: boolean;
6
+ city: string | null;
7
+ country: string | null;
8
+ description: string | null;
9
+ id: string;
10
+ isPublic: boolean;
11
+ name: string;
12
+ ownerId: string;
13
+ primaryLanguage: string;
14
+ scope: string;
8
15
  }
9
16
  export interface RimoriInfo {
10
17
  url: string;
@@ -19,6 +26,7 @@ export interface RimoriInfo {
19
26
  profile: UserInfo;
20
27
  mainPanelPlugin?: ActivePlugin;
21
28
  sidePanelPlugin?: ActivePlugin;
29
+ interfaceLanguage: string;
22
30
  }
23
31
  export declare class RimoriCommunicationHandler {
24
32
  private port;
@@ -26,15 +26,17 @@ export class RimoriCommunicationHandler {
26
26
  }
27
27
  initMessageChannel(worker = false) {
28
28
  const listener = (event) => {
29
- console.log('[PluginController] window message', { origin: event.origin, data: event.data });
29
+ // console.log('[PluginController] window message', { origin: event.origin, data: event.data });
30
30
  const { type, pluginId, queryParams, rimoriInfo } = event.data || {};
31
31
  const [transferredPort] = event.ports || [];
32
32
  if (type !== 'rimori:init' || !transferredPort || pluginId !== this.pluginId) {
33
- console.log('[PluginController] message ignored (not init or wrong plugin)', {
34
- type,
35
- pluginId,
36
- hasPort: !!transferredPort,
37
- });
33
+ // console.log('[PluginController] message ignored (not init or wrong plugin)', {
34
+ // type,
35
+ // pluginId,
36
+ // currentPluginId: this.pluginId,
37
+ // hasPortProperty: !!transferredPort,
38
+ // event
39
+ // });
38
40
  return;
39
41
  }
40
42
  this.queryParams = queryParams || {};
@@ -182,7 +184,7 @@ export class RimoriCommunicationHandler {
182
184
  else {
183
185
  // In main thread context, use EventBus
184
186
  const { data } = yield EventBus.request(this.pluginId, 'global.supabase.requestAccess');
185
- console.log({ data });
187
+ // console.log({ data });
186
188
  this.rimoriInfo = data;
187
189
  this.supabase = createClient(this.rimoriInfo.url, this.rimoriInfo.key, {
188
190
  accessToken: () => Promise.resolve(this.getToken()),
@@ -54,6 +54,7 @@ export declare class Logger {
54
54
  private getBrowserInfo;
55
55
  /**
56
56
  * Capture a screenshot of the current page.
57
+ * Dynamically imports html2canvas only in browser environments.
57
58
  * @returns Promise resolving to base64 screenshot or null if failed
58
59
  */
59
60
  private captureScreenshot;
@@ -7,7 +7,6 @@ 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 html2canvas from 'html2canvas';
11
10
  /**
12
11
  * Singleton Logger class for Rimori client plugins.
13
12
  * Handles all logging levels, production filtering, and log transmission to Rimori.
@@ -242,17 +241,30 @@ export class Logger {
242
241
  }
243
242
  /**
244
243
  * Capture a screenshot of the current page.
244
+ * Dynamically imports html2canvas only in browser environments.
245
245
  * @returns Promise resolving to base64 screenshot or null if failed
246
246
  */
247
247
  captureScreenshot() {
248
248
  return __awaiter(this, void 0, void 0, function* () {
249
- if (typeof window !== 'undefined' && typeof document !== 'undefined') {
249
+ // Only attempt to capture screenshot in browser environments
250
+ if (typeof window === 'undefined' || typeof document === 'undefined') {
251
+ return null;
252
+ }
253
+ try {
254
+ // Dynamically import html2canvas only when window is available
255
+ // This allows tree-shaking for environments without window (e.g., Node.js)
256
+ const html2canvas = (yield import('html2canvas')).default;
250
257
  const canvas = yield html2canvas(document.body);
251
258
  const screenshot = canvas.toDataURL('image/png');
252
259
  // this.originalConsole.log("screenshot captured", screenshot)
253
260
  return screenshot;
254
261
  }
255
- return null;
262
+ catch (error) {
263
+ // html2canvas may not be available or may fail to load
264
+ // Silently fail to avoid breaking logging functionality
265
+ this.originalConsole.warn('[Rimori Logger] Failed to capture screenshot:', error);
266
+ return null;
267
+ }
256
268
  });
257
269
  }
258
270
  /**
@@ -131,7 +131,8 @@ export declare class RimoriClient {
131
131
  * @param text Optional text to be used for the action like for example text that the translator would look up.
132
132
  */
133
133
  emitSidebarAction: (pluginId: string, actionKey: string, text?: string) => void;
134
- onMainPanelAction: (callback: (data: MainPanelAction) => void, actionsToListen?: string[]) => void;
134
+ onMainPanelAction: (callback: (data: MainPanelAction) => void, actionsToListen?: string | string[]) => void;
135
+ onSidePanelAction: (callback: (data: MainPanelAction) => void, actionsToListen?: string | string[]) => void;
135
136
  };
136
137
  navigation: {
137
138
  toDashboard: () => void;