@foisit/react-wrapper 2.0.1 → 2.1.2
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 +571 -29
- package/index.d.ts +6 -0
- package/index.js +412 -0
- package/index.mjs +1084 -0
- package/lib/components/AssistantActivator.d.ts +8 -0
- package/lib/components/AssistantProvider.d.ts +9 -0
- package/lib/hooks/useAssistant.d.ts +3 -0
- package/lib/hooks/useAssistantState.d.ts +3 -0
- package/lib/react-wrapper.d.ts +2 -0
- package/lib/services/AssistantService.d.ts +39 -0
- package/lib/types/index.d.ts +0 -0
- package/lib/utils/index.d.ts +0 -0
- package/package.json +6 -2
- package/style.css +0 -0
package/index.mjs
ADDED
|
@@ -0,0 +1,1084 @@
|
|
|
1
|
+
import { jsx as u } from "react/jsx-runtime";
|
|
2
|
+
import { createContext as A, useState as x, useEffect as w, useContext as I } from "react";
|
|
3
|
+
const E = {};
|
|
4
|
+
function Y() {
|
|
5
|
+
return /* @__PURE__ */ u("div", { className: E.container, children: /* @__PURE__ */ u("h1", { children: "Welcome to ReactWrapper!" }) });
|
|
6
|
+
}
|
|
7
|
+
class L {
|
|
8
|
+
constructor() {
|
|
9
|
+
this.endpoint = "https://foisit-ninja.netlify.app/.netlify/functions/intent";
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Determine the best matching command by calling the internalized proxy
|
|
13
|
+
*/
|
|
14
|
+
async determineIntent(e, t, i) {
|
|
15
|
+
try {
|
|
16
|
+
const s = await fetch(this.endpoint, {
|
|
17
|
+
method: "POST",
|
|
18
|
+
headers: {
|
|
19
|
+
"Content-Type": "application/json"
|
|
20
|
+
},
|
|
21
|
+
body: JSON.stringify({
|
|
22
|
+
userInput: e,
|
|
23
|
+
context: i,
|
|
24
|
+
commands: t.map((c) => ({
|
|
25
|
+
id: c.id,
|
|
26
|
+
command: c.command,
|
|
27
|
+
description: c.description,
|
|
28
|
+
keywords: c.keywords,
|
|
29
|
+
parameters: c.parameters
|
|
30
|
+
}))
|
|
31
|
+
})
|
|
32
|
+
});
|
|
33
|
+
if (!s.ok)
|
|
34
|
+
throw new Error("Proxy API request failed");
|
|
35
|
+
return await s.json();
|
|
36
|
+
} catch (s) {
|
|
37
|
+
return console.error("Foisit AI Error:", s), { type: "unknown" };
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
class M {
|
|
42
|
+
constructor(e = !0) {
|
|
43
|
+
this.commands = /* @__PURE__ */ new Map(), this.pendingConfirmation = null, this.conversationContext = null, this.enableSmartIntent = !0, this.enableSmartIntent = e, this.aiService = new L();
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Add a new command (supports simple string or rich object)
|
|
47
|
+
*/
|
|
48
|
+
addCommand(e, t) {
|
|
49
|
+
let i;
|
|
50
|
+
if (typeof e == "string") {
|
|
51
|
+
if (!t)
|
|
52
|
+
throw new Error("Action is required when passing a command string.");
|
|
53
|
+
i = {
|
|
54
|
+
command: e,
|
|
55
|
+
action: t,
|
|
56
|
+
id: `cmd_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
|
|
57
|
+
// Auto-gen ID
|
|
58
|
+
};
|
|
59
|
+
} else
|
|
60
|
+
i = e, i.id || (i.id = `cmd_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`);
|
|
61
|
+
const s = i.command.toLowerCase();
|
|
62
|
+
this.commands.set(s, i);
|
|
63
|
+
}
|
|
64
|
+
/** Remove an existing command by trigger phrase */
|
|
65
|
+
removeCommand(e) {
|
|
66
|
+
const t = e.toLowerCase();
|
|
67
|
+
this.commands.has(t) && this.commands.delete(t);
|
|
68
|
+
}
|
|
69
|
+
/** Execute a command by matching input (string or parameter object) */
|
|
70
|
+
async executeCommand(e) {
|
|
71
|
+
var c;
|
|
72
|
+
if (typeof e == "object" && this.conversationContext) {
|
|
73
|
+
const o = Array.from(this.commands.values()).find(
|
|
74
|
+
(a) => {
|
|
75
|
+
var n;
|
|
76
|
+
return a.id === ((n = this.conversationContext) == null ? void 0 : n.commandId);
|
|
77
|
+
}
|
|
78
|
+
);
|
|
79
|
+
if (o) {
|
|
80
|
+
const a = { ...this.conversationContext.params, ...e };
|
|
81
|
+
return this.conversationContext = null, this.runAction(o, a);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
const t = (typeof e == "string" ? e : "").toLowerCase().trim();
|
|
85
|
+
if (this.pendingConfirmation)
|
|
86
|
+
if (t === "yes" || t === "y" || t === "confirm") {
|
|
87
|
+
const o = this.pendingConfirmation;
|
|
88
|
+
return this.pendingConfirmation = null, this.runAction(o);
|
|
89
|
+
} else return t === "no" || t === "n" || t === "cancel" ? (this.pendingConfirmation = null, { type: "success", message: "Action cancelled." }) : {
|
|
90
|
+
type: "ambiguous",
|
|
91
|
+
message: `Please confirm: Are you sure you want to ${this.pendingConfirmation.description || this.pendingConfirmation.command}?`,
|
|
92
|
+
options: [
|
|
93
|
+
{ label: "Yes", value: "yes" },
|
|
94
|
+
{ label: "No", value: "no" }
|
|
95
|
+
]
|
|
96
|
+
};
|
|
97
|
+
if (!this.conversationContext) {
|
|
98
|
+
const o = this.commands.get(t);
|
|
99
|
+
if (o) {
|
|
100
|
+
if (!o.parameters || o.parameters.length === 0)
|
|
101
|
+
return o.critical ? (this.pendingConfirmation = o, {
|
|
102
|
+
type: "ambiguous",
|
|
103
|
+
message: `Are you sure you want to ${o.description || o.command}?`,
|
|
104
|
+
options: [
|
|
105
|
+
{ label: "Yes", value: "yes" },
|
|
106
|
+
{ label: "No", value: "no" }
|
|
107
|
+
]
|
|
108
|
+
}) : this.runAction(o);
|
|
109
|
+
this.conversationContext = {
|
|
110
|
+
commandId: o.id || "",
|
|
111
|
+
params: {}
|
|
112
|
+
};
|
|
113
|
+
const a = await Promise.all(
|
|
114
|
+
o.parameters.map(async (n) => {
|
|
115
|
+
let h = n.options;
|
|
116
|
+
if (n.getOptions)
|
|
117
|
+
try {
|
|
118
|
+
h = await n.getOptions();
|
|
119
|
+
} catch (m) {
|
|
120
|
+
console.error(`Error fetching options for ${n.name}`, m);
|
|
121
|
+
}
|
|
122
|
+
return {
|
|
123
|
+
name: n.name,
|
|
124
|
+
label: n.name.charAt(0).toUpperCase() + n.name.slice(1),
|
|
125
|
+
value: "",
|
|
126
|
+
required: n.required,
|
|
127
|
+
type: n.type || "string",
|
|
128
|
+
options: h
|
|
129
|
+
};
|
|
130
|
+
})
|
|
131
|
+
);
|
|
132
|
+
return {
|
|
133
|
+
type: "form",
|
|
134
|
+
message: o.description || `Please fill in the details for ${o.command}:`,
|
|
135
|
+
fields: a
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
const i = Array.from(this.commands.values());
|
|
140
|
+
if (this.enableSmartIntent) {
|
|
141
|
+
const o = await this.aiService.determineIntent(
|
|
142
|
+
t,
|
|
143
|
+
i,
|
|
144
|
+
this.conversationContext
|
|
145
|
+
);
|
|
146
|
+
if (o.type === "match" && o.match) {
|
|
147
|
+
const a = i.find((n) => n.id === o.match);
|
|
148
|
+
if (a) {
|
|
149
|
+
const n = {
|
|
150
|
+
...((c = this.conversationContext) == null ? void 0 : c.params) || {},
|
|
151
|
+
...o.params || {}
|
|
152
|
+
};
|
|
153
|
+
if (o.incomplete) {
|
|
154
|
+
if (this.conversationContext = {
|
|
155
|
+
commandId: a.id,
|
|
156
|
+
params: n
|
|
157
|
+
}, a.parameters && a.parameters.length > 0) {
|
|
158
|
+
const h = await Promise.all(
|
|
159
|
+
a.parameters.map(async (m) => {
|
|
160
|
+
let d = m.options;
|
|
161
|
+
if (m.getOptions)
|
|
162
|
+
try {
|
|
163
|
+
d = await m.getOptions();
|
|
164
|
+
} catch (g) {
|
|
165
|
+
console.error(`Error fetching options for ${m.name}`, g);
|
|
166
|
+
}
|
|
167
|
+
return {
|
|
168
|
+
name: m.name,
|
|
169
|
+
label: m.name.charAt(0).toUpperCase() + m.name.slice(1),
|
|
170
|
+
value: n[m.name] || "",
|
|
171
|
+
required: m.required,
|
|
172
|
+
type: m.type || "string",
|
|
173
|
+
options: d
|
|
174
|
+
};
|
|
175
|
+
})
|
|
176
|
+
);
|
|
177
|
+
return {
|
|
178
|
+
type: "form",
|
|
179
|
+
message: o.message || `Please fill in the details for ${a.command}:`,
|
|
180
|
+
fields: h
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
return {
|
|
184
|
+
type: "ambiguous",
|
|
185
|
+
message: o.message || `Need more info for ${a.command}...`
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
return this.conversationContext = null, a.critical ? (this.pendingConfirmation = {
|
|
189
|
+
...a,
|
|
190
|
+
action: () => a.action(n)
|
|
191
|
+
}, {
|
|
192
|
+
type: "ambiguous",
|
|
193
|
+
message: `Are you sure you want to ${a.description || a.command}?`,
|
|
194
|
+
options: [
|
|
195
|
+
{ label: "Yes", value: "yes" },
|
|
196
|
+
{ label: "No", value: "no" }
|
|
197
|
+
]
|
|
198
|
+
}) : this.runAction(a, n);
|
|
199
|
+
}
|
|
200
|
+
} else if (o.type === "ambiguous" && o.options)
|
|
201
|
+
return {
|
|
202
|
+
type: "ambiguous",
|
|
203
|
+
message: "Did you mean...",
|
|
204
|
+
options: o.options.map((a) => ({
|
|
205
|
+
label: a.label,
|
|
206
|
+
value: a.label
|
|
207
|
+
}))
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
return {
|
|
211
|
+
type: "ambiguous",
|
|
212
|
+
message: "I'm not sure what you mean. Here is what I can do:",
|
|
213
|
+
options: i.map((o) => ({
|
|
214
|
+
label: o.command,
|
|
215
|
+
value: o.command
|
|
216
|
+
}))
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
/** Run a command action and handle rich responses */
|
|
220
|
+
async runAction(e, t) {
|
|
221
|
+
try {
|
|
222
|
+
const i = await e.action(t);
|
|
223
|
+
return typeof i == "string" ? { type: "success", message: i } : i && i.type ? i : {
|
|
224
|
+
type: "success",
|
|
225
|
+
message: `Executed: ${e.command}`
|
|
226
|
+
};
|
|
227
|
+
} catch (i) {
|
|
228
|
+
return {
|
|
229
|
+
type: "error",
|
|
230
|
+
message: i.message || "An error occurred during execution."
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
/** List all registered commands */
|
|
235
|
+
getCommands() {
|
|
236
|
+
return Array.from(this.commands.keys());
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
class k {
|
|
240
|
+
constructor() {
|
|
241
|
+
this.synth = window.speechSynthesis;
|
|
242
|
+
}
|
|
243
|
+
speak(e, t) {
|
|
244
|
+
if (!this.synth) {
|
|
245
|
+
console.error("SpeechSynthesis API is not supported in this browser.");
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
const i = new SpeechSynthesisUtterance(e);
|
|
249
|
+
t && (i.pitch = t.pitch || 1, i.rate = t.rate || 1, i.volume = t.volume || 1), i.onend = () => {
|
|
250
|
+
console.log("Speech finished.");
|
|
251
|
+
}, i.onerror = (s) => {
|
|
252
|
+
console.error("Error during speech synthesis:", s.error);
|
|
253
|
+
}, this.synth.speak(i);
|
|
254
|
+
}
|
|
255
|
+
stopSpeaking() {
|
|
256
|
+
this.synth && this.synth.cancel();
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
class P {
|
|
260
|
+
constructor() {
|
|
261
|
+
this.fallbackMessage = "Sorry, I didn’t understand that.";
|
|
262
|
+
}
|
|
263
|
+
setFallbackMessage(e) {
|
|
264
|
+
this.fallbackMessage = e;
|
|
265
|
+
}
|
|
266
|
+
handleFallback(e) {
|
|
267
|
+
console.log(this.fallbackMessage), new k().speak(this.fallbackMessage);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
class T {
|
|
271
|
+
constructor(e = "en-US") {
|
|
272
|
+
this.isListening = !1, this.isStoppedSpeechRecog = !1, this.restartAllowed = !0;
|
|
273
|
+
}
|
|
274
|
+
/** Start listening for speech input */
|
|
275
|
+
startListening(e) {
|
|
276
|
+
console.warn("VoiceProcessor: Voice interaction is currently DISABLED.");
|
|
277
|
+
}
|
|
278
|
+
/** Stop listening for speech input */
|
|
279
|
+
stopListening() {
|
|
280
|
+
if (console.log("VoiceProcessor: Stopping listening..."), this.isStoppedSpeechRecog = !0, this.restartAllowed = !1, !this.isListening) {
|
|
281
|
+
console.warn("VoiceProcessor: Already stopped. Skipping stop...");
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
try {
|
|
285
|
+
this.recognition.stop(), this.isListening = !1, console.log("VoiceProcessor: Listening stopped.");
|
|
286
|
+
} catch (e) {
|
|
287
|
+
console.error("VoiceProcessor: Failed to stop SpeechRecognition:", e);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
/** Handle recognized speech results */
|
|
291
|
+
handleResult(e, t) {
|
|
292
|
+
}
|
|
293
|
+
/** Handle session end */
|
|
294
|
+
handleEnd() {
|
|
295
|
+
console.log("VoiceProcessor: Session ended."), this.isListening = !1, this.restartAllowed && !this.isStoppedSpeechRecog && (console.log(
|
|
296
|
+
"VoiceProcessor: Restarting session due to unexpected stop..."
|
|
297
|
+
), setTimeout(() => this.startListening(() => {
|
|
298
|
+
}), 560));
|
|
299
|
+
}
|
|
300
|
+
/** Handle errors during speech recognition */
|
|
301
|
+
handleError(e) {
|
|
302
|
+
console.error("VoiceProcessor: Error occurred:", e.error), e.error === "no-speech" ? console.log("VoiceProcessor: No speech detected.") : e.error === "audio-capture" && console.log("VoiceProcessor: Microphone not detected."), this.isListening = !1;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
class B {
|
|
306
|
+
constructor() {
|
|
307
|
+
this.lastTap = 0;
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* Sets up double-click and double-tap listeners
|
|
311
|
+
* @param onDoubleClickOrTap Callback to execute when a double-click or double-tap is detected
|
|
312
|
+
*/
|
|
313
|
+
setupDoubleTapListener(e) {
|
|
314
|
+
document.addEventListener("dblclick", () => {
|
|
315
|
+
e();
|
|
316
|
+
}), document.addEventListener("touchend", (t) => {
|
|
317
|
+
const i = (/* @__PURE__ */ new Date()).getTime(), s = i - this.lastTap;
|
|
318
|
+
s < 300 && s > 0 && e(), this.lastTap = i;
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
function D() {
|
|
323
|
+
if (document.querySelector("#assistant-styles")) {
|
|
324
|
+
console.log("Styles already injected");
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
const e = document.createElement("style");
|
|
328
|
+
e.id = "assistant-styles", e.innerHTML = `
|
|
329
|
+
/* Rounded shape with gradient animation */
|
|
330
|
+
.gradient-indicator {
|
|
331
|
+
position: fixed;
|
|
332
|
+
top: 20px;
|
|
333
|
+
right: 20px;
|
|
334
|
+
width: 60px;
|
|
335
|
+
height: 60px;
|
|
336
|
+
border-radius: 50%;
|
|
337
|
+
background: linear-gradient(135deg, #ff6ec4, #7873f5, #5e8cff, #6ed0f6);
|
|
338
|
+
box-shadow: 0 0 15px rgba(0, 0, 0, 0.2);
|
|
339
|
+
animation: amoeba 5s infinite ease-in-out;
|
|
340
|
+
z-index: 9999; /* Ensure it's above all other elements */
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/* Amoeba effect for the borders */
|
|
344
|
+
@keyframes amoeba {
|
|
345
|
+
0% {
|
|
346
|
+
border-radius: 50%;
|
|
347
|
+
}
|
|
348
|
+
25% {
|
|
349
|
+
border-radius: 40% 60% 60% 40%;
|
|
350
|
+
}
|
|
351
|
+
50% {
|
|
352
|
+
border-radius: 60% 40% 40% 60%;
|
|
353
|
+
}
|
|
354
|
+
75% {
|
|
355
|
+
border-radius: 40% 60% 60% 40%;
|
|
356
|
+
}
|
|
357
|
+
100% {
|
|
358
|
+
border-radius: 50%;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
`, document.head.appendChild(e), console.log("Gradient styles injected");
|
|
362
|
+
}
|
|
363
|
+
function R() {
|
|
364
|
+
if (document.querySelector("#gradient-indicator"))
|
|
365
|
+
return;
|
|
366
|
+
const r = document.createElement("div");
|
|
367
|
+
r.id = "gradient-indicator", D(), r.classList.add("gradient-indicator"), document.body.appendChild(r), console.log("Gradient indicator added to the DOM");
|
|
368
|
+
}
|
|
369
|
+
function H() {
|
|
370
|
+
const r = document.querySelector("#gradient-indicator");
|
|
371
|
+
r && (r.remove(), console.log("Gradient indicator removed from the DOM"));
|
|
372
|
+
}
|
|
373
|
+
class N {
|
|
374
|
+
constructor() {
|
|
375
|
+
this.state = "idle", this.subscribers = [];
|
|
376
|
+
}
|
|
377
|
+
getState() {
|
|
378
|
+
return this.state;
|
|
379
|
+
}
|
|
380
|
+
setState(e) {
|
|
381
|
+
this.state = e, this.notifySubscribers(), console.log("State updated:", e), e === "listening" ? R() : H();
|
|
382
|
+
}
|
|
383
|
+
// eslint-disable-next-line no-unused-vars
|
|
384
|
+
subscribe(e) {
|
|
385
|
+
this.subscribers.push(e);
|
|
386
|
+
}
|
|
387
|
+
notifySubscribers() {
|
|
388
|
+
this.subscribers.forEach((e) => e(this.state));
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
const l = class l {
|
|
392
|
+
constructor(e) {
|
|
393
|
+
this.isOpen = !1, this.submitCallback = null, this.closeCallback = null, this.chatContainer = null, this.config = {}, e && (this.config = e), this.injectStyles(), this.injectOverlay(), this.injectFloatingButton(), this.setupEventListeners(), this.chatContainer = document.getElementById(
|
|
394
|
+
"foisit-chat-history"
|
|
395
|
+
);
|
|
396
|
+
}
|
|
397
|
+
/**
|
|
398
|
+
* Cleanup everything from the DOM
|
|
399
|
+
*/
|
|
400
|
+
destroy() {
|
|
401
|
+
const e = document.getElementById(l.OVERLAY_ID);
|
|
402
|
+
e && e.remove();
|
|
403
|
+
const t = document.getElementById(l.STYLES_ID);
|
|
404
|
+
t && t.remove();
|
|
405
|
+
const i = document.getElementById("foisit-floating-btn");
|
|
406
|
+
i && i.remove(), this.isOpen = !1, this.chatContainer = null, this.submitCallback = null, this.closeCallback = null;
|
|
407
|
+
}
|
|
408
|
+
/**
|
|
409
|
+
* Register callbacks for the overlay (used by floating button)
|
|
410
|
+
*/
|
|
411
|
+
registerCallbacks(e, t) {
|
|
412
|
+
this.submitCallback = e, this.closeCallback = t || null;
|
|
413
|
+
}
|
|
414
|
+
/**
|
|
415
|
+
* Show the overlay and focus input
|
|
416
|
+
*/
|
|
417
|
+
show(e, t) {
|
|
418
|
+
const i = document.getElementById(l.OVERLAY_ID), s = document.getElementById(
|
|
419
|
+
l.INPUT_ID
|
|
420
|
+
);
|
|
421
|
+
this.chatContainer = document.getElementById(
|
|
422
|
+
"foisit-chat-history"
|
|
423
|
+
), i && s && (this.submitCallback = e, this.closeCallback = t || null, i.style.display = "flex", setTimeout(() => {
|
|
424
|
+
i.classList.add("visible"), s.focus(), s.focus(), s.value = "", this.chatContainer && (this.chatContainer.innerHTML = "");
|
|
425
|
+
}, 10), this.isOpen = !0);
|
|
426
|
+
}
|
|
427
|
+
/**
|
|
428
|
+
* Hide the overlay
|
|
429
|
+
*/
|
|
430
|
+
hide() {
|
|
431
|
+
const e = document.getElementById(l.OVERLAY_ID);
|
|
432
|
+
e && (e.classList.remove("visible"), setTimeout(() => {
|
|
433
|
+
e.style.display = "none";
|
|
434
|
+
}, 300), this.isOpen = !1, this.closeCallback && this.closeCallback());
|
|
435
|
+
}
|
|
436
|
+
/**
|
|
437
|
+
* Toggle the overlay
|
|
438
|
+
*/
|
|
439
|
+
toggle(e, t) {
|
|
440
|
+
this.isOpen ? this.hide() : this.show(e, t);
|
|
441
|
+
}
|
|
442
|
+
/**
|
|
443
|
+
* Add a message bubble to the chat
|
|
444
|
+
*/
|
|
445
|
+
addMessage(e, t = "system") {
|
|
446
|
+
if (!this.chatContainer) return;
|
|
447
|
+
const i = document.createElement("div");
|
|
448
|
+
i.className = `foisit-bubble ${t}`, i.textContent = e, this.chatContainer.appendChild(i), this.scrollToBottom();
|
|
449
|
+
}
|
|
450
|
+
/**
|
|
451
|
+
* Add clickable options to the chat
|
|
452
|
+
*/
|
|
453
|
+
addOptions(e) {
|
|
454
|
+
if (!this.chatContainer) return;
|
|
455
|
+
const t = document.createElement("div");
|
|
456
|
+
t.className = "foisit-options-container", e.forEach((i) => {
|
|
457
|
+
const s = document.createElement("button");
|
|
458
|
+
s.className = "foisit-option-chip", s.textContent = i.label, s.onclick = () => {
|
|
459
|
+
this.addMessage(i.label, "user"), this.submitCallback && this.submitCallback(i.value);
|
|
460
|
+
}, t.appendChild(s);
|
|
461
|
+
}), this.chatContainer.appendChild(t), this.scrollToBottom();
|
|
462
|
+
}
|
|
463
|
+
/**
|
|
464
|
+
* Inject the floating trigger button
|
|
465
|
+
*/
|
|
466
|
+
injectFloatingButton() {
|
|
467
|
+
var s, c, o, a;
|
|
468
|
+
if (((s = this.config.floatingButton) == null ? void 0 : s.visible) === !1) return;
|
|
469
|
+
const e = document.getElementById("foisit-floating-btn");
|
|
470
|
+
e && e.remove();
|
|
471
|
+
const t = document.createElement("div");
|
|
472
|
+
t.id = "foisit-floating-btn", t.className = "foisit-floating-btn", t.title = ((c = this.config.floatingButton) == null ? void 0 : c.tooltip) || "Open Foisit Assistant";
|
|
473
|
+
const i = (o = this.config.floatingButton) == null ? void 0 : o.position;
|
|
474
|
+
i && (t.style.bottom = i.bottom, t.style.right = i.right), (a = this.config.floatingButton) != null && a.customHtml ? t.innerHTML = this.config.floatingButton.customHtml : t.innerHTML = `
|
|
475
|
+
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
476
|
+
<path d="M12 2C6.48 2 2 6.48 2 12C2 17.52 6.48 22 12 22C17.52 22 22 17.52 22 12C22 6.48 17.52 2 12 2ZM12 4C16.42 4 20 7.58 20 12C20 16.42 16.42 20 12 20C7.58 20 4 16.42 4 12C4 7.58 7.58 4 12 4Z" fill="white" fill-opacity="0.8"/>
|
|
477
|
+
<path d="M12 6C8.69 6 6 8.69 6 12C6 15.31 8.69 18 12 18C15.31 18 18 15.31 18 12C18 8.69 15.31 6 12 6ZM12 16C9.79 16 8 14.21 8 12C8 9.79 9.79 8 12 8C14.21 8 16 9.79 16 12C16 14.21 14.21 16 12 16Z" fill="white"/>
|
|
478
|
+
</svg>
|
|
479
|
+
`, t.onclick = () => {
|
|
480
|
+
this.submitCallback && this.toggle(this.submitCallback, this.closeCallback || void 0);
|
|
481
|
+
}, document.body.appendChild(t);
|
|
482
|
+
}
|
|
483
|
+
/**
|
|
484
|
+
* Add a dynamic form to the chat
|
|
485
|
+
*/
|
|
486
|
+
addForm(e, t, i) {
|
|
487
|
+
if (!this.chatContainer) return;
|
|
488
|
+
const s = document.createElement("div");
|
|
489
|
+
s.className = "foisit-form-card";
|
|
490
|
+
const c = document.createElement("div");
|
|
491
|
+
c.className = "foisit-form-message", c.textContent = e, s.appendChild(c);
|
|
492
|
+
const o = {};
|
|
493
|
+
t.forEach((n) => {
|
|
494
|
+
var g, C;
|
|
495
|
+
const h = document.createElement("div");
|
|
496
|
+
h.className = "foisit-field-group";
|
|
497
|
+
const m = document.createElement("label");
|
|
498
|
+
m.textContent = n.label, h.appendChild(m);
|
|
499
|
+
let d;
|
|
500
|
+
if ((n.type === "select" || n.options) && ((g = n.options) != null && g.length) && n.options.length > 0) {
|
|
501
|
+
d = document.createElement("select"), d.className = "foisit-field-input";
|
|
502
|
+
const p = document.createElement("option");
|
|
503
|
+
p.value = "", p.textContent = `Select ${n.label}`, p.disabled = !0, p.selected = !n.value, d.appendChild(p), (C = n.options) == null || C.forEach((y) => {
|
|
504
|
+
const f = document.createElement("option");
|
|
505
|
+
f.value = y.value, f.textContent = y.label, n.value === y.value && (f.selected = !0), d.appendChild(f);
|
|
506
|
+
});
|
|
507
|
+
} else
|
|
508
|
+
d = document.createElement("input"), d.className = "foisit-field-input", d.placeholder = n.label, d.value = n.value || "", n.type === "date" ? d.type = "date" : n.type === "number" ? d.type = "number" : d.type = "text";
|
|
509
|
+
o[n.name] = d, h.appendChild(d), s.appendChild(h);
|
|
510
|
+
});
|
|
511
|
+
const a = document.createElement("button");
|
|
512
|
+
a.className = "foisit-form-submit", a.textContent = "Submit", a.onclick = () => {
|
|
513
|
+
const n = {};
|
|
514
|
+
Object.keys(o).forEach((h) => {
|
|
515
|
+
n[h] = o[h].value;
|
|
516
|
+
}), a.disabled = !0, a.textContent = "Submitted", s.style.opacity = "0.7", s.style.pointerEvents = "none", i(n);
|
|
517
|
+
}, s.appendChild(a), this.chatContainer.appendChild(s), this.scrollToBottom();
|
|
518
|
+
}
|
|
519
|
+
/**
|
|
520
|
+
* Show loading indicator
|
|
521
|
+
*/
|
|
522
|
+
showLoading() {
|
|
523
|
+
if (!this.chatContainer || document.getElementById("foisit-loading-bubble")) return;
|
|
524
|
+
const e = document.createElement("div");
|
|
525
|
+
e.id = "foisit-loading-bubble", e.className = "foisit-bubble system loading", e.innerHTML = "<span>.</span><span>.</span><span>.</span>", this.chatContainer.appendChild(e), this.scrollToBottom();
|
|
526
|
+
}
|
|
527
|
+
/**
|
|
528
|
+
* Hide loading indicator
|
|
529
|
+
*/
|
|
530
|
+
hideLoading() {
|
|
531
|
+
const e = document.getElementById("foisit-loading-bubble");
|
|
532
|
+
e && e.remove();
|
|
533
|
+
}
|
|
534
|
+
scrollToBottom() {
|
|
535
|
+
this.chatContainer && (this.chatContainer.scrollTop = this.chatContainer.scrollHeight);
|
|
536
|
+
}
|
|
537
|
+
injectStyles() {
|
|
538
|
+
if (document.getElementById(l.STYLES_ID)) return;
|
|
539
|
+
const e = document.createElement("style");
|
|
540
|
+
e.id = l.STYLES_ID, e.innerHTML = `
|
|
541
|
+
#${l.OVERLAY_ID} {
|
|
542
|
+
position: fixed;
|
|
543
|
+
top: 24px;
|
|
544
|
+
right: 24px;
|
|
545
|
+
width: 320px;
|
|
546
|
+
z-index: 99999;
|
|
547
|
+
display: none;
|
|
548
|
+
flex-direction: column;
|
|
549
|
+
opacity: 0;
|
|
550
|
+
transition: opacity 0.3s ease-in-out, transform 0.3s ease-in-out;
|
|
551
|
+
transform: translateY(-10px);
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
#${l.OVERLAY_ID}.visible {
|
|
555
|
+
opacity: 1;
|
|
556
|
+
transform: translateY(0);
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
.foisit-content {
|
|
560
|
+
width: 100%;
|
|
561
|
+
padding: 0;
|
|
562
|
+
border-radius: 16px;
|
|
563
|
+
overflow: hidden;
|
|
564
|
+
/* Stronger Frosted Look (User Provided) */
|
|
565
|
+
background: linear-gradient(
|
|
566
|
+
135deg,
|
|
567
|
+
rgba(255, 255, 255, 0.25),
|
|
568
|
+
rgba(255, 255, 255, 0.05)
|
|
569
|
+
);
|
|
570
|
+
backdrop-filter: blur(20px);
|
|
571
|
+
-webkit-backdrop-filter: blur(20px);
|
|
572
|
+
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.25);
|
|
573
|
+
border: 1px solid rgba(255, 255, 255, 0.4);
|
|
574
|
+
display: flex;
|
|
575
|
+
flex-direction: column;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
#${l.INPUT_ID} {
|
|
579
|
+
width: 100%;
|
|
580
|
+
background: transparent;
|
|
581
|
+
border: none;
|
|
582
|
+
font-size: 16px;
|
|
583
|
+
color: #333;
|
|
584
|
+
padding: 18px 20px 12px 20px;
|
|
585
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
|
586
|
+
outline: none;
|
|
587
|
+
text-align: left;
|
|
588
|
+
resize: none;
|
|
589
|
+
min-height: 48px;
|
|
590
|
+
max-height: 200px;
|
|
591
|
+
overflow-y: auto;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
#${l.INPUT_ID}::placeholder {
|
|
595
|
+
color: rgba(60, 60, 67, 0.7);
|
|
596
|
+
font-weight: 500;
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
@media (prefers-color-scheme: dark) {
|
|
600
|
+
.foisit-content {
|
|
601
|
+
background: linear-gradient(
|
|
602
|
+
135deg,
|
|
603
|
+
rgba(40, 40, 40, 0.65),
|
|
604
|
+
rgba(40, 40, 40, 0.25)
|
|
605
|
+
);
|
|
606
|
+
border: 1px solid rgba(255, 255, 255, 0.1);
|
|
607
|
+
}
|
|
608
|
+
#${l.INPUT_ID} {
|
|
609
|
+
color: white;
|
|
610
|
+
}
|
|
611
|
+
#${l.INPUT_ID}::placeholder {
|
|
612
|
+
color: rgba(235, 235, 245, 0.6);
|
|
613
|
+
}
|
|
614
|
+
.foisit-watermark {
|
|
615
|
+
color: rgba(255, 255, 255, 0.5);
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
.foisit-watermark {
|
|
620
|
+
align-self: flex-end;
|
|
621
|
+
margin: 0 12px 10px auto;
|
|
622
|
+
padding: 4px 8px;
|
|
623
|
+
border-radius: 8px;
|
|
624
|
+
|
|
625
|
+
font-size: 9px;
|
|
626
|
+
font-weight: 600;
|
|
627
|
+
letter-spacing: 0.8px;
|
|
628
|
+
text-transform: capitalize;
|
|
629
|
+
|
|
630
|
+
background: rgba(0, 0, 0, 0.35);
|
|
631
|
+
backdrop-filter: blur(6px);
|
|
632
|
+
-webkit-backdrop-filter: blur(6px);
|
|
633
|
+
|
|
634
|
+
color: rgba(255, 255, 255, 0.85);
|
|
635
|
+
pointer-events: none;
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
|
|
639
|
+
/* Chat UI Styles */
|
|
640
|
+
#foisit-chat-history {
|
|
641
|
+
flex: 1;
|
|
642
|
+
overflow-y: auto;
|
|
643
|
+
padding: 16px;
|
|
644
|
+
display: flex;
|
|
645
|
+
flex-direction: column;
|
|
646
|
+
gap: 12px;
|
|
647
|
+
max-height: 400px;
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
.foisit-bubble {
|
|
651
|
+
max-width: 85%;
|
|
652
|
+
padding: 8px 12px;
|
|
653
|
+
border-radius: 12px;
|
|
654
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
|
655
|
+
font-size: 14px;
|
|
656
|
+
line-height: 1.4;
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
.foisit-bubble.user {
|
|
660
|
+
align-self: flex-end;
|
|
661
|
+
background: rgba(0, 0, 0, 0.05); /* Subtle dark for user */
|
|
662
|
+
color: #333;
|
|
663
|
+
border-bottom-right-radius: 4px;
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
.foisit-bubble.system {
|
|
667
|
+
align-self: flex-start;
|
|
668
|
+
background: rgba(255, 255, 255, 0.4); /* Glassy white */
|
|
669
|
+
color: #333;
|
|
670
|
+
border-bottom-left-radius: 4px;
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
.foisit-options-container {
|
|
674
|
+
display: flex;
|
|
675
|
+
flex-wrap: wrap;
|
|
676
|
+
gap: 8px;
|
|
677
|
+
margin-left: 4px;
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
.foisit-option-chip {
|
|
681
|
+
padding: 6px 12px;
|
|
682
|
+
background: rgba(255, 255, 255, 0.5);
|
|
683
|
+
border: 1px solid rgba(0, 0, 0, 0.1);
|
|
684
|
+
border-radius: 20px;
|
|
685
|
+
font-size: 13px;
|
|
686
|
+
color: #333;
|
|
687
|
+
cursor: pointer;
|
|
688
|
+
transition: all 0.2s;
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
.foisit-option-chip:hover {
|
|
692
|
+
background: rgba(255, 255, 255, 0.8);
|
|
693
|
+
transform: translateY(-1px);
|
|
694
|
+
box-shadow: 0 2px 5px rgba(0,0,0,0.05);
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
@media (prefers-color-scheme: dark) {
|
|
698
|
+
.foisit-bubble.user {
|
|
699
|
+
background: rgba(255, 255, 255, 0.1);
|
|
700
|
+
color: white;
|
|
701
|
+
}
|
|
702
|
+
.foisit-bubble.system {
|
|
703
|
+
background: rgba(255, 255, 255, 0.05);
|
|
704
|
+
color: rgba(255, 255, 255, 0.9);
|
|
705
|
+
}
|
|
706
|
+
.foisit-option-chip {
|
|
707
|
+
background: rgba(255, 255, 255, 0.1);
|
|
708
|
+
border-color: rgba(255, 255, 255, 0.2);
|
|
709
|
+
color: white;
|
|
710
|
+
}
|
|
711
|
+
.foisit-option-chip:hover {
|
|
712
|
+
background: rgba(255, 255, 255, 0.2);
|
|
713
|
+
}
|
|
714
|
+
/* Loading State */
|
|
715
|
+
.foisit-loading-dots {
|
|
716
|
+
display: inline-flex;
|
|
717
|
+
gap: 4px;
|
|
718
|
+
align-items: center;
|
|
719
|
+
padding: 4px 8px;
|
|
720
|
+
background: rgba(255, 255, 255, 0.4);
|
|
721
|
+
border-radius: 12px;
|
|
722
|
+
margin-left: 12px;
|
|
723
|
+
align-self: flex-start;
|
|
724
|
+
}
|
|
725
|
+
.foisit-dot {
|
|
726
|
+
width: 6px;
|
|
727
|
+
height: 6px;
|
|
728
|
+
background: #666;
|
|
729
|
+
border-radius: 50%;
|
|
730
|
+
animation: foisit-bounce 1.4s infinite ease-in-out both;
|
|
731
|
+
}
|
|
732
|
+
.foisit-dot:nth-child(1) { animation-delay: -0.32s; }
|
|
733
|
+
.foisit-dot:nth-child(2) { animation-delay: -0.16s; }
|
|
734
|
+
|
|
735
|
+
@keyframes foisit-bounce {
|
|
736
|
+
0%, 80%, 100% { transform: scale(0); }
|
|
737
|
+
40% { transform: scale(1); }
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
/* Floating Trigger Button - Glassmorphism Style */
|
|
741
|
+
.foisit-floating-btn {
|
|
742
|
+
position: fixed;
|
|
743
|
+
bottom: 20px;
|
|
744
|
+
right: 20px;
|
|
745
|
+
width: 52px;
|
|
746
|
+
height: 52px;
|
|
747
|
+
/* Frosted Glass Effect */
|
|
748
|
+
background: linear-gradient(
|
|
749
|
+
135deg,
|
|
750
|
+
rgba(255, 255, 255, 0.25),
|
|
751
|
+
rgba(255, 255, 255, 0.08)
|
|
752
|
+
);
|
|
753
|
+
backdrop-filter: blur(16px);
|
|
754
|
+
-webkit-backdrop-filter: blur(16px);
|
|
755
|
+
border-radius: 50%;
|
|
756
|
+
display: flex;
|
|
757
|
+
align-items: center;
|
|
758
|
+
justify-content: center;
|
|
759
|
+
cursor: pointer;
|
|
760
|
+
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
|
|
761
|
+
border: 1px solid rgba(255, 255, 255, 0.35);
|
|
762
|
+
z-index: 9998;
|
|
763
|
+
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
.foisit-floating-btn:hover {
|
|
767
|
+
transform: scale(1.08);
|
|
768
|
+
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.3);
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
/* Dark mode for floating button */
|
|
772
|
+
@media (prefers-color-scheme: dark) {
|
|
773
|
+
.foisit-floating-btn {
|
|
774
|
+
background: linear-gradient(
|
|
775
|
+
135deg,
|
|
776
|
+
rgba(60, 60, 60, 0.6),
|
|
777
|
+
rgba(40, 40, 40, 0.35)
|
|
778
|
+
);
|
|
779
|
+
border-color: rgba(255, 255, 255, 0.15);
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
.foisit-floating-btn img {
|
|
784
|
+
width: 100%;
|
|
785
|
+
height: 100%;
|
|
786
|
+
object-fit: cover;
|
|
787
|
+
border-radius: 50%;
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
/* Dark mode support for inputs */
|
|
791
|
+
@media (prefers-color-scheme: dark) {
|
|
792
|
+
.foisit-field-input {
|
|
793
|
+
background: rgba(40, 40, 40, 0.6);
|
|
794
|
+
color: white;
|
|
795
|
+
border-color: rgba(255, 255, 255, 0.1);
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
.foisit-watermark {
|
|
799
|
+
color: rgba(255, 255, 255, 0.3);
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
/* Loading Animation */
|
|
804
|
+
.foisit-bubble.loading span {
|
|
805
|
+
animation: foisit-dots 1.4s infinite ease-in-out both;
|
|
806
|
+
font-size: 20px;
|
|
807
|
+
line-height: 10px;
|
|
808
|
+
margin: 0 1px;
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
.foisit-bubble.loading span:nth-child(1) { animation-delay: -0.32s; }
|
|
812
|
+
.foisit-bubble.loading span:nth-child(2) { animation-delay: -0.16s; }
|
|
813
|
+
|
|
814
|
+
@keyframes foisit-dots {
|
|
815
|
+
0%, 80%, 100% { transform: scale(0); opacity: 0.5; }
|
|
816
|
+
40% { transform: scale(1); opacity: 1; }
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
/* Form Styles */
|
|
820
|
+
.foisit-form-card {
|
|
821
|
+
align-self: flex-start;
|
|
822
|
+
background: rgba(255, 255, 255, 0.4);
|
|
823
|
+
padding: 16px;
|
|
824
|
+
border-radius: 12px;
|
|
825
|
+
width: 100%;
|
|
826
|
+
box-sizing: border-box;
|
|
827
|
+
border: 1px solid rgba(255, 255, 255, 0.2);
|
|
828
|
+
display: flex;
|
|
829
|
+
flex-direction: column;
|
|
830
|
+
gap: 12px;
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
.foisit-form-message {
|
|
834
|
+
font-family: -apple-system, sans-serif;
|
|
835
|
+
font-size: 14px;
|
|
836
|
+
color: #333;
|
|
837
|
+
margin-bottom: 4px;
|
|
838
|
+
font-weight: 500;
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
.foisit-field-group {
|
|
842
|
+
display: flex;
|
|
843
|
+
flex-direction: column;
|
|
844
|
+
gap: 4px;
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
.foisit-field-group label {
|
|
848
|
+
font-size: 11px;
|
|
849
|
+
font-weight: 600;
|
|
850
|
+
text-transform: uppercase;
|
|
851
|
+
color: rgba(0, 0, 0, 0.5);
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
.foisit-field-input {
|
|
855
|
+
background: rgba(255, 255, 255, 0.6);
|
|
856
|
+
border: 1px solid rgba(0, 0, 0, 0.1);
|
|
857
|
+
border-radius: 8px;
|
|
858
|
+
padding: 8px 12px;
|
|
859
|
+
font-size: 14px;
|
|
860
|
+
color: #333;
|
|
861
|
+
outline: none;
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
.foisit-field-input:focus {
|
|
865
|
+
border-color: rgba(0, 0, 0, 0.3);
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
.foisit-form-submit {
|
|
869
|
+
background: #000;
|
|
870
|
+
color: #fff;
|
|
871
|
+
border: none;
|
|
872
|
+
border-radius: 8px;
|
|
873
|
+
padding: 10px;
|
|
874
|
+
font-size: 14px;
|
|
875
|
+
font-weight: 500;
|
|
876
|
+
cursor: pointer;
|
|
877
|
+
transition: opacity 0.2s;
|
|
878
|
+
margin-top: 4px;
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
.foisit-form-submit:hover {
|
|
882
|
+
opacity: 0.8;
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
@media (prefers-color-scheme: dark) {
|
|
886
|
+
.foisit-form-card {
|
|
887
|
+
background: rgba(255, 255, 255, 0.05);
|
|
888
|
+
color: white;
|
|
889
|
+
}
|
|
890
|
+
.foisit-form-message {
|
|
891
|
+
color: white;
|
|
892
|
+
}
|
|
893
|
+
.foisit-field-group label {
|
|
894
|
+
color: rgba(255, 255, 255, 0.5);
|
|
895
|
+
}
|
|
896
|
+
.foisit-field-input {
|
|
897
|
+
background: rgba(0, 0, 0, 0.2);
|
|
898
|
+
border-color: rgba(255, 255, 255, 0.1);
|
|
899
|
+
color: white;
|
|
900
|
+
}
|
|
901
|
+
.foisit-form-submit {
|
|
902
|
+
background: #fff;
|
|
903
|
+
color: #000;
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
`, document.head.appendChild(e);
|
|
908
|
+
}
|
|
909
|
+
injectOverlay() {
|
|
910
|
+
if (document.getElementById(l.OVERLAY_ID)) return;
|
|
911
|
+
const e = this.config.inputPlaceholder || "How can I help you?", t = document.createElement("div");
|
|
912
|
+
t.id = l.OVERLAY_ID, t.innerHTML = `
|
|
913
|
+
<div class="foisit-content">
|
|
914
|
+
<div id="foisit-chat-history"></div>
|
|
915
|
+
<textarea id="${l.INPUT_ID}" placeholder="${e}" autocomplete="off" rows="1"></textarea>
|
|
916
|
+
<div class="foisit-watermark">Foisit</div>
|
|
917
|
+
</div>
|
|
918
|
+
`, document.body.appendChild(t);
|
|
919
|
+
}
|
|
920
|
+
setupEventListeners() {
|
|
921
|
+
const e = document.getElementById(l.OVERLAY_ID), t = document.getElementById(
|
|
922
|
+
l.INPUT_ID
|
|
923
|
+
);
|
|
924
|
+
!e || !t || (t.addEventListener("input", () => {
|
|
925
|
+
t.style.height = "auto", t.style.height = Math.min(t.scrollHeight, 200) + "px";
|
|
926
|
+
}), e.addEventListener("click", (i) => {
|
|
927
|
+
i.target === e && this.hide();
|
|
928
|
+
}), t.addEventListener("keydown", (i) => {
|
|
929
|
+
if (i.key === "Enter" && !i.shiftKey) {
|
|
930
|
+
i.preventDefault();
|
|
931
|
+
const s = t.value.trim();
|
|
932
|
+
s && this.submitCallback && (this.submitCallback(s), t.value = "", t.style.height = "auto");
|
|
933
|
+
}
|
|
934
|
+
i.key === "Escape" && this.hide();
|
|
935
|
+
}));
|
|
936
|
+
}
|
|
937
|
+
};
|
|
938
|
+
l.OVERLAY_ID = "foisit-assistant-overlay", l.INPUT_ID = "foisit-assistant-input", l.STYLES_ID = "foisit-assistant-styles";
|
|
939
|
+
let v = l;
|
|
940
|
+
class $ {
|
|
941
|
+
constructor(e) {
|
|
942
|
+
this.config = e, this.isActivated = !1, this.lastProcessedInput = "", this.processingLock = !1, this.defaultIntroMessage = "How can I help you?", this.commandHandler = new M(
|
|
943
|
+
this.config.enableSmartIntent !== !1
|
|
944
|
+
), this.fallbackHandler = new P(), this.voiceProcessor = new T(), this.textToSpeech = new k(), this.stateManager = new N(), this.gestureHandler = new B(), this.overlayManager = new v({
|
|
945
|
+
floatingButton: this.config.floatingButton,
|
|
946
|
+
inputPlaceholder: this.config.inputPlaceholder
|
|
947
|
+
}), this.config.commands.forEach((t) => this.commandHandler.addCommand(t)), this.config.fallbackResponse && this.fallbackHandler.setFallbackMessage(this.config.fallbackResponse), this.gestureHandler.setupDoubleTapListener(() => this.toggle()), this.overlayManager.registerCallbacks(
|
|
948
|
+
async (t) => {
|
|
949
|
+
this.overlayManager.addMessage(t, "user"), await this.handleCommand(t);
|
|
950
|
+
},
|
|
951
|
+
() => console.log("AssistantService: Overlay closed.")
|
|
952
|
+
);
|
|
953
|
+
}
|
|
954
|
+
/** Start listening for activation and commands */
|
|
955
|
+
startListening() {
|
|
956
|
+
console.log("AssistantService: Starting listening..."), this.voiceProcessor.startListening(async (e) => {
|
|
957
|
+
var s, c;
|
|
958
|
+
if (this.processingLock) return;
|
|
959
|
+
const t = e.toLowerCase().trim();
|
|
960
|
+
if (!t || t.length < 3 || t === this.lastProcessedInput) {
|
|
961
|
+
console.log("AssistantService: Ignoring irrelevant input.");
|
|
962
|
+
return;
|
|
963
|
+
}
|
|
964
|
+
if (this.lastProcessedInput = t, !this.isActivated) {
|
|
965
|
+
await this.processActivation(t);
|
|
966
|
+
return;
|
|
967
|
+
}
|
|
968
|
+
t === ((s = this.config.fallbackResponse) == null ? void 0 : s.toLowerCase()) || t === ((c = this.config.introMessage) == null ? void 0 : c.toLowerCase()) || t === this.defaultIntroMessage.toLowerCase() ? console.log("AssistantService: Ignoring fallback or intro message.") : await this.handleCommand(t), this.processingLock = !0, setTimeout(() => this.processingLock = !1, 1e3);
|
|
969
|
+
});
|
|
970
|
+
}
|
|
971
|
+
/** Stop listening */
|
|
972
|
+
stopListening() {
|
|
973
|
+
console.log("AssistantService: Stopping listening..."), this.voiceProcessor.stopListening(), this.isActivated = !1;
|
|
974
|
+
}
|
|
975
|
+
/** Process activation command */
|
|
976
|
+
async processActivation(e) {
|
|
977
|
+
var i;
|
|
978
|
+
const t = (i = this.config.activationCommand) == null ? void 0 : i.toLowerCase();
|
|
979
|
+
t && (e === t ? (console.log("AssistantService: Activation matched."), this.isActivated = !0, this.textToSpeech.speak(
|
|
980
|
+
this.config.introMessage || this.defaultIntroMessage
|
|
981
|
+
)) : console.log("AssistantService: Activation command not recognized."));
|
|
982
|
+
}
|
|
983
|
+
/** Handle recognized commands */
|
|
984
|
+
async handleCommand(e) {
|
|
985
|
+
this.overlayManager.showLoading();
|
|
986
|
+
const t = await this.commandHandler.executeCommand(e);
|
|
987
|
+
this.overlayManager.hideLoading(), t.message && this.overlayManager.addMessage(t.message, "system"), t.type === "form" && t.fields ? this.overlayManager.addForm(
|
|
988
|
+
t.message,
|
|
989
|
+
t.fields,
|
|
990
|
+
async (i) => {
|
|
991
|
+
this.overlayManager.showLoading();
|
|
992
|
+
const s = await this.commandHandler.executeCommand(i);
|
|
993
|
+
this.overlayManager.hideLoading(), this.processResponse(s);
|
|
994
|
+
}
|
|
995
|
+
) : t.type === "ambiguous" && t.options ? this.overlayManager.addOptions(t.options) : t.type === "error" && this.fallbackHandler.handleFallback(e);
|
|
996
|
+
}
|
|
997
|
+
/**
|
|
998
|
+
* Cleanup resources
|
|
999
|
+
*/
|
|
1000
|
+
destroy() {
|
|
1001
|
+
this.voiceProcessor.stopListening(), this.overlayManager.destroy();
|
|
1002
|
+
}
|
|
1003
|
+
/** Unified response processing */
|
|
1004
|
+
processResponse(e) {
|
|
1005
|
+
e.message && this.overlayManager.addMessage(e.message, "system"), e.type === "form" && e.fields ? this.overlayManager.addForm(
|
|
1006
|
+
e.message,
|
|
1007
|
+
e.fields,
|
|
1008
|
+
async (t) => {
|
|
1009
|
+
this.overlayManager.showLoading();
|
|
1010
|
+
const i = await this.commandHandler.executeCommand(t);
|
|
1011
|
+
this.overlayManager.hideLoading(), this.processResponse(i);
|
|
1012
|
+
}
|
|
1013
|
+
) : e.type === "ambiguous" && e.options && this.overlayManager.addOptions(e.options);
|
|
1014
|
+
}
|
|
1015
|
+
/** Add a command dynamically (supports string or rich object) */
|
|
1016
|
+
addCommand(e, t) {
|
|
1017
|
+
console.log(typeof e == "string" ? `AssistantService: Adding command "${e}".` : `AssistantService: Adding rich command "${e.command}".`), this.commandHandler.addCommand(e, t);
|
|
1018
|
+
}
|
|
1019
|
+
/** Remove a command dynamically */
|
|
1020
|
+
removeCommand(e) {
|
|
1021
|
+
console.log(`AssistantService: Removing command "${e}".`), this.commandHandler.removeCommand(e);
|
|
1022
|
+
}
|
|
1023
|
+
/** Get all registered commands */
|
|
1024
|
+
getCommands() {
|
|
1025
|
+
return this.commandHandler.getCommands();
|
|
1026
|
+
}
|
|
1027
|
+
/** Toggle the assistant overlay */
|
|
1028
|
+
toggle(e, t) {
|
|
1029
|
+
console.log("AssistantService: Toggling overlay..."), this.overlayManager.toggle(
|
|
1030
|
+
async (i) => {
|
|
1031
|
+
this.overlayManager.addMessage(i, "user"), e && e(i), await this.handleCommand(i);
|
|
1032
|
+
},
|
|
1033
|
+
() => {
|
|
1034
|
+
console.log("AssistantService: Overlay closed."), t && t();
|
|
1035
|
+
}
|
|
1036
|
+
);
|
|
1037
|
+
}
|
|
1038
|
+
}
|
|
1039
|
+
const S = A(null);
|
|
1040
|
+
let b = null;
|
|
1041
|
+
const z = ({ config: r, children: e }) => {
|
|
1042
|
+
const [t, i] = x(null), [s, c] = x(!1);
|
|
1043
|
+
return w(() => {
|
|
1044
|
+
b ? console.warn("Multiple AssistantProvider instances detected. Reusing global AssistantService.") : (console.log("Initializing global AssistantService..."), b = new $(r));
|
|
1045
|
+
const o = b;
|
|
1046
|
+
return i(o), o.startListening(), c(!0), () => {
|
|
1047
|
+
var a;
|
|
1048
|
+
console.log("Cleaning up AssistantService..."), (a = o.destroy) == null || a.call(o), b = null;
|
|
1049
|
+
};
|
|
1050
|
+
}, [r]), !s || !t ? /* @__PURE__ */ u("div", { children: "Loading Assistant..." }) : /* @__PURE__ */ u(S.Provider, { value: t, children: e });
|
|
1051
|
+
}, F = () => {
|
|
1052
|
+
const r = I(S);
|
|
1053
|
+
if (console.log("assistant", r), !r)
|
|
1054
|
+
throw new Error("useAssistant must be used within an AssistantProvider");
|
|
1055
|
+
return r;
|
|
1056
|
+
}, U = ({
|
|
1057
|
+
label: r = "Activate Assistant",
|
|
1058
|
+
onActivate: e
|
|
1059
|
+
}) => {
|
|
1060
|
+
const t = F();
|
|
1061
|
+
return /* @__PURE__ */ u("button", { onClick: () => {
|
|
1062
|
+
e && e(), t.reactivate();
|
|
1063
|
+
}, className: "assistant-activator", children: r });
|
|
1064
|
+
}, j = (r) => {
|
|
1065
|
+
const [e, t] = x(r.getState());
|
|
1066
|
+
return w(() => {
|
|
1067
|
+
const i = (s) => {
|
|
1068
|
+
t(s);
|
|
1069
|
+
};
|
|
1070
|
+
return r.subscribe(i), () => {
|
|
1071
|
+
r.subscribe(() => {
|
|
1072
|
+
});
|
|
1073
|
+
};
|
|
1074
|
+
}, [r]), e;
|
|
1075
|
+
};
|
|
1076
|
+
export {
|
|
1077
|
+
U as AssistantActivator,
|
|
1078
|
+
S as AssistantContext,
|
|
1079
|
+
z as AssistantProvider,
|
|
1080
|
+
$ as AssistantService,
|
|
1081
|
+
Y as ReactWrapper,
|
|
1082
|
+
F as useAssistant,
|
|
1083
|
+
j as useAssistantState
|
|
1084
|
+
};
|