@rimori/client 1.0.3 → 1.0.4

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 +51 -0
  2. package/dist/components/CRUDModal.js +0 -1
  3. package/dist/components/ai/Assistant.d.ts +9 -0
  4. package/dist/components/ai/Assistant.js +59 -0
  5. package/dist/components/ai/Avatar.d.ts +11 -0
  6. package/dist/components/ai/Avatar.js +39 -0
  7. package/dist/components/ai/EmbeddedAssistent/AudioInputField.d.ts +7 -0
  8. package/dist/components/ai/EmbeddedAssistent/AudioInputField.js +38 -0
  9. package/dist/components/ai/EmbeddedAssistent/CircleAudioAvatar.d.ts +7 -0
  10. package/dist/components/ai/EmbeddedAssistent/CircleAudioAvatar.js +59 -0
  11. package/dist/components/ai/EmbeddedAssistent/TTS/MessageSender.d.ts +19 -0
  12. package/dist/components/ai/EmbeddedAssistent/TTS/MessageSender.js +86 -0
  13. package/dist/components/ai/EmbeddedAssistent/TTS/Player.d.ts +25 -0
  14. package/dist/components/ai/EmbeddedAssistent/TTS/Player.js +180 -0
  15. package/dist/components/ai/EmbeddedAssistent/VoiceRecoder.d.ts +7 -0
  16. package/dist/components/ai/EmbeddedAssistent/VoiceRecoder.js +45 -0
  17. package/dist/components/ai/utils.d.ts +6 -0
  18. package/dist/components/ai/utils.js +14 -0
  19. package/dist/components/audio/Playbutton.js +4 -5
  20. package/dist/components/avatar/Assistant.d.ts +9 -0
  21. package/dist/components/avatar/Assistant.js +59 -0
  22. package/dist/components/avatar/Avatar.d.ts +12 -0
  23. package/dist/components/avatar/Avatar.js +42 -0
  24. package/dist/components/avatar/EmbeddedAssistent/AudioInputField.d.ts +7 -0
  25. package/dist/components/avatar/EmbeddedAssistent/AudioInputField.js +38 -0
  26. package/dist/components/avatar/EmbeddedAssistent/CircleAudioAvatar.d.ts +7 -0
  27. package/dist/components/avatar/EmbeddedAssistent/CircleAudioAvatar.js +59 -0
  28. package/dist/components/avatar/EmbeddedAssistent/TTS/MessageSender.d.ts +19 -0
  29. package/dist/components/avatar/EmbeddedAssistent/TTS/MessageSender.js +84 -0
  30. package/dist/components/avatar/EmbeddedAssistent/TTS/Player.d.ts +25 -0
  31. package/dist/components/avatar/EmbeddedAssistent/TTS/Player.js +180 -0
  32. package/dist/components/avatar/EmbeddedAssistent/VoiceRecoder.d.ts +7 -0
  33. package/dist/components/avatar/EmbeddedAssistent/VoiceRecoder.js +45 -0
  34. package/dist/components/avatar/utils.d.ts +6 -0
  35. package/dist/components/avatar/utils.js +14 -0
  36. package/dist/components.d.ts +9 -0
  37. package/dist/components.js +10 -0
  38. package/dist/controller/AIController.d.ts +4 -3
  39. package/dist/controller/AIController.js +32 -8
  40. package/dist/controller/ObjectController.d.ts +2 -2
  41. package/dist/controller/ObjectController.js +4 -5
  42. package/dist/controller/SettingsController.d.ts +2 -1
  43. package/dist/controller/SettingsController.js +9 -0
  44. package/dist/controller/SharedContentController.js +6 -6
  45. package/dist/core.d.ts +9 -0
  46. package/dist/core.js +10 -0
  47. package/dist/hooks/UseChatHook.js +2 -2
  48. package/dist/index.d.ts +3 -2
  49. package/dist/index.js +4 -2
  50. package/dist/plugin/PluginController.d.ts +4 -12
  51. package/dist/plugin/PluginController.js +43 -70
  52. package/dist/plugin/RimoriClient.d.ts +85 -32
  53. package/dist/plugin/RimoriClient.js +98 -77
  54. package/dist/plugin/fromRimori/EventBus.d.ts +98 -0
  55. package/dist/plugin/fromRimori/EventBus.js +240 -0
  56. package/dist/providers/PluginProvider.d.ts +1 -0
  57. package/dist/providers/PluginProvider.js +10 -12
  58. package/dist/worker/WorkerSetup.d.ts +6 -0
  59. package/dist/worker/WorkerSetup.js +79 -0
  60. package/package.json +16 -3
  61. package/src/components/CRUDModal.tsx +1 -3
  62. package/src/components/ai/Assistant.tsx +96 -0
  63. package/src/components/ai/Avatar.tsx +61 -0
  64. package/src/components/ai/EmbeddedAssistent/AudioInputField.tsx +64 -0
  65. package/src/components/ai/EmbeddedAssistent/CircleAudioAvatar.tsx +75 -0
  66. package/src/components/ai/EmbeddedAssistent/TTS/MessageSender.ts +91 -0
  67. package/src/components/ai/EmbeddedAssistent/TTS/Player.ts +192 -0
  68. package/src/components/ai/EmbeddedAssistent/VoiceRecoder.tsx +56 -0
  69. package/src/components/ai/utils.ts +23 -0
  70. package/src/components/audio/Playbutton.tsx +4 -5
  71. package/src/components.ts +10 -0
  72. package/src/controller/AIController.ts +84 -60
  73. package/src/controller/ObjectController.ts +4 -6
  74. package/src/controller/SettingsController.ts +9 -1
  75. package/src/controller/SharedContentController.ts +6 -6
  76. package/src/core.ts +10 -0
  77. package/src/hooks/UseChatHook.ts +2 -2
  78. package/src/index.ts +4 -2
  79. package/src/plugin/PluginController.ts +46 -76
  80. package/src/plugin/RimoriClient.ts +147 -85
  81. package/src/plugin/fromRimori/EventBus.ts +301 -0
  82. package/src/plugin/fromRimori/readme.md +2 -0
  83. package/src/providers/PluginProvider.tsx +12 -14
  84. package/src/worker/WorkerSetup.ts +80 -0
  85. package/dist/CRUDModal.d.ts +0 -16
  86. package/dist/CRUDModal.js +0 -31
  87. package/dist/MarkdownEditor.d.ts +0 -8
  88. package/dist/MarkdownEditor.js +0 -46
  89. package/dist/audio/Playbutton.d.ts +0 -14
  90. package/dist/audio/Playbutton.js +0 -73
  91. package/dist/components/hooks/UseChatHook.d.ts +0 -15
  92. package/dist/components/hooks/UseChatHook.js +0 -21
  93. package/dist/controller/PluginController.d.ts +0 -14
  94. package/dist/controller/PluginController.js +0 -30
  95. package/dist/plugin/AIController copy.d.ts +0 -22
  96. package/dist/plugin/AIController copy.js +0 -68
  97. package/dist/plugin/AIController.d.ts +0 -22
  98. package/dist/plugin/AIController.js +0 -68
  99. package/dist/plugin/ObjectController.d.ts +0 -34
  100. package/dist/plugin/ObjectController.js +0 -77
  101. package/dist/plugin/SettingController.d.ts +0 -13
  102. package/dist/plugin/SettingController.js +0 -55
  103. package/dist/plugin/VoiceController.d.ts +0 -2
  104. package/dist/plugin/VoiceController.js +0 -27
  105. package/dist/providers/EventEmitter.d.ts +0 -11
  106. package/dist/providers/EventEmitter.js +0 -41
  107. package/dist/providers/EventEmitterContext.d.ts +0 -6
  108. package/dist/providers/EventEmitterContext.js +0 -19
  109. package/dist/utils/DifficultyConverter.d.ts +0 -3
  110. package/dist/utils/DifficultyConverter.js +0 -7
  111. package/dist/utils/constants.d.ts +0 -4
  112. package/dist/utils/constants.js +0 -12
  113. package/dist/utils/plugin/Client.d.ts +0 -72
  114. package/dist/utils/plugin/Client.js +0 -118
  115. package/dist/utils/plugin/PluginController.d.ts +0 -36
  116. package/dist/utils/plugin/PluginController.js +0 -119
  117. package/dist/utils/plugin/PluginUtils.d.ts +0 -2
  118. package/dist/utils/plugin/PluginUtils.js +0 -23
  119. package/dist/utils/plugin/RimoriClient.d.ts +0 -72
  120. package/dist/utils/plugin/RimoriClient.js +0 -118
  121. package/dist/utils/plugin/ThemeSetter.d.ts +0 -1
  122. package/dist/utils/plugin/ThemeSetter.js +0 -13
  123. package/dist/utils/plugin/WhereClauseBuilder.d.ts +0 -24
  124. package/dist/utils/plugin/WhereClauseBuilder.js +0 -79
  125. package/dist/utils/plugin/providers/EventEmitter.d.ts +0 -11
  126. package/dist/utils/plugin/providers/EventEmitter.js +0 -41
  127. package/dist/utils/plugin/providers/EventEmitterContext.d.ts +0 -6
  128. package/dist/utils/plugin/providers/EventEmitterContext.js +0 -19
  129. package/dist/utils/plugin/providers/PluginProvider.d.ts +0 -8
  130. package/dist/utils/plugin/providers/PluginProvider.js +0 -49
  131. package/src/providers/EventEmitter.ts +0 -48
  132. package/src/providers/EventEmitterContext.tsx +0 -27
  133. package/src/utils/constants.ts +0 -18
