@rimori/client 1.1.9 → 1.2.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.
Files changed (151) hide show
  1. package/README.md +128 -45
  2. package/dist/cli/scripts/init/dev-registration.d.ts +35 -0
  3. package/dist/cli/scripts/init/dev-registration.js +175 -0
  4. package/dist/cli/scripts/init/env-setup.d.ts +9 -0
  5. package/dist/cli/scripts/init/env-setup.js +43 -0
  6. package/dist/cli/scripts/init/file-operations.d.ts +4 -0
  7. package/dist/cli/scripts/init/file-operations.js +51 -0
  8. package/dist/cli/scripts/init/html-cleaner.d.ts +4 -0
  9. package/dist/cli/scripts/init/html-cleaner.js +38 -0
  10. package/dist/cli/scripts/init/main.d.ts +2 -0
  11. package/dist/cli/scripts/init/main.js +159 -0
  12. package/dist/cli/scripts/init/package-setup.d.ts +32 -0
  13. package/dist/cli/scripts/init/package-setup.js +75 -0
  14. package/dist/cli/scripts/init/router-transformer.d.ts +6 -0
  15. package/dist/cli/scripts/init/router-transformer.js +254 -0
  16. package/dist/cli/scripts/init/tailwind-config.d.ts +4 -0
  17. package/dist/cli/scripts/init/tailwind-config.js +56 -0
  18. package/dist/cli/scripts/init/vite-config.d.ts +20 -0
  19. package/dist/cli/scripts/init/vite-config.js +54 -0
  20. package/dist/cli/scripts/release/release-config-upload.d.ts +7 -0
  21. package/dist/cli/scripts/release/release-config-upload.js +116 -0
  22. package/dist/cli/scripts/release/release-db-update.d.ts +6 -0
  23. package/dist/cli/scripts/release/release-db-update.js +100 -0
  24. package/dist/cli/scripts/release/release-file-upload.d.ts +6 -0
  25. package/dist/cli/scripts/release/release-file-upload.js +136 -0
  26. package/dist/cli/scripts/release/release.d.ts +23 -0
  27. package/dist/cli/scripts/release/release.js +70 -0
  28. package/dist/cli/types/DatabaseTypes.d.ts +103 -0
  29. package/dist/cli/types/DatabaseTypes.js +2 -0
  30. package/dist/components/ai/Assistant.js +4 -4
  31. package/dist/components/ai/Avatar.d.ts +3 -2
  32. package/dist/components/ai/Avatar.js +10 -5
  33. package/dist/components/ai/EmbeddedAssistent/CircleAudioAvatar.js +1 -1
  34. package/dist/components/ai/EmbeddedAssistent/VoiceRecoder.d.ts +1 -0
  35. package/dist/components/ai/EmbeddedAssistent/VoiceRecoder.js +12 -6
  36. package/dist/components/ai/utils.js +0 -1
  37. package/dist/components/audio/Playbutton.js +3 -3
  38. package/dist/{core → components}/components/ContextMenu.js +2 -2
  39. package/dist/components.d.ts +5 -5
  40. package/dist/components.js +5 -5
  41. package/dist/core/controller/AIController.d.ts +15 -0
  42. package/dist/core/controller/AIController.js +120 -0
  43. package/dist/{controller → core/controller}/ObjectController.d.ts +8 -0
  44. package/dist/{controller → core/controller}/SettingsController.d.ts +12 -4
  45. package/dist/{controller → core/controller}/SettingsController.js +0 -25
  46. package/dist/{controller → core/controller}/SharedContentController.d.ts +10 -19
  47. package/dist/{controller → core/controller}/SharedContentController.js +11 -11
  48. package/dist/core/core.d.ts +13 -0
  49. package/dist/core/core.js +8 -0
  50. package/dist/{plugin/fromRimori → fromRimori}/EventBus.d.ts +3 -3
  51. package/dist/{plugin/fromRimori → fromRimori}/EventBus.js +25 -8
  52. package/dist/fromRimori/PluginTypes.d.ts +171 -0
  53. package/dist/hooks/UseChatHook.d.ts +2 -1
  54. package/dist/hooks/UseChatHook.js +3 -3
  55. package/dist/index.d.ts +5 -3
  56. package/dist/index.js +4 -3
  57. package/dist/plugin/AccomplishmentHandler.d.ts +1 -1
  58. package/dist/plugin/AccomplishmentHandler.js +1 -1
  59. package/dist/plugin/PluginController.d.ts +16 -3
  60. package/dist/plugin/PluginController.js +24 -18
  61. package/dist/plugin/RimoriClient.d.ts +22 -17
  62. package/dist/plugin/RimoriClient.js +35 -25
  63. package/dist/plugin/StandaloneClient.js +11 -8
  64. package/dist/plugin/ThemeSetter.d.ts +1 -0
  65. package/dist/plugin/ThemeSetter.js +9 -6
  66. package/dist/providers/PluginProvider.d.ts +3 -0
  67. package/dist/providers/PluginProvider.js +4 -4
  68. package/dist/utils/Language.d.ts +2 -1
  69. package/dist/utils/Language.js +4 -2
  70. package/dist/utils/difficultyConverter.js +1 -1
  71. package/dist/utils/endpoint.d.ts +2 -0
  72. package/dist/utils/endpoint.js +2 -0
  73. package/dist/worker/WorkerSetup.js +3 -1
  74. package/example/docs/devdocs.md +231 -0
  75. package/example/docs/overview.md +29 -0
  76. package/example/docs/userdocs.md +123 -0
  77. package/example/rimori.config.ts +89 -0
  78. package/example/worker/vite.config.ts +23 -0
  79. package/example/worker/worker.ts +11 -0
  80. package/package.json +15 -9
  81. package/src/cli/scripts/init/dev-registration.ts +193 -0
  82. package/src/cli/scripts/init/env-setup.ts +44 -0
  83. package/src/cli/scripts/init/file-operations.ts +58 -0
  84. package/src/cli/scripts/init/html-cleaner.ts +48 -0
  85. package/src/cli/scripts/init/main.ts +171 -0
  86. package/src/cli/scripts/init/package-setup.ts +117 -0
  87. package/src/cli/scripts/init/router-transformer.ts +329 -0
  88. package/src/cli/scripts/init/tailwind-config.ts +75 -0
  89. package/src/cli/scripts/init/vite-config.ts +73 -0
  90. package/src/cli/scripts/release/release-config-upload.ts +114 -0
  91. package/src/cli/scripts/release/release-db-update.ts +97 -0
  92. package/src/cli/scripts/release/release-file-upload.ts +138 -0
  93. package/src/cli/scripts/release/release.ts +69 -0
  94. package/src/cli/types/DatabaseTypes.ts +117 -0
  95. package/src/components/ai/Assistant.tsx +4 -4
  96. package/src/components/ai/Avatar.tsx +24 -7
  97. package/src/components/ai/EmbeddedAssistent/CircleAudioAvatar.tsx +1 -1
  98. package/src/components/ai/EmbeddedAssistent/VoiceRecoder.tsx +16 -8
  99. package/src/components/ai/utils.ts +0 -2
  100. package/src/components/audio/Playbutton.tsx +3 -3
  101. package/src/{core → components}/components/ContextMenu.tsx +3 -3
  102. package/src/components.ts +6 -6
  103. package/src/core/controller/AIController.ts +122 -0
  104. package/src/core/controller/ObjectController.ts +115 -0
  105. package/src/{controller → core/controller}/SettingsController.ts +13 -29
  106. package/src/{controller → core/controller}/SharedContentController.ts +18 -28
  107. package/src/core/core.ts +15 -0
  108. package/src/{plugin/fromRimori → fromRimori}/EventBus.ts +28 -10
  109. package/src/fromRimori/PluginTypes.ts +203 -0
  110. package/src/hooks/UseChatHook.ts +5 -4
  111. package/src/index.ts +5 -3
  112. package/src/plugin/AccomplishmentHandler.ts +1 -1
  113. package/src/plugin/PluginController.ts +35 -23
  114. package/src/plugin/RimoriClient.ts +48 -41
  115. package/src/plugin/StandaloneClient.ts +11 -8
  116. package/src/plugin/ThemeSetter.ts +12 -8
  117. package/src/providers/PluginProvider.tsx +7 -4
  118. package/src/utils/Language.ts +4 -2
  119. package/src/utils/difficultyConverter.ts +3 -3
  120. package/src/utils/endpoint.ts +2 -0
  121. package/src/worker/WorkerSetup.ts +4 -2
  122. package/dist/components/PluginController.d.ts +0 -21
  123. package/dist/components/PluginController.js +0 -116
  124. package/dist/controller/AIController.d.ts +0 -23
  125. package/dist/controller/AIController.js +0 -93
  126. package/dist/controller/SidePluginController.d.ts +0 -3
  127. package/dist/controller/SidePluginController.js +0 -31
  128. package/dist/core.d.ts +0 -7
  129. package/dist/core.js +0 -7
  130. package/dist/plugin/ContextMenu.d.ts +0 -17
  131. package/dist/plugin/ContextMenu.js +0 -45
  132. package/dist/plugin/fromRimori/PluginTypes.d.ts +0 -48
  133. package/dist/plugin/fromRimori/SupabaseHandler.d.ts +0 -13
  134. package/dist/plugin/fromRimori/SupabaseHandler.js +0 -55
  135. package/dist/providers/PluginController.d.ts +0 -21
  136. package/dist/providers/PluginController.js +0 -116
  137. package/dist/types/Actions.d.ts +0 -4
  138. package/dist/types/Actions.js +0 -1
  139. package/src/controller/AIController.ts +0 -112
  140. package/src/controller/ObjectController.ts +0 -107
  141. package/src/controller/SidePluginController.ts +0 -25
  142. package/src/core.ts +0 -8
  143. package/src/plugin/fromRimori/PluginTypes.ts +0 -64
  144. package/src/types/Actions.ts +0 -6
  145. /package/dist/{core → components}/components/ContextMenu.d.ts +0 -0
  146. /package/dist/{controller → core/controller}/ObjectController.js +0 -0
  147. /package/dist/{controller → core/controller}/VoiceController.d.ts +0 -0
  148. /package/dist/{controller → core/controller}/VoiceController.js +0 -0
  149. /package/dist/{plugin/fromRimori → fromRimori}/PluginTypes.js +0 -0
  150. /package/src/{controller → core/controller}/VoiceController.ts +0 -0
  151. /package/src/{plugin/fromRimori → fromRimori}/readme.md +0 -0
