@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,115 +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 { createClient } from '@supabase/supabase-js';
|
|
11
|
-
import { EventBus } from '../fromRimori/EventBus';
|
|
12
|
-
import { DEFAULT_ANON_KEY, DEFAULT_ENDPOINT } from '../utils/endpoint';
|
|
13
|
-
export class StandaloneClient {
|
|
14
|
-
constructor(config) {
|
|
15
|
-
this.supabase = createClient(config.url, config.key);
|
|
16
|
-
this.config = config;
|
|
17
|
-
}
|
|
18
|
-
static getInstance() {
|
|
19
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
20
|
-
if (!StandaloneClient.instance) {
|
|
21
|
-
const config = yield fetch('https://app.rimori.se/config.json')
|
|
22
|
-
.then((res) => res.json())
|
|
23
|
-
.catch((err) => {
|
|
24
|
-
console.warn('Error fetching config.json, using default values', err);
|
|
25
|
-
});
|
|
26
|
-
StandaloneClient.instance = new StandaloneClient({
|
|
27
|
-
url: (config === null || config === void 0 ? void 0 : config.SUPABASE_URL) || DEFAULT_ENDPOINT,
|
|
28
|
-
key: (config === null || config === void 0 ? void 0 : config.SUPABASE_ANON_KEY) || DEFAULT_ANON_KEY,
|
|
29
|
-
backendUrl: (config === null || config === void 0 ? void 0 : config.BACKEND_URL) || 'https://api.rimori.se',
|
|
30
|
-
});
|
|
31
|
-
}
|
|
32
|
-
return StandaloneClient.instance;
|
|
33
|
-
});
|
|
34
|
-
}
|
|
35
|
-
getClient() {
|
|
36
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
37
|
-
return this.supabase;
|
|
38
|
-
});
|
|
39
|
-
}
|
|
40
|
-
needsLogin() {
|
|
41
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
42
|
-
const { error } = yield this.supabase.auth.getUser();
|
|
43
|
-
return error !== null;
|
|
44
|
-
});
|
|
45
|
-
}
|
|
46
|
-
login(email, password) {
|
|
47
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
48
|
-
const { error } = yield this.supabase.auth.signInWithPassword({ email, password });
|
|
49
|
-
if (error) {
|
|
50
|
-
console.error('Login failed:', error);
|
|
51
|
-
return false;
|
|
52
|
-
}
|
|
53
|
-
console.log('Successfully logged in');
|
|
54
|
-
return true;
|
|
55
|
-
});
|
|
56
|
-
}
|
|
57
|
-
static initListeners(pluginId) {
|
|
58
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
59
|
-
console.warn('The plugin seams to not be running inside the Rimori platform. Switching to development standalone mode.');
|
|
60
|
-
// console.log("event that needs to be handled", event);
|
|
61
|
-
const { supabase, config } = yield StandaloneClient.getInstance();
|
|
62
|
-
// EventBus.on("*", async (event) => {
|
|
63
|
-
EventBus.respond('standalone', 'global.supabase.requestAccess', () => __awaiter(this, void 0, void 0, function* () {
|
|
64
|
-
var _a;
|
|
65
|
-
const session = yield supabase.auth.getSession();
|
|
66
|
-
console.log('session', session);
|
|
67
|
-
// Call the NestJS backend endpoint instead of the Supabase edge function
|
|
68
|
-
// get current guild id if any
|
|
69
|
-
let guildId = null;
|
|
70
|
-
try {
|
|
71
|
-
const { data: { user }, } = yield supabase.auth.getUser();
|
|
72
|
-
if (user) {
|
|
73
|
-
const { data: profile } = yield supabase
|
|
74
|
-
.from('profiles')
|
|
75
|
-
.select('current_guild_id')
|
|
76
|
-
.eq('user_id', user.id)
|
|
77
|
-
.maybeSingle();
|
|
78
|
-
guildId = (profile === null || profile === void 0 ? void 0 : profile.current_guild_id) || null;
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
catch (_) {
|
|
82
|
-
guildId = null;
|
|
83
|
-
}
|
|
84
|
-
const response = yield fetch(`${config.backendUrl}/plugin/token`, {
|
|
85
|
-
method: 'POST',
|
|
86
|
-
headers: {
|
|
87
|
-
'Content-Type': 'application/json',
|
|
88
|
-
Authorization: `Bearer ${(_a = session.data.session) === null || _a === void 0 ? void 0 : _a.access_token}`,
|
|
89
|
-
},
|
|
90
|
-
body: JSON.stringify({
|
|
91
|
-
pluginId: pluginId,
|
|
92
|
-
guildId: guildId,
|
|
93
|
-
}),
|
|
94
|
-
});
|
|
95
|
-
if (!response.ok) {
|
|
96
|
-
const errorText = yield response.text();
|
|
97
|
-
throw new Error(`Failed to get plugin token. ${response.status}: ${errorText}`);
|
|
98
|
-
}
|
|
99
|
-
const data = yield response.json();
|
|
100
|
-
return {
|
|
101
|
-
token: data.token,
|
|
102
|
-
pluginId: pluginId,
|
|
103
|
-
url: config.url,
|
|
104
|
-
key: config.key,
|
|
105
|
-
backendUrl: config.backendUrl,
|
|
106
|
-
tablePrefix: pluginId,
|
|
107
|
-
expiration: new Date(Date.now() + 1000 * 60 * 60 * 1.5), // 1.5 hours
|
|
108
|
-
};
|
|
109
|
-
}));
|
|
110
|
-
EventBus.on('*', (event) => __awaiter(this, void 0, void 0, function* () {
|
|
111
|
-
console.log('[standalone] would send event to parent', event);
|
|
112
|
-
}), ['standalone']);
|
|
113
|
-
});
|
|
114
|
-
}
|
|
115
|
-
}
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
export type LanguageLevel = 'Pre-A1' | 'A1' | 'A2' | 'B1' | 'B2' | 'C1' | 'C2' | 'Post-C2';
|
|
2
|
-
export declare function getDifficultyLevel(difficulty: LanguageLevel): number;
|
|
3
|
-
export declare function getDifficultyLabel(difficulty: number): LanguageLevel;
|
|
4
|
-
export declare function getNeighborDifficultyLevel(difficulty: LanguageLevel, difficultyAdjustment: number): LanguageLevel;
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
const codes = ['Pre-A1', 'A1', 'A2', 'B1', 'B2', 'C1', 'C2', 'Post-C2'];
|
|
2
|
-
export function getDifficultyLevel(difficulty) {
|
|
3
|
-
return codes.indexOf(difficulty) + 1;
|
|
4
|
-
}
|
|
5
|
-
export function getDifficultyLabel(difficulty) {
|
|
6
|
-
return codes[difficulty];
|
|
7
|
-
}
|
|
8
|
-
export function getNeighborDifficultyLevel(difficulty, difficultyAdjustment) {
|
|
9
|
-
return getDifficultyLabel(getDifficultyLevel(difficulty) + difficultyAdjustment - 1);
|
|
10
|
-
}
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
export declare const DEFAULT_ENDPOINT = "https://pheptqdoqsdnadgoihvr.supabase.co";
|
|
2
|
-
export declare const DEFAULT_ANON_KEY = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InBoZXB0cWRvcXNkbmFkZ29paHZyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3MzE2OTY2ODcsImV4cCI6MjA0NzI3MjY4N30.4GPFAXTF8685FaXISdAPNCIM-H3RGLo8GbyhQpu1mP0";
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
export const DEFAULT_ENDPOINT = 'https://pheptqdoqsdnadgoihvr.supabase.co';
|
|
2
|
-
export const DEFAULT_ANON_KEY = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InBoZXB0cWRvcXNkbmFkZ29paHZyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3MzE2OTY2ODcsImV4cCI6MjA0NzI3MjY4N30.4GPFAXTF8685FaXISdAPNCIM-H3RGLo8GbyhQpu1mP0';
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
export function isFullscreen() {
|
|
2
|
-
return !!document.fullscreenElement;
|
|
3
|
-
}
|
|
4
|
-
export function triggerFullscreen(onStateChange, selector) {
|
|
5
|
-
document.addEventListener('fullscreenchange', () => {
|
|
6
|
-
onStateChange(isFullscreen());
|
|
7
|
-
});
|
|
8
|
-
try {
|
|
9
|
-
const ref = document.querySelector(selector || '#root');
|
|
10
|
-
if (!isFullscreen()) {
|
|
11
|
-
// @ts-ignore
|
|
12
|
-
ref.requestFullscreen() || ref.webkitRequestFullscreen();
|
|
13
|
-
}
|
|
14
|
-
else {
|
|
15
|
-
// @ts-ignore
|
|
16
|
-
document.exitFullscreen() || document.webkitExitFullscreen();
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
catch (error) {
|
|
20
|
-
console.error('Failed to enter fullscreen', error.message);
|
|
21
|
-
}
|
|
22
|
-
onStateChange(isFullscreen());
|
|
23
|
-
}
|
package/index.ts
DELETED
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
// Re-export everything
|
|
2
|
-
export * from './src/hooks/UseChatHook';
|
|
3
|
-
export * from './src/providers/PluginProvider';
|
|
4
|
-
export * from './src/utils/FullscreenUtils';
|
|
5
|
-
export { FirstMessages } from './src/components/ai/utils';
|
|
6
|
-
export { useTranslation } from './src/hooks/I18nHooks';
|
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
import { Markdown } from 'tiptap-markdown';
|
|
2
|
-
import StarterKit from '@tiptap/starter-kit';
|
|
3
|
-
import { PiCodeBlock } from 'react-icons/pi';
|
|
4
|
-
import { TbBlockquote } from 'react-icons/tb';
|
|
5
|
-
import { GoListOrdered } from 'react-icons/go';
|
|
6
|
-
import { AiOutlineUnorderedList } from 'react-icons/ai';
|
|
7
|
-
import { EditorProvider, useCurrentEditor } from '@tiptap/react';
|
|
8
|
-
import { LuHeading1, LuHeading2, LuHeading3 } from 'react-icons/lu';
|
|
9
|
-
import { FaBold, FaCode, FaItalic, FaParagraph, FaStrikethrough } from 'react-icons/fa';
|
|
10
|
-
|
|
11
|
-
// This inplementation is rooted in the Tiptap editor basic example https://codesandbox.io/p/devbox/editor-9x9dkd
|
|
12
|
-
|
|
13
|
-
interface EditorButtonProps {
|
|
14
|
-
action: string;
|
|
15
|
-
isActive?: boolean;
|
|
16
|
-
label: string | React.ReactNode;
|
|
17
|
-
disabled?: boolean;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const EditorButton = ({ action, isActive, label, disabled }: EditorButtonProps) => {
|
|
21
|
-
const { editor } = useCurrentEditor() as any;
|
|
22
|
-
|
|
23
|
-
if (!editor) {
|
|
24
|
-
return null;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
if (action.includes('heading')) {
|
|
28
|
-
const level = parseInt(action[action.length - 1]);
|
|
29
|
-
return (
|
|
30
|
-
<button
|
|
31
|
-
onClick={() => editor.chain().focus().toggleHeading({ level: level }).run()}
|
|
32
|
-
className={`pl-2 ${isActive ? 'is-active' : ''}`}
|
|
33
|
-
>
|
|
34
|
-
{label}
|
|
35
|
-
</button>
|
|
36
|
-
);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
return (
|
|
40
|
-
<button
|
|
41
|
-
onClick={() => editor.chain().focus()[action]().run()}
|
|
42
|
-
disabled={disabled ? !editor.can().chain().focus()[action]().run() : false}
|
|
43
|
-
className={`pl-2 ${isActive ? 'is-active' : ''}`}
|
|
44
|
-
>
|
|
45
|
-
{label}
|
|
46
|
-
</button>
|
|
47
|
-
);
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
const MenuBar = () => {
|
|
51
|
-
const { editor } = useCurrentEditor();
|
|
52
|
-
|
|
53
|
-
if (!editor) {
|
|
54
|
-
return null;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
return (
|
|
58
|
-
<div className="bg-gray-400 dark:bg-gray-800 dark:text-white text-lg flex flex-row flex-wrap items-center p-1">
|
|
59
|
-
<EditorButton action="toggleBold" isActive={editor.isActive('bold')} label={<FaBold />} disabled />
|
|
60
|
-
<EditorButton action="toggleItalic" isActive={editor.isActive('italic')} label={<FaItalic />} disabled />
|
|
61
|
-
<EditorButton action="toggleStrike" isActive={editor.isActive('strike')} label={<FaStrikethrough />} disabled />
|
|
62
|
-
<EditorButton action="toggleCode" isActive={editor.isActive('code')} label={<FaCode />} disabled />
|
|
63
|
-
<EditorButton action="setParagraph" isActive={editor.isActive('paragraph')} label={<FaParagraph />} />
|
|
64
|
-
<EditorButton
|
|
65
|
-
action="setHeading1"
|
|
66
|
-
isActive={editor.isActive('heading', { level: 1 })}
|
|
67
|
-
label={<LuHeading1 size={'24px'} />}
|
|
68
|
-
/>
|
|
69
|
-
<EditorButton
|
|
70
|
-
action="setHeading2"
|
|
71
|
-
isActive={editor.isActive('heading', { level: 2 })}
|
|
72
|
-
label={<LuHeading2 size={'24px'} />}
|
|
73
|
-
/>
|
|
74
|
-
<EditorButton
|
|
75
|
-
action="setHeading3"
|
|
76
|
-
isActive={editor.isActive('heading', { level: 3 })}
|
|
77
|
-
label={<LuHeading3 size={'24px'} />}
|
|
78
|
-
/>
|
|
79
|
-
<EditorButton
|
|
80
|
-
action="toggleBulletList"
|
|
81
|
-
isActive={editor.isActive('bulletList')}
|
|
82
|
-
label={<AiOutlineUnorderedList size={'24px'} />}
|
|
83
|
-
/>
|
|
84
|
-
<EditorButton
|
|
85
|
-
action="toggleOrderedList"
|
|
86
|
-
isActive={editor.isActive('orderedList')}
|
|
87
|
-
label={<GoListOrdered size={'24px'} />}
|
|
88
|
-
/>
|
|
89
|
-
<EditorButton
|
|
90
|
-
action="toggleCodeBlock"
|
|
91
|
-
isActive={editor.isActive('codeBlock')}
|
|
92
|
-
label={<PiCodeBlock size={'24px'} />}
|
|
93
|
-
/>
|
|
94
|
-
<EditorButton
|
|
95
|
-
action="toggleBlockquote"
|
|
96
|
-
isActive={editor.isActive('blockquote')}
|
|
97
|
-
label={<TbBlockquote size={'24px'} />}
|
|
98
|
-
/>
|
|
99
|
-
</div>
|
|
100
|
-
);
|
|
101
|
-
};
|
|
102
|
-
|
|
103
|
-
const extensions = [
|
|
104
|
-
StarterKit.configure({
|
|
105
|
-
bulletList: {
|
|
106
|
-
HTMLAttributes: {
|
|
107
|
-
class: 'list-disc list-inside dark:text-white p-1 mt-1 [&_li]:mb-1 [&_p]:inline m-0',
|
|
108
|
-
},
|
|
109
|
-
},
|
|
110
|
-
orderedList: {
|
|
111
|
-
HTMLAttributes: {
|
|
112
|
-
className: 'list-decimal list-inside dark:text-white p-1 mt-1 [&_li]:mb-1 [&_p]:inline m-0',
|
|
113
|
-
},
|
|
114
|
-
},
|
|
115
|
-
}),
|
|
116
|
-
Markdown,
|
|
117
|
-
];
|
|
118
|
-
|
|
119
|
-
interface Props {
|
|
120
|
-
content?: string;
|
|
121
|
-
editable: boolean;
|
|
122
|
-
className?: string;
|
|
123
|
-
onUpdate?: (content: string) => void;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
export const MarkdownEditor = (props: Props) => {
|
|
127
|
-
return (
|
|
128
|
-
<div
|
|
129
|
-
className={'text-md border border-gray-800 overflow-hidden ' + props.className}
|
|
130
|
-
style={{ borderWidth: props.editable ? 1 : 0 }}
|
|
131
|
-
>
|
|
132
|
-
<EditorProvider
|
|
133
|
-
key={(props.editable ? 'editable' : 'readonly') + props.content}
|
|
134
|
-
slotBefore={props.editable ? <MenuBar /> : null}
|
|
135
|
-
extensions={extensions}
|
|
136
|
-
content={props.content}
|
|
137
|
-
editable={props.editable}
|
|
138
|
-
onUpdate={(e) => {
|
|
139
|
-
props.onUpdate && props.onUpdate(e.editor.storage.markdown.getMarkdown());
|
|
140
|
-
}}
|
|
141
|
-
></EditorProvider>
|
|
142
|
-
</div>
|
|
143
|
-
);
|
|
144
|
-
};
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
|
|
3
|
-
interface SpinnerProps {
|
|
4
|
-
text?: string;
|
|
5
|
-
size?: string;
|
|
6
|
-
className?: string;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export const Spinner: React.FC<SpinnerProps> = ({ text, className, size = '30px' }) => {
|
|
10
|
-
return (
|
|
11
|
-
<div className={'flex items-center space-x-2 ' + className}>
|
|
12
|
-
<svg
|
|
13
|
-
style={{ width: size, height: size }}
|
|
14
|
-
className="animate-spin -ml-1 mr-3 h-5 w-5 text-white"
|
|
15
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
16
|
-
fill="none"
|
|
17
|
-
viewBox="0 0 24 24"
|
|
18
|
-
>
|
|
19
|
-
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
|
|
20
|
-
<path
|
|
21
|
-
className="opacity-75"
|
|
22
|
-
fill="currentColor"
|
|
23
|
-
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
|
24
|
-
></path>
|
|
25
|
-
</svg>
|
|
26
|
-
{text && <span className="">{text}</span>}
|
|
27
|
-
</div>
|
|
28
|
-
);
|
|
29
|
-
};
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
export function setTheme(theme?: string | null) {
|
|
2
|
-
document.documentElement.classList.add('dark:text-gray-200');
|
|
3
|
-
|
|
4
|
-
if (isDarkTheme(theme)) {
|
|
5
|
-
document.documentElement.setAttribute('data-theme', 'dark');
|
|
6
|
-
document.documentElement.classList.add('dark', 'dark:bg-gray-950');
|
|
7
|
-
document.documentElement.style.background = 'hsl(var(--background))';
|
|
8
|
-
}
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export function isDarkTheme(theme?: string | null): boolean {
|
|
12
|
-
// If no theme provided, try to get from URL as fallback (for standalone mode)
|
|
13
|
-
if (!theme) {
|
|
14
|
-
const urlParams = new URLSearchParams(window.location.search);
|
|
15
|
-
theme = urlParams.get('theme');
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
if (!theme || theme === 'system') {
|
|
19
|
-
return window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
return theme === 'dark';
|
|
23
|
-
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
export function isFullscreen() {
|
|
2
|
-
return !!document.fullscreenElement;
|
|
3
|
-
}
|
|
4
|
-
|
|
5
|
-
export function triggerFullscreen(onStateChange: (isFullscreen: boolean) => void, selector?: string) {
|
|
6
|
-
document.addEventListener('fullscreenchange', () => {
|
|
7
|
-
onStateChange(isFullscreen());
|
|
8
|
-
});
|
|
9
|
-
try {
|
|
10
|
-
const ref = document.querySelector(selector || '#root')!;
|
|
11
|
-
if (!isFullscreen()) {
|
|
12
|
-
// @ts-ignore
|
|
13
|
-
ref.requestFullscreen() || ref.webkitRequestFullscreen();
|
|
14
|
-
} else {
|
|
15
|
-
// @ts-ignore
|
|
16
|
-
document.exitFullscreen() || document.webkitExitFullscreen();
|
|
17
|
-
}
|
|
18
|
-
} catch (error: any) {
|
|
19
|
-
console.error('Failed to enter fullscreen', error.message);
|
|
20
|
-
}
|
|
21
|
-
onStateChange(isFullscreen());
|
|
22
|
-
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|