@@ -0,0 +1,240 @@
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 EventBusHandler {
11
+ constructor() {
12
+ this.listeners = new Map();
13
+ this.responseResolvers = new Map();
14
+ this.debugEnabled = false;
15
+ this.evName = "";
16
+ //private constructor
17
+ }
18
+ static getInstance(name) {
19
+ if (!EventBusHandler.instance) {
20
+ EventBusHandler.instance = new EventBusHandler();
21
+ EventBusHandler.instance.on("global.system.requestDebug", () => {
22
+ EventBusHandler.instance.debugEnabled = true;
23
+ console.log(`[${EventBusHandler.instance.evName}] Debug mode enabled. Make sure debugging messages are enabled in the browser console.`);
24
+ });
25
+ }
26
+ if (name && EventBusHandler.instance.evName === "") {
27
+ EventBusHandler.instance.evName = name;
28
+ }
29
+ return EventBusHandler.instance;
30
+ }
31
+ createEvent(sender, topic, data, eventId) {
32
+ const generatedEventId = eventId || Math.floor(Math.random() * 10000000000);
33
+ return {
34
+ eventId: generatedEventId,
35
+ timestamp: new Date().toISOString(),
36
+ sender,
37
+ topic,
38
+ data,
39
+ debug: this.debugEnabled,
40
+ };
41
+ }
42
+ /**
43
+ * Emits an event to the event bus. Can be a new event or a response to a request.
44
+ * @param sender - The sender of the event.
45
+ * @param topic - The topic of the event.
46
+ * @param data - The data of the event.
47
+ * @param eventId - The event id of the event.
48
+ *
49
+ * The topic format is: **pluginId.area.action**
50
+ *
51
+ * Example topics:
52
+ * - pl1234.card.requestHard
53
+ * - pl1234.card.requestNew
54
+ * - pl1234.card.requestAll
55
+ * - pl1234.card.create
56
+ * - pl1234.card.update
57
+ * - pl1234.card.delete
58
+ * - pl1234.card.triggerBackup
59
+ */
60
+ emit(sender, topic, data, eventId) {
61
+ this.emitInternal(sender, topic, data || {}, eventId);
62
+ }
63
+ emitInternal(sender, topic, data, eventId, skipResponseTrigger = false) {
64
+ if (!this.validateTopic(topic)) {
65
+ this.logAndThrowError(false, `Invalid topic: ` + topic);
66
+ return;
67
+ }
68
+ const event = this.createEvent(sender, topic, data, eventId);
69
+ const handlers = this.getMatchingHandlers(event.topic);
70
+ handlers.forEach(handler => {
71
+ if (handler.ignoreSender && handler.ignoreSender.includes(sender)) {
72
+ // console.log("ignore event as its in the ignoreSender list", { event, ignoreList: handler.ignoreSender });
73
+ return;
74
+ }
75
+ handler.handler(event);
76
+ });
77
+ this.logIfDebug(`Emitting event to ` + topic, event);
78
+ if (handlers.size === 0) {
79
+ this.logAndThrowError(false, `No handlers found for topic: ` + topic);
80
+ }
81
+ // If it's a response to a request
82
+ if (eventId && this.responseResolvers.has(eventId) && !skipResponseTrigger) {
83
+ // console.log("[Rimori] Resolving response to request: " + eventId, event.data);
84
+ this.responseResolvers.get(eventId)(event);
85
+ this.responseResolvers.delete(eventId);
86
+ }
87
+ }
88
+ /**
89
+ * Subscribes to an event on the event bus.
90
+ * @param topics - The topic of the event.
91
+ * @param handler - The handler to be called when the event is emitted.
92
+ * @param ignoreSender - The senders to ignore.
93
+ * @returns The ids of the listeners.
94
+ */
95
+ on(topics, handler, ignoreSender = []) {
96
+ return this.toArray(topics).map(topic => {
97
+ if (!this.validateTopic(topic)) {
98
+ this.logAndThrowError(true, `Invalid topic: ` + topic);
99
+ }
100
+ if (!this.listeners.has(topic)) {
101
+ this.listeners.set(topic, new Set());
102
+ }
103
+ const id = Math.floor(Math.random() * 10000000000);
104
+ // Type assertion to handle the generic type mismatch
105
+ const eventHandler = handler;
106
+ this.listeners.get(topic).add({ id, handler: eventHandler, ignoreSender });
107
+ this.logIfDebug(`Subscribed to ` + topic, { listenerId: id, ignoreSender });
108
+ return btoa(JSON.stringify({ topic, id }));
109
+ });
110
+ }
111
+ /**
112
+ * Subscribes to an event, processes the data and emits a response on the event bus.
113
+ * @param sender - The sender of the event.
114
+ * @param topic - The topic of the event.
115
+ * @param handler - The handler to be called when the event is received. The handler returns the data to be emitted. Can be a static object or a function.
116
+ * @returns The ids of the listeners.
117
+ */
118
+ respond(sender, topic, handler) {
119
+ const ids = this.on(topic, (data) => __awaiter(this, void 0, void 0, function* () {
120
+ const response = typeof handler === "function" ? yield handler(data) : handler;
121
+ this.emit(sender, topic, response, data.eventId);
122
+ }), [sender]);
123
+ this.logIfDebug(`Added respond listener ` + sender + " to topic " + topic, { listenerIds: ids, sender });
124
+ return ids;
125
+ }
126
+ /**
127
+ * Subscribes to an event on the event bus. The handler will be called once and then removed.
128
+ * @param topic - The topic of the event.
129
+ * @param handler - The handler to be called when the event is emitted.
130
+ */
131
+ once(topic, handler) {
132
+ if (!this.validateTopic(topic)) {
133
+ this.logAndThrowError(false, `Invalid topic: ` + topic);
134
+ return;
135
+ }
136
+ let ids = [];
137
+ const wrapper = (event) => {
138
+ handler(event);
139
+ this.off(ids);
140
+ };
141
+ ids = this.on(topic, wrapper);
142
+ this.logIfDebug(`Added once listener ` + topic, { listenerIds: ids, topic });
143
+ }
144
+ /**
145
+ * Unsubscribes from an event on the event bus.
146
+ * @param listenerIds - The ids of the listeners to unsubscribe from.
147
+ */
148
+ off(listenerIds) {
149
+ this.toArray(listenerIds).forEach(fullId => {
150
+ const { topic, id } = JSON.parse(atob(fullId));
151
+ const listeners = this.listeners.get(topic) || new Set();
152
+ listeners.forEach(listener => {
153
+ if (listener.id === Number(id)) {
154
+ listeners.delete(listener);
155
+ this.logIfDebug(`Removed listener ` + fullId, { topic, listenerId: id });
156
+ }
157
+ });
158
+ });
159
+ }
160
+ toArray(item) {
161
+ return Array.isArray(item) ? item : [item];
162
+ }
163
+ /**
164
+ * Requests data from the event bus.
165
+ * @param sender - The sender of the event.
166
+ * @param topic - The topic of the event.
167
+ * @param data - The data of the event.
168
+ * @returns A promise that resolves to the event.
169
+ */
170
+ request(sender, topic, data) {
171
+ return __awaiter(this, void 0, void 0, function* () {
172
+ if (!this.validateTopic(topic)) {
173
+ this.logAndThrowError(true, `Invalid topic: ` + topic);
174
+ }
175
+ const event = this.createEvent(sender, topic, data || {});
176
+ this.logIfDebug(`Requesting data from ` + topic, { event });
177
+ return new Promise(resolve => {
178
+ this.responseResolvers.set(event.eventId, (value) => resolve(value));
179
+ this.emitInternal(sender, topic, data || {}, event.eventId, true);
180
+ });
181
+ });
182
+ }
183
+ /**
184
+ * Gets the matching handlers for an event.
185
+ * @param topic - The topic of the event.
186
+ * @returns A set of handlers that match the event type.
187
+ */
188
+ getMatchingHandlers(topic) {
189
+ const exact = this.listeners.get(topic) || new Set();
190
+ // Find wildcard matches
191
+ const wildcard = [...this.listeners.entries()]
192
+ .filter(([key]) => key.endsWith("*") && topic.startsWith(key.slice(0, -1)))
193
+ .flatMap(([_, handlers]) => [...handlers]);
194
+ return new Set([...exact, ...wildcard]);
195
+ }
196
+ /**
197
+ * Validates the topic of an event.
198
+ * @param topic - The topic of the event.
199
+ * @returns True if the topic is valid, false otherwise.
200
+ */
201
+ validateTopic(topic) {
202
+ // Split event type into parts
203
+ const parts = topic.split(".");
204
+ const [plugin, area, action] = parts;
205
+ if (parts.length !== 3) {
206
+ if (parts.length === 1 && plugin === "*") {
207
+ return true;
208
+ }
209
+ if (parts.length === 2 && plugin !== "*" && area === "*") {
210
+ return true;
211
+ }
212
+ this.logAndThrowError(false, `Event type must have 3 parts separated by dots. Received: ` + topic);
213
+ return false;
214
+ }
215
+ if (action === "*") {
216
+ return true;
217
+ }
218
+ // Validate action part
219
+ const validActions = ["request", "create", "update", "delete", "trigger"];
220
+ if (validActions.some(a => action.startsWith(a))) {
221
+ return true;
222
+ }
223
+ this.logAndThrowError(false, `Invalid event topic name. The action: ` + action + ". Must be or start with one of: " + validActions.join(", "));
224
+ return false;
225
+ }
226
+ logIfDebug(...args) {
227
+ if (this.debugEnabled) {
228
+ console.debug(`[${this.evName}] ` + args[0], ...args.slice(1));
229
+ }
230
+ }
231
+ logAndThrowError(throwError, ...args) {
232
+ const message = `[${this.evName}] ` + args[0];
233
+ console.error(message, ...args.slice(1));
234
+ if (throwError) {
235
+ throw new Error(message);
236
+ }
237
+ }
238
+ }
239
+ EventBusHandler.instance = null;
240
+ export const EventBus = EventBusHandler.getInstance();
@@ -2,6 +2,7 @@ import React, { ReactNode } from 'react';
2
2
  import { RimoriClient } from '../plugin/RimoriClient';
