@rimori/react-client 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. package/.prettierignore +35 -0
  2. package/LICENSE +201 -0
  3. package/README copy.md +1216 -0
  4. package/README.md +1 -0
  5. package/dist/components/MarkdownEditor.d.ts +8 -0
  6. package/dist/components/MarkdownEditor.js +48 -0
  7. package/dist/components/Spinner.d.ts +8 -0
  8. package/dist/components/Spinner.js +4 -0
  9. package/dist/components/ai/Assistant.d.ts +9 -0
  10. package/dist/components/ai/Assistant.js +58 -0
  11. package/dist/components/ai/Avatar.d.ts +14 -0
  12. package/dist/components/ai/Avatar.js +59 -0
  13. package/dist/components/ai/EmbeddedAssistent/AudioInputField.d.ts +7 -0
  14. package/dist/components/ai/EmbeddedAssistent/AudioInputField.js +37 -0
  15. package/dist/components/ai/EmbeddedAssistent/CircleAudioAvatar.d.ts +8 -0
  16. package/dist/components/ai/EmbeddedAssistent/CircleAudioAvatar.js +79 -0
  17. package/dist/components/ai/EmbeddedAssistent/TTS/MessageSender.d.ts +19 -0
  18. package/dist/components/ai/EmbeddedAssistent/TTS/MessageSender.js +91 -0
  19. package/dist/components/ai/EmbeddedAssistent/TTS/Player.d.ts +27 -0
  20. package/dist/components/ai/EmbeddedAssistent/TTS/Player.js +185 -0
  21. package/dist/components/ai/EmbeddedAssistent/VoiceRecoder.d.ts +11 -0
  22. package/dist/components/ai/EmbeddedAssistent/VoiceRecoder.js +95 -0
  23. package/dist/components/ai/utils.d.ts +6 -0
  24. package/dist/components/ai/utils.js +13 -0
  25. package/dist/components/audio/Playbutton.d.ts +15 -0
  26. package/dist/components/audio/Playbutton.js +80 -0
  27. package/dist/components/components/ContextMenu.d.ts +10 -0
  28. package/dist/components/components/ContextMenu.js +135 -0
  29. package/dist/hooks/I18nHooks.d.ts +11 -0
  30. package/dist/hooks/I18nHooks.js +25 -0
  31. package/dist/hooks/UseChatHook.d.ts +10 -0
  32. package/dist/hooks/UseChatHook.js +29 -0
  33. package/dist/providers/PluginProvider.d.ts +11 -0
  34. package/dist/providers/PluginProvider.js +142 -0
  35. package/dist/react-client/plugin/ThemeSetter.d.ts +2 -0
  36. package/dist/react-client/plugin/ThemeSetter.js +19 -0
  37. package/dist/react-client/src/components/ContextMenu.d.ts +10 -0
  38. package/dist/react-client/src/components/ContextMenu.js +135 -0
  39. package/dist/react-client/src/components/MarkdownEditor.d.ts +8 -0
  40. package/dist/react-client/src/components/MarkdownEditor.js +48 -0
  41. package/dist/react-client/src/components/Spinner.d.ts +8 -0
  42. package/dist/react-client/src/components/Spinner.js +4 -0
  43. package/dist/react-client/src/components/ai/Assistant.d.ts +9 -0
  44. package/dist/react-client/src/components/ai/Assistant.js +58 -0
  45. package/dist/react-client/src/components/ai/Avatar.d.ts +14 -0
  46. package/dist/react-client/src/components/ai/Avatar.js +59 -0
  47. package/dist/react-client/src/components/ai/EmbeddedAssistent/AudioInputField.d.ts +7 -0
  48. package/dist/react-client/src/components/ai/EmbeddedAssistent/AudioInputField.js +37 -0
  49. package/dist/react-client/src/components/ai/EmbeddedAssistent/CircleAudioAvatar.d.ts +8 -0
  50. package/dist/react-client/src/components/ai/EmbeddedAssistent/CircleAudioAvatar.js +79 -0
  51. package/dist/react-client/src/components/ai/EmbeddedAssistent/TTS/MessageSender.d.ts +19 -0
  52. package/dist/react-client/src/components/ai/EmbeddedAssistent/TTS/MessageSender.js +91 -0
  53. package/dist/react-client/src/components/ai/EmbeddedAssistent/TTS/Player.d.ts +27 -0
  54. package/dist/react-client/src/components/ai/EmbeddedAssistent/TTS/Player.js +185 -0
  55. package/dist/react-client/src/components/ai/EmbeddedAssistent/VoiceRecoder.d.ts +11 -0
  56. package/dist/react-client/src/components/ai/EmbeddedAssistent/VoiceRecoder.js +95 -0
  57. package/dist/react-client/src/components/ai/utils.d.ts +6 -0
  58. package/dist/react-client/src/components/ai/utils.js +13 -0
  59. package/dist/react-client/src/components/audio/Playbutton.d.ts +15 -0
  60. package/dist/react-client/src/components/audio/Playbutton.js +82 -0
  61. package/dist/react-client/src/components/components/ContextMenu.d.ts +10 -0
  62. package/dist/react-client/src/components/components/ContextMenu.js +135 -0
  63. package/dist/react-client/src/hooks/I18nHooks.d.ts +11 -0
  64. package/dist/react-client/src/hooks/I18nHooks.js +25 -0
  65. package/dist/react-client/src/hooks/UseChatHook.d.ts +10 -0
  66. package/dist/react-client/src/hooks/UseChatHook.js +29 -0
  67. package/dist/react-client/src/plugin/ThemeSetter.d.ts +2 -0
  68. package/dist/react-client/src/plugin/ThemeSetter.js +19 -0
  69. package/dist/react-client/src/providers/PluginProvider.d.ts +12 -0
  70. package/dist/react-client/src/providers/PluginProvider.js +142 -0
  71. package/dist/react-client/src/utils/FullscreenUtils.d.ts +2 -0
  72. package/dist/react-client/src/utils/FullscreenUtils.js +23 -0
  73. package/dist/react-client/src/utils/PluginUtils.d.ts +2 -0
  74. package/dist/react-client/src/utils/PluginUtils.js +23 -0
  75. package/dist/rimori-client/src/cli/types/DatabaseTypes.d.ts +103 -0
  76. package/dist/rimori-client/src/cli/types/DatabaseTypes.js +2 -0
  77. package/dist/rimori-client/src/controller/AIController.d.ts +15 -0
  78. package/dist/rimori-client/src/controller/AIController.js +255 -0
  79. package/dist/rimori-client/src/controller/AccomplishmentController.d.ts +38 -0
  80. package/dist/rimori-client/src/controller/AccomplishmentController.js +112 -0
  81. package/dist/rimori-client/src/controller/AudioController.d.ts +37 -0
  82. package/dist/rimori-client/src/controller/AudioController.js +68 -0
  83. package/dist/rimori-client/src/controller/ExerciseController.d.ts +54 -0
  84. package/dist/rimori-client/src/controller/ExerciseController.js +74 -0
  85. package/dist/rimori-client/src/controller/ObjectController.d.ts +42 -0
  86. package/dist/rimori-client/src/controller/ObjectController.js +76 -0
  87. package/dist/rimori-client/src/controller/SettingsController.d.ts +79 -0
  88. package/dist/rimori-client/src/controller/SettingsController.js +118 -0
  89. package/dist/rimori-client/src/controller/SharedContentController.d.ts +106 -0
  90. package/dist/rimori-client/src/controller/SharedContentController.js +285 -0
  91. package/dist/rimori-client/src/controller/TranslationController.d.ts +38 -0
  92. package/dist/rimori-client/src/controller/TranslationController.js +106 -0
  93. package/dist/rimori-client/src/controller/VoiceController.d.ts +9 -0
  94. package/dist/rimori-client/src/controller/VoiceController.js +37 -0
  95. package/dist/rimori-client/src/fromRimori/EventBus.d.ts +101 -0
  96. package/dist/rimori-client/src/fromRimori/EventBus.js +263 -0
  97. package/dist/rimori-client/src/fromRimori/PluginTypes.d.ts +174 -0
  98. package/dist/rimori-client/src/fromRimori/PluginTypes.js +1 -0
  99. package/dist/rimori-client/src/index.d.ts +11 -0
  100. package/dist/rimori-client/src/index.js +10 -0
  101. package/dist/rimori-client/src/plugin/CommunicationHandler.d.ts +48 -0
  102. package/dist/rimori-client/src/plugin/CommunicationHandler.js +234 -0
  103. package/dist/rimori-client/src/plugin/Logger.d.ts +73 -0
  104. package/dist/rimori-client/src/plugin/Logger.js +308 -0
  105. package/dist/rimori-client/src/plugin/RimoriClient.d.ts +258 -0
  106. package/dist/rimori-client/src/plugin/RimoriClient.js +375 -0
  107. package/dist/rimori-client/src/plugin/StandaloneClient.d.ts +17 -0
  108. package/dist/rimori-client/src/plugin/StandaloneClient.js +115 -0
  109. package/dist/rimori-client/src/utils/difficultyConverter.d.ts +4 -0
  110. package/dist/rimori-client/src/utils/difficultyConverter.js +10 -0
  111. package/dist/rimori-client/src/utils/endpoint.d.ts +2 -0
  112. package/dist/rimori-client/src/utils/endpoint.js +2 -0
  113. package/dist/style.css +110 -0
  114. package/dist/style.css.map +1 -0
  115. package/dist/utils/PluginUtils.d.ts +2 -0
  116. package/dist/utils/PluginUtils.js +23 -0
  117. package/eslint.config.js +53 -0
  118. package/index.ts +6 -0
  119. package/package.json +47 -0
  120. package/prettier.config.js +8 -0
  121. package/src/components/ContextMenu.tsx +177 -0
  122. package/src/components/MarkdownEditor.tsx +144 -0
  123. package/src/components/Spinner.tsx +29 -0
  124. package/src/components/ai/Assistant.tsx +96 -0
  125. package/src/components/ai/Avatar.tsx +99 -0
  126. package/src/components/ai/EmbeddedAssistent/AudioInputField.tsx +73 -0
  127. package/src/components/ai/EmbeddedAssistent/CircleAudioAvatar.tsx +107 -0
  128. package/src/components/ai/EmbeddedAssistent/TTS/MessageSender.ts +96 -0
  129. package/src/components/ai/EmbeddedAssistent/TTS/Player.ts +197 -0
  130. package/src/components/ai/EmbeddedAssistent/VoiceRecoder.tsx +129 -0
  131. package/src/components/ai/utils.ts +21 -0
  132. package/src/components/audio/Playbutton.tsx +126 -0
  133. package/src/hooks/I18nHooks.ts +33 -0
  134. package/src/hooks/UseChatHook.ts +38 -0
  135. package/src/plugin/ThemeSetter.ts +23 -0
  136. package/src/providers/PluginProvider.tsx +197 -0
  137. package/src/style.scss +136 -0
  138. package/src/utils/FullscreenUtils.ts +22 -0
  139. package/tsconfig.json +23 -0