@@ -0,0 +1,203 @@
1
+ // whole configuration of a plugin (from the database)
2
+ export type Plugin = Omit<RimoriPluginConfig, 'context_menu_actions'> & {
3
+ version: string;
4
+ endpoint: string;
5
+ assetEndpoint: string;
6
+ context_menu_actions: MenuEntry[];
7
+ release_channel: "alpha" | "beta" | "stable";
8
+ }
9
+
10
+ // browsable page of a plugin
11
+ export interface PluginPage {
12
+ id: string;
13
+ name: string;
14
+ url: string;
15
+ // Whether the page should be shown in the navbar
16
+ show: boolean;
17
+ description: string;
18
+ root: "vocabulary" | "grammar" | "reading" | "listening" | "watching" | "writing" | "speaking" | "other" | "community";
19
+ // The actions that can be triggered in the plugin
20
+ // The key is the action key. The other entries are additional properties needed when triggering the action
21
+ action?: {
22
+ key: string;
23
+ parameters: ObjectTool;
24
+ }
25
+ }
26
+
27
+ // a sidebar page of a plugin
28
+ export interface SidebarPage {
29
+ // identifier of the page. Used to know which page to trigger when clicking on the sidebar
30
+ id: string;
31
+ // name of the page. Shown in the settings
32
+ name: string;
33
+ // description of the page. Shown in the settings
34
+ description: string;
35
+ // relative or absolute URL or path to the plugin's page
36
+ url: string;
37
+ // relative or absolute URL or path to the plugin's icon image
38
+ icon: string;
39
+ }
40
+
41
+ // context menu entry being configured in the plugin configuration
42
+ export interface MenuEntry {
43
+ // id of the plugin that the menu entry belongs to
44
+ plugin_id: string;
45
+ // identifier of the menu entry action. Used to know which entry to trigger when clicking on the context menu
46
+ action_key: string;
47
+ // text of the menu entry. Shown in the context menu
48
+ text: string;
49
+ // icon of the menu entry. Shown in the context menu
50
+ icon?: React.ReactNode;
51
+ }
52
+
53
+ // an action from the main panel that can be triggered and performs an action in the main panel
54
+ export type MainPanelAction = {
55
+ plugin_id: string;
56
+ action_key: string;
57
+ } & Record<string, string>;
58
+
59
+ // an action from the context menu that can be triggered and performs an action in the sidebar plugin
60
+ export interface ContextMenuAction {
61
+ // selected text when clicking on the context menu
62
+ text: string;
63
+ // id of the plugin that the action belongs to
64
+ plugin_id: string;
65
+ // key of the action. Used to know which action to trigger when clicking on the context menu
66
+ action_key: string
67
+ }
68
+
69
+ /**
70
+ * Rimori plugin structure representing the complete configuration
71
+ * of a Rimori plugin with all metadata and configuration options.
72
+ */
73
+ export interface RimoriPluginConfig {
74
+ id: string;
75
+ /**
76
+ * Basic information about the plugin including branding and core details.
77
+ */
78
+ info: {
79
+ /** The display name of the plugin shown to users */
80
+ title: string;
81
+ /** Detailed description introducing the plugin */
82
+ description: string;
83
+ /** relative or absolute URL or path to the plugin's logo/icon image */
84
+ logo: string;
85
+ /** Optional website URL for the plugin's homepage or link to plugins owner for contributions */
86
+ website?: string;
87
+ }
88
+ /**
89
+ * Configuration for different types of pages.
90
+ */
91
+ pages: {
92
+ /** Optional external URL where the plugin is hosted instead of the default CDN */
93
+ external_hosted_url?: string;
94
+ /** Array of main plugin pages that appear in the application's main navigation (can be disabled using the 'show' flag) */
95
+ main: PluginPage[];
96
+ /** Array of sidebar pages that appear in the sidebar for quick access (can be disabled using the 'show' flag) */
97
+ sidebar: SidebarPage[];
98
+ /** Optional path to the plugin's settings/configuration page */
99
+ settings?: string;
100
+ /** Optional array of event topics the plugin pages can listen to for cross-plugin communication */
101
+ topics?: string[];
102
+ }
103
+ /**
104
+ * Context menu actions that the plugin registers to appear in right-click menus throughout the application.
105
+ */
106
+ context_menu_actions: Omit<MenuEntry, "plugin_id">[];
107
+ /**
108
+ * Documentation paths for different types of plugin documentation.
109
+ */
110
+ documentation: {
111
+ /** Path to the general overview documentation. It's shown upon installation of the plugin. */
112
+ overview_path: string;
113
+ /** Path to user-facing documentation and guides */
114
+ user_path: string;
115
+ /** Path to developer documentation for plugin development */
116
+ developer_path: string;
117
+ }
118
+ /**
119
+ * Configuration for the plugin's web worker if it uses background processing or exposes actions to other plugins.
120
+ */
121
+ worker?: {
122
+ /** Relative path to the web worker JavaScript file. Mostly it's 'web-worker.js' which is located in the public folder. */
123
+ url: string;
124
+ /** Optional array of event topics the worker should listen to in addition to events having the pluginId in the topic. Can be a wildcard. Example: 'global.topic.*' or 'pluginId.*' */
125
+ topics?: string[];
126
+ };
127
+ }
128
+
129
+ // copied from llm edge function
130
+
131
+ export interface Tool {
132
+ name: string;
133
+ description: string;
134
+ parameters: {
135
+ name: string;
136
+ description: string;
137
+ type: "string" | "number" | "boolean";
138
+ }[];
139
+ execute?: (args: Record<string, any>) => Promise<unknown> | unknown | void;
140
+ }
141
+
142
+ /**
143
+ * The tool definition structure is used for LLM function calling and plugin action parameters.
144
+ * It defines the schema for tools that can be used by Language Learning Models (LLMs)
145
+ * and plugin actions.
146
+ *
147
+ * @example
148
+ * ```typescript
149
+ * const flashcardTool: Tool = {
150
+ * total_amount: {
151
+ * type: 'string',
152
+ * enum: ['default', '10', '20', '50'],
153
+ * description: 'Number of flashcards to practice'
154
+ * },
155
+ * deck: {
156
+ * type: 'string',
157
+ * enum: ['latest', 'random', 'oldest', 'mix', 'best_known'],
158
+ * description: 'Type of deck to practice'
159
+ * }
160
+ * };
161
+ * ```
162
+ *
163
+ */
164
+ export type ObjectTool = {
165
+ [key: string]: ToolParameter;
166
+ };
167
+
168
+ /**
169
+ * Parameter definition for LLM tools and plugin actions.
170
+ * Defines the structure, validation rules, and metadata for individual tool parameters.
171
+ * Used to create type-safe interfaces between LLMs, plugins, and the Rimori platform.
172
+ */
173
+ interface ToolParameter {
174
+ /** The data type of the parameter - can be primitive, nested object, or array */
175
+ type: ToolParameterType;
176
+ /** Human-readable description of the parameter's purpose and usage */
177
+ description: string;
178
+ /** Optional array of allowed values for enumerated parameters */
179
+ enum?: string[];
180
+ /** Whether the parameter is optional */
181
+ optional?: boolean;
182
+ }
183
+
184
+ /**
185
+ * Union type defining all possible parameter types for LLM tools.
186
+ * Supports primitive types, nested objects for complex data structures,
187
+ * and arrays of objects for collections. The tuple notation [{}] indicates
188
+ * arrays of objects with a specific structure.
189
+ *
190
+ * @example Primitive: 'string' | 'number' | 'boolean'
191
+ * @example Nested object: { name: { type: 'string' }, age: { type: 'number' } }
192
+ * @example Array of objects: [{ id: { type: 'string' }, value: { type: 'number' } }]
193
+ */
194
+ type ToolParameterType =
195
+ | PrimitiveType
196
+ | { [key: string]: ToolParameter } // for nested objects
197
+ | [{ [key: string]: ToolParameter }]; // for arrays of objects (notice the tuple type)
198
+
199
+ /**
200
+ * Primitive data types supported by the LLM tool system.
201
+ * These align with JSON schema primitive types and TypeScript basic types.
202
+ */
203
+ type PrimitiveType = 'string' | 'number' | 'boolean';
@@ -1,14 +1,15 @@
1
1
  import React from "react";
