@rimori/client 1.4.10 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (136) hide show
  1. package/README.md +140 -1171
  2. package/dist/{core/controller → controller}/AIController.d.ts +1 -1
  3. package/dist/{core/controller → controller}/AIController.js +2 -2
  4. package/dist/{plugin/AccomplishmentHandler.d.ts → controller/AccomplishmentController.d.ts} +1 -1
  5. package/dist/{plugin/AccomplishmentHandler.js → controller/AccomplishmentController.js} +1 -1
  6. package/dist/{core/controller → controller}/ExerciseController.d.ts +8 -6
  7. package/dist/{core/controller → controller}/ExerciseController.js +10 -9
  8. package/dist/{core/controller → controller}/ObjectController.d.ts +1 -1
  9. package/dist/{core/controller → controller}/ObjectController.js +1 -1
  10. package/dist/{core/controller → controller}/SettingsController.d.ts +2 -2
  11. package/dist/{core/controller → controller}/SharedContentController.d.ts +1 -1
  12. package/dist/{core/controller → controller}/VoiceController.d.ts +1 -1
  13. package/dist/index.d.ts +14 -8
  14. package/dist/index.js +7 -7
  15. package/dist/plugin/{PluginController.d.ts → CommunicationHandler.d.ts} +4 -7
  16. package/dist/plugin/{PluginController.js → CommunicationHandler.js} +19 -27
  17. package/dist/plugin/RimoriClient.d.ts +67 -68
  18. package/dist/plugin/RimoriClient.js +101 -43
  19. package/dist/worker/WorkerSetup.js +3 -3
  20. package/example/docs/devdocs.md +1 -1
  21. package/package.json +5 -26
  22. package/src/{core/controller → controller}/AIController.ts +3 -3
  23. package/src/{plugin/AccomplishmentHandler.ts → controller/AccomplishmentController.ts} +1 -1
  24. package/src/{core/controller → controller}/ExerciseController.ts +14 -11
  25. package/src/{core/controller → controller}/ObjectController.ts +2 -2
  26. package/src/{core/controller → controller}/SettingsController.ts +2 -2
  27. package/src/{core/controller → controller}/SharedContentController.ts +1 -1
  28. package/src/{core/controller → controller}/VoiceController.ts +2 -2
  29. package/src/fromRimori/readme.md +1 -1
  30. package/src/index.ts +14 -8
  31. package/src/plugin/{PluginController.ts → CommunicationHandler.ts} +24 -36
  32. package/src/plugin/RimoriClient.ts +127 -118
  33. package/src/worker/WorkerSetup.ts +6 -4
  34. package/tsconfig.json +5 -2
  35. package/dist/cli/scripts/release/release-translation-upload.d.ts +0 -6
  36. package/dist/cli/scripts/release/release-translation-upload.js +0 -87
  37. package/dist/components/CRUDModal.d.ts +0 -17
  38. package/dist/components/CRUDModal.js +0 -24
  39. package/dist/components/MarkdownEditor.d.ts +0 -8
  40. package/dist/components/MarkdownEditor.js +0 -48
  41. package/dist/components/Spinner.d.ts +0 -8
  42. package/dist/components/Spinner.js +0 -4
  43. package/dist/components/ai/Assistant.d.ts +0 -9
  44. package/dist/components/ai/Assistant.js +0 -58
  45. package/dist/components/ai/Avatar.d.ts +0 -14
  46. package/dist/components/ai/Avatar.js +0 -59
  47. package/dist/components/ai/EmbeddedAssistent/AudioInputField.d.ts +0 -7
  48. package/dist/components/ai/EmbeddedAssistent/AudioInputField.js +0 -37
  49. package/dist/components/ai/EmbeddedAssistent/CircleAudioAvatar.d.ts +0 -8
  50. package/dist/components/ai/EmbeddedAssistent/CircleAudioAvatar.js +0 -79
  51. package/dist/components/ai/EmbeddedAssistent/TTS/MessageSender.d.ts +0 -19
  52. package/dist/components/ai/EmbeddedAssistent/TTS/MessageSender.js +0 -91
  53. package/dist/components/ai/EmbeddedAssistent/TTS/Player.d.ts +0 -27
  54. package/dist/components/ai/EmbeddedAssistent/TTS/Player.js +0 -185
  55. package/dist/components/ai/EmbeddedAssistent/VoiceRecoder.d.ts +0 -11
  56. package/dist/components/ai/EmbeddedAssistent/VoiceRecoder.js +0 -95
  57. package/dist/components/ai/utils.d.ts +0 -6
  58. package/dist/components/ai/utils.js +0 -13
  59. package/dist/components/audio/Playbutton.d.ts +0 -15
  60. package/dist/components/audio/Playbutton.js +0 -80
  61. package/dist/components/components/ContextMenu.d.ts +0 -10
  62. package/dist/components/components/ContextMenu.js +0 -135
  63. package/dist/components.d.ts +0 -10
  64. package/dist/components.js +0 -11
  65. package/dist/core/controller/EnhancedUserInfo.d.ts +0 -0
  66. package/dist/core/controller/EnhancedUserInfo.js +0 -1
  67. package/dist/core/core.d.ts +0 -14
  68. package/dist/core/core.js +0 -7
  69. package/dist/hooks/I18nHooks.d.ts +0 -11
  70. package/dist/hooks/I18nHooks.js +0 -25
  71. package/dist/hooks/UseChatHook.d.ts +0 -10
  72. package/dist/hooks/UseChatHook.js +0 -29
  73. package/dist/i18n/I18nHooks.d.ts +0 -11
  74. package/dist/i18n/I18nHooks.js +0 -25
  75. package/dist/i18n/Translator.d.ts +0 -43
  76. package/dist/i18n/Translator.js +0 -118
  77. package/dist/i18n/config.d.ts +0 -7
  78. package/dist/i18n/config.js +0 -20
  79. package/dist/i18n/createI18nInstance.d.ts +0 -7
  80. package/dist/i18n/createI18nInstance.js +0 -31
  81. package/dist/i18n/hooks.d.ts +0 -11
  82. package/dist/i18n/hooks.js +0 -25
  83. package/dist/i18n/index.d.ts +0 -4
  84. package/dist/i18n/index.js +0 -4
  85. package/dist/i18n/types.d.ts +0 -7
  86. package/dist/i18n/types.js +0 -1
  87. package/dist/i18n/useRimoriI18n.d.ts +0 -11
  88. package/dist/i18n/useRimoriI18n.js +0 -41
  89. package/dist/plugin/ThemeSetter.d.ts +0 -2
  90. package/dist/plugin/ThemeSetter.js +0 -19
  91. package/dist/plugin/Translator.d.ts +0 -38
  92. package/dist/plugin/Translator.js +0 -101
  93. package/dist/providers/PluginProvider.d.ts +0 -12
  94. package/dist/providers/PluginProvider.js +0 -152
  95. package/dist/style.css +0 -110
  96. package/dist/style.css.map +0 -1
  97. package/dist/utils/Language.d.ts +0 -67
  98. package/dist/utils/Language.js +0 -69
  99. package/dist/utils/LanguageClass.d.ts +0 -36
  100. package/dist/utils/LanguageClass.example.d.ts +0 -0
  101. package/dist/utils/LanguageClass.example.js +0 -1
  102. package/dist/utils/LanguageClass.js +0 -50
  103. package/dist/utils/LanguageClass.test.d.ts +0 -0
  104. package/dist/utils/LanguageClass.test.js +0 -1
  105. package/dist/utils/PluginUtils.d.ts +0 -2
  106. package/dist/utils/PluginUtils.js +0 -23
  107. package/src/components/CRUDModal.tsx +0 -75
  108. package/src/components/MarkdownEditor.tsx +0 -144
  109. package/src/components/Spinner.tsx +0 -29
  110. package/src/components/ai/Assistant.tsx +0 -96
  111. package/src/components/ai/Avatar.tsx +0 -99
  112. package/src/components/ai/EmbeddedAssistent/AudioInputField.tsx +0 -73
  113. package/src/components/ai/EmbeddedAssistent/CircleAudioAvatar.tsx +0 -107
  114. package/src/components/ai/EmbeddedAssistent/TTS/MessageSender.ts +0 -96
  115. package/src/components/ai/EmbeddedAssistent/TTS/Player.ts +0 -197
  116. package/src/components/ai/EmbeddedAssistent/VoiceRecoder.tsx +0 -129
  117. package/src/components/ai/utils.ts +0 -21
  118. package/src/components/audio/Playbutton.tsx +0 -126
  119. package/src/components/components/ContextMenu.tsx +0 -179
  120. package/src/components.ts +0 -11
  121. package/src/core/core.ts +0 -15
  122. package/src/hooks/I18nHooks.ts +0 -33
  123. package/src/hooks/UseChatHook.ts +0 -38
  124. package/src/plugin/ThemeSetter.ts +0 -23
  125. package/src/providers/PluginProvider.tsx +0 -209
  126. package/src/style.scss +0 -136
  127. package/src/utils/PluginUtils.ts +0 -22
  128. /package/dist/{plugin → controller}/AudioController.d.ts +0 -0
  129. /package/dist/{plugin → controller}/AudioController.js +0 -0
  130. /package/dist/{core/controller → controller}/SettingsController.js +0 -0
  131. /package/dist/{core/controller → controller}/SharedContentController.js +0 -0
  132. /package/dist/{plugin → controller}/TranslationController.d.ts +0 -0
  133. /package/dist/{plugin → controller}/TranslationController.js +0 -0
  134. /package/dist/{core/controller → controller}/VoiceController.js +0 -0
  135. /package/src/{plugin → controller}/AudioController.ts +0 -0
  136. /package/src/{plugin → controller}/TranslationController.ts +0 -0
