@rimori/client 1.4.0 → 1.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (133) hide show
  1. package/README.md +77 -71
  2. package/dist/cli/scripts/init/dev-registration.d.ts +1 -1
  3. package/dist/cli/scripts/init/dev-registration.js +4 -4
  4. package/dist/cli/scripts/init/main.js +1 -1
  5. package/dist/cli/scripts/init/package-setup.d.ts +1 -1
  6. package/dist/cli/scripts/init/package-setup.js +3 -3
  7. package/dist/cli/scripts/init/router-transformer.js +19 -12
  8. package/dist/cli/scripts/init/vite-config.d.ts +2 -2
  9. package/dist/cli/scripts/init/vite-config.js +2 -2
  10. package/dist/cli/scripts/release/release-config-upload.js +9 -9
  11. package/dist/cli/scripts/release/release-db-update.d.ts +1 -1
  12. package/dist/cli/scripts/release/release-db-update.js +9 -9
  13. package/dist/cli/scripts/release/release-file-upload.js +1 -1
  14. package/dist/cli/scripts/release/release.js +2 -2
  15. package/dist/components/CRUDModal.d.ts +1 -1
  16. package/dist/components/CRUDModal.js +3 -3
  17. package/dist/components/MarkdownEditor.js +16 -16
  18. package/dist/components/Spinner.js +2 -2
  19. package/dist/components/ai/Assistant.js +7 -8
  20. package/dist/components/ai/Avatar.d.ts +2 -2
  21. package/dist/components/ai/Avatar.js +10 -5
  22. package/dist/components/ai/EmbeddedAssistent/AudioInputField.js +5 -6
  23. package/dist/components/ai/EmbeddedAssistent/CircleAudioAvatar.d.ts +1 -1
  24. package/dist/components/ai/EmbeddedAssistent/CircleAudioAvatar.js +1 -2
  25. package/dist/components/ai/EmbeddedAssistent/TTS/MessageSender.d.ts +1 -2
  26. package/dist/components/ai/EmbeddedAssistent/TTS/MessageSender.js +4 -2
  27. package/dist/components/ai/EmbeddedAssistent/VoiceRecoder.js +2 -3
  28. package/dist/components/audio/Playbutton.js +10 -7
  29. package/dist/components/components/ContextMenu.d.ts +1 -1
  30. package/dist/components/components/ContextMenu.js +19 -16
  31. package/dist/components.d.ts +10 -10
  32. package/dist/components.js +10 -10
  33. package/dist/core/controller/AIController.d.ts +2 -2
  34. package/dist/core/controller/AIController.js +12 -12
  35. package/dist/core/controller/ExerciseController.d.ts +2 -2
  36. package/dist/core/controller/ExerciseController.js +2 -2
  37. package/dist/core/controller/ObjectController.js +5 -5
  38. package/dist/core/controller/SettingsController.d.ts +22 -7
  39. package/dist/core/controller/SettingsController.js +73 -8
  40. package/dist/core/controller/SharedContentController.d.ts +3 -3
  41. package/dist/core/controller/SharedContentController.js +38 -20
  42. package/dist/core/controller/VoiceController.js +6 -4
  43. package/dist/core/core.d.ts +15 -15
  44. package/dist/core/core.js +7 -7
  45. package/dist/fromRimori/EventBus.js +23 -23
  46. package/dist/fromRimori/PluginTypes.d.ts +4 -4
  47. package/dist/hooks/UseChatHook.d.ts +3 -3
  48. package/dist/hooks/UseChatHook.js +9 -3
  49. package/dist/index.d.ts +10 -10
  50. package/dist/index.js +9 -9
  51. package/dist/plugin/AccomplishmentHandler.d.ts +5 -5
  52. package/dist/plugin/AccomplishmentHandler.js +31 -27
  53. package/dist/plugin/AudioController.d.ts +1 -1
  54. package/dist/plugin/AudioController.js +6 -6
  55. package/dist/plugin/Logger.js +15 -13
  56. package/dist/plugin/PluginController.d.ts +7 -1
  57. package/dist/plugin/PluginController.js +32 -27
  58. package/dist/plugin/RimoriClient.d.ts +17 -18
  59. package/dist/plugin/RimoriClient.js +31 -31
  60. package/dist/plugin/StandaloneClient.d.ts +1 -1
  61. package/dist/plugin/StandaloneClient.js +35 -16
  62. package/dist/plugin/ThemeSetter.js +4 -4
  63. package/dist/providers/PluginProvider.js +44 -14
  64. package/dist/utils/Language.js +57 -57
  65. package/dist/utils/PluginUtils.js +3 -3
  66. package/dist/utils/difficultyConverter.d.ts +1 -1
  67. package/dist/utils/difficultyConverter.js +1 -1
  68. package/dist/utils/endpoint.js +2 -2
  69. package/dist/worker/WorkerSetup.d.ts +1 -1
  70. package/dist/worker/WorkerSetup.js +6 -6
  71. package/example/docs/devdocs.md +50 -40
  72. package/example/docs/overview.md +1 -1
  73. package/example/docs/userdocs.md +4 -1
  74. package/example/rimori.config.ts +51 -49
  75. package/example/worker/vite.config.ts +3 -3
  76. package/example/worker/worker.ts +2 -2
  77. package/package.json +14 -8
  78. package/prettier.config.js +1 -1
  79. package/src/cli/scripts/init/dev-registration.ts +5 -8
  80. package/src/cli/scripts/init/env-setup.ts +1 -1
  81. package/src/cli/scripts/init/file-operations.ts +1 -1
  82. package/src/cli/scripts/init/html-cleaner.ts +2 -5
  83. package/src/cli/scripts/init/main.ts +16 -13
  84. package/src/cli/scripts/init/package-setup.ts +11 -15
  85. package/src/cli/scripts/init/router-transformer.ts +40 -37
  86. package/src/cli/scripts/init/tailwind-config.ts +17 -26
  87. package/src/cli/scripts/init/vite-config.ts +3 -3
  88. package/src/cli/scripts/release/release-config-upload.ts +11 -11
  89. package/src/cli/scripts/release/release-db-update.ts +12 -12
  90. package/src/cli/scripts/release/release-file-upload.ts +2 -2
  91. package/src/cli/scripts/release/release.ts +4 -4
  92. package/src/cli/types/DatabaseTypes.ts +2 -10
  93. package/src/components/CRUDModal.tsx +64 -48
  94. package/src/components/MarkdownEditor.tsx +58 -27
  95. package/src/components/Spinner.tsx +24 -17
  96. package/src/components/ai/Assistant.tsx +70 -70
  97. package/src/components/ai/Avatar.tsx +17 -14
  98. package/src/components/ai/EmbeddedAssistent/AudioInputField.tsx +63 -54
  99. package/src/components/ai/EmbeddedAssistent/CircleAudioAvatar.tsx +14 -5
  100. package/src/components/ai/EmbeddedAssistent/TTS/MessageSender.ts +75 -74
  101. package/src/components/ai/EmbeddedAssistent/TTS/Player.ts +3 -4
  102. package/src/components/ai/EmbeddedAssistent/VoiceRecoder.tsx +109 -94
  103. package/src/components/ai/utils.ts +4 -4
  104. package/src/components/audio/Playbutton.tsx +101 -93
  105. package/src/components/components/ContextMenu.tsx +47 -35
  106. package/src/components.ts +10 -10
  107. package/src/core/controller/AIController.ts +29 -19
  108. package/src/core/controller/ExerciseController.ts +16 -23
  109. package/src/core/controller/ObjectController.ts +15 -10
  110. package/src/core/controller/SettingsController.ts +89 -16
  111. package/src/core/controller/SharedContentController.ts +80 -44
  112. package/src/core/controller/VoiceController.ts +10 -8
  113. package/src/core/core.ts +15 -16
  114. package/src/fromRimori/EventBus.ts +76 -47
  115. package/src/fromRimori/PluginTypes.ts +26 -17
  116. package/src/fromRimori/readme.md +2 -2
  117. package/src/hooks/UseChatHook.ts +25 -15
  118. package/src/index.ts +10 -10
  119. package/src/plugin/AccomplishmentHandler.ts +53 -35
  120. package/src/plugin/AudioController.ts +18 -12
  121. package/src/plugin/Logger.ts +28 -21
  122. package/src/plugin/PluginController.ts +60 -44
  123. package/src/plugin/RimoriClient.ts +102 -72
  124. package/src/plugin/StandaloneClient.ts +51 -24
  125. package/src/plugin/ThemeSetter.ts +5 -5
  126. package/src/providers/PluginProvider.tsx +90 -36
  127. package/src/style.scss +3 -3
  128. package/src/utils/Language.ts +58 -58
  129. package/src/utils/PluginUtils.ts +16 -20
  130. package/src/utils/difficultyConverter.ts +2 -2
  131. package/src/utils/endpoint.ts +3 -2
  132. package/src/worker/WorkerSetup.ts +8 -9
  133. package/tsconfig.json +2 -4