@@ -0,0 +1,42 @@
1
+ type PrimitiveType = 'string' | 'number' | 'boolean';
2
+ type ObjectToolParameterType = PrimitiveType | {
3
+ [key: string]: ObjectToolParameter;
4
+ } | [{
5
+ [key: string]: ObjectToolParameter;
6
+ }];
7
+ interface ObjectToolParameter {
8
+ type: ObjectToolParameterType;
9
+ description?: string;
10
+ enum?: string[];
11
+ optional?: boolean;
12
+ }
13
+ /**
14
+ * The tools that the AI can use.
15
+ *
16
+ * The key is the name of the tool.
17
+ * The value is the parameter of the tool.
18
+ *
19
+ */
20
+ export type ObjectTool = {
21
+ [key: string]: ObjectToolParameter;
22
+ };
23
+ export interface ObjectRequest {
24
+ /**
25
+ * The tools that the AI can use.
26
+ */
27
+ tool: ObjectTool;
28
+ /**
29
+ * High level instructions for the AI to follow. Behaviour, tone, restrictions, etc.
30
+ * Example: "Act like a recipe writer."
31
+ */
32
+ behaviour?: string;
33
+ /**
34
+ * The specific instruction for the AI to follow.
35
+ * Example: "Generate a recipe using chicken, rice and vegetables."
36
+ */
37
+ instructions: string;
38
+ }
39
+ export declare function generateObject<T = any>(backendUrl: string, request: ObjectRequest, token: string): Promise<T>;
40
+ export type OnLLMResponse = (id: string, response: string, finished: boolean, toolInvocations?: any[]) => void;
41
+ export declare function streamObject(backendUrl: string, request: ObjectRequest, onResponse: OnLLMResponse, token: string): Promise<void>;
42
+ export {};
@@ -0,0 +1,76 @@
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 generateObject(backendUrl, request, token) {
11
+ return __awaiter(this, void 0, void 0, function* () {
12
+ return yield fetch(`${backendUrl}/ai/llm-object`, {
13
+ method: 'POST',
14
+ body: JSON.stringify({
15
+ stream: false,
16
+ tool: request.tool,
17
+ behaviour: request.behaviour,
18
+ instructions: request.instructions,
19
+ }),
20
+ headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' },
21
+ }).then((response) => response.json());
22
+ });
23
+ }
24
+ export function streamObject(backendUrl, request, onResponse, token) {
25
+ return __awaiter(this, void 0, void 0, function* () {
26
+ const messageId = Math.random().toString(36).substring(3);
27
+ const response = yield fetch(`${backendUrl}/ai/llm-object`, {
28
+ method: 'POST',
29
+ body: JSON.stringify({
30
+ stream: true,
31
+ tools: request.tool,
32
+ systemInstructions: request.behaviour,
33
+ secondaryInstructions: request.instructions,
34
+ }),
35
+ headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' },
36
+ });
37
+ if (!response.body) {
38
+ console.error('No response body.');
39
+ return;
40
+ }
41
+ const reader = response.body.getReader();
42
+ const decoder = new TextDecoder('utf-8');
43
+ let content = '';
44
+ let done = false;
45
+ const toolInvocations = [];
46
+ while (!done) {
47
+ const { value } = yield reader.read();
48
+ if (value) {
49
+ const chunk = decoder.decode(value, { stream: true });
50
+ const lines = chunk.split('\n').filter((line) => line.trim() !== '');
51
+ for (const line of lines) {
52
+ const data = line.substring(3, line.length - 1);
53
+ const command = line.substring(0, 1);
54
+ // console.log("data: ", { line, data, command });
55
+ if (command === '0') {
56
+ content += data;
57
+ // console.log("AI response:", content);
58
+ //content \n\n should be real line break when message is displayed
59
+ onResponse(messageId, content.replace(/\\n/g, '\n').replace(/\\+"/g, '"'), false);
60
+ }
61
+ else if (command === 'd') {
62
+ // console.log("AI usage:", JSON.parse(line.substring(2)));
63
+ done = true;
64
+ break;
65
+ }
66
+ else if (command === '9') {
67
+ // console.log("tool call:", JSON.parse(line.substring(2)));
68
+ // console.log("tools", tools);
69
+ toolInvocations.push(JSON.parse(line.substring(2)));
70
+ }
71
+ }
72
+ }
73
+ }
74
+ onResponse(messageId, content.replace(/\\n/g, '\n').replace(/\\+"/g, '"'), true, toolInvocations);
75
+ });
76
+ }
@@ -0,0 +1,79 @@
1
+ import { SupabaseClient } from '@supabase/supabase-js';
2
+ import { LanguageLevel } from '../utils/difficultyConverter';
3
+ import { Guild } from '../plugin/CommunicationHandler';
4
+ export interface Buddy {
5
+ id: string;
6
+ name: string;
7
+ description: string;
8
+ avatarUrl: string;
9
+ voiceId: string;
10
+ aiPersonality: string;
11
+ }
12
+ export interface Language {
13
+ code: string;
14
+ name: string;
15
+ native: string;
16
+ capitalized: string;
17
+ uppercase: string;
18
+ }
19
+ export interface UserInfo {
20
+ skill_level_reading: LanguageLevel;
21
+ skill_level_writing: LanguageLevel;
22
+ skill_level_grammar: LanguageLevel;
23
+ skill_level_speaking: LanguageLevel;
24
+ skill_level_listening: LanguageLevel;
25
+ skill_level_understanding: LanguageLevel;
26
+ goal_longterm: string;
27
+ goal_weekly: string;
28
+ study_buddy: Buddy;
29
+ story_genre: string;
30
+ study_duration: number;
31
+ /**
32
+ * The 2 letter language code of the language the user speaks natively.
33
+ * With the function getLanguageName, the language name can be retrieved.
34
+ */
35
+ mother_tongue: Language;
36
+ /**
37
+ * The language the user targets to learn.
38
+ */
39
+ target_language: Language;
40
+ motivation_type: string;
41
+ onboarding_completed: boolean;
42
+ context_menu_on_select: boolean;
43
+ user_name?: string;
44
+ /**
45
+ * ISO 3166-1 alpha-2 country code of user's target location (exposed to plugins)
46
+ */
47
+ target_country: string;
48
+ /**
49
+ * Optional: nearest big city (>100,000) near user's location
50
+ */
51
+ target_city?: string;
52
+ }
53
+ export declare class SettingsController {
54
+ private pluginId;
55
+ private supabase;
56
+ private guild;
57
+ constructor(supabase: SupabaseClient, pluginId: string, guild: Guild);
58
+ /**
59
+ * Fetches settings based on guild configuration.
60
+ * If guild doesn't allow user settings, fetches guild-level settings.
61
+ * Otherwise, fetches user-specific settings.
62
+ * @returns The settings object or null if not found.
63
+ */
64
+ private fetchSettings;
65
+ /**
66
+ * Sets settings for the plugin.
67
+ * Automatically saves as guild settings if guild doesn't allow user settings,
68
+ * otherwise saves as user-specific settings.
69
+ * @param settings - The settings object to save.
70
+ * @throws {Error} if RLS blocks the operation.
71
+ */
72
+ setSettings(settings: any): Promise<void>;
73
+ /**
74
+ * Get the settings for the plugin. T can be any type of settings, UserSettings or SystemSettings.
75
+ * @param defaultSettings The default settings to use if no settings are found.
76
+ * @returns The settings for the plugin.
77
+ */
78
+ getSettings<T extends object>(defaultSettings: T): Promise<T>;
79
+ }
@@ -0,0 +1,118 @@
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
+ }
@@ -0,0 +1,106 @@
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
+ }