package/README.md CHANGED
@@ -1,23 +1,35 @@
1
1
  # Rimori Client Package
2
2
 
3
- The **@rimori/client** package is a comprehensive React library that enables plugins to seamlessly integrate with the Rimori learning platform. It provides database access, AI/LLM integration, inter-plugin communication, community features, and pre-built UI components.
3
+ The `@rimori/client` package is the framework-agnostic runtime and CLI that powers Rimori plugins. Use it inside plugin iframes, workers, and build scripts to access Rimori platform features such as database access, AI, shared content, and the event bus. All React-specific helpers and UI components are now published separately in `@rimori/react-client`.
4
4
 
5
5
  ## Table of Contents
6
-
6
+ - [Overview](#overview)
7
7
  - [Installation](#installation)
8
- - [Quick Start Plugin Development](#quick-start-plugin-development)
9
- - [Releasing Your Plugin to Rimori](#releasing-your-plugin-to-rimori)
8
+ - [Relationship to @rimori/react-client](#relationship-to-rimori-react-client)
10
9
  - [Quick Start](#quick-start)
11
- - [Core API - usePlugin Hook](#core-api---useplugin-hook)
12
- - [Database Integration](#database-integration)
13
- - [LLM Integration](#llm-integration)
14
- - [Event System](#event-system)
15
- - [Community Features](#community-features)
16
- - [Components](#components)
17
- - [Hooks](#hooks)
10
+ - [CLI Tooling](#cli-tooling)
11
+ - [Runtime API](#runtime-api)
12
+ - [Bootstrapping](#bootstrapping)
13
+ - [Plugin Interface](#plugin-interface)
14
+ - [Database Access](#database-access)
15
+ - [AI & Voice](#ai--voice)
16
+ - [Event Bus & Actions](#event-bus--actions)
17
+ - [Community Content](#community-content)
18
+ - [Workers & Standalone Development](#workers--standalone-development)
18
19
  - [Utilities](#utilities)
19
20
  - [TypeScript Support](#typescript-support)
20
- - [Examples](#examples)
21
+ - [Example Integration](#example-integration)
22
+ - [Troubleshooting](#troubleshooting)
23
+
24
+ ## Overview
25
+
26
+ `@rimori/client` gives you direct, typed access to the Rimori platform:
27
+ - Bootstrap authenticated plugin sessions and fetch Rimori context.
28
+ - Run Supabase queries against your plugin's dedicated schema.
29
+ - Call AI services for text, structured data, or voice.
30
+ - Communicate with Rimori and other plugins through the event bus.
31
+ - Share content with the community and emit accomplishments.
32
+ - Ship and upgrade plugins by using the bundled CLI.
21
33
 
22
34
  ## Installation
23
35
 
@@ -27,1239 +39,196 @@ npm install @rimori/client
27
39
  yarn add @rimori/client
28
40
  ```
29
41
 
30
- ## Quick Start Plugin Development
31
-
32
- The Rimori Client package includes powerful CLI tools to eliminate the tedious setup process and get you building your plugin fast. The initialization script handles authentication, plugin registration, environment setup, and all necessary boilerplate configuration.
33
-
34
- ### Prerequisites
35
-
36
- Before initializing your plugin, ensure you have:
37
-
38
- 1. **Node.js and yarn/npm installed**
39
- 2. **A Rimori account** - You'll need to login during initialization to receive your access token
40
-
41
- ### Initializing a New Plugin
42
+ ## Relationship to @rimori/react-client
42
43
 
43
- Open Lovable and vibe code the look of your plugin.
44
-
45
- Then connect it to your Github account.
46
-
47
- Clone the git repository:
44
+ If you are building a React-based plugin UI, install the companion package:
48
45
 
49
46
  ```bash
50
- git clone ...
51
- cd my-awesome-plugin
52
-
53
- # Initialize with Rimori Client (this handles everything!)
54
- npx @rimori/client rimori-init
47
+ npm install @rimori/react-client
55
48
  ```
56
49
 
57
- ### What the Init Script Does
50
+ `@rimori/react-client` wraps this core runtime with React context, hooks, and prebuilt UI components. Use it for UI concerns (`PluginProvider`, `useRimori`, `useChat`, widgets). Keep importing non-UI functionality such as `RimoriClient`, `StandaloneClient`, `setupWorker`, or the CLI directly from `@rimori/client`.
58
51
 
59
- The `rimori-init` command automates the entire plugin setup process:
52
+ ## Quick Start
60
53
 
61
- 1. **🔐 Authentication**: Prompts for your Rimori credentials and authenticates with the platform
62
- 2. **🚀 Plugin Registration**: Automatically registers your plugin and generates a unique plugin ID
63
- 3. **🔑 Access Token**: Provides you with an access token for future plugin releases
64
- 4. **📦 Package Configuration**: Updates `package.json` with plugin-specific settings
65
- 5. **⚙️ Environment Setup**: Creates `.env` files with your credentials
66
- 6. **📁 File Structure**: Copies all necessary boilerplate files and examples
67
- 7. **🎨 Configuration**: Sets up Vite, Tailwind, and router configurations
68
- 8. **📖 Documentation**: Provides example documentation and getting started guides
54
+ Instantiate the client once in your application entry point and reuse it everywhere:
69
55
 
70
- ### Upgrade Mode
56
+ ```ts
57
+ import { RimoriClient } from "@rimori/client";
71
58
 
72
- If you need to upgrade an existing plugin's configuration without changing the plugin ID:
73
-
74
- ```bash
75
- yarn rimori-init --upgrade
76
- ```
59
+ async function bootstrap() {
60
+ const client = await RimoriClient.getInstance("your-plugin-id");
77
61
 
78
- ### Development Setup
62
+ const user = client.plugin.getUserInfo();
63
+ const { data } = await client.db
64
+ .from("notes")
65
+ .select("*")
66
+ .eq("user_id", user.profile_id);
79
67
 
80
- After initialization, start developing immediately:
81
-
82
- ```bash
83
- # Start development server
84
- yarn dev
68
+ console.log("Loaded notes", data);
69
+ }
85
70
 
86
- # Your plugin will be available at:
87
- # http://localhost:3000 (or your chosen port)
71
+ bootstrap().catch(console.error);
88
72
  ```
89
73
 
90
- Rimori client comes pre-configured with:
91
-
92
- - ✅ **Hot reload** for instant development feedback
93
- - ✅ **TypeScript support** with full type safety
94
- - ✅ **TailwindCSS** for modern styling
95
- - ✅ **React Router** for navigation
96
- - ✅ **Example components** and documentation
74
+ The instance exposes high-level controllers grouped under properties such as `plugin`, `db`, `ai`, `event`, `community`, `runtime`, and `navigation`.
97
75
 
98
- ## Releasing Your Plugin to Rimori
76
+ ## CLI Tooling
99
77
 
100
- Publishing your plugin to the Rimori platform is streamlined through the built-in `rimori-release` CLI tool. The release process handles database updates, file uploads, and plugin activation automatically.
78
+ Two CLI commands ship with the package (also available through `npx`):
101
79
 
102
- ### Prerequisites
80
+ ### `rimori-init`
103
81
 
104
- 1. **Plugin must be initialized** - Use `rimori-init` first to set up your plugin
105
- 2. **Build your plugin** - Ensure your plugin is built and the output is in the `dist/` directory
106
- 3. **Environment configured** - Your `.env` file should contain `RIMORI_TOKEN` (set during initialization)
82
+ - Authenticates against Rimori using your developer credentials.
83
+ - Registers the plugin and writes the plugin ID (`r_id`) into `package.json`.
84
+ - Provisions environment files, Vite/Tailwind scaffolding, worker configuration, and sample assets.
107
85
 
108
- ### Quick Release (Recommended)
109
-
110
- During plugin initialization, convenient release scripts are automatically added to your `package.json`. These scripts handle building and releasing in one command:
86
+ Usage:
111
87
 
112
88
  ```bash
113
- # Quick release commands (build + release)
114
- yarn release:alpha # Build and release to alpha channel
115
- yarn release:beta # Build and release to beta channel
116
- yarn release:stable # Build and release to stable channel
89
+ npx @rimori/client rimori-init
90
+ npx @rimori/client rimori-init --upgrade # refresh config without changing the plugin ID
117
91
  ```
118
92
 
119
- ### Manual Release Process
93
+ ### `rimori-release`
94
+
95
+ - Builds (optionally) and uploads the plugin bundle to Rimori.
96
+ - Updates release metadata, database migrations, and activates the chosen channel (`alpha`, `beta`, `stable`).
120
97
 
121
- If you prefer more control or need to understand the underlying process, you can use the `rimori-release` command directly:
98
+ Usage:
122
99
 
123
100
  ```bash
124
- # Build your plugin first
125
101
  yarn build
126
-
127
- # Then release to different channels
128
- yarn rimori-release alpha # For alpha testing
129
- yarn rimori-release beta # For beta releases
130
- yarn rimori-release stable # For production releases
131
- ```
132
-
133
- The plugin version is automatically read from your `package.json`, and the plugin ID is retrieved from the `r_id` field (set during initialization).
134
-
135
- ### What the Release Script Does
136
-
137
- The `rimori-release` command performs a complete release workflow:
138
-
139
- 1. **📋 Configuration Upload**: Sends plugin metadata and configuration to the platform
140
- 2. **🗄️ Database Updates**: Updates plugin information and release records
141
- 3. **📁 File Upload**: Uploads all files from your `dist/` directory to the platform
142
- 4. **🚀 Plugin Activation**: Activates the new version on the specified release channel
143
-
144
- ### Release Channels
145
-
146
- - **`alpha`**: Early development releases for internal testing
147
- - **`beta`**: Pre-release versions for beta testers
148
- - **`stable`**: Production-ready releases for all users
149
-
150
- ### Automatic Configuration
151
-
152
- During plugin initialization, the following are automatically configured:
153
-
154
- - `RIMORI_TOKEN`: Your authentication token (stored in `.env`)
155
- - `r_id`: Your unique plugin ID (stored in `package.json`)
156
- - **Release scripts**: `yarn release:alpha`, `yarn release:beta`, `yarn release:stable`
157
- - **Build configuration**: TypeScript checking, Vite setup, and worker builds
158
-
159
- ### Troubleshooting
160
-
161
- If you encounter release issues:
162
-
163
- 1. **Missing token**: Ensure `RIMORI_TOKEN` is in your `.env` file
164
- 2. **No plugin ID**: Verify `r_id` exists in your `package.json`
165
- 3. **Build errors**: Run `yarn build` successfully before releasing
166
- 4. **Authentication**: Your token may have expired - re-run `rimori-init` if needed
167
-
168
- #### Worker Process Errors
169
-
170
- If you encounter errors like:
171
-
102
+ npx @rimori/client rimori-release alpha
172
103
  ```
173
- ReferenceError: process is not defined
174
- ```
175
-
176
- **Root Cause**: Your plugin is importing a library that tries to access `process` or `process.env` in the worker context. Web workers don't have access to Node.js globals like `process`.
177
-
178
- **Debugging Steps**:
179
-
180
- 1. **Check your imports**: Look through the files used in the worker which might import libraries that might access `process.env`:
181
- - React libraries (React Router, React Query, etc.)
182
- - UI component libraries (Material-UI, Ant Design, etc.)
183
- - Utility libraries that check for environment variables
184
-
185
- 2. **Verify Rimori Client imports**: Ensure you're importing from `@rimori/client/core` and not from `@rimori/client`:
186
104
 
187
- ```typescript
188
- // ✅ Correct - import from rimori client
189
- import { RimoriClient } from '@rimori/client/core';
105
+ During initialization, convenience scripts (`release:alpha`, `release:beta`, `release:stable`) are added to your project automatically.
190
106
 
191
- // Incorrect - direct imports that might access process.env directly or through their dependencies
192
- import { Avatar } from '@rimori/client';
193
- import { useQuery } from '@tanstack/react-query';
194
- ```
107
+ ## Runtime API
195
108
 
196
- 3. **Check your dependencies**: Look at your `package.json` for libraries that might be bundled into the worker:
197
- - UI frameworks (React, Vue, etc.)
198
- - State management libraries
199
- - Routing libraries
200
- - Any library that checks `process.env.NODE_ENV`
109
+ ### Bootstrapping
201
110
 
202
- **Solution**: Use only `@rimori/client/core` exports in your worker code. The Rimori client provides all necessary functionality without requiring Node.js globals.
203
-
204
- **Note**: The worker bundling order is determined by the build system and cannot be controlled. Libraries that access `process.env` will always cause this error in the worker context.
205
-
206
- ````
207
-
208
- ## Core API - usePlugin Hook
209
-
210
- The `useRimori()` hook is the main interface for accessing Rimori platform features:
211
-
212
- ```typescript
213
- import { useRimori } from "@rimori/client";
214
-
215
- const MyComponent = () => {
216
- const client = useRimori();
217
-
218
- // Access all client features
219
- const { db, llm, event, community, plugin } = client;
220
-
221
- return <div>My Plugin Content</div>;
222
- };
223
- ````
111
+ - `RimoriClient.getInstance(pluginId)` connect the sandboxed iframe to Rimori.
112
+ - `StandaloneClient` – authenticate when developing a plugin outside Rimori.
113
+ - `setupWorker()` register worker scripts that need the Rimori runtime.
224
114
 
225
115
  ### Plugin Interface
226
116
 
227
- ```typescript
228
- const { plugin } = useRimori();
229
-
230
- // Plugin information and settings
231
- plugin.pluginId: string // Current plugin ID
232
- plugin.getSettings<T>(defaultSettings: T): Promise<T> // Get plugin settings
233
- plugin.setSettings(settings: any): Promise<void> // Update plugin settings
234
- plugin.getInstalled(): Promise<Plugin[]> // Get all installed plugins
235
- plugin.getUserInfo(): Promise<UserInfo> // Get current user information
236
- ```
237
-
238
- **Example: Managing Flashcard Plugin Settings**
239
-
240
- ```typescript
241
- interface FlashcardSettings {
242
- dailyGoal: number;
243
- reviewInterval: 'easy' | 'medium' | 'hard';
244
- showAnswerDelay: number;
245
- enableAudioPronunciation: boolean;
246
- difficultyAlgorithm: 'spaced-repetition' | 'random' | 'progressive';
247
- }
248
-
249
- const FlashcardSettingsComponent = () => {
250
- const { plugin } = useRimori();
251
- const [settings, setSettings] = useState<FlashcardSettings>();
252
-
253
- useEffect(() => {
254
- const loadSettings = async () => {
255
- const defaultSettings: FlashcardSettings = {
256
- dailyGoal: 20,
257
- reviewInterval: 'medium',
258
- showAnswerDelay: 3,
259
- enableAudioPronunciation: true,
260
- difficultyAlgorithm: 'spaced-repetition'
261
- };
262
-
263
- const currentSettings = await plugin.getSettings(defaultSettings);
264
- setSettings(currentSettings);
265
- };
266
-
267
- loadSettings();
268
- }, []);
269
-
270
- const updateSettings = async (newSettings: Partial<FlashcardSettings>) => {
271
- const updated = { ...settings, ...newSettings };
272
- await plugin.setSettings(updated);
273
- setSettings(updated);
274
- };
275
-
276
- return (
277
- <div className="flashcard-settings">
278
- <label>
279
- Daily Goal: {settings?.dailyGoal} cards
280
- <input
281
- type="range"
282
- min="5"
283
- max="100"
284
- value={settings?.dailyGoal}
285
- onChange={(e) => updateSettings({ dailyGoal: parseInt(e.target.value) })}
286
- />
287
- </label>
288
-
289
- <label>
290
- Review Interval:
291
- <select
292
- value={settings?.reviewInterval}
293
- onChange={(e) => updateSettings({ reviewInterval: e.target.value as any })}
294
- >
295
- <option value="easy">Easy (longer intervals)</option>
296
- <option value="medium">Medium</option>
297
- <option value="hard">Hard (shorter intervals)</option>
298
- </select>
299
- </label>
300
-
301
- <label>
302
- <input
303
- type="checkbox"
304
- checked={settings?.enableAudioPronunciation}
305
- onChange={(e) => updateSettings({ enableAudioPronunciation: e.target.checked })}
306
- />
307
- Enable Audio Pronunciation
308
- </label>
309
- </div>
310
- );
311
- };
312
- ```
313
-
314
- ## Database Integration
315
-
316
- Access your plugin's dedicated database tables with full TypeScript support:
317
-
318
- ```typescript
319
- const { db } = useRimori();
320
-
321
- // Database interface
322
- db.from(tableName) // Query builder for tables/views - supports ALL Supabase operations
323
- db.storage // File storage access
324
- db.tablePrefix: string // Your plugin's table prefix
325
- db.getTableName(table: string): string // Get full table name with prefix
326
- ```
327
-
328
- The `db.from()` method provides access to the complete Supabase PostgREST API, supporting all database operations including:
329
-
330
- - **CRUD Operations**: `insert()`, `select()`, `update()`, `delete()`, `upsert()`
331
- - **Filtering**: `eq()`, `neq()`, `gt()`, `gte()`, `lt()`, `lte()`, `like()`, `ilike()`, `is()`, `in()`, `contains()`, `containedBy()`, `rangeLt()`, `rangeGt()`, `rangeGte()`, `rangeLte()`, `rangeAdjacent()`, `overlaps()`, `textSearch()`, `match()`, `not()`, `or()`, `filter()`
332
- - **Modifiers**: `order()`, `limit()`, `range()`, `single()`, `maybe_single()`, `csv()`, `geojson()`, `explain()`
333
- - **Aggregations**: `count()`, `sum()`, `avg()`, `min()`, `max()`
334
- - **Advanced Features**: Row Level Security (RLS), real-time subscriptions, stored procedures, and custom functions
335
-
336
- All operations automatically use your plugin's table prefix for security and isolation.
337
-
338
- **Column deprecation and removal (short policy)**
339
-
340
- - To remove a column, use a two-step process:
341
- 1. Mark the column with `deprecated: true` in your `rimori/db.config.ts` and release. The backend renames the column to `<name>_old`.
342
- 2. In a subsequent release, remove the column from the config. The backend drops `<name>_old`.
343
-
344
- **Example: CRUD Operations**
345
-
346
- ```typescript
347
- interface StudySession {
348
- id?: string;
349
- user_id: string;
350
- topic: string;
351
- duration: number;
352
- completed_at: string;
353
- }
354
-
355
- const StudySessionManager = () => {
356
- const { db } = useRimori();
357
-
358
- // Create a new study session
359
- const createSession = async (session: Omit<StudySession, 'id'>) => {
360
- const { data, error } = await db
361
- .from('study_sessions') // Automatically prefixed
362
- .insert(session)
363
- .select()
364
- .single();
365
-
366
- if (error) throw error;
367
- return data;
368
- };
369
-
370
- // Get user's study sessions
371
- const getUserSessions = async (userId: string) => {
372
- const { data, error } = await db
373
- .from('study_sessions')
374
- .select('*')
375
- .eq('user_id', userId)
376
- .order('completed_at', { ascending: false });
377
-
378
- if (error) throw error;
379
- return data;
380
- };
381
-
382
- // Update session
383
- const updateSession = async (id: string, updates: Partial<StudySession>) => {
384
- const { data, error } = await db
385
- .from('study_sessions')
386
- .update(updates)
387
- .eq('id', id)
388
- .select()
389
- .single();
390
-
391
- if (error) throw error;
392
- return data;
393
- };
394
-
395
- return (
396
- <div>
397
- {/* Your component UI */}
398
- </div>
399
- );
400
- };
401
- ```
402
-
403
- **File Storage Example**
404
-
405
- ```typescript
406
- const FileManager = () => {
407
- const { db } = useRimori();
408
-
409
- const uploadFile = async (file: File) => {
410
- const fileName = `uploads/${Date.now()}-${file.name}`;
411
-
412
- const { data, error } = await db.storage
413
- .from('plugin-files')
414
- .upload(fileName, file);
415
-
416
- if (error) throw error;
417
- return data;
418
- };
419
-
420
- const downloadFile = async (filePath: string) => {
421
- const { data, error } = await db.storage
422
- .from('plugin-files')
423
- .download(filePath);
424
-
425
- if (error) throw error;
426
- return data;
427
- };
428
-
429
- return <div>File Manager UI</div>;
430
- };
431
- ```
432
-
433
- ## LLM Integration
434
-
435
- Powerful AI/Language Model capabilities built-in:
436
-
437
- ```typescript
438
- const { llm } = useRimori();
439
-
440
- // Text generation
441
- llm.getText(messages: Message[], tools?: Tool[]): Promise<string>
442
-
443
- // Streaming text generation
444
- llm.getSteamedText(messages: Message[], onMessage: OnLLMResponse, tools?: Tool[]): void
445
-
446
- // Structured object generation
447
- llm.getObject(request: ObjectRequest): Promise<any>
448
-
449
- // Text-to-speech
450
- llm.getVoice(text: string, voice?: string, speed?: number, language?: string): Promise<Blob>
451
-
452
- // Speech-to-text
453
- llm.getTextFromVoice(file: Blob): Promise<string>
454
- ```
455
-
456
- **Example: AI Chat Assistant**
457
-
458
- ```typescript
459
- import { useChat } from "@rimori/client";
460
-
461
- const ChatAssistant = () => {
462
- const { messages, append, isLoading } = useChat();
463
- const [input, setInput] = useState('');
464
-
465
- const sendMessage = () => {
466
- if (!input.trim()) return;
467
-
468
- append([{
469
- role: 'user',
470
- content: input
471
- }]);
472
-
473
- setInput('');
474
- };
475
-
476
- return (
477
- <div className="chat-container">
478
- <div className="messages">
479
- {messages.map((message, index) => (
480
- <div key={index} className={`message ${message.role}`}>
481
- {message.content}
482
- </div>
483
- ))}
484
- {isLoading && <div className="message assistant">Thinking...</div>}
485
- </div>
486
-
487
- <div className="input-area">
488
- <input
489
- value={input}
490
- onChange={(e) => setInput(e.target.value)}
491
- onKeyPress={(e) => e.key === 'Enter' && sendMessage()}
492
- placeholder="Ask anything..."
493
- />
494
- <button onClick={sendMessage} disabled={isLoading}>
495
- Send
496
- </button>
497
- </div>
498
- </div>
499
- );
500
- };
501
- ```
502
-
503
- **Example: Structured Data Generation**
504
-
505
- ```typescript
506
- const QuizGenerator = () => {
507
- const { llm } = useRimori();
508
-
509
- const generateQuiz = async (topic: string) => {
510
- const quiz = await llm.getObject({
511
- schema: {
512
- type: "object",
513
- properties: {
514
- title: { type: "string" },
515
- questions: {
516
- type: "array",
517
- items: {
518
- type: "object",
519
- properties: {
520
- question: { type: "string" },
521
- options: { type: "array", items: { type: "string" } },
522
- correctAnswer: { type: "number" },
523
- explanation: { type: "string" }
524
- }
525
- }
526
- }
527
- }
528
- },
529
- prompt: `Create a quiz about ${topic} with 5 multiple choice questions.`
530
- });
531
-
532
- return quiz;
533
- };
534
-
535
- return <div>Quiz Generator UI</div>;
536
- };
537
- ```
538
-
539
- **Example: Voice Integration**
540
-
541
- ```typescript
542
- const VoiceAssistant = () => {
543
- const { llm } = useRimori();
544
-
545
- const speakText = async (text: string) => {
546
- const audioBlob = await llm.getVoice(text, "alloy", 1, "en");
547
- const audioUrl = URL.createObjectURL(audioBlob);
548
- const audio = new Audio(audioUrl);
549
- audio.play();
550
- };
551
-
552
- const transcribeAudio = async (audioFile: File) => {
553
- const transcript = await llm.getTextFromVoice(audioFile);
554
- return transcript;
555
- };
556
-
557
- return <div>Voice Assistant UI</div>;
558
- };
559
- ```
560
-
561
- ## Event System
562
-
563
- Robust inter-plugin communication and platform integration:
564
-
565
- ```typescript
566
- const { event } = useRimori();
567
-
568
- // Event methods
569
- event.emit(topic: string, data?: any, eventId?: number): void
570
- event.request<T>(topic: string, data?: any): Promise<EventBusMessage<T>>
571
- event.on<T>(topic: string | string[], callback: EventHandler<T>): string[]
572
- event.once<T>(topic: string, callback: EventHandler<T>): void
573
- event.respond<T>(topic: string, data: EventPayload | Function): void
574
-
575
- // Accomplishments
576
- event.emitAccomplishment(payload: AccomplishmentPayload): void
577
- event.onAccomplishment(topic: string, callback: Function): void
578
-
579
- // Sidebar actions
580
- event.emitSidebarAction(pluginId: string, actionKey: string, text?: string): void
581
- ```
582
-
583
- **Example: Plugin Communication**
584
-
585
- ```typescript
586
- const PluginCommunicator = () => {
587
- const { event } = useRimori();
588
-
589
- useEffect(() => {
590
- // Listen for messages from other plugins
591
- const unsubscribe = event.on('flashcards.newCard', (message) => {
592
- console.log('New flashcard created:', message.data);
593
- });
594
-
595
- // Listen for global events
596
- event.on('global.userProgress', (message) => {
597
- console.log('User progress updated:', message.data);
598
- });
599
-
600
- return () => {
601
- // Cleanup subscriptions
602
- unsubscribe.forEach(id => event.off(id));
603
- };
604
- }, []);
605
-
606
- const shareData = () => {
607
- // Emit data to other plugins
608
- event.emit('studyplan.dataUpdate', {
609
- type: 'session_completed',
610
- sessionId: '123',
611
- score: 85
612
- });
613
- };
614
-
615
- const requestData = async () => {
616
- // Request data from another plugin
617
- const response = await event.request('flashcards.getStats', {
618
- timeframe: 'week'
619
- });
620
-
621
- console.log('Flashcard stats:', response.data);
622
- };
623
-
624
- return (
625
- <div>
626
- <button onClick={shareData}>Share Progress</button>
627
- <button onClick={requestData}>Get Flashcard Stats</button>
628
- </div>
629
- );
630
- };
631
- ```
632
-
633
- **Example: Accomplishment System**
634
-
635
- ```typescript
636
- const AccomplishmentTracker = () => {
637
- const { event } = useRimori();
638
-
639
- const trackAccomplishment = () => {
640
- event.emitAccomplishment({
641
- type: 'study_milestone',
642
- title: 'Study Streak',
643
- description: 'Completed 7 days of studying',
644
- points: 100,
645
- metadata: {
646
- streakDays: 7,
647
- subject: 'Spanish'
648
- }
649
- });
650
- };
651
-
652
- useEffect(() => {
653
- // Listen for accomplishments from this plugin
654
- event.onAccomplishment('study_milestone', (accomplishment) => {
655
- console.log('New accomplishment:', accomplishment);
656
- // Show notification, update UI, etc.
657
- });
658
- }, []);
659
-
660
- return <div>Accomplishment Tracker UI</div>;
661
- };
662
- ```
663
-
664
- **Example: Sidebar Integration**
665
-
666
- ```typescript
667
- const SidebarIntegration = () => {
668
- const { event } = useRimori();
669
-
670
- const openTranslator = (text: string) => {
671
- // Trigger translator plugin in sidebar
672
- event.emitSidebarAction('translator', 'translate', text);
673
- };
674
-
675
- const openFlashcards = () => {
676
- // Open flashcards plugin
677
- event.emitSidebarAction('flashcards', 'review');
678
- };
679
-
680
- return (
681
- <div>
682
- <button onClick={() => openTranslator('Hello world')}>
683
- Translate "Hello world"
684
- </button>
685
- <button onClick={openFlashcards}>
686
- Review Flashcards
687
- </button>
688
- </div>
689
- );
690
- };
691
- ```
692
-
693
- ## Community Features
694
-
695
- Share and discover content created by other users:
696
-
697
- ```typescript
698
- const { community } = useRimori();
699
-
700
- // Shared content methods
701
- community.sharedContent.get<T>(contentType: string, id: string): Promise<BasicAssignment<T>>
702
- community.sharedContent.getList<T>(contentType: string, filter?: SharedContentFilter, limit?: number): Promise<BasicAssignment<T>[]>
703
- community.sharedContent.getNew<T>(contentType: string, instructions: SharedContentObjectRequest, filter?: SharedContentFilter, privateTopic?: boolean): Promise<BasicAssignment<T>>
704
- community.sharedContent.create<T>(content: SharedContent<T>): Promise<BasicAssignment<T>>
705
- community.sharedContent.update<T>(id: string, content: Partial<SharedContent<T>>): Promise<BasicAssignment<T>>
706
- community.sharedContent.remove(id: string): Promise<BasicAssignment<any>>
707
- community.sharedContent.complete(contentType: string, assignmentId: string): Promise<void>
708
- ```
709
-
710
- **Example: Exercise Sharing Platform**
711
-
712
- ```typescript
713
- interface Exercise {
714
- title: string;
715
- description: string;
716
- difficulty: 'beginner' | 'intermediate' | 'advanced';
717
- questions: Array<{
718
- question: string;
719
- answer: string;
720
- hints?: string[];
721
- }>;
722
- }
723
-
724
- const ExerciseManager = () => {
725
- const { community } = useRimori();
726
- const [exercises, setExercises] = useState<BasicAssignment<Exercise>[]>([]);
727
-
728
- // Load community exercises
729
- const loadExercises = async () => {
730
- const exerciseList = await community.sharedContent.getList<Exercise>(
731
- 'grammar_exercises',
732
- { column: 'difficulty', value: 'beginner' },
733
- 10
734
- );
735
- setExercises(exerciseList);
736
- };
737
-
738
- // Create new exercise
739
- const createExercise = async (exercise: Exercise) => {
740
- const newExercise = await community.sharedContent.create({
741
- content_type: 'grammar_exercises',
742
- content: exercise,
743
- metadata: {
744
- difficulty: exercise.difficulty,
745
- questionCount: exercise.questions.length
746
- }
747
- });
748
-
749
- return newExercise;
750
- };
751
-
752
- // Generate AI exercise
753
- const generateExercise = async (topic: string) => {
754
- const aiExercise = await community.sharedContent.getNew<Exercise>(
755
- 'grammar_exercises',
756
- {
757
- prompt: `Create a grammar exercise about ${topic}`,
758
- schema: {
759
- type: "object",
760
- properties: {
761
- title: { type: "string" },
762
- description: { type: "string" },
763
- difficulty: { type: "string", enum: ["beginner", "intermediate", "advanced"] },
764
- questions: {
765
- type: "array",
766
- items: {
767
- type: "object",
768
- properties: {
769
- question: { type: "string" },
770
- answer: { type: "string" },
771
- hints: { type: "array", items: { type: "string" } }
772
- }
773
- }
774
- }
775
- }
776
- }
777
- },
778
- { column: 'difficulty', value: 'beginner' }
779
- );
780
-
781
- return aiExercise;
782
- };
783
-
784
- // Complete exercise
785
- const completeExercise = async (exerciseId: string) => {
786
- await community.sharedContent.complete('grammar_exercises', exerciseId);
787
- // Exercise is now marked as completed for the user
788
- };
789
-
790
- return (
791
- <div>
792
- <button onClick={loadExercises}>Load Exercises</button>
793
- <button onClick={() => generateExercise('present tense')}>
794
- Generate Present Tense Exercise
795
- </button>
796
- {/* Exercise list UI */}
797
- </div>
798
- );
799
- };
800
- ```
801
-
802
- ## Components
803
-
804
- Pre-built React components for common functionality:
805
-
806
- ### MarkdownEditor
807
-
808
- Rich text editor with markdown support:
809
-
810
- ```typescript
811
- import { MarkdownEditor } from "@rimori/client";
812
-
813
- const EditorExample = () => {
814
- const [content, setContent] = useState('');
815
-
816
- return (
817
- <MarkdownEditor
818
- value={content}
819
- onChange={setContent}
820
- placeholder="Start writing..."
821
- />
822
- );
823
- };
824
- ```
825
-
826
- ### CRUDModal
827
-
828
- Modal component for create/update operations:
829
-
830
- ```typescript
831
- import { CRUDModal } from "@rimori/client";
832
-
833
- const DataManager = () => {
834
- const [isOpen, setIsOpen] = useState(false);
835
- const [editItem, setEditItem] = useState(null);
836
-
837
- return (
838
- <CRUDModal
839
- isOpen={isOpen}
840
- onClose={() => setIsOpen(false)}
841
- title={editItem ? "Edit Item" : "Create Item"}
842
- onSave={(data) => {
843
- // Handle save logic
844
- console.log('Saving:', data);
845
- setIsOpen(false);
846
- }}
847
- initialData={editItem}
848
- >
849
- {/* Your form content */}
850
- <input placeholder="Item name" />
851
- <textarea placeholder="Description" />
852
- </CRUDModal>
853
- );
854
- };
855
- ```
856
-
857
- ### Spinner
858
-
859
- Loading indicator component:
860
-
861
- ```typescript
862
- import { Spinner } from "@rimori/client";
863
-
864
- const LoadingExample = () => {
865
- const [isLoading, setIsLoading] = useState(true);
866
-
867
- if (isLoading) {
868
- return <Spinner size="large" />;
869
- }
117
+ Access metadata and settings through `client.plugin`:
870
118
 
871
- return <div>Content loaded!</div>;
872
- };
873
- ```
874
-
875
- ### PlayButton
119
+ - `plugin.pluginId` – current plugin identifier.
120
+ - `plugin.getSettings(defaults)` / `plugin.setSettings(settings)` – persist configuration.
121
+ - `plugin.getPluginInfo()` – read active/installed plugin information.
122
+ - `plugin.getUserInfo()` – obtain user profile details (language, name, guild, etc.).
123
+ - `plugin.getTranslator()` – lazily initialize the translator for manual i18n.
876
124
 
877
- Audio playback component:
125
+ ### Database Access
878
126
 
879
- ```typescript
880
- import { PlayButton } from "@rimori/client";
127
+ `client.db` wraps the Supabase client that is scoped to your plugin tables:
881
128
 
882
- const AudioPlayer = () => {
883
- return (
884
- <PlayButton
885
- audioUrl="https://example.com/audio.mp3"
886
- onPlay={() => console.log('Audio playing')}
887
- onPause={() => console.log('Audio paused')}
888
- />
889
- );
890
- };
129
+ ```ts
130
+ const { data, error } = await client.db
131
+ .from("study_sessions")
132
+ .select("*")
133
+ .order("completed_at", { ascending: false });
891
134
  ```
892
135
 
893
- ### AI Components
894
-
895
- ```typescript
896
- import { Avatar, Assistant } from "@rimori/client";
897
-
898
- const AIInterface = () => {
899
- return (
900
- <div>
901
- <Avatar
902
- name="AI Assistant"
903
- status="online"
904
- size="large"
905
- />
906
-
907
- <Assistant
908
- onMessage={(message) => console.log('AI message:', message)}
909
- placeholder="Ask the AI assistant..."
910
- />
911
- </div>
912
- );
913
- };
914
- ```
915
-
916
- ## Hooks
917
-
918
- ### useChat
919
-
920
- Manage AI chat conversations:
921
-
922
- ```typescript
923
- import { useChat } from "@rimori/client";
924
-
925
- const ChatExample = () => {
926
- const { messages, append, isLoading, setMessages } = useChat([
927
- // Optional tools for the AI
928
- {
929
- name: "get_weather",
930
- description: "Get current weather",
931
- parameters: {
932
- type: "object",
933
- properties: {
934
- location: { type: "string" }
935
- }
936
- }
937
- }
938
- ]);
939
-
940
- const sendMessage = (content: string) => {
941
- append([{ role: 'user', content }]);
942
- };
943
-
944
- return (
945
- <div>
946
- {messages.map((msg, index) => (
947
- <div key={index}>{msg.content}</div>
948
- ))}
949
- {isLoading && <div>AI is typing...</div>}
950
- </div>
951
- );
952
- };
953
- ```
954
-
955
- ### useTranslation
956
-
957
- Internationalization (i18n) support built on i18next:
958
-
959
- ```typescript
960
- import { useTranslation } from "@rimori/client";
961
-
962
- const TranslatedComponent = () => {
963
- const { t, ready } = useTranslation();
964
-
965
- if (!ready) {
966
- return <div>Loading translations...</div>;
967
- }
968
-
969
- return (
970
- <div>
971
- <h1>{t('discussion.title')}</h1>
972
- <p>{t('discussion.whatToTalkAbout')}</p>
973
- </div>
974
- );
975
- };
976
- ```
977
-
978
- ## Translation Feature
979
-
980
- Rimori includes a comprehensive internationalization (i18n) system built on i18next that allows plugins to support multiple languages with minimal developer effort.
981
-
982
- ### How it works
136
+ Helpers:
983
137
 
984
- - **Developer Focus**: Developers only need to ensure their interface works in English
985
- - **Automatic Translations**: With every release, translations for all other languages are generated automatically
986
- - **Local Testing**: For local development, you can test translations by:
987
- 1. Setting your user language to a non-English locale (e.g., German)
988
- 2. Creating a local translation file with "local-" prefix (e.g., `local-de.json`) in the `public/locales/` directory
989
- 3. The translator will automatically use the local translation file in development mode
990
- - **Manual Translations**: If developers want to manually translate files, they should place the language file manually in the `public/locales/` folder with the language code as filename (e.g., `de.json`, `fr.json`)
138
+ - `db.tablePrefix` plugin-specific prefix applied to all tables.
139
+ - `db.getTableName("notes")` resolve the fully qualified table name.
140
+ - Supabase query builder methods (`insert`, `update`, `delete`, `eq`, `limit`, etc.) are available out of the box.
991
141
 
992
- ### Usage
142
+ ### AI & Voice
993
143
 
994
- #### Using the Hook (Recommended)
144
+ The `client.ai` controller surfaces AI capabilities:
995
145
 
996
- ```typescript
997
- import { useTranslation } from "@rimori/client";
146
+ - `getText(messages, tools?)` – chat completion (string result).
147
+ - `getSteamedText(messages, onMessage, tools?)` streamed responses.
148
+ - `getObject(request)` – structured JSON generation.
149
+ - `getVoice(text, voice?, speed?, language?)` – text-to-speech (returns `Blob`).
150
+ - `getTextFromVoice(file)` – speech-to-text transcription.
998
151
 
999
- function MyComponent() {
1000
- const { t, ready } = useTranslation();
1001
-
1002
- if (!ready) {
1003
- return <div>Loading translations...</div>;
1004
- }
1005
-
1006
- return (
1007
- <div>
1008
- <h1>{t('discussion.title')}</h1>
1009
- <p>{t('discussion.whatToTalkAbout')}</p>
1010
- </div>
1011
- );
1012
- }
1013
- ```
152
+ Use `client.runtime.fetchBackend` for authenticated calls to Rimori-managed HTTP endpoints.
1014
153
 
1015
- #### Using the Translator Instance
154
+ ### Event Bus & Actions
1016
155
 
1017
- ```typescript
1018
- import { useRimori } from "@rimori/client";
156
+ `client.event` lets you collaborate with Rimori and other plugins:
1019
157
 
1020
- const { plugin } = useRimori();
1021
- const translator = await plugin.getTranslator()
158
+ - `emit(topic, data?)`, `request(topic, data?)` – publish and request data.
159
+ - `on(topic, handler)` / `once(topic, handler)` / `respond(topic, handler)` – subscribe and reply (each call returns an object with `off()` for cleanup).
160
+ - `emitAccomplishment(payload)` / `onAccomplishment(topic, handler)` – report learning milestones.
161
+ - `emitSidebarAction(pluginId, actionKey, text?)` – trigger sidebar plugins.
162
+ - `onMainPanelAction(handler, actionsToListen?)` – react to dashboard actions.
163
+ - `client.navigation.toDashboard()` – navigate the user back to Rimori.
1022
164
 
1023
- const translatedText = translator.t("discussion.title");
1024
- ```
165
+ ### Community Content
1025
166
 
1026
- ### Translation File Structure
1027
-
1028
- - **Location**: `public/locales/`
1029
- - **Production Files**: Must be named `{language}.json` (e.g., `en.json`, `de.json`, `fr.json`)
1030
- - **Local Development Files**: Must be named `local-{language}.json` (e.g., `local-de.json`, `local-fr.json`)
1031
- - **Format**: Standard JSON with nested objects for organization
1032
- - **English Requirement**: `en.json` is required as the base language
1033
- - **Release Process**: Files starting with "local-" are ignored during the release process
1034
-
1035
- Example translation file structure:
1036
-
1037
- ```json
1038
- {
1039
- "discussion": {
1040
- "title": "Discussion",
1041
- "whatToTalkAbout": "What do you want to talk about?",
1042
- "topics": {
1043
- "everyday": {
1044
- "title": "Everyday Conversations",
1045
- "description": "Ordering coffee, asking for directions, etc."
1046
- }
1047
- }
1048
- }
1049
- }
1050
- ```
167
+ `client.community.sharedContent` exposes helpers to share or consume content:
1051
168
 
1052
- ### Features
169
+ - `get(contentType, id)`
170
+ - `getList(contentType, filter?, limit?)`
171
+ - `getNew(contentType, instructions, filter?, options?)`
172
+ - `create(payload)`
173
+ - `update(id, payload)`
174
+ - `remove(id)`
175
+ - `complete(contentType, assignmentId)`
1053
176
 
1054
- - **I18next Support**: All i18next features work with these translations including:
1055
- - Variable interpolation: `{{name}}`
1056
- - Pluralization
1057
- - Fallback mechanisms
1058
- - **Automatic Fallback**: If a translation is missing, it falls back to English
1059
- - **Development Mode**: Local translation files are prioritized in development
1060
- - **Production Ready**: Automatic translation generation for production releases
177
+ The controller handles topic generation, metadata, and completion tracking automatically.
1061
178
 
1062
- ### Limitations
179
+ ### Workers & Standalone Development
1063
180
 
1064
- - Only one translation file per language is allowed
1065
- - Namespaces are not supported
1066
- - Production translation files must be named `{language}.json` and placed in `public/locales/`
1067
- - Local development files must be named `local-{language}.json` and placed in `public/locales/`
1068
- - English (`en.json`) is required as the base language
1069
- - Local files (prefixed with "local-") are ignored during the release process
181
+ - `setupWorker()` wires the Rimori event bus into worker contexts.
182
+ - `StandaloneClient.getInstance()` signs in against Rimori when your plugin runs outside the platform (e.g., local development).
183
+ - `client.getQueryParam(key)` reads values provided by Rimori through the sandbox handshake (such as `applicationMode` or theme information).
1070
184
 
1071
185
  ## Utilities
1072
186
 
1073
- ### difficultyConverter
1074
-
1075
- Convert between different difficulty representations:
187
+ Import additional helpers as needed:
1076
188
 
1077
- ```typescript
1078
- import { difficultyConverter } from '@rimori/client';
1079
-
1080
- const difficulty = difficultyConverter.toNumber('intermediate'); // Returns: 2
1081
- const difficultyText = difficultyConverter.toString(3); // Returns: 'advanced'
1082
- ```
1083
-
1084
- ### PluginUtils
1085
-
1086
- Various utility functions:
1087
-
1088
- ```typescript
1089
- import { PluginUtils } from '@rimori/client';
1090
-
1091
- // Utility functions for common plugin operations
1092
- const utils = PluginUtils.getInstance();
1093
- // Access various helper methods
1094
- ```
1095
-
1096
- ### Language Utilities
1097
-
1098
- Language detection and processing:
1099
-
1100
- ```typescript
1101
- import { Language } from '@rimori/client';
1102
-
1103
- // Language-related utility functions
1104
- const languageCode = Language.detectLanguage(text);
1105
- const isSupported = Language.isSupported('es');
1106
- ```
189
+ - `AudioController` – high-level audio playback/recording utilities for non-React environments.
190
+ - `Translator` encapsulated i18next integration for manual translation flows.
191
+ - `difficultyConverter` – convert between textual and numeric difficulty levels.
192
+ - Type definitions for AI messages, shared content, triggers, accomplishments, and more.
1107
193
 
1108
194
  ## TypeScript Support
1109
195
 
1110
- The package is fully typed with comprehensive TypeScript definitions:
1111
-
1112
- ```typescript
1113
- import type {
1114
- MainPanelAction,
1115
- Message,
1116
- Tool,
1117
- EventPayload,
1118
- AccomplishmentPayload,
1119
- SharedContent,
1120
- BasicAssignment,
1121
- UserInfo,
1122
- } from '@rimori/client';
1123
-
1124
- // All interfaces and types are exported for use in your plugin
1125
- interface MyPluginData extends SharedContent<any> {
1126
- // Your custom properties
1127
- }
1128
- ```
1129
-
1130
- The SharedContent has this type definition:
196
+ All exports are fully typed. You can import the type definitions directly:
1131
197
 
198
+ ```ts
199
+ import type { Message, Tool, SharedContent, MacroAccomplishmentPayload } from "@rimori/client";
1132
200
  ```
1133
- export interface SharedContent<T> {
1134
- /** The type/category of the content (e.g. 'grammar_exercises', 'flashcards', etc.) */
1135
- contentType: string;
1136
201
 
1137
- /** The human readable title/topic of the content */
1138
- topic: string;
202
+ The generated declaration files cover every controller and helper to keep plugins strictly typed.
1139
203
 
1140
- /** Array of keywords/tags associated with the content for search and categorization */
1141
- keywords: string[];
204
+ ## Example Integration
1142
205
 
1143
- /** The actual content data of type T */
1144
- data: T;
206
+ React users should install `@rimori/react-client` and wrap their app:
1145
207
 
1146
- /** Whether this content should only be visible to the creator. Defaults to false if not specified */
1147
- privateTopic?: boolean;
1148
- }
1149
- ```
208
+ ```tsx
209
+ import { PluginProvider, useRimori, useChat } from "@rimori/react-client";
1150
210
 
1151
- ## Examples
1152
-
1153
- ### Complete Plugin Example
1154
-
1155
- ```typescript
1156
- import React, { useState, useEffect } from 'react';
1157
- import {
1158
- PluginProvider,
1159
- usePlugin,
1160
- MarkdownEditor,
1161
- Spinner,
1162
- useChat
1163
- } from '@rimori/client';
1164
- import { HashRouter, Route, Routes } from 'react-router-dom';
1165
-
1166
- const StudyNotesPlugin = () => {
1167
- const { db, llm, plugin, community } = useRimori();
1168
- const [notes, setNotes] = useState([]);
1169
- const [isLoading, setIsLoading] = useState(true);
211
+ function Dashboard() {
212
+ const client = useRimori();
1170
213
  const { messages, append } = useChat();
1171
214
 
1172
- useEffect(() => {
1173
- loadNotes();
1174
- }, []);
1175
-
1176
- const loadNotes = async () => {
1177
- try {
1178
- const { data } = await db.from('notes').select('*').order('created_at', { ascending: false });
1179
- setNotes(data || []);
1180
- } catch (error) {
1181
- console.error('Error loading notes:', error);
1182
- } finally {
1183
- setIsLoading(false);
1184
- }
1185
- };
1186
-
1187
- const saveNote = async (content: string) => {
1188
- const { data } = await db.from('notes').insert({
1189
- content,
1190
- created_at: new Date().toISOString()
1191
- }).select().single();
1192
-
1193
- setNotes([data, ...notes]);
1194
-
1195
- // Share with community
1196
- await community.sharedContent.create({
1197
- content_type: 'study_notes',
1198
- content: { text: content },
1199
- metadata: { wordCount: content.length }
1200
- });
1201
- };
1202
-
1203
- const generateSummary = async (noteContent: string) => {
1204
- const summary = await llm.getText([
1205
- { role: 'user', content: `Summarize this study note: ${noteContent}` }
1206
- ]);
1207
-
1208
- return summary;
1209
- };
1210
-
1211
- if (isLoading) return <Spinner size="large" />;
215
+ // interact with the core API through the client instance
216
+ // e.g. client.db.from("notes")...
217
+ }
1212
218
 
219
+ export function App() {
1213
220
  return (
1214
- <div className="study-notes-plugin">
1215
- <h1>Study Notes</h1>
1216
-
1217
- <div className="notes-grid">
1218
- {notes.map(note => (
1219
- <div key={note.id} className="note-card">
1220
- <MarkdownEditor
1221
- value={note.content}
1222
- onChange={(content) => {/* Update logic */}}
1223
- />
1224
- <button onClick={() => generateSummary(note.content)}>
1225
- Generate Summary
1226
- </button>
1227
- </div>
1228
- ))}
1229
- </div>
1230
-
1231
- <div className="ai-chat">
1232
- <h2>Study Assistant</h2>
1233
- {messages.map((msg, index) => (
1234
- <div key={index} className={`message ${msg.role}`}>
1235
- {msg.content}
1236
- </div>
1237
- ))}
1238
- <button onClick={() => append([{ role: 'user', content: 'Help me study' }])}>
1239
- Get Study Help
1240
- </button>
1241
- </div>
1242
- </div>
221
+ <PluginProvider pluginId="your-plugin-id">
222
+ <Dashboard />
223
+ </PluginProvider>
1243
224
  );
1244
- };
1245
-
1246
- const App = () => (
1247
- <PluginProvider pluginId="study-notes-plugin">
1248
- <HashRouter>
1249
- <Routes>
1250
- <Route path="/" element={<StudyNotesPlugin />} />
1251
- </Routes>
1252
- </HashRouter>
1253
- </PluginProvider>
1254
- );
1255
-
1256
- export default App;
225
+ }
1257
226
  ```
1258
227
 
1259
- ## Best Practices
228
+ Non-React projects can interact with the same client instance directly via the examples in the sections above.
229
+
230
+ ## Troubleshooting
1260
231
 
1261
- 1. **Performance**: Use lazy loading for pages and implement proper loading states
1262
- 2. **State Management**: Leverage React hooks and context when needed
1263
- 3. **Type Safety**: Use TypeScript interfaces for all data structures
1264
- 4. **Event Cleanup**: Always unsubscribe from events in useEffect cleanup
1265
- 5. **Responsive Design**: Use TailwindCSS classes for responsive layouts
232
+ - **`ReferenceError: process is not defined` in workers** ensure worker bundles only import from `@rimori/client`. Packages that reference `process.env` are not compatible with Rimori workers.
233
+ - **Missing plugin ID or token** – re-run `rimori-init` to regenerate configuration and authentication secrets.
234
+ - **Event bus listeners firing twice** store the listener returned by `event.on` and call `listener.off()` during cleanup (React users get this cleanup inside the hooks provided by `@rimori/react-client`).