2
- import { Message, Tool, ToolInvocation } from "../controller/AIController";
2
+ import { Tool } from "../fromRimori/PluginTypes";
3
3
  import { usePlugin } from "../providers/PluginProvider";
4
+ import { Message, ToolInvocation } from "../core/controller/AIController";
4
5
 
5
6
  export function useChat(tools?: Tool[]) {
6
7
  const [messages, setMessages] = React.useState<Message[]>([]);
7
8
  const [isLoading, setIsLoading] = React.useState(false);
8
- const { llm } = usePlugin();
9
+ const { ai } = usePlugin();
9
10
 
10
11
  const append = (appendMessages: Message[]) => {
11
- llm.getSteamedText([...messages, ...appendMessages], (id, message, finished: boolean, toolInvocations?: ToolInvocation[]) => {
12
+ ai.getSteamedText([...messages, ...appendMessages], (id, message, finished: boolean, toolInvocations?: ToolInvocation[]) => {
12
13
  const lastMessage = messages[messages.length - 1];
13
14
  setIsLoading(!finished);
14
15
 
@@ -16,7 +17,7 @@ export function useChat(tools?: Tool[]) {
16
17
  lastMessage.content = message;
17
18
  setMessages([...messages, lastMessage]);
18
19
  } else {
19
- setMessages([...messages, ...appendMessages, { id, role: 'assistant', content: message, toolInvocations }]);
20
+ setMessages([...messages, ...appendMessages, { id, role: 'assistant', content: message, toolCalls: toolInvocations }]);
20
21
  }
21
22
  }, tools);
22
23
  };
package/src/index.ts CHANGED
@@ -1,9 +1,11 @@
1
1
  // Re-export everything
2
- export * from './core';
3
2
  export * from './components';
4
3
  export * from "./hooks/UseChatHook";
5
- export * from "./plugin/RimoriClient";
4
+ export * from "./plugin/PluginController";
6
5
  export * from "./providers/PluginProvider";
6
+ export * from "./cli/types/DatabaseTypes";
7
7
  export * from "./utils/difficultyConverter";
8
8
  export * from "./utils/PluginUtils";
9
- export * from "./plugin/PluginController";
9
+ export * from "./utils/Language";
10
+ export * from "./fromRimori/PluginTypes";
11
+ export { FirstMessages } from "./components/ai/utils";
@@ -1,4 +1,4 @@
1
- import { EventBus, EventBusMessage } from "./fromRimori/EventBus";
1
+ import { EventBus, EventBusMessage } from "../fromRimori/EventBus";
2
2
 
3
3
  export type AccomplishmentMessage = EventBusMessage<MicroAccomplishmentPayload>;
4
4
 
@@ -1,19 +1,24 @@
1
- import { AuthSessionMissingError, createClient, SupabaseClient } from '@supabase/supabase-js';
2
- import { EventBus, EventBusMessage } from './fromRimori/EventBus';
1
+ import { createClient, SupabaseClient } from '@supabase/supabase-js';
2
+ import { UserInfo } from '../core/controller/SettingsController';
3
+ import { EventBus, EventBusMessage } from '../fromRimori/EventBus';
4
+ import { Plugin } from '../fromRimori/PluginTypes';
3
5
  import { RimoriClient } from "./RimoriClient";
4
- import { setTheme } from './ThemeSetter';
5
6
  import { StandaloneClient } from './StandaloneClient';
7
+ import { setTheme } from './ThemeSetter';
6
8
 
7
9
  // Add declaration for WorkerGlobalScope
8
10
  declare const WorkerGlobalScope: any;
9
11
 
10
- interface SupabaseInfo {
12
+ export interface RimoriInfo {
11
13
  url: string,
12
14
  key: string,
15
+ backendUrl: string,
13
16
  token: string,
14
17
  expiration: Date,
15
18
  tablePrefix: string,
16
19
  pluginId: string
20
+ installedPlugins: Plugin[]
21
+ profile: UserInfo
17
22
  }
18
23
 
19
24
  export class PluginController {
@@ -21,7 +26,7 @@ export class PluginController {
21
26
  private static instance: PluginController;
22
27
  private communicationSecret: string | null = null;
23
28
  private supabase: SupabaseClient | null = null;
24
- private supabaseInfo: SupabaseInfo | null = null;
29
+ private rimoriInfo: RimoriInfo | null = null;
25
30
  private pluginId: string;
26
31
 
27
32
  private constructor(pluginId: string, standalone: boolean) {
@@ -34,7 +39,7 @@ export class PluginController {
34
39
 
35
40
  //no need to forward messages to parent in standalone mode
36
41
  if (standalone) return;
37
-
42
+
38
43
  window.addEventListener("message", (event) => {
39
44
  // console.log("client: message received", event);
40
45
  const { topic, sender, data, eventId } = event.data.event as EventBusMessage;
@@ -78,47 +83,54 @@ export class PluginController {
78
83
  return this.communicationSecret;
79
84
  }
80
85
 
81
- public async getClient(): Promise<{ supabase: SupabaseClient, tablePrefix: string, pluginId: string }> {
86
+ public async getClient(): Promise<{ supabase: SupabaseClient, info: RimoriInfo }> {
82
87
  if (
83
88
  this.supabase &&
84
- this.supabaseInfo &&
85
- this.supabaseInfo.expiration > new Date()
89
+ this.rimoriInfo &&
90
+ this.rimoriInfo.expiration > new Date()
86
91
  ) {
87
- return { supabase: this.supabase, tablePrefix: this.supabaseInfo.tablePrefix, pluginId: this.supabaseInfo.pluginId };
92
+ return { supabase: this.supabase, info: this.rimoriInfo };
88
93
  }
89
94
 
90
- const { data } = await EventBus.request<SupabaseInfo>(this.pluginId, "global.supabase.requestAccess");
91
- this.supabaseInfo = data;
92
- this.supabase = createClient(this.supabaseInfo.url, this.supabaseInfo.key, {
95
+ const { data } = await EventBus.request<RimoriInfo>(this.pluginId, "global.supabase.requestAccess");
96
+ this.rimoriInfo = data;
97
+ this.supabase = createClient(this.rimoriInfo.url, this.rimoriInfo.key, {
93
98
  accessToken: () => Promise.resolve(this.getToken())
94
99
  });
95
100
 
96
- return { supabase: this.supabase, tablePrefix: this.supabaseInfo.tablePrefix, pluginId: this.supabaseInfo.pluginId };
101
+ return { supabase: this.supabase, info: this.rimoriInfo };
97
102
  }
98
103
 
99
104
  public async getToken() {
100
- if (this.supabaseInfo && this.supabaseInfo.expiration && this.supabaseInfo.expiration > new Date()) {
101
- return this.supabaseInfo.token;
105
+ if (this.rimoriInfo && this.rimoriInfo.expiration && this.rimoriInfo.expiration > new Date()) {
106
+ return this.rimoriInfo.token;
102
107
  }
103
108
 
104
109
  const { data } = await EventBus.request<{ token: string, expiration: Date }>(this.pluginId, "global.supabase.requestAccess");
105
110
 
106
- if (!this.supabaseInfo) {
111
+ if (!this.rimoriInfo) {
107
112
  throw new Error("Supabase info not found");
108
113
  }
109
114
 
110
- this.supabaseInfo.token = data.token;
111
- this.supabaseInfo.expiration = data.expiration;
115
+ this.rimoriInfo.token = data.token;
116
+ this.rimoriInfo.expiration = data.expiration;
112
117
 
113
- return this.supabaseInfo.token;
118
+ return this.rimoriInfo.token;
114
119
  }
115
120
 
116
121
  public getSupabaseUrl() {
117
- if (!this.supabaseInfo) {
122
+ if (!this.rimoriInfo) {
118
123
  throw new Error("Supabase info not found");
119
124
  }
120
125
 
121
- return this.supabaseInfo.url;
126
+ return this.rimoriInfo.url;
127
+ }
128
+
129
+ public getBackendUrl() {
130
+ if (!this.rimoriInfo) {
131
+ throw new Error("Rimori info not found");
132
+ }
133
+ return this.rimoriInfo.backendUrl;
122
134
  }
123
135
 
124
136
  public getGlobalEventTopic(preliminaryTopic: string) {
@@ -138,7 +150,7 @@ export class PluginController {
138
150
  throw new Error(`The event topic must consist of 3 parts. <pluginId>.<topic area>.<action>. Received: ${preliminaryTopic}`);
139
151
  }
140
152
 
141
- const topicRoot = this.supabaseInfo?.pluginId ?? "global";
153
+ const topicRoot = this.rimoriInfo?.pluginId ?? "global";
142
154
  return `${topicRoot}.${preliminaryTopic}`;
143
155
  }
144
156
 
@@ -1,23 +1,16 @@
1
1
  import { PostgrestQueryBuilder } from "@supabase/postgrest-js";
2
2
  import { SupabaseClient } from "@supabase/supabase-js";
3
3
  import { GenericSchema } from "@supabase/supabase-js/dist/module/lib/types";
4
- import { generateText, Message, OnLLMResponse, streamChatGPT, Tool } from "../controller/AIController";
5
- import { generateObject as generateObjectFunction, ObjectRequest } from "../controller/ObjectController";
6
- import { SettingsController, UserInfo } from "../controller/SettingsController";
7
- import { BasicAssignment, SharedContent, SharedContentController, SharedContentFilter, SharedContentObjectRequest } from "../controller/SharedContentController";
8
- import { getPlugins } from "../controller/SidePluginController";
9
- import { getSTTResponse, getTTSResponse } from "../controller/VoiceController";
4
+ import { generateText, Message, OnLLMResponse, streamChatGPT } from "../core/controller/AIController";
5
+ import { generateObject as generateObjectFunction, ObjectRequest } from "../core/controller/ObjectController";
6
+ import { SettingsController, UserInfo } from "../core/controller/SettingsController";
7
+ import { SharedContent, SharedContentController, SharedContentFilter, SharedContentObjectRequest } from "../core/controller/SharedContentController";
8
+ import { getSTTResponse, getTTSResponse } from "../core/controller/VoiceController";
9
+ import { EventBus, EventBusMessage, EventHandler, EventPayload } from "../fromRimori/EventBus";
10
+ import { Plugin, Tool } from "../fromRimori/PluginTypes";
10
11
  import { AccomplishmentHandler, AccomplishmentPayload } from "./AccomplishmentHandler";
11
- import { EventBus, EventBusMessage, EventHandler, EventPayload } from "./fromRimori/EventBus";
12
- import { Plugin } from "./fromRimori/PluginTypes";
13
- import { PluginController } from "./PluginController";
12
+ import { PluginController, RimoriInfo } from "./PluginController";
14
13
 
15
- interface RimoriClientOptions {
16
- pluginController: PluginController;
17
- supabase: SupabaseClient;
18
- tablePrefix: string;
19
- pluginId: string;
20
- }
21
14
 
22
15
  interface Db {
23
16
  from: {
@@ -66,16 +59,20 @@ export class RimoriClient {
66
59
  private sharedContentController: SharedContentController;
67
60
  private accomplishmentHandler: AccomplishmentHandler;
68
61
  private supabaseUrl: string;
69
- public db: Db;
62
+ private installedPlugins: Plugin[];
63
+ private profile: UserInfo;
70
64
  public plugin: PluginInterface;
65
+ public db: Db;
71
66
 
72
- private constructor(options: RimoriClientOptions) {
73
- this.superbase = options.supabase;
74
- this.pluginController = options.pluginController;
75
- this.settingsController = new SettingsController(options.supabase, options.pluginId);
67
+ private constructor(supabase: SupabaseClient, info: RimoriInfo, pluginController: PluginController) {
68
+ this.superbase = supabase;
69
+ this.pluginController = pluginController;
70
+ this.settingsController = new SettingsController(supabase, info.pluginId);
76
71
  this.sharedContentController = new SharedContentController(this.superbase, this);
77
72
  this.supabaseUrl = this.pluginController.getSupabaseUrl();
78
- this.accomplishmentHandler = new AccomplishmentHandler(options.pluginId);
73
+ this.accomplishmentHandler = new AccomplishmentHandler(info.pluginId);
74
+ this.installedPlugins = info.installedPlugins;
75
+ this.profile = info.profile;
79
76
 
80
77
  this.from = this.from.bind(this);
81
78
 
@@ -83,11 +80,11 @@ export class RimoriClient {
83
80
  from: this.from,
84
81
  storage: this.superbase.storage,
85
82
  // functions: this.superbase.functions,
86
- tablePrefix: options.tablePrefix,
83
+ tablePrefix: info.tablePrefix,
87
84
  getTableName: this.getTableName.bind(this),
88
85
  }
89
86
  this.plugin = {
90
- pluginId: options.pluginId,
87
+ pluginId: info.pluginId,
91
88
  setSettings: async (settings: any) => {
92
89
  await this.settingsController.setSettings(settings);
93
90
  },
@@ -95,10 +92,10 @@ export class RimoriClient {
95
92
  return await this.settingsController.getSettings<T>(defaultSettings);
96
93
  },
97
94
  getInstalled: async (): Promise<Plugin[]> => {
98
- return getPlugins(this.superbase);
95
+ return this.installedPlugins;
99
96
  },
100
97
  getUserInfo: async (): Promise<UserInfo> => {
101
- return this.settingsController.getUserInfo();
98
+ return this.profile;
102
99
  }
103
100
  }
104
101
  }
@@ -132,11 +129,11 @@ export class RimoriClient {
132
129
  * Subscribe to an event.
133
130
  * @param topic The topic to subscribe to.
134
131
  * @param callback The callback to call when the event is emitted.
135
- * @returns The unsubscribe ids.
132
+ * @returns An EventListener object containing an off() method to unsubscribe the listeners.
136
133
  */
137
134
  on: <T = EventPayload>(topic: string | string[], callback: EventHandler<T>) => {
138
135
  const topics = Array.isArray(topic) ? topic : [topic];
139
- return topics.map(topic => EventBus.on<T>(this.pluginController.getGlobalEventTopic(topic), callback));
136
+ return EventBus.on<T>(topics.map(t => this.pluginController.getGlobalEventTopic(t)), callback);
140
137
  },
141
138
  /**
142
139
  * Subscribe to an event once.
@@ -151,8 +148,9 @@ export class RimoriClient {
151
148
  * @param topic The topic to respond to.
152
149
  * @param data The data to respond with.
153
150
  */
154
- respond: <T = EventPayload>(topic: string, data: EventPayload | ((data: EventBusMessage<T>) => EventPayload | Promise<EventPayload>)) => {
155
- EventBus.respond(this.plugin.pluginId, this.pluginController.getGlobalEventTopic(topic), data);
151
+ respond: <T = EventPayload>(topic: string | string[], data: EventPayload | ((data: EventBusMessage<T>) => EventPayload | Promise<EventPayload>)) => {
152
+ const topics = Array.isArray(topic) ? topic : [topic];
153
+ EventBus.respond(this.plugin.pluginId, topics.map(t => this.pluginController.getGlobalEventTopic(t)), data);
156
154
  },
157
155
  /**
158
156
  * Emit an accomplishment.
@@ -176,14 +174,20 @@ export class RimoriClient {
176
174
  * @param text Optional text to be used for the action like for example text that the translator would look up.
177
175
  */
178
176
  emitSidebarAction: (pluginId: string, actionKey: string, text?: string) => {
179
- this.event.emit("global.sidebar.triggerAction", { pluginId, actionKey, text });
177
+ this.event.emit("global.sidebar.triggerAction", { plugin_id: pluginId, action_key: actionKey, text });
178
+ }
179
+ }
180
+
181
+ public navigation = {
182
+ toDashboard: () => {
183
+ this.event.emit("global.navigation.triggerToDashboard");
180
184
  }
181
185
  }
182
186
 
183
187
  public static async getInstance(pluginController: PluginController): Promise<RimoriClient> {
184
188
  if (!RimoriClient.instance) {
185
- const { supabase, tablePrefix, pluginId } = await pluginController.getClient();
186
- RimoriClient.instance = new RimoriClient({ pluginController, supabase, tablePrefix, pluginId });
189
+ const client = await pluginController.getClient();
190
+ RimoriClient.instance = new RimoriClient(client.supabase, client.info, pluginController);
187
191
  }
188
192
  return RimoriClient.instance;
189
193
  }
@@ -201,17 +205,20 @@ export class RimoriClient {
201
205
  }
202
206
 
203
207
  private getTableName(type: string) {
208
+ if (type.startsWith("global_")) {
209
+ return type.replace("global_", "");
210
+ }
204
211
  return this.db.tablePrefix + "_" + type;
205
212
  }
206
213
 
207
- public llm = {
214
+ public ai = {
208
215
  getText: async (messages: Message[], tools?: Tool[]): Promise<string> => {
209
216
  const token = await this.pluginController.getToken();
210
- return generateText(this.supabaseUrl, messages, tools || [], token).then(({ messages }) => messages[0].content[0].text);
217
+ return generateText(this.pluginController.getBackendUrl(), messages, tools || [], token).then(({ messages }) => messages[0].content[0].text);
211
218
  },
212
219
  getSteamedText: async (messages: Message[], onMessage: OnLLMResponse, tools?: Tool[]) => {
213
220
  const token = await this.pluginController.getToken();
214
- streamChatGPT(this.supabaseUrl, messages, tools || [], onMessage, token);
221
+ streamChatGPT(this.pluginController.getBackendUrl(), messages, tools || [], onMessage, token);
215
222
  },
216
223
  getVoice: async (text: string, voice = "alloy", speed = 1, language?: string): Promise<Blob> => {
217
224
  const token = await this.pluginController.getToken();
@@ -240,7 +247,7 @@ export class RimoriClient {
240
247
  * @param id The id of the shared content item.
241
248
  * @returns The shared content item.
242
249
  */
243
- get: async <T = any>(contentType: string, id: string): Promise<BasicAssignment<T>> => {
250
+ get: async <T = any>(contentType: string, id: string): Promise<SharedContent<T>> => {
244
251
  return await this.sharedContentController.getSharedContent(contentType, id);
245
252
  },
246
253
  /**
@@ -250,7 +257,7 @@ export class RimoriClient {
250
257
  * @param limit The optional limit for the number of results.
251
258
  * @returns The list of shared content items.
252
259
  */
253
- getList: async <T = any>(contentType: string, filter?: SharedContentFilter, limit?: number): Promise<BasicAssignment<T>[]> => {
260
+ getList: async <T = any>(contentType: string, filter?: SharedContentFilter, limit?: number): Promise<SharedContent<T>[]> => {
254
261
  return await this.sharedContentController.getSharedContentList(contentType, filter, limit);
255
262
  },
256
263
  /**
@@ -266,7 +273,7 @@ export class RimoriClient {
266
273
  generatorInstructions: SharedContentObjectRequest,
267
274
  filter?: SharedContentFilter,
268
275
  privateTopic?: boolean,
269
- ): Promise<BasicAssignment<T>> => {
276
+ ): Promise<SharedContent<T>> => {
270
277
  return await this.sharedContentController.getNewSharedContent(contentType, generatorInstructions, filter, privateTopic);
271
278
  },
272
279
  /**
@@ -274,7 +281,7 @@ export class RimoriClient {
274
281
  * @param content The content to create.
275
282
  * @returns The new shared content item.
276
283
  */
277
- create: async <T = any>(content: Omit<SharedContent<T>, 'id'>): Promise<BasicAssignment<T>> => {
284
+ create: async <T = any>(content: Omit<SharedContent<T>, 'id'>): Promise<SharedContent<T>> => {
278
285
  return await this.sharedContentController.createSharedContent(content);
279
286
  },
280
287
  /**
@@ -283,7 +290,7 @@ export class RimoriClient {
283
290
  * @param content The content to update.
284
291
  * @returns The updated shared content item.
285
292
  */
286
- update: async <T = any>(id: string, content: Partial<SharedContent<T>>): Promise<BasicAssignment<T>> => {
293
+ update: async <T = any>(id: string, content: Partial<SharedContent<T>>): Promise<SharedContent<T>> => {
287
294
  return await this.sharedContentController.updateSharedContent(id, content);
288
295
  },
289
296
  /**
@@ -299,7 +306,7 @@ export class RimoriClient {
299
306
  * @param id The id of the shared content item to remove.
300
307
  * @returns The removed shared content item.
301
308
  */
302
- remove: async (id: string): Promise<BasicAssignment<any>> => {
309
+ remove: async (id: string): Promise<SharedContent<any>> => {
303
310
  return await this.sharedContentController.removeSharedContent(id);
304
311
  }
305
312
  }
@@ -1,5 +1,6 @@
1
- import { EventBus } from "./fromRimori/EventBus";
2
1
  import { createClient, SupabaseClient } from "@supabase/supabase-js";
2
+ import { EventBus } from "../fromRimori/EventBus";
3
+ import { DEFAULT_ANON_KEY, DEFAULT_ENDPOINT } from "../utils/endpoint";
3
4
 
4
5
  export interface StandaloneConfig {
5
6
  url: string,
@@ -18,14 +19,13 @@ export class StandaloneClient {
18
19
 
19
20
  public static async getInstance(): Promise<StandaloneClient> {
20
21
  if (!StandaloneClient.instance) {
21
- const config = await fetch("http://localhost:3000/config.json").then(res => res.json()).catch(err => {
22
+ const config = await fetch("https://app.rimori.se/config.json").then(res => res.json()).catch(err => {
22
23
  console.warn("Error fetching config.json, using default values", err);
23
- return {
24
- SUPABASE_URL: "https://pheptqdoqsdnadgoihvr.supabase.co",
25
- SUPABASE_ANON_KEY: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InBoZXB0cWRvcXNkbmFkZ29paHZyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3MzE2OTY2ODcsImV4cCI6MjA0NzI3MjY4N30.4GPFAXTF8685FaXISdAPNCIM-H3RGLo8GbyhQpu1mP0",
26
- }
27
24
  });
28
- StandaloneClient.instance = new StandaloneClient({ url: config.SUPABASE_URL, key: config.SUPABASE_ANON_KEY });
25
+ StandaloneClient.instance = new StandaloneClient({
26
+ url: config?.SUPABASE_URL || DEFAULT_ENDPOINT,
27
+ key: config?.SUPABASE_ANON_KEY || DEFAULT_ANON_KEY,
28
+ });
29
29
  }
30
30
  return StandaloneClient.instance;
31
31
  }
@@ -58,7 +58,10 @@ export class StandaloneClient {
58
58
  EventBus.respond("standalone", "global.supabase.requestAccess", async () => {
59
59
  const session = await supabase.auth.getSession();
60
60
  console.log("session", session);
61
- const { data, error } = await supabase.functions.invoke("plugin-token", { headers: { authorization: `Bearer ${session.data.session?.access_token}` } });
61
+ const { data, error } = await supabase.functions.invoke("plugin-token", {
62
+ body: { pluginId },
63
+ headers: { authorization: `Bearer ${session.data.session?.access_token}` },
64
+ });
62
65
  if (error) {
63
66
  throw new Error("Failed to get plugin token. " + error.message);
64
67
  }
@@ -1,16 +1,20 @@
1
1
  export function setTheme() {
2
- const urlParams = new URLSearchParams(window.location.search);
3
-
4
- let theme = urlParams.get('theme');
5
- if (!theme || theme === 'system') {
6
- theme = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
7
- }
8
-
9
2
  document.documentElement.classList.add("dark:text-gray-200");
10
3
 
11
- if (theme === 'dark') {
4
+ if (isDarkTheme()) {
12
5
  document.documentElement.setAttribute("data-theme", "dark");
13
6
  document.documentElement.classList.add('dark', "dark:bg-gray-950");
14
7
  document.documentElement.style.background = "hsl(var(--background))";
15
8
  }
9
+ }
10
+
11
+ export function isDarkTheme(): boolean {
12
+ const urlParams = new URLSearchParams(window.location.search);
13
+
14
+ let theme = urlParams.get('theme');
15
+ if (!theme || theme === 'system') {
16
+ return window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
17
+ }
18
+
19
+ return theme === 'dark';
16
20
  }