@rimori/client 1.1.0 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +949 -35
- package/dist/components/ai/Avatar.d.ts +1 -1
- package/dist/components/ai/EmbeddedAssistent/AudioInputField.js +1 -1
- package/dist/components/ai/EmbeddedAssistent/CircleAudioAvatar.js +1 -1
- package/dist/controller/SidePluginController.js +2 -1
- package/dist/core.d.ts +0 -5
- package/dist/core.js +0 -5
- package/dist/index.d.ts +0 -8
- package/dist/index.js +0 -8
- package/dist/plugin/fromRimori/PluginTypes.d.ts +0 -3
- package/package.json +1 -1
- package/src/components/ai/Avatar.tsx +1 -1
- package/src/components/ai/EmbeddedAssistent/AudioInputField.tsx +1 -1
- package/src/components/ai/EmbeddedAssistent/CircleAudioAvatar.tsx +1 -1
- package/src/controller/SidePluginController.ts +2 -1
- package/src/core.ts +0 -5
- package/src/index.ts +0 -8
- package/src/plugin/fromRimori/PluginTypes.ts +0 -3
package/README.md
CHANGED
|
@@ -1,64 +1,978 @@
|
|
|
1
1
|
# Rimori Client Package
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
communicate with the Rimori platform.
|
|
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.
|
|
5
4
|
|
|
6
|
-
##
|
|
5
|
+
## Table of Contents
|
|
7
6
|
|
|
8
|
-
|
|
7
|
+
- [Installation](#installation)
|
|
8
|
+
- [Quick Start](#quick-start)
|
|
9
|
+
- [Core API - usePlugin Hook](#core-api---useplugin-hook)
|
|
10
|
+
- [Database Integration](#database-integration)
|
|
11
|
+
- [LLM Integration](#llm-integration)
|
|
12
|
+
- [Event System](#event-system)
|
|
13
|
+
- [Community Features](#community-features)
|
|
14
|
+
- [Components](#components)
|
|
15
|
+
- [Hooks](#hooks)
|
|
16
|
+
- [Utilities](#utilities)
|
|
17
|
+
- [TypeScript Support](#typescript-support)
|
|
18
|
+
- [Examples](#examples)
|
|
19
|
+
|
|
20
|
+
## Installation
|
|
9
21
|
|
|
10
22
|
```bash
|
|
11
|
-
npm
|
|
23
|
+
npm install @rimori/client
|
|
24
|
+
# or
|
|
25
|
+
yarn add @rimori/client
|
|
12
26
|
```
|
|
13
27
|
|
|
14
|
-
|
|
28
|
+
## Quick Start
|
|
29
|
+
|
|
30
|
+
### Basic Setup
|
|
15
31
|
|
|
16
32
|
```typescript
|
|
17
33
|
import { lazy } from "react";
|
|
18
|
-
import { PluginProvider } from "@rimori/client";
|
|
34
|
+
import { PluginProvider, usePlugin } from "@rimori/client";
|
|
19
35
|
import { HashRouter, Route, Routes } from "react-router-dom";
|
|
20
36
|
|
|
21
|
-
//
|
|
22
|
-
|
|
23
|
-
const queryClient = new QueryClient();
|
|
24
|
-
|
|
25
|
-
// load all pages lazy for fast loading speed
|
|
37
|
+
// Load pages lazily for optimal performance
|
|
26
38
|
const SettingsPage = lazy(() => import("./pages/settings/SettingsPage"));
|
|
27
|
-
const
|
|
39
|
+
const MainPage = lazy(() => import("./pages/MainPage"));
|
|
28
40
|
|
|
29
41
|
const App = () => (
|
|
30
|
-
|
|
31
|
-
<
|
|
32
|
-
|
|
33
|
-
<
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
<Route path="/settings" element={<SettingsPage />} />
|
|
39
|
-
</Routes>
|
|
40
|
-
</HashRouter>
|
|
41
|
-
</PluginProvider>
|
|
42
|
+
<PluginProvider pluginId="rimori-plugin-id">
|
|
43
|
+
<HashRouter future={{ v7_startTransition: true, v7_relativeSplatPath: true }}>
|
|
44
|
+
<Routes>
|
|
45
|
+
<Route path="/" element={<MainPage />} />
|
|
46
|
+
<Route path="/settings" element={<SettingsPage />} />
|
|
47
|
+
</Routes>
|
|
48
|
+
</HashRouter>
|
|
49
|
+
</PluginProvider>
|
|
42
50
|
);
|
|
43
51
|
|
|
44
52
|
export default App;
|
|
45
53
|
```
|
|
46
54
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
```typescript
|
|
50
|
-
const { getSettings, ... } = usePlugin();
|
|
51
|
-
```
|
|
55
|
+
### TailwindCSS Configuration
|
|
52
56
|
|
|
53
|
-
|
|
57
|
+
Add the library to your `tailwind.config.js`:
|
|
54
58
|
|
|
55
59
|
```javascript
|
|
56
60
|
export default {
|
|
57
|
-
darkMode: ["class"], //
|
|
61
|
+
darkMode: ["class"], // Required for theme detection
|
|
58
62
|
content: [
|
|
59
|
-
|
|
63
|
+
"./src/**/*.{js,jsx,ts,tsx}",
|
|
60
64
|
"node_modules/@rimori/client/dist/components/**/*.{js,jsx}",
|
|
61
65
|
],
|
|
62
|
-
|
|
66
|
+
// ... rest of config
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Core API - usePlugin Hook
|
|
71
|
+
|
|
72
|
+
The `usePlugin()` hook is the main interface for accessing Rimori platform features:
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
import { usePlugin } from "@rimori/client";
|
|
76
|
+
|
|
77
|
+
const MyComponent = () => {
|
|
78
|
+
const client = usePlugin();
|
|
79
|
+
|
|
80
|
+
// Access all client features
|
|
81
|
+
const { db, llm, event, community, plugin } = client;
|
|
82
|
+
|
|
83
|
+
return <div>My Plugin Content</div>;
|
|
84
|
+
};
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Plugin Interface
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
const { plugin } = usePlugin();
|
|
91
|
+
|
|
92
|
+
// Plugin information and settings
|
|
93
|
+
plugin.pluginId: string // Current plugin ID
|
|
94
|
+
plugin.getSettings<T>(defaultSettings: T): Promise<T> // Get plugin settings
|
|
95
|
+
plugin.setSettings(settings: any): Promise<void> // Update plugin settings
|
|
96
|
+
plugin.getInstalled(): Promise<Plugin[]> // Get all installed plugins
|
|
97
|
+
plugin.getUserInfo(): Promise<UserInfo> // Get current user information
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
**Example: Managing Flashcard Plugin Settings**
|
|
101
|
+
|
|
102
|
+
```typescript
|
|
103
|
+
interface FlashcardSettings {
|
|
104
|
+
dailyGoal: number;
|
|
105
|
+
reviewInterval: 'easy' | 'medium' | 'hard';
|
|
106
|
+
showAnswerDelay: number;
|
|
107
|
+
enableAudioPronunciation: boolean;
|
|
108
|
+
difficultyAlgorithm: 'spaced-repetition' | 'random' | 'progressive';
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const FlashcardSettingsComponent = () => {
|
|
112
|
+
const { plugin } = usePlugin();
|
|
113
|
+
const [settings, setSettings] = useState<FlashcardSettings>();
|
|
114
|
+
|
|
115
|
+
useEffect(() => {
|
|
116
|
+
const loadSettings = async () => {
|
|
117
|
+
const defaultSettings: FlashcardSettings = {
|
|
118
|
+
dailyGoal: 20,
|
|
119
|
+
reviewInterval: 'medium',
|
|
120
|
+
showAnswerDelay: 3,
|
|
121
|
+
enableAudioPronunciation: true,
|
|
122
|
+
difficultyAlgorithm: 'spaced-repetition'
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
const currentSettings = await plugin.getSettings(defaultSettings);
|
|
126
|
+
setSettings(currentSettings);
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
loadSettings();
|
|
130
|
+
}, []);
|
|
131
|
+
|
|
132
|
+
const updateSettings = async (newSettings: Partial<FlashcardSettings>) => {
|
|
133
|
+
const updated = { ...settings, ...newSettings };
|
|
134
|
+
await plugin.setSettings(updated);
|
|
135
|
+
setSettings(updated);
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
return (
|
|
139
|
+
<div className="flashcard-settings">
|
|
140
|
+
<label>
|
|
141
|
+
Daily Goal: {settings?.dailyGoal} cards
|
|
142
|
+
<input
|
|
143
|
+
type="range"
|
|
144
|
+
min="5"
|
|
145
|
+
max="100"
|
|
146
|
+
value={settings?.dailyGoal}
|
|
147
|
+
onChange={(e) => updateSettings({ dailyGoal: parseInt(e.target.value) })}
|
|
148
|
+
/>
|
|
149
|
+
</label>
|
|
150
|
+
|
|
151
|
+
<label>
|
|
152
|
+
Review Interval:
|
|
153
|
+
<select
|
|
154
|
+
value={settings?.reviewInterval}
|
|
155
|
+
onChange={(e) => updateSettings({ reviewInterval: e.target.value as any })}
|
|
156
|
+
>
|
|
157
|
+
<option value="easy">Easy (longer intervals)</option>
|
|
158
|
+
<option value="medium">Medium</option>
|
|
159
|
+
<option value="hard">Hard (shorter intervals)</option>
|
|
160
|
+
</select>
|
|
161
|
+
</label>
|
|
162
|
+
|
|
163
|
+
<label>
|
|
164
|
+
<input
|
|
165
|
+
type="checkbox"
|
|
166
|
+
checked={settings?.enableAudioPronunciation}
|
|
167
|
+
onChange={(e) => updateSettings({ enableAudioPronunciation: e.target.checked })}
|
|
168
|
+
/>
|
|
169
|
+
Enable Audio Pronunciation
|
|
170
|
+
</label>
|
|
171
|
+
</div>
|
|
172
|
+
);
|
|
173
|
+
};
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## Database Integration
|
|
177
|
+
|
|
178
|
+
Access your plugin's dedicated database tables with full TypeScript support:
|
|
179
|
+
|
|
180
|
+
```typescript
|
|
181
|
+
const { db } = usePlugin();
|
|
182
|
+
|
|
183
|
+
// Database interface
|
|
184
|
+
db.from(tableName) // Query builder for tables/views - supports ALL Supabase operations
|
|
185
|
+
db.storage // File storage access
|
|
186
|
+
db.tablePrefix: string // Your plugin's table prefix
|
|
187
|
+
db.getTableName(table: string): string // Get full table name with prefix
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
The `db.from()` method provides access to the complete Supabase PostgREST API, supporting all database operations including:
|
|
191
|
+
- **CRUD Operations**: `insert()`, `select()`, `update()`, `delete()`, `upsert()`
|
|
192
|
+
- **Filtering**: `eq()`, `neq()`, `gt()`, `gte()`, `lt()`, `lte()`, `like()`, `ilike()`, `is()`, `in()`, `contains()`, `containedBy()`, `rangeLt()`, `rangeGt()`, `rangeGte()`, `rangeLte()`, `rangeAdjacent()`, `overlaps()`, `textSearch()`, `match()`, `not()`, `or()`, `filter()`
|
|
193
|
+
- **Modifiers**: `order()`, `limit()`, `range()`, `single()`, `maybe_single()`, `csv()`, `geojson()`, `explain()`
|
|
194
|
+
- **Aggregations**: `count()`, `sum()`, `avg()`, `min()`, `max()`
|
|
195
|
+
- **Advanced Features**: Row Level Security (RLS), real-time subscriptions, stored procedures, and custom functions
|
|
196
|
+
|
|
197
|
+
All operations automatically use your plugin's table prefix for security and isolation.
|
|
198
|
+
|
|
199
|
+
**Example: CRUD Operations**
|
|
200
|
+
|
|
201
|
+
```typescript
|
|
202
|
+
interface StudySession {
|
|
203
|
+
id?: string;
|
|
204
|
+
user_id: string;
|
|
205
|
+
topic: string;
|
|
206
|
+
duration: number;
|
|
207
|
+
completed_at: string;
|
|
63
208
|
}
|
|
64
|
-
|
|
209
|
+
|
|
210
|
+
const StudySessionManager = () => {
|
|
211
|
+
const { db } = usePlugin();
|
|
212
|
+
|
|
213
|
+
// Create a new study session
|
|
214
|
+
const createSession = async (session: Omit<StudySession, 'id'>) => {
|
|
215
|
+
const { data, error } = await db
|
|
216
|
+
.from('study_sessions') // Automatically prefixed
|
|
217
|
+
.insert(session)
|
|
218
|
+
.select()
|
|
219
|
+
.single();
|
|
220
|
+
|
|
221
|
+
if (error) throw error;
|
|
222
|
+
return data;
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
// Get user's study sessions
|
|
226
|
+
const getUserSessions = async (userId: string) => {
|
|
227
|
+
const { data, error } = await db
|
|
228
|
+
.from('study_sessions')
|
|
229
|
+
.select('*')
|
|
230
|
+
.eq('user_id', userId)
|
|
231
|
+
.order('completed_at', { ascending: false });
|
|
232
|
+
|
|
233
|
+
if (error) throw error;
|
|
234
|
+
return data;
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
// Update session
|
|
238
|
+
const updateSession = async (id: string, updates: Partial<StudySession>) => {
|
|
239
|
+
const { data, error } = await db
|
|
240
|
+
.from('study_sessions')
|
|
241
|
+
.update(updates)
|
|
242
|
+
.eq('id', id)
|
|
243
|
+
.select()
|
|
244
|
+
.single();
|
|
245
|
+
|
|
246
|
+
if (error) throw error;
|
|
247
|
+
return data;
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
return (
|
|
251
|
+
<div>
|
|
252
|
+
{/* Your component UI */}
|
|
253
|
+
</div>
|
|
254
|
+
);
|
|
255
|
+
};
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
**File Storage Example**
|
|
259
|
+
|
|
260
|
+
```typescript
|
|
261
|
+
const FileManager = () => {
|
|
262
|
+
const { db } = usePlugin();
|
|
263
|
+
|
|
264
|
+
const uploadFile = async (file: File) => {
|
|
265
|
+
const fileName = `uploads/${Date.now()}-${file.name}`;
|
|
266
|
+
|
|
267
|
+
const { data, error } = await db.storage
|
|
268
|
+
.from('plugin-files')
|
|
269
|
+
.upload(fileName, file);
|
|
270
|
+
|
|
271
|
+
if (error) throw error;
|
|
272
|
+
return data;
|
|
273
|
+
};
|
|
274
|
+
|
|
275
|
+
const downloadFile = async (filePath: string) => {
|
|
276
|
+
const { data, error } = await db.storage
|
|
277
|
+
.from('plugin-files')
|
|
278
|
+
.download(filePath);
|
|
279
|
+
|
|
280
|
+
if (error) throw error;
|
|
281
|
+
return data;
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
return <div>File Manager UI</div>;
|
|
285
|
+
};
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
## LLM Integration
|
|
289
|
+
|
|
290
|
+
Powerful AI/Language Model capabilities built-in:
|
|
291
|
+
|
|
292
|
+
```typescript
|
|
293
|
+
const { llm } = usePlugin();
|
|
294
|
+
|
|
295
|
+
// Text generation
|
|
296
|
+
llm.getText(messages: Message[], tools?: Tool[]): Promise<string>
|
|
297
|
+
|
|
298
|
+
// Streaming text generation
|
|
299
|
+
llm.getSteamedText(messages: Message[], onMessage: OnLLMResponse, tools?: Tool[]): void
|
|
300
|
+
|
|
301
|
+
// Structured object generation
|
|
302
|
+
llm.getObject(request: ObjectRequest): Promise<any>
|
|
303
|
+
|
|
304
|
+
// Text-to-speech
|
|
305
|
+
llm.getVoice(text: string, voice?: string, speed?: number, language?: string): Promise<Blob>
|
|
306
|
+
|
|
307
|
+
// Speech-to-text
|
|
308
|
+
llm.getTextFromVoice(file: Blob): Promise<string>
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
**Example: AI Chat Assistant**
|
|
312
|
+
|
|
313
|
+
```typescript
|
|
314
|
+
import { useChat } from "@rimori/client";
|
|
315
|
+
|
|
316
|
+
const ChatAssistant = () => {
|
|
317
|
+
const { messages, append, isLoading } = useChat();
|
|
318
|
+
const [input, setInput] = useState('');
|
|
319
|
+
|
|
320
|
+
const sendMessage = () => {
|
|
321
|
+
if (!input.trim()) return;
|
|
322
|
+
|
|
323
|
+
append([{
|
|
324
|
+
role: 'user',
|
|
325
|
+
content: input
|
|
326
|
+
}]);
|
|
327
|
+
|
|
328
|
+
setInput('');
|
|
329
|
+
};
|
|
330
|
+
|
|
331
|
+
return (
|
|
332
|
+
<div className="chat-container">
|
|
333
|
+
<div className="messages">
|
|
334
|
+
{messages.map((message, index) => (
|
|
335
|
+
<div key={index} className={`message ${message.role}`}>
|
|
336
|
+
{message.content}
|
|
337
|
+
</div>
|
|
338
|
+
))}
|
|
339
|
+
{isLoading && <div className="message assistant">Thinking...</div>}
|
|
340
|
+
</div>
|
|
341
|
+
|
|
342
|
+
<div className="input-area">
|
|
343
|
+
<input
|
|
344
|
+
value={input}
|
|
345
|
+
onChange={(e) => setInput(e.target.value)}
|
|
346
|
+
onKeyPress={(e) => e.key === 'Enter' && sendMessage()}
|
|
347
|
+
placeholder="Ask anything..."
|
|
348
|
+
/>
|
|
349
|
+
<button onClick={sendMessage} disabled={isLoading}>
|
|
350
|
+
Send
|
|
351
|
+
</button>
|
|
352
|
+
</div>
|
|
353
|
+
</div>
|
|
354
|
+
);
|
|
355
|
+
};
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
**Example: Structured Data Generation**
|
|
359
|
+
|
|
360
|
+
```typescript
|
|
361
|
+
const QuizGenerator = () => {
|
|
362
|
+
const { llm } = usePlugin();
|
|
363
|
+
|
|
364
|
+
const generateQuiz = async (topic: string) => {
|
|
365
|
+
const quiz = await llm.getObject({
|
|
366
|
+
schema: {
|
|
367
|
+
type: "object",
|
|
368
|
+
properties: {
|
|
369
|
+
title: { type: "string" },
|
|
370
|
+
questions: {
|
|
371
|
+
type: "array",
|
|
372
|
+
items: {
|
|
373
|
+
type: "object",
|
|
374
|
+
properties: {
|
|
375
|
+
question: { type: "string" },
|
|
376
|
+
options: { type: "array", items: { type: "string" } },
|
|
377
|
+
correctAnswer: { type: "number" },
|
|
378
|
+
explanation: { type: "string" }
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
},
|
|
384
|
+
prompt: `Create a quiz about ${topic} with 5 multiple choice questions.`
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
return quiz;
|
|
388
|
+
};
|
|
389
|
+
|
|
390
|
+
return <div>Quiz Generator UI</div>;
|
|
391
|
+
};
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
**Example: Voice Integration**
|
|
395
|
+
|
|
396
|
+
```typescript
|
|
397
|
+
const VoiceAssistant = () => {
|
|
398
|
+
const { llm } = usePlugin();
|
|
399
|
+
|
|
400
|
+
const speakText = async (text: string) => {
|
|
401
|
+
const audioBlob = await llm.getVoice(text, "alloy", 1, "en");
|
|
402
|
+
const audioUrl = URL.createObjectURL(audioBlob);
|
|
403
|
+
const audio = new Audio(audioUrl);
|
|
404
|
+
audio.play();
|
|
405
|
+
};
|
|
406
|
+
|
|
407
|
+
const transcribeAudio = async (audioFile: File) => {
|
|
408
|
+
const transcript = await llm.getTextFromVoice(audioFile);
|
|
409
|
+
return transcript;
|
|
410
|
+
};
|
|
411
|
+
|
|
412
|
+
return <div>Voice Assistant UI</div>;
|
|
413
|
+
};
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
## Event System
|
|
417
|
+
|
|
418
|
+
Robust inter-plugin communication and platform integration:
|
|
419
|
+
|
|
420
|
+
```typescript
|
|
421
|
+
const { event } = usePlugin();
|
|
422
|
+
|
|
423
|
+
// Event methods
|
|
424
|
+
event.emit(topic: string, data?: any, eventId?: number): void
|
|
425
|
+
event.request<T>(topic: string, data?: any): Promise<EventBusMessage<T>>
|
|
426
|
+
event.on<T>(topic: string | string[], callback: EventHandler<T>): string[]
|
|
427
|
+
event.once<T>(topic: string, callback: EventHandler<T>): void
|
|
428
|
+
event.respond<T>(topic: string, data: EventPayload | Function): void
|
|
429
|
+
|
|
430
|
+
// Accomplishments
|
|
431
|
+
event.emitAccomplishment(payload: AccomplishmentPayload): void
|
|
432
|
+
event.onAccomplishment(topic: string, callback: Function): void
|
|
433
|
+
|
|
434
|
+
// Sidebar actions
|
|
435
|
+
event.emitSidebarAction(pluginId: string, actionKey: string, text?: string): void
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
**Example: Plugin Communication**
|
|
439
|
+
|
|
440
|
+
```typescript
|
|
441
|
+
const PluginCommunicator = () => {
|
|
442
|
+
const { event } = usePlugin();
|
|
443
|
+
|
|
444
|
+
useEffect(() => {
|
|
445
|
+
// Listen for messages from other plugins
|
|
446
|
+
const unsubscribe = event.on('flashcards.newCard', (message) => {
|
|
447
|
+
console.log('New flashcard created:', message.data);
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
// Listen for global events
|
|
451
|
+
event.on('global.userProgress', (message) => {
|
|
452
|
+
console.log('User progress updated:', message.data);
|
|
453
|
+
});
|
|
454
|
+
|
|
455
|
+
return () => {
|
|
456
|
+
// Cleanup subscriptions
|
|
457
|
+
unsubscribe.forEach(id => event.off(id));
|
|
458
|
+
};
|
|
459
|
+
}, []);
|
|
460
|
+
|
|
461
|
+
const shareData = () => {
|
|
462
|
+
// Emit data to other plugins
|
|
463
|
+
event.emit('studyplan.dataUpdate', {
|
|
464
|
+
type: 'session_completed',
|
|
465
|
+
sessionId: '123',
|
|
466
|
+
score: 85
|
|
467
|
+
});
|
|
468
|
+
};
|
|
469
|
+
|
|
470
|
+
const requestData = async () => {
|
|
471
|
+
// Request data from another plugin
|
|
472
|
+
const response = await event.request('flashcards.getStats', {
|
|
473
|
+
timeframe: 'week'
|
|
474
|
+
});
|
|
475
|
+
|
|
476
|
+
console.log('Flashcard stats:', response.data);
|
|
477
|
+
};
|
|
478
|
+
|
|
479
|
+
return (
|
|
480
|
+
<div>
|
|
481
|
+
<button onClick={shareData}>Share Progress</button>
|
|
482
|
+
<button onClick={requestData}>Get Flashcard Stats</button>
|
|
483
|
+
</div>
|
|
484
|
+
);
|
|
485
|
+
};
|
|
486
|
+
```
|
|
487
|
+
|
|
488
|
+
**Example: Accomplishment System**
|
|
489
|
+
|
|
490
|
+
```typescript
|
|
491
|
+
const AccomplishmentTracker = () => {
|
|
492
|
+
const { event } = usePlugin();
|
|
493
|
+
|
|
494
|
+
const trackAccomplishment = () => {
|
|
495
|
+
event.emitAccomplishment({
|
|
496
|
+
type: 'study_milestone',
|
|
497
|
+
title: 'Study Streak',
|
|
498
|
+
description: 'Completed 7 days of studying',
|
|
499
|
+
points: 100,
|
|
500
|
+
metadata: {
|
|
501
|
+
streakDays: 7,
|
|
502
|
+
subject: 'Spanish'
|
|
503
|
+
}
|
|
504
|
+
});
|
|
505
|
+
};
|
|
506
|
+
|
|
507
|
+
useEffect(() => {
|
|
508
|
+
// Listen for accomplishments from this plugin
|
|
509
|
+
event.onAccomplishment('study_milestone', (accomplishment) => {
|
|
510
|
+
console.log('New accomplishment:', accomplishment);
|
|
511
|
+
// Show notification, update UI, etc.
|
|
512
|
+
});
|
|
513
|
+
}, []);
|
|
514
|
+
|
|
515
|
+
return <div>Accomplishment Tracker UI</div>;
|
|
516
|
+
};
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
**Example: Sidebar Integration**
|
|
520
|
+
|
|
521
|
+
```typescript
|
|
522
|
+
const SidebarIntegration = () => {
|
|
523
|
+
const { event } = usePlugin();
|
|
524
|
+
|
|
525
|
+
const openTranslator = (text: string) => {
|
|
526
|
+
// Trigger translator plugin in sidebar
|
|
527
|
+
event.emitSidebarAction('translator', 'translate', text);
|
|
528
|
+
};
|
|
529
|
+
|
|
530
|
+
const openFlashcards = () => {
|
|
531
|
+
// Open flashcards plugin
|
|
532
|
+
event.emitSidebarAction('flashcards', 'review');
|
|
533
|
+
};
|
|
534
|
+
|
|
535
|
+
return (
|
|
536
|
+
<div>
|
|
537
|
+
<button onClick={() => openTranslator('Hello world')}>
|
|
538
|
+
Translate "Hello world"
|
|
539
|
+
</button>
|
|
540
|
+
<button onClick={openFlashcards}>
|
|
541
|
+
Review Flashcards
|
|
542
|
+
</button>
|
|
543
|
+
</div>
|
|
544
|
+
);
|
|
545
|
+
};
|
|
546
|
+
```
|
|
547
|
+
|
|
548
|
+
## Community Features
|
|
549
|
+
|
|
550
|
+
Share and discover content created by other users:
|
|
551
|
+
|
|
552
|
+
```typescript
|
|
553
|
+
const { community } = usePlugin();
|
|
554
|
+
|
|
555
|
+
// Shared content methods
|
|
556
|
+
community.sharedContent.get<T>(contentType: string, id: string): Promise<BasicAssignment<T>>
|
|
557
|
+
community.sharedContent.getList<T>(contentType: string, filter?: SharedContentFilter, limit?: number): Promise<BasicAssignment<T>[]>
|
|
558
|
+
community.sharedContent.getNew<T>(contentType: string, instructions: SharedContentObjectRequest, filter?: SharedContentFilter, privateTopic?: boolean): Promise<BasicAssignment<T>>
|
|
559
|
+
community.sharedContent.create<T>(content: SharedContent<T>): Promise<BasicAssignment<T>>
|
|
560
|
+
community.sharedContent.update<T>(id: string, content: Partial<SharedContent<T>>): Promise<BasicAssignment<T>>
|
|
561
|
+
community.sharedContent.complete(contentType: string, assignmentId: string): Promise<void>
|
|
562
|
+
```
|
|
563
|
+
|
|
564
|
+
**Example: Exercise Sharing Platform**
|
|
565
|
+
|
|
566
|
+
```typescript
|
|
567
|
+
interface Exercise {
|
|
568
|
+
title: string;
|
|
569
|
+
description: string;
|
|
570
|
+
difficulty: 'beginner' | 'intermediate' | 'advanced';
|
|
571
|
+
questions: Array<{
|
|
572
|
+
question: string;
|
|
573
|
+
answer: string;
|
|
574
|
+
hints?: string[];
|
|
575
|
+
}>;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
const ExerciseManager = () => {
|
|
579
|
+
const { community } = usePlugin();
|
|
580
|
+
const [exercises, setExercises] = useState<BasicAssignment<Exercise>[]>([]);
|
|
581
|
+
|
|
582
|
+
// Load community exercises
|
|
583
|
+
const loadExercises = async () => {
|
|
584
|
+
const exerciseList = await community.sharedContent.getList<Exercise>(
|
|
585
|
+
'grammar_exercises',
|
|
586
|
+
{ column: 'difficulty', value: 'beginner' },
|
|
587
|
+
10
|
|
588
|
+
);
|
|
589
|
+
setExercises(exerciseList);
|
|
590
|
+
};
|
|
591
|
+
|
|
592
|
+
// Create new exercise
|
|
593
|
+
const createExercise = async (exercise: Exercise) => {
|
|
594
|
+
const newExercise = await community.sharedContent.create({
|
|
595
|
+
content_type: 'grammar_exercises',
|
|
596
|
+
content: exercise,
|
|
597
|
+
metadata: {
|
|
598
|
+
difficulty: exercise.difficulty,
|
|
599
|
+
questionCount: exercise.questions.length
|
|
600
|
+
}
|
|
601
|
+
});
|
|
602
|
+
|
|
603
|
+
return newExercise;
|
|
604
|
+
};
|
|
605
|
+
|
|
606
|
+
// Generate AI exercise
|
|
607
|
+
const generateExercise = async (topic: string) => {
|
|
608
|
+
const aiExercise = await community.sharedContent.getNew<Exercise>(
|
|
609
|
+
'grammar_exercises',
|
|
610
|
+
{
|
|
611
|
+
prompt: `Create a grammar exercise about ${topic}`,
|
|
612
|
+
schema: {
|
|
613
|
+
type: "object",
|
|
614
|
+
properties: {
|
|
615
|
+
title: { type: "string" },
|
|
616
|
+
description: { type: "string" },
|
|
617
|
+
difficulty: { type: "string", enum: ["beginner", "intermediate", "advanced"] },
|
|
618
|
+
questions: {
|
|
619
|
+
type: "array",
|
|
620
|
+
items: {
|
|
621
|
+
type: "object",
|
|
622
|
+
properties: {
|
|
623
|
+
question: { type: "string" },
|
|
624
|
+
answer: { type: "string" },
|
|
625
|
+
hints: { type: "array", items: { type: "string" } }
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
},
|
|
632
|
+
{ column: 'difficulty', value: 'beginner' }
|
|
633
|
+
);
|
|
634
|
+
|
|
635
|
+
return aiExercise;
|
|
636
|
+
};
|
|
637
|
+
|
|
638
|
+
// Complete exercise
|
|
639
|
+
const completeExercise = async (exerciseId: string) => {
|
|
640
|
+
await community.sharedContent.complete('grammar_exercises', exerciseId);
|
|
641
|
+
// Exercise is now marked as completed for the user
|
|
642
|
+
};
|
|
643
|
+
|
|
644
|
+
return (
|
|
645
|
+
<div>
|
|
646
|
+
<button onClick={loadExercises}>Load Exercises</button>
|
|
647
|
+
<button onClick={() => generateExercise('present tense')}>
|
|
648
|
+
Generate Present Tense Exercise
|
|
649
|
+
</button>
|
|
650
|
+
{/* Exercise list UI */}
|
|
651
|
+
</div>
|
|
652
|
+
);
|
|
653
|
+
};
|
|
654
|
+
```
|
|
655
|
+
|
|
656
|
+
## Components
|
|
657
|
+
|
|
658
|
+
Pre-built React components for common functionality:
|
|
659
|
+
|
|
660
|
+
### MarkdownEditor
|
|
661
|
+
Rich text editor with markdown support:
|
|
662
|
+
|
|
663
|
+
```typescript
|
|
664
|
+
import { MarkdownEditor } from "@rimori/client";
|
|
665
|
+
|
|
666
|
+
const EditorExample = () => {
|
|
667
|
+
const [content, setContent] = useState('');
|
|
668
|
+
|
|
669
|
+
return (
|
|
670
|
+
<MarkdownEditor
|
|
671
|
+
value={content}
|
|
672
|
+
onChange={setContent}
|
|
673
|
+
placeholder="Start writing..."
|
|
674
|
+
/>
|
|
675
|
+
);
|
|
676
|
+
};
|
|
677
|
+
```
|
|
678
|
+
|
|
679
|
+
### CRUDModal
|
|
680
|
+
Modal component for create/update operations:
|
|
681
|
+
|
|
682
|
+
```typescript
|
|
683
|
+
import { CRUDModal } from "@rimori/client";
|
|
684
|
+
|
|
685
|
+
const DataManager = () => {
|
|
686
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
687
|
+
const [editItem, setEditItem] = useState(null);
|
|
688
|
+
|
|
689
|
+
return (
|
|
690
|
+
<CRUDModal
|
|
691
|
+
isOpen={isOpen}
|
|
692
|
+
onClose={() => setIsOpen(false)}
|
|
693
|
+
title={editItem ? "Edit Item" : "Create Item"}
|
|
694
|
+
onSave={(data) => {
|
|
695
|
+
// Handle save logic
|
|
696
|
+
console.log('Saving:', data);
|
|
697
|
+
setIsOpen(false);
|
|
698
|
+
}}
|
|
699
|
+
initialData={editItem}
|
|
700
|
+
>
|
|
701
|
+
{/* Your form content */}
|
|
702
|
+
<input placeholder="Item name" />
|
|
703
|
+
<textarea placeholder="Description" />
|
|
704
|
+
</CRUDModal>
|
|
705
|
+
);
|
|
706
|
+
};
|
|
707
|
+
```
|
|
708
|
+
|
|
709
|
+
### Spinner
|
|
710
|
+
Loading indicator component:
|
|
711
|
+
|
|
712
|
+
```typescript
|
|
713
|
+
import { Spinner } from "@rimori/client";
|
|
714
|
+
|
|
715
|
+
const LoadingExample = () => {
|
|
716
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
717
|
+
|
|
718
|
+
if (isLoading) {
|
|
719
|
+
return <Spinner size="large" />;
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
return <div>Content loaded!</div>;
|
|
723
|
+
};
|
|
724
|
+
```
|
|
725
|
+
|
|
726
|
+
### PlayButton
|
|
727
|
+
Audio playback component:
|
|
728
|
+
|
|
729
|
+
```typescript
|
|
730
|
+
import { PlayButton } from "@rimori/client";
|
|
731
|
+
|
|
732
|
+
const AudioPlayer = () => {
|
|
733
|
+
return (
|
|
734
|
+
<PlayButton
|
|
735
|
+
audioUrl="https://example.com/audio.mp3"
|
|
736
|
+
onPlay={() => console.log('Audio playing')}
|
|
737
|
+
onPause={() => console.log('Audio paused')}
|
|
738
|
+
/>
|
|
739
|
+
);
|
|
740
|
+
};
|
|
741
|
+
```
|
|
742
|
+
|
|
743
|
+
### AI Components
|
|
744
|
+
|
|
745
|
+
```typescript
|
|
746
|
+
import { Avatar, Assistant } from "@rimori/client";
|
|
747
|
+
|
|
748
|
+
const AIInterface = () => {
|
|
749
|
+
return (
|
|
750
|
+
<div>
|
|
751
|
+
<Avatar
|
|
752
|
+
name="AI Assistant"
|
|
753
|
+
status="online"
|
|
754
|
+
size="large"
|
|
755
|
+
/>
|
|
756
|
+
|
|
757
|
+
<Assistant
|
|
758
|
+
onMessage={(message) => console.log('AI message:', message)}
|
|
759
|
+
placeholder="Ask the AI assistant..."
|
|
760
|
+
/>
|
|
761
|
+
</div>
|
|
762
|
+
);
|
|
763
|
+
};
|
|
764
|
+
```
|
|
765
|
+
|
|
766
|
+
## Hooks
|
|
767
|
+
|
|
768
|
+
### useChat
|
|
769
|
+
Manage AI chat conversations:
|
|
770
|
+
|
|
771
|
+
```typescript
|
|
772
|
+
import { useChat } from "@rimori/client";
|
|
773
|
+
|
|
774
|
+
const ChatExample = () => {
|
|
775
|
+
const { messages, append, isLoading, setMessages } = useChat([
|
|
776
|
+
// Optional tools for the AI
|
|
777
|
+
{
|
|
778
|
+
name: "get_weather",
|
|
779
|
+
description: "Get current weather",
|
|
780
|
+
parameters: {
|
|
781
|
+
type: "object",
|
|
782
|
+
properties: {
|
|
783
|
+
location: { type: "string" }
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
]);
|
|
788
|
+
|
|
789
|
+
const sendMessage = (content: string) => {
|
|
790
|
+
append([{ role: 'user', content }]);
|
|
791
|
+
};
|
|
792
|
+
|
|
793
|
+
return (
|
|
794
|
+
<div>
|
|
795
|
+
{messages.map((msg, index) => (
|
|
796
|
+
<div key={index}>{msg.content}</div>
|
|
797
|
+
))}
|
|
798
|
+
{isLoading && <div>AI is typing...</div>}
|
|
799
|
+
</div>
|
|
800
|
+
);
|
|
801
|
+
};
|
|
802
|
+
```
|
|
803
|
+
|
|
804
|
+
## Utilities
|
|
805
|
+
|
|
806
|
+
### difficultyConverter
|
|
807
|
+
Convert between different difficulty representations:
|
|
808
|
+
|
|
809
|
+
```typescript
|
|
810
|
+
import { difficultyConverter } from "@rimori/client";
|
|
811
|
+
|
|
812
|
+
const difficulty = difficultyConverter.toNumber('intermediate'); // Returns: 2
|
|
813
|
+
const difficultyText = difficultyConverter.toString(3); // Returns: 'advanced'
|
|
814
|
+
```
|
|
815
|
+
|
|
816
|
+
### PluginUtils
|
|
817
|
+
Various utility functions:
|
|
818
|
+
|
|
819
|
+
```typescript
|
|
820
|
+
import { PluginUtils } from "@rimori/client";
|
|
821
|
+
|
|
822
|
+
// Utility functions for common plugin operations
|
|
823
|
+
const utils = PluginUtils.getInstance();
|
|
824
|
+
// Access various helper methods
|
|
825
|
+
```
|
|
826
|
+
|
|
827
|
+
### Language Utilities
|
|
828
|
+
Language detection and processing:
|
|
829
|
+
|
|
830
|
+
```typescript
|
|
831
|
+
import { Language } from "@rimori/client";
|
|
832
|
+
|
|
833
|
+
// Language-related utility functions
|
|
834
|
+
const languageCode = Language.detectLanguage(text);
|
|
835
|
+
const isSupported = Language.isSupported('es');
|
|
836
|
+
```
|
|
837
|
+
|
|
838
|
+
## TypeScript Support
|
|
839
|
+
|
|
840
|
+
The package is fully typed with comprehensive TypeScript definitions:
|
|
841
|
+
|
|
842
|
+
```typescript
|
|
843
|
+
import type {
|
|
844
|
+
MainPanelAction,
|
|
845
|
+
Message,
|
|
846
|
+
Tool,
|
|
847
|
+
EventPayload,
|
|
848
|
+
AccomplishmentPayload,
|
|
849
|
+
SharedContent,
|
|
850
|
+
BasicAssignment,
|
|
851
|
+
UserInfo
|
|
852
|
+
} from "@rimori/client";
|
|
853
|
+
|
|
854
|
+
// All interfaces and types are exported for use in your plugin
|
|
855
|
+
interface MyPluginData extends SharedContent<any> {
|
|
856
|
+
// Your custom properties
|
|
857
|
+
}
|
|
858
|
+
```
|
|
859
|
+
|
|
860
|
+
## Examples
|
|
861
|
+
|
|
862
|
+
### Complete Plugin Example
|
|
863
|
+
|
|
864
|
+
```typescript
|
|
865
|
+
import React, { useState, useEffect } from 'react';
|
|
866
|
+
import {
|
|
867
|
+
PluginProvider,
|
|
868
|
+
usePlugin,
|
|
869
|
+
MarkdownEditor,
|
|
870
|
+
Spinner,
|
|
871
|
+
useChat
|
|
872
|
+
} from '@rimori/client';
|
|
873
|
+
import { HashRouter, Route, Routes } from 'react-router-dom';
|
|
874
|
+
|
|
875
|
+
const StudyNotesPlugin = () => {
|
|
876
|
+
const { db, llm, plugin, community } = usePlugin();
|
|
877
|
+
const [notes, setNotes] = useState([]);
|
|
878
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
879
|
+
const { messages, append } = useChat();
|
|
880
|
+
|
|
881
|
+
useEffect(() => {
|
|
882
|
+
loadNotes();
|
|
883
|
+
}, []);
|
|
884
|
+
|
|
885
|
+
const loadNotes = async () => {
|
|
886
|
+
try {
|
|
887
|
+
const { data } = await db.from('notes').select('*').order('created_at', { ascending: false });
|
|
888
|
+
setNotes(data || []);
|
|
889
|
+
} catch (error) {
|
|
890
|
+
console.error('Error loading notes:', error);
|
|
891
|
+
} finally {
|
|
892
|
+
setIsLoading(false);
|
|
893
|
+
}
|
|
894
|
+
};
|
|
895
|
+
|
|
896
|
+
const saveNote = async (content: string) => {
|
|
897
|
+
const { data } = await db.from('notes').insert({
|
|
898
|
+
content,
|
|
899
|
+
created_at: new Date().toISOString()
|
|
900
|
+
}).select().single();
|
|
901
|
+
|
|
902
|
+
setNotes([data, ...notes]);
|
|
903
|
+
|
|
904
|
+
// Share with community
|
|
905
|
+
await community.sharedContent.create({
|
|
906
|
+
content_type: 'study_notes',
|
|
907
|
+
content: { text: content },
|
|
908
|
+
metadata: { wordCount: content.length }
|
|
909
|
+
});
|
|
910
|
+
};
|
|
911
|
+
|
|
912
|
+
const generateSummary = async (noteContent: string) => {
|
|
913
|
+
const summary = await llm.getText([
|
|
914
|
+
{ role: 'user', content: `Summarize this study note: ${noteContent}` }
|
|
915
|
+
]);
|
|
916
|
+
|
|
917
|
+
return summary;
|
|
918
|
+
};
|
|
919
|
+
|
|
920
|
+
if (isLoading) return <Spinner size="large" />;
|
|
921
|
+
|
|
922
|
+
return (
|
|
923
|
+
<div className="study-notes-plugin">
|
|
924
|
+
<h1>Study Notes</h1>
|
|
925
|
+
|
|
926
|
+
<div className="notes-grid">
|
|
927
|
+
{notes.map(note => (
|
|
928
|
+
<div key={note.id} className="note-card">
|
|
929
|
+
<MarkdownEditor
|
|
930
|
+
value={note.content}
|
|
931
|
+
onChange={(content) => {/* Update logic */}}
|
|
932
|
+
/>
|
|
933
|
+
<button onClick={() => generateSummary(note.content)}>
|
|
934
|
+
Generate Summary
|
|
935
|
+
</button>
|
|
936
|
+
</div>
|
|
937
|
+
))}
|
|
938
|
+
</div>
|
|
939
|
+
|
|
940
|
+
<div className="ai-chat">
|
|
941
|
+
<h2>Study Assistant</h2>
|
|
942
|
+
{messages.map((msg, index) => (
|
|
943
|
+
<div key={index} className={`message ${msg.role}`}>
|
|
944
|
+
{msg.content}
|
|
945
|
+
</div>
|
|
946
|
+
))}
|
|
947
|
+
<button onClick={() => append([{ role: 'user', content: 'Help me study' }])}>
|
|
948
|
+
Get Study Help
|
|
949
|
+
</button>
|
|
950
|
+
</div>
|
|
951
|
+
</div>
|
|
952
|
+
);
|
|
953
|
+
};
|
|
954
|
+
|
|
955
|
+
const App = () => (
|
|
956
|
+
<PluginProvider pluginId="study-notes-plugin">
|
|
957
|
+
<HashRouter>
|
|
958
|
+
<Routes>
|
|
959
|
+
<Route path="/" element={<StudyNotesPlugin />} />
|
|
960
|
+
</Routes>
|
|
961
|
+
</HashRouter>
|
|
962
|
+
</PluginProvider>
|
|
963
|
+
);
|
|
964
|
+
|
|
965
|
+
export default App;
|
|
966
|
+
```
|
|
967
|
+
|
|
968
|
+
## Best Practices
|
|
969
|
+
|
|
970
|
+
1. **Performance**: Use lazy loading for pages and implement proper loading states
|
|
971
|
+
2. **State Management**: Leverage React hooks and context when needed
|
|
972
|
+
3. **Type Safety**: Use TypeScript interfaces for all data structures
|
|
973
|
+
4. **Event Cleanup**: Always unsubscribe from events in useEffect cleanup
|
|
974
|
+
5. **Responsive Design**: Use TailwindCSS classes for responsive layouts
|
|
975
|
+
|
|
976
|
+
## License
|
|
977
|
+
|
|
978
|
+
MIT License - see LICENSE file for details.
|
|
@@ -30,7 +30,7 @@ export function AudioInputField({ onSubmit, onAudioControl, blockSubmission = fa
|
|
|
30
30
|
return (_jsxs("div", { className: "flex items-center bg-gray-600 pt-2 pb-2 p-2", children: [onAudioControl && _jsx("button", { onClick: () => {
|
|
31
31
|
onAudioControl(!audioEnabled);
|
|
32
32
|
setAudioEnabled(!audioEnabled);
|
|
33
|
-
}, className: "cursor-default", children: audioEnabled ? _jsx(HiMiniSpeakerWave, { className: 'w-9 h-9 cursor-pointer' }) : _jsx(HiMiniSpeakerXMark, { className: 'w-9 h-9 cursor-pointer' }) }), _jsx(VoiceRecorder, { onVoiceRecorded: (m) => {
|
|
33
|
+
}, className: "cursor-default", children: audioEnabled ? _jsx(HiMiniSpeakerWave, { className: 'w-9 h-9 cursor-pointer' }) : _jsx(HiMiniSpeakerXMark, { className: 'w-9 h-9 cursor-pointer' }) }), _jsx(VoiceRecorder, { onRecordingStatusChange: () => { }, onVoiceRecorded: (m) => {
|
|
34
34
|
console.log('onVoiceRecorded', m);
|
|
35
35
|
handleSubmit(m);
|
|
36
36
|
} }), _jsx("textarea", { value: text, onChange: (e) => setText(e.target.value), onKeyDown: handleKeyDown, className: "flex-1 border-none rounded-lg p-2 text-gray-800 focus::outline-none", placeholder: 'Type a message...', disabled: blockSubmission }), _jsx("button", { onClick: () => handleSubmit(), className: "cursor-default", disabled: blockSubmission, children: _jsx(BiSolidRightArrow, { className: 'w-9 h-10 cursor-pointer' }) })] }));
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect, useRef } from 'react';
|
|
3
|
-
import { EventBus } from '../../../
|
|
3
|
+
import { EventBus } from '../../../plugin/fromRimori/EventBus';
|
|
4
4
|
export function CircleAudioAvatar({ imageUrl, className, isDarkTheme = false, width = "150px" }) {
|
|
5
5
|
const canvasRef = useRef(null);
|
|
6
6
|
const currentLoudnessRef = useRef(0);
|
|
@@ -17,8 +17,9 @@ export function getPlugins(supabase) {
|
|
|
17
17
|
return (data || []).map((plugin) => ({
|
|
18
18
|
id: plugin.id,
|
|
19
19
|
title: plugin.title,
|
|
20
|
+
description: plugin.description,
|
|
20
21
|
icon_url: plugin.icon_url,
|
|
21
|
-
|
|
22
|
+
endpoint: plugin.endpoint,
|
|
22
23
|
context_menu_actions: plugin.context_menu_actions,
|
|
23
24
|
plugin_pages: plugin.plugin_pages,
|
|
24
25
|
sidebar_pages: plugin.sidebar_pages,
|
package/dist/core.d.ts
CHANGED
|
@@ -1,11 +1,6 @@
|
|
|
1
|
-
export * from "./controller/AIController";
|
|
2
|
-
export * from "./controller/SharedContentController";
|
|
3
|
-
export * from "./controller/SettingsController";
|
|
4
1
|
export * from "./plugin/RimoriClient";
|
|
5
2
|
export * from "./plugin/PluginController";
|
|
6
3
|
export * from "./utils/difficultyConverter";
|
|
7
4
|
export * from "./utils/PluginUtils";
|
|
8
5
|
export * from "./worker/WorkerSetup";
|
|
9
|
-
export * from "./plugin/fromRimori/EventBus";
|
|
10
|
-
export * from "./plugin/AccomplishmentHandler";
|
|
11
6
|
export * from "./utils/Language";
|
package/dist/core.js
CHANGED
|
@@ -1,12 +1,7 @@
|
|
|
1
1
|
// Core functionality exports
|
|
2
|
-
export * from "./controller/AIController";
|
|
3
|
-
export * from "./controller/SharedContentController";
|
|
4
|
-
export * from "./controller/SettingsController";
|
|
5
2
|
export * from "./plugin/RimoriClient";
|
|
6
3
|
export * from "./plugin/PluginController";
|
|
7
4
|
export * from "./utils/difficultyConverter";
|
|
8
5
|
export * from "./utils/PluginUtils";
|
|
9
6
|
export * from "./worker/WorkerSetup";
|
|
10
|
-
export * from "./plugin/fromRimori/EventBus";
|
|
11
|
-
export * from "./plugin/AccomplishmentHandler";
|
|
12
7
|
export * from "./utils/Language";
|
package/dist/index.d.ts
CHANGED
|
@@ -1,15 +1,7 @@
|
|
|
1
1
|
export * from './core';
|
|
2
2
|
export * from './components';
|
|
3
|
-
export * from "./components/MarkdownEditor";
|
|
4
|
-
export * from "./components/CRUDModal";
|
|
5
|
-
export * from "./components/Spinner";
|
|
6
|
-
export * from "./components/audio/Playbutton";
|
|
7
|
-
export * from "./controller/AIController";
|
|
8
|
-
export * from "./controller/SharedContentController";
|
|
9
|
-
export * from "./controller/SettingsController";
|
|
10
3
|
export * from "./hooks/UseChatHook";
|
|
11
4
|
export * from "./plugin/RimoriClient";
|
|
12
|
-
export * from "./plugin/ThemeSetter";
|
|
13
5
|
export * from "./providers/PluginProvider";
|
|
14
6
|
export * from "./utils/difficultyConverter";
|
|
15
7
|
export * from "./utils/PluginUtils";
|
package/dist/index.js
CHANGED
|
@@ -1,16 +1,8 @@
|
|
|
1
1
|
// Re-export everything
|
|
2
2
|
export * from './core';
|
|
3
3
|
export * from './components';
|
|
4
|
-
export * from "./components/MarkdownEditor";
|
|
5
|
-
export * from "./components/CRUDModal";
|
|
6
|
-
export * from "./components/Spinner";
|
|
7
|
-
export * from "./components/audio/Playbutton";
|
|
8
|
-
export * from "./controller/AIController";
|
|
9
|
-
export * from "./controller/SharedContentController";
|
|
10
|
-
export * from "./controller/SettingsController";
|
|
11
4
|
export * from "./hooks/UseChatHook";
|
|
12
5
|
export * from "./plugin/RimoriClient";
|
|
13
|
-
export * from "./plugin/ThemeSetter";
|
|
14
6
|
export * from "./providers/PluginProvider";
|
|
15
7
|
export * from "./utils/difficultyConverter";
|
|
16
8
|
export * from "./utils/PluginUtils";
|
|
@@ -2,11 +2,8 @@ export interface Plugin {
|
|
|
2
2
|
id: string;
|
|
3
3
|
title: string;
|
|
4
4
|
description: string;
|
|
5
|
-
git_repository: string;
|
|
6
|
-
website: string;
|
|
7
5
|
icon_url: string;
|
|
8
6
|
version: string;
|
|
9
|
-
author: string;
|
|
10
7
|
endpoint: string;
|
|
11
8
|
context_menu_actions: MenuEntry[];
|
|
12
9
|
plugin_pages: PluginPage[];
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { Tool } from '../../core';
|
|
2
1
|
import { useEffect, useMemo, useState } from 'react';
|
|
3
2
|
import { VoiceRecorder } from './EmbeddedAssistent/VoiceRecoder';
|
|
4
3
|
import { MessageSender } from './EmbeddedAssistent/TTS/MessageSender';
|
|
5
4
|
import { CircleAudioAvatar } from './EmbeddedAssistent/CircleAudioAvatar';
|
|
5
|
+
import { Tool } from '../../controller/AIController';
|
|
6
6
|
import { useChat } from '../../hooks/UseChatHook';
|
|
7
7
|
import { usePlugin } from '../../components';
|
|
8
8
|
import { getFirstMessages } from './utils';
|
|
@@ -43,7 +43,7 @@ export function AudioInputField({ onSubmit, onAudioControl, blockSubmission = fa
|
|
|
43
43
|
className="cursor-default">
|
|
44
44
|
{audioEnabled ? <HiMiniSpeakerWave className='w-9 h-9 cursor-pointer' /> : <HiMiniSpeakerXMark className='w-9 h-9 cursor-pointer' />}
|
|
45
45
|
</button>}
|
|
46
|
-
<VoiceRecorder onVoiceRecorded={(m: string) => {
|
|
46
|
+
<VoiceRecorder onRecordingStatusChange={() => {}} onVoiceRecorded={(m: string) => {
|
|
47
47
|
console.log('onVoiceRecorded', m);
|
|
48
48
|
handleSubmit(m);
|
|
49
49
|
}}
|
|
@@ -12,8 +12,9 @@ export async function getPlugins(supabase: SupabaseClient): Promise<Plugin[]> {
|
|
|
12
12
|
return (data || []).map((plugin: any) => ({
|
|
13
13
|
id: plugin.id,
|
|
14
14
|
title: plugin.title,
|
|
15
|
+
description: plugin.description,
|
|
15
16
|
icon_url: plugin.icon_url,
|
|
16
|
-
|
|
17
|
+
endpoint: plugin.endpoint,
|
|
17
18
|
context_menu_actions: plugin.context_menu_actions,
|
|
18
19
|
plugin_pages: plugin.plugin_pages,
|
|
19
20
|
sidebar_pages: plugin.sidebar_pages,
|
package/src/core.ts
CHANGED
|
@@ -1,12 +1,7 @@
|
|
|
1
1
|
// Core functionality exports
|
|
2
|
-
export * from "./controller/AIController";
|
|
3
|
-
export * from "./controller/SharedContentController";
|
|
4
|
-
export * from "./controller/SettingsController";
|
|
5
2
|
export * from "./plugin/RimoriClient";
|
|
6
3
|
export * from "./plugin/PluginController";
|
|
7
4
|
export * from "./utils/difficultyConverter";
|
|
8
5
|
export * from "./utils/PluginUtils";
|
|
9
6
|
export * from "./worker/WorkerSetup";
|
|
10
|
-
export * from "./plugin/fromRimori/EventBus";
|
|
11
|
-
export * from "./plugin/AccomplishmentHandler";
|
|
12
7
|
export * from "./utils/Language";
|
package/src/index.ts
CHANGED
|
@@ -1,16 +1,8 @@
|
|
|
1
1
|
// Re-export everything
|
|
2
2
|
export * from './core';
|
|
3
3
|
export * from './components';
|
|
4
|
-
export * from "./components/MarkdownEditor";
|
|
5
|
-
export * from "./components/CRUDModal";
|
|
6
|
-
export * from "./components/Spinner";
|
|
7
|
-
export * from "./components/audio/Playbutton";
|
|
8
|
-
export * from "./controller/AIController";
|
|
9
|
-
export * from "./controller/SharedContentController";
|
|
10
|
-
export * from "./controller/SettingsController";
|
|
11
4
|
export * from "./hooks/UseChatHook";
|
|
12
5
|
export * from "./plugin/RimoriClient";
|
|
13
|
-
export * from "./plugin/ThemeSetter";
|
|
14
6
|
export * from "./providers/PluginProvider";
|
|
15
7
|
export * from "./utils/difficultyConverter";
|
|
16
8
|
export * from "./utils/PluginUtils";
|
|
@@ -5,11 +5,8 @@ export interface Plugin {
|
|
|
5
5
|
id: string;
|
|
6
6
|
title: string;
|
|
7
7
|
description: string;
|
|
8
|
-
git_repository: string;
|
|
9
|
-
website: string;
|
|
10
8
|
icon_url: string;
|
|
11
9
|
version: string;
|
|
12
|
-
author: string;
|
|
13
10
|
endpoint: string;
|
|
14
11
|
context_menu_actions: MenuEntry[];
|
|
15
12
|
plugin_pages: PluginPage[];
|