@@ -38,9 +38,9 @@ const config = {
38
38
  release_channel: releaseChannel,
39
39
  plugin_id: pluginId,
40
40
  token: RIMORI_TOKEN,
41
- domain: process.env.RIMORI_BACKEND_URL || "https://api.rimori.se",
41
+ domain: process.env.RIMORI_BACKEND_URL || 'https://api.rimori.se',
42
42
  rimori_client_version: packageJson.dependencies['@rimori/client'].replace('^', ''),
43
- }
43
+ };
44
44
 
45
45
  export type Config = typeof config;
46
46
 
@@ -61,9 +61,9 @@ async function releaseProcess(): Promise<void> {
61
61
  // Then release the plugin
62
62
  await releasePlugin(config, release_id);
63
63
  } catch (error: any) {
64
- console.log("❌ Error:", error.message);
64
+ console.log('❌ Error:', error.message);
65
65
  process.exit(1);
66
66
  }
67
67
  }
68
68
 
69
- releaseProcess();
69
+ releaseProcess();
@@ -3,14 +3,7 @@
3
3
  /**
4
4
  * Supported database column data types for table schema definitions.
5
5
  */
6
- type DbColumnType =
7
- | 'decimal'
8
- | 'integer'
9
- | 'text'
10
- | 'boolean'
11
- | 'json'
12
- | 'timestamp'
13
- | 'uuid';
6
+ type DbColumnType = 'decimal' | 'integer' | 'text' | 'boolean' | 'json' | 'timestamp' | 'uuid';
14
7
 