3
3
  interface PluginProviderProps {
4
4
  children: ReactNode;
5
+ pluginId: string;
5
6
  }
6
7
  export declare const PluginProvider: React.FC<PluginProviderProps>;
7
8
  export declare const usePlugin: () => RimoriClient;
@@ -1,8 +1,10 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { createContext, useContext, useEffect, useState } from 'react';
3
3
  import { PluginController } from '../plugin/PluginController';
4
+ import { EventBusHandler } from '../plugin/fromRimori/EventBus';
5
+ EventBusHandler.getInstance("Plugin EventBus");
4
6
  const PluginContext = createContext(null);
5
- export const PluginProvider = ({ children }) => {
7
+ export const PluginProvider = ({ children, pluginId }) => {
6
8
  const [plugin, setPlugin] = useState(null);
7
9
  const [contextMenuOnSelect, setContextMenuOnTextSelection] = useState(false);
8
10
  //route change
@@ -12,21 +14,17 @@ export const PluginProvider = ({ children }) => {
12
14
  if (lastHash !== window.location.hash) {
13
15
  lastHash = window.location.hash;
14
16
  console.log('url changed:', lastHash);
15
- plugin === null || plugin === void 0 ? void 0 : plugin.emit('urlChange', window.location.hash);
17
+ plugin === null || plugin === void 0 ? void 0 : plugin.event.emit('session.triggerUrlChange', window.location.hash);
16
18
  }
17
19
  }, 100);
18
- PluginController.getInstance().then(setPlugin);
20
+ PluginController.getInstance(pluginId).then(setPlugin);
19
21
  }, []);
