@rimori/client 1.0.1 → 1.0.3
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/dist/components/audio/Playbutton.js +1 -1
- package/dist/controller/PluginController.d.ts +14 -0
- package/dist/controller/PluginController.js +30 -0
- package/dist/controller/SettingsController.d.ts +1 -0
- package/dist/controller/SidePluginController.d.ts +14 -0
- package/dist/controller/SidePluginController.js +30 -0
- package/dist/controller/VoiceController.js +1 -1
- package/dist/index.d.ts +9 -8
- package/dist/index.js +9 -8
- package/dist/plugin/RimoriClient.d.ts +7 -0
- package/dist/plugin/RimoriClient.js +13 -0
- package/dist/providers/PluginProvider.js +62 -8
- package/package.json +1 -1
- package/src/components/audio/Playbutton.tsx +1 -1
- package/src/controller/SettingsController.ts +1 -0
- package/src/controller/SidePluginController.ts +36 -0
- package/src/controller/VoiceController.ts +1 -1
- package/src/index.ts +9 -8
- package/src/plugin/RimoriClient.ts +13 -0
- package/src/providers/PluginProvider.tsx +67 -9
|
@@ -11,7 +11,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
11
11
|
import { useState, useEffect } from 'react';
|
|
12
12
|
import { FaPlayCircle, FaStopCircle } from "react-icons/fa";
|
|
13
13
|
import { usePlugin } from "../../providers/PluginProvider";
|
|
14
|
-
import Spinner from '../Spinner';
|
|
14
|
+
import { Spinner } from '../Spinner';
|
|
15
15
|
import { EmitterSingleton } from '../../providers/EventEmitter';
|
|
16
16
|
export const AudioPlayOptions = [0.8, 0.9, 1.0, 1.1, 1.2, 1.5];
|
|
17
17
|
let isFetchingAudio = false;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { SupabaseClient } from '@supabase/supabase-js';
|
|
2
|
+
export interface Plugin {
|
|
3
|
+
id: string;
|
|
4
|
+
title: string;
|
|
5
|
+
icon_url: string;
|
|
6
|
+
website: string;
|
|
7
|
+
context_menu_actions: string;
|
|
8
|
+
plugin_pages: string;
|
|
9
|
+
sidebar_pages: string;
|
|
10
|
+
settings_page: string;
|
|
11
|
+
version: string;
|
|
12
|
+
external_hosted_url: string;
|
|
13
|
+
}
|
|
14
|
+
export declare function getPlugins(supabase: SupabaseClient): Promise<Plugin[]>;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
export function getPlugins(supabase) {
|
|
11
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
12
|
+
let { data, error } = yield supabase.from('plugins').select('*');
|
|
13
|
+
if (error) {
|
|
14
|
+
console.error(error);
|
|
15
|
+
return [];
|
|
16
|
+
}
|
|
17
|
+
return (data || []).map((plugin) => ({
|
|
18
|
+
id: plugin.id,
|
|
19
|
+
title: plugin.title,
|
|
20
|
+
icon_url: plugin.icon_url,
|
|
21
|
+
website: plugin.website,
|
|
22
|
+
context_menu_actions: plugin.context_menu_actions,
|
|
23
|
+
plugin_pages: plugin.plugin_pages,
|
|
24
|
+
sidebar_pages: plugin.sidebar_pages,
|
|
25
|
+
settings_page: plugin.settings_page,
|
|
26
|
+
version: plugin.version,
|
|
27
|
+
external_hosted_url: plugin.external_hosted_url,
|
|
28
|
+
}));
|
|
29
|
+
});
|
|
30
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { SupabaseClient } from '@supabase/supabase-js';
|
|
2
|
+
export interface Plugin {
|
|
3
|
+
id: string;
|
|
4
|
+
title: string;
|
|
5
|
+
icon_url: string;
|
|
6
|
+
website: string;
|
|
7
|
+
context_menu_actions: string;
|
|
8
|
+
plugin_pages: string;
|
|
9
|
+
sidebar_pages: string;
|
|
10
|
+
settings_page: string;
|
|
11
|
+
version: string;
|
|
12
|
+
external_hosted_url: string;
|
|
13
|
+
}
|
|
14
|
+
export declare function getPlugins(supabase: SupabaseClient): Promise<Plugin[]>;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
export function getPlugins(supabase) {
|
|
11
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
12
|
+
let { data, error } = yield supabase.from('plugins').select('*');
|
|
13
|
+
if (error) {
|
|
14
|
+
console.error(error);
|
|
15
|
+
return [];
|
|
16
|
+
}
|
|
17
|
+
return (data || []).map((plugin) => ({
|
|
18
|
+
id: plugin.id,
|
|
19
|
+
title: plugin.title,
|
|
20
|
+
icon_url: plugin.icon_url,
|
|
21
|
+
website: plugin.website,
|
|
22
|
+
context_menu_actions: plugin.context_menu_actions,
|
|
23
|
+
plugin_pages: plugin.plugin_pages,
|
|
24
|
+
sidebar_pages: plugin.sidebar_pages,
|
|
25
|
+
settings_page: plugin.settings_page,
|
|
26
|
+
version: plugin.version,
|
|
27
|
+
external_hosted_url: plugin.external_hosted_url,
|
|
28
|
+
}));
|
|
29
|
+
});
|
|
30
|
+
}
|
|
@@ -11,7 +11,7 @@ export function getSTTResponse(supabase, audio) {
|
|
|
11
11
|
return __awaiter(this, void 0, void 0, function* () {
|
|
12
12
|
const formData = new FormData();
|
|
13
13
|
formData.append('file', audio);
|
|
14
|
-
return yield supabase.functions.invoke('speech', { method: 'POST', body: formData }).then((
|
|
14
|
+
return yield supabase.functions.invoke('speech', { method: 'POST', body: formData }).then(({ data }) => data.text);
|
|
15
15
|
});
|
|
16
16
|
}
|
|
17
17
|
export function getTTSResponse(supabaseUrl, request, token) {
|
package/dist/index.d.ts
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
export * from "./components/MarkdownEditor";
|
|
2
2
|
export * from "./components/CRUDModal";
|
|
3
|
-
export * from "./plugin/ThemeSetter";
|
|
4
|
-
export * from "./components/audio/Playbutton";
|
|
5
|
-
export * from "./providers/EventEmitterContext";
|
|
6
|
-
export * from "./providers/PluginProvider";
|
|
7
|
-
export * from "./providers/EventEmitter";
|
|
8
3
|
export * from "./components/Spinner";
|
|
9
|
-
export * from "./
|
|
10
|
-
export * from "./utils/PluginUtils";
|
|
11
|
-
export * from "./plugin/RimoriClient";
|
|
4
|
+
export * from "./components/audio/Playbutton";
|
|
12
5
|
export * from "./controller/AIController";
|
|
13
6
|
export * from "./controller/SharedContentController";
|
|
14
7
|
export * from "./controller/SettingsController";
|
|
8
|
+
export * from "./hooks/UseChatHook";
|
|
9
|
+
export * from "./plugin/RimoriClient";
|
|
10
|
+
export * from "./plugin/ThemeSetter";
|
|
11
|
+
export * from "./providers/EventEmitter";
|
|
12
|
+
export * from "./providers/EventEmitterContext";
|
|
13
|
+
export * from "./providers/PluginProvider";
|
|
14
|
+
export * from "./utils/difficultyConverter";
|
|
15
|
+
export * from "./utils/PluginUtils";
|
package/dist/index.js
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
export * from "./components/MarkdownEditor";
|
|
2
2
|
export * from "./components/CRUDModal";
|
|
3
|
-
export * from "./plugin/ThemeSetter";
|
|
4
|
-
export * from "./components/audio/Playbutton";
|
|
5
|
-
export * from "./providers/EventEmitterContext";
|
|
6
|
-
export * from "./providers/PluginProvider";
|
|
7
|
-
export * from "./providers/EventEmitter";
|
|
8
3
|
export * from "./components/Spinner";
|
|
9
|
-
export * from "./
|
|
10
|
-
export * from "./utils/PluginUtils";
|
|
11
|
-
export * from "./plugin/RimoriClient";
|
|
4
|
+
export * from "./components/audio/Playbutton";
|
|
12
5
|
export * from "./controller/AIController";
|
|
13
6
|
export * from "./controller/SharedContentController";
|
|
14
7
|
export * from "./controller/SettingsController";
|
|
8
|
+
export * from "./hooks/UseChatHook";
|
|
9
|
+
export * from "./plugin/RimoriClient";
|
|
10
|
+
export * from "./plugin/ThemeSetter";
|
|
11
|
+
export * from "./providers/EventEmitter";
|
|
12
|
+
export * from "./providers/EventEmitterContext";
|
|
13
|
+
export * from "./providers/PluginProvider";
|
|
14
|
+
export * from "./utils/difficultyConverter";
|
|
15
|
+
export * from "./utils/PluginUtils";
|
|
@@ -5,6 +5,7 @@ import { PostgrestQueryBuilder, PostgrestFilterBuilder } from "@supabase/postgre
|
|
|
5
5
|
import { BasicAssignment } from "../controller/SharedContentController";
|
|
6
6
|
import { Message, Tool, OnLLMResponse } from "../controller/AIController";
|
|
7
7
|
import { ObjectRequest } from "../controller/ObjectController";
|
|
8
|
+
import { Plugin } from "../controller/SidePluginController";
|
|
8
9
|
export declare class RimoriClient {
|
|
9
10
|
private static instance;
|
|
10
11
|
private superbase;
|
|
@@ -63,6 +64,11 @@ export declare class RimoriClient {
|
|
|
63
64
|
getAIResponseStream(messages: Message[], onMessage: OnLLMResponse, tools?: Tool[]): Promise<void>;
|
|
64
65
|
getVoiceResponse(text: string, voice?: string, speed?: number, language?: string): Promise<Blob>;
|
|
65
66
|
getVoiceToTextResponse(file: Blob): Promise<string>;
|
|
67
|
+
/**
|
|
68
|
+
* Fetches all installed plugins.
|
|
69
|
+
* @returns A promise that resolves to an array of plugins
|
|
70
|
+
*/
|
|
71
|
+
getPlugins(): Promise<Plugin[]>;
|
|
66
72
|
generateObject(request: ObjectRequest): Promise<any>;
|
|
67
73
|
/**
|
|
68
74
|
* Fetch new shared content.
|
|
@@ -88,4 +94,5 @@ export declare class RimoriClient {
|
|
|
88
94
|
* @param assignmentId The id of the shared content item to complete.
|
|
89
95
|
*/
|
|
90
96
|
completeSharedContent(type: string, assignmentId: string): Promise<void>;
|
|
97
|
+
triggerSidebarAction(pluginId: string, actionKey: string, text?: string): void;
|
|
91
98
|
}
|
|
@@ -12,6 +12,7 @@ import { getSTTResponse, getTTSResponse } from "../controller/VoiceController";
|
|
|
12
12
|
import { SharedContentController } from "../controller/SharedContentController";
|
|
13
13
|
import { streamChatGPT, generateText } from "../controller/AIController";
|
|
14
14
|
import { generateObject as generateObjectFunction } from "../controller/ObjectController";
|
|
15
|
+
import { getPlugins } from "../controller/SidePluginController";
|
|
15
16
|
export class RimoriClient {
|
|
16
17
|
constructor(options) {
|
|
17
18
|
this.superbase = options.supabase;
|
|
@@ -121,6 +122,15 @@ export class RimoriClient {
|
|
|
121
122
|
getVoiceToTextResponse(file) {
|
|
122
123
|
return getSTTResponse(this.superbase, file);
|
|
123
124
|
}
|
|
125
|
+
/**
|
|
126
|
+
* Fetches all installed plugins.
|
|
127
|
+
* @returns A promise that resolves to an array of plugins
|
|
128
|
+
*/
|
|
129
|
+
getPlugins() {
|
|
130
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
131
|
+
return getPlugins(this.superbase);
|
|
132
|
+
});
|
|
133
|
+
}
|
|
124
134
|
generateObject(request) {
|
|
125
135
|
return __awaiter(this, void 0, void 0, function* () {
|
|
126
136
|
const token = yield this.plugin.getToken();
|
|
@@ -160,4 +170,7 @@ export class RimoriClient {
|
|
|
160
170
|
return this.sharedContentController.completeSharedContent(type, assignmentId);
|
|
161
171
|
});
|
|
162
172
|
}
|
|
173
|
+
triggerSidebarAction(pluginId, actionKey, text) {
|
|
174
|
+
this.emit("triggerSidebarAction", { pluginId, actionKey, text });
|
|
175
|
+
}
|
|
163
176
|
}
|
|
@@ -4,6 +4,7 @@ import { PluginController } from '../plugin/PluginController';
|
|
|
4
4
|
const PluginContext = createContext(null);
|
|
5
5
|
export const PluginProvider = ({ children }) => {
|
|
6
6
|
const [plugin, setPlugin] = useState(null);
|
|
7
|
+
const [contextMenuOnSelect, setContextMenuOnTextSelection] = useState(false);
|
|
7
8
|
//route change
|
|
8
9
|
useEffect(() => {
|
|
9
10
|
let lastHash = window.location.hash;
|
|
@@ -16,28 +17,81 @@ export const PluginProvider = ({ children }) => {
|
|
|
16
17
|
}, 100);
|
|
17
18
|
PluginController.getInstance().then(setPlugin);
|
|
18
19
|
}, []);
|
|
20
|
+
//check if context menu opens on text selection
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
if (!plugin)
|
|
23
|
+
return;
|
|
24
|
+
plugin.getSettings({
|
|
25
|
+
languageLevel: "A1",
|
|
26
|
+
motherTongue: "English",
|
|
27
|
+
contextMenuOnSelect: false,
|
|
28
|
+
}, "user").then((settings) => {
|
|
29
|
+
setContextMenuOnTextSelection(settings.contextMenuOnSelect);
|
|
30
|
+
}).catch(error => {
|
|
31
|
+
console.error('Error fetching settings:', error);
|
|
32
|
+
});
|
|
33
|
+
}, [plugin]);
|
|
34
|
+
//detect page height change
|
|
35
|
+
useEffect(() => {
|
|
36
|
+
const body = document.body;
|
|
37
|
+
const handleResize = () => plugin === null || plugin === void 0 ? void 0 : plugin.emit('heightAdjustment', body.clientHeight);
|
|
38
|
+
body.addEventListener('resize', handleResize);
|
|
39
|
+
handleResize();
|
|
40
|
+
return () => body.removeEventListener('resize', handleResize);
|
|
41
|
+
}, [plugin]);
|
|
19
42
|
//context menu
|
|
20
43
|
useEffect(() => {
|
|
21
|
-
let
|
|
44
|
+
let lastMouseX = 0;
|
|
45
|
+
let lastMouseY = 0;
|
|
46
|
+
let isSelecting = false;
|
|
47
|
+
// Track mouse position
|
|
48
|
+
const handleMouseMove = (e) => {
|
|
49
|
+
lastMouseX = e.clientX;
|
|
50
|
+
lastMouseY = e.clientY;
|
|
51
|
+
};
|
|
22
52
|
const handleContextMenu = (e) => {
|
|
23
53
|
var _a;
|
|
24
54
|
const selection = (_a = window.getSelection()) === null || _a === void 0 ? void 0 : _a.toString().trim();
|
|
25
55
|
if (selection) {
|
|
26
56
|
e.preventDefault();
|
|
27
|
-
// console.log('context menu', selection);
|
|
57
|
+
// console.log('context menu handled', selection);
|
|
28
58
|
plugin === null || plugin === void 0 ? void 0 : plugin.emit('contextMenu', { text: selection, x: e.clientX, y: e.clientY, open: true });
|
|
29
|
-
isOpen = true;
|
|
30
59
|
}
|
|
31
60
|
};
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
61
|
+
const handleSelectionChange = () => {
|
|
62
|
+
var _a;
|
|
63
|
+
// if (triggerOnTextSelection) {
|
|
64
|
+
const selection = (_a = window.getSelection()) === null || _a === void 0 ? void 0 : _a.toString().trim();
|
|
65
|
+
const open = !!selection && isSelecting;
|
|
66
|
+
// console.log('Selection change, contextMenuOnSelect:', contextMenuOnSelect);
|
|
67
|
+
plugin === null || plugin === void 0 ? void 0 : plugin.emit('contextMenu', { text: selection, x: lastMouseX, y: lastMouseY, open });
|
|
68
|
+
// }
|
|
69
|
+
};
|
|
70
|
+
const handleMouseUpDown = (e) => {
|
|
71
|
+
if (e.type === 'mousedown') {
|
|
72
|
+
isSelecting = false;
|
|
73
|
+
}
|
|
74
|
+
else if (e.type === 'mouseup') {
|
|
75
|
+
isSelecting = true;
|
|
76
|
+
// console.log('mouseup, contextMenuOnSelect:', contextMenuOnSelect);
|
|
77
|
+
if (contextMenuOnSelect) {
|
|
78
|
+
handleSelectionChange();
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
};
|
|
35
82
|
document.addEventListener('contextmenu', handleContextMenu);
|
|
83
|
+
document.addEventListener('selectionchange', handleSelectionChange);
|
|
84
|
+
document.addEventListener("mousemove", handleMouseMove);
|
|
85
|
+
document.addEventListener('mousedown', handleMouseUpDown);
|
|
86
|
+
document.addEventListener('mouseup', handleMouseUpDown);
|
|
36
87
|
return () => {
|
|
37
|
-
document.removeEventListener("
|
|
88
|
+
document.removeEventListener("mousemove", handleMouseMove);
|
|
38
89
|
document.removeEventListener('contextmenu', handleContextMenu);
|
|
90
|
+
document.removeEventListener('selectionchange', handleSelectionChange);
|
|
91
|
+
document.removeEventListener('mousedown', handleMouseUpDown);
|
|
92
|
+
document.removeEventListener('mouseup', handleMouseUpDown);
|
|
39
93
|
};
|
|
40
|
-
}, [plugin]);
|
|
94
|
+
}, [plugin, contextMenuOnSelect]);
|
|
41
95
|
if (!plugin) {
|
|
42
96
|
return "";
|
|
43
97
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React, { useState, useEffect } from 'react';
|
|
2
2
|
import { FaPlayCircle, FaStopCircle } from "react-icons/fa";
|
|
3
3
|
import { usePlugin } from "../../providers/PluginProvider";
|
|
4
|
-
import Spinner from '../Spinner';
|
|
4
|
+
import { Spinner } from '../Spinner';
|
|
5
5
|
import { EmitterSingleton } from '../../providers/EventEmitter';
|
|
6
6
|
|
|
7
7
|
type AudioPlayerProps = {
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { SupabaseClient } from '@supabase/supabase-js';
|
|
2
|
+
|
|
3
|
+
export interface Plugin {
|
|
4
|
+
id: string;
|
|
5
|
+
title: string;
|
|
6
|
+
icon_url: string;
|
|
7
|
+
website: string;
|
|
8
|
+
context_menu_actions: string;
|
|
9
|
+
plugin_pages: string;
|
|
10
|
+
sidebar_pages: string;
|
|
11
|
+
settings_page: string;
|
|
12
|
+
version: string;
|
|
13
|
+
external_hosted_url: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export async function getPlugins(supabase: SupabaseClient): Promise<Plugin[]> {
|
|
17
|
+
let { data, error } = await supabase.from('plugins').select('*');
|
|
18
|
+
|
|
19
|
+
if (error) {
|
|
20
|
+
console.error(error);
|
|
21
|
+
return [];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return (data || []).map((plugin: any) => ({
|
|
25
|
+
id: plugin.id,
|
|
26
|
+
title: plugin.title,
|
|
27
|
+
icon_url: plugin.icon_url,
|
|
28
|
+
website: plugin.website,
|
|
29
|
+
context_menu_actions: plugin.context_menu_actions,
|
|
30
|
+
plugin_pages: plugin.plugin_pages,
|
|
31
|
+
sidebar_pages: plugin.sidebar_pages,
|
|
32
|
+
settings_page: plugin.settings_page,
|
|
33
|
+
version: plugin.version,
|
|
34
|
+
external_hosted_url: plugin.external_hosted_url,
|
|
35
|
+
}));
|
|
36
|
+
}
|
|
@@ -4,7 +4,7 @@ export async function getSTTResponse(supabase: SupabaseClient, audio: Blob) {
|
|
|
4
4
|
const formData = new FormData();
|
|
5
5
|
formData.append('file', audio);
|
|
6
6
|
|
|
7
|
-
return await supabase.functions.invoke('speech', { method: 'POST', body: formData }).then((
|
|
7
|
+
return await supabase.functions.invoke('speech', { method: 'POST', body: formData }).then(({ data }) => data.text);
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
export async function getTTSResponse(supabaseUrl: string, request: TTSRequest, token: string) {
|
package/src/index.ts
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
export * from "./components/MarkdownEditor";
|
|
2
2
|
export * from "./components/CRUDModal";
|
|
3
|
-
export * from "./plugin/ThemeSetter";
|
|
4
|
-
export * from "./components/audio/Playbutton";
|
|
5
|
-
export * from "./providers/EventEmitterContext";
|
|
6
|
-
export * from "./providers/PluginProvider";
|
|
7
|
-
export * from "./providers/EventEmitter";
|
|
8
3
|
export * from "./components/Spinner";
|
|
9
|
-
export * from "./
|
|
10
|
-
export * from "./utils/PluginUtils";
|
|
11
|
-
export * from "./plugin/RimoriClient";
|
|
4
|
+
export * from "./components/audio/Playbutton";
|
|
12
5
|
export * from "./controller/AIController";
|
|
13
6
|
export * from "./controller/SharedContentController";
|
|
14
7
|
export * from "./controller/SettingsController";
|
|
8
|
+
export * from "./hooks/UseChatHook";
|
|
9
|
+
export * from "./plugin/RimoriClient";
|
|
10
|
+
export * from "./plugin/ThemeSetter";
|
|
11
|
+
export * from "./providers/EventEmitter";
|
|
12
|
+
export * from "./providers/EventEmitterContext";
|
|
13
|
+
export * from "./providers/PluginProvider";
|
|
14
|
+
export * from "./utils/difficultyConverter";
|
|
15
|
+
export * from "./utils/PluginUtils";
|
|
@@ -7,6 +7,7 @@ import { PostgrestQueryBuilder, PostgrestFilterBuilder } from "@supabase/postgre
|
|
|
7
7
|
import { SharedContentController, BasicAssignment } from "../controller/SharedContentController";
|
|
8
8
|
import { streamChatGPT, Message, Tool, OnLLMResponse, generateText } from "../controller/AIController";
|
|
9
9
|
import { generateObject as generateObjectFunction, ObjectRequest } from "../controller/ObjectController";
|
|
10
|
+
import { getPlugins, Plugin } from "../controller/SidePluginController";
|
|
10
11
|
|
|
11
12
|
interface RimoriClientOptions {
|
|
12
13
|
pluginController: PluginController;
|
|
@@ -166,6 +167,14 @@ export class RimoriClient {
|
|
|
166
167
|
return getSTTResponse(this.superbase, file);
|
|
167
168
|
}
|
|
168
169
|
|
|
170
|
+
/**
|
|
171
|
+
* Fetches all installed plugins.
|
|
172
|
+
* @returns A promise that resolves to an array of plugins
|
|
173
|
+
*/
|
|
174
|
+
public async getPlugins(): Promise<Plugin[]> {
|
|
175
|
+
return getPlugins(this.superbase);
|
|
176
|
+
}
|
|
177
|
+
|
|
169
178
|
public async generateObject(request: ObjectRequest): Promise<any> {
|
|
170
179
|
const token = await this.plugin.getToken();
|
|
171
180
|
return generateObjectFunction(request, token);
|
|
@@ -204,4 +213,8 @@ export class RimoriClient {
|
|
|
204
213
|
public async completeSharedContent(type: string, assignmentId: string) {
|
|
205
214
|
return this.sharedContentController.completeSharedContent(type, assignmentId);
|
|
206
215
|
}
|
|
216
|
+
|
|
217
|
+
public triggerSidebarAction(pluginId: string, actionKey: string, text?: string) {
|
|
218
|
+
this.emit("triggerSidebarAction", { pluginId, actionKey, text });
|
|
219
|
+
}
|
|
207
220
|
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import React, { createContext, useContext, ReactNode, useEffect, useState } from 'react';
|
|
2
2
|
import { PluginController } from '../plugin/PluginController';
|
|
3
3
|
import { RimoriClient } from '../plugin/RimoriClient';
|
|
4
|
+
import { UserSettings } from '../controller/SettingsController';
|
|
5
|
+
|
|
4
6
|
interface PluginProviderProps {
|
|
5
7
|
children: ReactNode;
|
|
6
8
|
}
|
|
@@ -10,6 +12,8 @@ const PluginContext = createContext<RimoriClient | null>(null);
|
|
|
10
12
|
|
|
11
13
|
export const PluginProvider: React.FC<PluginProviderProps> = ({ children }) => {
|
|
12
14
|
const [plugin, setPlugin] = useState<RimoriClient | null>(null);
|
|
15
|
+
const [contextMenuOnSelect, setContextMenuOnTextSelection] = useState(false);
|
|
16
|
+
|
|
13
17
|
//route change
|
|
14
18
|
useEffect(() => {
|
|
15
19
|
let lastHash = window.location.hash;
|
|
@@ -24,31 +28,85 @@ export const PluginProvider: React.FC<PluginProviderProps> = ({ children }) => {
|
|
|
24
28
|
PluginController.getInstance().then(setPlugin);
|
|
25
29
|
}, []);
|
|
26
30
|
|
|
31
|
+
//check if context menu opens on text selection
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
if (!plugin) return;
|
|
34
|
+
plugin.getSettings<UserSettings>({
|
|
35
|
+
languageLevel: "A1",
|
|
36
|
+
motherTongue: "English",
|
|
37
|
+
contextMenuOnSelect: false,
|
|
38
|
+
}, "user").then((settings) => {
|
|
39
|
+
setContextMenuOnTextSelection(settings.contextMenuOnSelect);
|
|
40
|
+
}).catch(error => {
|
|
41
|
+
console.error('Error fetching settings:', error);
|
|
42
|
+
});
|
|
43
|
+
}, [plugin]);
|
|
44
|
+
|
|
45
|
+
//detect page height change
|
|
46
|
+
useEffect(() => {
|
|
47
|
+
const body = document.body;
|
|
48
|
+
const handleResize = () => plugin?.emit('heightAdjustment', body.clientHeight);
|
|
49
|
+
body.addEventListener('resize', handleResize);
|
|
50
|
+
handleResize();
|
|
51
|
+
return () => body.removeEventListener('resize', handleResize);
|
|
52
|
+
}, [plugin]);
|
|
53
|
+
|
|
27
54
|
//context menu
|
|
28
55
|
useEffect(() => {
|
|
29
|
-
let
|
|
56
|
+
let lastMouseX = 0;
|
|
57
|
+
let lastMouseY = 0;
|
|
58
|
+
let isSelecting = false;
|
|
59
|
+
|
|
60
|
+
// Track mouse position
|
|
61
|
+
const handleMouseMove = (e: MouseEvent) => {
|
|
62
|
+
lastMouseX = e.clientX;
|
|
63
|
+
lastMouseY = e.clientY;
|
|
64
|
+
};
|
|
65
|
+
|
|
30
66
|
const handleContextMenu = (e: MouseEvent) => {
|
|
31
67
|
const selection = window.getSelection()?.toString().trim();
|
|
32
68
|
if (selection) {
|
|
33
69
|
e.preventDefault();
|
|
34
|
-
// console.log('context menu', selection);
|
|
70
|
+
// console.log('context menu handled', selection);
|
|
35
71
|
plugin?.emit('contextMenu', { text: selection, x: e.clientX, y: e.clientY, open: true });
|
|
36
|
-
isOpen = true;
|
|
37
72
|
}
|
|
38
73
|
};
|
|
39
74
|
|
|
40
|
-
|
|
41
|
-
|
|
75
|
+
const handleSelectionChange = () => {
|
|
76
|
+
// if (triggerOnTextSelection) {
|
|
77
|
+
const selection = window.getSelection()?.toString().trim();
|
|
78
|
+
const open = !!selection && isSelecting;
|
|
79
|
+
// console.log('Selection change, contextMenuOnSelect:', contextMenuOnSelect);
|
|
80
|
+
plugin?.emit('contextMenu', { text: selection, x: lastMouseX, y: lastMouseY, open });
|
|
81
|
+
// }
|
|
82
|
+
};
|
|
83
|
+
const handleMouseUpDown = (e: MouseEvent) => {
|
|
84
|
+
if (e.type === 'mousedown') {
|
|
85
|
+
isSelecting = false;
|
|
86
|
+
} else if (e.type === 'mouseup') {
|
|
87
|
+
isSelecting = true;
|
|
88
|
+
// console.log('mouseup, contextMenuOnSelect:', contextMenuOnSelect);
|
|
89
|
+
if (contextMenuOnSelect) {
|
|
90
|
+
handleSelectionChange();
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
};
|
|
42
94
|
|
|
43
|
-
document.addEventListener("click", handleClick);
|
|
44
95
|
document.addEventListener('contextmenu', handleContextMenu);
|
|
96
|
+
document.addEventListener('selectionchange', handleSelectionChange);
|
|
97
|
+
document.addEventListener("mousemove", handleMouseMove);
|
|
98
|
+
document.addEventListener('mousedown', handleMouseUpDown);
|
|
99
|
+
document.addEventListener('mouseup', handleMouseUpDown);
|
|
45
100
|
return () => {
|
|
46
|
-
document.removeEventListener("
|
|
101
|
+
document.removeEventListener("mousemove", handleMouseMove);
|
|
47
102
|
document.removeEventListener('contextmenu', handleContextMenu);
|
|
103
|
+
document.removeEventListener('selectionchange', handleSelectionChange);
|
|
104
|
+
document.removeEventListener('mousedown', handleMouseUpDown);
|
|
105
|
+
document.removeEventListener('mouseup', handleMouseUpDown);
|
|
48
106
|
};
|
|
49
|
-
}, [plugin]);
|
|
107
|
+
}, [plugin, contextMenuOnSelect]);
|
|
50
108
|
|
|
51
|
-
if(!plugin){
|
|
109
|
+
if (!plugin) {
|
|
52
110
|
return ""
|
|
53
111
|
}
|
|
54
112
|
|