15
8
  /**
16
9
  * Foreign key relationship configuration with cascade delete support.
@@ -120,5 +113,4 @@ export interface DbPermissionDefinition {
120
113
  /**
121
114
  * Full table definition that includes automatically generated fields.
122
115
  */
123
- export type FullTable<T extends Record<string, DbColumnDefinition>> = T &
124
- BaseTableStructure;
116
+ export type FullTable<T extends Record<string, DbColumnDefinition>> = T & BaseTableStructure;
@@ -1,59 +1,75 @@
1
- import { useEffect, useRef } from "react";
1
+ import { useEffect, useRef } from 'react';
2
2
 
3
3
  interface Props {
4
- title: string;
5
- show?: boolean;
6
- className?: string;
7
- closeAble?: boolean;
8
- children: React.ReactNode;
9
- actionbuttons: ActionButton[];
10
- buttonText?: string | React.ReactNode;
11
- onClose?: () => void;
4
+ title: string;
5
+ show?: boolean;
6
+ className?: string;
7
+ closeAble?: boolean;
8
+ children: React.ReactNode;
9
+ actionbuttons: ActionButton[];
10
+ buttonText?: string | React.ReactNode;
11
+ onClose?: () => void;
12
12
  }
13
13
 
14
14
  interface ActionButton {
15
- text: string;
16
- onClick: () => void;
17
- closeModal?: boolean;
15
+ text: string;
16
+ onClick: () => void;
17
+ closeModal?: boolean;
18
18
  }
