@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.
Files changed (115) hide show
  1. package/README.md +142 -1
  2. package/dist/{react-client/src/components → components}/ContextMenu.js +1 -2
  3. package/dist/components/Spinner.d.ts +0 -7
  4. package/dist/components/Spinner.js +1 -4
  5. package/dist/components/ai/Assistant.js +1 -1
  6. package/dist/components/ai/Avatar.d.ts +2 -3
  7. package/dist/components/ai/Avatar.js +6 -4
  8. package/dist/components/ai/EmbeddedAssistent/AudioInputField.js +1 -1
  9. package/dist/components/ai/EmbeddedAssistent/CircleAudioAvatar.js +1 -1
  10. package/dist/components/ai/EmbeddedAssistent/VoiceRecoder.js +2 -2
  11. package/dist/components/audio/Playbutton.js +13 -9
  12. package/dist/hooks/I18nHooks.d.ts +1 -1
  13. package/dist/{react-client/src/plugin → hooks}/ThemeSetter.d.ts +1 -1
  14. package/dist/hooks/ThemeSetter.js +31 -0
  15. package/dist/hooks/UseChatHook.d.ts +2 -2
  16. package/dist/index.d.ts +9 -0
  17. package/dist/index.js +9 -0
  18. package/dist/{react-client/plugin → plugin}/ThemeSetter.d.ts +1 -1
  19. package/dist/plugin/ThemeSetter.js +31 -0
  20. package/dist/providers/PluginProvider.d.ts +2 -1
  21. package/dist/providers/PluginProvider.js +10 -7
  22. package/dist/{react-client/src/utils → utils}/FullscreenUtils.js +2 -2
  23. package/package.json +4 -7
  24. package/src/components/ContextMenu.tsx +2 -2
  25. package/src/components/ai/Avatar.tsx +9 -4
  26. package/src/components/ai/EmbeddedAssistent/AudioInputField.tsx +1 -1
  27. package/src/components/audio/Playbutton.tsx +28 -1
  28. package/src/hooks/ThemeSetter.ts +40 -0
  29. package/src/index.ts +10 -0
  30. package/src/providers/PluginProvider.tsx +12 -8
  31. package/tsconfig.json +6 -12
  32. package/dist/components/components/ContextMenu.d.ts +0 -10
  33. package/dist/components/components/ContextMenu.js +0 -135
  34. package/dist/react-client/plugin/ThemeSetter.js +0 -19
  35. package/dist/react-client/src/components/MarkdownEditor.d.ts +0 -8
  36. package/dist/react-client/src/components/MarkdownEditor.js +0 -48
  37. package/dist/react-client/src/components/Spinner.d.ts +0 -8
  38. package/dist/react-client/src/components/Spinner.js +0 -4
  39. package/dist/react-client/src/components/ai/Assistant.d.ts +0 -9
  40. package/dist/react-client/src/components/ai/Assistant.js +0 -58
  41. package/dist/react-client/src/components/ai/Avatar.d.ts +0 -14
  42. package/dist/react-client/src/components/ai/Avatar.js +0 -59
  43. package/dist/react-client/src/components/ai/EmbeddedAssistent/AudioInputField.d.ts +0 -7
  44. package/dist/react-client/src/components/ai/EmbeddedAssistent/AudioInputField.js +0 -37
  45. package/dist/react-client/src/components/ai/EmbeddedAssistent/CircleAudioAvatar.d.ts +0 -8
  46. package/dist/react-client/src/components/ai/EmbeddedAssistent/CircleAudioAvatar.js +0 -79
  47. package/dist/react-client/src/components/ai/EmbeddedAssistent/TTS/MessageSender.d.ts +0 -19
  48. package/dist/react-client/src/components/ai/EmbeddedAssistent/TTS/MessageSender.js +0 -91
  49. package/dist/react-client/src/components/ai/EmbeddedAssistent/TTS/Player.d.ts +0 -27
  50. package/dist/react-client/src/components/ai/EmbeddedAssistent/TTS/Player.js +0 -185
  51. package/dist/react-client/src/components/ai/utils.d.ts +0 -6
  52. package/dist/react-client/src/components/ai/utils.js +0 -13
  53. package/dist/react-client/src/components/audio/Playbutton.d.ts +0 -15
  54. package/dist/react-client/src/components/audio/Playbutton.js +0 -82
  55. package/dist/react-client/src/components/components/ContextMenu.d.ts +0 -10
  56. package/dist/react-client/src/components/components/ContextMenu.js +0 -135
  57. package/dist/react-client/src/hooks/I18nHooks.d.ts +0 -11
  58. package/dist/react-client/src/hooks/I18nHooks.js +0 -25
  59. package/dist/react-client/src/hooks/UseChatHook.d.ts +0 -10
  60. package/dist/react-client/src/hooks/UseChatHook.js +0 -29
  61. package/dist/react-client/src/plugin/ThemeSetter.js +0 -19
  62. package/dist/react-client/src/providers/PluginProvider.d.ts +0 -12
  63. package/dist/react-client/src/providers/PluginProvider.js +0 -142
  64. package/dist/react-client/src/utils/PluginUtils.d.ts +0 -2
  65. package/dist/react-client/src/utils/PluginUtils.js +0 -23
  66. package/dist/rimori-client/src/cli/types/DatabaseTypes.d.ts +0 -103
  67. package/dist/rimori-client/src/cli/types/DatabaseTypes.js +0 -2
  68. package/dist/rimori-client/src/controller/AIController.d.ts +0 -15
  69. package/dist/rimori-client/src/controller/AIController.js +0 -255
  70. package/dist/rimori-client/src/controller/AccomplishmentController.d.ts +0 -38
  71. package/dist/rimori-client/src/controller/AccomplishmentController.js +0 -112
  72. package/dist/rimori-client/src/controller/AudioController.d.ts +0 -37
  73. package/dist/rimori-client/src/controller/AudioController.js +0 -68
  74. package/dist/rimori-client/src/controller/ExerciseController.d.ts +0 -54
  75. package/dist/rimori-client/src/controller/ExerciseController.js +0 -74
  76. package/dist/rimori-client/src/controller/ObjectController.d.ts +0 -42
  77. package/dist/rimori-client/src/controller/ObjectController.js +0 -76
  78. package/dist/rimori-client/src/controller/SettingsController.d.ts +0 -79
  79. package/dist/rimori-client/src/controller/SettingsController.js +0 -118
  80. package/dist/rimori-client/src/controller/SharedContentController.d.ts +0 -106
  81. package/dist/rimori-client/src/controller/SharedContentController.js +0 -285
  82. package/dist/rimori-client/src/controller/TranslationController.d.ts +0 -38
  83. package/dist/rimori-client/src/controller/TranslationController.js +0 -106
  84. package/dist/rimori-client/src/controller/VoiceController.d.ts +0 -9
  85. package/dist/rimori-client/src/controller/VoiceController.js +0 -37
  86. package/dist/rimori-client/src/fromRimori/EventBus.d.ts +0 -101
  87. package/dist/rimori-client/src/fromRimori/EventBus.js +0 -263
  88. package/dist/rimori-client/src/fromRimori/PluginTypes.d.ts +0 -174
  89. package/dist/rimori-client/src/fromRimori/PluginTypes.js +0 -1
  90. package/dist/rimori-client/src/index.d.ts +0 -11
  91. package/dist/rimori-client/src/index.js +0 -10
  92. package/dist/rimori-client/src/plugin/CommunicationHandler.d.ts +0 -48
  93. package/dist/rimori-client/src/plugin/CommunicationHandler.js +0 -234
  94. package/dist/rimori-client/src/plugin/Logger.d.ts +0 -73
  95. package/dist/rimori-client/src/plugin/Logger.js +0 -308
  96. package/dist/rimori-client/src/plugin/RimoriClient.d.ts +0 -258
  97. package/dist/rimori-client/src/plugin/RimoriClient.js +0 -375
  98. package/dist/rimori-client/src/plugin/StandaloneClient.d.ts +0 -17
  99. package/dist/rimori-client/src/plugin/StandaloneClient.js +0 -115
  100. package/dist/rimori-client/src/utils/difficultyConverter.d.ts +0 -4
  101. package/dist/rimori-client/src/utils/difficultyConverter.js +0 -10
  102. package/dist/rimori-client/src/utils/endpoint.d.ts +0 -2
  103. package/dist/rimori-client/src/utils/endpoint.js +0 -2
  104. package/dist/utils/PluginUtils.d.ts +0 -2
  105. package/dist/utils/PluginUtils.js +0 -23
  106. package/index.ts +0 -6
  107. package/src/components/MarkdownEditor.tsx +0 -144
  108. package/src/components/Spinner.tsx +0 -29
  109. package/src/plugin/ThemeSetter.ts +0 -23
  110. package/src/utils/FullscreenUtils.ts +0 -22
  111. /package/dist/{react-client/src/components → components}/ContextMenu.d.ts +0 -0
  112. /package/dist/{react-client/src/components/ai/EmbeddedAssistent/VoiceRecoder.d.ts → components/ai/EmbeddedAssistent/VoiceRecorder.d.ts} +0 -0
  113. /package/dist/{react-client/src/components/ai/EmbeddedAssistent/VoiceRecoder.js → components/ai/EmbeddedAssistent/VoiceRecorder.js} +0 -0
  114. /package/dist/{react-client/src/utils → utils}/FullscreenUtils.d.ts +0 -0
  115. /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,2 +0,0 @@
1
- export declare function isFullscreen(): boolean;
2
- export declare function triggerFullscreen(onStateChange: (isFullscreen: boolean) => void, selector?: string): void;
@@ -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
- }