@rimori/react-client 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +142 -1
- package/dist/{react-client/src/components → components}/ContextMenu.js +1 -2
- package/dist/components/Spinner.d.ts +0 -7
- package/dist/components/Spinner.js +1 -4
- package/dist/components/ai/Assistant.js +1 -1
- package/dist/components/ai/Avatar.d.ts +2 -3
- package/dist/components/ai/Avatar.js +6 -4
- package/dist/components/ai/EmbeddedAssistent/AudioInputField.js +1 -1
- package/dist/components/ai/EmbeddedAssistent/CircleAudioAvatar.js +1 -1
- package/dist/components/ai/EmbeddedAssistent/VoiceRecoder.js +2 -2
- package/dist/components/audio/Playbutton.js +13 -9
- package/dist/hooks/I18nHooks.d.ts +1 -1
- package/dist/{react-client/src/plugin → hooks}/ThemeSetter.d.ts +1 -1
- package/dist/hooks/ThemeSetter.js +31 -0
- package/dist/hooks/UseChatHook.d.ts +2 -2
- package/dist/index.d.ts +9 -0
- package/dist/index.js +9 -0
- package/dist/{react-client/plugin → plugin}/ThemeSetter.d.ts +1 -1
- package/dist/plugin/ThemeSetter.js +31 -0
- package/dist/providers/PluginProvider.d.ts +2 -1
- package/dist/providers/PluginProvider.js +10 -7
- package/dist/{react-client/src/utils → utils}/FullscreenUtils.js +2 -2
- package/package.json +4 -7
- package/src/components/ContextMenu.tsx +2 -2
- package/src/components/ai/Avatar.tsx +9 -4
- package/src/components/ai/EmbeddedAssistent/AudioInputField.tsx +1 -1
- package/src/components/audio/Playbutton.tsx +28 -1
- package/src/hooks/ThemeSetter.ts +40 -0
- package/src/index.ts +10 -0
- package/src/providers/PluginProvider.tsx +12 -8
- package/tsconfig.json +6 -12
- package/dist/components/components/ContextMenu.d.ts +0 -10
- package/dist/components/components/ContextMenu.js +0 -135
- package/dist/react-client/plugin/ThemeSetter.js +0 -19
- package/dist/react-client/src/components/MarkdownEditor.d.ts +0 -8
- package/dist/react-client/src/components/MarkdownEditor.js +0 -48
- package/dist/react-client/src/components/Spinner.d.ts +0 -8
- package/dist/react-client/src/components/Spinner.js +0 -4
- package/dist/react-client/src/components/ai/Assistant.d.ts +0 -9
- package/dist/react-client/src/components/ai/Assistant.js +0 -58
- package/dist/react-client/src/components/ai/Avatar.d.ts +0 -14
- package/dist/react-client/src/components/ai/Avatar.js +0 -59
- package/dist/react-client/src/components/ai/EmbeddedAssistent/AudioInputField.d.ts +0 -7
- package/dist/react-client/src/components/ai/EmbeddedAssistent/AudioInputField.js +0 -37
- package/dist/react-client/src/components/ai/EmbeddedAssistent/CircleAudioAvatar.d.ts +0 -8
- package/dist/react-client/src/components/ai/EmbeddedAssistent/CircleAudioAvatar.js +0 -79
- package/dist/react-client/src/components/ai/EmbeddedAssistent/TTS/MessageSender.d.ts +0 -19
- package/dist/react-client/src/components/ai/EmbeddedAssistent/TTS/MessageSender.js +0 -91
- package/dist/react-client/src/components/ai/EmbeddedAssistent/TTS/Player.d.ts +0 -27
- package/dist/react-client/src/components/ai/EmbeddedAssistent/TTS/Player.js +0 -185
- package/dist/react-client/src/components/ai/utils.d.ts +0 -6
- package/dist/react-client/src/components/ai/utils.js +0 -13
- package/dist/react-client/src/components/audio/Playbutton.d.ts +0 -15
- package/dist/react-client/src/components/audio/Playbutton.js +0 -82
- package/dist/react-client/src/components/components/ContextMenu.d.ts +0 -10
- package/dist/react-client/src/components/components/ContextMenu.js +0 -135
- package/dist/react-client/src/hooks/I18nHooks.d.ts +0 -11
- package/dist/react-client/src/hooks/I18nHooks.js +0 -25
- package/dist/react-client/src/hooks/UseChatHook.d.ts +0 -10
- package/dist/react-client/src/hooks/UseChatHook.js +0 -29
- package/dist/react-client/src/plugin/ThemeSetter.js +0 -19
- package/dist/react-client/src/providers/PluginProvider.d.ts +0 -12
- package/dist/react-client/src/providers/PluginProvider.js +0 -142
- package/dist/react-client/src/utils/PluginUtils.d.ts +0 -2
- package/dist/react-client/src/utils/PluginUtils.js +0 -23
- package/dist/rimori-client/src/cli/types/DatabaseTypes.d.ts +0 -103
- package/dist/rimori-client/src/cli/types/DatabaseTypes.js +0 -2
- package/dist/rimori-client/src/controller/AIController.d.ts +0 -15
- package/dist/rimori-client/src/controller/AIController.js +0 -255
- package/dist/rimori-client/src/controller/AccomplishmentController.d.ts +0 -38
- package/dist/rimori-client/src/controller/AccomplishmentController.js +0 -112
- package/dist/rimori-client/src/controller/AudioController.d.ts +0 -37
- package/dist/rimori-client/src/controller/AudioController.js +0 -68
- package/dist/rimori-client/src/controller/ExerciseController.d.ts +0 -54
- package/dist/rimori-client/src/controller/ExerciseController.js +0 -74
- package/dist/rimori-client/src/controller/ObjectController.d.ts +0 -42
- package/dist/rimori-client/src/controller/ObjectController.js +0 -76
- package/dist/rimori-client/src/controller/SettingsController.d.ts +0 -79
- package/dist/rimori-client/src/controller/SettingsController.js +0 -118
- package/dist/rimori-client/src/controller/SharedContentController.d.ts +0 -106
- package/dist/rimori-client/src/controller/SharedContentController.js +0 -285
- package/dist/rimori-client/src/controller/TranslationController.d.ts +0 -38
- package/dist/rimori-client/src/controller/TranslationController.js +0 -106
- package/dist/rimori-client/src/controller/VoiceController.d.ts +0 -9
- package/dist/rimori-client/src/controller/VoiceController.js +0 -37
- package/dist/rimori-client/src/fromRimori/EventBus.d.ts +0 -101
- package/dist/rimori-client/src/fromRimori/EventBus.js +0 -263
- package/dist/rimori-client/src/fromRimori/PluginTypes.d.ts +0 -174
- package/dist/rimori-client/src/fromRimori/PluginTypes.js +0 -1
- package/dist/rimori-client/src/index.d.ts +0 -11
- package/dist/rimori-client/src/index.js +0 -10
- package/dist/rimori-client/src/plugin/CommunicationHandler.d.ts +0 -48
- package/dist/rimori-client/src/plugin/CommunicationHandler.js +0 -234
- package/dist/rimori-client/src/plugin/Logger.d.ts +0 -73
- package/dist/rimori-client/src/plugin/Logger.js +0 -308
- package/dist/rimori-client/src/plugin/RimoriClient.d.ts +0 -258
- package/dist/rimori-client/src/plugin/RimoriClient.js +0 -375
- package/dist/rimori-client/src/plugin/StandaloneClient.d.ts +0 -17
- package/dist/rimori-client/src/plugin/StandaloneClient.js +0 -115
- package/dist/rimori-client/src/utils/difficultyConverter.d.ts +0 -4
- package/dist/rimori-client/src/utils/difficultyConverter.js +0 -10
- package/dist/rimori-client/src/utils/endpoint.d.ts +0 -2
- package/dist/rimori-client/src/utils/endpoint.js +0 -2
- package/dist/utils/PluginUtils.d.ts +0 -2
- package/dist/utils/PluginUtils.js +0 -23
- package/index.ts +0 -6
- package/src/components/MarkdownEditor.tsx +0 -144
- package/src/components/Spinner.tsx +0 -29
- package/src/plugin/ThemeSetter.ts +0 -23
- package/src/utils/FullscreenUtils.ts +0 -22
- /package/dist/{react-client/src/components → components}/ContextMenu.d.ts +0 -0
- /package/dist/{react-client/src/components/ai/EmbeddedAssistent/VoiceRecoder.d.ts → components/ai/EmbeddedAssistent/VoiceRecorder.d.ts} +0 -0
- /package/dist/{react-client/src/components/ai/EmbeddedAssistent/VoiceRecoder.js → components/ai/EmbeddedAssistent/VoiceRecorder.js} +0 -0
- /package/dist/{react-client/src/utils → utils}/FullscreenUtils.d.ts +0 -0
- /package/src/components/ai/EmbeddedAssistent/{VoiceRecoder.tsx → VoiceRecorder.tsx} +0 -0
|
@@ -1,118 +0,0 @@
|
|
|
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 class SettingsController {
|
|
11
|
-
constructor(supabase, pluginId, guild) {
|
|
12
|
-
this.supabase = supabase;
|
|
13
|
-
this.pluginId = pluginId;
|
|
14
|
-
this.guild = guild;
|
|
15
|
-
}
|
|
16
|
-
/**
|
|
17
|
-
* Fetches settings based on guild configuration.
|
|
18
|
-
* If guild doesn't allow user settings, fetches guild-level settings.
|
|
19
|
-
* Otherwise, fetches user-specific settings.
|
|
20
|
-
* @returns The settings object or null if not found.
|
|
21
|
-
*/
|
|
22
|
-
fetchSettings() {
|
|
23
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
24
|
-
var _a;
|
|
25
|
-
const isGuildSetting = !this.guild.allowUserPluginSettings;
|
|
26
|
-
const { data } = yield this.supabase
|
|
27
|
-
.from('plugin_settings')
|
|
28
|
-
.select('*')
|
|
29
|
-
.eq('plugin_id', this.pluginId)
|
|
30
|
-
.eq('guild_id', this.guild.id)
|
|
31
|
-
.eq('is_guild_setting', isGuildSetting)
|
|
32
|
-
.maybeSingle();
|
|
33
|
-
return (_a = data === null || data === void 0 ? void 0 : data.settings) !== null && _a !== void 0 ? _a : null;
|
|
34
|
-
});
|
|
35
|
-
}
|
|
36
|
-
/**
|
|
37
|
-
* Sets settings for the plugin.
|
|
38
|
-
* Automatically saves as guild settings if guild doesn't allow user settings,
|
|
39
|
-
* otherwise saves as user-specific settings.
|
|
40
|
-
* @param settings - The settings object to save.
|
|
41
|
-
* @throws {Error} if RLS blocks the operation.
|
|
42
|
-
*/
|
|
43
|
-
setSettings(settings) {
|
|
44
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
45
|
-
var _a;
|
|
46
|
-
const isGuildSetting = !this.guild.allowUserPluginSettings;
|
|
47
|
-
const payload = {
|
|
48
|
-
plugin_id: this.pluginId,
|
|
49
|
-
settings,
|
|
50
|
-
guild_id: this.guild.id,
|
|
51
|
-
is_guild_setting: isGuildSetting,
|
|
52
|
-
};
|
|
53
|
-
if (isGuildSetting) {
|
|
54
|
-
payload.user_id = null;
|
|
55
|
-
}
|
|
56
|
-
// Try UPDATE first (safe with RLS). If nothing updated, INSERT.
|
|
57
|
-
const updateQuery = this.supabase
|
|
58
|
-
.from('plugin_settings')
|
|
59
|
-
.update({ settings })
|
|
60
|
-
.eq('plugin_id', this.pluginId)
|
|
61
|
-
.eq('guild_id', this.guild.id)
|
|
62
|
-
.eq('is_guild_setting', isGuildSetting);
|
|
63
|
-
const { data: updatedRows, error: updateError } = yield (isGuildSetting
|
|
64
|
-
? updateQuery.is('user_id', null).select('id')
|
|
65
|
-
: updateQuery.select('id'));
|
|
66
|
-
if (updateError) {
|
|
67
|
-
if (updateError.code === '42501' || ((_a = updateError.message) === null || _a === void 0 ? void 0 : _a.includes('policy'))) {
|
|
68
|
-
throw new Error(`Cannot set ${isGuildSetting ? 'guild' : 'user'} settings: Permission denied.`);
|
|
69
|
-
}
|
|
70
|
-
// proceed to try insert in case of other issues
|
|
71
|
-
}
|
|
72
|
-
if (updatedRows && updatedRows.length > 0) {
|
|
73
|
-
return; // updated successfully
|
|
74
|
-
}
|
|
75
|
-
// No row updated -> INSERT
|
|
76
|
-
const { error: insertError } = yield this.supabase.from('plugin_settings').insert(payload);
|
|
77
|
-
if (insertError) {
|
|
78
|
-
// In case of race condition (duplicate), try one more UPDATE
|
|
79
|
-
if (insertError.code === '23505' /* unique_violation */) {
|
|
80
|
-
const retry = this.supabase
|
|
81
|
-
.from('plugin_settings')
|
|
82
|
-
.update({ settings })
|
|
83
|
-
.eq('plugin_id', this.pluginId)
|
|
84
|
-
.eq('guild_id', this.guild.id)
|
|
85
|
-
.eq('is_guild_setting', isGuildSetting);
|
|
86
|
-
const { error: retryError } = yield (isGuildSetting ? retry.is('user_id', null) : retry);
|
|
87
|
-
if (!retryError)
|
|
88
|
-
return;
|
|
89
|
-
}
|
|
90
|
-
throw insertError;
|
|
91
|
-
}
|
|
92
|
-
});
|
|
93
|
-
}
|
|
94
|
-
/**
|
|
95
|
-
* Get the settings for the plugin. T can be any type of settings, UserSettings or SystemSettings.
|
|
96
|
-
* @param defaultSettings The default settings to use if no settings are found.
|
|
97
|
-
* @returns The settings for the plugin.
|
|
98
|
-
*/
|
|
99
|
-
getSettings(defaultSettings) {
|
|
100
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
101
|
-
const storedSettings = (yield this.fetchSettings());
|
|
102
|
-
if (!storedSettings) {
|
|
103
|
-
yield this.setSettings(defaultSettings);
|
|
104
|
-
return defaultSettings;
|
|
105
|
-
}
|
|
106
|
-
// Handle settings migration
|
|
107
|
-
const storedKeys = Object.keys(storedSettings);
|
|
108
|
-
const defaultKeys = Object.keys(defaultSettings);
|
|
109
|
-
if (storedKeys.length !== defaultKeys.length) {
|
|
110
|
-
const validStoredSettings = Object.fromEntries(Object.entries(storedSettings).filter(([key]) => defaultKeys.includes(key)));
|
|
111
|
-
const mergedSettings = Object.assign(Object.assign({}, defaultSettings), validStoredSettings);
|
|
112
|
-
yield this.setSettings(mergedSettings);
|
|
113
|
-
return mergedSettings;
|
|
114
|
-
}
|
|
115
|
-
return storedSettings;
|
|
116
|
-
});
|
|
117
|
-
}
|
|
118
|
-
}
|
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
import { SupabaseClient } from '@supabase/supabase-js';
|
|
2
|
-
import { RimoriClient } from '../plugin/RimoriClient';
|
|
3
|
-
import { ObjectRequest } from './ObjectController';
|
|
4
|
-
export interface SharedContentObjectRequest extends ObjectRequest {
|
|
5
|
-
fixedProperties?: Record<string, string | number | boolean>;
|
|
6
|
-
}
|
|
7
|
-
export type SharedContentFilter = Record<string, string | number | boolean>;
|
|
8
|
-
export declare class SharedContentController {
|
|
9
|
-
private supabase;
|
|
10
|
-
private rimoriClient;
|
|
11
|
-
constructor(supabase: SupabaseClient, rimoriClient: RimoriClient);
|
|
12
|
-
/**
|
|
13
|
-
* Fetch new shared content for a given content type.
|
|
14
|
-
* @param contentType - The type of content to fetch.
|
|
15
|
-
* @param generatorInstructions - The instructions for the generator. The object needs to have a tool property with a topic and keywords property to let a new unique topic be generated.
|
|
16
|
-
* @param filter - An optional filter to apply to the query.
|
|
17
|
-
* @param options - Optional options.
|
|
18
|
-
* @param options.privateTopic - If the topic should be private and only be visible to the user.
|
|
19
|
-
* @param options.skipDbSave - If true, do not persist a newly generated content to the DB (default false).
|
|
20
|
-
* @param options.alwaysGenerateNew - If true, always generate a new content even if there is already a content with the same filter.
|
|
21
|
-
* @param options.excludeIds - Optional list of shared_content ids to exclude from selection.
|
|
22
|
-
* @returns The new shared content.
|
|
23
|
-
*/
|
|
24
|
-
getNewSharedContent<T>(contentType: string, generatorInstructions: SharedContentObjectRequest, filter?: SharedContentFilter, options?: {
|
|
25
|
-
privateTopic?: boolean;
|
|
26
|
-
skipDbSave?: boolean;
|
|
27
|
-
alwaysGenerateNew?: boolean;
|
|
28
|
-
excludeIds?: string[];
|
|
29
|
-
}): Promise<SharedContent<T>>;
|
|
30
|
-
private generateNewAssignment;
|
|
31
|
-
private getGeneratorInstructions;
|
|
32
|
-
private getCompletedTopics;
|
|
33
|
-
getSharedContent<T>(contentType: string, id: string): Promise<SharedContent<T>>;
|
|
34
|
-
completeSharedContent(contentType: string, assignmentId: string): Promise<void>;
|
|
35
|
-
/**
|
|
36
|
-
* Update state details for a shared content entry in shared_content_completed.
|
|
37
|
-
* Assumes table has columns: state ('completed'|'ongoing'|'hidden'), reaction ('liked'|'disliked'|null), bookmarked boolean.
|
|
38
|
-
* Upserts per (id, content_type, user).
|
|
39
|
-
* @param param
|
|
40
|
-
* @param param.contentType - The content type.
|
|
41
|
-
* @param param.id - The shared content id.
|
|
42
|
-
* @param param.state - The state to set.
|
|
43
|
-
* @param param.reaction - Optional reaction.
|
|
44
|
-
* @param param.bookmarked - Optional bookmark flag.
|
|
45
|
-
*/
|
|
46
|
-
updateSharedContentState({ contentType, id, state, reaction, bookmarked, }: {
|
|
47
|
-
contentType: string;
|
|
48
|
-
id: string;
|
|
49
|
-
state?: 'completed' | 'ongoing' | 'hidden';
|
|
50
|
-
reaction?: 'liked' | 'disliked' | null;
|
|
51
|
-
bookmarked?: boolean;
|
|
52
|
-
}): Promise<void>;
|
|
53
|
-
/**
|
|
54
|
-
* Fetch shared content from the database based on optional filters.
|
|
55
|
-
* @param contentType - The type of content to fetch.
|
|
56
|
-
* @param filter - Optional filter to apply to the query.
|
|
57
|
-
* @param limit - Optional limit for the number of results.
|
|
58
|
-
* @returns Array of shared content matching the criteria.
|
|
59
|
-
*/
|
|
60
|
-
getSharedContentList<T>(contentType: string, filter?: SharedContentFilter, limit?: number): Promise<SharedContent<T>[]>;
|
|
61
|
-
/**
|
|
62
|
-
* Insert new shared content into the database.
|
|
63
|
-
* @param param
|
|
64
|
-
* @param param.contentType - The type of content to insert.
|
|
65
|
-
* @param param.title - The title of the content.
|
|
66
|
-
* @param param.keywords - Keywords associated with the content.
|
|
67
|
-
* @param param.data - The content data to store.
|
|
68
|
-
* @param param.privateTopic - Optional flag to indicate if the topic should be private.
|
|
69
|
-
* @returns The inserted shared content.
|
|
70
|
-
* @throws {Error} if insertion fails.
|
|
71
|
-
*/
|
|
72
|
-
createSharedContent<T>({ contentType, title, keywords, data, privateTopic, }: Omit<SharedContent<T>, 'id'>): Promise<SharedContent<T>>;
|
|
73
|
-
/**
|
|
74
|
-
* Update existing shared content in the database.
|
|
75
|
-
* @param id - The ID of the content to update.
|
|
76
|
-
* @param updates - The updates to apply to the shared content.
|
|
77
|
-
* @returns The updated shared content.
|
|
78
|
-
* @throws {Error} if update fails.
|
|
79
|
-
*/
|
|
80
|
-
updateSharedContent<T>(id: string, updates: Partial<SharedContent<T>>): Promise<SharedContent<T>>;
|
|
81
|
-
/**
|
|
82
|
-
* Soft delete shared content by setting the deleted_at timestamp.
|
|
83
|
-
* @param id - The ID of the content to delete.
|
|
84
|
-
* @returns The deleted shared content record.
|
|
85
|
-
* @throws {Error} if deletion fails or content not found.
|
|
86
|
-
*/
|
|
87
|
-
removeSharedContent(id: string): Promise<SharedContent<any>>;
|
|
88
|
-
}
|
|
89
|
-
/**
|
|
90
|
-
* Interface representing shared content in the system.
|
|
91
|
-
* @template T The type of data stored in the content
|
|
92
|
-
*/
|
|
93
|
-
export interface SharedContent<T> {
|
|
94
|
-
/** The id of the content */
|
|
95
|
-
id: string;
|
|
96
|
-
/** The type/category of the content (e.g. 'grammar_exercises', 'flashcards', etc.) */
|
|
97
|
-
contentType: string;
|
|
98
|
-
/** The human readable title of the content */
|
|
99
|
-
title: string;
|
|
100
|
-
/** Array of keywords/tags associated with the content for search and categorization */
|
|
101
|
-
keywords: string[];
|
|
102
|
-
/** The actual content data of type T */
|
|
103
|
-
data: T;
|
|
104
|
-
/** Whether this content should only be visible to the creator. Defaults to false if not specified */
|
|
105
|
-
privateTopic?: boolean;
|
|
106
|
-
}
|
|
@@ -1,285 +0,0 @@
|
|
|
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 class SharedContentController {
|
|
11
|
-
constructor(supabase, rimoriClient) {
|
|
12
|
-
this.supabase = supabase;
|
|
13
|
-
this.rimoriClient = rimoriClient;
|
|
14
|
-
}
|
|
15
|
-
/**
|
|
16
|
-
* Fetch new shared content for a given content type.
|
|
17
|
-
* @param contentType - The type of content to fetch.
|
|
18
|
-
* @param generatorInstructions - The instructions for the generator. The object needs to have a tool property with a topic and keywords property to let a new unique topic be generated.
|
|
19
|
-
* @param filter - An optional filter to apply to the query.
|
|
20
|
-
* @param options - Optional options.
|
|
21
|
-
* @param options.privateTopic - If the topic should be private and only be visible to the user.
|
|
22
|
-
* @param options.skipDbSave - If true, do not persist a newly generated content to the DB (default false).
|
|
23
|
-
* @param options.alwaysGenerateNew - If true, always generate a new content even if there is already a content with the same filter.
|
|
24
|
-
* @param options.excludeIds - Optional list of shared_content ids to exclude from selection.
|
|
25
|
-
* @returns The new shared content.
|
|
26
|
-
*/
|
|
27
|
-
getNewSharedContent(contentType, generatorInstructions,
|
|
28
|
-
//this filter is there if the content should be filtered additionally by a column and value
|
|
29
|
-
filter, options) {
|
|
30
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
31
|
-
let query = this.supabase
|
|
32
|
-
.from('shared_content')
|
|
33
|
-
.select('*, scc:shared_content_completed(id, state)')
|
|
34
|
-
.eq('content_type', contentType)
|
|
35
|
-
.not('scc.state', 'in', '("completed","ongoing","hidden")')
|
|
36
|
-
.is('deleted_at', null);
|
|
37
|
-
if ((options === null || options === void 0 ? void 0 : options.excludeIds) && options.excludeIds.length > 0) {
|
|
38
|
-
const excludeIds = options.excludeIds.filter((id) => !id.startsWith('internal-temp-id-'));
|
|
39
|
-
// Supabase expects raw PostgREST syntax like '("id1","id2")'.
|
|
40
|
-
const excludeList = `(${excludeIds.map((id) => `"${id}"`).join(',')})`;
|
|
41
|
-
query = query.not('id', 'in', excludeList);
|
|
42
|
-
}
|
|
43
|
-
if (filter) {
|
|
44
|
-
query.contains('data', filter);
|
|
45
|
-
}
|
|
46
|
-
const { data: newAssignments, error } = yield query.limit(30);
|
|
47
|
-
if (error) {
|
|
48
|
-
console.error('error fetching new assignments:', error);
|
|
49
|
-
throw new Error('error fetching new assignments');
|
|
50
|
-
}
|
|
51
|
-
// console.log('newAssignments:', newAssignments);
|
|
52
|
-
if (!(options === null || options === void 0 ? void 0 : options.alwaysGenerateNew) && newAssignments.length > 0) {
|
|
53
|
-
const index = Math.floor(Math.random() * newAssignments.length);
|
|
54
|
-
return newAssignments[index];
|
|
55
|
-
}
|
|
56
|
-
const instructions = yield this.generateNewAssignment(contentType, generatorInstructions, filter);
|
|
57
|
-
console.log('instructions:', instructions);
|
|
58
|
-
//create the shared content object
|
|
59
|
-
const data = {
|
|
60
|
-
id: 'internal-temp-id-' + Math.random().toString(36).substring(2, 15),
|
|
61
|
-
contentType,
|
|
62
|
-
title: instructions.title,
|
|
63
|
-
keywords: instructions.keywords.map(({ text }) => text),
|
|
64
|
-
data: Object.assign(Object.assign(Object.assign({}, instructions), { title: undefined, keywords: undefined }), generatorInstructions.fixedProperties),
|
|
65
|
-
privateTopic: options === null || options === void 0 ? void 0 : options.privateTopic,
|
|
66
|
-
};
|
|
67
|
-
if (options === null || options === void 0 ? void 0 : options.skipDbSave) {
|
|
68
|
-
return data;
|
|
69
|
-
}
|
|
70
|
-
return yield this.createSharedContent(data);
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
generateNewAssignment(contentType, generatorInstructions, filter) {
|
|
74
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
75
|
-
const fullInstructions = yield this.getGeneratorInstructions(contentType, generatorInstructions, filter);
|
|
76
|
-
console.log('fullInstructions:', fullInstructions);
|
|
77
|
-
return yield this.rimoriClient.ai.getObject(fullInstructions);
|
|
78
|
-
});
|
|
79
|
-
}
|
|
80
|
-
getGeneratorInstructions(contentType, generatorInstructions, filter) {
|
|
81
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
82
|
-
const completedTopics = yield this.getCompletedTopics(contentType, filter);
|
|
83
|
-
generatorInstructions.instructions += `
|
|
84
|
-
The following topics are already taken: ${completedTopics.join(', ')}`;
|
|
85
|
-
generatorInstructions.tool.title = {
|
|
86
|
-
type: 'string',
|
|
87
|
-
description: 'What the topic is about. Short. ',
|
|
88
|
-
};
|
|
89
|
-
generatorInstructions.tool.keywords = {
|
|
90
|
-
type: [{ text: { type: 'string' } }],
|
|
91
|
-
description: 'Keywords around the topic of the assignment.',
|
|
92
|
-
};
|
|
93
|
-
return generatorInstructions;
|
|
94
|
-
});
|
|
95
|
-
}
|
|
96
|
-
getCompletedTopics(contentType, filter) {
|
|
97
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
98
|
-
const query = this.supabase
|
|
99
|
-
.from('shared_content')
|
|
100
|
-
.select('title, keywords, scc:shared_content_completed(id)')
|
|
101
|
-
.eq('content_type', contentType)
|
|
102
|
-
.not('scc.id', 'is', null)
|
|
103
|
-
.is('deleted_at', null);
|
|
104
|
-
if (filter) {
|
|
105
|
-
query.contains('data', filter);
|
|
106
|
-
}
|
|
107
|
-
const { data: oldAssignments, error } = yield query;
|
|
108
|
-
if (error) {
|
|
109
|
-
console.error('error fetching old assignments:', error);
|
|
110
|
-
return [];
|
|
111
|
-
}
|
|
112
|
-
return oldAssignments.map(({ title, keywords }) => `${title}(${keywords.join(',')})`);
|
|
113
|
-
});
|
|
114
|
-
}
|
|
115
|
-
getSharedContent(contentType, id) {
|
|
116
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
117
|
-
const { data, error } = yield this.supabase
|
|
118
|
-
.from('shared_content')
|
|
119
|
-
.select()
|
|
120
|
-
.eq('content_type', contentType)
|
|
121
|
-
.eq('id', id)
|
|
122
|
-
.is('deleted_at', null)
|
|
123
|
-
.single();
|
|
124
|
-
if (error) {
|
|
125
|
-
console.error('error fetching shared content:', error);
|
|
126
|
-
throw new Error('error fetching shared content');
|
|
127
|
-
}
|
|
128
|
-
return data;
|
|
129
|
-
});
|
|
130
|
-
}
|
|
131
|
-
completeSharedContent(contentType, assignmentId) {
|
|
132
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
133
|
-
// Idempotent completion: upsert on (id, user_id) so repeated calls don't fail
|
|
134
|
-
const { error } = yield this.supabase
|
|
135
|
-
.from('shared_content_completed')
|
|
136
|
-
.upsert({ content_type: contentType, id: assignmentId }, { onConflict: 'id' });
|
|
137
|
-
if (error) {
|
|
138
|
-
console.error('error completing shared content:', error);
|
|
139
|
-
throw new Error('error completing shared content');
|
|
140
|
-
}
|
|
141
|
-
});
|
|
142
|
-
}
|
|
143
|
-
/**
|
|
144
|
-
* Update state details for a shared content entry in shared_content_completed.
|
|
145
|
-
* Assumes table has columns: state ('completed'|'ongoing'|'hidden'), reaction ('liked'|'disliked'|null), bookmarked boolean.
|
|
146
|
-
* Upserts per (id, content_type, user).
|
|
147
|
-
* @param param
|
|
148
|
-
* @param param.contentType - The content type.
|
|
149
|
-
* @param param.id - The shared content id.
|
|
150
|
-
* @param param.state - The state to set.
|
|
151
|
-
* @param param.reaction - Optional reaction.
|
|
152
|
-
* @param param.bookmarked - Optional bookmark flag.
|
|
153
|
-
*/
|
|
154
|
-
updateSharedContentState(_a) {
|
|
155
|
-
return __awaiter(this, arguments, void 0, function* ({ contentType, id, state, reaction, bookmarked, }) {
|
|
156
|
-
const payload = { content_type: contentType, id };
|
|
157
|
-
if (state !== undefined)
|
|
158
|
-
payload.state = state;
|
|
159
|
-
if (reaction !== undefined)
|
|
160
|
-
payload.reaction = reaction;
|
|
161
|
-
if (bookmarked !== undefined)
|
|
162
|
-
payload.bookmarked = bookmarked;
|
|
163
|
-
// Prefer upsert, fall back to insert/update if upsert not allowed
|
|
164
|
-
const { error } = yield this.supabase.from('shared_content_completed').upsert(payload, { onConflict: 'id' });
|
|
165
|
-
if (error) {
|
|
166
|
-
console.error('error updating shared content state:', error);
|
|
167
|
-
throw new Error('error updating shared content state');
|
|
168
|
-
}
|
|
169
|
-
});
|
|
170
|
-
}
|
|
171
|
-
/**
|
|
172
|
-
* Fetch shared content from the database based on optional filters.
|
|
173
|
-
* @param contentType - The type of content to fetch.
|
|
174
|
-
* @param filter - Optional filter to apply to the query.
|
|
175
|
-
* @param limit - Optional limit for the number of results.
|
|
176
|
-
* @returns Array of shared content matching the criteria.
|
|
177
|
-
*/
|
|
178
|
-
getSharedContentList(contentType, filter, limit) {
|
|
179
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
180
|
-
const query = this.supabase
|
|
181
|
-
.from('shared_content')
|
|
182
|
-
.select('*')
|
|
183
|
-
.eq('content_type', contentType)
|
|
184
|
-
.is('deleted_at', null)
|
|
185
|
-
.limit(limit !== null && limit !== void 0 ? limit : 30);
|
|
186
|
-
if (filter) {
|
|
187
|
-
query.contains('data', filter);
|
|
188
|
-
}
|
|
189
|
-
const { data, error } = yield query;
|
|
190
|
-
if (error) {
|
|
191
|
-
console.error('error fetching shared content:', error);
|
|
192
|
-
throw new Error('error fetching shared content');
|
|
193
|
-
}
|
|
194
|
-
return data;
|
|
195
|
-
});
|
|
196
|
-
}
|
|
197
|
-
/**
|
|
198
|
-
* Insert new shared content into the database.
|
|
199
|
-
* @param param
|
|
200
|
-
* @param param.contentType - The type of content to insert.
|
|
201
|
-
* @param param.title - The title of the content.
|
|
202
|
-
* @param param.keywords - Keywords associated with the content.
|
|
203
|
-
* @param param.data - The content data to store.
|
|
204
|
-
* @param param.privateTopic - Optional flag to indicate if the topic should be private.
|
|
205
|
-
* @returns The inserted shared content.
|
|
206
|
-
* @throws {Error} if insertion fails.
|
|
207
|
-
*/
|
|
208
|
-
createSharedContent(_a) {
|
|
209
|
-
return __awaiter(this, arguments, void 0, function* ({ contentType, title, keywords, data, privateTopic, }) {
|
|
210
|
-
const { data: newContent, error } = yield this.supabase
|
|
211
|
-
.from('shared_content')
|
|
212
|
-
.insert({
|
|
213
|
-
private: privateTopic,
|
|
214
|
-
content_type: contentType,
|
|
215
|
-
title,
|
|
216
|
-
keywords,
|
|
217
|
-
data,
|
|
218
|
-
})
|
|
219
|
-
.select();
|
|
220
|
-
if (error) {
|
|
221
|
-
console.error('error inserting shared content:', error);
|
|
222
|
-
throw new Error('error inserting shared content');
|
|
223
|
-
}
|
|
224
|
-
return newContent[0];
|
|
225
|
-
});
|
|
226
|
-
}
|
|
227
|
-
/**
|
|
228
|
-
* Update existing shared content in the database.
|
|
229
|
-
* @param id - The ID of the content to update.
|
|
230
|
-
* @param updates - The updates to apply to the shared content.
|
|
231
|
-
* @returns The updated shared content.
|
|
232
|
-
* @throws {Error} if update fails.
|
|
233
|
-
*/
|
|
234
|
-
updateSharedContent(id, updates) {
|
|
235
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
236
|
-
const updateData = {};
|
|
237
|
-
if (updates.contentType)
|
|
238
|
-
updateData.content_type = updates.contentType;
|
|
239
|
-
if (updates.title)
|
|
240
|
-
updateData.title = updates.title;
|
|
241
|
-
if (updates.keywords)
|
|
242
|
-
updateData.keywords = updates.keywords;
|
|
243
|
-
if (updates.data)
|
|
244
|
-
updateData.data = updates.data;
|
|
245
|
-
if (updates.privateTopic !== undefined)
|
|
246
|
-
updateData.private = updates.privateTopic;
|
|
247
|
-
const { data: updatedContent, error } = yield this.supabase
|
|
248
|
-
.from('shared_content')
|
|
249
|
-
.update(updateData)
|
|
250
|
-
.eq('id', id)
|
|
251
|
-
.select();
|
|
252
|
-
if (error) {
|
|
253
|
-
console.error('error updating shared content:', error);
|
|
254
|
-
throw new Error('error updating shared content');
|
|
255
|
-
}
|
|
256
|
-
if (!updatedContent || updatedContent.length === 0) {
|
|
257
|
-
throw new Error('shared content not found');
|
|
258
|
-
}
|
|
259
|
-
return updatedContent[0];
|
|
260
|
-
});
|
|
261
|
-
}
|
|
262
|
-
/**
|
|
263
|
-
* Soft delete shared content by setting the deleted_at timestamp.
|
|
264
|
-
* @param id - The ID of the content to delete.
|
|
265
|
-
* @returns The deleted shared content record.
|
|
266
|
-
* @throws {Error} if deletion fails or content not found.
|
|
267
|
-
*/
|
|
268
|
-
removeSharedContent(id) {
|
|
269
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
270
|
-
const { data: deletedContent, error } = yield this.supabase
|
|
271
|
-
.from('shared_content')
|
|
272
|
-
.update({ deleted_at: new Date().toISOString() })
|
|
273
|
-
.eq('id', id)
|
|
274
|
-
.select();
|
|
275
|
-
if (error) {
|
|
276
|
-
console.error('error deleting shared content:', error);
|
|
277
|
-
throw new Error('error deleting shared content');
|
|
278
|
-
}
|
|
279
|
-
if (!deletedContent || deletedContent.length === 0) {
|
|
280
|
-
throw new Error('shared content not found or already deleted');
|
|
281
|
-
}
|
|
282
|
-
return deletedContent[0];
|
|
283
|
-
});
|
|
284
|
-
}
|
|
285
|
-
}
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import { ThirdPartyModule, TOptions } from 'i18next';
|
|
2
|
-
/**
|
|
3
|
-
* Translator class for handling internationalization
|
|
4
|
-
*/
|
|
5
|
-
export declare class Translator {
|
|
6
|
-
private currentLanguage;
|
|
7
|
-
private isInitialized;
|
|
8
|
-
private i18n;
|
|
9
|
-
constructor(initialLanguage: string);
|
|
10
|
-
/**
|
|
11
|
-
* Initialize translator with user's language
|
|
12
|
-
* @param userLanguage - Language code from user info
|
|
13
|
-
*/
|
|
14
|
-
initialize(): Promise<void>;
|
|
15
|
-
private getTranslationUrl;
|
|
16
|
-
usePlugin(plugin: ThirdPartyModule): void;
|
|
17
|
-
/**
|
|
18
|
-
* Fetch translations manually from the current domain
|
|
19
|
-
* @param language - Language code to fetch
|
|
20
|
-
* @returns Promise with translation data
|
|
21
|
-
*/
|
|
22
|
-
private fetchTranslations;
|
|
23
|
-
/**
|
|
24
|
-
* Get translation for a key
|
|
25
|
-
* @param key - Translation key
|
|
26
|
-
* @param options - Translation options
|
|
27
|
-
* @returns Translated string
|
|
28
|
-
*/
|
|
29
|
-
t(key: string, options?: TOptions): string;
|
|
30
|
-
/**
|
|
31
|
-
* Get current language
|
|
32
|
-
*/
|
|
33
|
-
getCurrentLanguage(): string;
|
|
34
|
-
/**
|
|
35
|
-
* Check if translator is initialized
|
|
36
|
-
*/
|
|
37
|
-
isReady(): boolean;
|
|
38
|
-
}
|
|
@@ -1,106 +0,0 @@
|
|
|
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
|
-
import { createInstance } from 'i18next';
|
|
11
|
-
/**
|
|
12
|
-
* Translator class for handling internationalization
|
|
13
|
-
*/
|
|
14
|
-
export class Translator {
|
|
15
|
-
constructor(initialLanguage) {
|
|
16
|
-
this.isInitialized = false;
|
|
17
|
-
this.currentLanguage = initialLanguage;
|
|
18
|
-
}
|
|
19
|
-
/**
|
|
20
|
-
* Initialize translator with user's language
|
|
21
|
-
* @param userLanguage - Language code from user info
|
|
22
|
-
*/
|
|
23
|
-
initialize() {
|
|
24
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
25
|
-
if (this.isInitialized)
|
|
26
|
-
return;
|
|
27
|
-
const translations = yield this.fetchTranslations(this.currentLanguage);
|
|
28
|
-
const instance = createInstance({
|
|
29
|
-
lng: this.currentLanguage,
|
|
30
|
-
resources: {
|
|
31
|
-
[this.currentLanguage]: {
|
|
32
|
-
translation: translations,
|
|
33
|
-
},
|
|
34
|
-
},
|
|
35
|
-
debug: window.location.hostname === 'localhost',
|
|
36
|
-
});
|
|
37
|
-
yield instance.init();
|
|
38
|
-
this.i18n = instance;
|
|
39
|
-
this.isInitialized = true;
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
|
-
getTranslationUrl(language) {
|
|
43
|
-
// For localhost development, use local- prefix for non-English languages
|
|
44
|
-
if (window.location.hostname === 'localhost') {
|
|
45
|
-
const filename = language !== 'en' ? `local-${language}` : language;
|
|
46
|
-
return `${window.location.origin}/locales/${filename}.json`;
|
|
47
|
-
}
|
|
48
|
-
return `./locales/${language}.json`;
|
|
49
|
-
}
|
|
50
|
-
usePlugin(plugin) {
|
|
51
|
-
if (!this.i18n) {
|
|
52
|
-
throw new Error('Translator is not initialized');
|
|
53
|
-
}
|
|
54
|
-
this.i18n.use(plugin);
|
|
55
|
-
}
|
|
56
|
-
/**
|
|
57
|
-
* Fetch translations manually from the current domain
|
|
58
|
-
* @param language - Language code to fetch
|
|
59
|
-
* @returns Promise with translation data
|
|
60
|
-
*/
|
|
61
|
-
fetchTranslations(language) {
|
|
62
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
63
|
-
try {
|
|
64
|
-
const response = yield fetch(this.getTranslationUrl(language));
|
|
65
|
-
if (!response.ok) {
|
|
66
|
-
throw new Error(`Failed to fetch translations for ${language}`);
|
|
67
|
-
}
|
|
68
|
-
return (yield response.json());
|
|
69
|
-
}
|
|
70
|
-
catch (error) {
|
|
71
|
-
console.warn(`Failed to fetch translations for ${language}:`, error);
|
|
72
|
-
if (language === 'en')
|
|
73
|
-
return {};
|
|
74
|
-
// Fallback to English
|
|
75
|
-
return this.fetchTranslations('en').catch((error) => {
|
|
76
|
-
console.error('Failed to fetch fallback translations:', error);
|
|
77
|
-
return {};
|
|
78
|
-
});
|
|
79
|
-
}
|
|
80
|
-
});
|
|
81
|
-
}
|
|
82
|
-
/**
|
|
83
|
-
* Get translation for a key
|
|
84
|
-
* @param key - Translation key
|
|
85
|
-
* @param options - Translation options
|
|
86
|
-
* @returns Translated string
|
|
87
|
-
*/
|
|
88
|
-
t(key, options) {
|
|
89
|
-
if (!this.i18n) {
|
|
90
|
-
throw new Error('Translator is not initialized');
|
|
91
|
-
}
|
|
92
|
-
return this.i18n.t(key, options);
|
|
93
|
-
}
|
|
94
|
-
/**
|
|
95
|
-
* Get current language
|
|
96
|
-
*/
|
|
97
|
-
getCurrentLanguage() {
|
|
98
|
-
return this.currentLanguage;
|
|
99
|
-
}
|
|
100
|
-
/**
|
|
101
|
-
* Check if translator is initialized
|
|
102
|
-
*/
|
|
103
|
-
isReady() {
|
|
104
|
-
return this.isInitialized;
|
|
105
|
-
}
|
|
106
|
-
}
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
export declare function getSTTResponse(backendUrl: string, audio: Blob, token: string): Promise<string>;
|
|
2
|
-
export declare function getTTSResponse(backendUrl: string, request: TTSRequest, token: string): Promise<Blob>;
|
|
3
|
-
interface TTSRequest {
|
|
4
|
-
input: string;
|
|
5
|
-
voice: string;
|
|
6
|
-
speed: number;
|
|
7
|
-
language?: string;
|
|
8
|
-
}
|
|
9
|
-
export {};
|