19
19
 
20
- export function CRUDModal({ actionbuttons, children, title, buttonText, className, closeAble = true, show = false, onClose }: Props) {
21
- const dialogRef = useRef<HTMLDialogElement>(null);
20
+ export function CRUDModal({
21
+ actionbuttons,
22
+ children,
23
+ title,
24
+ buttonText,
25
+ className,
26
+ closeAble = true,
27
+ show = false,
28
+ onClose,
29
+ }: Props) {
30
+ const dialogRef = useRef<HTMLDialogElement>(null);
22
31
 
23
- useEffect(() => {
24
- if (show) {
25
- dialogRef.current?.showModal();
26
- } else {
27
- dialogRef.current?.close();
28
- }
29
- }, [show]);
32
+ useEffect(() => {
33
+ if (show) {
34
+ dialogRef.current?.showModal();
35
+ } else {
36
+ dialogRef.current?.close();
37
+ }
38
+ }, [show]);
30
39
 
31
- const handleClose = () => {
32
- dialogRef.current?.close();
33
- onClose?.();
34
- };
40
+ const handleClose = () => {
41
+ dialogRef.current?.close();
42
+ onClose?.();
43
+ };
35
44
 
36
- return (
37
- <>
38
- {!!buttonText && <button className={className} onClick={() => dialogRef.current?.showModal()}>{buttonText}</button>}
39
- <dialog ref={dialogRef} className="bg-gray-400 rounded-lg font-normal" onClose={handleClose}>
40
- <div className="bg-gray-500 text-xl flex flex-row justify-between p-3 items-start font-bold">
41
- <h2>{title}</h2>
42
- {closeAble && <button onClick={handleClose}>&times;</button>}
43
- </div>
44
- <div className="modal-body p-2">
45
- {children}
46
- </div>
47
- <div className="modal-footer px-2 py-2 flex flex-row gap-2 border-t-2">
48
- {actionbuttons.map(({ onClick, text, closeModal = true }, index) => (
49
- <button key={index} className="bg-blue-500 hover:bg-blue-600 dark:border-gray-900 rounded-md py-2 px-4 dark:text-white font-bold"
50
- onClick={() => {
51
- if (closeModal) handleClose();
52
- onClick();
53
- }}>{text}</button>
54
- ))}
55
- </div>
56
- </dialog>
57
- </>
58
- );
45
+ return (
46
+ <>
47
+ {!!buttonText && (
48
+ <button className={className} onClick={() => dialogRef.current?.showModal()}>
49
+ {buttonText}
50
+ </button>
51
+ )}
52
+ <dialog ref={dialogRef} className="bg-gray-400 rounded-lg font-normal" onClose={handleClose}>
53
+ <div className="bg-gray-500 text-xl flex flex-row justify-between p-3 items-start font-bold">
54
+ <h2>{title}</h2>
55
+ {closeAble && <button onClick={handleClose}>&times;</button>}
56
+ </div>
57
+ <div className="modal-body p-2">{children}</div>
58
+ <div className="modal-footer px-2 py-2 flex flex-row gap-2 border-t-2">
59
+ {actionbuttons.map(({ onClick, text, closeModal = true }, index) => (
60
+ <button
61
+ key={index}
62
+ className="bg-blue-500 hover:bg-blue-600 dark:border-gray-900 rounded-md py-2 px-4 dark:text-white font-bold"
63
+ onClick={() => {
64
+ if (closeModal) handleClose();
65
+ onClick();
66
+ }}
67
+ >
68
+ {text}
69
+ </button>
70
+ ))}
71
+ </div>
72
+ </dialog>
73
+ </>
74
+ );
59
75
  }
@@ -1,12 +1,12 @@
1
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";
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
10
 
11
11
  // This inplementation is rooted in the Tiptap editor basic example https://codesandbox.io/p/devbox/editor-9x9dkd
12
12
 
@@ -24,12 +24,12 @@ const EditorButton = ({ action, isActive, label, disabled }: EditorButtonProps)
24
24
  return null;
25
25
  }