20
22
  //check if context menu opens on text selection
21
23
  useEffect(() => {
22
24
  if (!plugin)
23
25
  return;
24
- plugin.getSettings({
25
- languageLevel: "A1",
26
- motherTongue: "English",
27
- contextMenuOnSelect: false,
28
- }, "user").then((settings) => {
29
- setContextMenuOnTextSelection(settings.contextMenuOnSelect);
26
+ plugin.plugin.getUserInfo().then((userInfo) => {
27
+ setContextMenuOnTextSelection(userInfo.contextMenuOnSelect);
30
28
  }).catch(error => {
31
29
  console.error('Error fetching settings:', error);
32
30
  });
@@ -34,7 +32,7 @@ export const PluginProvider = ({ children }) => {
34
32
  //detect page height change
35
33
  useEffect(() => {
36
34
  const body = document.body;
37
- const handleResize = () => plugin === null || plugin === void 0 ? void 0 : plugin.emit('heightAdjustment', body.clientHeight);
35
+ const handleResize = () => plugin === null || plugin === void 0 ? void 0 : plugin.event.emit('session.triggerHeightChange', body.clientHeight);
38
36
  body.addEventListener('resize', handleResize);
39
37
  handleResize();
40
38
  return () => body.removeEventListener('resize', handleResize);
@@ -55,7 +53,7 @@ export const PluginProvider = ({ children }) => {
55
53
  if (selection) {
56
54
  e.preventDefault();
57
55
  // console.log('context menu handled', selection);
58
- plugin === null || plugin === void 0 ? void 0 : plugin.emit('contextMenu', { text: selection, x: e.clientX, y: e.clientY, open: true });
56
+ plugin === null || plugin === void 0 ? void 0 : plugin.event.emit('global.contextMenu.trigger', { text: selection, x: e.clientX, y: e.clientY, open: true });
59
57
  }
60
58
  };
61
59
  const handleSelectionChange = () => {
@@ -64,7 +62,7 @@ export const PluginProvider = ({ children }) => {
64
62
  const selection = (_a = window.getSelection()) === null || _a === void 0 ? void 0 : _a.toString().trim();
65
63
  const open = !!selection && isSelecting;
66
64
  // console.log('Selection change, contextMenuOnSelect:', contextMenuOnSelect);
67
- plugin === null || plugin === void 0 ? void 0 : plugin.emit('contextMenu', { text: selection, x: lastMouseX, y: lastMouseY, open });
65
+ plugin === null || plugin === void 0 ? void 0 : plugin.event.emit('global.contextMenu.trigger', { text: selection, x: lastMouseX, y: lastMouseY, open });
68
66
  // }
69
67
  };
70
68
  const handleMouseUpDown = (e) => {
@@ -0,0 +1,6 @@
1
+ import { RimoriClient } from "../plugin/RimoriClient";
2
+ /**
3
+ * Sets up the web worker for the plugin to be able receive and send messages to Rimori.
4
+ * @param init - The function containing the subscription logic.
5
+ */
6
+ export declare function setupWorker(init: (controller: RimoriClient) => void | Promise<void>): void;
@@ -0,0 +1,79 @@
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 { PluginController } from "../plugin/PluginController";
11
+ import { EventBus, EventBusHandler } from "../plugin/fromRimori/EventBus";
12
+ let controller = null;
13
+ const listeners = [];
14
+ let debugEnabled = false;
15
+ /**
16
+ * Sets up the web worker for the plugin to be able receive and send messages to Rimori.
17
+ * @param init - The function containing the subscription logic.
18
+ */
19
+ export function setupWorker(init) {
20
+ // Mock of the window object for the worker context to be able to use the PluginController.
21
+ const mockWindow = {
22
+ location: { search: '?secret=123' },
23
+ parent: {
24
+ postMessage: (message) => {
25
+ message.event.sender = "worker." + message.event.sender;
26
+ checkDebugMode(message.event);
27
+ logIfDebug('[Worker] sending event to Rimori', message.event);
28
+ self.postMessage(message);
29
+ }
30
+ },
31
+ addEventListener: (_, listener) => {
32
+ listeners.push(listener);
33
+ },
34
+ APP_CONFIG: {
35
+ SUPABASE_URL: 'NOT_SET',
36
+ SUPABASE_ANON_KEY: 'NOT_SET',
37
+ },
38
+ };
39
+ // Assign the mock to globalThis.
40
+ Object.assign(globalThis, { window: mockWindow });
41
+ EventBusHandler.getInstance("Worker EventBus");
42
+ // Handle init message from Rimori.
43
+ self.onmessage = (response) => __awaiter(this, void 0, void 0, function* () {
44
+ checkDebugMode(response.data);
45
+ logIfDebug('[Worker] message received', response.data);
46
+ const event = response.data;
47
+ if (event.topic === 'global.worker.requestInit') {
48
+ if (!controller) {
49
+ mockWindow.APP_CONFIG.SUPABASE_URL = event.data.supabaseUrl;
50
+ mockWindow.APP_CONFIG.SUPABASE_ANON_KEY = event.data.supabaseAnonKey;
51
+ controller = yield PluginController.getInstance(event.data.pluginId);
52
+ logIfDebug('[Worker] Worker initialized.');
53
+ yield init(controller);
54
+ logIfDebug('[Worker] Plugin listeners initialized.');
55
+ }
56
+ const initEvent = {
57
+ timestamp: new Date().toISOString(),
58
+ eventId: event.eventId,
59
+ sender: "worker." + event.sender,
60
+ topic: 'global.worker.requestInit',
61
+ data: { success: true },
62
+ debug: debugEnabled
63
+ };
64
+ return self.postMessage({ secret: "123", event: initEvent });
65
+ }
66
+ listeners.forEach(listener => listener({ data: { event: response.data, secret: "123" } }));
67
+ });
68
+ }
69
+ function checkDebugMode(event) {
70
+ if (event.topic === 'global.system.requestDebug' || event.debug) {
71
+ debugEnabled = true;
72
+ EventBus.emit("worker", "global.system.requestDebug");
73
+ }
74
+ }
75
+ function logIfDebug(...args) {
76
+ if (debugEnabled) {
77
+ console.debug('[Worker] ' + args[0], ...args.slice(1));
78
+ }
79
+ }
package/package.json CHANGED
@@ -1,8 +1,22 @@
1
1
  {
2
2
  "name": "@rimori/client",
3
- "version": "1.0.3",
3
+ "version": "1.0.4",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
+ "exports": {
7
+ ".": {
8
+ "types": "./dist/index.d.ts",
9
+ "default": "./dist/index.js"
10
+ },
11
+ "./core": {
12
+ "types": "./dist/core.d.ts",
13
+ "default": "./dist/core.js"
14
+ },
15
+ "./components": {
16
+ "types": "./dist/components.d.ts",
17
+ "default": "./dist/components.js"
18
+ }
19
+ },
6
20
  "scripts": {
7
21
  "build": "tsc && sass src/style.scss:dist/style.css",
8
22
  "dev": "tsc -w",
@@ -16,7 +30,6 @@
16
30
  "@supabase/supabase-js": "^2.48.1",
17
31
  "@tiptap/react": "2.10.3",
18
32
  "@tiptap/starter-kit": "2.10.3",
19
- "ibridge-flex": "0.0.1",
20
33
  "uuid": "11.1.0",
21
34
  "react-icons": "^5.4.0",
22
35
  "tiptap-markdown": "^0.8.10",
@@ -27,4 +40,4 @@
27
40
  "@types/react-dom": "^18.0.1",
28
41
  "sass": "^1.82.0"
29
42
  }
30
- }
43
+ }
@@ -1,6 +1,4 @@
1
- "use client";
2
-
3
- import { useEffect, useState, useRef } from "react";
1
+ import { useEffect, useRef } from "react";
4
2
 
5
3
  interface Props {
6
4
  title: string;
@@ -0,0 +1,96 @@
1
+ import React, { useEffect, useMemo } from 'react';
2
+ import { CircleAudioAvatar } from './EmbeddedAssistent/CircleAudioAvatar';
3
+ import { AudioInputField } from './EmbeddedAssistent/AudioInputField';
4
+ import { MessageSender } from './EmbeddedAssistent/TTS/MessageSender';
5
+ import Markdown from 'react-markdown';
6
+ import { useChat } from '../../hooks/UseChatHook';
7
+ import { usePlugin } from '../../components';
8
+ import { FirstMessages, getFirstMessages } from './utils';
9
+
10
+ interface Props {
11
+ voiceId: any;
12
+ avatarImageUrl: string;
13
+ onComplete: (result: any) => void;
14
+ autoStartConversation?: FirstMessages;
15
+ }
16
+
17
+ export function AssistantChat({ avatarImageUrl, voiceId, onComplete, autoStartConversation }: Props) {
18
+ const [oralCommunication, setOralCommunication] = React.useState(true);
19
+ const { llm, event } = usePlugin();
20
+ const sender = useMemo(() => new MessageSender(llm.getVoice, voiceId), []);
21
+ const { messages, append, isLoading, setMessages } = useChat();
22
+
23
+ const lastAssistantMessage = [...messages].filter((m) => m.role === 'assistant').pop()?.content;
24
+
25
+ useEffect(() => {
26
+ sender.setOnLoudnessChange((value: number) => event.emit('self.avatar.triggerLoudness', value));
27
+
28
+ if (!autoStartConversation) {
29
+ return;
30
+ }
31
+
32
+ setMessages(getFirstMessages(autoStartConversation));
33
+ // append([{ role: 'user', content: autoStartConversation.userMessage }]);
34
+
35
+ if (autoStartConversation.assistantMessage) {
36
+ // console.log("autostartmessages", { autoStartConversation, isLoading });
37
+ sender.handleNewText(autoStartConversation.assistantMessage, isLoading);
38
+ }
39
+ }, []);
40
+
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]);
48
+
49
+ const lastMessage = messages[messages.length - 1];
50
+
51
+ useEffect(() => {
52
+ console.log("lastMessage", lastMessage);
53
+ const toolInvocations = lastMessage?.toolInvocations;
54
+ if (toolInvocations && toolInvocations.length > 0) {
55
+ console.log("toolInvocations", toolInvocations);
56
+ onComplete(toolInvocations[0].args);
57
+ }
58
+ }, [lastMessage]);
59
+
60
+ if (lastMessage?.toolInvocations && lastMessage.toolInvocations.length > 0) {
61
+ console.log("lastMessage test2", lastMessage);
62
+ const args = lastMessage.toolInvocations[0].args;
63
+
64
+ const success = args.explanationUnderstood === "TRUE" || args.studentKnowsTopic === "TRUE";
65
+
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
+ }
73
+
74
+ 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>
92
+ );
93
+ };
94
+
95
+
96
+
@@ -0,0 +1,61 @@
1
+ import { Tool } from '../../core';
2
+ import { useEffect, useMemo } from 'react';
3
+ import { VoiceRecorder } from './EmbeddedAssistent/VoiceRecoder';
4
+ import { MessageSender } from './EmbeddedAssistent/TTS/MessageSender';
5
+ import { CircleAudioAvatar } from './EmbeddedAssistent/CircleAudioAvatar';
6
+ import { useChat } from '../../hooks/UseChatHook';
7
+ import { usePlugin } from '../../components';
8
+ import { getFirstMessages } from './utils';
9
+ import { FirstMessages } from './utils';
10
+
11
+ interface Props {
12
+ title?: string;
13
+ voiceId: any;
14
+ avatarImageUrl: string;
15
+ agentTools: Tool[];
16
+ autoStartConversation?: FirstMessages;
17
+ }
18
+
19
+ export function Avatar({ avatarImageUrl, voiceId, title, agentTools, autoStartConversation }: Props) {
20
+ const { llm, event } = usePlugin();
21
+ const sender = useMemo(() => new MessageSender(llm.getVoice, voiceId), []);
22
+ const { messages, append, isLoading, lastMessage, setMessages } = useChat(agentTools);
23
+
24
+ useEffect(() => {
25
+ console.log("messages", messages);
26
+ }, [messages]);
27
+
28
+ useEffect(() => {
29
+ sender.setOnLoudnessChange((value: number) => event.emit('self.avatar.triggerLoudness', value));
30
+
31
+ if (!autoStartConversation) return;
32
+
33
+ setMessages(getFirstMessages(autoStartConversation));
34
+ // append([{ role: 'user', content: autoStartConversation.userMessage }]);
35
+
36
+ if (autoStartConversation.assistantMessage) {
37
+ // console.log("autostartmessages", { autoStartConversation, isLoading });
38
+ sender.handleNewText(autoStartConversation.assistantMessage, isLoading);
39
+ } else if (autoStartConversation.userMessage) {
40
+ append([{ role: 'user', content: autoStartConversation.userMessage, id: messages.length.toString() }]);
41
+ }
42
+ }, []);
43
+
44
+ useEffect(() => {
45
+ if (lastMessage?.role === 'assistant') {
46
+ sender.handleNewText(lastMessage.content, isLoading);
47
+ }
48
+ }, [lastMessage, isLoading]);
49
+
50
+ return (
51
+ <div className='pb-8'>
52
+ {title && <p className="text-center mt-5 w-3/4 mx-auto rounded-lg dark:text-gray-100">{title}</p>}
53
+ <CircleAudioAvatar imageUrl={avatarImageUrl} width={"250px"} className='mx-auto' />
54
+ <div className='w-16 h-16 flex text-4xl shadow-lg flex-row justify-center items-center rounded-full mx-auto bg-gray-400 dark:bg-gray-800'>
55
+ <VoiceRecorder className='w-7' iconSize='300' onVoiceRecorded={(message) => {
56
+ append([{ role: 'user', content: "Message(" + Math.floor((messages.length + 1) / 2) + "): " + message, id: messages.length.toString() }]);
57
+ }} />
58
+ </div>
59
+ </div>
60
+ );
61
+ };
@@ -0,0 +1,64 @@
1
+ import React, { useState } from 'react';
2
+ import { VoiceRecorder } from './VoiceRecoder';
3
+ import { BiSolidRightArrow } from "react-icons/bi";
4
+ import { HiMiniSpeakerXMark, HiMiniSpeakerWave } from "react-icons/hi2";
5
+
6
+ interface AudioInputFieldProps {
7
+ onSubmit: (text: string) => void;
8
+ onAudioControl?: (voice: boolean) => void;
9
+ blockSubmission?: boolean;
10
+ }
11
+
12
+ export function AudioInputField({ onSubmit, onAudioControl, blockSubmission = false }: AudioInputFieldProps) {
13
+ const [text, setText] = useState('');
14
+ const [audioEnabled, setAudioEnabled] = useState(true);
15
+
16
+ const handleSubmit = (manualText?: string) => {
17
+ if (blockSubmission) return;
18
+ const sendableText = manualText || text;
19
+ if (sendableText.trim()) {
20
+ onSubmit(sendableText);
21
+ setTimeout(() => {
22
+ setText('');
23
+ }, 100);
24
+ }
25
+ };
26
+
27
+ const handleKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {
28
+ if (blockSubmission) return;
29
+ if (e.key === 'Enter' && e.ctrlKey) {
30
+ setText(text + '\n');
31
+ } else if (e.key === 'Enter') {
32
+ handleSubmit();
33
+ }
34
+ };
35
+
36
+ return (
37
+ <div className="flex items-center bg-gray-600 pt-2 pb-2 p-2">
38
+ {onAudioControl && <button
39
+ onClick={() => {
40
+ onAudioControl(!audioEnabled);
41
+ setAudioEnabled(!audioEnabled);
42
+ }}
43
+ className="cursor-default">
44
+ {audioEnabled ? <HiMiniSpeakerWave className='w-9 h-9 cursor-pointer' /> : <HiMiniSpeakerXMark className='w-9 h-9 cursor-pointer' />}
45
+ </button>}
46
+ <VoiceRecorder onVoiceRecorded={(m: string) => {
47
+ console.log('onVoiceRecorded', m);
48
+ handleSubmit(m);
49
+ }}
50
+ />
51
+ <textarea
52
+ value={text}
53
+ onChange={(e) => setText(e.target.value)}
54
+ onKeyDown={handleKeyDown}
55
+ className="flex-1 border-none rounded-lg p-2 text-gray-800 focus::outline-none"
56
+ placeholder='Type a message...'
57
+ disabled={blockSubmission}
58
+ />
59
+ <button onClick={() => handleSubmit()} className="cursor-default" disabled={blockSubmission}>
60
+ <BiSolidRightArrow className='w-9 h-10 cursor-pointer' />
61
+ </button>
62
+ </div>
63
+ );
64
+ };