26
26
 
27
- if (action.includes("heading")) {
27
+ if (action.includes('heading')) {
28
28
  const level = parseInt(action[action.length - 1]);
29
29
  return (
30
30
  <button
31
31
  onClick={() => editor.chain().focus().toggleHeading({ level: level }).run()}
32
- className={`pl-2 ${isActive ? "is-active" : ""}`}
32
+ className={`pl-2 ${isActive ? 'is-active' : ''}`}
33
33
  >
34
34
  {label}
35
35
  </button>
@@ -40,7 +40,7 @@ const EditorButton = ({ action, isActive, label, disabled }: EditorButtonProps)
40
40
  <button
41
41
  onClick={() => editor.chain().focus()[action]().run()}
42
42
  disabled={disabled ? !editor.can().chain().focus()[action]().run() : false}
43
- className={`pl-2 ${isActive ? "is-active" : ""}`}
43
+ className={`pl-2 ${isActive ? 'is-active' : ''}`}
44
44
  >
45
45
  {label}
46
46
  </button>
@@ -56,18 +56,46 @@ const MenuBar = () => {
56
56
 
57
57
  return (
58
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 action='setHeading1' isActive={editor.isActive("heading", { level: 1 })} label={<LuHeading1 size={"24px"} />} />
65
- <EditorButton action='setHeading2' isActive={editor.isActive("heading", { level: 2 })} label={<LuHeading2 size={"24px"} />} />
66
- <EditorButton action='setHeading3' isActive={editor.isActive("heading", { level: 3 })} label={<LuHeading3 size={"24px"} />} />
67
- <EditorButton action="toggleBulletList" isActive={editor.isActive("bulletList")} label={<AiOutlineUnorderedList size={"24px"} />} />
68
- <EditorButton action="toggleOrderedList" isActive={editor.isActive("orderedList")} label={<GoListOrdered size={"24px"} />} />
69
- <EditorButton action="toggleCodeBlock" isActive={editor.isActive("codeBlock")} label={<PiCodeBlock size={"24px"} />} />
70
- <EditorButton action="toggleBlockquote" isActive={editor.isActive("blockquote")} label={<TbBlockquote size={"24px"} />} />
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
+ />
71
99
  </div>
72
100
  );
73
101
  };
@@ -76,12 +104,12 @@ const extensions = [
76
104
  StarterKit.configure({
77
105
  bulletList: {
78
106
  HTMLAttributes: {
79
- class: "list-disc list-inside dark:text-white p-1 mt-1 [&_li]:mb-1 [&_p]:inline m-0",
107
+ class: 'list-disc list-inside dark:text-white p-1 mt-1 [&_li]:mb-1 [&_p]:inline m-0',
80
108
  },
81
109
  },
82
110
  orderedList: {
83
111
  HTMLAttributes: {
84
- className: "list-decimal list-inside dark:text-white p-1 mt-1 [&_li]:mb-1 [&_p]:inline m-0",
112
+ className: 'list-decimal list-inside dark:text-white p-1 mt-1 [&_li]:mb-1 [&_p]:inline m-0',
85
113
  },
86
114
  },
87
115
  }),
@@ -97,9 +125,12 @@ interface Props {
97
125
 
98
126
  export const MarkdownEditor = (props: Props) => {
99
127
  return (
100
- <div className={"text-md border border-gray-800 overflow-hidden " + props.className} style={{ borderWidth: props.editable ? 1 : 0 }}>
128
+ <div
129
+ className={'text-md border border-gray-800 overflow-hidden ' + props.className}
130
+ style={{ borderWidth: props.editable ? 1 : 0 }}
131
+ >
101
132
  <EditorProvider
102
- key={(props.editable ? "editable" : "readonly") + props.content}
133
+ key={(props.editable ? 'editable' : 'readonly') + props.content}
103
134
  slotBefore={props.editable ? <MenuBar /> : null}
104
135
  extensions={extensions}
105
136
  content={props.content}
@@ -1,22 +1,29 @@
1
1
  import React from 'react';
2
2
 
3
3
  interface SpinnerProps {
4
- text?: string;
5
- size?: string
6
- className?: string;
4
+ text?: string;
5
+ size?: string;
6
+ className?: string;
7
7
  }
8
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" fill="none" viewBox="0 0 24 24">
16
- <circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
17
- <path className="opacity-75" fill="currentColor" 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"></path>
18
- </svg>
19
- {text && <span className="">{text}</span>}
20
- </div>
21
- );
22
- };
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
+ };
@@ -8,89 +8,89 @@ import { useRimori } from '../../components';
8
8
  import { FirstMessages, getFirstMessages } from './utils';
9
9
 
10
10
  interface Props {
11
- voiceId: any;
12
- avatarImageUrl: string;
13
- onComplete: (result: any) => void;
14
- autoStartConversation?: FirstMessages;
11
+ voiceId: any;
12
+ avatarImageUrl: string;
13
+ onComplete: (result: any) => void;
14
+ autoStartConversation?: FirstMessages;
15
15
  }
16
16
 
17
17
  export function AssistantChat({ avatarImageUrl, voiceId, onComplete, autoStartConversation }: Props) {
18
- const [oralCommunication, setOralCommunication] = React.useState(true);
19
- const { ai: llm, event } = useRimori();
20
- const sender = useMemo(() => new MessageSender(llm.getVoice, voiceId), []);
21
- const { messages, append, isLoading, setMessages } = useChat();
18
+ const [oralCommunication, setOralCommunication] = React.useState(true);
19
+ const { ai: llm, event } = useRimori();
20
+ const sender = useMemo(() => new MessageSender(llm.getVoice, voiceId), []);
21
+ const { messages, append, isLoading, setMessages } = useChat();
22
22
 
23
- const lastAssistantMessage = [...messages].filter((m) => m.role === 'assistant').pop()?.content;
23
+ const lastAssistantMessage = [...messages].filter((m) => m.role === 'assistant').pop()?.content;
24
24
 
25
- useEffect(() => {
26
- sender.setOnLoudnessChange((value: number) => event.emit('self.avatar.triggerLoudness', { loudness: value }));
25
+ useEffect(() => {
26
+ sender.setOnLoudnessChange((value: number) => event.emit('self.avatar.triggerLoudness', { loudness: value }));
27
27
 
28
- if (!autoStartConversation) {
29
- return;
30
- }
31
-
32
- setMessages(getFirstMessages(autoStartConversation));
33
- // append([{ role: 'user', content: autoStartConversation.userMessage }]);
28
+ if (!autoStartConversation) {
29
+ return;
30
+ }
34
31
 
35
- if (autoStartConversation.assistantMessage) {
36
- // console.log("autostartmessages", { autoStartConversation, isLoading });
37
- sender.handleNewText(autoStartConversation.assistantMessage, isLoading);
38
- }
39
- }, []);
32
+ setMessages(getFirstMessages(autoStartConversation));
33
+ // append([{ role: 'user', content: autoStartConversation.userMessage }]);
40
34
 
41
- useEffect(() => {
42
- let message = lastAssistantMessage;
43
- if (message !== messages[messages.length - 1]?.content) {
44
- message = undefined;
45
- }
46
- sender.handleNewText(message, isLoading);
47
- }, [messages, isLoading]);
35
+ if (autoStartConversation.assistantMessage) {
36
+ // console.log("autostartmessages", { autoStartConversation, isLoading });
37
+ sender.handleNewText(autoStartConversation.assistantMessage, isLoading);
38
+ }
39
+ }, []);
48
40
 
49
- const lastMessage = messages[messages.length - 1];
41
+ useEffect(() => {
42
+ let message = lastAssistantMessage;
43
+ if (message !== messages[messages.length - 1]?.content) {
44
+ message = undefined;
45
+ }
46
+ sender.handleNewText(message, isLoading);
47
+ }, [messages, isLoading]);
50
48
 
51
- useEffect(() => {
52
- console.log("lastMessage", lastMessage);
53
- const toolInvocations = lastMessage?.toolCalls;
54
- if (toolInvocations && toolInvocations.length > 0) {
55
- console.log("toolInvocations", toolInvocations);
56
- onComplete(toolInvocations[0].args);
57
- }
58
- }, [lastMessage]);
49
+ const lastMessage = messages[messages.length - 1];
59
50
 
60
- if (lastMessage?.toolCalls && lastMessage.toolCalls.length > 0) {
61
- console.log("lastMessage test2", lastMessage);
62
- const args = lastMessage.toolCalls[0].args;
51
+ useEffect(() => {
52
+ console.log('lastMessage', lastMessage);
53
+ const toolInvocations = lastMessage?.toolCalls;
54
+ if (toolInvocations && toolInvocations.length > 0) {
55
+ console.log('toolInvocations', toolInvocations);
56
+ onComplete(toolInvocations[0].args);
57
+ }
58
+ }, [lastMessage]);
63
59
 
64
- const success = args.explanationUnderstood === "TRUE" || args.studentKnowsTopic === "TRUE";
60
+ if (lastMessage?.toolCalls && lastMessage.toolCalls.length > 0) {
61
+ console.log('lastMessage test2', lastMessage);
62
+ const args = lastMessage.toolCalls[0].args;
65
63
 
66
- return <div className="px-5 pt-5 overflow-y-auto text-center" style={{ height: "478px" }}>
67
- <h1 className='text-center mt-5 mb-5'>
68
- {success ? "Great job!" : "You failed"}
69
- </h1>
70
- <p>{args.improvementHints}</p>
71
- </div>
72
- }
64
+ const success = args.explanationUnderstood === 'TRUE' || args.studentKnowsTopic === 'TRUE';
73
65
 
74
66
  return (
75
- <div>
76
- {oralCommunication && <CircleAudioAvatar imageUrl={avatarImageUrl} className='mx-auto my-10' />}
77
- <div className="w-full">
78
- {lastAssistantMessage && <div className="px-5 pt-5 overflow-y-auto remirror-theme" style={{ height: "4k78px" }}>
79
- <Markdown>{lastAssistantMessage}</Markdown>
80
- </div>}
81
- </div>
82
- <AudioInputField
83
- blockSubmission={isLoading}
84
- onSubmit={message => {
85
- append([{ role: 'user', content: message, id: messages.length.toString() }]);
86
- }}
87
- onAudioControl={voice => {
88
- setOralCommunication(voice);
89
- sender.setVolume(voice ? 1 : 0);
90
- }} />
91
- </div>
67
+ <div className="px-5 pt-5 overflow-y-auto text-center" style={{ height: '478px' }}>
68
+ <h1 className="text-center mt-5 mb-5">{success ? 'Great job!' : 'You failed'}</h1>
69
+ <p>{args.improvementHints}</p>
70
+ </div>
92
71
  );
93
- };
94
-
95
-
96
-
72
+ }
73
+
74
+ return (
75
+ <div>
76
+ {oralCommunication && <CircleAudioAvatar imageUrl={avatarImageUrl} className="mx-auto my-10" />}
77
+ <div className="w-full">
78
+ {lastAssistantMessage && (
79
+ <div className="px-5 pt-5 overflow-y-auto remirror-theme" style={{ height: '4k78px' }}>
80
+ <Markdown>{lastAssistantMessage}</Markdown>
81
+ </div>
82
+ )}
83
+ </div>
84
+ <AudioInputField
85
+ blockSubmission={isLoading}
86
+ onSubmit={(message) => {
87
+ append([{ role: 'user', content: message, id: messages.length.toString() }]);
88
+ }}
89
+ onAudioControl={(voice) => {
90
+ setOralCommunication(voice);
91
+ sender.setVolume(voice ? 1 : 0);
92
+ }}
93
+ />
94
+ </div>
95
+ );
96
+ }
@@ -9,7 +9,7 @@ import { getFirstMessages } from './utils';
9
9
  import { FirstMessages } from './utils';
10
10
 
11
11
  interface Props {
12
- voiceId: any;
12
+ voiceId: string;
13
13
  agentTools: Tool[];
14
14
  avatarImageUrl: string;
15
15
  circleSize?: string;
@@ -26,8 +26,8 @@ export function Avatar({
26
26
  autoStartConversation,
27
27
  children,
28
28
  isDarkTheme = false,
29
- circleSize = "300px",
30
- className
29
+ circleSize = '300px',
30
+ className,
31
31
  }: Props) {
32
32
  const { ai, event } = useRimori();
33
33
  const [agentReplying, setAgentReplying] = useState(false);
@@ -36,7 +36,7 @@ export function Avatar({
36
36
  const { messages, append, isLoading, lastMessage, setMessages } = useChat(agentTools);
37
37
 
38
38
  useEffect(() => {
39
- console.log("messages", messages);
39
+ console.log('messages', messages);
40
40
  }, [messages]);
41
41
 
42
42
  useEffect(() => {
@@ -74,23 +74,26 @@ export function Avatar({
74
74
 
75
75
  return (
76
76
  <div className={`md:pb-8 ${className || ''}`}>
77
- <CircleAudioAvatar
78
- width={circleSize}
79
- className='mx-auto'
80
- imageUrl={avatarImageUrl}
81
- isDarkTheme={isDarkTheme} />
77
+ <CircleAudioAvatar width={circleSize} className="mx-auto" imageUrl={avatarImageUrl} isDarkTheme={isDarkTheme} />
82
78
  {children}
83
79
  <VoiceRecorder
84
- iconSize='30'
85
- className='w-16 h-16 shadow-lg rounded-full bg-gray-400 dark:bg-gray-800'
80
+ iconSize="30"
81
+ className="w-16 h-16 shadow-lg rounded-full bg-gray-400 dark:bg-gray-800"
86
82
  disabled={agentReplying}
87
83
  loading={isProcessingMessage}
88
84
  enablePushToTalk={true}
89
85
  onVoiceRecorded={(message) => {
90
86
  setAgentReplying(true);
91
- append([{ role: 'user', content: "Message(" + Math.floor((messages.length + 1) / 2) + "): " + message, id: messages.length.toString() }]);
87
+ append([
88
+ {
89
+ role: 'user',
90
+ content: 'Message(' + Math.floor((messages.length + 1) / 2) + '): ' + message,
91
+ id: messages.length.toString(),
92
+ },
93
+ ]);
92
94
  }}
93
- onRecordingStatusChange={(running) => !running && setIsProcessingMessage(true)} />
95
+ onRecordingStatusChange={(running) => !running && setIsProcessingMessage(true)}
96
+ />
94
97
  </div>
95
98
  );
96
- };
99
+ }